196b903f5SPaul E. McKenney // SPDX-License-Identifier: GPL-2.0+
2cc44ca84SOleg Nesterov /*
3cc44ca84SOleg Nesterov * RCU-based infrastructure for lightweight reader-writer locking
4cc44ca84SOleg Nesterov *
5cc44ca84SOleg Nesterov * Copyright (c) 2015, Red Hat, Inc.
6cc44ca84SOleg Nesterov *
7cc44ca84SOleg Nesterov * Author: Oleg Nesterov <[email protected]>
8cc44ca84SOleg Nesterov */
9cc44ca84SOleg Nesterov
10cc44ca84SOleg Nesterov #include <linux/rcu_sync.h>
11cc44ca84SOleg Nesterov #include <linux/sched.h>
12cc44ca84SOleg Nesterov
1389da3b94SOleg Nesterov enum { GP_IDLE = 0, GP_ENTER, GP_PASSED, GP_EXIT, GP_REPLAY };
14cc44ca84SOleg Nesterov
15cc44ca84SOleg Nesterov #define rss_lock gp_wait.lock
16cc44ca84SOleg Nesterov
17cc44ca84SOleg Nesterov /**
18cc44ca84SOleg Nesterov * rcu_sync_init() - Initialize an rcu_sync structure
19cc44ca84SOleg Nesterov * @rsp: Pointer to rcu_sync structure to be initialized
20cc44ca84SOleg Nesterov */
rcu_sync_init(struct rcu_sync * rsp)2195bf33b5SOleg Nesterov void rcu_sync_init(struct rcu_sync *rsp)
22cc44ca84SOleg Nesterov {
23cc44ca84SOleg Nesterov memset(rsp, 0, sizeof(*rsp));
24cc44ca84SOleg Nesterov init_waitqueue_head(&rsp->gp_wait);
25cc44ca84SOleg Nesterov }
26cc44ca84SOleg Nesterov
2789da3b94SOleg Nesterov static void rcu_sync_func(struct rcu_head *rhp);
2889da3b94SOleg Nesterov
rcu_sync_call(struct rcu_sync * rsp)2989da3b94SOleg Nesterov static void rcu_sync_call(struct rcu_sync *rsp)
3089da3b94SOleg Nesterov {
317651d6b2SJoel Fernandes (Google) call_rcu_hurry(&rsp->cb_head, rcu_sync_func);
3289da3b94SOleg Nesterov }
3389da3b94SOleg Nesterov
3489da3b94SOleg Nesterov /**
3589da3b94SOleg Nesterov * rcu_sync_func() - Callback function managing reader access to fastpath
3689da3b94SOleg Nesterov * @rhp: Pointer to rcu_head in rcu_sync structure to use for synchronization
3789da3b94SOleg Nesterov *
3889da3b94SOleg Nesterov * This function is passed to call_rcu() function by rcu_sync_enter() and
3989da3b94SOleg Nesterov * rcu_sync_exit(), so that it is invoked after a grace period following the
4089da3b94SOleg Nesterov * that invocation of enter/exit.
4189da3b94SOleg Nesterov *
4289da3b94SOleg Nesterov * If it is called by rcu_sync_enter() it signals that all the readers were
4389da3b94SOleg Nesterov * switched onto slow path.
4489da3b94SOleg Nesterov *
4589da3b94SOleg Nesterov * If it is called by rcu_sync_exit() it takes action based on events that
4689da3b94SOleg Nesterov * have taken place in the meantime, so that closely spaced rcu_sync_enter()
4789da3b94SOleg Nesterov * and rcu_sync_exit() pairs need not wait for a grace period.
4889da3b94SOleg Nesterov *
4989da3b94SOleg Nesterov * If another rcu_sync_enter() is invoked before the grace period
5089da3b94SOleg Nesterov * ended, reset state to allow the next rcu_sync_exit() to let the
5189da3b94SOleg Nesterov * readers back onto their fastpaths (after a grace period). If both
5289da3b94SOleg Nesterov * another rcu_sync_enter() and its matching rcu_sync_exit() are invoked
5389da3b94SOleg Nesterov * before the grace period ended, re-invoke call_rcu() on behalf of that
5489da3b94SOleg Nesterov * rcu_sync_exit(). Otherwise, set all state back to idle so that readers
5589da3b94SOleg Nesterov * can again use their fastpaths.
5689da3b94SOleg Nesterov */
rcu_sync_func(struct rcu_head * rhp)5789da3b94SOleg Nesterov static void rcu_sync_func(struct rcu_head *rhp)
5889da3b94SOleg Nesterov {
5989da3b94SOleg Nesterov struct rcu_sync *rsp = container_of(rhp, struct rcu_sync, cb_head);
6089da3b94SOleg Nesterov unsigned long flags;
6189da3b94SOleg Nesterov
6289da3b94SOleg Nesterov WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_IDLE);
6389da3b94SOleg Nesterov WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_PASSED);
6489da3b94SOleg Nesterov
6589da3b94SOleg Nesterov spin_lock_irqsave(&rsp->rss_lock, flags);
6689da3b94SOleg Nesterov if (rsp->gp_count) {
6789da3b94SOleg Nesterov /*
6889da3b94SOleg Nesterov * We're at least a GP after the GP_IDLE->GP_ENTER transition.
6989da3b94SOleg Nesterov */
7089da3b94SOleg Nesterov WRITE_ONCE(rsp->gp_state, GP_PASSED);
7189da3b94SOleg Nesterov wake_up_locked(&rsp->gp_wait);
7289da3b94SOleg Nesterov } else if (rsp->gp_state == GP_REPLAY) {
7389da3b94SOleg Nesterov /*
7489da3b94SOleg Nesterov * A new rcu_sync_exit() has happened; requeue the callback to
7589da3b94SOleg Nesterov * catch a later GP.
7689da3b94SOleg Nesterov */
7789da3b94SOleg Nesterov WRITE_ONCE(rsp->gp_state, GP_EXIT);
7889da3b94SOleg Nesterov rcu_sync_call(rsp);
7989da3b94SOleg Nesterov } else {
8089da3b94SOleg Nesterov /*
81a616aec9SIngo Molnar * We're at least a GP after the last rcu_sync_exit(); everybody
8289da3b94SOleg Nesterov * will now have observed the write side critical section.
83a616aec9SIngo Molnar * Let 'em rip!
8489da3b94SOleg Nesterov */
8589da3b94SOleg Nesterov WRITE_ONCE(rsp->gp_state, GP_IDLE);
8689da3b94SOleg Nesterov }
8789da3b94SOleg Nesterov spin_unlock_irqrestore(&rsp->rss_lock, flags);
8889da3b94SOleg Nesterov }
8989da3b94SOleg Nesterov
903942a9bdSPeter Zijlstra /**
91cc44ca84SOleg Nesterov * rcu_sync_enter() - Force readers onto slowpath
92cc44ca84SOleg Nesterov * @rsp: Pointer to rcu_sync structure to use for synchronization
93cc44ca84SOleg Nesterov *
94cc44ca84SOleg Nesterov * This function is used by updaters who need readers to make use of
95cc44ca84SOleg Nesterov * a slowpath during the update. After this function returns, all
96cc44ca84SOleg Nesterov * subsequent calls to rcu_sync_is_idle() will return false, which
97cc44ca84SOleg Nesterov * tells readers to stay off their fastpaths. A later call to
98f1efe84dSDavid Vernet * rcu_sync_exit() re-enables reader fastpaths.
99cc44ca84SOleg Nesterov *
100cc44ca84SOleg Nesterov * When called in isolation, rcu_sync_enter() must wait for a grace
101cc44ca84SOleg Nesterov * period, however, closely spaced calls to rcu_sync_enter() can
102cc44ca84SOleg Nesterov * optimize away the grace-period wait via a state machine implemented
103cc44ca84SOleg Nesterov * by rcu_sync_enter(), rcu_sync_exit(), and rcu_sync_func().
104cc44ca84SOleg Nesterov */
rcu_sync_enter(struct rcu_sync * rsp)105cc44ca84SOleg Nesterov void rcu_sync_enter(struct rcu_sync *rsp)
106cc44ca84SOleg Nesterov {
10789da3b94SOleg Nesterov int gp_state;
108cc44ca84SOleg Nesterov
109cc44ca84SOleg Nesterov spin_lock_irq(&rsp->rss_lock);
11089da3b94SOleg Nesterov gp_state = rsp->gp_state;
11189da3b94SOleg Nesterov if (gp_state == GP_IDLE) {
11289da3b94SOleg Nesterov WRITE_ONCE(rsp->gp_state, GP_ENTER);
11389da3b94SOleg Nesterov WARN_ON_ONCE(rsp->gp_count);
11489da3b94SOleg Nesterov /*
11589da3b94SOleg Nesterov * Note that we could simply do rcu_sync_call(rsp) here and
11689da3b94SOleg Nesterov * avoid the "if (gp_state == GP_IDLE)" block below.
11789da3b94SOleg Nesterov *
11889da3b94SOleg Nesterov * However, synchronize_rcu() can be faster if rcu_expedited
11989da3b94SOleg Nesterov * or rcu_blocking_is_gp() is true.
12089da3b94SOleg Nesterov *
12189da3b94SOleg Nesterov * Another reason is that we can't wait for rcu callback if
12289da3b94SOleg Nesterov * we are called at early boot time but this shouldn't happen.
12389da3b94SOleg Nesterov */
12489da3b94SOleg Nesterov }
125*6f4cec22SOleg Nesterov rsp->gp_count++;
126cc44ca84SOleg Nesterov spin_unlock_irq(&rsp->rss_lock);
127cc44ca84SOleg Nesterov
12889da3b94SOleg Nesterov if (gp_state == GP_IDLE) {
12989da3b94SOleg Nesterov /*
13089da3b94SOleg Nesterov * See the comment above, this simply does the "synchronous"
13189da3b94SOleg Nesterov * call_rcu(rcu_sync_func) which does GP_ENTER -> GP_PASSED.
13289da3b94SOleg Nesterov */
13395bf33b5SOleg Nesterov synchronize_rcu();
13489da3b94SOleg Nesterov rcu_sync_func(&rsp->cb_head);
13589da3b94SOleg Nesterov /* Not really needed, wait_event() would see GP_PASSED. */
13689da3b94SOleg Nesterov return;
137cc44ca84SOleg Nesterov }
13889da3b94SOleg Nesterov
13989da3b94SOleg Nesterov wait_event(rsp->gp_wait, READ_ONCE(rsp->gp_state) >= GP_PASSED);
140cc44ca84SOleg Nesterov }
141cc44ca84SOleg Nesterov
142cc44ca84SOleg Nesterov /**
14389da3b94SOleg Nesterov * rcu_sync_exit() - Allow readers back onto fast path after grace period
144cc44ca84SOleg Nesterov * @rsp: Pointer to rcu_sync structure to use for synchronization
145cc44ca84SOleg Nesterov *
146cc44ca84SOleg Nesterov * This function is used by updaters who have completed, and can therefore
147cc44ca84SOleg Nesterov * now allow readers to make use of their fastpaths after a grace period
148cc44ca84SOleg Nesterov * has elapsed. After this grace period has completed, all subsequent
149cc44ca84SOleg Nesterov * calls to rcu_sync_is_idle() will return true, which tells readers that
150cc44ca84SOleg Nesterov * they can once again use their fastpaths.
151cc44ca84SOleg Nesterov */
rcu_sync_exit(struct rcu_sync * rsp)152cc44ca84SOleg Nesterov void rcu_sync_exit(struct rcu_sync *rsp)
153cc44ca84SOleg Nesterov {
15489da3b94SOleg Nesterov WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_IDLE);
15589da3b94SOleg Nesterov
156cc44ca84SOleg Nesterov spin_lock_irq(&rsp->rss_lock);
157*6f4cec22SOleg Nesterov WARN_ON_ONCE(rsp->gp_count == 0);
158*6f4cec22SOleg Nesterov if (!--rsp->gp_count) {
15989da3b94SOleg Nesterov if (rsp->gp_state == GP_PASSED) {
16089da3b94SOleg Nesterov WRITE_ONCE(rsp->gp_state, GP_EXIT);
16189da3b94SOleg Nesterov rcu_sync_call(rsp);
16289da3b94SOleg Nesterov } else if (rsp->gp_state == GP_EXIT) {
16389da3b94SOleg Nesterov WRITE_ONCE(rsp->gp_state, GP_REPLAY);
164cc44ca84SOleg Nesterov }
165cc44ca84SOleg Nesterov }
166cc44ca84SOleg Nesterov spin_unlock_irq(&rsp->rss_lock);
167cc44ca84SOleg Nesterov }
16807899a6eSOleg Nesterov
16907899a6eSOleg Nesterov /**
17007899a6eSOleg Nesterov * rcu_sync_dtor() - Clean up an rcu_sync structure
17107899a6eSOleg Nesterov * @rsp: Pointer to rcu_sync structure to be cleaned up
17207899a6eSOleg Nesterov */
rcu_sync_dtor(struct rcu_sync * rsp)17307899a6eSOleg Nesterov void rcu_sync_dtor(struct rcu_sync *rsp)
17407899a6eSOleg Nesterov {
17589da3b94SOleg Nesterov int gp_state;
17607899a6eSOleg Nesterov
17789da3b94SOleg Nesterov WARN_ON_ONCE(READ_ONCE(rsp->gp_state) == GP_PASSED);
17807899a6eSOleg Nesterov
17907899a6eSOleg Nesterov spin_lock_irq(&rsp->rss_lock);
180*6f4cec22SOleg Nesterov WARN_ON_ONCE(rsp->gp_count);
18189da3b94SOleg Nesterov if (rsp->gp_state == GP_REPLAY)
18289da3b94SOleg Nesterov WRITE_ONCE(rsp->gp_state, GP_EXIT);
18389da3b94SOleg Nesterov gp_state = rsp->gp_state;
18407899a6eSOleg Nesterov spin_unlock_irq(&rsp->rss_lock);
18507899a6eSOleg Nesterov
18689da3b94SOleg Nesterov if (gp_state != GP_IDLE) {
18795bf33b5SOleg Nesterov rcu_barrier();
18889da3b94SOleg Nesterov WARN_ON_ONCE(rsp->gp_state != GP_IDLE);
18907899a6eSOleg Nesterov }
19007899a6eSOleg Nesterov }
191