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