Message ID | 20230612093541.598260416@infradead.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Scope-based Resource Management | expand |
This patch looks completely broken to me. You now do if (err == -EAGAIN) goto restart; *within* the RCU-guarded section, and the "goto restart" will guard it again. So no. Sending out a series of 57 patches that can have these kinds of bugs in it is not ok. By patch 56 (which just happened to come in fairly early for me), all sane peoples eyes have glazed over and they don't react to this kind of breakage any more. Linus On Mon, Jun 12, 2023 at 2:39 AM Peter Zijlstra <peterz@infradead.org> wrote: > > --- a/kernel/events/core.c > +++ b/kernel/events/core.c > @@ -7977,7 +7977,8 @@ static void perf_pmu_output_stop(struct > int err, cpu; > > restart: > - rcu_read_lock(); > + /* cannot have a label in front of a decl */; > + guard(rcu)(); > list_for_each_entry_rcu(iter, &event->rb->event_list, rb_entry) { > /* > * For per-CPU events, we need to make sure that neither they > @@ -7993,12 +7994,9 @@ static void perf_pmu_output_stop(struct > continue; > > err = cpu_function_call(cpu, __perf_pmu_output_stop, event); > - if (err == -EAGAIN) { > - rcu_read_unlock(); > + if (err == -EAGAIN) > goto restart; > - } > } > - rcu_read_unlock(); > }
On Mon, Jun 12, 2023, Linus Torvalds wrote: > This patch looks completely broken to me. > > You now do > > if (err == -EAGAIN) > goto restart; > > *within* the RCU-guarded section, and the "goto restart" will guard it again. 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? E.g. something like static void perf_pmu_output_stop(struct perf_event *event) fn_guard(rcu) { ... } or just "guard(rcu)" if possible. IIUC, function scopes like that will be possible once -Wdeclaration-after-statement goes away. Bugs aside, IMO guards that are buried in the middle of a function and implicitly scoped to the function are all too easy to overlook. Requiring explicit scoping would make bugs like this easier to spot since the goto would jump out of scope (and I assume prematurely release the resource/lock?). As a bonus, annotating the function itself would also serve as documentation. The only downside is that the code for function-scoped locks that are acquired partway through the function would be more verbose and/or cumbersome to write, but that can be mitigated to some extent, e.g. by moving the locked portion to a separate helper. > On Mon, Jun 12, 2023 at 2:39 AM Peter Zijlstra <peterz@infradead.org> wrote: > > > > --- a/kernel/events/core.c > > +++ b/kernel/events/core.c > > @@ -7977,7 +7977,8 @@ static void perf_pmu_output_stop(struct > > int err, cpu; > > > > restart: > > - rcu_read_lock(); > > + /* cannot have a label in front of a decl */; > > + guard(rcu)(); > > list_for_each_entry_rcu(iter, &event->rb->event_list, rb_entry) { > > /* > > * For per-CPU events, we need to make sure that neither they > > @@ -7993,12 +7994,9 @@ static void perf_pmu_output_stop(struct > > continue; > > > > err = cpu_function_call(cpu, __perf_pmu_output_stop, event); > > - if (err == -EAGAIN) { > > - rcu_read_unlock(); > > + if (err == -EAGAIN) > > goto restart; > > - } > > } > > - rcu_read_unlock(); > > }
On Mon, Jun 12, 2023 at 10:12 AM Sean Christopherson <seanjc@google.com> 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
On Mon, Jun 12, 2023 at 09:19:19AM -0700, Linus Torvalds wrote: > This patch looks completely broken to me. > > You now do > > if (err == -EAGAIN) > goto restart; > > *within* the RCU-guarded section, and the "goto restart" will guard it again. restart: guard(rcu)(); list_for_each_entry_rcu(iter, &head, rb_entry) { ... if (err == -EAGAIN) goto restart; } So the restart is *before* the variable exists, eg. it's out-of-scope. per the last email's guard.c, if changed like so: void main(void) { int done = 0; restart: lock_guard(spin, moo, &lock); for (;!done;) { done = 1; goto restart; } } $ gcc -O2 -o guard guard.c && ./guard spin_lock spin_unlock spin_lock spin_unlock Which is exactly the expected result.
--- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -7977,7 +7977,8 @@ static void perf_pmu_output_stop(struct int err, cpu; restart: - rcu_read_lock(); + /* cannot have a label in front of a decl */; + guard(rcu)(); list_for_each_entry_rcu(iter, &event->rb->event_list, rb_entry) { /* * For per-CPU events, we need to make sure that neither they @@ -7993,12 +7994,9 @@ static void perf_pmu_output_stop(struct continue; err = cpu_function_call(cpu, __perf_pmu_output_stop, event); - if (err == -EAGAIN) { - rcu_read_unlock(); + if (err == -EAGAIN) goto restart; - } } - rcu_read_unlock(); } /*
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> --- kernel/events/core.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)