diff mbox series

[1/2,v4] cleanup: Add cond_guard() to conditional guards

Message ID 20240208130424.59568-2-fabio.maria.de.francesco@linux.intel.com
State Superseded
Headers show
Series Add cond_guard() to conditional guards | expand

Commit Message

Fabio M. De Francesco Feb. 8, 2024, 1:04 p.m. UTC
Add cond_guard() macro to conditional guards.

cond_guard() is a guard to be used with the conditional variants of locks,
like down_read_trylock() or mutex_lock_interruptible().

It takes a statement (or statement-expression) that is passed as its
second argument. That statement (or statement-expression) is executed if
waiting for a lock is interrupted or if a _trylock() fails in case of
contention.

Usage example:

	cond_guard(mutex_intr, return -EINTR, &mutex);

Consistent with other usage of _guard(), locks are unlocked at the exit of the
scope where cond_guard() is called.

Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Suggested-by: Dan Williams <dan.j.williams@intel.com>
Suggested-by: Ira Weiny <ira.weiny@intel.com>
Reviewed-by: Dave Jiang <dave.jiang@intel.com>
Reviewed-by: Ira Weiny <ira.weiny@intel.com>
Signed-off-by: Fabio M. De Francesco <fabio.maria.de.francesco@linux.intel.com>
---
 include/linux/cleanup.h | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

Comments

Jonathan Cameron Feb. 8, 2024, 2:04 p.m. UTC | #1
On Thu,  8 Feb 2024 14:04:23 +0100
"Fabio M. De Francesco" <fabio.maria.de.francesco@linux.intel.com> wrote:

> Add cond_guard() macro to conditional guards.
> 
> cond_guard() is a guard to be used with the conditional variants of locks,
> like down_read_trylock() or mutex_lock_interruptible().
> 
> It takes a statement (or statement-expression) that is passed as its
> second argument. That statement (or statement-expression) is executed if
> waiting for a lock is interrupted or if a _trylock() fails in case of
> contention.
> 
> Usage example:
> 
> 	cond_guard(mutex_intr, return -EINTR, &mutex);
> 
> Consistent with other usage of _guard(), locks are unlocked at the exit of the
> scope where cond_guard() is called.
> 
> Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
I like the defensive else {}

Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>


> Cc: Peter Zijlstra <peterz@infradead.org>
> Suggested-by: Dan Williams <dan.j.williams@intel.com>
> Suggested-by: Ira Weiny <ira.weiny@intel.com>
> Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> Reviewed-by: Ira Weiny <ira.weiny@intel.com>
> Signed-off-by: Fabio M. De Francesco <fabio.maria.de.francesco@linux.intel.com>
> ---
>  include/linux/cleanup.h | 15 +++++++++++++++
>  1 file changed, 15 insertions(+)
> 
> diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
> index c2d09bc4f976..7b54ee996414 100644
> --- a/include/linux/cleanup.h
> +++ b/include/linux/cleanup.h
> @@ -134,6 +134,16 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
>   *	an anonymous instance of the (guard) class, not recommended for
>   *	conditional locks.
>   *
> + * cond_guard(name, fail, args...):
> + *	a guard to be used with the conditional variants of locks, like
> + *	down_read_trylock() or mutex_lock_interruptible. 'fail' is a
> + *	statement or statement-expression that is executed if waiting for a
> + *	lock is interrupted or if a _trylock() fails in case of contention.
> + *
> + *	Example:
> + *
> + *		cond_guard(mutex_intr, return -EINTR, &mutex);
> + *
>   * 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
> @@ -165,6 +175,11 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
>  
>  #define __guard_ptr(_name) class_##_name##_lock_ptr
>  
> +#define cond_guard(_name, _fail, args...) \
> +	CLASS(_name, scope)(args); \
> +	if (!__guard_ptr(_name)(&scope)) _fail; \
> +	else { }
> +
>  #define scoped_guard(_name, args...)					\
>  	for (CLASS(_name, scope)(args),					\
>  	     *done = NULL; __guard_ptr(_name)(&scope) && !done; done = (void *)1)
Ira Weiny Feb. 8, 2024, 4:21 p.m. UTC | #2
Jonathan Cameron wrote:
> On Thu,  8 Feb 2024 14:04:23 +0100
> "Fabio M. De Francesco" <fabio.maria.de.francesco@linux.intel.com> wrote:
> 
> > Add cond_guard() macro to conditional guards.
> > 
> > cond_guard() is a guard to be used with the conditional variants of locks,
> > like down_read_trylock() or mutex_lock_interruptible().
> > 
> > It takes a statement (or statement-expression) that is passed as its
> > second argument. That statement (or statement-expression) is executed if
> > waiting for a lock is interrupted or if a _trylock() fails in case of
> > contention.
> > 
> > Usage example:
> > 
> > 	cond_guard(mutex_intr, return -EINTR, &mutex);
> > 
> > Consistent with other usage of _guard(), locks are unlocked at the exit of the
> > scope where cond_guard() is called.
> > 
> > Cc: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> I like the defensive else {}

