xref: /freebsd/sys/compat/linuxkpi/common/include/linux/cleanup.h (revision 902136e0fe112383ec64d2ef43a446063b5e6417)
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  * Note: "_T" are special as they are exposed into common code for
25  * statements.  Extra care should be taken when changing the code.
26  */
27 #define	DEFINE_GUARD(_n, _dt, _lock, _unlock)				\
28 									\
29     typedef _dt CLEANUP_NAME(_n, _t);					\
30 									\
31     static inline _dt							\
32     CLEANUP_NAME(_n, _create)( _dt _T)					\
33     {									\
34 	_dt c;								\
35 									\
36 	c = ({ _lock; _T; });						\
37 	return (c);							\
38     }									\
39 									\
40     static inline void							\
41     CLEANUP_NAME(_n, _destroy)(_dt *t)					\
42     {									\
43 	_dt _T;								\
44 									\
45 	_T = *t;							\
46 	if (_T) { _unlock; };						\
47     }
48 
49 /* We need to keep these calls unique. */
50 #define	_guard(_n, _x)							\
51     DECLARE(_n, _x)
52 #define	guard(_n)							\
53     _guard(_n, guard_ ## _n ## _ ## __COUNTER__)
54 
55 #define	DEFINE_FREE(_n, _t, _f)						\
56     static inline void							\
57     __free_ ## _n(void *p)						\
58     {									\
59 	_t _T;								\
60 									\
61 	_T = *(_t *)p;							\
62 	_f;								\
63     }
64 
65 #define	__free(_n)		__cleanup(__free_##_n)
66 
67 /*
68  * Our initial version go broken up.  Some simplifications like using
69  * "bool" for the lock had to be changed to a more general type.
70  * _T is still special and, like other bits, may not always be used,
71  * so tag with __unused (or better the LinuxKPI __maybe_unused).
72  */
73 #define	_DEFINE_LOCK_GUARD_0(_n, _lock)					\
74     static inline CLEANUP_NAME(_n, _t)					\
75     CLEANUP_NAME(_n, _create)(void)					\
76     {									\
77 	CLEANUP_NAME(_n, _t) _tmp;					\
78 	CLEANUP_NAME(_n, _t) *_T __maybe_unused;			\
79 									\
80 	_tmp.lock = (void *)1;						\
81 	_T = &_tmp;							\
82 	_lock;								\
83 	return (_tmp);							\
84     }
85 
86 #define	_DEFINE_LOCK_GUARD_1(_n, _type, _lock)				\
87     static inline CLEANUP_NAME(_n, _t)					\
88     CLEANUP_NAME(_n, _create)(_type *l)					\
89     {									\
90 	CLEANUP_NAME(_n, _t) _tmp;					\
91 	CLEANUP_NAME(_n, _t) *_T __maybe_unused;			\
92 									\
93 	_tmp.lock = l;							\
94 	_T = &_tmp;							\
95 	_lock;								\
96 	return (_tmp);							\
97     }
98 
99 #define	_GUARD_IS_ERR(_v)						\
100     ({									\
101 	uintptr_t x = (uintptr_t)(void *)(_v);				\
102 	IS_ERR_VALUE(x);						\
103     })
104 
105 #define	__is_cond_ptr(_n)						\
106     CLEANUP_NAME(_n, _is_cond)
107 #define	__guard_ptr(_n)							\
108     CLEANUP_NAME(_n, _ptr)
109 
110 #define	_DEFINE_CLEANUP_IS_CONDITIONAL(_n, _b)				\
111     static const bool CLEANUP_NAME(_n, _is_cond) __maybe_unused = _b
112 
113 #define	_DEFINE_GUARD_LOCK_PTR(_n, _lp)					\
114     static inline void *						\
115     CLEANUP_NAME(_n, _lock_ptr)(CLEANUP_NAME(_n, _t) *_T)		\
116     {									\
117 	void *_p;							\
118 									\
119 	_p = (void *)(uintptr_t)*(_lp);					\
120 	if (IS_ERR(_p))							\
121 		_p = NULL;						\
122 	return (_p);							\
123     }
124 
125 #define	_DEFINE_UNLOCK_GUARD(_n, _type, _unlock, ...)			\
126     typedef struct {							\
127 	_type *lock;							\
128 	__VA_ARGS__;							\
129     } CLEANUP_NAME(_n, _t);	    					\
130 									\
131     static inline void							\
132     CLEANUP_NAME(_n, _destroy)(CLEANUP_NAME(_n, _t) *_T)		\
133     {									\
134 	if (!_GUARD_IS_ERR(_T->lock)) {					\
135 	    _unlock;							\
136 	}								\
137     }									\
138 									\
139     _DEFINE_GUARD_LOCK_PTR(_n, &_T->lock)
140 
141 #define	DEFINE_LOCK_GUARD_0(_n, _lock, _unlock, ...)			\
142     _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false);				\
143     _DEFINE_UNLOCK_GUARD(_n, void, _unlock, __VA_ARGS__)		\
144     _DEFINE_LOCK_GUARD_0(_n, _lock)
145 
146 /* This allows the type to be set. */
147 #define	DEFINE_LOCK_GUARD_1(_n, _t, _lock, _unlock, ...)		\
148     _DEFINE_CLEANUP_IS_CONDITIONAL(_n, false);				\
149     _DEFINE_UNLOCK_GUARD(_n, _t, _unlock, __VA_ARGS__)			\
150     _DEFINE_LOCK_GUARD_1(_n, _t, _lock)
151 
152 #define	_scoped_guard(_n, _l, ...)					\
153     for (DECLARE(_n, _scoped)(__VA_ARGS__);				\
154 	1 /*__guard_ptr(_n)(&_scoped) || !__is_cond_ptr(_n) */;		\
155 	({ goto _l; }))							\
156 		if (0) {						\
157 _l:									\
158 			break;						\
159 		} else
160 
161 #define	scoped_guard(_n, ...)						\
162     _scoped_guard(_n, ___label_ ## __COUNTER__, ##__VA_ARGS__)
163 
164 #endif	/* _LINUXKPI_LINUX_CLEANUP_H */
165