]> Git Repo - linux.git/commitdiff
Merge branches 'doc.2022.10.20a', 'fixes.2022.10.21a', 'lazy.2022.11.30a', 'srcunmisa...
authorPaul E. McKenney <[email protected]>
Wed, 30 Nov 2022 21:20:05 +0000 (13:20 -0800)
committerPaul E. McKenney <[email protected]>
Wed, 30 Nov 2022 21:20:05 +0000 (13:20 -0800)
doc.2022.10.20a: Documentation updates.
fixes.2022.10.21a: Miscellaneous fixes.
lazy.2022.11.30a: Lazy call_rcu() and NOCB updates.
srcunmisafe.2022.11.09a: NMI-safe SRCU readers.
torture.2022.10.18c: Torture-test updates.
torturescript.2022.10.20a: Torture-test scripting updates.

1  2  3  4  5  6 
include/linux/rcupdate.h
kernel/rcu/Kconfig
kernel/rcu/rcutorture.c
kernel/rcu/tree.c

diff --combined include/linux/rcupdate.h
index 08605ce7379d7b0826cadaff8e6c520292a2fdfd,08605ce7379d7b0826cadaff8e6c520292a2fdfd,611c11383d236978f1b17e2997d1f01c440f5d2c,65178c40ab6fdea47d0779449750cb2e435e9d85,08605ce7379d7b0826cadaff8e6c520292a2fdfd,08605ce7379d7b0826cadaff8e6c520292a2fdfd..4da98ca6273e9aa5a3c5e6aa7786b3702c0ecebc
@@@@@@@ -108,6 -108,6 -108,15 -108,6 -108,6 -108,6 +108,15 @@@@@@@ static inline int rcu_preempt_depth(voi
      
      #endif /* #else #ifdef CONFIG_PREEMPT_RCU */
      
++ +++#ifdef CONFIG_RCU_LAZY
++ +++void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func);
++ +++#else
++ +++static inline void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func)
++ +++{
++ +++  call_rcu(head, func);
++ +++}
++ +++#endif
++ +++
      /* Internal to kernel */
      void rcu_init(void);
      extern int rcu_scheduler_active;
@@@@@@@ -340,6 -340,6 -349,6 -340,11 -340,6 -340,6 +349,11 @@@@@@@ static inline int rcu_read_lock_any_hel
        return !preemptible();
      }
      
+++ ++static inline int debug_lockdep_rcu_enabled(void)
+++ ++{
+++ ++  return 0;
+++ ++}
+++ ++
      #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
      
      #ifdef CONFIG_PROVE_RCU
diff --combined kernel/rcu/Kconfig
index d471d22a5e21b4343c8f37d41b449b3622e15dbe,d471d22a5e21b4343c8f37d41b449b3622e15dbe,d78f6181c8aad5aa8c8f1d2486107aed5ff4b30e,f53ad63b2bc631ca2e614fd4ae3cfbaa5849f121,d471d22a5e21b4343c8f37d41b449b3622e15dbe,d471d22a5e21b4343c8f37d41b449b3622e15dbe..05106e6fbd0edd5b9432db53151b9765cdb35872
@@@@@@@ -72,6 -72,6 -72,6 -72,9 -72,6 -72,6 +72,9 @@@@@@@ config TREE_SRC
        help
          This option selects the full-fledged version of SRCU.
      
+++ ++config NEED_SRCU_NMI_SAFE
+++ ++  def_bool HAVE_NMI && !ARCH_HAS_NMI_SAFE_THIS_CPU_OPS && !TINY_SRCU
+++ ++
      config TASKS_RCU_GENERIC
        def_bool TASKS_RCU || TASKS_RUDE_RCU || TASKS_TRACE_RCU
        select SRCU
@@@@@@@ -311,4 -311,4 -311,12 -314,4 -311,4 -311,4 +314,12 @@@@@@@ config TASKS_TRACE_RCU_READ_M
          Say N here if you hate read-side memory barriers.
          Take the default if you are unsure.
      
++ +++config RCU_LAZY
++ +++  bool "RCU callback lazy invocation functionality"
++ +++  depends on RCU_NOCB_CPU
++ +++  default n
++ +++  help
++ +++    To save power, batch RCU callbacks and flush after delay, memory
++ +++    pressure, or callback list growing too big.
++ +++
      endmenu # "RCU Subsystem"