Agreed.

Re-

Reviewed-by: Ira Weiny <ira.weiny@intel.com>

> 
> Reviewed-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
> 
> 
> > Cc: Peter Zijlstra <peterz@infradead.org>
> > Suggested-by: Dan Williams <dan.j.williams@intel.com>
> > Suggested-by: Ira Weiny <ira.weiny@intel.com>
> > Reviewed-by: Dave Jiang <dave.jiang@intel.com>
> > Reviewed-by: Ira Weiny <ira.weiny@intel.com>
> > Signed-off-by: Fabio M. De Francesco <fabio.maria.de.francesco@linux.intel.com>
> > ---
> >  include/linux/cleanup.h | 15 +++++++++++++++
> >  1 file changed, 15 insertions(+)
> > 
> > diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
> > index c2d09bc4f976..7b54ee996414 100644
> > --- a/include/linux/cleanup.h
> > +++ b/include/linux/cleanup.h
> > @@ -134,6 +134,16 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
> >   *	an anonymous instance of the (guard) class, not recommended for
> >   *	conditional locks.
> >   *
> > + * cond_guard(name, fail, args...):
> > + *	a guard to be used with the conditional variants of locks, like
> > + *	down_read_trylock() or mutex_lock_interruptible. 'fail' is a
> > + *	statement or statement-expression that is executed if waiting for a
> > + *	lock is interrupted or if a _trylock() fails in case of contention.
> > + *
> > + *	Example:
> > + *
> > + *		cond_guard(mutex_intr, return -EINTR, &mutex);
> > + *
> >   * 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
> > @@ -165,6 +175,11 @@ static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
> >  
> >  #define __guard_ptr(_name) class_##_name##_lock_ptr
> >  
> > +#define cond_guard(_name, _fail, args...) \
> > +	CLASS(_name, scope)(args); \
> > +	if (!__guard_ptr(_name)(&scope)) _fail; \
> > +	else { }
> > +
> >  #define scoped_guard(_name, args...)					\
> >  	for (CLASS(_name, scope)(args),					\
> >  	     *done = NULL; __guard_ptr(_name)(&scope) && !done; done = (void *)1)
> 
>
Fabio M. De Francesco Feb. 13, 2024, 4:51 p.m. UTC | #3
On Thursday, 8 February 2024 14:04:23 CET Fabio M. De Francesco wrote:
> Add cond_guard() macro to conditional guards.
> 
> cond_guard() is a guard to be used with the conditional variants of locks,
> like down_read_trylock() or mutex_lock_interruptible().
> 
> It takes a statement (or statement-expression) that is passed as its
> second argument. That statement (or statement-expression) is executed if
> waiting for a lock is interrupted or if a _trylock() fails in case of
> contention.
> 
> Usage example:
> 
> 	cond_guard(mutex_intr, return -EINTR, &mutex);
> 
> Consistent with other usage of _guard(), locks are unlocked at the exit of
> the scope where cond_guard() is called.
> 
[snip]
> 
> +#define cond_guard(_name, _fail, args...) \
> +	CLASS(_name, scope)(args); \
> +	if (!__guard_ptr(_name)(&scope)) _fail; \
> +	else { }
> +

