[PATCH 1/3] cleanup: Add DEFINE_GUARD_ARGS_CLASS macro for resource alloc/free functions with multiple arguments
Frank Li
Frank.Li at nxp.com
Fri Oct 10 11:50:24 PDT 2025
Some resource alloc/free functions require passing more than one argument,
such as dma_map_single() and dma_unmap_single(). Add
DEFINE_GUARD_ARGS_CLASS() to support these cases by introducing a helper
structure to store all arguments for the resource cleanup function.
DEFINE_GUARD_ARGS_CLASS(..., _list_class_fields, _list_func_args, _list_args)
@_list_class_fields: argument list with types, separated by ';'
e.g. (struct device *dev; void *ptr; size_t size; enum dma_data_direction dir; int attr)
@_list_func_args: argument list with types, separated by ','
e.g. (struct device *dev, void *ptr, size_t size, enum dma_data_direction dir, int attr)
@_list_args: argument list without types, separated by ','
e.g. (dev, ptr, size, dir, attr)
Three lists are needed because C syntax differs between struct fields (';'),
function parameters (','), and variable arguments (no types).
Example usage for dma_map_single():
DEFINE_GUARD_ARGS_CLASS(dma_map_single, dma_addr_t,
dma_mapping_error(_T.args.dev, _T.dma_addr),
dma_unmap_single(_T->args.dev, _T->ret,
_T->args.size, _T->args.dir),
dma_map_single,
(struct device *dev; void *ptr; size_t size; enum dma_data_direction dir),
(struct device *dev, void *ptr, size_t size, enum dma_data_direction dir),
(dev, ptr, size, dir))
Example:
fun()
{
CLASS(dma_map_single, dma)(dev, ...);
...
if (error)
return -EIO; // dma_unmap_single() will be auto-called
}
Add retain_and_empty() to keep resource when functions.
Example:
fun()
{
CLASS(dma_map_single, dma)(dev, ...);
...
if (error)
return -EIO; // dma_unmap_single() will be auto-called
retain_and_empty(dma); // dma_umap_single() will NOT called.
return 0;
}
Signed-off-by: Frank Li <Frank.Li at nxp.com>
---
There are some checkpatch.pl error, but these is correct and better
readablity.
ERROR: Macros with complex values should be enclosed in parentheses
+#define DEFINE_GUARD_ARGS_CLASS(_name, _return_type, _is_err, _exit, _init, _list_class_fields, _list_func_args, _list_args) \
+__DEFINE_GUARD_ARGS_CLASS(_name, _return_type, _list_class_fields) \
+__DEFINE_GUARD_ARGS_ENTRY(_name, _is_err, _init, _list_func_args, _list_args) \
+__DEFINE_GUARD_ARGS_EXIT(_name, _exit)
ERROR: Macros with complex values should be enclosed in parentheses
+#define __REMOVE_PAREN(x) __REMOVE_PAREN_HELP x
---
include/linux/cleanup.h | 73 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 73 insertions(+)
diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
index 2573585b7f068abe992af1ac05f478fef7b34306..84b27dd374f2200382b6dc1c450320b79406fd87 100644
--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -518,4 +518,77 @@ __DEFINE_LOCK_GUARD_0(_name, _lock)
#define DEFINE_LOCK_GUARD_1_COND(X...) CONCATENATE(DEFINE_LOCK_GUARD_1_COND_, COUNT_ARGS(X))(X)
+/*
+ * Many resource allocation/free function pairs require passing multiple
+ * arguments — for example, dma_map_single() and dma_unmap_single()
+ *
+ * DEFINE_GUARD_ARGS_CLASS(dma_map_single, dma_addr_t,
+ * dma_mapping_error(_T.args.dev, _T.ret),
+ * dma_unmap_single(_T->args.dev, _T->ret, _T->args.size, _T->args.dir),
+ * dma_map_single,
+ * (struct device *dev; void *ptr; size_t size; enum dma_data_direction dir),
+ * (struct device *dev, void *ptr, size_t size, enum dma_data_direction dir),
+ * (dev, ptr, size, dir))
+ * Example usage:
+ *
+ * int fun() {
+ * CLASS(dma_map_single, dma)(dev, ptr, size, dir);
+ * if (!dma.okay)
+ * return -EIO
+ * ...
+ * if (condition)
+ * return -EIO // cleanup will auto unmap
+ *
+ * req->dma_addr = retain_and_empty(dma); //keep mapping
+ * return 0;
+ * }
+ */
+#define __REMOVE_PAREN_HELP(...) __VA_ARGS__
+#define __REMOVE_PAREN(x) __REMOVE_PAREN_HELP x
+
+#define __DEFINE_GUARD_ARGS_CLASS(_name, _return_type, _list_class_fields) \
+typedef struct { \
+ _return_type ret; \
+ bool okay; \
+ struct { \
+ __REMOVE_PAREN(_list_class_fields); \
+ } args; \
+} class_##_name##_t;
+
+#define __DEFINE_GUARD_ARGS_EXIT(_name, _exit) \
+static inline void class_##_name##_destructor(class_##_name##_t *_T) \
+{ if (_T->okay) { _exit; } }
+
+#define __DEFINE_GUARD_ARGS_ENTRY(_name, _is_err, _init, _list_func_args, _list__args) \
+static inline class_##_name##_t class_##_name##_constructor(__REMOVE_PAREN(_list_func_args)) \
+{ \
+ class_##_name##_t _T = { .args = { __REMOVE_PAREN(_list__args) } }; \
+ _T.ret = _init _list__args; \
+ _T.okay = !(_is_err); \
+ return _T; \
+}
+
+/**
+ * @_name: class name
+ * @_return_type: return data type
+ * @_is_err: macro to check init function return value
+ * @_exit: macro to do resource clean up
+ * @_init: macro/func name to alloc resource
+ * @_list_class_fields: (args list with type, use ; as split)
+ * @_list_func_args: (args list with type, use , as split)
+ * @_list_args: (args list without type, use , as split)
+ */
+#define DEFINE_GUARD_ARGS_CLASS(_name, _return_type, _is_err, _exit, _init, _list_class_fields, _list_func_args, _list_args) \
+__DEFINE_GUARD_ARGS_CLASS(_name, _return_type, _list_class_fields) \
+__DEFINE_GUARD_ARGS_ENTRY(_name, _is_err, _init, _list_func_args, _list_args) \
+__DEFINE_GUARD_ARGS_EXIT(_name, _exit)
+
+#define retain_and_empty(t) \
+ ({ \
+ __auto_type __ptr = &(t); typeof(t) empty = {}; \
+ __auto_type __val = *__ptr; \
+ *__ptr = empty; \
+ __val.ret; \
+ })
+
#endif /* _LINUX_CLEANUP_H */
--
2.34.1
More information about the linux-arm-kernel
mailing list