RCU changes for v3.5
Subsystems (7)
Linus,
Please pull the latest core-rcu-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git core-rcu-for-linus
HEAD: 2d84e023cb5ec00403ff5d447533c6fd58fcc7ff Merge branch 'rcu/next' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu into core/rcu
This is the v3.5 RCU tree from Paul E. McKenney:
1) A set of improvements and fixes to the RCU_FAST_NO_HZ feature
(with more on the way for 3.6). Posted to LKML: https://lkml.org/lkml/2012/4/23/324 (commits 1-3 and 5), https://lkml.org/lkml/2012/4/16/611 (commit 4), https://lkml.org/lkml/2012/4/30/390 (commit 6), and https://lkml.org/lkml/2012/5/4/410 (commit 7, combined with the other commits for the convenience of the tester).
2) Changes to make rcu_barrier() avoid disrupting execution of CPUs
that have no RCU callbacks. Posted to LKML: https://lkml.org/lkml/2012/4/23/322.
3) A couple of commits that improve the efficiency of the interaction
between preemptible RCU and the scheduler, these two being all that survived an abortive attempt to allow preemptible RCU's __rcu_read_lock() to be inlined. The full set was posted to LKML at https://lkml.org/lkml/2012/4/14/143, and the first and third patches of that set remain.
4) Lai Jiangshan's algorithmic implementation of SRCU, which includes
call_srcu() and srcu_barrier(). A major feature of this new implementation is that synchronize_srcu() no longer disturbs the execution of other CPUs. This work is based on earlier implementations by Peter Zijlstra and Paul E. McKenney. Posted to
LKML: https://lkml.org/lkml/2012/2/22/82.
5) A number of miscellaneous bug fixes and improvements which were
posted to LKML at: https://lkml.org/lkml/2012/4/23/353 with subsequent updates posted to LKML.
Thanks,
Ingo
------------------>
Dave Jones (1):
rcu: List-debug variants of rcu list routines.
Jan Engelhardt (1):
rcu: Make __kfree_rcu() less dependent on compiler choices
Lai Jiangshan (10):
rcu: Remove fast check path from __synchronize_srcu()
rcu: Increment upper bit only for srcu_read_lock()
rcu: Flip ->completed only once per SRCU grace period
rcu: Improve SRCU's wait_idx() comments
rcu: Implement a variant of Peter's SRCU algorithm
rcu: Remove unused srcu_barrier()
rcu: Improve srcu_readers_active_idx()'s cache locality
rcu: Use single value to handle expedited SRCU grace periods
rcu: Implement per-domain single-threaded call_srcu() state machine
rcu: Add rcutorture test for call_srcu()
Michel Machado (1):
rcu: Replace list_first_entry_rcu() with list_first_or_null_rcu()
Paul E. McKenney (19):
rcu: Clarify help text for RCU_BOOST_PRIO
rcu: Reduce cache-miss initialization latencies for large systems
rcu: Document kernel command-line parameters
rcu: Document why rcu_blocking_is_gp() is safe
rcu: Add RCU_FAST_NO_HZ tracing for idle exit
rcu: Make RCU_FAST_NO_HZ use timer rather than hrtimer
rcu: Make RCU_FAST_NO_HZ account for pauses out of idle
rcu: Fixes to rcutorture error handling and cleanup
rcu: Add warning for RCU_FAST_NO_HZ timer firing
timer: Fix mod_timer_pinned() header comment
rcu: Introduce rcutorture testing for rcu_barrier()
rcu: Direct algorithmic SRCU implementation
rcu: Ensure that RCU_FAST_NO_HZ timers expire on correct CPU
rcu: Move PREEMPT_RCU preemption to switch_to() invocation
rcu: Make exit_rcu() more precise and consolidate
rcu: Update RCU maintainership
rcu: Make RCU_FAST_NO_HZ handle timer migration
rcu: Explicitly initialize RCU_FAST_NO_HZ per-CPU variables
rcu: Make rcu_barrier() less disruptive
Documentation/RCU/torture.txt | 15 +- Documentation/kernel-parameters.txt | 88 ++++++- MAINTAINERS | 14 +- arch/um/drivers/mconsole_kern.c | 1 + include/linux/rculist.h | 40 +++- include/linux/rcupdate.h | 20 ++ include/linux/rcutiny.h | 11 - include/linux/rcutree.h | 19 -- include/linux/sched.h | 10 + include/linux/srcu.h | 48 +++- include/trace/events/rcu.h | 2 + init/Kconfig | 50 +++- kernel/rcupdate.c | 28 ++ kernel/rcutiny_plugin.h | 16 - kernel/rcutorture.c | 257 +++++++++++++++- kernel/rcutree.c | 332 ++++++++++++++++------ kernel/rcutree.h | 23 +- kernel/rcutree_plugin.h | 154 +++++++---- kernel/rcutree_trace.c | 4 +- kernel/sched/core.c | 1 + kernel/srcu.c | 548 +++++++++++++++++++++++++++------- kernel/timer.c | 8 +- lib/list_debug.c | 22 ++ 23 files changed, 1358 insertions(+), 353 deletions(-)
Documentation/RCU/torture.txt +14 -1
@@ -47,6 +47,16 @@ irqreader Says to invoke RCU readers from irq level. This is currently permit this. (Or, more accurately, variants of RCU that do -not- permit this know to ignore this variable.) +n_barrier_cbs If this is nonzero, RCU barrier testing will be conducted, + in which case n_barrier_cbs specifies the number of + RCU callbacks (and corresponding kthreads) to use for + this testing. The value cannot be negative. If you + specify this to be non-zero when torture_type indicates a + synchronous RCU implementation (one for which a member of + the synchronize_rcu() rather than the call_rcu() family is + used -- see the documentation for torture_type below), an + error will be reported and no testing will be carried out. + nfakewriters This is the number of RCU fake writer threads to run. Fake writer threads repeatedly use the synchronous "wait for current readers" function of the interface selected by
@@ -188,7 +198,7 @@ OUTPUT The statistics output is as follows: rcu-torture:--- Start of test: nreaders=16 nfakewriters=4 stat_interval=30 verbose=0 test_no_idle_hz=1 shuffle_interval=3 stutter=5 irqreader=1 fqs_duration=0 fqs_holdoff=0 fqs_stutter=3 test_boost=1/0 test_boost_interval=7 test_boost_duration=4 - rcu-torture: rtc: (null) ver: 155441 tfle: 0 rta: 155441 rtaf: 8884 rtf: 155440 rtmbe: 0 rtbke: 0 rtbre: 0 rtbf: 0 rtb: 0 nt: 3055767 + rcu-torture: rtc: (null) ver: 155441 tfle: 0 rta: 155441 rtaf: 8884 rtf: 155440 rtmbe: 0 rtbe: 0 rtbke: 0 rtbre: 0 rtbf: 0 rtb: 0 nt: 3055767 rcu-torture: Reader Pipe: 727860534 34213 0 0 0 0 0 0 0 0 0 rcu-torture: Reader Batch: 727877838 17003 0 0 0 0 0 0 0 0 0 rcu-torture: Free-Block Circulation: 155440 155440 155440 155440 155440 155440 155440 155440 155440 155440 0
@@ -230,6 +240,9 @@ o "rtmbe": A non-zero value indicates that rcutorture believes that rcu_assign_pointer() and rcu_dereference() are not working correctly. This value should be zero. +o "rtbe": A non-zero value indicates that one of the rcu_barrier() + family of functions is not working correctly. + o "rtbke": rcutorture was unable to create the real-time kthreads used to force RCU priority inversion. This value should be zero.
Documentation/kernel-parameters.txt +85 -3
103 lines not on this page · raw message
MAINTAINERS +11 -3
@@ -5608,14 +5608,13 @@ F: net/rds/ READ-COPY UPDATE (RCU) M: Dipankar Sarma <[email protected]> M: "Paul E. McKenney" <[email protected]> -W: http://www.rdrop.com/users/paulmck/rclock/ +W: http://www.rdrop.com/users/paulmck/RCU/ S: Supported T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git F: Documentation/RCU/ +X: Documentation/RCU/torture.txt F: include/linux/rcu* -F: include/linux/srcu* F: kernel/rcu* -F: kernel/srcu* X: kernel/rcutorture.c REAL TIME CLOCK (RTC) SUBSYSTEM
@@ -6132,6 +6131,15 @@ S: Maintained F: include/linux/sl?b*.h F: mm/sl?b.c +SLEEPABLE READ-COPY UPDATE (SRCU) +M: Lai Jiangshan <[email protected]> +M: "Paul E. McKenney" <[email protected]> +W: http://www.rdrop.com/users/paulmck/RCU/ +S: Supported +T: git git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu.git +F: include/linux/srcu* +F: kernel/srcu* + SMC91x ETHERNET DRIVER M: Nicolas Pitre <[email protected]> S: Odd Fixes
arch/um/drivers/mconsole_kern.c +1 -0
@@ -705,6 +705,7 @@ static void stack_proc(void *arg)struct task_struct *from = current, *to = arg; to->thread.saved_task = from; +rcu_switch_from (from);switch_to (from, to, from); }
include/linux/rculist.h +35 -5
@@ -30,6 +30,7 @@* This is only for internal list manipulation where we know * the prev/next entries already! */ +#ifndef CONFIG_DEBUG_LIST static inline void __list_add_rcu (struct list_head *new,struct list_head *prev,struct list_head *next) {
@@ -38,6 +39,10 @@ static inline void __list_add_rcu(struct list_head *new,rcu_assign_pointer (list_next_rcu (prev), new); next->prev = new; } +#else +extern void __list_add_rcu (struct list_head *new, +struct list_head *prev,struct list_head *next); +#endif /** * list_add_rcu - add a new entry to rcu-protected list
@@ -108,7 +113,7 @@ static inline void list_add_tail_rcu(struct list_head *new,*/ static inline void list_del_rcu (struct list_head *entry) { -__list_del (entry->prev, entry->next); +__list_del_entry (entry); entry->prev =LIST_POISON2 ; }
@@ -228,18 +233,43 @@ static inline void list_splice_init_rcu(struct list_head *list, })/** -* list_first_entry_rcu - get the first element from a list +* Where are list_empty_rcu() and list_first_entry_rcu()? +* +* Implementing those functions following their counterparts list_empty() and +* list_first_entry() is not advisable because they lead to subtle race +* conditions as the following snippet shows: +* +* if (!list_empty_rcu(mylist)) { +* struct foo *bar = list_first_entry_rcu(mylist, struct foo, list_member); +* do_something(bar); +* } +* +* The list may not be empty when list_empty_rcu checks it, but it may be when +* list_first_entry_rcu rereads the ->next pointer. +* +* Rereading the ->next pointer is not a problem for list_empty() and +* list_first_entry() because they would be protected by a lock that blocks +* writers. +* +* See list_first_or_null_rcu for an alternative. +*/ + +/** +* list_first_or_null_rcu - get the first element from a list * @ptr: the list head to take the element from. * @type: the type of the struct this is embedded in. * @member: the name of the list_struct within the struct. * -* Note, that list is expected to be not empty. +* Note that if the list is empty, it returns NULL. * * This primitive may safely run concurrently with the _rcu list-mutation * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock(). */ -#define list_first_entry_rcu (ptr, type, member) \ - list_entry_rcu((ptr)->next, type, member) +#define list_first_or_null_rcu (ptr, type, member) \ + ({struct list_head *__ptr = (ptr); \ + struct list_head __rcu *__next = list_next_rcu(__ptr); \ + likely(__ptr != __next) ? container_of(__next, type, member) : NULL; \ + })/** * list_for_each_entry_rcu - iterate over rcu list of given type
include/linux/rcupdate.h +20 -0
@@ -184,12 +184,14 @@ static inline int rcu_preempt_depth(void)/* Internal to kernel */ extern void rcu_sched_qs (int cpu);extern void rcu_bh_qs (int cpu); +extern void rcu_preempt_note_context_switch (void );extern void rcu_check_callbacks (int cpu,int user);struct notifier_block ;extern void rcu_idle_enter (void );extern void rcu_idle_exit (void );extern void rcu_irq_enter (void );extern void rcu_irq_exit (void ); +extern void exit_rcu (void );/** * RCU_NONIDLE - Indicate idle-loop code that needs RCU readers
@@ -922,6 +924,21 @@ void __kfree_rcu(struct rcu_head *head, unsigned long offset)kfree_call_rcu (head, (rcu_callback )offset); } +/* +* Does the specified offset indicate that the corresponding rcu_head +* structure can be handled by kfree_rcu()? +*/ +#define __is_kfree_rcu_offset (offset) ((offset) < 4096) + +/* +* Helper macro for kfree_rcu() to prevent argument-expansion eyestrain. +*/ +#define __kfree_rcu (head, offset) \ + do { \ + BUILD_BUG_ON(!__is_kfree_rcu_offset(offset)); \ + call_rcu(head, (void (*)(struct rcu_head *))(unsigned long)(offset)); \ + } while (0) +/** * kfree_rcu() - kfree an object after a grace period. * @ptr: pointer to kfree
@@ -944,6 +961,9 @@ void __kfree_rcu(struct rcu_head *head, unsigned long offset)* * Note that the allowable offset might decrease in the future, for example, * to allow something like kmem_cache_free_rcu(). +* +* The BUILD_BUG_ON check must not involve any function calls, hence the +* checks are done in macros here. */ #define kfree_rcu (ptr, rcu_head) \ __kfree_rcu(&((ptr)->rcu_head), offsetof(typeof(*(ptr)), rcu_head))
include/linux/rcutiny.h +0 -11
@@ -87,14 +87,6 @@ static inline void kfree_call_rcu(struct rcu_head *head,#ifdef CONFIG_TINY_RCU -static inline void rcu_preempt_note_context_switch (void ) -{ -} - -static inline void exit_rcu (void ) -{ -} -static inline int rcu_needs_cpu (int cpu) {return 0 ;
@@ -102,8 +94,6 @@ static inline int rcu_needs_cpu(int cpu)#else /* #ifdef CONFIG_TINY_RCU */ -void rcu_preempt_note_context_switch (void ); -extern void exit_rcu (void );int rcu_preempt_needs_cpu (void );static inline int rcu_needs_cpu (int cpu)
@@ -116,7 +106,6 @@ static inline int rcu_needs_cpu(int cpu)static inline void rcu_note_context_switch (int cpu) {rcu_sched_qs (cpu); -rcu_preempt_note_context_switch (); }/*
include/linux/rcutree.h +0 -19
@@ -45,18 +45,6 @@ static inline void rcu_virt_note_context_switch(int cpu)rcu_note_context_switch (cpu); } -#ifdef CONFIG_TREE_PREEMPT_RCU - -extern void exit_rcu (void ); - -#else /* #ifdef CONFIG_TREE_PREEMPT_RCU */ - -static inline void exit_rcu (void ) -{ -} - -#endif /* #else #ifdef CONFIG_TREE_PREEMPT_RCU */ -extern void synchronize_rcu_bh (void );extern void synchronize_sched_expedited (void );extern void synchronize_rcu_expedited (void );
@@ -98,13 +86,6 @@ extern void rcu_force_quiescent_state(void);extern void rcu_bh_force_quiescent_state (void );extern void rcu_sched_force_quiescent_state (void ); -/* A context switch is a grace period for RCU-sched and RCU-bh. */ -static inline int rcu_blocking_is_gp (void ) -{ -might_sleep ();/* Check for RCU read-side critical section. */ -return num_online_cpus () ==1 ; -} -extern void rcu_scheduler_starting (void );extern int rcu_scheduler_active__read_mostly ;
include/linux/sched.h +10 -0
@@ -1905,12 +1905,22 @@ static inline void rcu_copy_process(struct task_struct *p) INIT_LIST_HEAD(&p->rcu_node_entry); } +static inline void rcu_switch_from (struct task_struct *prev) +{ +if (prev->rcu_read_lock_nesting !=0 ) +rcu_preempt_note_context_switch (); +} +#else static inline void rcu_copy_process (struct task_struct *p) { } +static inline void rcu_switch_from (struct task_struct *prev) +{ +} +#endif #ifdef CONFIG_SMP
include/linux/srcu.h +39 -9
77 lines not on this page · raw message
include/trace/events/rcu.h +2 -0
@@ -292,6 +292,8 @@ TRACE_EVENT(rcu_dyntick,* "More callbacks": Still more callbacks, try again to clear them out. * "Callbacks drained": All callbacks processed, off to dyntick idle! * "Timer": Timer fired to cause CPU to continue processing callbacks. +* "Demigrate": Timer fired on wrong CPU, woke up correct CPU. +* "Cleanup after idle": Idle exited, timer canceled. */ TRACE_EVENT(rcu_prep_idle,
init/Kconfig +46 -4
62 lines not on this page · raw message
kernel/rcupdate.c +28 -0
@@ -51,6 +51,34 @@#include "rcu.h" +#ifdef CONFIG_PREEMPT_RCU + +/* +* Check for a task exiting while in a preemptible-RCU read-side +* critical section, clean up if so. No need to issue warnings, +* as debug_check_no_locks_held() already does this if lockdep +* is enabled. +*/ +void exit_rcu (void ) +{ +struct task_struct *t = current; + +if (likely (list_empty (¤t->rcu_node_entry))) +return ; + t->rcu_read_lock_nesting =1 ; +barrier (); + t->rcu_read_unlock_special =RCU_READ_UNLOCK_BLOCKED ; +__rcu_read_unlock (); +} + +#else /* #ifdef CONFIG_PREEMPT_RCU */ + +void exit_rcu (void ) +{ +} + +#endif /* #else #ifdef CONFIG_PREEMPT_RCU */ +#ifdef CONFIG_DEBUG_LOCK_ALLOC static struct lock_class_key rcu_lock_key; struct lockdep_map rcu_lock_map =
kernel/rcutiny_plugin.h +0 -16
@@ -851,22 +851,6 @@ int rcu_preempt_needs_cpu(void) return rcu_preempt_ctrlblk.rcb.rcucblist != NULL; } -/* -* Check for a task exiting while in a preemptible -RCU read-side -* critical section, clean up if so. No need to issue warnings, -* as debug_check_no_locks_held() already does this if lockdep -* is enabled. -*/ -void exit_rcu (void ) -{ -struct task_struct *t = current; - -if (t->rcu_read_lock_nesting ==0 ) -return ; - t->rcu_read_lock_nesting =1 ; -__rcu_read_unlock (); -} -#else /* #ifdef CONFIG_TINY_PREEMPT_RCU */ #ifdef CONFIG_RCU_TRACE
kernel/rcutorture.c +242 -15
462 lines not on this page · raw message
kernel/rcutree.c +244 -88
458 lines not on this page · raw message
kernel/rcutree.h +15 -8
@@ -29,18 +29,14 @@#include <linux/seqlock.h> /* -* Define shape of hierarchy based on NR_CPUS and CONFIG_RCU_FANOUT. +* Define shape of hierarchy based on NR_CPUS, CONFIG_RCU_FANOUT, and +* CONFIG_RCU_FANOUT_LEAF. * In theory, it should be possible to add more levels straightforwardly. * In practice, this did work well going from three levels to four. * Of course, your mileage may vary. */ #define MAX_RCU_LVLS 4 -#if CONFIG_RCU_FANOUT >16 -#define RCU_FANOUT_LEAF 16 -#else /* #if CONFIG_RCU_FANOUT > 16 */ -#define RCU_FANOUT_LEAF (CONFIG_RCU_FANOUT) -#endif /* #else #if CONFIG_RCU_FANOUT > 16 */ -#define RCU_FANOUT_1 (RCU_FANOUT_LEAF) +#define RCU_FANOUT_1 (CONFIG_RCU_FANOUT_LEAF)#define RCU_FANOUT_2 (RCU_FANOUT_1 * CONFIG_RCU_FANOUT)#define RCU_FANOUT_3 (RCU_FANOUT_2 * CONFIG_RCU_FANOUT)#define RCU_FANOUT_4 (RCU_FANOUT_3 * CONFIG_RCU_FANOUT)
@@ -371,6 +367,17 @@ struct rcu_state {raw_spinlock_t onofflock;/* exclude on/offline and */ /* starting new GP. */ +struct rcu_head *orphan_nxtlist;/* Orphaned callbacks that */ +/* need a grace period. */ +struct rcu_head **orphan_nxttail;/* Tail of above. */ +struct rcu_head *orphan_donelist;/* Orphaned callbacks that */ +/* are ready to invoke. */ +struct rcu_head **orphan_donetail;/* Tail of above. */ +long qlen_lazy;/* Number of lazy callbacks. */ +long qlen;/* Total number of callbacks. */ +struct task_struct *rcu_barrier_in_progress; +/* Task doing rcu_barrier(), */ +/* or NULL if no barrier. */ raw_spinlock_t fqslock;/* Only one task forcing */ /* quiescent states. */ unsigned long jiffies_force_qs;/* Time at which to invoke */
@@ -423,7 +430,6 @@ DECLARE_PER_CPU(char, rcu_cpu_has_work);/* Forward declarations for rcutree_plugin.h */ static void rcu_bootup_announce (void );long rcu_batches_completed (void ); -static void rcu_preempt_note_context_switch (int cpu);static int rcu_preempt_blocked_readers_cgp (struct rcu_node *rnp);#ifdef CONFIG_HOTPLUG_CPU static void rcu_report_unblock_qs_rnp(struct rcu_node *rnp,
@@ -471,6 +477,7 @@ static void __cpuinit rcu_prepare_kthreads(int cpu);static void rcu_prepare_for_idle_init (int cpu);static void rcu_cleanup_after_idle (int cpu);static void rcu_prepare_for_idle (int cpu); +static void rcu_idle_count_callbacks_posted (void );static void print_cpu_stall_info_begin (void );static void print_cpu_stall_info (struct rcu_state *rsp,int cpu);static void print_cpu_stall_info_end (void );
kernel/rcutree_plugin.h +99 -55
258 lines not on this page · raw message
kernel/rcutree_trace.c +2 -2
@@ -271,13 +271,13 @@ static void print_one_rcu_state(struct seq_file *m, struct rcu_state *rsp) gpnum = rsp->gpnum;seq_printf (m,"c=%lu g=%lu s=%d jfq=%ld j=%x " -"nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu\n" , +"nfqs=%lu/nfqsng=%lu(%lu) fqlh=%lu oqlen=%ld/%ld\n" , rsp->completed, gpnum, rsp->fqs_state, (long )(rsp->jiffies_force_qs - jiffies), (int )(jiffies &0xffff ), rsp->n_force_qs, rsp->n_force_qs_ngp, rsp->n_force_qs - rsp->n_force_qs_ngp, - rsp->n_force_qs_lh); + rsp->n_force_qs_lh, rsp->qlen_lazy, rsp->qlen);for (rnp = &rsp->node[0 ]; rnp - &rsp->node[0 ] <NUM_RCU_NODES ; rnp++) {if (rnp->level != level) {seq_puts (m,"\n" );
kernel/sched/core.c +1 -0
@@ -2083,6 +2083,7 @@ context_switch(struct rq *rq, struct task_struct *prev,#endif /* Here we just switch the register state and the stack. */ +rcu_switch_from (prev);switch_to (prev, next, prev);barrier ();
kernel/srcu.c +435 -113
642 lines not on this page · raw message
kernel/timer.c +7 -1
@@ -861,7 +861,13 @@ EXPORT_SYMBOL(mod_timer);* * mod_timer_pinned() is a way to update the expire field of an * active timer (if the timer is inactive it will be activated) -* and not allow the timer to be migrated to a different CPU. +* and to ensure that the timer is scheduled on the current CPU. +* +* Note that this does not prevent the timer from being migrated +* when the current CPU goes offline. If this is a problem for +* you, use CPU-hotplug notifiers to handle it correctly, for +* example, cancelling the timer when the corresponding CPU goes +* offline. * * mod_timer_pinned(timer, expires) is equivalent to: *
lib/list_debug.c +22 -0
@@ -10,6 +10,7 @@#include <linux/list.h> #include <linux/bug.h> #include <linux/kernel.h> +#include <linux/rculist.h> /* * Insert a new entry between two known consecutive entries.
@@ -75,3 +76,24 @@ void list_del(struct list_head *entry) entry->prev =LIST_POISON2 ; }EXPORT_SYMBOL (list_del); + +/* +* RCU variants. +*/ +void __list_add_rcu (struct list_head *new, +struct list_head *prev,struct list_head *next) +{ +WARN (next->prev != prev, +"list_add_rcu corruption. next->prev should be " +"prev (%p), but was %p. (next=%p).\n" , + prev, next->prev, next); +WARN (prev->next != next, +"list_add_rcu corruption. prev->next should be " +"next (%p), but was %p. (prev=%p).\n" , + next, prev->next, prev); + new->next = next; + new->prev = prev; +rcu_assign_pointer (list_next_rcu (prev), new); + next->prev = new; +} +EXPORT_SYMBOL (__list_add_rcu);