rcu: Make tiny RCU support leak callbacks for debug-object errors
authorZqiang <qiang1.zhang@intel.com>
Fri, 1 Jul 2022 02:44:04 +0000 (10:44 +0800)
committerPaul E. McKenney <paulmck@kernel.org>
Wed, 31 Aug 2022 12:03:14 +0000 (05:03 -0700)
Currently, only Tree RCU leaks callbacks setting when it detects a
duplicate call_rcu().  This commit causes Tiny RCU to also leak
callbacks in this situation.

Because this is Tiny RCU, kernel size is important:

1. CONFIG_TINY_RCU=y and CONFIG_DEBUG_OBJECTS_RCU_HEAD=n
   (Production kernel)

    Original:
    text      data      bss       dec       hex     filename
    26290663  20159823  15212544  61663030  3ace736 vmlinux

    With this commit:
    text      data      bss       dec       hex     filename
    26290663  20159823  15212544  61663030  3ace736 vmlinux

2. CONFIG_TINY_RCU=y and CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
   (Debugging kernel)

    Original:
    text      data      bss       dec       hex     filename
    26291319  20160143  15212544  61664006  3aceb06 vmlinux

    With this commit:
    text      data      bss       dec       hex     filename
    26291319  20160431  15212544  61664294  3acec26 vmlinux

These results show that the kernel size is unchanged for production
kernels, as desired.

Signed-off-by: Zqiang <qiang1.zhang@intel.com>
Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
kernel/rcu/tiny.c

index f0561ee..943d431 100644 (file)
@@ -158,6 +158,10 @@ void synchronize_rcu(void)
 }
 EXPORT_SYMBOL_GPL(synchronize_rcu);
 
+static void tiny_rcu_leak_callback(struct rcu_head *rhp)
+{
+}
+
 /*
  * Post an RCU callback to be invoked after the end of an RCU grace
  * period.  But since we have but one CPU, that would be after any
@@ -165,9 +169,20 @@ EXPORT_SYMBOL_GPL(synchronize_rcu);
  */
 void call_rcu(struct rcu_head *head, rcu_callback_t func)
 {
+       static atomic_t doublefrees;
        unsigned long flags;
 
-       debug_rcu_head_queue(head);
+       if (debug_rcu_head_queue(head)) {
+               if (atomic_inc_return(&doublefrees) < 4) {
+                       pr_err("%s(): Double-freed CB %p->%pS()!!!  ", __func__, head, head->func);
+                       mem_dump_obj(head);
+               }
+
+               if (!__is_kvfree_rcu_offset((unsigned long)head->func))
+                       WRITE_ONCE(head->func, tiny_rcu_leak_callback);
+               return;
+       }
+
        head->func = func;
        head->next = NULL;