Re: [PATCH v3 56/57] perf: Simplify perf_pmu_output_stop()

From: Linus Torvalds
Date: Mon Jun 12 2023 - 13:50:33 EST


On Mon, Jun 12, 2023 at 10:12 AM Sean Christopherson <seanjc@xxxxxxxxxx> wrote:
>
> What if we require that all guarded sections have explicit scoping? E.g. drop
> the current version of guard() and rename scoped_guard() => guard(). And then
> figure out macro magic to guard an entire function?

Hmm. I didn't love the excessive scoping, but most of the cases I was
thinking about were for the freeing part, not the locking part.

I agree that explicit scoping might be a good idea for locks as a
rule, but that "entire function" case _is_ a special case, and I don't
see how to do it sanely with a scoping guard.

Unless you accept ugly syntax like

int fn(..)
{ scoped_guard(rcu)() {
...
}}

which is just a violation of our usual scoping indentation rules.

Of course, at that point, the "scoped" part doesn't actually buy us
anything either, so you'd probably just be better off listing all the
guarding locks, and make it be

int fn(..)
{ guard(rcu)(); guard(mutex)(&mymutex); {
...
}}

or whatever.

Ugly, ugly.

End result: I think the non-explicitly scoped syntax is pretty much
required for sane use. The scoped version just causes too much
indentation (or forces us to have the above kind of special "we don't
indent this" rules).

> IIUC, function scopes like that will be possible once
> -Wdeclaration-after-statement goes away.

Well, "-Wdeclaration-after-statement" already went away early in
Peter's series, because without that you can't sanely do the normal
"__free()" cleanup thng.

But no, it doesn't help the C syntax case.

If you were to wrap a whole function with a macro, you need to do some
rather ugly things. They are ugly things that we already do: see our
whole "SYSCALL_DEFINEx()" set of macros, so it's not *impossible*, but
it's not possible with normal C syntax (and the normal C
preprocessor).

Of course, one way to do the "whole function scope" is to just do it
in the caller, and not using the cleanup attribute at all.

IOW, we could have things like

#define WRAP(a,b,c) \
({ __typeof__(b) __ret; a; __ret = (b); c; __ret; })

and then you can do things like

#define guard_fn_mutex(mutex, fn) \
WRAP(mutex_lock(mutex), fn, mutex_unlock(mutex))

or

#define rcu_read_action(x) WRAP(rcu_read_lock(), x, rcu_read_unlock())

and now you can easily guard the call-site (or any simple expression
that doesn't include any non-local control flow). Nothing new and
fancy required.

But when you don't want to do the wrapping in the caller, you do want
to have a non-scoping guard at the top of the function, I suspect.

Linus