I have converted and tested several functions in drivers/cxl and found that 
there are cases where this macro needs to be called twice in the same scope.

The current implementation fails to compile because any subsequent call to 
cond_guard() redefines "scope".

I have a solution for this, which is to instantiate a differently named 
variable each time cond_guard() is used:

#define __UNIQUE_LINE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)
#define cond_guard(_name, _fail, args...) \
        CLASS(_name, __UNIQUE_LINE_ID(scope))(args); \
        if (!__guard_ptr(_name)(&__UNIQUE_LINE_ID(scope))) _fail; \
        else { }

But, before sending v5, I think it's best to wait for comments from those with 
more experience than me.

Fabio
Jonathan Cameron Feb. 14, 2024, 6:04 p.m. UTC | #4
On Tue, 13 Feb 2024 17:51:26 +0100
"Fabio M. De Francesco" <fabio.maria.de.francesco@linux.intel.com> wrote:

> On Thursday, 8 February 2024 14:04:23 CET Fabio M. De Francesco wrote:
> > Add cond_guard() macro to conditional guards.
> > 
> > cond_guard() is a guard to be used with the conditional variants of locks,
> > like down_read_trylock() or mutex_lock_interruptible().
> > 
> > It takes a statement (or statement-expression) that is passed as its
> > second argument. That statement (or statement-expression) is executed if
> > waiting for a lock is interrupted or if a _trylock() fails in case of
> > contention.
> > 
> > Usage example:
> > 
> > 	cond_guard(mutex_intr, return -EINTR, &mutex);
> > 
> > Consistent with other usage of _guard(), locks are unlocked at the exit of
> > the scope where cond_guard() is called.
> >   
> [snip]
> > 
> > +#define cond_guard(_name, _fail, args...) \
> > +	CLASS(_name, scope)(args); \
> > +	if (!__guard_ptr(_name)(&scope)) _fail; \
> > +	else { }
> > +  
> 
> I have converted and tested several functions in drivers/cxl and found that 
> there are cases where this macro needs to be called twice in the same scope.
> 
> The current implementation fails to compile because any subsequent call to 
> cond_guard() redefines "scope".
> 
> I have a solution for this, which is to instantiate a differently named 
> variable each time cond_guard() is used:
> 
> #define __UNIQUE_LINE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)
> #define cond_guard(_name, _fail, args...) \
>         CLASS(_name, __UNIQUE_LINE_ID(scope))(args); \
>         if (!__guard_ptr(_name)(&__UNIQUE_LINE_ID(scope))) _fail; \
>         else { }
> 
> But, before sending v5, I think it's best to wait for comments from those with 
> more experience than me.

Ah. So you can't use __UNIQUE_ID as guard does because we need it to be stable
across the two uses.  What you have looks fine to me.
We might end up with someone putting multiple calls in a macro but in my
view anyone doing that level of complexity in a macro is shooting themselves
in the foot.

Jonathan


> 
> Fabio
> 
> 
> 
>
Jonathan Cameron Feb. 15, 2024, 10:26 a.m. UTC | #5
On Wed, 14 Feb 2024 18:04:52 +0000
Jonathan Cameron <Jonathan.Cameron@Huawei.com> wrote:

