]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
signal: Skip the altstack update when not needed
authorChang S. Bae <chang.seok.bae@intel.com>
Fri, 10 Dec 2021 22:55:03 +0000 (14:55 -0800)
committerStefan Bader <stefan.bader@canonical.com>
Thu, 5 May 2022 07:14:19 +0000 (09:14 +0200)
BugLink: https://bugs.launchpad.net/bugs/1967750
== Background ==

Support for large, "dynamic" fpstates was recently merged.  This
included code to ensure that sigaltstacks are sufficiently sized for
these large states.  A new lock was added to remove races between
enabling large features and setting up sigaltstacks.

== Problem ==

The new lock (sigaltstack_lock()) is acquired in the sigreturn path
before restoring the old sigaltstack.  Unfortunately, contention on the
new lock causes a measurable signal handling performance regression [1].
However, the common case is that no *changes* are made to the
sigaltstack state at sigreturn.

== Solution ==

do_sigaltstack() acquires sigaltstack_lock() and is used for both
sys_sigaltstack() and restoring the sigaltstack in sys_sigreturn().
Check for changes to the sigaltstack before taking the lock.  If no
changes were made, return before acquiring the lock.

This removes lock contention from the common-case sigreturn path.

[1] https://lore.kernel.org/lkml/20211207012128.GA16074@xsang-OptiPlex-9020/

Fixes: 3aac3ebea08f ("x86/signal: Implement sigaltstack size validation")
Reported-by: kernel test robot <oliver.sang@intel.com>
Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Dave Hansen <dave.hansen@linux.intel.com>
Reviewed-by: Thomas Gleixner <tglx@linutronix.de>
Link: https://lkml.kernel.org/r/20211210225503.12734-1-chang.seok.bae@intel.com
(cherry picked from commit 6c3118c32129b4197999a8928ba776bcabd0f5c4)
Acked-by: Dimitri John Ledkov <dimitri.ledkov@canonical.com>
Signed-off-by: Andrea Righi <andrea.righi@canonical.com>
kernel/signal.c

index 0b6a9ab634ccf8e4e8632a0ddb9785242d4288b2..2b423df596a496bb11b098e60384936135281695 100644 (file)
@@ -4211,6 +4211,15 @@ do_sigaltstack (const stack_t *ss, stack_t *oss, unsigned long sp,
                                ss_mode != 0))
                        return -EINVAL;
 
+               /*
+                * Return before taking any locks if no actual
+                * sigaltstack changes were requested.
+                */
+               if (t->sas_ss_sp == (unsigned long)ss_sp &&
+                   t->sas_ss_size == ss_size &&
+                   t->sas_ss_flags == ss_flags)
+                       return 0;
+
                sigaltstack_lock();
                if (ss_mode == SS_DISABLE) {
                        ss_size = 0;