On Thu, Jun 30, 2022 at 6:59 AM Alexander Aring <aahringo@xxxxxxxxxx> wrote: > > I send this patch series as RFC because it was necessary to do a kref > change after adding __cond_lock() to refcount_dec_and_lock() > functionality. Can you try something like this instead? This is two separate patches - one for sparse, and one for the kernel. This is only *very* lightly tested (ie I tested it on a single kernel file that used refcount_dec_and_lock()) Linus
linearize.c | 24 ++++++++++++++++++++++-- validation/context.c | 15 +++++++++++++++ 2 files changed, 37 insertions(+), 2 deletions(-) diff --git a/linearize.c b/linearize.c index d9aed61b..8dd005af 100644 --- a/linearize.c +++ b/linearize.c @@ -1537,6 +1537,8 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi add_one_insn(ep, insn); if (ctype) { + struct basic_block *post_call = NULL; + FOR_EACH_PTR(ctype->contexts, context) { int in = context->in; int out = context->out; @@ -1547,8 +1549,21 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi in = 0; } if (out < 0) { - check = 0; - out = 0; + struct basic_block *set_context; + if (retval == VOID) { + warning(expr->pos, "nonsensical conditional output context with no condition"); + break; + } + if (check || in) { + warning(expr->pos, "nonsensical conditional input context"); + break; + } + if (!post_call) + post_call = alloc_basic_block(ep, expr->pos); + set_context = alloc_basic_block(ep, expr->pos); + add_branch(ep, retval, set_context, post_call); + set_activeblock(ep, set_context); + out = -out; } context_diff = out - in; if (check || context_diff) { @@ -1560,6 +1575,11 @@ static pseudo_t linearize_call_expression(struct entrypoint *ep, struct expressi } } END_FOR_EACH_PTR(context); + if (post_call) { + add_goto(ep, post_call); + set_activeblock(ep, post_call); + } + if (ctype->modifiers & MOD_NORETURN) add_unreachable(ep); } diff --git a/validation/context.c b/validation/context.c index b9500dc7..f8962f19 100644 --- a/validation/context.c +++ b/validation/context.c @@ -10,6 +10,10 @@ static void r(void) __attribute__((context(1,0))) __context__(-1); } +// Negative output means "conditional positive output" +extern int cond_get(void) __attribute((context(0,-1))); +extern void nonsensical_cond_get(void) __attribute((context(0,-1))); + extern int _ca(int fail); #define ca(fail) __cond_lock(_ca(fail)) @@ -19,6 +23,17 @@ static void good_paired1(void) r(); } +static void good_conditional(void) +{ + if (cond_get()) + r(); +} + +static void nonsensical_conditional(void) +{ + nonsensical_cond_get(); +} + static void good_paired2(void) { a();
include/linux/compiler_types.h | 2 ++ include/linux/refcount.h | 6 +++--- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/include/linux/compiler_types.h b/include/linux/compiler_types.h index d08dfcb0ac68..4f2a819fd60a 100644 --- a/include/linux/compiler_types.h +++ b/include/linux/compiler_types.h @@ -24,6 +24,7 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } /* context/locking */ # define __must_hold(x) __attribute__((context(x,1,1))) # define __acquires(x) __attribute__((context(x,0,1))) +# define __cond_acquires(x) __attribute__((context(x,0,-1))) # define __releases(x) __attribute__((context(x,1,0))) # define __acquire(x) __context__(x,1) # define __release(x) __context__(x,-1) @@ -50,6 +51,7 @@ static inline void __chk_io_ptr(const volatile void __iomem *ptr) { } /* context/locking */ # define __must_hold(x) # define __acquires(x) +# define __cond_acquires(x) # define __releases(x) # define __acquire(x) (void)0 # define __release(x) (void)0 diff --git a/include/linux/refcount.h b/include/linux/refcount.h index b8a6e387f8f9..a62fcca97486 100644 --- a/include/linux/refcount.h +++ b/include/linux/refcount.h @@ -361,9 +361,9 @@ static inline void refcount_dec(refcount_t *r) extern __must_check bool refcount_dec_if_one(refcount_t *r); extern __must_check bool refcount_dec_not_one(refcount_t *r); -extern __must_check bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock); -extern __must_check bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock); +extern __must_check bool refcount_dec_and_mutex_lock(refcount_t *r, struct mutex *lock) __cond_acquires(lock); +extern __must_check bool refcount_dec_and_lock(refcount_t *r, spinlock_t *lock) __cond_acquires(lock); extern __must_check bool refcount_dec_and_lock_irqsave(refcount_t *r, spinlock_t *lock, - unsigned long *flags); + unsigned long *flags) __cond_acquires(lock); #endif /* _LINUX_REFCOUNT_H */