1e7ee1501SPaul E. McKenney // SPDX-License-Identifier: GPL-2.0+ 2d8be8173SPaul E. McKenney /* 3d8be8173SPaul E. McKenney * Sleepable Read-Copy Update mechanism for mutual exclusion, 4d8be8173SPaul E. McKenney * tiny version for non-preemptible single-CPU use. 5d8be8173SPaul E. McKenney * 6d8be8173SPaul E. McKenney * Copyright (C) IBM Corporation, 2017 7d8be8173SPaul E. McKenney * 8e7ee1501SPaul E. McKenney * Author: Paul McKenney <[email protected]> 9d8be8173SPaul E. McKenney */ 10d8be8173SPaul E. McKenney 11d8be8173SPaul E. McKenney #include <linux/export.h> 12d8be8173SPaul E. McKenney #include <linux/mutex.h> 13d8be8173SPaul E. McKenney #include <linux/preempt.h> 14d8be8173SPaul E. McKenney #include <linux/rcupdate_wait.h> 15d8be8173SPaul E. McKenney #include <linux/sched.h> 16d8be8173SPaul E. McKenney #include <linux/delay.h> 17d8be8173SPaul E. McKenney #include <linux/srcu.h> 18d8be8173SPaul E. McKenney 19d8be8173SPaul E. McKenney #include <linux/rcu_node_tree.h> 2045753c5fSIngo Molnar #include "rcu_segcblist.h" 21d8be8173SPaul E. McKenney #include "rcu.h" 22d8be8173SPaul E. McKenney 23825c5bd2SPaul E. McKenney int rcu_scheduler_active __read_mostly; 24e0fcba9aSPaul E. McKenney static LIST_HEAD(srcu_boot_list); 25e0fcba9aSPaul E. McKenney static bool srcu_init_done; 26825c5bd2SPaul E. McKenney 27aacb5d91SPaul E. McKenney static int init_srcu_struct_fields(struct srcu_struct *ssp) 28d8be8173SPaul E. McKenney { 29aacb5d91SPaul E. McKenney ssp->srcu_lock_nesting[0] = 0; 30aacb5d91SPaul E. McKenney ssp->srcu_lock_nesting[1] = 0; 31aacb5d91SPaul E. McKenney init_swait_queue_head(&ssp->srcu_wq); 32aacb5d91SPaul E. McKenney ssp->srcu_cb_head = NULL; 33aacb5d91SPaul E. McKenney ssp->srcu_cb_tail = &ssp->srcu_cb_head; 34aacb5d91SPaul E. McKenney ssp->srcu_gp_running = false; 35aacb5d91SPaul E. McKenney ssp->srcu_gp_waiting = false; 36aacb5d91SPaul E. McKenney ssp->srcu_idx = 0; 378b5bd67cSPaul E. McKenney ssp->srcu_idx_max = 0; 38aacb5d91SPaul E. McKenney INIT_WORK(&ssp->srcu_work, srcu_drive_gp); 39aacb5d91SPaul E. McKenney INIT_LIST_HEAD(&ssp->srcu_work.entry); 40d8be8173SPaul E. McKenney return 0; 41d8be8173SPaul E. McKenney } 42d8be8173SPaul E. McKenney 43d8be8173SPaul E. McKenney #ifdef CONFIG_DEBUG_LOCK_ALLOC 44d8be8173SPaul E. McKenney 45aacb5d91SPaul E. McKenney int __init_srcu_struct(struct srcu_struct *ssp, const char *name, 46d8be8173SPaul E. McKenney struct lock_class_key *key) 47d8be8173SPaul E. McKenney { 48d8be8173SPaul E. McKenney /* Don't re-initialize a lock while it is held. */ 49aacb5d91SPaul E. McKenney debug_check_no_locks_freed((void *)ssp, sizeof(*ssp)); 50aacb5d91SPaul E. McKenney lockdep_init_map(&ssp->dep_map, name, key, 0); 51aacb5d91SPaul E. McKenney return init_srcu_struct_fields(ssp); 52d8be8173SPaul E. McKenney } 53d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__init_srcu_struct); 54d8be8173SPaul E. McKenney 55d8be8173SPaul E. McKenney #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 56d8be8173SPaul E. McKenney 57d8be8173SPaul E. McKenney /* 58d8be8173SPaul E. McKenney * init_srcu_struct - initialize a sleep-RCU structure 59aacb5d91SPaul E. McKenney * @ssp: structure to initialize. 60d8be8173SPaul E. McKenney * 61d8be8173SPaul E. McKenney * Must invoke this on a given srcu_struct before passing that srcu_struct 62d8be8173SPaul E. McKenney * to any other function. Each srcu_struct represents a separate domain 63d8be8173SPaul E. McKenney * of SRCU protection. 64d8be8173SPaul E. McKenney */ 65aacb5d91SPaul E. McKenney int init_srcu_struct(struct srcu_struct *ssp) 66d8be8173SPaul E. McKenney { 67aacb5d91SPaul E. McKenney return init_srcu_struct_fields(ssp); 68d8be8173SPaul E. McKenney } 69d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(init_srcu_struct); 70d8be8173SPaul E. McKenney 71d8be8173SPaul E. McKenney #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 72d8be8173SPaul E. McKenney 73d8be8173SPaul E. McKenney /* 74d8be8173SPaul E. McKenney * cleanup_srcu_struct - deconstruct a sleep-RCU structure 75aacb5d91SPaul E. McKenney * @ssp: structure to clean up. 76d8be8173SPaul E. McKenney * 77d8be8173SPaul E. McKenney * Must invoke this after you are finished using a given srcu_struct that 78d8be8173SPaul E. McKenney * was initialized via init_srcu_struct(), else you leak memory. 79d8be8173SPaul E. McKenney */ 80f5ad3991SPaul E. McKenney void cleanup_srcu_struct(struct srcu_struct *ssp) 81d8be8173SPaul E. McKenney { 82aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_lock_nesting[0] || ssp->srcu_lock_nesting[1]); 83aacb5d91SPaul E. McKenney flush_work(&ssp->srcu_work); 84aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_gp_running); 85aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_gp_waiting); 86aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_cb_head); 87aacb5d91SPaul E. McKenney WARN_ON(&ssp->srcu_cb_head != ssp->srcu_cb_tail); 888b5bd67cSPaul E. McKenney WARN_ON(ssp->srcu_idx != ssp->srcu_idx_max); 898b5bd67cSPaul E. McKenney WARN_ON(ssp->srcu_idx & 0x1); 90d8be8173SPaul E. McKenney } 91f5ad3991SPaul E. McKenney EXPORT_SYMBOL_GPL(cleanup_srcu_struct); 92d8be8173SPaul E. McKenney 93d8be8173SPaul E. McKenney /* 94d8be8173SPaul E. McKenney * Removes the count for the old reader from the appropriate element of 95cdf7abc4SPaolo Bonzini * the srcu_struct. 96d8be8173SPaul E. McKenney */ 97aacb5d91SPaul E. McKenney void __srcu_read_unlock(struct srcu_struct *ssp, int idx) 98d8be8173SPaul E. McKenney { 9965b4a595SPaul E. McKenney int newval; 100d8be8173SPaul E. McKenney 10165b4a595SPaul E. McKenney preempt_disable(); // Needed for PREEMPT_AUTO 10265b4a595SPaul E. McKenney newval = READ_ONCE(ssp->srcu_lock_nesting[idx]) - 1; 103aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_lock_nesting[idx], newval); 10465b4a595SPaul E. McKenney preempt_enable(); 1051f8da406SPaul E. McKenney if (!newval && READ_ONCE(ssp->srcu_gp_waiting) && in_task()) 106aacb5d91SPaul E. McKenney swake_up_one(&ssp->srcu_wq); 107d8be8173SPaul E. McKenney } 108d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__srcu_read_unlock); 109d8be8173SPaul E. McKenney 110d8be8173SPaul E. McKenney /* 111d8be8173SPaul E. McKenney * Workqueue handler to drive one grace period and invoke any callbacks 11290326f05SSebastian Andrzej Siewior * that become ready as a result. Single-CPU and !PREEMPTION operation 113d8be8173SPaul E. McKenney * means that we get away with murder on synchronization. ;-) 114d8be8173SPaul E. McKenney */ 115d8be8173SPaul E. McKenney void srcu_drive_gp(struct work_struct *wp) 116d8be8173SPaul E. McKenney { 117d8be8173SPaul E. McKenney int idx; 1182464dd94SPaul E. McKenney struct rcu_head *lh; 119d8be8173SPaul E. McKenney struct rcu_head *rhp; 120aacb5d91SPaul E. McKenney struct srcu_struct *ssp; 121d8be8173SPaul E. McKenney 122aacb5d91SPaul E. McKenney ssp = container_of(wp, struct srcu_struct, srcu_work); 12365b4a595SPaul E. McKenney preempt_disable(); // Needed for PREEMPT_AUTO 12465b4a595SPaul E. McKenney if (ssp->srcu_gp_running || ULONG_CMP_GE(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max))) { 125d8be8173SPaul E. McKenney return; /* Already running or nothing to do. */ 12665b4a595SPaul E. McKenney preempt_enable(); 12765b4a595SPaul E. McKenney } 128d8be8173SPaul E. McKenney 1292464dd94SPaul E. McKenney /* Remove recently arrived callbacks and wait for readers. */ 130aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_running, true); 1312464dd94SPaul E. McKenney local_irq_disable(); 132aacb5d91SPaul E. McKenney lh = ssp->srcu_cb_head; 133aacb5d91SPaul E. McKenney ssp->srcu_cb_head = NULL; 134aacb5d91SPaul E. McKenney ssp->srcu_cb_tail = &ssp->srcu_cb_head; 1352464dd94SPaul E. McKenney local_irq_enable(); 13674612a07SPaul E. McKenney idx = (ssp->srcu_idx & 0x2) / 2; 13774612a07SPaul E. McKenney WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); 138aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_waiting, true); /* srcu_read_unlock() wakes! */ 13965b4a595SPaul E. McKenney preempt_enable(); 140aacb5d91SPaul E. McKenney swait_event_exclusive(ssp->srcu_wq, !READ_ONCE(ssp->srcu_lock_nesting[idx])); 14165b4a595SPaul E. McKenney preempt_disable(); // Needed for PREEMPT_AUTO 142aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_waiting, false); /* srcu_read_unlock() cheap. */ 14374612a07SPaul E. McKenney WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); 14465b4a595SPaul E. McKenney preempt_enable(); 145d8be8173SPaul E. McKenney 1462464dd94SPaul E. McKenney /* Invoke the callbacks we removed above. */ 1472464dd94SPaul E. McKenney while (lh) { 1482464dd94SPaul E. McKenney rhp = lh; 1492464dd94SPaul E. McKenney lh = lh->next; 1502cbc482dSZhen Lei debug_rcu_head_callback(rhp); 151d8be8173SPaul E. McKenney local_bh_disable(); 152d8be8173SPaul E. McKenney rhp->func(rhp); 153d8be8173SPaul E. McKenney local_bh_enable(); 154d8be8173SPaul E. McKenney } 155d8be8173SPaul E. McKenney 156d8be8173SPaul E. McKenney /* 1572464dd94SPaul E. McKenney * Enable rescheduling, and if there are more callbacks, 1582464dd94SPaul E. McKenney * reschedule ourselves. This can race with a call_srcu() 1592464dd94SPaul E. McKenney * at interrupt level, but the ->srcu_gp_running checks will 1602464dd94SPaul E. McKenney * straighten that out. 161d8be8173SPaul E. McKenney */ 16265b4a595SPaul E. McKenney preempt_disable(); // Needed for PREEMPT_AUTO 163aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_running, false); 16465b4a595SPaul E. McKenney idx = ULONG_CMP_LT(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max)); 16565b4a595SPaul E. McKenney preempt_enable(); 16665b4a595SPaul E. McKenney if (idx) 167aacb5d91SPaul E. McKenney schedule_work(&ssp->srcu_work); 168d8be8173SPaul E. McKenney } 169d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(srcu_drive_gp); 170d8be8173SPaul E. McKenney 1711a893c71SPaul E. McKenney static void srcu_gp_start_if_needed(struct srcu_struct *ssp) 1721a893c71SPaul E. McKenney { 1735fe89191SPaul E. McKenney unsigned long cookie; 1748b5bd67cSPaul E. McKenney 17565b4a595SPaul E. McKenney preempt_disable(); // Needed for PREEMPT_AUTO 1768b5bd67cSPaul E. McKenney cookie = get_state_synchronize_srcu(ssp); 17765b4a595SPaul E. McKenney if (ULONG_CMP_GE(READ_ONCE(ssp->srcu_idx_max), cookie)) { 17865b4a595SPaul E. McKenney preempt_enable(); 1798b5bd67cSPaul E. McKenney return; 18065b4a595SPaul E. McKenney } 1818b5bd67cSPaul E. McKenney WRITE_ONCE(ssp->srcu_idx_max, cookie); 1821a893c71SPaul E. McKenney if (!READ_ONCE(ssp->srcu_gp_running)) { 1831a893c71SPaul E. McKenney if (likely(srcu_init_done)) 1841a893c71SPaul E. McKenney schedule_work(&ssp->srcu_work); 1851a893c71SPaul E. McKenney else if (list_empty(&ssp->srcu_work.entry)) 1861a893c71SPaul E. McKenney list_add(&ssp->srcu_work.entry, &srcu_boot_list); 1871a893c71SPaul E. McKenney } 18865b4a595SPaul E. McKenney preempt_enable(); 1891a893c71SPaul E. McKenney } 1901a893c71SPaul E. McKenney 191d8be8173SPaul E. McKenney /* 192d8be8173SPaul E. McKenney * Enqueue an SRCU callback on the specified srcu_struct structure, 193d8be8173SPaul E. McKenney * initiating grace-period processing if it is not already running. 194d8be8173SPaul E. McKenney */ 195aacb5d91SPaul E. McKenney void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp, 196d8be8173SPaul E. McKenney rcu_callback_t func) 197d8be8173SPaul E. McKenney { 198d8be8173SPaul E. McKenney unsigned long flags; 199d8be8173SPaul E. McKenney 2002464dd94SPaul E. McKenney rhp->func = func; 2012464dd94SPaul E. McKenney rhp->next = NULL; 20265b4a595SPaul E. McKenney preempt_disable(); // Needed for PREEMPT_AUTO 203d8be8173SPaul E. McKenney local_irq_save(flags); 204aacb5d91SPaul E. McKenney *ssp->srcu_cb_tail = rhp; 205aacb5d91SPaul E. McKenney ssp->srcu_cb_tail = &rhp->next; 206d8be8173SPaul E. McKenney local_irq_restore(flags); 2071a893c71SPaul E. McKenney srcu_gp_start_if_needed(ssp); 20865b4a595SPaul E. McKenney preempt_enable(); 209d8be8173SPaul E. McKenney } 210d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(call_srcu); 211d8be8173SPaul E. McKenney 212d8be8173SPaul E. McKenney /* 213d8be8173SPaul E. McKenney * synchronize_srcu - wait for prior SRCU read-side critical-section completion 214d8be8173SPaul E. McKenney */ 215aacb5d91SPaul E. McKenney void synchronize_srcu(struct srcu_struct *ssp) 216d8be8173SPaul E. McKenney { 217d8be8173SPaul E. McKenney struct rcu_synchronize rs; 218d8be8173SPaul E. McKenney 219f0f44752SBoqun Feng srcu_lock_sync(&ssp->dep_map); 220f0f44752SBoqun Feng 22151f5f78aSZqiang RCU_LOCKDEP_WARN(lockdep_is_held(ssp) || 22251f5f78aSZqiang lock_is_held(&rcu_bh_lock_map) || 22351f5f78aSZqiang lock_is_held(&rcu_lock_map) || 22451f5f78aSZqiang lock_is_held(&rcu_sched_lock_map), 22551f5f78aSZqiang "Illegal synchronize_srcu() in same-type SRCU (or in RCU) read-side critical section"); 22651f5f78aSZqiang 22751f5f78aSZqiang if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE) 22851f5f78aSZqiang return; 22951f5f78aSZqiang 23051f5f78aSZqiang might_sleep(); 231d8be8173SPaul E. McKenney init_rcu_head_on_stack(&rs.head); 232d8be8173SPaul E. McKenney init_completion(&rs.completion); 233aacb5d91SPaul E. McKenney call_srcu(ssp, &rs.head, wakeme_after_rcu); 234d8be8173SPaul E. McKenney wait_for_completion(&rs.completion); 235d8be8173SPaul E. McKenney destroy_rcu_head_on_stack(&rs.head); 236d8be8173SPaul E. McKenney } 237d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(synchronize_srcu); 238825c5bd2SPaul E. McKenney 2398b5bd67cSPaul E. McKenney /* 2408b5bd67cSPaul E. McKenney * get_state_synchronize_srcu - Provide an end-of-grace-period cookie 2418b5bd67cSPaul E. McKenney */ 2428b5bd67cSPaul E. McKenney unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp) 2438b5bd67cSPaul E. McKenney { 2448b5bd67cSPaul E. McKenney unsigned long ret; 2458b5bd67cSPaul E. McKenney 2468b5bd67cSPaul E. McKenney barrier(); 2478b5bd67cSPaul E. McKenney ret = (READ_ONCE(ssp->srcu_idx) + 3) & ~0x1; 2488b5bd67cSPaul E. McKenney barrier(); 2495fe89191SPaul E. McKenney return ret; 2508b5bd67cSPaul E. McKenney } 2518b5bd67cSPaul E. McKenney EXPORT_SYMBOL_GPL(get_state_synchronize_srcu); 2528b5bd67cSPaul E. McKenney 2538b5bd67cSPaul E. McKenney /* 2548b5bd67cSPaul E. McKenney * start_poll_synchronize_srcu - Provide cookie and start grace period 2558b5bd67cSPaul E. McKenney * 2568b5bd67cSPaul E. McKenney * The difference between this and get_state_synchronize_srcu() is that 2578b5bd67cSPaul E. McKenney * this function ensures that the poll_state_synchronize_srcu() will 2588b5bd67cSPaul E. McKenney * eventually return the value true. 2598b5bd67cSPaul E. McKenney */ 2608b5bd67cSPaul E. McKenney unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp) 2618b5bd67cSPaul E. McKenney { 26265b4a595SPaul E. McKenney unsigned long ret; 2638b5bd67cSPaul E. McKenney 26465b4a595SPaul E. McKenney preempt_disable(); // Needed for PREEMPT_AUTO 26565b4a595SPaul E. McKenney ret = get_state_synchronize_srcu(ssp); 2668b5bd67cSPaul E. McKenney srcu_gp_start_if_needed(ssp); 26765b4a595SPaul E. McKenney preempt_enable(); 2688b5bd67cSPaul E. McKenney return ret; 2698b5bd67cSPaul E. McKenney } 2708b5bd67cSPaul E. McKenney EXPORT_SYMBOL_GPL(start_poll_synchronize_srcu); 2718b5bd67cSPaul E. McKenney 2728b5bd67cSPaul E. McKenney /* 2738b5bd67cSPaul E. McKenney * poll_state_synchronize_srcu - Has cookie's grace period ended? 2748b5bd67cSPaul E. McKenney */ 2758b5bd67cSPaul E. McKenney bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie) 2768b5bd67cSPaul E. McKenney { 2775fe89191SPaul E. McKenney unsigned long cur_s = READ_ONCE(ssp->srcu_idx); 2788b5bd67cSPaul E. McKenney 2798b5bd67cSPaul E. McKenney barrier(); 280*e206f33eSPaul E. McKenney return cookie == SRCU_GET_STATE_COMPLETED || 281*e206f33eSPaul E. McKenney ULONG_CMP_GE(cur_s, cookie) || ULONG_CMP_LT(cur_s, cookie - 3); 2828b5bd67cSPaul E. McKenney } 2838b5bd67cSPaul E. McKenney EXPORT_SYMBOL_GPL(poll_state_synchronize_srcu); 2848b5bd67cSPaul E. McKenney 285825c5bd2SPaul E. McKenney /* Lockdep diagnostics. */ 286825c5bd2SPaul E. McKenney void __init rcu_scheduler_starting(void) 287825c5bd2SPaul E. McKenney { 288825c5bd2SPaul E. McKenney rcu_scheduler_active = RCU_SCHEDULER_RUNNING; 289825c5bd2SPaul E. McKenney } 290e0fcba9aSPaul E. McKenney 291e0fcba9aSPaul E. McKenney /* 292e0fcba9aSPaul E. McKenney * Queue work for srcu_struct structures with early boot callbacks. 293e0fcba9aSPaul E. McKenney * The work won't actually execute until the workqueue initialization 294e0fcba9aSPaul E. McKenney * phase that takes place after the scheduler starts. 295e0fcba9aSPaul E. McKenney */ 296e0fcba9aSPaul E. McKenney void __init srcu_init(void) 297e0fcba9aSPaul E. McKenney { 298aacb5d91SPaul E. McKenney struct srcu_struct *ssp; 299e0fcba9aSPaul E. McKenney 300e0fcba9aSPaul E. McKenney srcu_init_done = true; 301e0fcba9aSPaul E. McKenney while (!list_empty(&srcu_boot_list)) { 302aacb5d91SPaul E. McKenney ssp = list_first_entry(&srcu_boot_list, 3034e6ea4efSPaul E. McKenney struct srcu_struct, srcu_work.entry); 304aacb5d91SPaul E. McKenney list_del_init(&ssp->srcu_work.entry); 305aacb5d91SPaul E. McKenney schedule_work(&ssp->srcu_work); 306e0fcba9aSPaul E. McKenney } 307e0fcba9aSPaul E. McKenney } 308