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

rcu: Make exit_rcu() more precise and consolidate

When running preemptible RCU, if a task exits in an RCU read-side
critical section having blocked within that same RCU read-side critical
section, the task must be removed from the list of tasks blocking a
grace period (perhaps the current grace period, perhaps the next grace
period, depending on timing). The exit() path invokes exit_rcu() to
do this cleanup.

However, the current implementation of exit_rcu() needlessly does the
cleanup even if the task did not block within the current RCU read-side
critical section, which wastes time and needlessly increases the size
of the state space. Fix this by only doing the cleanup if the current
task is actually on the list of tasks blocking some grace period.

While we are at it, consolidate the two identical exit_rcu() functions
into a single function.

Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Tested-by: Linus Torvalds <torvalds@linux-foundation.org>

Conflicts:

kernel/rcupdate.c

authored by

Paul E. McKenney and committed by
Paul E. McKenney
9dd8fb16 616c310e

+29 -49
+1
include/linux/rcupdate.h
··· 191 191 extern void rcu_idle_exit(void); 192 192 extern void rcu_irq_enter(void); 193 193 extern void rcu_irq_exit(void); 194 + extern void exit_rcu(void); 194 195 195 196 /** 196 197 * RCU_NONIDLE - Indicate idle-loop code that needs RCU readers
-5
include/linux/rcutiny.h
··· 87 87 88 88 #ifdef CONFIG_TINY_RCU 89 89 90 - static inline void exit_rcu(void) 91 - { 92 - } 93 - 94 90 static inline int rcu_needs_cpu(int cpu) 95 91 { 96 92 return 0; ··· 94 98 95 99 #else /* #ifdef CONFIG_TINY_RCU */ 96 100 97 - extern void exit_rcu(void); 98 101 int rcu_preempt_needs_cpu(void); 99 102 100 103 static inline int rcu_needs_cpu(int cpu)
-12
include/linux/rcutree.h
··· 45 45 rcu_note_context_switch(cpu); 46 46 } 47 47 48 - #ifdef CONFIG_TREE_PREEMPT_RCU 49 - 50 - extern void exit_rcu(void); 51 - 52 - #else /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 53 - 54 - static inline void exit_rcu(void) 55 - { 56 - } 57 - 58 - #endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */ 59 - 60 48 extern void synchronize_rcu_bh(void); 61 49 extern void synchronize_sched_expedited(void); 62 50 extern void synchronize_rcu_expedited(void);
+28
kernel/rcupdate.c
··· 51 51 52 52 #include "rcu.h" 53 53 54 + #ifdef CONFIG_PREEMPT_RCU 55 + 56 + /* 57 + * Check for a task exiting while in a preemptible-RCU read-side 58 + * critical section, clean up if so. No need to issue warnings, 59 + * as debug_check_no_locks_held() already does this if lockdep 60 + * is enabled. 61 + */ 62 + void exit_rcu(void) 63 + { 64 + struct task_struct *t = current; 65 + 66 + if (likely(list_empty(&current->rcu_node_entry))) 67 + return; 68 + t->rcu_read_lock_nesting = 1; 69 + barrier(); 70 + t->rcu_read_unlock_special = RCU_READ_UNLOCK_BLOCKED; 71 + __rcu_read_unlock(); 72 + } 73 + 74 + #else /* #ifdef CONFIG_PREEMPT_RCU */ 75 + 76 + void exit_rcu(void) 77 + { 78 + } 79 + 80 + #endif /* #else #ifdef CONFIG_PREEMPT_RCU */ 81 + 54 82 #ifdef CONFIG_DEBUG_LOCK_ALLOC 55 83 static struct lock_class_key rcu_lock_key; 56 84 struct lockdep_map rcu_lock_map =
-16
kernel/rcutiny_plugin.h
··· 851 851 return rcu_preempt_ctrlblk.rcb.rcucblist != NULL; 852 852 } 853 853 854 - /* 855 - * Check for a task exiting while in a preemptible -RCU read-side 856 - * critical section, clean up if so. No need to issue warnings, 857 - * as debug_check_no_locks_held() already does this if lockdep 858 - * is enabled. 859 - */ 860 - void exit_rcu(void) 861 - { 862 - struct task_struct *t = current; 863 - 864 - if (t->rcu_read_lock_nesting == 0) 865 - return; 866 - t->rcu_read_lock_nesting = 1; 867 - __rcu_read_unlock(); 868 - } 869 - 870 854 #else /* #ifdef CONFIG_TINY_PREEMPT_RCU */ 871 855 872 856 #ifdef CONFIG_RCU_TRACE
-16
kernel/rcutree_plugin.h
··· 969 969 rcu_init_one(&rcu_preempt_state, &rcu_preempt_data); 970 970 } 971 971 972 - /* 973 - * Check for a task exiting while in a preemptible-RCU read-side 974 - * critical section, clean up if so. No need to issue warnings, 975 - * as debug_check_no_locks_held() already does this if lockdep 976 - * is enabled. 977 - */ 978 - void exit_rcu(void) 979 - { 980 - struct task_struct *t = current; 981 - 982 - if (t->rcu_read_lock_nesting == 0) 983 - return; 984 - t->rcu_read_lock_nesting = 1; 985 - __rcu_read_unlock(); 986 - } 987 - 988 972 #else /* #ifdef CONFIG_TREE_PREEMPT_RCU */ 989 973 990 974 static struct rcu_state *rcu_state = &rcu_sched_state;