Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

rcu: Don't redundantly disable irqs in rcu_irq_{enter,exit}()

This commit replaces a local_irq_save()/local_irq_restore() pair with
a lockdep assertion that interrupts are already disabled. This should
remove the corresponding overhead from the interrupt entry/exit fastpaths.

This change was inspired by the fact that Iftekhar Ahmed's mutation
testing showed that removing rcu_irq_enter()'s call to local_ird_restore()
had no effect, which might indicate that interrupts were always enabled
anyway.

Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>

+40 -10
+2 -2
include/linux/rcupdate.h
··· 379 379 */ 380 380 #define RCU_NONIDLE(a) \ 381 381 do { \ 382 - rcu_irq_enter(); \ 382 + rcu_irq_enter_irqson(); \ 383 383 do { a; } while (0); \ 384 - rcu_irq_exit(); \ 384 + rcu_irq_exit_irqson(); \ 385 385 } while (0) 386 386 387 387 /*
+8
include/linux/rcutiny.h
··· 181 181 { 182 182 } 183 183 184 + static inline void rcu_irq_exit_irqson(void) 185 + { 186 + } 187 + 188 + static inline void rcu_irq_enter_irqson(void) 189 + { 190 + } 191 + 184 192 static inline void rcu_irq_exit(void) 185 193 { 186 194 }
+2
include/linux/rcutree.h
··· 97 97 void rcu_idle_exit(void); 98 98 void rcu_irq_enter(void); 99 99 void rcu_irq_exit(void); 100 + void rcu_irq_enter_irqson(void); 101 + void rcu_irq_exit_irqson(void); 100 102 101 103 void exit_rcu(void); 102 104
+2 -2
include/linux/tracepoint.h
··· 171 171 TP_PROTO(data_proto), \ 172 172 TP_ARGS(data_args), \ 173 173 TP_CONDITION(cond), \ 174 - rcu_irq_enter(), \ 175 - rcu_irq_exit()); \ 174 + rcu_irq_enter_irqson(), \ 175 + rcu_irq_exit_irqson()); \ 176 176 } 177 177 #else 178 178 #define __DECLARE_TRACE_RCU(name, proto, args, cond, data_proto, data_args)
+26 -6
kernel/rcu/tree.c
··· 732 732 * 733 733 * Exit from an interrupt handler, which might possibly result in entering 734 734 * idle mode, in other words, leaving the mode in which read-side critical 735 - * sections can occur. 735 + * sections can occur. The caller must have disabled interrupts. 736 736 * 737 737 * This code assumes that the idle loop never does anything that might 738 738 * result in unbalanced calls to irq_enter() and irq_exit(). If your ··· 745 745 */ 746 746 void rcu_irq_exit(void) 747 747 { 748 - unsigned long flags; 749 748 long long oldval; 750 749 struct rcu_dynticks *rdtp; 751 750 752 - local_irq_save(flags); 751 + RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_exit() invoked with irqs enabled!!!"); 753 752 rdtp = this_cpu_ptr(&rcu_dynticks); 754 753 oldval = rdtp->dynticks_nesting; 755 754 rdtp->dynticks_nesting--; ··· 759 760 else 760 761 rcu_eqs_enter_common(oldval, true); 761 762 rcu_sysidle_enter(1); 763 + } 764 + 765 + /* 766 + * Wrapper for rcu_irq_exit() where interrupts are enabled. 767 + */ 768 + void rcu_irq_exit_irqson(void) 769 + { 770 + unsigned long flags; 771 + 772 + local_irq_save(flags); 773 + rcu_irq_exit(); 762 774 local_irq_restore(flags); 763 775 } 764 776 ··· 867 857 * 868 858 * Enter an interrupt handler, which might possibly result in exiting 869 859 * idle mode, in other words, entering the mode in which read-side critical 870 - * sections can occur. 860 + * sections can occur. The caller must have disabled interrupts. 871 861 * 872 862 * Note that the Linux kernel is fully capable of entering an interrupt 873 863 * handler that it never exits, for example when doing upcalls to ··· 883 873 */ 884 874 void rcu_irq_enter(void) 885 875 { 886 - unsigned long flags; 887 876 struct rcu_dynticks *rdtp; 888 877 long long oldval; 889 878 890 - local_irq_save(flags); 879 + RCU_LOCKDEP_WARN(!irqs_disabled(), "rcu_irq_enter() invoked with irqs enabled!!!"); 891 880 rdtp = this_cpu_ptr(&rcu_dynticks); 892 881 oldval = rdtp->dynticks_nesting; 893 882 rdtp->dynticks_nesting++; ··· 897 888 else 898 889 rcu_eqs_exit_common(oldval, true); 899 890 rcu_sysidle_exit(1); 891 + } 892 + 893 + /* 894 + * Wrapper for rcu_irq_enter() where interrupts are enabled. 895 + */ 896 + void rcu_irq_enter_irqson(void) 897 + { 898 + unsigned long flags; 899 + 900 + local_irq_save(flags); 901 + rcu_irq_enter(); 900 902 local_irq_restore(flags); 901 903 } 902 904