1*d8be8173SPaul E. McKenney /* 2*d8be8173SPaul E. McKenney * Sleepable Read-Copy Update mechanism for mutual exclusion, 3*d8be8173SPaul E. McKenney * tiny version for non-preemptible single-CPU use. 4*d8be8173SPaul E. McKenney * 5*d8be8173SPaul E. McKenney * This program is free software; you can redistribute it and/or modify 6*d8be8173SPaul E. McKenney * it under the terms of the GNU General Public License as published by 7*d8be8173SPaul E. McKenney * the Free Software Foundation; either version 2 of the License, or 8*d8be8173SPaul E. McKenney * (at your option) any later version. 9*d8be8173SPaul E. McKenney * 10*d8be8173SPaul E. McKenney * This program is distributed in the hope that it will be useful, 11*d8be8173SPaul E. McKenney * but WITHOUT ANY WARRANTY; without even the implied warranty of 12*d8be8173SPaul E. McKenney * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13*d8be8173SPaul E. McKenney * GNU General Public License for more details. 14*d8be8173SPaul E. McKenney * 15*d8be8173SPaul E. McKenney * You should have received a copy of the GNU General Public License 16*d8be8173SPaul E. McKenney * along with this program; if not, you can access it online at 17*d8be8173SPaul E. McKenney * http://www.gnu.org/licenses/gpl-2.0.html. 18*d8be8173SPaul E. McKenney * 19*d8be8173SPaul E. McKenney * Copyright (C) IBM Corporation, 2017 20*d8be8173SPaul E. McKenney * 21*d8be8173SPaul E. McKenney * Author: Paul McKenney <[email protected]> 22*d8be8173SPaul E. McKenney */ 23*d8be8173SPaul E. McKenney 24*d8be8173SPaul E. McKenney #include <linux/export.h> 25*d8be8173SPaul E. McKenney #include <linux/mutex.h> 26*d8be8173SPaul E. McKenney #include <linux/preempt.h> 27*d8be8173SPaul E. McKenney #include <linux/rcupdate_wait.h> 28*d8be8173SPaul E. McKenney #include <linux/sched.h> 29*d8be8173SPaul E. McKenney #include <linux/delay.h> 30*d8be8173SPaul E. McKenney #include <linux/srcu.h> 31*d8be8173SPaul E. McKenney 32*d8be8173SPaul E. McKenney #include <linux/rcu_node_tree.h> 33*d8be8173SPaul E. McKenney #include "rcu.h" 34*d8be8173SPaul E. McKenney 35*d8be8173SPaul E. McKenney static int init_srcu_struct_fields(struct srcu_struct *sp) 36*d8be8173SPaul E. McKenney { 37*d8be8173SPaul E. McKenney sp->srcu_lock_nesting[0] = 0; 38*d8be8173SPaul E. McKenney sp->srcu_lock_nesting[1] = 0; 39*d8be8173SPaul E. McKenney init_swait_queue_head(&sp->srcu_wq); 40*d8be8173SPaul E. McKenney sp->srcu_gp_seq = 0; 41*d8be8173SPaul E. McKenney rcu_segcblist_init(&sp->srcu_cblist); 42*d8be8173SPaul E. McKenney sp->srcu_gp_running = false; 43*d8be8173SPaul E. McKenney sp->srcu_gp_waiting = false; 44*d8be8173SPaul E. McKenney sp->srcu_idx = 0; 45*d8be8173SPaul E. McKenney INIT_WORK(&sp->srcu_work, srcu_drive_gp); 46*d8be8173SPaul E. McKenney return 0; 47*d8be8173SPaul E. McKenney } 48*d8be8173SPaul E. McKenney 49*d8be8173SPaul E. McKenney #ifdef CONFIG_DEBUG_LOCK_ALLOC 50*d8be8173SPaul E. McKenney 51*d8be8173SPaul E. McKenney int __init_srcu_struct(struct srcu_struct *sp, const char *name, 52*d8be8173SPaul E. McKenney struct lock_class_key *key) 53*d8be8173SPaul E. McKenney { 54*d8be8173SPaul E. McKenney /* Don't re-initialize a lock while it is held. */ 55*d8be8173SPaul E. McKenney debug_check_no_locks_freed((void *)sp, sizeof(*sp)); 56*d8be8173SPaul E. McKenney lockdep_init_map(&sp->dep_map, name, key, 0); 57*d8be8173SPaul E. McKenney return init_srcu_struct_fields(sp); 58*d8be8173SPaul E. McKenney } 59*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__init_srcu_struct); 60*d8be8173SPaul E. McKenney 61*d8be8173SPaul E. McKenney #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 62*d8be8173SPaul E. McKenney 63*d8be8173SPaul E. McKenney /* 64*d8be8173SPaul E. McKenney * init_srcu_struct - initialize a sleep-RCU structure 65*d8be8173SPaul E. McKenney * @sp: structure to initialize. 66*d8be8173SPaul E. McKenney * 67*d8be8173SPaul E. McKenney * Must invoke this on a given srcu_struct before passing that srcu_struct 68*d8be8173SPaul E. McKenney * to any other function. Each srcu_struct represents a separate domain 69*d8be8173SPaul E. McKenney * of SRCU protection. 70*d8be8173SPaul E. McKenney */ 71*d8be8173SPaul E. McKenney int init_srcu_struct(struct srcu_struct *sp) 72*d8be8173SPaul E. McKenney { 73*d8be8173SPaul E. McKenney return init_srcu_struct_fields(sp); 74*d8be8173SPaul E. McKenney } 75*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(init_srcu_struct); 76*d8be8173SPaul E. McKenney 77*d8be8173SPaul E. McKenney #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ 78*d8be8173SPaul E. McKenney 79*d8be8173SPaul E. McKenney /* 80*d8be8173SPaul E. McKenney * cleanup_srcu_struct - deconstruct a sleep-RCU structure 81*d8be8173SPaul E. McKenney * @sp: structure to clean up. 82*d8be8173SPaul E. McKenney * 83*d8be8173SPaul E. McKenney * Must invoke this after you are finished using a given srcu_struct that 84*d8be8173SPaul E. McKenney * was initialized via init_srcu_struct(), else you leak memory. 85*d8be8173SPaul E. McKenney */ 86*d8be8173SPaul E. McKenney void cleanup_srcu_struct(struct srcu_struct *sp) 87*d8be8173SPaul E. McKenney { 88*d8be8173SPaul E. McKenney WARN_ON(sp->srcu_lock_nesting[0] || sp->srcu_lock_nesting[1]); 89*d8be8173SPaul E. McKenney flush_work(&sp->srcu_work); 90*d8be8173SPaul E. McKenney WARN_ON(rcu_seq_state(sp->srcu_gp_seq)); 91*d8be8173SPaul E. McKenney WARN_ON(sp->srcu_gp_running); 92*d8be8173SPaul E. McKenney WARN_ON(sp->srcu_gp_waiting); 93*d8be8173SPaul E. McKenney WARN_ON(!rcu_segcblist_empty(&sp->srcu_cblist)); 94*d8be8173SPaul E. McKenney } 95*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(cleanup_srcu_struct); 96*d8be8173SPaul E. McKenney 97*d8be8173SPaul E. McKenney /* 98*d8be8173SPaul E. McKenney * Counts the new reader in the appropriate per-CPU element of the 99*d8be8173SPaul E. McKenney * srcu_struct. Must be called from process context. 100*d8be8173SPaul E. McKenney * Returns an index that must be passed to the matching srcu_read_unlock(). 101*d8be8173SPaul E. McKenney */ 102*d8be8173SPaul E. McKenney int __srcu_read_lock(struct srcu_struct *sp) 103*d8be8173SPaul E. McKenney { 104*d8be8173SPaul E. McKenney int idx; 105*d8be8173SPaul E. McKenney 106*d8be8173SPaul E. McKenney idx = READ_ONCE(sp->srcu_idx); 107*d8be8173SPaul E. McKenney WRITE_ONCE(sp->srcu_lock_nesting[idx], sp->srcu_lock_nesting[idx] + 1); 108*d8be8173SPaul E. McKenney return idx; 109*d8be8173SPaul E. McKenney } 110*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__srcu_read_lock); 111*d8be8173SPaul E. McKenney 112*d8be8173SPaul E. McKenney /* 113*d8be8173SPaul E. McKenney * Removes the count for the old reader from the appropriate element of 114*d8be8173SPaul E. McKenney * the srcu_struct. Must be called from process context. 115*d8be8173SPaul E. McKenney */ 116*d8be8173SPaul E. McKenney void __srcu_read_unlock(struct srcu_struct *sp, int idx) 117*d8be8173SPaul E. McKenney { 118*d8be8173SPaul E. McKenney int newval = sp->srcu_lock_nesting[idx] - 1; 119*d8be8173SPaul E. McKenney 120*d8be8173SPaul E. McKenney WRITE_ONCE(sp->srcu_lock_nesting[idx], newval); 121*d8be8173SPaul E. McKenney if (!newval && READ_ONCE(sp->srcu_gp_waiting)) 122*d8be8173SPaul E. McKenney swake_up(&sp->srcu_wq); 123*d8be8173SPaul E. McKenney } 124*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(__srcu_read_unlock); 125*d8be8173SPaul E. McKenney 126*d8be8173SPaul E. McKenney /* 127*d8be8173SPaul E. McKenney * Workqueue handler to drive one grace period and invoke any callbacks 128*d8be8173SPaul E. McKenney * that become ready as a result. Single-CPU and !PREEMPT operation 129*d8be8173SPaul E. McKenney * means that we get away with murder on synchronization. ;-) 130*d8be8173SPaul E. McKenney */ 131*d8be8173SPaul E. McKenney void srcu_drive_gp(struct work_struct *wp) 132*d8be8173SPaul E. McKenney { 133*d8be8173SPaul E. McKenney int idx; 134*d8be8173SPaul E. McKenney struct rcu_cblist ready_cbs; 135*d8be8173SPaul E. McKenney struct srcu_struct *sp; 136*d8be8173SPaul E. McKenney struct rcu_head *rhp; 137*d8be8173SPaul E. McKenney 138*d8be8173SPaul E. McKenney sp = container_of(wp, struct srcu_struct, srcu_work); 139*d8be8173SPaul E. McKenney if (sp->srcu_gp_running || rcu_segcblist_empty(&sp->srcu_cblist)) 140*d8be8173SPaul E. McKenney return; /* Already running or nothing to do. */ 141*d8be8173SPaul E. McKenney 142*d8be8173SPaul E. McKenney /* Tag recently arrived callbacks and wait for readers. */ 143*d8be8173SPaul E. McKenney WRITE_ONCE(sp->srcu_gp_running, true); 144*d8be8173SPaul E. McKenney rcu_segcblist_accelerate(&sp->srcu_cblist, 145*d8be8173SPaul E. McKenney rcu_seq_snap(&sp->srcu_gp_seq)); 146*d8be8173SPaul E. McKenney rcu_seq_start(&sp->srcu_gp_seq); 147*d8be8173SPaul E. McKenney idx = sp->srcu_idx; 148*d8be8173SPaul E. McKenney WRITE_ONCE(sp->srcu_idx, !sp->srcu_idx); 149*d8be8173SPaul E. McKenney WRITE_ONCE(sp->srcu_gp_waiting, true); /* srcu_read_unlock() wakes! */ 150*d8be8173SPaul E. McKenney swait_event(sp->srcu_wq, !READ_ONCE(sp->srcu_lock_nesting[idx])); 151*d8be8173SPaul E. McKenney WRITE_ONCE(sp->srcu_gp_waiting, false); /* srcu_read_unlock() cheap. */ 152*d8be8173SPaul E. McKenney rcu_seq_end(&sp->srcu_gp_seq); 153*d8be8173SPaul E. McKenney 154*d8be8173SPaul E. McKenney /* Update callback list based on GP, and invoke ready callbacks. */ 155*d8be8173SPaul E. McKenney rcu_segcblist_advance(&sp->srcu_cblist, 156*d8be8173SPaul E. McKenney rcu_seq_current(&sp->srcu_gp_seq)); 157*d8be8173SPaul E. McKenney if (rcu_segcblist_ready_cbs(&sp->srcu_cblist)) { 158*d8be8173SPaul E. McKenney rcu_cblist_init(&ready_cbs); 159*d8be8173SPaul E. McKenney local_irq_disable(); 160*d8be8173SPaul E. McKenney rcu_segcblist_extract_done_cbs(&sp->srcu_cblist, &ready_cbs); 161*d8be8173SPaul E. McKenney local_irq_enable(); 162*d8be8173SPaul E. McKenney rhp = rcu_cblist_dequeue(&ready_cbs); 163*d8be8173SPaul E. McKenney for (; rhp != NULL; rhp = rcu_cblist_dequeue(&ready_cbs)) { 164*d8be8173SPaul E. McKenney local_bh_disable(); 165*d8be8173SPaul E. McKenney rhp->func(rhp); 166*d8be8173SPaul E. McKenney local_bh_enable(); 167*d8be8173SPaul E. McKenney } 168*d8be8173SPaul E. McKenney local_irq_disable(); 169*d8be8173SPaul E. McKenney rcu_segcblist_insert_count(&sp->srcu_cblist, &ready_cbs); 170*d8be8173SPaul E. McKenney local_irq_enable(); 171*d8be8173SPaul E. McKenney } 172*d8be8173SPaul E. McKenney WRITE_ONCE(sp->srcu_gp_running, false); 173*d8be8173SPaul E. McKenney 174*d8be8173SPaul E. McKenney /* 175*d8be8173SPaul E. McKenney * If more callbacks, reschedule ourselves. This can race with 176*d8be8173SPaul E. McKenney * a call_srcu() at interrupt level, but the ->srcu_gp_running 177*d8be8173SPaul E. McKenney * checks will straighten that out. 178*d8be8173SPaul E. McKenney */ 179*d8be8173SPaul E. McKenney if (!rcu_segcblist_empty(&sp->srcu_cblist)) 180*d8be8173SPaul E. McKenney schedule_work(&sp->srcu_work); 181*d8be8173SPaul E. McKenney } 182*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(srcu_drive_gp); 183*d8be8173SPaul E. McKenney 184*d8be8173SPaul E. McKenney /* 185*d8be8173SPaul E. McKenney * Enqueue an SRCU callback on the specified srcu_struct structure, 186*d8be8173SPaul E. McKenney * initiating grace-period processing if it is not already running. 187*d8be8173SPaul E. McKenney */ 188*d8be8173SPaul E. McKenney void call_srcu(struct srcu_struct *sp, struct rcu_head *head, 189*d8be8173SPaul E. McKenney rcu_callback_t func) 190*d8be8173SPaul E. McKenney { 191*d8be8173SPaul E. McKenney unsigned long flags; 192*d8be8173SPaul E. McKenney 193*d8be8173SPaul E. McKenney head->func = func; 194*d8be8173SPaul E. McKenney local_irq_save(flags); 195*d8be8173SPaul E. McKenney rcu_segcblist_enqueue(&sp->srcu_cblist, head, false); 196*d8be8173SPaul E. McKenney local_irq_restore(flags); 197*d8be8173SPaul E. McKenney if (!READ_ONCE(sp->srcu_gp_running)) 198*d8be8173SPaul E. McKenney schedule_work(&sp->srcu_work); 199*d8be8173SPaul E. McKenney } 200*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(call_srcu); 201*d8be8173SPaul E. McKenney 202*d8be8173SPaul E. McKenney /* 203*d8be8173SPaul E. McKenney * synchronize_srcu - wait for prior SRCU read-side critical-section completion 204*d8be8173SPaul E. McKenney */ 205*d8be8173SPaul E. McKenney void synchronize_srcu(struct srcu_struct *sp) 206*d8be8173SPaul E. McKenney { 207*d8be8173SPaul E. McKenney struct rcu_synchronize rs; 208*d8be8173SPaul E. McKenney 209*d8be8173SPaul E. McKenney init_rcu_head_on_stack(&rs.head); 210*d8be8173SPaul E. McKenney init_completion(&rs.completion); 211*d8be8173SPaul E. McKenney call_srcu(sp, &rs.head, wakeme_after_rcu); 212*d8be8173SPaul E. McKenney wait_for_completion(&rs.completion); 213*d8be8173SPaul E. McKenney destroy_rcu_head_on_stack(&rs.head); 214*d8be8173SPaul E. McKenney } 215*d8be8173SPaul E. McKenney EXPORT_SYMBOL_GPL(synchronize_srcu); 216