> On Tue, 13 Feb 2024 17:51:26 +0100
> "Fabio M. De Francesco" <fabio.maria.de.francesco@linux.intel.com> wrote:
> 
> > On Thursday, 8 February 2024 14:04:23 CET Fabio M. De Francesco wrote:  
> > > Add cond_guard() macro to conditional guards.
> > > 
> > > cond_guard() is a guard to be used with the conditional variants of locks,
> > > like down_read_trylock() or mutex_lock_interruptible().
> > > 
> > > It takes a statement (or statement-expression) that is passed as its
> > > second argument. That statement (or statement-expression) is executed if
> > > waiting for a lock is interrupted or if a _trylock() fails in case of
> > > contention.
> > > 
> > > Usage example:
> > > 
> > > 	cond_guard(mutex_intr, return -EINTR, &mutex);
> > > 
> > > Consistent with other usage of _guard(), locks are unlocked at the exit of
> > > the scope where cond_guard() is called.
> > >     
> > [snip]  
> > > 
> > > +#define cond_guard(_name, _fail, args...) \
> > > +	CLASS(_name, scope)(args); \
> > > +	if (!__guard_ptr(_name)(&scope)) _fail; \
> > > +	else { }
> > > +    
> > 
> > I have converted and tested several functions in drivers/cxl and found that 
> > there are cases where this macro needs to be called twice in the same scope.
> > 
> > The current implementation fails to compile because any subsequent call to 
> > cond_guard() redefines "scope".
> > 
> > I have a solution for this, which is to instantiate a differently named 
> > variable each time cond_guard() is used:
> > 
> > #define __UNIQUE_LINE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __LINE__)
> > #define cond_guard(_name, _fail, args...) \
> >         CLASS(_name, __UNIQUE_LINE_ID(scope))(args); \
> >         if (!__guard_ptr(_name)(&__UNIQUE_LINE_ID(scope))) _fail; \
> >         else { }
> > 
> > But, before sending v5, I think it's best to wait for comments from those with 
> > more experience than me.  
> 
> Ah. So you can't use __UNIQUE_ID as guard does because we need it to be stable
> across the two uses.  What you have looks fine to me.
> We might end up with someone putting multiple calls in a macro but in my
> view anyone doing that level of complexity in a macro is shooting themselves
> in the foot.

Thought more on this whilst cycling home.  Can you use another level
of macros in combination with __UNIQUE_ID that guard uses?
My skills with macros are very limited so I'm sure I got something wrong,
but along the lines of.

#define __cond_class(__unique, _name, _fail, args...) \
   CLASS(_name, __unique)(args); \
         if (!__guard_ptr(_name)(&__unique)) _fail; \
         else { }
