rcutorture: Include grace-period sequence numbers in failure/close-call
authorPaul E. McKenney <paulmck@kernel.org>
Thu, 14 Nov 2024 21:55:32 +0000 (13:55 -0800)
committerBoqun Feng <boqun.feng@gmail.com>
Wed, 5 Feb 2025 15:14:39 +0000 (07:14 -0800)
This commit includes the grace-period sequence numbers at the beginning
and end of each segment in the "Failure/close-call rcutorture reader
segments" list.  These are in hexadecimal, and only the bottom byte.
Currently, only RCU is supported, with its three sequence numbers (normal,
expedited, and polled).

Note that if all the grace-period sequence numbers remain the same across
a given reader segment, only one copy of the number will be printed.
Of course, if there is a change, both sets of values will be printed.

Because the overhead of collecting this information can suppress
heisenbugs, this information is collected and printed only in kernels
built with CONFIG_RCU_TORTURE_TEST_LOG_GP=y.

[ paulmck: Apply Nathan Chancellor feedback for IS_ENABLED(). ]
[ paulmck: Apply feedback from kernel test robot. ]

Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
Tested-by: kernel test robot <oliver.sang@intel.com>
Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
kernel/rcu/Kconfig.debug
kernel/rcu/rcu.h
kernel/rcu/rcutorture.c
kernel/rcu/tiny.c
kernel/rcu/tree.c

index 6af90510a1ca7882d476ef04ebc12ab737a6b7d8..25a9dc2be0dcdb2c0b1614b4061c1dc97edeb587 100644 (file)
@@ -84,6 +84,20 @@ config RCU_TORTURE_TEST_LOG_CPU
          Say Y here if you want CPU IDs logged.
          Say N if you are unsure.
 
+config RCU_TORTURE_TEST_LOG_GP
+       bool "Log grace-period numbers for rcutorture failures"
+       depends on RCU_TORTURE_TEST
+       default n
+       help
+         This option causes rcutorture to decorate each entry of its
+         log of failure/close-call rcutorture reader segments with the
+         corresponding grace-period sequence numbers.  This information
+         can be useful, but it does incur additional overhead, overhead
+         that can make both failures and close calls less probable.
+
+         Say Y here if you want grace-period sequence numbers logged.
+         Say N if you are unsure.
+
 config RCU_REF_SCALE_TEST
        tristate "Scalability tests for read-side synchronization (RCU and others)"
        depends on DEBUG_KERNEL
index feb3ac1dc5d59030db1131677dda08fa9caa41a7..a6098997a14ba2df9dd3e4fa0d1fe9500300180f 100644 (file)
@@ -590,6 +590,8 @@ void do_trace_rcu_torture_read(const char *rcutorturename,
 #endif
 static inline void rcu_gp_set_torture_wait(int duration) { }
 #endif
+unsigned long rcutorture_gather_gp_seqs(void);
+void rcutorture_format_gp_seqs(unsigned long seqs, char *cp);
 
 #ifdef CONFIG_TINY_SRCU
 
index fbf1d7fcf61d0f27ed02e7a3840a1679aedcd5e0..2113583cae34770583e98b183cfdec35ff6513ba 100644 (file)
@@ -273,6 +273,8 @@ struct rt_read_seg {
        bool rt_preempted;
        int rt_cpu;
        int rt_end_cpu;
+       unsigned long rt_gp_seq;
+       unsigned long rt_gp_seq_end;
 };
 static int err_segs_recorded;
 static struct rt_read_seg err_segs[RCUTORTURE_RDR_MAX_SEGS];
@@ -407,6 +409,8 @@ struct rcu_torture_ops {
        void (*gp_slow_register)(atomic_t *rgssp);
        void (*gp_slow_unregister)(atomic_t *rgssp);
        bool (*reader_blocked)(void);
+       unsigned long (*gather_gp_seqs)(void);
+       void (*format_gp_seqs)(unsigned long seqs, char *cp);
        long cbflood_max;
        int irq_capable;
        int can_boost;
@@ -611,6 +615,8 @@ static struct rcu_torture_ops rcu_ops = {
        .reader_blocked         = IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_CPU)
                                  ? has_rcu_reader_blocked
                                  : NULL,
+       .gather_gp_seqs         = rcutorture_gather_gp_seqs,
+       .format_gp_seqs         = rcutorture_format_gp_seqs,
        .irq_capable            = 1,
        .can_boost              = IS_ENABLED(CONFIG_RCU_BOOST),
        .extendables            = RCUTORTURE_MAX_EXTEND,
@@ -656,6 +662,8 @@ static struct rcu_torture_ops rcu_busted_ops = {
        .sync           = synchronize_rcu_busted,
        .exp_sync       = synchronize_rcu_busted,
        .call           = call_rcu_busted,
+       .gather_gp_seqs = rcutorture_gather_gp_seqs,
+       .format_gp_seqs = rcutorture_format_gp_seqs,
        .irq_capable    = 1,
        .extendables    = RCUTORTURE_MAX_EXTEND,
        .name           = "busted"
@@ -1978,6 +1986,12 @@ static void rcutorture_one_extend(int *readstate, int newstate, bool insoftirq,
                                rtrsp[-1].rt_preempted = cur_ops->reader_blocked();
                }
        }
+       // Sample grace-period sequence number, as good a place as any.
+       if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP) && cur_ops->gather_gp_seqs) {
+               rtrsp->rt_gp_seq = cur_ops->gather_gp_seqs();
+               if (!first)
+                       rtrsp[-1].rt_gp_seq_end = rtrsp->rt_gp_seq;
+       }
 
        /*
         * Next, remove old protection, in decreasing order of strength
@@ -3566,6 +3580,7 @@ rcu_torture_cleanup(void)
        int flags = 0;
        unsigned long gp_seq = 0;
        int i;
+       int j;
 
        if (torture_cleanup_begin()) {
                if (cur_ops->cb_barrier != NULL) {
@@ -3661,6 +3676,25 @@ rcu_torture_cleanup(void)
                                else
                                        pr_cont(" ...");
                        }
+                       if (IS_ENABLED(CONFIG_RCU_TORTURE_TEST_LOG_GP) &&
+                           cur_ops->gather_gp_seqs && cur_ops->format_gp_seqs) {
+                               char buf1[16+1];
+                               char buf2[16+1];
+                               char sepchar = '-';
+
+                               cur_ops->format_gp_seqs(err_segs[i].rt_gp_seq, buf1);
+                               cur_ops->format_gp_seqs(err_segs[i].rt_gp_seq_end, buf2);
+                               if (err_segs[i].rt_gp_seq == err_segs[i].rt_gp_seq_end) {
+                                       if (buf2[0]) {
+                                               for (j = 0; buf2[j]; j++)
+                                                       buf2[j] = '.';
+                                               if (j)
+                                                       buf2[j - 1] = ' ';
+                                       }
+                                       sepchar = ' ';
+                               }
+                               pr_cont(" %s%c%s", buf1, sepchar, buf2);
+                       }
                        if (err_segs[i].rt_delay_ms != 0) {
                                pr_cont(" %s%ldms", firsttime ? "" : "+",
                                        err_segs[i].rt_delay_ms);
index 4b3f319114650d372d490919669a2a9e248e731d..f9c4a24dc59c2ae7ef87467e93b1f26931c8f588 100644 (file)
@@ -257,6 +257,20 @@ void kvfree_call_rcu(struct rcu_head *head, void *ptr)
 EXPORT_SYMBOL_GPL(kvfree_call_rcu);
 #endif
 
+#if IS_ENABLED(CONFIG_RCU_TORTURE_TEST)
+unsigned long rcutorture_gather_gp_seqs(void)
+{
+       return READ_ONCE(rcu_ctrlblk.gp_seq) & 0xff;
+}
+EXPORT_SYMBOL_GPL(rcutorture_gather_gp_seqs);
+
+void rcutorture_format_gp_seqs(unsigned long seqs, char *cp)
+{
+       snprintf(cp, 8, "g%02lx", seqs & 0xff);
+}
+EXPORT_SYMBOL_GPL(rcutorture_format_gp_seqs);
+#endif
+
 void __init rcu_init(void)
 {
        open_softirq(RCU_SOFTIRQ, rcu_process_callbacks);
index 475f31deed1418d73d097bb4d39fe714814fefa3..e40c4b5c3267a4b04871a6b02f5bef5d02351e62 100644 (file)
@@ -538,6 +538,26 @@ void rcutorture_get_gp_data(int *flags, unsigned long *gp_seq)
 }
 EXPORT_SYMBOL_GPL(rcutorture_get_gp_data);
 
+/* Gather grace-period sequence numbers for rcutorture diagnostics. */
+unsigned long rcutorture_gather_gp_seqs(void)
+{
+       return ((READ_ONCE(rcu_state.gp_seq) & 0xff) << 16) |
+              ((READ_ONCE(rcu_state.expedited_sequence) & 0xff) << 8) |
+              (READ_ONCE(rcu_state.gp_seq_polled) & 0xff);
+}
+EXPORT_SYMBOL_GPL(rcutorture_gather_gp_seqs);
+
+/* Format grace-period sequence numbers for rcutorture diagnostics. */
+void rcutorture_format_gp_seqs(unsigned long seqs, char *cp)
+{
+       unsigned int egp = (seqs >> 8) & 0xff;
+       unsigned int ggp = (seqs >> 16) & 0xff;
+       unsigned int pgp = seqs & 0xff;
+
+       snprintf(cp, 16, "g%02x:e%02x:p%02x", ggp, egp, pgp);
+}
+EXPORT_SYMBOL_GPL(rcutorture_format_gp_seqs);
+
 #if defined(CONFIG_NO_HZ_FULL) && (!defined(CONFIG_GENERIC_ENTRY) || !defined(CONFIG_KVM_XFER_TO_GUEST_WORK))
 /*
  * An empty function that will trigger a reschedule on