diff --combined kernel/rcu/rcutorture.c
index 503c2aa845a4a607d483045ea3dad0448ff38f9f,503c2aa845a4a607d483045ea3dad0448ff38f9f,2226f86f54f7864900a7cd231a1ff98208cd300d,b4c74ce1022560b0ac388acaff763a3992ec5e6d,9ad5301385a41f73186e95ca44d557e563a83529,503c2aa845a4a607d483045ea3dad0448ff38f9f..634df26a2c27cdd2f18e71d8fbce0e2b10a9d5a3
@@@@@@@ -357,6 -357,6 -357,6 -357,6 -357,10 -357,6 +357,10 @@@@@@@ struct rcu_torture_ops 
        bool (*poll_gp_state_exp)(unsigned long oldstate);
        void (*cond_sync_exp)(unsigned long oldstate);
        void (*cond_sync_exp_full)(struct rcu_gp_oldstate *rgosp);
++++ +  unsigned long (*get_comp_state)(void);
++++ +  void (*get_comp_state_full)(struct rcu_gp_oldstate *rgosp);
++++ +  bool (*same_gp_state)(unsigned long oldstate1, unsigned long oldstate2);
++++ +  bool (*same_gp_state_full)(struct rcu_gp_oldstate *rgosp1, struct rcu_gp_oldstate *rgosp2);
        unsigned long (*get_gp_state)(void);
        void (*get_gp_state_full)(struct rcu_gp_oldstate *rgosp);
        unsigned long (*get_gp_completed)(void);
@@@@@@@ -510,7 -510,7 -510,7 -510,7 -514,7 -510,7 +514,7 @@@@@@@ static unsigned long rcu_no_completed(v
      
      static void rcu_torture_deferred_free(struct rcu_torture *p)
      {
-- ---  call_rcu(&p->rtort_rcu, rcu_torture_cb);
++ +++  call_rcu_hurry(&p->rtort_rcu, rcu_torture_cb);
      }
      
      static void rcu_sync_torture_init(void)
@@@@@@@ -535,6 -535,6 -535,6 -535,6 -539,10 -535,6 +539,10 @@@@@@@ static struct rcu_torture_ops rcu_ops 
        .deferred_free          = rcu_torture_deferred_free,
        .sync                   = synchronize_rcu,
        .exp_sync               = synchronize_rcu_expedited,
++++ +  .same_gp_state          = same_state_synchronize_rcu,
++++ +  .same_gp_state_full     = same_state_synchronize_rcu_full,
++++ +  .get_comp_state         = get_completed_synchronize_rcu,
++++ +  .get_comp_state_full    = get_completed_synchronize_rcu_full,
        .get_gp_state           = get_state_synchronize_rcu,
        .get_gp_state_full      = get_state_synchronize_rcu_full,
        .get_gp_completed       = get_completed_synchronize_rcu,
        .start_gp_poll_exp_full = start_poll_synchronize_rcu_expedited_full,
        .poll_gp_state_exp      = poll_state_synchronize_rcu,
        .cond_sync_exp          = cond_synchronize_rcu_expedited,
-- ---  .call                   = call_rcu,
++ +++  .call                   = call_rcu_hurry,
        .cb_barrier             = rcu_barrier,
        .fqs                    = rcu_force_quiescent_state,
        .stats                  = NULL,
@@@@@@@ -615,10 -615,10 -615,10 -615,14 -623,10 -615,10 +623,14 @@@@@@@ static struct rcu_torture_ops rcu_buste
      DEFINE_STATIC_SRCU(srcu_ctl);
      static struct srcu_struct srcu_ctld;
      static struct srcu_struct *srcu_ctlp = &srcu_ctl;
+++ ++static struct rcu_torture_ops srcud_ops;
      
      static int srcu_torture_read_lock(void) __acquires(srcu_ctlp)
      {
--- --  return srcu_read_lock(srcu_ctlp);
+++ ++  if (cur_ops == &srcud_ops)
+++ ++          return srcu_read_lock_nmisafe(srcu_ctlp);
+++ ++  else
+++ ++          return srcu_read_lock(srcu_ctlp);
      }
      
      static void
@@@@@@@ -642,7 -642,7 -642,7 -646,10 -650,7 -642,7 +654,10 @@@@@@@ srcu_read_delay(struct torture_random_s
      
      static void srcu_torture_read_unlock(int idx) __releases(srcu_ctlp)
      {
--- --  srcu_read_unlock(srcu_ctlp, idx);
+++ ++  if (cur_ops == &srcud_ops)
+++ ++          srcu_read_unlock_nmisafe(srcu_ctlp, idx);
+++ ++  else
+++ ++          srcu_read_unlock(srcu_ctlp, idx);
      }
      
      static int torture_srcu_read_lock_held(void)
@@@@@@@ -848,7 -848,7 -848,7 -855,7 -856,7 -848,7 +863,7 @@@@@@@ static void rcu_tasks_torture_deferred_
      
      static void synchronize_rcu_mult_test(void)
      {
-- ---  synchronize_rcu_mult(call_rcu_tasks, call_rcu);
++ +++  synchronize_rcu_mult(call_rcu_tasks, call_rcu_hurry);
      }
      
      static struct rcu_torture_ops tasks_ops = {
@@@@@@@ -1258,13 -1258,13 -1258,13 -1265,13 -1266,15 -1258,13 +1273,15 @@@@@@@ static void rcu_torture_write_types(voi
        } else if (gp_normal && !cur_ops->deferred_free) {
                pr_alert("%s: gp_normal without primitives.\n", __func__);
        }
---- -  if (gp_poll1 && cur_ops->start_gp_poll && cur_ops->poll_gp_state) {
++++ +  if (gp_poll1 && cur_ops->get_comp_state && cur_ops->same_gp_state &&
++++ +      cur_ops->start_gp_poll && cur_ops->poll_gp_state) {
                synctype[nsynctypes++] = RTWS_POLL_GET;
                pr_info("%s: Testing polling GPs.\n", __func__);
        } else if (gp_poll && (!cur_ops->start_gp_poll || !cur_ops->poll_gp_state)) {
                pr_alert("%s: gp_poll without primitives.\n", __func__);
        }
---- -  if (gp_poll_full1 && cur_ops->start_gp_poll_full && cur_ops->poll_gp_state_full) {
++++ +  if (gp_poll_full1 && cur_ops->get_comp_state_full && cur_ops->same_gp_state_full
++++ +      && cur_ops->start_gp_poll_full && cur_ops->poll_gp_state_full) {
                synctype[nsynctypes++] = RTWS_POLL_GET_FULL;
                pr_info("%s: Testing polling full-state GPs.\n", __func__);
        } else if (gp_poll_full && (!cur_ops->start_gp_poll_full || !cur_ops->poll_gp_state_full)) {
@@@@@@@ -1339,14 -1339,14 -1339,14 -1346,14 -1349,18 -1339,14 +1356,18 @@@@@@@ rcu_torture_writer(void *arg
        struct rcu_gp_oldstate cookie_full;
        int expediting = 0;
        unsigned long gp_snap;
++++ +  unsigned long gp_snap1;
        struct rcu_gp_oldstate gp_snap_full;
++++ +  struct rcu_gp_oldstate gp_snap1_full;
        int i;
        int idx;
        int oldnice = task_nice(current);
++++ +  struct rcu_gp_oldstate rgo[NUM_ACTIVE_RCU_POLL_FULL_OLDSTATE];
        struct rcu_torture *rp;
        struct rcu_torture *old_rp;
        static DEFINE_TORTURE_RANDOM(rand);
        bool stutter_waited;
++++ +  unsigned long ulo[NUM_ACTIVE_RCU_POLL_OLDSTATE];
      
        VERBOSE_TOROUT_STRING("rcu_torture_writer task started");
        if (!can_expedite)
                                break;
                        case RTWS_POLL_GET:
                                rcu_torture_writer_state = RTWS_POLL_GET;
++++ +                          for (i = 0; i < ARRAY_SIZE(ulo); i++)
++++ +                                  ulo[i] = cur_ops->get_comp_state();
                                gp_snap = cur_ops->start_gp_poll();
                                rcu_torture_writer_state = RTWS_POLL_WAIT;
---- -                          while (!cur_ops->poll_gp_state(gp_snap))
++++ +                          while (!cur_ops->poll_gp_state(gp_snap)) {
++++ +                                  gp_snap1 = cur_ops->get_gp_state();
++++ +                                  for (i = 0; i < ARRAY_SIZE(ulo); i++)
++++ +                                          if (cur_ops->poll_gp_state(ulo[i]) ||
++++ +                                              cur_ops->same_gp_state(ulo[i], gp_snap1)) {
++++ +                                                  ulo[i] = gp_snap1;
++++ +                                                  break;
++++ +                                          }
++++ +                                  WARN_ON_ONCE(i >= ARRAY_SIZE(ulo));
                                        torture_hrtimeout_jiffies(torture_random(&rand) % 16,
                                                                  &rand);
++++ +                          }
                                rcu_torture_pipe_update(old_rp);
                                break;
                        case RTWS_POLL_GET_FULL:
                                rcu_torture_writer_state = RTWS_POLL_GET_FULL;
++++ +                          for (i = 0; i < ARRAY_SIZE(rgo); i++)
++++ +                                  cur_ops->get_comp_state_full(&rgo[i]);
                                cur_ops->start_gp_poll_full(&gp_snap_full);
                                rcu_torture_writer_state = RTWS_POLL_WAIT_FULL;
---- -                          while (!cur_ops->poll_gp_state_full(&gp_snap_full))
++++ +                          while (!cur_ops->poll_gp_state_full(&gp_snap_full)) {
++++ +                                  cur_ops->get_gp_state_full(&gp_snap1_full);
++++ +                                  for (i = 0; i < ARRAY_SIZE(rgo); i++)
++++ +                                          if (cur_ops->poll_gp_state_full(&rgo[i]) ||
++++ +                                              cur_ops->same_gp_state_full(&rgo[i],
++++ +                                                                          &gp_snap1_full)) {
++++ +                                                  rgo[i] = gp_snap1_full;
++++ +                                                  break;
++++ +                                          }
++++ +                                  WARN_ON_ONCE(i >= ARRAY_SIZE(rgo));
                                        torture_hrtimeout_jiffies(torture_random(&rand) % 16,
                                                                  &rand);
++++ +                          }
                                rcu_torture_pipe_update(old_rp);
                                break;
                        case RTWS_POLL_GET_EXP:
@@@@@@@ -3388,13 -3388,13 -3388,13 -3395,13 -3425,13 -3388,13 +3432,13 @@@@@@@ static void rcu_test_debug_objects(void
        /* Try to queue the rh2 pair of callbacks for the same grace period. */
        preempt_disable(); /* Prevent preemption from interrupting test. */
        rcu_read_lock(); /* Make it impossible to finish a grace period. */
-- ---  call_rcu(&rh1, rcu_torture_leak_cb); /* Start grace period. */
++ +++  call_rcu_hurry(&rh1, rcu_torture_leak_cb); /* Start grace period. */
        local_irq_disable(); /* Make it harder to start a new grace period. */
-- ---  call_rcu(&rh2, rcu_torture_leak_cb);
-- ---  call_rcu(&rh2, rcu_torture_err_cb); /* Duplicate callback. */
++ +++  call_rcu_hurry(&rh2, rcu_torture_leak_cb);
++ +++  call_rcu_hurry(&rh2, rcu_torture_err_cb); /* Duplicate callback. */
        if (rhp) {
-- ---          call_rcu(rhp, rcu_torture_leak_cb);
-- ---          call_rcu(rhp, rcu_torture_err_cb); /* Another duplicate callback. */
++ +++          call_rcu_hurry(rhp, rcu_torture_leak_cb);
++ +++          call_rcu_hurry(rhp, rcu_torture_err_cb); /* Another duplicate callback. */
        }
        local_irq_enable();
        rcu_read_unlock();
diff --combined kernel/rcu/tree.c
index 6bb8e72bc8151ef2eb4093f2464f32c225672f88,1e1d333d07ffe89f437496bc64f32bbba1f65de6,4b68e50312d95a69666b2c7f5384ddd28e3d4b7f,6bb8e72bc8151ef2eb4093f2464f32c225672f88,6bb8e72bc8151ef2eb4093f2464f32c225672f88,6bb8e72bc8151ef2eb4093f2464f32c225672f88..d7764ffda6e522f7fe415b95c6f28bd206d91701
@@@@@@@ -301,12 -301,6 -301,12 -301,12 -301,12 -301,12 +301,6 @@@@@@@ static bool rcu_dynticks_in_eqs(int sna
        return !(snap & RCU_DYNTICKS_IDX);
      }
      
- ----/* Return true if the specified CPU is currently idle from an RCU viewpoint.  */
- ----bool rcu_is_idle_cpu(int cpu)
- ----{
- ----  return rcu_dynticks_in_eqs(rcu_dynticks_snap(cpu));
- ----}
- ----
      /*
       * Return true if the CPU corresponding to the specified rcu_data
       * structure has spent some time in an extended quiescent state since
@@@@@@@ -2106,7 -2100,7 -2106,7 -2106,7 -2106,7 -2106,7 +2100,7 @@@@@@@ int rcutree_dying_cpu(unsigned int cpu
        if (!IS_ENABLED(CONFIG_HOTPLUG_CPU))
                return 0;
      
- ----  blkd = !!(rnp->qsmask & rdp->grpmask);
+ ++++  blkd = !!(READ_ONCE(rnp->qsmask) & rdp->grpmask);
        trace_rcu_grace_period(rcu_state.name, READ_ONCE(rnp->gp_seq),
                               blkd ? TPS("cpuofl-bgp") : TPS("cpuofl"));
        return 0;
@@@@@@@ -2416,7 -2410,7 -2416,7 -2416,7 -2416,7 -2416,7 +2410,7 @@@@@@@ void rcu_force_quiescent_state(void
        struct rcu_node *rnp_old = NULL;
      
        /* Funnel through hierarchy to reduce memory contention. */
- ----  rnp = __this_cpu_read(rcu_data.mynode);
+ ++++  rnp = raw_cpu_read(rcu_data.mynode);
        for (; rnp != NULL; rnp = rnp->parent) {
                ret = (READ_ONCE(rcu_state.gp_flags) & RCU_GP_FLAG_FQS) ||
                       !raw_spin_trylock(&rnp->fqslock);
@@@@@@@ -2728,47 -2722,47 -2728,8 -2728,47 -2728,47 -2728,47 +2722,8 @@@@@@@ static void check_cb_ovld(struct rcu_da
        raw_spin_unlock_rcu_node(rnp);
      }
      
-- ---/**
-- --- * call_rcu() - Queue an RCU callback for invocation after a grace period.
-- --- * @head: structure to be used for queueing the RCU updates.
-- --- * @func: actual callback function to be invoked after the grace period
-- --- *
-- --- * The callback function will be invoked some time after a full grace
-- --- * period elapses, in other words after all pre-existing RCU read-side
-- --- * critical sections have completed.  However, the callback function
-- --- * might well execute concurrently with RCU read-side critical sections
-- --- * that started after call_rcu() was invoked.
-- --- *
-- --- * RCU read-side critical sections are delimited by rcu_read_lock()
-- --- * and rcu_read_unlock(), and may be nested.  In addition, but only in
-- --- * v5.0 and later, regions of code across which interrupts, preemption,
-- --- * or softirqs have been disabled also serve as RCU read-side critical
-- --- * sections.  This includes hardware interrupt handlers, softirq handlers,
-- --- * and NMI handlers.
-- --- *
-- --- * Note that all CPUs must agree that the grace period extended beyond
-- --- * all pre-existing RCU read-side critical section.  On systems with more
-- --- * than one CPU, this means that when "func()" is invoked, each CPU is
-- --- * guaranteed to have executed a full memory barrier since the end of its
-- --- * last RCU read-side critical section whose beginning preceded the call
-- --- * to call_rcu().  It also means that each CPU executing an RCU read-side
-- --- * critical section that continues beyond the start of "func()" must have
-- --- * executed a memory barrier after the call_rcu() but before the beginning
-- --- * of that RCU read-side critical section.  Note that these guarantees
-- --- * include CPUs that are offline, idle, or executing in user mode, as
-- --- * well as CPUs that are executing in the kernel.
-- --- *
-- --- * Furthermore, if CPU A invoked call_rcu() and CPU B invoked the
-- --- * resulting RCU callback function "func()", then both CPU A and CPU B are
-- --- * guaranteed to execute a full memory barrier during the time interval
-- --- * between the call to call_rcu() and the invocation of "func()" -- even
-- --- * if CPU A and CPU B are the same CPU (but again only if the system has
-- --- * more than one CPU).
-- --- *
-- --- * Implementation of these memory-ordering guarantees is described here:
-- --- * Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst.
-- --- */
-- ---void call_rcu(struct rcu_head *head, rcu_callback_t func)
++ +++static void
++ +++__call_rcu_common(struct rcu_head *head, rcu_callback_t func, bool lazy)
      {
        static atomic_t doublefrees;
        unsigned long flags;
        }
      
        check_cb_ovld(rdp);
-- ---  if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags))
++ +++  if (rcu_nocb_try_bypass(rdp, head, &was_alldone, flags, lazy))
                return; // Enqueued onto ->nocb_bypass, so just leave.
        // If no-CBs CPU gets here, rcu_nocb_try_bypass() acquired ->nocb_lock.
        rcu_segcblist_enqueue(&rdp->cblist, head);
                local_irq_restore(flags);
        }
      }
-- ---EXPORT_SYMBOL_GPL(call_rcu);
      
++ +++#ifdef CONFIG_RCU_LAZY
++ +++/**
++ +++ * call_rcu_hurry() - Queue RCU callback for invocation after grace period, and
++ +++ * flush all lazy callbacks (including the new one) to the main ->cblist while
++ +++ * doing so.
++ +++ *
++ +++ * @head: structure to be used for queueing the RCU updates.
++ +++ * @func: actual callback function to be invoked after the grace period
++ +++ *
++ +++ * The callback function will be invoked some time after a full grace
++ +++ * period elapses, in other words after all pre-existing RCU read-side
++ +++ * critical sections have completed.
++ +++ *
++ +++ * Use this API instead of call_rcu() if you don't want the callback to be
++ +++ * invoked after very long periods of time, which can happen on systems without
++ +++ * memory pressure and on systems which are lightly loaded or mostly idle.
++ +++ * This function will cause callbacks to be invoked sooner than later at the
++ +++ * expense of extra power. Other than that, this function is identical to, and
++ +++ * reuses call_rcu()'s logic. Refer to call_rcu() for more details about memory
++ +++ * ordering and other functionality.
++ +++ */
++ +++void call_rcu_hurry(struct rcu_head *head, rcu_callback_t func)
++ +++{
++ +++  return __call_rcu_common(head, func, false);
++ +++}
++ +++EXPORT_SYMBOL_GPL(call_rcu_hurry);
++ +++#endif
++ +++
++ +++/**
++ +++ * call_rcu() - Queue an RCU callback for invocation after a grace period.
++ +++ * By default the callbacks are 'lazy' and are kept hidden from the main
++ +++ * ->cblist to prevent starting of grace periods too soon.
++ +++ * If you desire grace periods to start very soon, use call_rcu_hurry().
++ +++ *
++ +++ * @head: structure to be used for queueing the RCU updates.
++ +++ * @func: actual callback function to be invoked after the grace period
++ +++ *
++ +++ * The callback function will be invoked some time after a full grace
++ +++ * period elapses, in other words after all pre-existing RCU read-side
++ +++ * critical sections have completed.  However, the callback function
++ +++ * might well execute concurrently with RCU read-side critical sections
++ +++ * that started after call_rcu() was invoked.
++ +++ *
++ +++ * RCU read-side critical sections are delimited by rcu_read_lock()
++ +++ * and rcu_read_unlock(), and may be nested.  In addition, but only in
++ +++ * v5.0 and later, regions of code across which interrupts, preemption,
++ +++ * or softirqs have been disabled also serve as RCU read-side critical
++ +++ * sections.  This includes hardware interrupt handlers, softirq handlers,
++ +++ * and NMI handlers.
++ +++ *
++ +++ * Note that all CPUs must agree that the grace period extended beyond
++ +++ * all pre-existing RCU read-side critical section.  On systems with more
++ +++ * than one CPU, this means that when "func()" is invoked, each CPU is
++ +++ * guaranteed to have executed a full memory barrier since the end of its
++ +++ * last RCU read-side critical section whose beginning preceded the call
++ +++ * to call_rcu().  It also means that each CPU executing an RCU read-side
++ +++ * critical section that continues beyond the start of "func()" must have
++ +++ * executed a memory barrier after the call_rcu() but before the beginning
++ +++ * of that RCU read-side critical section.  Note that these guarantees
++ +++ * include CPUs that are offline, idle, or executing in user mode, as
++ +++ * well as CPUs that are executing in the kernel.
++ +++ *
++ +++ * Furthermore, if CPU A invoked call_rcu() and CPU B invoked the
++ +++ * resulting RCU callback function "func()", then both CPU A and CPU B are
++ +++ * guaranteed to execute a full memory barrier during the time interval
++ +++ * between the call to call_rcu() and the invocation of "func()" -- even
++ +++ * if CPU A and CPU B are the same CPU (but again only if the system has
++ +++ * more than one CPU).
++ +++ *
++ +++ * Implementation of these memory-ordering guarantees is described here:
++ +++ * Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst.
++ +++ */
++ +++void call_rcu(struct rcu_head *head, rcu_callback_t func)
++ +++{
++ +++  return __call_rcu_common(head, func, IS_ENABLED(CONFIG_RCU_LAZY));
++ +++}
++ +++EXPORT_SYMBOL_GPL(call_rcu);
      
      /* Maximum number of jiffies to wait before draining a batch. */
      #define KFREE_DRAIN_JIFFIES (5 * HZ)
@@@@@@@ -3507,7 -3501,7 -3544,7 -3507,7 -3507,7 -3507,7 +3538,7 @@@@@@@ void synchronize_rcu(void
                if (rcu_gp_is_expedited())
                        synchronize_rcu_expedited();
                else
-- ---                  wait_rcu_gp(call_rcu);
++ +++                  wait_rcu_gp(call_rcu_hurry);
                return;
        }
      
@@@@@@@ -3894,6 -3888,6 -3931,8 -3894,6 -3894,6 -3894,6 +3925,8 @@@@@@@ static void rcu_barrier_entrain(struct 
      {
        unsigned long gseq = READ_ONCE(rcu_state.barrier_sequence);
        unsigned long lseq = READ_ONCE(rdp->barrier_seq_snap);
++ +++  bool wake_nocb = false;
++ +++  bool was_alldone = false;
      
        lockdep_assert_held(&rcu_state.barrier_lock);
        if (rcu_seq_state(lseq) || !rcu_seq_state(gseq) || rcu_seq_ctr(lseq) != rcu_seq_ctr(gseq))
        rdp->barrier_head.func = rcu_barrier_callback;
        debug_rcu_head_queue(&rdp->barrier_head);
        rcu_nocb_lock(rdp);
-- ---  WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies));
++ +++  /*
++ +++   * Flush bypass and wakeup rcuog if we add callbacks to an empty regular
++ +++   * queue. This way we don't wait for bypass timer that can reach seconds
++ +++   * if it's fully lazy.
++ +++   */
++ +++  was_alldone = rcu_rdp_is_offloaded(rdp) && !rcu_segcblist_pend_cbs(&rdp->cblist);
++ +++  WARN_ON_ONCE(!rcu_nocb_flush_bypass(rdp, NULL, jiffies, false));
++ +++  wake_nocb = was_alldone && rcu_segcblist_pend_cbs(&rdp->cblist);
        if (rcu_segcblist_entrain(&rdp->cblist, &rdp->barrier_head)) {
                atomic_inc(&rcu_state.barrier_cpu_count);
        } else {
                rcu_barrier_trace(TPS("IRQNQ"), -1, rcu_state.barrier_sequence);
        }
        rcu_nocb_unlock(rdp);
++ +++  if (wake_nocb)
++ +++          wake_nocb_gp(rdp, false);
        smp_store_release(&rdp->barrier_seq_snap, gseq);
      }
      
@@@@@@@ -4276,8 -4270,6 -4324,8 -4276,8 -4276,8 -4276,8 +4318,6 @@@@@@@ void rcu_report_dead(unsigned int cpu
        // Do any dangling deferred wakeups.
        do_nocb_deferred_wakeup(rdp);
      
- ----  /* QS for any half-done expedited grace period. */
- ----  rcu_report_exp_rdp(rdp);
        rcu_preempt_deferred_qs(current);
      
        /* Remove outgoing CPU from mask in the leaf rcu_node structure. */
@@@@@@@ -4325,7 -4317,7 -4373,7 -4325,7 -4325,7 -4325,7 +4365,7 @@@@@@@ void rcutree_migrate_callbacks(int cpu
        my_rdp = this_cpu_ptr(&rcu_data);
        my_rnp = my_rdp->mynode;
        rcu_nocb_lock(my_rdp); /* irqs already disabled. */
-- ---  WARN_ON_ONCE(!rcu_nocb_flush_bypass(my_rdp, NULL, jiffies));
++ +++  WARN_ON_ONCE(!rcu_nocb_flush_bypass(my_rdp, NULL, jiffies, false));
        raw_spin_lock_rcu_node(my_rnp); /* irqs already disabled. */
        /* Leverage recent GPs and set GP for new callbacks. */
        needwake = rcu_advance_cbs(my_rnp, rdp) ||
This page took 0.113568 seconds and 4 git commands to generate.