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; 37aacb5d91SPaul E. McKenney INIT_WORK(&ssp->srcu_work, srcu_drive_gp); 38aacb5d91SPaul E. McKenney INIT_LIST_HEAD(&ssp->srcu_work.entry); 39d8be8173SPaul E. McKenney return 0; 40d8be8173SPaul E. McKenney } 41d8be8173SPaul E. McKenney 42d8be8173SPaul E. McKenney #ifdef CONFIG_DEBUG_LOCK_ALLOC 43d8be8173SPaul E. McKenney 44aacb5d91SPaul E. McKenney int __init_srcu_struct(struct srcu_struct *ssp, const char *name, 45d8be8173SPaul E. McKenney struct lock_class_key *key) 46d8be8173SPaul E. McKenney { 47d8be8173SPaul E. McKenney /* Don't re-initialize a lock while it is held. */ 48aacb5d91SPaul E. McKenney debug_check_no_locks_freed((void *)ssp, sizeof(*ssp)); 49aacb5d91SPaul E. McKenney lockdep_init_map(&ssp->dep_map, name, key, 0); 50aacb5d91SPaul E. McKenney return init_srcu_struct_fields(ssp); 51d8be8173SPaul E. McKenney } 52d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__init_srcu_struct); 53d8be8173SPaul E. McKenney 54d8be8173SPaul E. McKenney #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 55d8be8173SPaul E. McKenney 56d8be8173SPaul E. McKenney /* 57d8be8173SPaul E. McKenney * init_srcu_struct - initialize a sleep-RCU structure 58aacb5d91SPaul E. McKenney * @ssp: structure to initialize. 59d8be8173SPaul E. McKenney * 60d8be8173SPaul E. McKenney * Must invoke this on a given srcu_struct before passing that srcu_struct 61d8be8173SPaul E. McKenney * to any other function. Each srcu_struct represents a separate domain 62d8be8173SPaul E. McKenney * of SRCU protection. 63d8be8173SPaul E. McKenney */ 64aacb5d91SPaul E. McKenney int init_srcu_struct(struct srcu_struct *ssp) 65d8be8173SPaul E. McKenney { 66aacb5d91SPaul E. McKenney return init_srcu_struct_fields(ssp); 67d8be8173SPaul E. McKenney } 68d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(init_srcu_struct); 69d8be8173SPaul E. McKenney 70d8be8173SPaul E. McKenney #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 71d8be8173SPaul E. McKenney 72d8be8173SPaul E. McKenney /* 73d8be8173SPaul E. McKenney * cleanup_srcu_struct - deconstruct a sleep-RCU structure 74aacb5d91SPaul E. McKenney * @ssp: structure to clean up. 75d8be8173SPaul E. McKenney * 76d8be8173SPaul E. McKenney * Must invoke this after you are finished using a given srcu_struct that 77d8be8173SPaul E. McKenney * was initialized via init_srcu_struct(), else you leak memory. 78d8be8173SPaul E. McKenney */ 79f5ad3991SPaul E. McKenney void cleanup_srcu_struct(struct srcu_struct *ssp) 80d8be8173SPaul E. McKenney { 81aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_lock_nesting[0] || ssp->srcu_lock_nesting[1]); 82aacb5d91SPaul E. McKenney flush_work(&ssp->srcu_work); 83aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_gp_running); 84aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_gp_waiting); 85aacb5d91SPaul E. McKenney WARN_ON(ssp->srcu_cb_head); 86aacb5d91SPaul E. McKenney WARN_ON(&ssp->srcu_cb_head != ssp->srcu_cb_tail); 87d8be8173SPaul E. McKenney } 88f5ad3991SPaul E. McKenney EXPORT_SYMBOL_GPL(cleanup_srcu_struct); 89d8be8173SPaul E. McKenney 90d8be8173SPaul E. McKenney /* 91d8be8173SPaul E. McKenney * Removes the count for the old reader from the appropriate element of 92cdf7abc4SPaolo Bonzini * the srcu_struct. 93d8be8173SPaul E. McKenney */ 94aacb5d91SPaul E. McKenney void __srcu_read_unlock(struct srcu_struct *ssp, int idx) 95d8be8173SPaul E. McKenney { 96aacb5d91SPaul E. McKenney int newval = ssp->srcu_lock_nesting[idx] - 1; 97d8be8173SPaul E. McKenney 98aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_lock_nesting[idx], newval); 99aacb5d91SPaul E. McKenney if (!newval && READ_ONCE(ssp->srcu_gp_waiting)) 100aacb5d91SPaul E. McKenney swake_up_one(&ssp->srcu_wq); 101d8be8173SPaul E. McKenney } 102d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__srcu_read_unlock); 103d8be8173SPaul E. McKenney 104d8be8173SPaul E. McKenney /* 105d8be8173SPaul E. McKenney * Workqueue handler to drive one grace period and invoke any callbacks 10690326f05SSebastian Andrzej Siewior * that become ready as a result. Single-CPU and !PREEMPTION operation 107d8be8173SPaul E. McKenney * means that we get away with murder on synchronization. ;-) 108d8be8173SPaul E. McKenney */ 109d8be8173SPaul E. McKenney void srcu_drive_gp(struct work_struct *wp) 110d8be8173SPaul E. McKenney { 111d8be8173SPaul E. McKenney int idx; 1122464dd94SPaul E. McKenney struct rcu_head *lh; 113d8be8173SPaul E. McKenney struct rcu_head *rhp; 114aacb5d91SPaul E. McKenney struct srcu_struct *ssp; 115d8be8173SPaul E. McKenney 116aacb5d91SPaul E. McKenney ssp = container_of(wp, struct srcu_struct, srcu_work); 117aacb5d91SPaul E. McKenney if (ssp->srcu_gp_running || !READ_ONCE(ssp->srcu_cb_head)) 118d8be8173SPaul E. McKenney return; /* Already running or nothing to do. */ 119d8be8173SPaul E. McKenney 1202464dd94SPaul E. McKenney /* Remove recently arrived callbacks and wait for readers. */ 121aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_running, true); 1222464dd94SPaul E. McKenney local_irq_disable(); 123aacb5d91SPaul E. McKenney lh = ssp->srcu_cb_head; 124aacb5d91SPaul E. McKenney ssp->srcu_cb_head = NULL; 125aacb5d91SPaul E. McKenney ssp->srcu_cb_tail = &ssp->srcu_cb_head; 1262464dd94SPaul E. McKenney local_irq_enable(); 12774612a07SPaul E. McKenney idx = (ssp->srcu_idx & 0x2) / 2; 12874612a07SPaul E. McKenney WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); 129aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_waiting, true); /* srcu_read_unlock() wakes! */ 130aacb5d91SPaul E. McKenney swait_event_exclusive(ssp->srcu_wq, !READ_ONCE(ssp->srcu_lock_nesting[idx])); 131aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_waiting, false); /* srcu_read_unlock() cheap. */ 13274612a07SPaul E. McKenney WRITE_ONCE(ssp->srcu_idx, ssp->srcu_idx + 1); 133d8be8173SPaul E. McKenney 1342464dd94SPaul E. McKenney /* Invoke the callbacks we removed above. */ 1352464dd94SPaul E. McKenney while (lh) { 1362464dd94SPaul E. McKenney rhp = lh; 1372464dd94SPaul E. McKenney lh = lh->next; 138d8be8173SPaul E. McKenney local_bh_disable(); 139d8be8173SPaul E. McKenney rhp->func(rhp); 140d8be8173SPaul E. McKenney local_bh_enable(); 141d8be8173SPaul E. McKenney } 142d8be8173SPaul E. McKenney 143d8be8173SPaul E. McKenney /* 1442464dd94SPaul E. McKenney * Enable rescheduling, and if there are more callbacks, 1452464dd94SPaul E. McKenney * reschedule ourselves. This can race with a call_srcu() 1462464dd94SPaul E. McKenney * at interrupt level, but the ->srcu_gp_running checks will 1472464dd94SPaul E. McKenney * straighten that out. 148d8be8173SPaul E. McKenney */ 149aacb5d91SPaul E. McKenney WRITE_ONCE(ssp->srcu_gp_running, false); 150aacb5d91SPaul E. McKenney if (READ_ONCE(ssp->srcu_cb_head)) 151aacb5d91SPaul E. McKenney schedule_work(&ssp->srcu_work); 152d8be8173SPaul E. McKenney } 153d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(srcu_drive_gp); 154d8be8173SPaul E. McKenney 155*1a893c71SPaul E. McKenney static void srcu_gp_start_if_needed(struct srcu_struct *ssp) 156*1a893c71SPaul E. McKenney { 157*1a893c71SPaul E. McKenney if (!READ_ONCE(ssp->srcu_gp_running)) { 158*1a893c71SPaul E. McKenney if (likely(srcu_init_done)) 159*1a893c71SPaul E. McKenney schedule_work(&ssp->srcu_work); 160*1a893c71SPaul E. McKenney else if (list_empty(&ssp->srcu_work.entry)) 161*1a893c71SPaul E. McKenney list_add(&ssp->srcu_work.entry, &srcu_boot_list); 162*1a893c71SPaul E. McKenney } 163*1a893c71SPaul E. McKenney } 164*1a893c71SPaul E. McKenney 165d8be8173SPaul E. McKenney /* 166d8be8173SPaul E. McKenney * Enqueue an SRCU callback on the specified srcu_struct structure, 167d8be8173SPaul E. McKenney * initiating grace-period processing if it is not already running. 168d8be8173SPaul E. McKenney */ 169aacb5d91SPaul E. McKenney void call_srcu(struct srcu_struct *ssp, struct rcu_head *rhp, 170d8be8173SPaul E. McKenney rcu_callback_t func) 171d8be8173SPaul E. McKenney { 172d8be8173SPaul E. McKenney unsigned long flags; 173d8be8173SPaul E. McKenney 1742464dd94SPaul E. McKenney rhp->func = func; 1752464dd94SPaul E. McKenney rhp->next = NULL; 176d8be8173SPaul E. McKenney local_irq_save(flags); 177aacb5d91SPaul E. McKenney *ssp->srcu_cb_tail = rhp; 178aacb5d91SPaul E. McKenney ssp->srcu_cb_tail = &rhp->next; 179d8be8173SPaul E. McKenney local_irq_restore(flags); 180*1a893c71SPaul E. McKenney srcu_gp_start_if_needed(ssp); 181d8be8173SPaul E. McKenney } 182d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(call_srcu); 183d8be8173SPaul E. McKenney 184d8be8173SPaul E. McKenney /* 185d8be8173SPaul E. McKenney * synchronize_srcu - wait for prior SRCU read-side critical-section completion 186d8be8173SPaul E. McKenney */ 187aacb5d91SPaul E. McKenney void synchronize_srcu(struct srcu_struct *ssp) 188d8be8173SPaul E. McKenney { 189d8be8173SPaul E. McKenney struct rcu_synchronize rs; 190d8be8173SPaul E. McKenney 191d8be8173SPaul E. McKenney init_rcu_head_on_stack(&rs.head); 192d8be8173SPaul E. McKenney init_completion(&rs.completion); 193aacb5d91SPaul E. McKenney call_srcu(ssp, &rs.head, wakeme_after_rcu); 194d8be8173SPaul E. McKenney wait_for_completion(&rs.completion); 195d8be8173SPaul E. McKenney destroy_rcu_head_on_stack(&rs.head); 196d8be8173SPaul E. McKenney } 197d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(synchronize_srcu); 198825c5bd2SPaul E. McKenney 199825c5bd2SPaul E. McKenney /* Lockdep diagnostics. */ 200825c5bd2SPaul E. McKenney void __init rcu_scheduler_starting(void) 201825c5bd2SPaul E. McKenney { 202825c5bd2SPaul E. McKenney rcu_scheduler_active = RCU_SCHEDULER_RUNNING; 203825c5bd2SPaul E. McKenney } 204e0fcba9aSPaul E. McKenney 205e0fcba9aSPaul E. McKenney /* 206e0fcba9aSPaul E. McKenney * Queue work for srcu_struct structures with early boot callbacks. 207e0fcba9aSPaul E. McKenney * The work won't actually execute until the workqueue initialization 208e0fcba9aSPaul E. McKenney * phase that takes place after the scheduler starts. 209e0fcba9aSPaul E. McKenney */ 210e0fcba9aSPaul E. McKenney void __init srcu_init(void) 211e0fcba9aSPaul E. McKenney { 212aacb5d91SPaul E. McKenney struct srcu_struct *ssp; 213e0fcba9aSPaul E. McKenney 214e0fcba9aSPaul E. McKenney srcu_init_done = true; 215e0fcba9aSPaul E. McKenney while (!list_empty(&srcu_boot_list)) { 216aacb5d91SPaul E. McKenney ssp = list_first_entry(&srcu_boot_list, 2174e6ea4efSPaul E. McKenney struct srcu_struct, srcu_work.entry); 218aacb5d91SPaul E. McKenney list_del_init(&ssp->srcu_work.entry); 219aacb5d91SPaul E. McKenney schedule_work(&ssp->srcu_work); 220e0fcba9aSPaul E. McKenney } 221e0fcba9aSPaul E. McKenney } 222