Skip to content

Commit 8fa6f68

Browse files
pkitszelgregkh
authored andcommitted
cleanup: Adjust scoped_guard() macros to avoid potential warning
[ Upstream commit fcc22ac ] Change scoped_guard() and scoped_cond_guard() macros to make reasoning about them easier for static analysis tools (smatch, compiler diagnostics), especially to enable them to tell if the given usage of scoped_guard() is with a conditional lock class (interruptible-locks, try-locks) or not (like simple mutex_lock()). Add compile-time error if scoped_cond_guard() is used for non-conditional lock class. Beyond easier tooling and a little shrink reported by bloat-o-meter this patch enables developer to write code like: int foo(struct my_drv *adapter) { scoped_guard(spinlock, &adapter->some_spinlock) return adapter->spinlock_protected_var; } Current scoped_guard() implementation does not support that, due to compiler complaining: error: control reaches end of non-void function [-Werror=return-type] Technical stuff about the change: scoped_guard() macro uses common idiom of using "for" statement to declare a scoped variable. Unfortunately, current logic is too hard for compiler diagnostics to be sure that there is exactly one loop step; fix that. To make any loop so trivial that there is no above warning, it must not depend on any non-const variable to tell if there are more steps. There is no obvious solution for that in C, but one could use the compound statement expression with "goto" jumping past the "loop", effectively leaving only the subscope part of the loop semantics. More impl details: one more level of macro indirection is now needed to avoid duplicating label names; I didn't spot any other place that is using the "for (...; goto label) if (0) label: break;" idiom, so it's not packed for reuse beyond scoped_guard() family, what makes actual macros code cleaner. There was also a need to introduce const true/false variable per lock class, it is used to aid compiler diagnostics reasoning about "exactly 1 step" loops (note that converting that to function would undo the whole benefit). Big thanks to Andy Shevchenko for help on this patch, both internal and public, ranging from whitespace/formatting, through commit message clarifications, general improvements, ending with presenting alternative approaches - all despite not even liking the idea. Big thanks to Dmitry Torokhov for the idea of compile-time check for scoped_cond_guard() (to use it only with conditional locsk), and general improvements for the patch. Big thanks to David Lechner for idea to cover also scoped_cond_guard(). Signed-off-by: Przemek Kitszel <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Reviewed-by: Dmitry Torokhov <[email protected]> Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Sasha Levin <[email protected]>
1 parent 873df38 commit 8fa6f68

File tree

1 file changed

+42
-10
lines changed

1 file changed

+42
-10
lines changed

include/linux/cleanup.h

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -113,14 +113,20 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
113113
* similar to scoped_guard(), except it does fail when the lock
114114
* acquire fails.
115115
*
116+
* Only for conditional locks.
116117
*/
117118

119+
#define __DEFINE_CLASS_IS_CONDITIONAL(_name, _is_cond) \
120+
static __maybe_unused const bool class_##_name##_is_conditional = _is_cond
121+
118122
#define DEFINE_GUARD(_name, _type, _lock, _unlock) \
123+
__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \
119124
DEFINE_CLASS(_name, _type, if (_T) { _unlock; }, ({ _lock; _T; }), _type _T); \
120125
static inline void * class_##_name##_lock_ptr(class_##_name##_t *_T) \
121126
{ return *_T; }
122127

123128
#define DEFINE_GUARD_COND(_name, _ext, _condlock) \
129+
__DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \
124130
EXTEND_CLASS(_name, _ext, \
125131
({ void *_t = _T; if (_T && !(_condlock)) _t = NULL; _t; }), \
126132
class_##_name##_t _T) \
@@ -131,17 +137,40 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
131137
CLASS(_name, __UNIQUE_ID(guard))
132138

133139
#define __guard_ptr(_name) class_##_name##_lock_ptr
140+
#define __is_cond_ptr(_name) class_##_name##_is_conditional
134141

135-
#define scoped_guard(_name, args...) \
136-
for (CLASS(_name, scope)(args), \
137-
*done = NULL; __guard_ptr(_name)(&scope) && !done; done = (void *)1)
138-
139-
#define scoped_cond_guard(_name, _fail, args...) \
140-
for (CLASS(_name, scope)(args), \
141-
*done = NULL; !done; done = (void *)1) \
142-
if (!__guard_ptr(_name)(&scope)) _fail; \
143-
else
144-
142+
/*
143+
* Helper macro for scoped_guard().
144+
*
145+
* Note that the "!__is_cond_ptr(_name)" part of the condition ensures that
146+
* compiler would be sure that for the unconditional locks the body of the
147+
* loop (caller-provided code glued to the else clause) could not be skipped.
148+
* It is needed because the other part - "__guard_ptr(_name)(&scope)" - is too
149+
* hard to deduce (even if could be proven true for unconditional locks).
150+
*/
151+
#define __scoped_guard(_name, _label, args...) \
152+
for (CLASS(_name, scope)(args); \
153+
__guard_ptr(_name)(&scope) || !__is_cond_ptr(_name); \
154+
({ goto _label; })) \
155+
if (0) { \
156+
_label: \
157+
break; \
158+
} else
159+
160+
#define scoped_guard(_name, args...) \
161+
__scoped_guard(_name, __UNIQUE_ID(label), args)
162+
163+
#define __scoped_cond_guard(_name, _fail, _label, args...) \
164+
for (CLASS(_name, scope)(args); true; ({ goto _label; })) \
165+
if (!__guard_ptr(_name)(&scope)) { \
166+
BUILD_BUG_ON(!__is_cond_ptr(_name)); \
167+
_fail; \
168+
_label: \
169+
break; \
170+
} else
171+
172+
#define scoped_cond_guard(_name, _fail, args...) \
173+
__scoped_cond_guard(_name, _fail, __UNIQUE_ID(label), args)
145174
/*
146175
* Additional helper macros for generating lock guards with types, either for
147176
* locks that don't have a native type (eg. RCU, preempt) or those that need a
@@ -197,14 +226,17 @@ static inline class_##_name##_t class_##_name##_constructor(void) \
197226
}
198227

199228
#define DEFINE_LOCK_GUARD_1(_name, _type, _lock, _unlock, ...) \
229+
__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \
200230
__DEFINE_UNLOCK_GUARD(_name, _type, _unlock, __VA_ARGS__) \
201231
__DEFINE_LOCK_GUARD_1(_name, _type, _lock)
202232

203233
#define DEFINE_LOCK_GUARD_0(_name, _lock, _unlock, ...) \
234+
__DEFINE_CLASS_IS_CONDITIONAL(_name, false); \
204235
__DEFINE_UNLOCK_GUARD(_name, void, _unlock, __VA_ARGS__) \
205236
__DEFINE_LOCK_GUARD_0(_name, _lock)
206237

207238
#define DEFINE_LOCK_GUARD_1_COND(_name, _ext, _condlock) \
239+
__DEFINE_CLASS_IS_CONDITIONAL(_name##_ext, true); \
208240
EXTEND_CLASS(_name, _ext, \
209241
({ class_##_name##_t _t = { .lock = l }, *_T = &_t;\
210242
if (_T->lock && !(_condlock)) _T->lock = NULL; \

0 commit comments

Comments
 (0)