Line data Source code
1 : // SPDX-License-Identifier: GPL-2.0+ 2 : /* 3 : * RCU-based infrastructure for lightweight reader-writer locking 4 : * 5 : * Copyright (c) 2015, Red Hat, Inc. 6 : * 7 : * Author: Oleg Nesterov <oleg@redhat.com> 8 : */ 9 : 10 : #include <linux/rcu_sync.h> 11 : #include <linux/sched.h> 12 : 13 : enum { GP_IDLE = 0, GP_ENTER, GP_PASSED, GP_EXIT, GP_REPLAY }; 14 : 15 : #define rss_lock gp_wait.lock 16 : 17 : /** 18 : * rcu_sync_init() - Initialize an rcu_sync structure 19 : * @rsp: Pointer to rcu_sync structure to be initialized 20 : */ 21 370 : void rcu_sync_init(struct rcu_sync *rsp) 22 : { 23 370 : memset(rsp, 0, sizeof(*rsp)); 24 370 : init_waitqueue_head(&rsp->gp_wait); 25 370 : } 26 : 27 : /** 28 : * rcu_sync_enter_start - Force readers onto slow path for multiple updates 29 : * @rsp: Pointer to rcu_sync structure to use for synchronization 30 : * 31 : * Must be called after rcu_sync_init() and before first use. 32 : * 33 : * Ensures rcu_sync_is_idle() returns false and rcu_sync_{enter,exit}() 34 : * pairs turn into NO-OPs. 35 : */ 36 1 : void rcu_sync_enter_start(struct rcu_sync *rsp) 37 : { 38 1 : rsp->gp_count++; 39 1 : rsp->gp_state = GP_PASSED; 40 1 : } 41 : 42 : 43 : static void rcu_sync_func(struct rcu_head *rhp); 44 : 45 2 : static void rcu_sync_call(struct rcu_sync *rsp) 46 : { 47 2 : call_rcu(&rsp->cb_head, rcu_sync_func); 48 2 : } 49 : 50 : /** 51 : * rcu_sync_func() - Callback function managing reader access to fastpath 52 : * @rhp: Pointer to rcu_head in rcu_sync structure to use for synchronization 53 : * 54 : * This function is passed to call_rcu() function by rcu_sync_enter() and 55 : * rcu_sync_exit(), so that it is invoked after a grace period following the 56 : * that invocation of enter/exit. 57 : * 58 : * If it is called by rcu_sync_enter() it signals that all the readers were 59 : * switched onto slow path. 60 : * 61 : * If it is called by rcu_sync_exit() it takes action based on events that 62 : * have taken place in the meantime, so that closely spaced rcu_sync_enter() 63 : * and rcu_sync_exit() pairs need not wait for a grace period. 64 : * 65 : * If another rcu_sync_enter() is invoked before the grace period 66 : * ended, reset state to allow the next rcu_sync_exit() to let the 67 : * readers back onto their fastpaths (after a grace period). If both 68 : * another rcu_sync_enter() and its matching rcu_sync_exit() are invoked 69 : * before the grace period ended, re-invoke call_rcu() on behalf of that 70 : * rcu_sync_exit(). Otherwise, set all state back to idle so that readers 71 : * can again use their fastpaths. 72 : */ 73 3 : static void rcu_sync_func(struct rcu_head *rhp) 74 : { 75 3 : struct rcu_sync *rsp = container_of(rhp, struct rcu_sync, cb_head); 76 3 : unsigned long flags; 77 : 78 3 : WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_IDLE); 79 3 : WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_PASSED); 80 : 81 3 : spin_lock_irqsave(&rsp->rss_lock, flags); 82 3 : if (rsp->gp_count) { 83 : /* 84 : * We're at least a GP after the GP_IDLE->GP_ENTER transition. 85 : */ 86 1 : WRITE_ONCE(rsp->gp_state, GP_PASSED); 87 1 : wake_up_locked(&rsp->gp_wait); 88 2 : } else if (rsp->gp_state == GP_REPLAY) { 89 : /* 90 : * A new rcu_sync_exit() has happened; requeue the callback to 91 : * catch a later GP. 92 : */ 93 1 : WRITE_ONCE(rsp->gp_state, GP_EXIT); 94 1 : rcu_sync_call(rsp); 95 : } else { 96 : /* 97 : * We're at least a GP after the last rcu_sync_exit(); eveybody 98 : * will now have observed the write side critical section. 99 : * Let 'em rip!. 100 : */ 101 1 : WRITE_ONCE(rsp->gp_state, GP_IDLE); 102 : } 103 3 : spin_unlock_irqrestore(&rsp->rss_lock, flags); 104 3 : } 105 : 106 : /** 107 : * rcu_sync_enter() - Force readers onto slowpath 108 : * @rsp: Pointer to rcu_sync structure to use for synchronization 109 : * 110 : * This function is used by updaters who need readers to make use of 111 : * a slowpath during the update. After this function returns, all 112 : * subsequent calls to rcu_sync_is_idle() will return false, which 113 : * tells readers to stay off their fastpaths. A later call to 114 : * rcu_sync_exit() re-enables reader slowpaths. 115 : * 116 : * When called in isolation, rcu_sync_enter() must wait for a grace 117 : * period, however, closely spaced calls to rcu_sync_enter() can 118 : * optimize away the grace-period wait via a state machine implemented 119 : * by rcu_sync_enter(), rcu_sync_exit(), and rcu_sync_func(). 120 : */ 121 201 : void rcu_sync_enter(struct rcu_sync *rsp) 122 : { 123 201 : int gp_state; 124 : 125 201 : spin_lock_irq(&rsp->rss_lock); 126 201 : gp_state = rsp->gp_state; 127 201 : if (gp_state == GP_IDLE) { 128 1 : WRITE_ONCE(rsp->gp_state, GP_ENTER); 129 1 : WARN_ON_ONCE(rsp->gp_count); 130 : /* 131 : * Note that we could simply do rcu_sync_call(rsp) here and 132 : * avoid the "if (gp_state == GP_IDLE)" block below. 133 : * 134 : * However, synchronize_rcu() can be faster if rcu_expedited 135 : * or rcu_blocking_is_gp() is true. 136 : * 137 : * Another reason is that we can't wait for rcu callback if 138 : * we are called at early boot time but this shouldn't happen. 139 : */ 140 : } 141 201 : rsp->gp_count++; 142 201 : spin_unlock_irq(&rsp->rss_lock); 143 : 144 201 : if (gp_state == GP_IDLE) { 145 : /* 146 : * See the comment above, this simply does the "synchronous" 147 : * call_rcu(rcu_sync_func) which does GP_ENTER -> GP_PASSED. 148 : */ 149 1 : synchronize_rcu(); 150 1 : rcu_sync_func(&rsp->cb_head); 151 : /* Not really needed, wait_event() would see GP_PASSED. */ 152 1 : return; 153 : } 154 : 155 200 : wait_event(rsp->gp_wait, READ_ONCE(rsp->gp_state) >= GP_PASSED); 156 : } 157 : 158 : /** 159 : * rcu_sync_exit() - Allow readers back onto fast path after grace period 160 : * @rsp: Pointer to rcu_sync structure to use for synchronization 161 : * 162 : * This function is used by updaters who have completed, and can therefore 163 : * now allow readers to make use of their fastpaths after a grace period 164 : * has elapsed. After this grace period has completed, all subsequent 165 : * calls to rcu_sync_is_idle() will return true, which tells readers that 166 : * they can once again use their fastpaths. 167 : */ 168 201 : void rcu_sync_exit(struct rcu_sync *rsp) 169 : { 170 201 : WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_IDLE); 171 201 : WARN_ON_ONCE(READ_ONCE(rsp->gp_count) == 0); 172 : 173 201 : spin_lock_irq(&rsp->rss_lock); 174 201 : if (!--rsp->gp_count) { 175 3 : if (rsp->gp_state == GP_PASSED) { 176 1 : WRITE_ONCE(rsp->gp_state, GP_EXIT); 177 1 : rcu_sync_call(rsp); 178 2 : } else if (rsp->gp_state == GP_EXIT) { 179 1 : WRITE_ONCE(rsp->gp_state, GP_REPLAY); 180 : } 181 : } 182 201 : spin_unlock_irq(&rsp->rss_lock); 183 201 : } 184 : 185 : /** 186 : * rcu_sync_dtor() - Clean up an rcu_sync structure 187 : * @rsp: Pointer to rcu_sync structure to be cleaned up 188 : */ 189 297 : void rcu_sync_dtor(struct rcu_sync *rsp) 190 : { 191 297 : int gp_state; 192 : 193 297 : WARN_ON_ONCE(READ_ONCE(rsp->gp_count)); 194 297 : WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_PASSED); 195 : 196 297 : spin_lock_irq(&rsp->rss_lock); 197 297 : if (rsp->gp_state == GP_REPLAY) 198 0 : WRITE_ONCE(rsp->gp_state, GP_EXIT); 199 297 : gp_state = rsp->gp_state; 200 297 : spin_unlock_irq(&rsp->rss_lock); 201 : 202 297 : if (gp_state != GP_IDLE) { 203 0 : rcu_barrier(); 204 0 : WARN_ON_ONCE(rsp->gp_state != GP_IDLE); 205 : } 206 297 : }