From: Peter Zijlstra <peterz@infradead.org>
To: Linus Torvalds <torvalds@linux-foundation.org>
Cc: keescook@chromium.org, gregkh@linuxfoundation.org,
pbonzini@redhat.com, linux-kernel@vger.kernel.org,
ojeda@kernel.org, ndesaulniers@google.com, mingo@redhat.com,
will@kernel.org, longman@redhat.com, boqun.feng@gmail.com,
juri.lelli@redhat.com, vincent.guittot@linaro.org,
dietmar.eggemann@arm.com, rostedt@goodmis.org,
bsegall@google.com, mgorman@suse.de, bristot@redhat.com,
vschneid@redhat.com, paulmck@kernel.org, frederic@kernel.org,
quic_neeraju@quicinc.com, joel@joelfernandes.org,
josh@joshtriplett.org, mathieu.desnoyers@efficios.com,
jiangshanlai@gmail.com, rcu@vger.kernel.org, tj@kernel.org,
tglx@linutronix.de
Subject: Re: [PATCH v2 0/2] Lock and Pointer guards
Date: Tue, 30 May 2023 11:23:42 +0200 [thread overview]
Message-ID: <20230530092342.GA149947@hirez.programming.kicks-ass.net> (raw)
In-Reply-To: <CAHk-=wg2RHZKTN29Gr7MhgYfaNtzz58wry9jCNP75LAmQ9t8-A@mail.gmail.com>
On Sat, May 27, 2023 at 12:18:04PM -0700, Linus Torvalds wrote:
> So how about we take a step back, and say "what if we don't create a
> new scope at all"?
Note that the lock_guard() and ptr_guard() options I have don't require
the new scope thing. The scope thing is optional.
> I think it actually improves on everything. The macros become
> *trivial*. The code becomes more legible.
>
> And you can have multiple different scopes very naturally, or you can
> just share a scope.
>
> Let me build up my argument here. Let's start with this *trivial* helper:
>
> /* Trivial "generic" auto-release macro */
> #define auto_release_name(type, name, init, exit) \
> type name __attribute__((__cleanup__(exit))) = (init)
>
> it's truly stupid: it creates a new variable of type 'type', with name
> 'name', and with the initial value 'init', and with the cleanup
> function 'exit'.
>
> It looks stupid, but it's the *good* stupid. It's really really
> obvious, and there are no games here.
I really don't like the auto naming since C++/C23 use auto for type
inference.
> Let me then introduce *one* other helper, because it turns out that
> much of the time, you don't really want to pick a name. So we'll use
> the above macro, but make a version of it that just automatically
> picks a unique name for you:
>
> #define auto_release(type, init, exit) \
> auto_release_name(type, \
> __UNIQUE_ID(auto) __maybe_unused, \
> init, exit)
I like that option.
> And it turns out that the above two trivial macros are actually quite
> useful in themselves. You want to do an auto-cleanup version of
> 'struct fd'? It's trivial:
>
> /* Trivial "getfd()" wrapper */
> static inline void release_fd(struct fd *fd)
> { fdput(*fd); }
>
> #define auto_getfd(name, n) \
> auto_release_name(struct fd, name, fdget(n), release_fd)
>
> - I think the above is simpler and objectively better in every way
> from the explicitly scoped thing
Well, I think having that as a option would still be very nice.
> - I also suspect that to get maximum advantage of this all, we would
> have to get rid of -Wdeclaration-after-statement
>
> That last point is something that some people will celebrate, but I do
> think it has helped keep our code base somewhat controlled, and made
> people think more about the variables they declare.
>
> But if you start doing consecutive cleanup operations, you really end
> up wanting to do thigns like this:
>
> int testfd2(int fd1, int fd2)
> {
> auto_getfd(f1, fd1);
> if (!f1.file)
> return -EBADF;
> auto_getfd(f2, fd2);
> if (!f2.file)
> return -EBADF;
> return do_something (f1, f2);
> }
If you extend the ptr_guard() idea you don't need to get rid of
-Wdeclaration-after-statement and we could write it like:
int testfd2(int fd1, int fd2)
{
ptr_guard(fdput, f1) = fdget(fd1);
ptr_guard(fdput, f2) = null_ptr(fdput);
if (!f1.file)
return -EBADF;
f2 = fdget(f2);
if (!f2.file)
return -EBADF;
return do_something(f1, f2);
}
Yes, the macros would be a little more involved, but not horribly so I
think.
typedef struct fd guard_fdput_t;
static const struct fd guard_fdput_null = __to_fd(0);
static inline void guard_fdput_cleanup(struct fd *fd)
{
fdput(*fd);
}
#define ptr_guard(_guard, _name) \
guard##_guard##_t _name __cleanup(guard##_guard##_cleanup)
#define null_ptr(_guard) guard##_guard##_null;
And for actual pointer types (as opposed to fat pointer wrappers like
struct fd) we can have a regular helper macro like earlier:
#define DEFINE_PTR_GUARD(_guard, _type, _put) \
typdef _type *guard##_guard##_t; \
static const _type *guard##_guard##_null = NULL; \
static inline void guard##_guard##_cleanup(_type **ptr) \
{ if (*ptr) _put(*ptr); }
[NOTE: value propagation gets rid of the above conditional where
appropriate]
eg.:
DEFINE_PTR_GUARD(put_task, struct task_struct, put_task_struct);
Possibly with a helper:
#define null_guard(_guard, _name) \
ptr_guard(_guard, _name) = null_ptr(_guard)
Now, ptr_guard() per the above, is asymmetric in that it only cares
about release, let guard() be the symmetric thing that also cares about
init like so:
#define DEFINE_GUARD(_guard, _type, _put, _get) \
DEFINE_PTR_GUARD(_guard, _type, _put) \
static inline void guard##_guard##_init(guard##_guard##_t arg) \
{ _get(arg); return arg; }
#define guard(_guard, _name, _var...) \
ptr_guard(_guard, _name) = guard##_guard@##_init(_var)
#define anon_guard(_guard, _var..) \
guard(_guard, __UNIQUE_ID(guard), _var)
for eg.:
DEFINE_GUARD(mutex, struct mutex, mutex_unlock, mutex_lock);
which then lets one write:
int testfd2(int fd1, int fd2)
{
anon_guard(mutex, &cgroup_mutex);
ptr_guard(fdput, f1) = fdget(fd1);
null_guard(fdput, f2);
if (!f1.file)
return -EBADF;
f2 = fdget(fd2);
if (!f2.file)
return -EBADf;
return do_something(f1,f2);
}
The RCU thing can then either be manually done like:
struct rcu_guard;
typedef struct rcu_guard *guard_rcu_t;
static const guard_rcu_null = NULL;
static inline guard_rcu_cleanup(struct rcu_guard **ptr)
{ if (*ptr) rcu_read_unlock(); }
static inline struct rcu_guard *guard_rcu_init(void)
{ rcu_read_lock(); return (void*)1; }
(or because we'll need this pattern a few more times, with yet another
DEFINE_foo_GUARD helper)
and:
anon_guard(rcu);
works.
And at this point the previous scope() things are just one helper macro
away:
#define scope(_guard, _var..) \
for (guard##_guard##_t *done = NULL, scope = guard##_guard##_init(var); \
!done; done++)
to be used where appropriate etc..
Yes, it's a wee bit more involved, but I'm thinking it gives a fair
amount of flexibility and we don't need to ret rid of
-Wdeclaration-after-statement.
Hmm?
next prev parent reply other threads:[~2023-05-30 9:25 UTC|newest]
Thread overview: 42+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-05-26 20:52 [PATCH v2 0/2] Lock and Pointer guards Peter Zijlstra
2023-05-26 20:52 ` [PATCH v2 1/2] locking: Introduce __cleanup__ based guards Peter Zijlstra
2023-05-26 21:24 ` Peter Zijlstra
2023-05-26 21:54 ` Linus Torvalds
2023-05-27 8:57 ` Peter Zijlstra
2023-05-26 20:52 ` [PATCH v2 2/2] sched: Use fancy new guards Peter Zijlstra
2023-05-27 17:21 ` [PATCH v2 0/2] Lock and Pointer guards Mathieu Desnoyers
2023-05-27 19:18 ` Linus Torvalds
2023-05-29 12:09 ` Paolo Bonzini
2023-05-29 19:04 ` Linus Torvalds
2023-05-29 21:27 ` Ian Lance Taylor
2023-05-30 0:06 ` Linus Torvalds
2023-05-30 9:23 ` Peter Zijlstra [this message]
2023-05-30 9:34 ` Peter Zijlstra
2023-05-30 13:58 ` Valentin Schneider
2023-06-06 9:42 ` Peter Zijlstra
2023-06-06 13:17 ` Linus Torvalds
2023-06-06 13:40 ` Peter Zijlstra
2023-06-06 14:50 ` Linus Torvalds
2023-06-06 16:06 ` Kees Cook
2023-06-06 18:08 ` Peter Zijlstra
2023-06-06 23:22 ` Linus Torvalds
2023-06-07 9:41 ` Peter Zijlstra
2023-06-08 8:52 ` Peter Zijlstra
2023-06-08 9:04 ` Greg KH
2023-06-08 15:45 ` Linus Torvalds
2023-06-08 16:47 ` Kees Cook
2023-06-08 16:59 ` Linus Torvalds
2023-06-08 17:20 ` Nick Desaulniers
2023-06-08 18:51 ` Peter Zijlstra
2023-06-08 20:14 ` Nick Desaulniers
2023-06-09 10:20 ` Paolo Bonzini
2023-06-08 20:06 ` Peter Zijlstra
2023-06-09 2:25 ` Linus Torvalds
2023-06-09 8:14 ` Peter Zijlstra
2023-06-09 21:18 ` Kees Cook
2023-06-09 8:27 ` Rasmus Villemoes
2023-06-06 15:31 ` Kees Cook
2023-06-06 15:45 ` Linus Torvalds
2023-06-06 16:08 ` Kees Cook
2023-06-08 16:25 ` David Laight
2023-05-30 9:26 ` Peter Zijlstra
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20230530092342.GA149947@hirez.programming.kicks-ass.net \
--to=peterz@infradead.org \
--cc=boqun.feng@gmail.com \
--cc=bristot@redhat.com \
--cc=bsegall@google.com \
--cc=dietmar.eggemann@arm.com \
--cc=frederic@kernel.org \
--cc=gregkh@linuxfoundation.org \
--cc=jiangshanlai@gmail.com \
--cc=joel@joelfernandes.org \
--cc=josh@joshtriplett.org \
--cc=juri.lelli@redhat.com \
--cc=keescook@chromium.org \
--cc=linux-kernel@vger.kernel.org \
--cc=longman@redhat.com \
--cc=mathieu.desnoyers@efficios.com \
--cc=mgorman@suse.de \
--cc=mingo@redhat.com \
--cc=ndesaulniers@google.com \
--cc=ojeda@kernel.org \
--cc=paulmck@kernel.org \
--cc=pbonzini@redhat.com \
--cc=quic_neeraju@quicinc.com \
--cc=rcu@vger.kernel.org \
--cc=rostedt@goodmis.org \
--cc=tglx@linutronix.de \
--cc=tj@kernel.org \
--cc=torvalds@linux-foundation.org \
--cc=vincent.guittot@linaro.org \
--cc=vschneid@redhat.com \
--cc=will@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox