Messages in this thread Patch in this message | | | From | Linus Torvalds <> | Date | Mon, 24 Feb 2020 12:09:04 -0800 | Subject | Re: [LKP] Re: [perf/x86] 81ec3f3c4c: will-it-scale.per_process_ops -5.5% regression |
| |
On Mon, Feb 24, 2020 at 11:24 AM Linus Torvalds <torvalds@linux-foundation.org> wrote: > > I don't know. This does not seem to be a particularly serious load. > But it does feel like it should be possible to combine the two atomic > accesses into one, where you don't need to do the refcount thing > except for the case where sigcount goes from zero to non-zero (and > back to zero again).
Ok, that looks just as simple as I thought it would be.
TOTALLY UNTESTED patch attached. It may be completely buggy garbage, but it _looks_ trivial enough. Just make the rule be that "if we have any user->sigpending cases, we'll get a ref to the user for the first one, and drop it only when getting rid of the last one".
So it might be worth testing this. But again: I have NOT done so.
There might be some silly reason why this doesn't work because I just did the tests wrong or missed some case.
Or there might be some subtle reason why it doesn't work because I didn't think this through properly.
But it _looks_ obvious and simple enough. And it compiles for me. So maybe it works.
Linus kernel/signal.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-)
diff --git a/kernel/signal.c b/kernel/signal.c index 9ad8dea93dbb..00addaa8319f 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -417,10 +417,15 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi /* * Protect access to @t credentials. This can go away when all * callers hold rcu read lock. + * + * NOTE! A pending signal will hold on to the user refcount, + * and we get/put the refcount only when the sigpending count + * changes from/to zero. */ rcu_read_lock(); - user = get_uid(__task_cred(t)->user); - atomic_inc(&user->sigpending); + user = __task_cred(t)->user; + if (atomic_inc_return(&user->sigpending) == 1) + get_uid(user); rcu_read_unlock(); if (override_rlimit || @@ -432,8 +437,8 @@ __sigqueue_alloc(int sig, struct task_struct *t, gfp_t flags, int override_rlimi } if (unlikely(q == NULL)) { - atomic_dec(&user->sigpending); - free_uid(user); + if (atomic_dec_and_test(&user->sigpending)) + free_uid(user); } else { INIT_LIST_HEAD(&q->list); q->flags = 0; @@ -447,8 +452,8 @@ static void __sigqueue_free(struct sigqueue *q) { if (q->flags & SIGQUEUE_PREALLOC) return; - atomic_dec(&q->user->sigpending); - free_uid(q->user); + if (atomic_dec_and_test(&q->user->sigpending)) + free_uid(q->user); kmem_cache_free(sigqueue_cachep, q); } | |