xref: /freebsd/sys/compat/linuxkpi/common/include/linux/cleanup.h (revision 592ae60e2b2eff6c2ec467c34be9a457ce24b044)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2024-2026 The FreeBSD Foundation
5  *
6  * This software was developed by Björn Zeeb under sponsorship from
7  * the FreeBSD Foundation.
8  */
9 
10 #ifndef	_LINUXKPI_LINUX_CLEANUP_H
11 #define	_LINUXKPI_LINUX_CLEANUP_H
12 
13 #include <linux/err.h>
14 
15 #define	CLEANUP_NAME(_n, _s)	__CONCAT(__CONCAT(cleanup_, _n), _s)
16 
17 #define	__cleanup(_f)		__attribute__((__cleanup__(_f)))
18 
19 #define	DECLARE(_n, _x)							\
20     CLEANUP_NAME(_n, _t) _x __cleanup(CLEANUP_NAME(_n, _destroy)) =	\
21 	CLEANUP_NAME(_n, _create)
22 
23 /*
24  * On Linux, the following macro is called `DEFINE_CLASS()`. However it
25  * conflicts with FreeBSD's macro defined in <sys/kobj.h>.
26  *
27  * Note: "_T" are special as they are exposed into common code for
28  * statements.  Extra care should be taken when changing the code.
29  */
30 #define	LINUXKPI_DEFINE_CLASS(_name, _type, _exit, _init, _init_args...)\
31     typedef _type class_##_name##_t;					\
32 									\
33     static inline _type class_##_name##_constructor(_init_args)		\
34     {									\
35 	_type v = _init;						\
36 	return (v);							\
37     }									\
38 									\
39     static inline void class_##_name##_destructor(_type *p)		\
40     {									\
41 	_type _T = *p;							\
42 	_exit;								\
43     }
44 
45 #define	CLASS(_name, _var)						\
46     class_##_name##_t _var __cleanup(class_##_name##_destructor) =	\
47 	class_##_name##_constructor
48 
49 /*
50  * Note: "_T" are special as they are exposed into common code for
51  * statements.  Extra care should be taken when changing the code.
52  */
53 #define	DEFINE_GUARD(_n, _dt, _lock, _unlock)				\
54 									\
55     typedef _dt CLEANUP_NAME(_n, _t);					\
56 									\
57     static inline _dt							\
58     CLEANUP_NAME(_n, _create)( _dt _T)					\
59     {									\
60 	_dt c;								\
61 									\
62 	c = ({ _lock; _T; });						\
63 	return (c);							\
64     }									\
65 									\
66     static inline void							\
67     CLEANUP_NAME(_n, _destroy)(_dt *t)					\
68     {									\
69 	_dt _T;								\
70 									\
71 	_T = *t;							\
72 	if (_T) { _unlock; };						\
73     }
74 
75 /* We need to keep these calls unique. */
76 #define	_guard(_n, _x)							\
77     DECLARE(_n, _x)
78 #define	guard(_n)							\
79     _guard(_n, guard_ ## _n ## _ ## __COUNTER__)
80 
81 #define	DEFINE_FREE(_n, _t, _f)						\
82     static inline void							\
83     __free_ ## _n(void *p)						\
84     {									\
85 	_t _T;								\
86 									\
87 	_T = *(_t *)p;							\
88 	_f;								\
89     }
90 
91 #define	__free(_n)		__cleanup(__free_##_n)
92 
93 /*
94  * Our initial version go broken up.  Some simplifications like using
95  * "bool" for the lock had to be changed to a more general type.
96  * _T is still special and, like other bits, may not always be used,
97  * so tag with __unused (or better the LinuxKPI __maybe_unused).
98  */
99 #define	_DEFINE_LOCK_GUARD_0(_n, _lock)					\
100     static inline CLEANUP_NAME(_n, _t)					\
101     CLEANUP_NAME(_n, _create)(void)					\
102     {									\
103 	CLEANUP_NAME(_n, _t) _tmp;					\
104 	CLEANUP_NAME(_n, _t) *_T __maybe_unused;			\
105 									\
106 	_tmp.lock = (void *)1;						\
107 	_T = &_tmp;							\
108 	_lock;								\
109 	return (_tmp);							\
110     }
111 
112 #define	_DEFINE_LOCK_GUARD_1(_n, _type, _lock)				\
113     static inline CLEANUP_NAME(_n, _t)					\
114     CLEANUP_NAME(_n, _create)(_type *l)					\
115     {									\
116 	CLEANUP_NAME(_n, _t) _tmp;					\
117 	CLEANUP_NAME(_n, _t) *_T __maybe_unused;			\
118 									\
119 	_tmp.lock = l;							\
120 	_T = &_tmp;							\
121 	_lock;								\
122 	return (_tmp);							\
123     }
124 
125 #define	_GUARD_IS_ERR(_v)						\
126     ({									\
127 	uintptr_t x = (uintptr_t)(void *)(_v);				\
128 	IS_ERR_VALUE(x);						\
129     })
130 
131 #define	__is_cond_ptr(_n)						\
132     CLEANUP_NAME(_n, _is_cond)
133 #define	__guard_ptr(_n)							\
134     CLEANUP_NAME(_n, _ptr)
135 
136 #define	_DEFINE_CLEANUP_IS_CONDITIONAL(_n, _b)				\
137     static const bool CLEANUP_NAME(_n, _is_cond) __maybe_unused = _b
138 
139 #define	_DEFINE_GUARD_LOCK_PTR(_n, _lp)					\
140     static inline void *						\
141     CLEANUP_NAME(_n, _lock_ptr)(CLEANUP_NAME(_n, _t) *_T)		\
142     {									\
143 	void *_p;							\
144 									\
145 	_p = (void *)(uintptr_t)*(_lp);					\
146 	if (IS_ERR(_p))							\
147 		_p = NULL;						\
148 	return (_p);							\
149     }
150 
151 #define	_DEFINE_UNLOCK_GUARD(_n, _type, _unlock, ...)			\
152     typedef struct {							\
153 	_type *lock;							\
154 	__VA_ARGS__;							\
155     } CLEANUP_NAME(_n, _t);	    					\
156 									\
157     static inline void							\
158     CLEANUP_NAME(_n, _destroy)(CLEANUP_NAME(_n, _t) *_T)		\
159     {									\
160 	if (!_GUARD_IS_ERR(_T->lock)) {					\
161 	    _unlock;							\
162 	}								\
163     }									\
164 									\
165     _DEFINE_GUARD_LOCK_PTR(_n, &_T->lock)
166 
167 #define	DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...)			\
168     _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false);				\
169     _DEFINE_UNLOCK_GUARD(_n, void, _unlock, __VA_ARGS__)		\
170     _DEFINE_LOCK_GUARD_0(_n, _lock)
171 
172 /* This allows the type to be set. */
173 #define	DEFINE_LOCK_GUARD_1(_n, _t, _lock, _unlock, ...)		\
174     _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false);				\
175     _DEFINE_UNLOCK_GUARD(_n, _t, _unlock, __VA_ARGS__)			\
176     _DEFINE_LOCK_GUARD_1(_n, _t, _lock)
177 
178 #define	_scoped_guard(_n, _l, ...)					\
179     for (DECLARE(_n, _scoped)(__VA_ARGS__);				\
180 	1 /*__guard_ptr(_n)(&_scoped) || !__is_cond_ptr(_n) */;		\
181 	({ goto _l; }))							\
182 		if (0) {						\
183 _l:									\
184 			break;						\
185 		} else
186 
187 #define	scoped_guard(_n, ...)						\
188     _scoped_guard(_n, ___label_ ## __COUNTER__, ##__VA_ARGS__)
189 
190 #endif	/* _LINUXKPI_LINUX_CLEANUP_H */
191