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