forked from analogdevicesinc/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
locking: Introduce __cleanup() based infrastructure
Use __attribute__((__cleanup__(func))) to build: - simple auto-release pointers using __free() - 'classes' with constructor and destructor semantics for scope-based resource management. - lock guards based on the above classes. Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/20230612093537.614161713%40infradead.org
- Loading branch information
Showing
16 changed files
with
272 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
/* SPDX-License-Identifier: GPL-2.0 */ | ||
#ifndef __LINUX_GUARDS_H | ||
#define __LINUX_GUARDS_H | ||
|
||
#include <linux/compiler.h> | ||
|
||
/* | ||
* DEFINE_FREE(name, type, free): | ||
* simple helper macro that defines the required wrapper for a __free() | ||
* based cleanup function. @free is an expression using '_T' to access | ||
* the variable. | ||
* | ||
* __free(name): | ||
* variable attribute to add a scoped based cleanup to the variable. | ||
* | ||
* no_free_ptr(var): | ||
* like a non-atomic xchg(var, NULL), such that the cleanup function will | ||
* be inhibited -- provided it sanely deals with a NULL value. | ||
* | ||
* return_ptr(p): | ||
* returns p while inhibiting the __free(). | ||
* | ||
* Ex. | ||
* | ||
* DEFINE_FREE(kfree, void *, if (_T) kfree(_T)) | ||
* | ||
* struct obj *p __free(kfree) = kmalloc(...); | ||
* if (!p) | ||
* return NULL; | ||
* | ||
* if (!init_obj(p)) | ||
* return NULL; | ||
* | ||
* return_ptr(p); | ||
*/ | ||
|
||
#define DEFINE_FREE(_name, _type, _free) \ | ||
static inline void __free_##_name(void *p) { _type _T = *(_type *)p; _free; } | ||
|
||
#define __free(_name) __cleanup(__free_##_name) | ||
|
||
#define no_free_ptr(p) \ | ||
({ __auto_type __ptr = (p); (p) = NULL; __ptr; }) | ||
|
||
#define return_ptr(p) return no_free_ptr(p) | ||
|
||
|
||
/* | ||
* DEFINE_CLASS(name, type, exit, init, init_args...): | ||
* helper to define the destructor and constructor for a type. | ||
* @exit is an expression using '_T' -- similar to FREE above. | ||
* @init is an expression in @init_args resulting in @type | ||
* | ||
* EXTEND_CLASS(name, ext, init, init_args...): | ||
* extends class @name to @name@ext with the new constructor | ||
* | ||
* CLASS(name, var)(args...): | ||
* declare the variable @var as an instance of the named class | ||
* | ||
* Ex. | ||
* | ||
* DEFINE_CLASS(fdget, struct fd, fdput(_T), fdget(fd), int fd) | ||
* | ||
* CLASS(fdget, f)(fd); | ||
* if (!f.file) | ||
* return -EBADF; | ||
* | ||
* // use 'f' without concern | ||
*/ | ||
|
||
#define DEFINE_CLASS(_name, _type, _exit, _init, _init_args...) \ | ||
typedef _type class_##_name##_t; \ | ||
static inline void class_##_name##_destructor(_type *p) \ | ||
{ _type _T = *p; _exit; } \ | ||
static inline _type class_##_name##_constructor(_init_args) \ | ||
{ _type t = _init; return t; } | ||
|
||
#define EXTEND_CLASS(_name, ext, _init, _init_args...) \ | ||
typedef class_##_name##_t class_##_name##ext##_t; \ | ||
static inline void class_##_name##ext##_destructor(class_##_name##_t *p)\ | ||
{ class_##_name##_destructor(p); } \ | ||
static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \ | ||
{ class_##_name##_t t = _init; return t; } | ||
|
||
#define CLASS(_name, var) \ | ||
class_##_name##_t var __cleanup(class_##_name##_destructor) = \ | ||
class_##_name##_constructor | ||
|
||
|
||
/* | ||
* DEFINE_GUARD(name, type, lock, unlock): | ||
* trivial wrapper around DEFINE_CLASS() above specifically | ||
* for locks. | ||
* | ||
* guard(name): | ||
* an anonymous instance of the (guard) class | ||
* | ||
* scoped_guard (name, args...) { }: | ||
* similar to CLASS(name, scope)(args), except the variable (with the | ||
* explicit name 'scope') is declard in a for-loop such that its scope is | ||
* bound to the next (compound) statement. | ||
* | ||
*/ | ||
|
||
#define DEFINE_GUARD(_name, _type, _lock, _unlock) \ | ||
DEFINE_CLASS(_name, _type, _unlock, ({ _lock; _T; }), _type _T) | ||
|
||
#define guard(_name) \ | ||
CLASS(_name, __UNIQUE_ID(guard)) | ||
|
||
#define scoped_guard(_name, args...) \ | ||
for (CLASS(_name, scope)(args), \ | ||
*done = NULL; !done; done = (void *)1) | ||
|
||
/* | ||
* Additional helper macros for generating lock guards with types, either for | ||
* locks that don't have a native type (eg. RCU, preempt) or those that need a | ||
* 'fat' pointer (eg. spin_lock_irqsave). | ||
* | ||
* DEFINE_LOCK_GUARD_0(name, lock, unlock, ...) | ||
* DEFINE_LOCK_GUARD_1(name, type, lock, unlock, ...) | ||
* | ||
* will result in the following type: | ||
* | ||
* typedef struct { | ||
* type *lock; // 'type := void' for the _0 variant | ||
* __VA_ARGS__; | ||
* } class_##name##_t; | ||
* | ||
* As above, both _lock and _unlock are statements, except this time '_T' will | ||
* be a pointer to the above struct. | ||
*/ | ||
|
||
#define __DEFINE_UNLOCK_GUARD(_name, _type, _unlock, ...) \ | ||
typedef struct { \ | ||
_type *lock; \ | ||
__VA_ARGS__; \ | ||
} class_##_name##_t; \ | ||
\ | ||
static inline void class_##_name##_destructor(class_##_name##_t *_T) \ | ||
{ \ | ||
if (_T->lock) { _unlock; } \ | ||
} | ||
|
||
|
||
#define __DEFINE_LOCK_GUARD_1(_name, _type, _lock) \ | ||
static inline class_##_name##_t class_##_name##_constructor(_type *l) \ | ||
{ \ | ||
class_##_name##_t _t = { .lock = l }, *_T = &_t; \ | ||
_lock; \ | ||
return _t; \ | ||
} | ||
|
||
#define __DEFINE_LOCK_GUARD_0(_name, _lock) \ | ||
static inline class_##_name##_t class_##_name##_constructor(void) \ | ||
{ \ | ||
class_##_name##_t _t = { .lock = (void*)1 }, \ | ||
*_T __maybe_unused = &_t; \ | ||
_lock; \ | ||
return _t; \ | ||
} | ||
|
||
#define DEFINE_LOCK_GUARD_1(_name, _type, _lock, _unlock, ...) \ | ||
__DEFINE_UNLOCK_GUARD(_name, _type, _unlock, __VA_ARGS__) \ | ||
__DEFINE_LOCK_GUARD_1(_name, _type, _lock) | ||
|
||
#define DEFINE_LOCK_GUARD_0(_name, _lock, _unlock, ...) \ | ||
__DEFINE_UNLOCK_GUARD(_name, void, _unlock, __VA_ARGS__) \ | ||
__DEFINE_LOCK_GUARD_0(_name, _lock) | ||
|
||
#endif /* __LINUX_GUARDS_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.