On 5 October 2026 01:56:41 BST, Joel Fernandes <[email protected]>
wrote:
>The writer kthreads keep their state in five parallel arrays indexed by
>writer number: writer_tasks, writer_durations, writer_n_durations,
>writer_done and writer_freelists.  Each must be allocated, NULL-checked,
>freed and cleared separately, and the cleanup path needs several
>guards to cope with any one of them being missing.
>
>Collapse them into a single array of struct writer_state, holding the
>task pointer, the duration buffer, the measurement count, the done flag
>and the existing struct writer_freelist.
>
>This reduces initialization to one allocation and one out-of-memory
>check, and cleanup to a single kfree().  As a side effect, this also
>fixes a leak: cleanup only ran its frees under "if (writer_tasks)", so
>if the writer_tasks allocation failed while a later allocation (for
>example writer_durations or writer_n_durations) still succeeded, those
>already-allocated arrays were never freed.  With a single allocation,
>that partial-allocation state can no longer occur.

All I've gotta say here, is one huge thank you, this cleanup WASS needed.

Reviewed-by: Bradley Morgan <[email protected]>

Also I ran basic tests:

Tested-by: Bradley Morgan <[email protected]> # Power10 

>
>Assisted-by: Codex
>Fixes: 8704baab9bc8 ("rcutorture: Add RCU grace-period performance tests")
>Signed-off-by: Joel Fernandes <[email protected]>
>---
> kernel/rcu/rcuscale.c | 127 ++++++++++++++++++------------------------
> 1 file changed, 55 insertions(+), 72 deletions(-)
>
>diff --git a/kernel/rcu/rcuscale.c b/kernel/rcu/rcuscale.c
>index 1097ec15879c..f0bca5dcc754 100644
>--- a/kernel/rcu/rcuscale.c
>+++ b/kernel/rcu/rcuscale.c
>@@ -115,17 +115,21 @@ struct writer_freelist {
>       struct writer_mblock *ws_mblocks;
> };
> 
>+struct writer_state {
>+      struct writer_freelist wst_fl;
>+      struct task_struct *wst_task;
>+      u64 *wst_durations;
>+      int wst_n_durations;
>+      bool wst_done;
>+};
>+
> static int nrealreaders;
> static int nrealwriters;
> static int nrealexp;
>-static struct task_struct **writer_tasks;
> static struct task_struct **reader_tasks;
> static struct task_struct **exp_tasks;
> 
>-static u64 **writer_durations;
>-static bool *writer_done;
>-static struct writer_freelist *writer_freelists;
>-static int *writer_n_durations;
>+static struct writer_state *writer_states;
> static atomic_t n_rcu_scale_reader_started;
> static atomic_t n_rcu_scale_writer_started;
> static atomic_t n_rcu_scale_writer_finished;
>@@ -504,9 +508,9 @@ static struct writer_mblock *rcu_scale_alloc(long me)
>       struct writer_freelist *wflp;
>       struct writer_mblock *wmbp;
> 
>-      if (WARN_ON_ONCE(!writer_freelists))
>+      if (WARN_ON_ONCE(!writer_states))
>               return NULL;
>-      wflp = &writer_freelists[me];
>+      wflp = &writer_states[me].wst_fl;
>       if (llist_empty(&wflp->ws_lhp)) {
>               // ->ws_lhp is private to its rcu_scale_writer task.
>               wmbp = container_of(llist_del_all(&wflp->ws_lhg), struct 
> writer_mblock, wmb_node);
>@@ -560,8 +564,9 @@ rcu_scale_writer(void *arg)
>       u64 t;
>       DEFINE_TORTURE_RANDOM(tr);
>       u64 *wdp;
>-      u64 *wdpp = writer_durations[me];
>-      struct writer_freelist *wflp = &writer_freelists[me];
>+      struct writer_state *wsp = &writer_states[me];
>+      u64 *wdpp = wsp->wst_durations;
>+      struct writer_freelist *wflp = &wsp->wst_fl;
>       struct writer_mblock *wmbp = NULL;
> 
>       VERBOSE_SCALEOUT_STRING("rcu_scale_writer task started");
>@@ -631,7 +636,7 @@ rcu_scale_writer(void *arg)
>                       started = true;
>               if (!done && i >= MIN_MEAS && time_after(jiffies, jdone)) {
>                       done = true;
>-                      WRITE_ONCE(writer_done[me], true);
>+                      WRITE_ONCE(wsp->wst_done, true);
>                       sched_set_normal(current, 0);
>                       pr_alert("%s%s rcu_scale_writer %ld has %d 
> measurements\n",
>                                scale_type, SCALE_FLAG, me, MIN_MEAS);
>@@ -649,7 +654,7 @@ rcu_scale_writer(void *arg)
>                                               cur_ops->get_gp_seq();
>                               }
>                               if (shutdown_secs) {
>-                                      writer_tasks[me] = NULL;
>+                                      wsp->wst_task = NULL;
>                                       smp_mb(); /* Assign before wake. */
>                                       rcu_scale_cleanup();
>                                       kernel_power_off();
>@@ -665,10 +670,10 @@ rcu_scale_writer(void *arg)
> 
>                       if (!atomic_xchg(&dumped, 1)) {
>                               for (i = 0; i < nrealwriters; i++) {
>-                                      if (writer_done[i])
>+                                      if (writer_states[i].wst_done)
>                                               continue;
>                                       pr_info("%s: Task %ld flags writer 
> %d:\n", __func__, me, i);
>-                                      sched_show_task(writer_tasks[i]);
>+                                      
>sched_show_task(writer_states[i].wst_task);
>                               }
>                               if (cur_ops->stats)
>                                       cur_ops->stats();
>@@ -676,7 +681,7 @@ rcu_scale_writer(void *arg)
>               }
>               if (!selfreport && time_after(jiffies, jdone + HZ * (70 + me))) 
> {
>                       pr_info("%s: Writer %ld self-report: started %d done 
> %d/%d->%d i %d jdone %lu.\n",
>-                              __func__, me, started, done, writer_done[me], 
>atomic_read(&n_rcu_scale_writer_finished), i, jiffies - jdone);
>+                              __func__, me, started, done, wsp->wst_done, 
>atomic_read(&n_rcu_scale_writer_finished), i, jiffies - jdone);
>                       selfreport = true;
>               }
>               if (gp_succeeded && started && !alldone && i < MAX_MEAS - 1)
>@@ -687,7 +692,7 @@ rcu_scale_writer(void *arg)
>               rcu_scale_free(wmbp);
>               cur_ops->gp_barrier();
>       }
>-      writer_n_durations[me] = i_max + 1;
>+      wsp->wst_n_durations = i_max + 1;
>       torture_kthread_stopping("rcu_scale_writer");
>       return 0;
> }
>@@ -1031,13 +1036,11 @@ rcu_scale_cleanup(void)
>               reader_tasks = NULL;
>       }
> 
>-      if (writer_tasks) {
>+      if (writer_states) {
>               for (i = 0; i < nrealwriters; i++) {
>                       torture_stop_kthread(rcu_scale_writer,
>-                                           writer_tasks[i]);
>-                      if (!writer_n_durations)
>-                              continue;
>-                      j = writer_n_durations[i];
>+                                           writer_states[i].wst_task);
>+                      j = writer_states[i].wst_n_durations;
>                       pr_alert("%s%s writer %d gps: %d\n",
>                                scale_type, SCALE_FLAG, i, j);
>                       ngps += j;
>@@ -1051,14 +1054,12 @@ rcu_scale_cleanup(void)
>                        rcuscale_seq_diff(b_rcu_gp_test_finished,
>                                          b_rcu_gp_test_started));
>               for (i = 0; i < nrealwriters; i++) {
>-                      if (!writer_durations)
>-                              break;
>-                      if (!writer_n_durations)
>-                              continue;
>-                      wdpp = writer_durations[i];
>+                      struct writer_state *wsp = &writer_states[i];
>+
>+                      wdpp = wsp->wst_durations;
>                       if (!wdpp)
>                               continue;
>-                      for (j = 0; j < writer_n_durations[i]; j++) {
>+                      for (j = 0; j < wsp->wst_n_durations; j++) {
>                               wdp = &wdpp[j];
>                               pr_alert("%s%s %4d writer-duration: %5d %llu\n",
>                                       scale_type, SCALE_FLAG,
>@@ -1066,34 +1067,23 @@ rcu_scale_cleanup(void)
>                               if (j % 100 == 0)
>                                       schedule_timeout_uninterruptible(1);
>                       }
>-                      kfree(writer_durations[i]);
>-                      if (writer_freelists) {
>+                      kfree(wdpp);
>+                      if (wsp->wst_fl.ws_mblocks) {
>                               int ctr = 0;
>                               struct llist_node *llnp;
>-                              struct writer_freelist *wflp = 
>&writer_freelists[i];
> 
>-                              if (wflp->ws_mblocks) {
>-                                      llist_for_each(llnp, wflp->ws_lhg.first)
>-                                              ctr++;
>-                                      llist_for_each(llnp, wflp->ws_lhp.first)
>-                                              ctr++;
>-                                      WARN_ONCE(ctr != gp_async_max,
>-                                                "%s: ctr = %d gp_async_max = 
>%d\n",
>-                                                __func__, ctr, gp_async_max);
>-                                      kfree(wflp->ws_mblocks);
>-                              }
>+                              llist_for_each(llnp, wsp->wst_fl.ws_lhg.first)
>+                                      ctr++;
>+                              llist_for_each(llnp, wsp->wst_fl.ws_lhp.first)
>+                                      ctr++;
>+                              WARN_ONCE(ctr != gp_async_max,
>+                                        "%s: ctr = %d gp_async_max = %d\n",
>+                                        __func__, ctr, gp_async_max);
>+                              kfree(wsp->wst_fl.ws_mblocks);
>                       }
>               }
>-              kfree(writer_tasks);
>-              writer_tasks = NULL;
>-              kfree(writer_durations);
>-              writer_durations = NULL;
>-              kfree(writer_n_durations);
>-              writer_n_durations = NULL;
>-              kfree(writer_done);
>-              writer_done = NULL;
>-              kfree(writer_freelists);
>-              writer_freelists = NULL;
>+              kfree(writer_states);
>+              writer_states = NULL;
>       }
> 
>       /* Do torture-type-specific cleanup operations.  */
>@@ -1187,37 +1177,30 @@ rcu_scale_init(void)
>                               goto unwind;
>               }
>       }
>-      writer_tasks = kzalloc_objs(writer_tasks[0], nrealwriters);
>-      writer_durations = kcalloc(nrealwriters, sizeof(*writer_durations), 
>GFP_KERNEL);
>-      writer_n_durations = kzalloc_objs(*writer_n_durations, nrealwriters);
>-      writer_done = kzalloc_objs(writer_done[0], nrealwriters);
>-      if (gp_async) {
>-              if (gp_async_max <= 0) {
>-                      pr_warn("%s: gp_async_max = %d must be greater than 
>zero.\n",
>-                              __func__, gp_async_max);
>-                      WARN_ON_ONCE(IS_BUILTIN(CONFIG_RCU_TORTURE_TEST));
>-                      firsterr = -EINVAL;
>-                      goto unwind;
>-              }
>-              writer_freelists = kzalloc_objs(writer_freelists[0],
>-                                              nrealwriters);
>+      writer_states = kzalloc_objs(writer_states[0], nrealwriters);
>+      if (gp_async && gp_async_max <= 0) {
>+              pr_warn("%s: gp_async_max = %d must be greater than zero.\n",
>+                      __func__, gp_async_max);
>+              WARN_ON_ONCE(IS_BUILTIN(CONFIG_RCU_TORTURE_TEST));
>+              firsterr = -EINVAL;
>+              goto unwind;
>       }
>-      if (!writer_tasks || !writer_durations || !writer_n_durations || 
>!writer_done ||
>-          (gp_async && !writer_freelists)) {
>+      if (!writer_states) {
>               SCALEOUT_ERRSTRING("out of memory");
>               firsterr = -ENOMEM;
>               goto unwind;
>       }
>       for (i = 0; i < nrealwriters; i++) {
>-              writer_durations[i] =
>-                      kcalloc(MAX_MEAS, sizeof(*writer_durations[i]),
>-                              GFP_KERNEL);
>-              if (!writer_durations[i]) {
>+              struct writer_state *wsp = &writer_states[i];
>+
>+              wsp->wst_durations = kcalloc(MAX_MEAS, 
>sizeof(*wsp->wst_durations),
>+                                           GFP_KERNEL);
>+              if (!wsp->wst_durations) {
>                       firsterr = -ENOMEM;
>                       goto unwind;
>               }
>-              if (writer_freelists) {
>-                      struct writer_freelist *wflp = &writer_freelists[i];
>+              if (gp_async) {
>+                      struct writer_freelist *wflp = &wsp->wst_fl;
> 
>                       init_llist_head(&wflp->ws_lhg);
>                       init_llist_head(&wflp->ws_lhp);
>@@ -1235,7 +1218,7 @@ rcu_scale_init(void)
>                       }
>               }
>               firsterr = torture_create_kthread(rcu_scale_writer, (void *)i,
>-                                                writer_tasks[i]);
>+                                                wsp->wst_task);
>               if (torture_init_error(firsterr))
>                       goto unwind;
>       }
>

--- Thanks!
"I'm not a very positive person" - Linus torvalds

Reply via email to