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 { 9965bfdd36SPaul E. McKenney int newval = READ_ONCE(ssp->srcu_lock_nesting[idx]) - 1; 100d8be8173SPaul E. McKenney 101aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_lock_nesting[idx], newval); 1021f8da406SPaul E. McKenney if (!newval && READ_ONCE(ssp->srcu_gp_waiting) && in_task()) 103aacb5d91SPaul E. McKenney swake_up_one(&ssp->srcu_wq); 104d8be8173SPaul E. McKenney } 105d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__srcu_read_unlock); 106d8be8173SPaul E. McKenney 107d8be8173SPaul E. McKenney /* 108d8be8173SPaul E. McKenney * Workqueue handler to drive one grace period and invoke any callbacks 10990326f05SSebastian Andrzej Siewior * that become ready as a result. Single-CPU and !PREEMPTION operation 110d8be8173SPaul E. McKenney * means that we get away with murder on synchronization. ;-) 111d8be8173SPaul E. McKenney */ 112d8be8173SPaul E. McKenney void srcu_drive_gp(struct work_struct *wp) 113d8be8173SPaul E. McKenney { 114d8be8173SPaul E. McKenney int idx; 1152464dd94SPaul E. McKenney struct rcu_head *lh; 116d8be8173SPaul E. McKenney struct rcu_head *rhp; 117aacb5d91SPaul E. McKenney struct srcu_struct *ssp; 118d8be8173SPaul E. McKenney 119aacb5d91SPaul E. McKenney ssp = container_of(wp, struct srcu_struct, srcu_work); 1205fe89191SPaul E. McKenney if (ssp->srcu_gp_running || ULONG_CMP_GE(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max))) 121d8be8173SPaul E. McKenney return; /* Already running or nothing to do. */ 122d8be8173SPaul E. McKenney 1232464dd94SPaul E. McKenney /* Remove recently arrived callbacks and wait for readers. */ 124aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_running, true); 1252464dd94SPaul E. McKenney local_irq_disable(); 126aacb5d91SPaul E. McKenney lh = ssp->srcu_cb_head; 127aacb5d91SPaul E. McKenney ssp->srcu_cb_head = NULL; 128aacb5d91SPaul E. McKenney ssp->srcu_cb_tail = &ssp->srcu_cb_head; 1292464dd94SPaul E. McKenney local_irq_enable(); 13074612a07SPaul E. McKenney idx = (ssp->srcu_idx & 0x2) / 2; 13174612a07SPaul E. McKenney WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); 132aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_waiting, true); /* srcu_read_unlock() wakes! */ 133aacb5d91SPaul E. McKenney swait_event_exclusive(ssp->srcu_wq, !READ_ONCE(ssp->srcu_lock_nesting[idx])); 134aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_waiting, false); /* srcu_read_unlock() cheap. */ 13574612a07SPaul E. McKenney WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); 136d8be8173SPaul E. McKenney 1372464dd94SPaul E. McKenney /* Invoke the callbacks we removed above. */ 1382464dd94SPaul E. McKenney while (lh) { 1392464dd94SPaul E. McKenney rhp = lh; 1402464dd94SPaul E. McKenney lh = lh->next; 141d8be8173SPaul E. McKenney local_bh_disable(); 142d8be8173SPaul E. McKenney rhp->func(rhp); 143d8be8173SPaul E. McKenney local_bh_enable(); 144d8be8173SPaul E. McKenney } 145d8be8173SPaul E. McKenney 146d8be8173SPaul E. McKenney /* 1472464dd94SPaul E. McKenney * Enable rescheduling, and if there are more callbacks, 1482464dd94SPaul E. McKenney * reschedule ourselves. This can race with a call_srcu() 1492464dd94SPaul E. McKenney * at interrupt level, but the ->srcu_gp_running checks will 1502464dd94SPaul E. McKenney * straighten that out. 151d8be8173SPaul E. McKenney */ 152aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_running, false); 1535fe89191SPaul E. McKenney if (ULONG_CMP_LT(ssp->srcu_idx, READ_ONCE(ssp->srcu_idx_max))) 154aacb5d91SPaul E. McKenney schedule_work(&ssp->srcu_work); 155d8be8173SPaul E. McKenney } 156d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(srcu_drive_gp); 157d8be8173SPaul E. McKenney 1581a893c71SPaul E. McKenney static void srcu_gp_start_if_needed(struct srcu_struct *ssp) 1591a893c71SPaul E. McKenney { 1605fe89191SPaul E. McKenney unsigned long cookie; 1618b5bd67cSPaul E. McKenney 1628b5bd67cSPaul E. McKenney cookie = get_state_synchronize_srcu(ssp); 1635fe89191SPaul E. McKenney if (ULONG_CMP_GE(READ_ONCE(ssp->srcu_idx_max), cookie)) 1648b5bd67cSPaul E. McKenney return; 1658b5bd67cSPaul E. McKenney WRITE_ONCE(ssp->srcu_idx_max, cookie); 1661a893c71SPaul E. McKenney if (!READ_ONCE(ssp->srcu_gp_running)) { 1671a893c71SPaul E. McKenney if (likely(srcu_init_done)) 1681a893c71SPaul E. McKenney schedule_work(&ssp->srcu_work); 1691a893c71SPaul E. McKenney else if (list_empty(&ssp->srcu_work.entry)) 1701a893c71SPaul E. McKenney list_add(&ssp->srcu_work.entry, &srcu_boot_list); 1711a893c71SPaul E. McKenney } 1721a893c71SPaul E. McKenney } 1731a893c71SPaul E. McKenney 174d8be8173SPaul E. McKenney /* 175d8be8173SPaul E. McKenney * Enqueue an SRCU callback on the specified srcu_struct structure, 176d8be8173SPaul E. McKenney * initiating grace-period processing if it is not already running. 177d8be8173SPaul E. McKenney */ 178aacb5d91SPaul E. McKenney void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp, 179d8be8173SPaul E. McKenney rcu_callback_t func) 180d8be8173SPaul E. McKenney { 181d8be8173SPaul E. McKenney unsigned long flags; 182d8be8173SPaul E. McKenney 1832464dd94SPaul E. McKenney rhp->func = func; 1842464dd94SPaul E. McKenney rhp->next = NULL; 185d8be8173SPaul E. McKenney local_irq_save(flags); 186aacb5d91SPaul E. McKenney *ssp->srcu_cb_tail = rhp; 187aacb5d91SPaul E. McKenney ssp->srcu_cb_tail = &rhp->next; 188d8be8173SPaul E. McKenney local_irq_restore(flags); 1891a893c71SPaul E. McKenney srcu_gp_start_if_needed(ssp); 190d8be8173SPaul E. McKenney } 191d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(call_srcu); 192d8be8173SPaul E. McKenney 193d8be8173SPaul E. McKenney /* 194d8be8173SPaul E. McKenney * synchronize_srcu - wait for prior SRCU read-side critical-section completion 195d8be8173SPaul E. McKenney */ 196aacb5d91SPaul E. McKenney void synchronize_srcu(struct srcu_struct *ssp) 197d8be8173SPaul E. McKenney { 198d8be8173SPaul E. McKenney struct rcu_synchronize rs; 199d8be8173SPaul E. McKenney 200*51f5f78aSZqiang RCU_LOCKDEP_WARN(lockdep_is_held(ssp) || 201*51f5f78aSZqiang lock_is_held(&rcu_bh_lock_map) || 202*51f5f78aSZqiang lock_is_held(&rcu_lock_map) || 203*51f5f78aSZqiang lock_is_held(&rcu_sched_lock_map), 204*51f5f78aSZqiang "Illegal synchronize_srcu() in same-type SRCU (or in RCU) read-side critical section"); 205*51f5f78aSZqiang 206*51f5f78aSZqiang if (rcu_scheduler_active == RCU_SCHEDULER_INACTIVE) 207*51f5f78aSZqiang return; 208*51f5f78aSZqiang 209*51f5f78aSZqiang might_sleep(); 210d8be8173SPaul E. McKenney init_rcu_head_on_stack(&rs.head); 211d8be8173SPaul E. McKenney init_completion(&rs.completion); 212aacb5d91SPaul E. McKenney call_srcu(ssp, &rs.head, wakeme_after_rcu); 213d8be8173SPaul E. McKenney wait_for_completion(&rs.completion); 214d8be8173SPaul E. McKenney destroy_rcu_head_on_stack(&rs.head); 215d8be8173SPaul E. McKenney } 216d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(synchronize_srcu); 217825c5bd2SPaul E. McKenney 2188b5bd67cSPaul E. McKenney /* 2198b5bd67cSPaul E. McKenney * get_state_synchronize_srcu - Provide an end-of-grace-period cookie 2208b5bd67cSPaul E. McKenney */ 2218b5bd67cSPaul E. McKenney unsigned long get_state_synchronize_srcu(struct srcu_struct *ssp) 2228b5bd67cSPaul E. McKenney { 2238b5bd67cSPaul E. McKenney unsigned long ret; 2248b5bd67cSPaul E. McKenney 2258b5bd67cSPaul E. McKenney barrier(); 2268b5bd67cSPaul E. McKenney ret = (READ_ONCE(ssp->srcu_idx) + 3) & ~0x1; 2278b5bd67cSPaul E. McKenney barrier(); 2285fe89191SPaul E. McKenney return ret; 2298b5bd67cSPaul E. McKenney } 2308b5bd67cSPaul E. McKenney EXPORT_SYMBOL_GPL(get_state_synchronize_srcu); 2318b5bd67cSPaul E. McKenney 2328b5bd67cSPaul E. McKenney /* 2338b5bd67cSPaul E. McKenney * start_poll_synchronize_srcu - Provide cookie and start grace period 2348b5bd67cSPaul E. McKenney * 2358b5bd67cSPaul E. McKenney * The difference between this and get_state_synchronize_srcu() is that 2368b5bd67cSPaul E. McKenney * this function ensures that the poll_state_synchronize_srcu() will 2378b5bd67cSPaul E. McKenney * eventually return the value true. 2388b5bd67cSPaul E. McKenney */ 2398b5bd67cSPaul E. McKenney unsigned long start_poll_synchronize_srcu(struct srcu_struct *ssp) 2408b5bd67cSPaul E. McKenney { 2418b5bd67cSPaul E. McKenney unsigned long ret = get_state_synchronize_srcu(ssp); 2428b5bd67cSPaul E. McKenney 2438b5bd67cSPaul E. McKenney srcu_gp_start_if_needed(ssp); 2448b5bd67cSPaul E. McKenney return ret; 2458b5bd67cSPaul E. McKenney } 2468b5bd67cSPaul E. McKenney EXPORT_SYMBOL_GPL(start_poll_synchronize_srcu); 2478b5bd67cSPaul E. McKenney 2488b5bd67cSPaul E. McKenney /* 2498b5bd67cSPaul E. McKenney * poll_state_synchronize_srcu - Has cookie's grace period ended? 2508b5bd67cSPaul E. McKenney */ 2518b5bd67cSPaul E. McKenney bool poll_state_synchronize_srcu(struct srcu_struct *ssp, unsigned long cookie) 2528b5bd67cSPaul E. McKenney { 2535fe89191SPaul E. McKenney unsigned long cur_s = READ_ONCE(ssp->srcu_idx); 2548b5bd67cSPaul E. McKenney 2558b5bd67cSPaul E. McKenney barrier(); 2565fe89191SPaul E. McKenney return ULONG_CMP_GE(cur_s, cookie) || ULONG_CMP_LT(cur_s, cookie - 3); 2578b5bd67cSPaul E. McKenney } 2588b5bd67cSPaul E. McKenney EXPORT_SYMBOL_GPL(poll_state_synchronize_srcu); 2598b5bd67cSPaul E. McKenney 260825c5bd2SPaul E. McKenney /* Lockdep diagnostics. */ 261825c5bd2SPaul E. McKenney void __init rcu_scheduler_starting(void) 262825c5bd2SPaul E. McKenney { 263825c5bd2SPaul E. McKenney rcu_scheduler_active = RCU_SCHEDULER_RUNNING; 264825c5bd2SPaul E. McKenney } 265e0fcba9aSPaul E. McKenney 266e0fcba9aSPaul E. McKenney /* 267e0fcba9aSPaul E. McKenney * Queue work for srcu_struct structures with early boot callbacks. 268e0fcba9aSPaul E. McKenney * The work won't actually execute until the workqueue initialization 269e0fcba9aSPaul E. McKenney * phase that takes place after the scheduler starts. 270e0fcba9aSPaul E. McKenney */ 271e0fcba9aSPaul E. McKenney void __init srcu_init(void) 272e0fcba9aSPaul E. McKenney { 273aacb5d91SPaul E. McKenney struct srcu_struct *ssp; 274e0fcba9aSPaul E. McKenney 275e0fcba9aSPaul E. McKenney srcu_init_done = true; 276e0fcba9aSPaul E. McKenney while (!list_empty(&srcu_boot_list)) { 277aacb5d91SPaul E. McKenney ssp = list_first_entry(&srcu_boot_list, 2784e6ea4efSPaul E. McKenney struct srcu_struct, srcu_work.entry); 279aacb5d91SPaul E. McKenney list_del_init(&ssp->srcu_work.entry); 280aacb5d91SPaul E. McKenney schedule_work(&ssp->srcu_work); 281e0fcba9aSPaul E. McKenney } 282e0fcba9aSPaul E. McKenney } 283