Message ID | 20230612093539.895253662@infradead.org (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Scope-based Resource Management | expand |
This seems to have the same pattern that I *think* it's entirely broken, ie you are doing that guard(perf_pmu_disable)(event->pmu); inside a loop, and then you are randomly just removing the perf_pmu_enable(event->pmu); at the end of the loop, or when you do a "continue"./ That's not right. The thing does not not go out of scope when the loop *iterates*. It only goes out of scope when the loop *ends*. Big difference as far as cleanup goes. So you have not "simplified" the unlocking code, you've broken it. Now it no longer locks and unlocks for each iteration, it tries to re-lock every time. Or have I mis-understood something completely? Linus
On Mon, Jun 12, 2023 at 09:27:09AM -0700, Linus Torvalds wrote: > The thing does not not go out of scope when the loop *iterates*. > > It only goes out of scope when the loop *ends*. > Or have I mis-understood something completely? I tried this before I used it and variables inside a for() loop have a scope of a single iteration. $ gcc -O2 -o guard guard.c && ./guard spin_lock ponies __raw_spin_lock_irqsave can haz raw_spin_unlock_irqrestore mutex_lock mutex_unlock mutex_lock mutex_unlock mutex_lock mutex_unlock mutex_lock mutex_unlock mutex_lock mutex_unlock spin_unlock --- #include <stdio.h> #include <stdbool.h> typedef struct { } raw_spinlock_t; typedef struct { } spinlock_t; typedef struct { } mutex_t; void raw_spin_lock(raw_spinlock_t *) { printf("%s\n", __FUNCTION__); } void raw_spin_unlock(raw_spinlock_t *) { printf("%s\n", __FUNCTION__); } unsigned long __raw_spin_lock_irqsave(raw_spinlock_t *lock) { printf("%s\n", __FUNCTION__); return 0; } #define raw_spin_lock_irqsave(lock, flags) \ flags = __raw_spin_lock_irqsave(lock) void raw_spin_unlock_irqrestore(raw_spinlock_t *lock, unsigned long flags) { printf("%s\n", __FUNCTION__); } void spin_lock(spinlock_t *) { printf("%s\n", __FUNCTION__); } void spin_unlock(spinlock_t *) { printf("%s\n", __FUNCTION__); } void mutex_lock(mutex_t *) { printf("%s\n", __FUNCTION__); } void mutex_unlock(mutex_t *) { printf("%s\n", __FUNCTION__); } #define DEFINE_LOCK_GUARD(_type, _Type, _Lock, _Unlock, ...) \ typedef struct { \ _Type *lock; \ __VA_ARGS__ \ } lock_guard_##_type##_t; \ \ static inline void lock_guard_##_type##_cleanup(void *_g) \ { \ lock_guard_##_type##_t *_G = _g; \ if (_G->lock) \ _Unlock; \ } \ \ static inline lock_guard_##_type##_t lock_guard_##_type##_init(_Type *lock) \ { \ lock_guard_##_type##_t _g = { .lock = lock }, *_G = &_g; \ _Lock; \ return _g; \ } DEFINE_LOCK_GUARD(raw, raw_spinlock_t, raw_spin_lock(_G->lock), raw_spin_unlock(_G->lock) ) DEFINE_LOCK_GUARD(spin, spinlock_t, spin_lock(_G->lock), spin_unlock(_G->lock) ) DEFINE_LOCK_GUARD(mutex, mutex_t, mutex_lock(_G->lock), mutex_unlock(_G->lock) ) DEFINE_LOCK_GUARD(raw_irqsave, raw_spinlock_t, raw_spin_lock_irqsave(_G->lock, _G->flags), raw_spin_unlock_irqrestore(_G->lock, _G->flags), unsigned long flags; ) #define __cleanup(func) __attribute__((__cleanup__(func))) #define lock_guard(_type, _name, _ptr) \ lock_guard_##_type##_t _name __cleanup(lock_guard_##_type##_cleanup) = \ lock_guard_##_type##_init(_ptr) #define lock_scope(_type, _ptr) \ for (struct { lock_guard_##_type##_t guard ; bool done; } _scope __cleanup(lock_guard_##_type##_cleanup) = \ { .guard = lock_guard_##_type##_init(_ptr), .done = false }; \ !_scope.done; _scope.done = true) raw_spinlock_t raw_lock; spinlock_t lock; mutex_t mutex; void main(void) { lock_guard(spin, guard, &lock); printf("ponies\n"); lock_scope(raw_irqsave, &raw_lock) { printf("can haz\n"); } for (int i=0; i<5; i++) { lock_guard(mutex, foo, &mutex); continue; } }
On Mon, Jun 12, 2023 at 11:44 AM Peter Zijlstra <peterz@infradead.org> wrote: > > I tried this before I used it and variables inside a for() loop have a > scope of a single iteration. Whee. Ok, that surprised me. But I guess that I shouldn't have found it surprising, since the value doesn't survive from one iteration to the next. My mental picture of the scope was just different - and apparently wrong. But thinking about it, it's not just that the value doesn't survive, it's also that the "continue" will exit the scope in order to go back to the "for()" loop. I guess the "goto repeat" ends up being similar, since - as Ian Lance Taylor said in one of the earlier discussions - that "__cleanup__" ends up creating an implicit hidden scope for the variable. So a 'goto' to before the variable was declared implicitly exits the scope. Ugh. I do really hate how subtle that is, though. So while it might not be the horrible bug I thought it was, I'd _really_ like us to not do those things just from a sanity angle. Linus
On Mon, Jun 12, 2023 at 11:55:57AM -0700, Linus Torvalds wrote: > So while it might not be the horrible bug I thought it was, I'd > _really_ like us to not do those things just from a sanity angle. OK, let me go see if there's another way to sanely write those. Is this less offensive? restart: scoped_guard (rcu) { list_for_each_entry_rcu(..) { ... if (err == -EAGAIN) goto restart; } } That has the explicit scope on the rcu and now the goto explicitly exist it. Gonna have to think what to do about the continue ...
On Mon, Jun 12, 2023 at 11:55:57AM -0700, Linus Torvalds wrote: > But thinking about it, it's not just that the value doesn't survive, > it's also that the "continue" will exit the scope in order to go back > to the "for()" loop. So if you still feel the continue is a step too far; the alternative isn't horrible either.. --- --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4090,7 +4090,7 @@ perf_adjust_freq_unthr_context(struct pe if (!(ctx->nr_freq || unthrottle)) return; - raw_spin_lock(&ctx->lock); + guard(raw_spinlock)(&ctx->lock); list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { if (event->state != PERF_EVENT_STATE_ACTIVE) @@ -4100,7 +4100,7 @@ perf_adjust_freq_unthr_context(struct pe if (!event_filter_match(event)) continue; - perf_pmu_disable(event->pmu); + guard(perf_pmu_disable)(event->pmu); hwc = &event->hw; @@ -4110,34 +4110,29 @@ perf_adjust_freq_unthr_context(struct pe event->pmu->start(event, 0); } - if (!event->attr.freq || !event->attr.sample_freq) - goto next; + if (event->attr.freq && event->attr.sample_freq) { + /* + * stop the event and update event->count + */ + event->pmu->stop(event, PERF_EF_UPDATE); + + now = local64_read(&event->count); + delta = now - hwc->freq_count_stamp; + hwc->freq_count_stamp = now; + + /* + * restart the event + * reload only if value has changed + * we have stopped the event so tell that + * to perf_adjust_period() to avoid stopping it + * twice. + */ + if (delta > 0) + perf_adjust_period(event, period, delta, false); - /* - * stop the event and update event->count - */ - event->pmu->stop(event, PERF_EF_UPDATE); - - now = local64_read(&event->count); - delta = now - hwc->freq_count_stamp; - hwc->freq_count_stamp = now; - - /* - * restart the event - * reload only if value has changed - * we have stopped the event so tell that - * to perf_adjust_period() to avoid stopping it - * twice. - */ - if (delta > 0) - perf_adjust_period(event, period, delta, false); - - event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); - next: - perf_pmu_enable(event->pmu); + event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); + } } - - raw_spin_unlock(&ctx->lock); } /*
--- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -4100,7 +4100,7 @@ perf_adjust_freq_unthr_context(struct pe if (!(ctx->nr_freq || unthrottle)) return; - raw_spin_lock(&ctx->lock); + guard(raw_spinlock)(&ctx->lock); list_for_each_entry_rcu(event, &ctx->event_list, event_entry) { if (event->state != PERF_EVENT_STATE_ACTIVE) @@ -4110,7 +4110,7 @@ perf_adjust_freq_unthr_context(struct pe if (!event_filter_match(event)) continue; - perf_pmu_disable(event->pmu); + guard(perf_pmu_disable)(event->pmu); hwc = &event->hw; @@ -4121,7 +4121,7 @@ perf_adjust_freq_unthr_context(struct pe } if (!event->attr.freq || !event->attr.sample_freq) - goto next; + continue; /* * stop the event and update event->count @@ -4143,11 +4143,7 @@ perf_adjust_freq_unthr_context(struct pe perf_adjust_period(event, period, delta, false); event->pmu->start(event, delta > 0 ? PERF_EF_RELOAD : 0); - next: - perf_pmu_enable(event->pmu); } - - raw_spin_unlock(&ctx->lock); } /*
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> --- kernel/events/core.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)