#include <linux/workqueue.h>
 #include <linux/rcu_segcblist.h>
 
-struct srcu_array {
-       unsigned long lock_count[2];
-       unsigned long unlock_count[2];
-};
-
-struct srcu_struct {
-       unsigned long completed;
-       unsigned long srcu_gp_seq;
-       atomic_t srcu_exp_cnt;
-       struct srcu_array __percpu *per_cpu_ref;
-       spinlock_t queue_lock; /* protect ->srcu_cblist */
-       struct rcu_segcblist srcu_cblist;
-       struct delayed_work work;
-#ifdef CONFIG_DEBUG_LOCK_ALLOC
-       struct lockdep_map dep_map;
-#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
-};
-
-/* Values for -> state variable. */
-#define SRCU_STATE_IDLE                0
-#define SRCU_STATE_SCAN1       1
-#define SRCU_STATE_SCAN2       2
+struct srcu_struct;
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 
 #define __SRCU_DEP_MAP_INIT(srcu_name)
 #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
 
-void process_srcu(struct work_struct *work);
-
-#define __SRCU_STRUCT_INIT(name)                                       \
-       {                                                               \
-               .completed = -300,                                      \
-               .per_cpu_ref = &name##_srcu_array,                      \
-               .queue_lock = __SPIN_LOCK_UNLOCKED(name.queue_lock),    \
-               .srcu_cblist = RCU_SEGCBLIST_INITIALIZER(name.srcu_cblist),\
-               .work = __DELAYED_WORK_INITIALIZER(name.work, process_srcu, 0),\
-               __SRCU_DEP_MAP_INIT(name)                               \
-       }
-
-/*
- * Define and initialize a srcu struct at build time.
- * Do -not- call init_srcu_struct() nor cleanup_srcu_struct() on it.
- *
- * Note that although DEFINE_STATIC_SRCU() hides the name from other
- * files, the per-CPU variable rules nevertheless require that the
- * chosen name be globally unique.  These rules also prohibit use of
- * DEFINE_STATIC_SRCU() within a function.  If these rules are too
- * restrictive, declare the srcu_struct manually.  For example, in
- * each file:
- *
- *     static struct srcu_struct my_srcu;
- *
- * Then, before the first use of each my_srcu, manually initialize it:
- *
- *     init_srcu_struct(&my_srcu);
- *
- * See include/linux/percpu-defs.h for the rules on per-CPU variables.
- */
-#define __DEFINE_SRCU(name, is_static)                                 \
-       static DEFINE_PER_CPU(struct srcu_array, name##_srcu_array);\
-       is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
-#define DEFINE_SRCU(name)              __DEFINE_SRCU(name, /* not static */)
-#define DEFINE_STATIC_SRCU(name)       __DEFINE_SRCU(name, static)
+#ifdef CONFIG_TINY_SRCU
+#include <linux/srcutiny.h>
+#elif defined(CONFIG_TREE_SRCU)
+#include <linux/srcutree.h>
+#else
+#error "Unknown SRCU implementation specified to kernel configuration"
+#endif
 
 /**
  * call_srcu() - Queue a callback for invocation after an SRCU grace period
 int __srcu_read_lock(struct srcu_struct *sp) __acquires(sp);
 void __srcu_read_unlock(struct srcu_struct *sp, int idx) __releases(sp);
 void synchronize_srcu(struct srcu_struct *sp);
-void synchronize_srcu_expedited(struct srcu_struct *sp);
-unsigned long srcu_batches_completed(struct srcu_struct *sp);
-void srcu_barrier(struct srcu_struct *sp);
 
 #ifdef CONFIG_DEBUG_LOCK_ALLOC
 
 
--- /dev/null
+/*
+ * Sleepable Read-Copy Update mechanism for mutual exclusion,
+ *     tiny variant.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * Copyright (C) IBM Corporation, 2017
+ *
+ * Author: Paul McKenney <paulmck@us.ibm.com>
+ */
+
+#ifndef _LINUX_SRCU_TINY_H
+#define _LINUX_SRCU_TINY_H
+
+#include <linux/swait.h>
+
+struct srcu_struct {
+       int srcu_lock_nesting[2];       /* srcu_read_lock() nesting depth. */
+       struct swait_queue_head srcu_wq;
+                                       /* Last srcu_read_unlock() wakes GP. */
+       unsigned long srcu_gp_seq;      /* GP seq # for callback tagging. */
+       struct rcu_segcblist srcu_cblist;
+                                       /* Pending SRCU callbacks. */
+       int srcu_idx;                   /* Current reader array element. */
+       bool srcu_gp_running;           /* GP workqueue running? */
+       bool srcu_gp_waiting;           /* GP waiting for readers? */
+       struct work_struct srcu_work;   /* For driving grace periods. */
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       struct lockdep_map dep_map;
+#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+};
+
+void srcu_drive_gp(struct work_struct *wp);
+
+#define __SRCU_STRUCT_INIT(name)                                       \
+{                                                                      \
+       .srcu_wq = __SWAIT_QUEUE_HEAD_INITIALIZER(name.srcu_wq),        \
+       .srcu_cblist = RCU_SEGCBLIST_INITIALIZER(name.srcu_cblist),     \
+       .srcu_work = __WORK_INITIALIZER(name.srcu_work, srcu_drive_gp), \
+       __SRCU_DEP_MAP_INIT(name)                                       \
+}
+
+/*
+ * This odd _STATIC_ arrangement is needed for API compatibility with
+ * Tree SRCU, which needs some per-CPU data.
+ */
+#define DEFINE_SRCU(name) \
+       struct srcu_struct name = __SRCU_STRUCT_INIT(name)
+#define DEFINE_STATIC_SRCU(name) \
+       static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
+
+void synchronize_srcu(struct srcu_struct *sp);
+
+static inline void synchronize_srcu_expedited(struct srcu_struct *sp)
+{
+       synchronize_srcu(sp);
+}
+
+static inline void srcu_barrier(struct srcu_struct *sp)
+{
+       synchronize_srcu(sp);
+}
+
+static inline unsigned long srcu_batches_completed(struct srcu_struct *sp)
+{
+       return 0;
+}
+
+#endif
 
--- /dev/null
+/*
+ * Sleepable Read-Copy Update mechanism for mutual exclusion,
+ *     tree variant.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * Copyright (C) IBM Corporation, 2017
+ *
+ * Author: Paul McKenney <paulmck@us.ibm.com>
+ */
+
+#ifndef _LINUX_SRCU_TREE_H
+#define _LINUX_SRCU_TREE_H
+
+struct srcu_array {
+       unsigned long lock_count[2];
+       unsigned long unlock_count[2];
+};
+
+struct srcu_struct {
+       unsigned long completed;
+       unsigned long srcu_gp_seq;
+       atomic_t srcu_exp_cnt;
+       struct srcu_array __percpu *per_cpu_ref;
+       spinlock_t queue_lock; /* protect ->srcu_cblist */
+       struct rcu_segcblist srcu_cblist;
+       struct delayed_work work;
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+       struct lockdep_map dep_map;
+#endif /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+};
+
+/* Values for -> state variable. */
+#define SRCU_STATE_IDLE                0
+#define SRCU_STATE_SCAN1       1
+#define SRCU_STATE_SCAN2       2
+
+void process_srcu(struct work_struct *work);
+
+#define __SRCU_STRUCT_INIT(name)                                       \
+       {                                                               \
+               .completed = -300,                                      \
+               .per_cpu_ref = &name##_srcu_array,                      \
+               .queue_lock = __SPIN_LOCK_UNLOCKED(name.queue_lock),    \
+               .srcu_cblist = RCU_SEGCBLIST_INITIALIZER(name.srcu_cblist),\
+               .work = __DELAYED_WORK_INITIALIZER(name.work, process_srcu, 0),\
+               __SRCU_DEP_MAP_INIT(name)                               \
+       }
+
+/*
+ * Define and initialize a srcu struct at build time.
+ * Do -not- call init_srcu_struct() nor cleanup_srcu_struct() on it.
+ *
+ * Note that although DEFINE_STATIC_SRCU() hides the name from other
+ * files, the per-CPU variable rules nevertheless require that the
+ * chosen name be globally unique.  These rules also prohibit use of
+ * DEFINE_STATIC_SRCU() within a function.  If these rules are too
+ * restrictive, declare the srcu_struct manually.  For example, in
+ * each file:
+ *
+ *     static struct srcu_struct my_srcu;
+ *
+ * Then, before the first use of each my_srcu, manually initialize it:
+ *
+ *     init_srcu_struct(&my_srcu);
+ *
+ * See include/linux/percpu-defs.h for the rules on per-CPU variables.
+ */
+#define __DEFINE_SRCU(name, is_static)                                 \
+       static DEFINE_PER_CPU(struct srcu_array, name##_srcu_array);\
+       is_static struct srcu_struct name = __SRCU_STRUCT_INIT(name)
+#define DEFINE_SRCU(name)              __DEFINE_SRCU(name, /* not static */)
+#define DEFINE_STATIC_SRCU(name)       __DEFINE_SRCU(name, static)
+
+void synchronize_srcu_expedited(struct srcu_struct *sp);
+void srcu_barrier(struct srcu_struct *sp);
+unsigned long srcu_batches_completed(struct srcu_struct *sp);
+
+#endif
 
          permits arbitrary sleeping or blocking within RCU read-side critical
          sections.
 
+config TINY_SRCU
+       bool
+       default y if TINY_RCU
+       help
+         This option selects the single-CPU non-preemptible version of SRCU.
+
+config TREE_SRCU
+       bool
+       default y if !TINY_RCU
+       help
+         This option selects the full-fledged version of SRCU.
+
 config TASKS_RCU
        bool
        default n
 
 KCOV_INSTRUMENT := n
 
 obj-y += update.o sync.o
-obj-$(CONFIG_SRCU) += srcu.o
+obj-$(CONFIG_TREE_SRCU) += srcu.o
+obj-$(CONFIG_TINY_SRCU) += srcutiny.o
 obj-$(CONFIG_RCU_TORTURE_TEST) += rcutorture.o
 obj-$(CONFIG_RCU_PERF_TEST) += rcuperf.o
 obj-$(CONFIG_TREE_RCU) += tree.o
 
 
 static void srcu_torture_stats(void)
 {
+#ifdef CONFIG_TREE_SRCU
        int cpu;
        int idx = srcu_ctlp->completed & 0x1;
 
                pr_cont(" %d(%ld,%ld)", cpu, c0, c1);
        }
        pr_cont("\n");
+#endif
 }
 
 static void srcu_torture_synchronize_expedited(void)
 
--- /dev/null
+/*
+ * Sleepable Read-Copy Update mechanism for mutual exclusion,
+ *     tiny version for non-preemptible single-CPU use.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, you can access it online at
+ * http://www.gnu.org/licenses/gpl-2.0.html.
+ *
+ * Copyright (C) IBM Corporation, 2017
+ *
+ * Author: Paul McKenney <paulmck@us.ibm.com>
+ */
+
+#include <linux/export.h>
+#include <linux/mutex.h>
+#include <linux/preempt.h>
+#include <linux/rcupdate_wait.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/srcu.h>
+
+#include <linux/rcu_node_tree.h>
+#include "rcu.h"
+
+static int init_srcu_struct_fields(struct srcu_struct *sp)
+{
+       sp->srcu_lock_nesting[0] = 0;
+       sp->srcu_lock_nesting[1] = 0;
+       init_swait_queue_head(&sp->srcu_wq);
+       sp->srcu_gp_seq = 0;
+       rcu_segcblist_init(&sp->srcu_cblist);
+       sp->srcu_gp_running = false;
+       sp->srcu_gp_waiting = false;
+       sp->srcu_idx = 0;
+       INIT_WORK(&sp->srcu_work, srcu_drive_gp);
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+
+int __init_srcu_struct(struct srcu_struct *sp, const char *name,
+                      struct lock_class_key *key)
+{
+       /* Don't re-initialize a lock while it is held. */
+       debug_check_no_locks_freed((void *)sp, sizeof(*sp));
+       lockdep_init_map(&sp->dep_map, name, key, 0);
+       return init_srcu_struct_fields(sp);
+}
+EXPORT_SYMBOL_GPL(__init_srcu_struct);
+
+#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+
+/*
+ * init_srcu_struct - initialize a sleep-RCU structure
+ * @sp: structure to initialize.
+ *
+ * Must invoke this on a given srcu_struct before passing that srcu_struct
+ * to any other function.  Each srcu_struct represents a separate domain
+ * of SRCU protection.
+ */
+int init_srcu_struct(struct srcu_struct *sp)
+{
+       return init_srcu_struct_fields(sp);
+}
+EXPORT_SYMBOL_GPL(init_srcu_struct);
+
+#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+
+/*
+ * cleanup_srcu_struct - deconstruct a sleep-RCU structure
+ * @sp: structure to clean up.
+ *
+ * Must invoke this after you are finished using a given srcu_struct that
+ * was initialized via init_srcu_struct(), else you leak memory.
+ */
+void cleanup_srcu_struct(struct srcu_struct *sp)
+{
+       WARN_ON(sp->srcu_lock_nesting[0] || sp->srcu_lock_nesting[1]);
+       flush_work(&sp->srcu_work);
+       WARN_ON(rcu_seq_state(sp->srcu_gp_seq));
+       WARN_ON(sp->srcu_gp_running);
+       WARN_ON(sp->srcu_gp_waiting);
+       WARN_ON(!rcu_segcblist_empty(&sp->srcu_cblist));
+}
+EXPORT_SYMBOL_GPL(cleanup_srcu_struct);
+
+/*
+ * Counts the new reader in the appropriate per-CPU element of the
+ * srcu_struct.  Must be called from process context.
+ * Returns an index that must be passed to the matching srcu_read_unlock().
+ */
+int __srcu_read_lock(struct srcu_struct *sp)
+{
+       int idx;
+
+       idx = READ_ONCE(sp->srcu_idx);
+       WRITE_ONCE(sp->srcu_lock_nesting[idx], sp->srcu_lock_nesting[idx] + 1);
+       return idx;
+}
+EXPORT_SYMBOL_GPL(__srcu_read_lock);
+
+/*
+ * Removes the count for the old reader from the appropriate element of
+ * the srcu_struct.  Must be called from process context.
+ */
+void __srcu_read_unlock(struct srcu_struct *sp, int idx)
+{
+       int newval = sp->srcu_lock_nesting[idx] - 1;
+
+       WRITE_ONCE(sp->srcu_lock_nesting[idx], newval);
+       if (!newval && READ_ONCE(sp->srcu_gp_waiting))
+               swake_up(&sp->srcu_wq);
+}
+EXPORT_SYMBOL_GPL(__srcu_read_unlock);
+
+/*
+ * Workqueue handler to drive one grace period and invoke any callbacks
+ * that become ready as a result.  Single-CPU and !PREEMPT operation
+ * means that we get away with murder on synchronization.  ;-)
+ */
+void srcu_drive_gp(struct work_struct *wp)
+{
+       int idx;
+       struct rcu_cblist ready_cbs;
+       struct srcu_struct *sp;
+       struct rcu_head *rhp;
+
+       sp = container_of(wp, struct srcu_struct, srcu_work);
+       if (sp->srcu_gp_running || rcu_segcblist_empty(&sp->srcu_cblist))
+               return; /* Already running or nothing to do. */
+
+       /* Tag recently arrived callbacks and wait for readers. */
+       WRITE_ONCE(sp->srcu_gp_running, true);
+       rcu_segcblist_accelerate(&sp->srcu_cblist,
+                                rcu_seq_snap(&sp->srcu_gp_seq));
+       rcu_seq_start(&sp->srcu_gp_seq);
+       idx = sp->srcu_idx;
+       WRITE_ONCE(sp->srcu_idx, !sp->srcu_idx);
+       WRITE_ONCE(sp->srcu_gp_waiting, true);  /* srcu_read_unlock() wakes! */
+       swait_event(sp->srcu_wq, !READ_ONCE(sp->srcu_lock_nesting[idx]));
+       WRITE_ONCE(sp->srcu_gp_waiting, false); /* srcu_read_unlock() cheap. */
+       rcu_seq_end(&sp->srcu_gp_seq);
+
+       /* Update callback list based on GP, and invoke ready callbacks. */
+       rcu_segcblist_advance(&sp->srcu_cblist,
+                             rcu_seq_current(&sp->srcu_gp_seq));
+       if (rcu_segcblist_ready_cbs(&sp->srcu_cblist)) {
+               rcu_cblist_init(&ready_cbs);
+               local_irq_disable();
+               rcu_segcblist_extract_done_cbs(&sp->srcu_cblist, &ready_cbs);
+               local_irq_enable();
+               rhp = rcu_cblist_dequeue(&ready_cbs);
+               for (; rhp != NULL; rhp = rcu_cblist_dequeue(&ready_cbs)) {
+                       local_bh_disable();
+                       rhp->func(rhp);
+                       local_bh_enable();
+               }
+               local_irq_disable();
+               rcu_segcblist_insert_count(&sp->srcu_cblist, &ready_cbs);
+               local_irq_enable();
+       }
+       WRITE_ONCE(sp->srcu_gp_running, false);
+
+       /*
+        * If more callbacks, reschedule ourselves.  This can race with
+        * a call_srcu() at interrupt level, but the ->srcu_gp_running
+        * checks will straighten that out.
+        */
+       if (!rcu_segcblist_empty(&sp->srcu_cblist))
+               schedule_work(&sp->srcu_work);
+}
+EXPORT_SYMBOL_GPL(srcu_drive_gp);
+
+/*
+ * Enqueue an SRCU callback on the specified srcu_struct structure,
+ * initiating grace-period processing if it is not already running.
+ */
+void call_srcu(struct srcu_struct *sp, struct rcu_head *head,
+              rcu_callback_t func)
+{
+       unsigned long flags;
+
+       head->func = func;
+       local_irq_save(flags);
+       rcu_segcblist_enqueue(&sp->srcu_cblist, head, false);
+       local_irq_restore(flags);
+       if (!READ_ONCE(sp->srcu_gp_running))
+               schedule_work(&sp->srcu_work);
+}
+EXPORT_SYMBOL_GPL(call_srcu);
+
+/*
+ * synchronize_srcu - wait for prior SRCU read-side critical-section completion
+ */
+void synchronize_srcu(struct srcu_struct *sp)
+{
+       struct rcu_synchronize rs;
+
+       init_rcu_head_on_stack(&rs.head);
+       init_completion(&rs.completion);
+       call_srcu(sp, &rs.head, wakeme_after_rcu);
+       wait_for_completion(&rs.completion);
+       destroy_rcu_head_on_stack(&rs.head);
+}
+EXPORT_SYMBOL_GPL(synchronize_srcu);