#define cond_class(_name, _fail, args... ) \
    __cond_class(__UNIQUE_ID(guard), _name, _fail, args...

?

> 
> Jonathan
> 
> 
> > 
> > Fabio
> > 
> > 
> > 
> >   
> 
>
Fabio M. De Francesco Feb. 15, 2024, 1:12 p.m. UTC | #6
On Thursday, 15 February 2024 11:26:42 CET Jonathan Cameron wrote:
> On Wed, 14 Feb 2024 18:04:52 +0000
> 
> Jonathan Cameron <Jonathan.Cameron@Huawei.com> wrote:
> > On Tue, 13 Feb 2024 17:51:26 +0100
> > 
> > "Fabio M. De Francesco" <fabio.maria.de.francesco@linux.intel.com> wrote:
> > > On Thursday, 8 February 2024 14:04:23 CET Fabio M. De Francesco wrote:
> > > > Add cond_guard() macro to conditional guards.
> > > > 
> > > > cond_guard() is a guard to be used with the conditional variants of
> > > > locks,
> > > > like down_read_trylock() or mutex_lock_interruptible().
> > > > 
> > > > It takes a statement (or statement-expression) that is passed as its
> > > > second argument. That statement (or statement-expression) is executed
> > > > if
> > > > waiting for a lock is interrupted or if a _trylock() fails in case of
> > > > contention.
> > > > 
> > > > Usage example:
> > > > 	cond_guard(mutex_intr, return -EINTR, &mutex);
> > > > 
> > > > Consistent with other usage of _guard(), locks are unlocked at the
> > > > exit of
> > > > the scope where cond_guard() is called.
> > > 
> > > [snip]
> > > 
> > > > +#define cond_guard(_name, _fail, args...) \
> > > > +	CLASS(_name, scope)(args); \
> > > > +	if (!__guard_ptr(_name)(&scope)) _fail; \
> > > > +	else { }
> > > > +
> > > 
> > > I have converted and tested several functions in drivers/cxl and found
> > > that
> > > there are cases where this macro needs to be called twice in the same
> > > scope.
> > > 
> > > The current implementation fails to compile because any subsequent call
> > > to
> > > cond_guard() redefines "scope".
> > > 
> > > I have a solution for this, which is to instantiate a differently named
> > > variable each time cond_guard() is used:
> > > 
> > > #define __UNIQUE_LINE_ID(prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix),
> > > __LINE__) #define cond_guard(_name, _fail, args...) \
> > > 
> > >         CLASS(_name, __UNIQUE_LINE_ID(scope))(args); \
> > >         if (!__guard_ptr(_name)(&__UNIQUE_LINE_ID(scope))) _fail; \
> > >         else { }
> > > 
> > > But, before sending v5, I think it's best to wait for comments from
> > > those with more experience than me.
> > 
> > Ah. So you can't use __UNIQUE_ID as guard does because we need it to be
> > stable across the two uses.  What you have looks fine to me.
> > We might end up with someone putting multiple calls in a macro but in my
> > view anyone doing that level of complexity in a macro is shooting
> > themselves in the foot.
> 
> Thought more on this whilst cycling home.  Can you use another level
> of macros in combination with __UNIQUE_ID that guard uses?
> My skills with macros are very limited so I'm sure I got something wrong,
> but along the lines of.
> 
> #define __cond_class(__unique, _name, _fail, args...) \
>    CLASS(_name, __unique)(args); \
>          if (!__guard_ptr(_name)(&__unique)) _fail; \
>          else { }
> #define cond_class(_name, _fail, args... ) \
>     __cond_class(__UNIQUE_ID(guard), _name, _fail, args...
> 
> ?
>
Yes, certainly.

Your solution is more elegant. We can reuse __UNIQUE_ID(). There is no need of 
a new helper macro. Thanks.

But with s/cond_class/cond_guard/ and s/guard/scope/ (I think that "scope" 
makes the purpose of that variable clearer).  

I think I'll wait a bit more in case someone else wants to comment and then 
I'll submit v5.

Fabio
> 
> > Jonathan
> > 
> > > Fabio
diff mbox series

Patch

diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
index c2d09bc4f976..7b54ee996414 100644
--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -134,6 +134,16 @@  static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
  *	an anonymous instance of the (guard) class, not recommended for
  *	conditional locks.
  *
+ * cond_guard(name, fail, args...):
+ *	a guard to be used with the conditional variants of locks, like
+ *	down_read_trylock() or mutex_lock_interruptible. 'fail' is a
+ *	statement or statement-expression that is executed if waiting for a
+ *	lock is interrupted or if a _trylock() fails in case of contention.
+ *
+ *	Example:
+ *
+ *		cond_guard(mutex_intr, return -EINTR, &mutex);
+ *
  * 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
@@ -165,6 +175,11 @@  static inline class_##_name##_t class_##_name##ext##_constructor(_init_args) \
 
 #define __guard_ptr(_name) class_##_name##_lock_ptr
 
+#define cond_guard(_name, _fail, args...) \
+	CLASS(_name, scope)(args); \
+	if (!__guard_ptr(_name)(&scope)) _fail; \
+	else { }
+
 #define scoped_guard(_name, args...)					\
 	for (CLASS(_name, scope)(args),					\
 	     *done = NULL; __guard_ptr(_name)(&scope) && !done; done = (void *)1)