The rcu_do_batch() function extracts the ready-to-invoke callbacks from the rcu_segcblist located in the ->cblist field of the current CPU's rcu_data structure. These callbacks are first moved to a local (unsegmented) rcu_cblist. The rcu_do_batch() function then uses this rcu_cblist's ->len field to count how many CBs it has invoked, but it does so by counting that field down from zero. Finally, this function negates the value in this ->len field (resulting in a positive number) and subtracts the result from the ->len field of the current CPU's ->cblist field. Except that it is sometimes necessary for rcu_do_batch() to stop invoking callbacks mid-stream, despite there being more ready to invoke, for example, if a high-priority task wakes up. In this case the remaining not-yet-invoked callbacks are requeued back onto the CPU's ->cblist, but remain in the ready-to-invoke segment of that list. As above, the negative of the local rcu_cblist's ->len field is still subtracted from the ->len field of the current CPU's ->cblist field. The design of counting down from 0 is confusing and error-prone, plus use of a positive count will make it easier to provide a uniform and consistent API to deal with the per-segment counts that are added later in this series. For example, rcu_segcblist_extract_done_cbs() can unconditionally populate the resulting unsegmented list's ->len field during extraction. This commit therefore explicitly counts how many callbacks were executed in rcu_do_batch() itself, counting up from zero, and then uses that to update the per-CPU segcb list's ->len field, without relying on the downcounting of rcl->len from zero.
Unified · Split
| @@ -95,7 +95,7 @@ static void rcu_segcblist_set_len(struct rcu_segcblist *rsclp, long v) · 94 unmodified lines | ||
| 95 | 95 | * This increase is fully ordered with respect to the callers accesses |
| 96 | 96 | * both before and after. |
| 97 | 97 | */ |
| 98 | −static void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v) | |
| 98 | +void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v) | |
| 99 | 99 | { |
| 100 | 100 | #ifdef CONFIG_RCU_NOCB_CPU |
| 101 | 101 | smp_mb__before_atomic(); /* Up to the caller! */ |
| @@ -76,6 +76,7 @@ static inline bool rcu_segcblist_restempty(struct rcu_segcblist *rsclp, int seg) · 75 unmodified lines | ||
| 76 | 76 | } |
| 77 | 77 | |
| 78 | 78 | void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp); |
| 79 | +void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v); | |
| 79 | 80 | void rcu_segcblist_init(struct rcu_segcblist *rsclp); |
| 80 | 81 | void rcu_segcblist_disable(struct rcu_segcblist *rsclp); |
| 81 | 82 | void rcu_segcblist_offload(struct rcu_segcblist *rsclp); |
| @@ -2434,7 +2434,7 @@ static void rcu_do_batch(struct rcu_data *rdp) · 2433 unmodified lines | ||
| 2434 | 2434 | const bool offloaded = rcu_segcblist_is_offloaded(&rdp->cblist); |
| 2435 | 2435 | struct rcu_head *rhp; |
| 2436 | 2436 | struct rcu_cblist rcl = RCU_CBLIST_INITIALIZER(rcl); |
| 2437 | − long bl, count; | |
| 2437 | + long bl, count = 0; | |
| 2438 | 2438 | long pending, tlimit = 0; |
| 2439 | 2439 | |
| 2440 | 2440 | /* If no callbacks are ready, just return. */ |
| @@ -2479,6 +2479,7 @@ static void rcu_do_batch(struct rcu_data *rdp) · 38 unmodified lines | ||
| 2479 | 2479 | for (; rhp; rhp = rcu_cblist_dequeue(&rcl)) { |
| 2480 | 2480 | rcu_callback_t f; |
| 2481 | 2481 | |
| 2482 | + count++; | |
| 2482 | 2483 | debug_rcu_head_unqueue(rhp); |
| 2483 | 2484 | |
| 2484 | 2485 | rcu_lock_acquire(&rcu_callback_map); |
| @@ -2492,15 +2493,14 @@ static void rcu_do_batch(struct rcu_data *rdp) · 7 unmodified lines | ||
| 2492 | 2493 | |
| 2493 | 2494 | /* |
| 2494 | 2495 | * Stop only if limit reached and CPU has something to do. |
| 2495 | − * Note: The rcl structure counts down from zero. | |
| 2496 | 2496 | */ |
| 2497 | − if (-rcl.len >= bl && !offloaded && | |
| 2497 | + if (count >= bl && !offloaded && | |
| 2498 | 2498 | (need_resched() || |
| 2499 | 2499 | (!is_idle_task(current) && !rcu_is_callbacks_kthread()))) |
| 2500 | 2500 | break; |
| 2501 | 2501 | if (unlikely(tlimit)) { |
| 2502 | 2502 | /* only call local_clock() every 32 callbacks */ |
| 2503 | − if (likely((-rcl.len & 31) || local_clock() < tlimit)) | |
| 2503 | + if (likely((count & 31) || local_clock() < tlimit)) | |
| 2504 | 2504 | continue; |
| 2505 | 2505 | /* Exceeded the time limit, so leave. */ |
| 2506 | 2506 | break; |
| @@ -2517,7 +2517,6 @@ static void rcu_do_batch(struct rcu_data *rdp) · 10 unmodified lines | ||
| 2517 | 2517 | |
| 2518 | 2518 | local_irq_save(flags); |
| 2519 | 2519 | rcu_nocb_lock(rdp); |
| 2520 | − count = -rcl.len; | |
| 2521 | 2520 | rdp->n_cbs_invoked += count; |
| 2522 | 2521 | trace_rcu_batch_end(rcu_state.name, count, !!rcl.head, need_resched(), |
| 2523 | 2522 | is_idle_task(current), rcu_is_callbacks_kthread()); |
| @@ -2525,7 +2524,7 @@ static void rcu_do_batch(struct rcu_data *rdp) · 1 unmodified line | ||
| 2525 | 2524 | /* Update counts and requeue any remaining callbacks. */ |
| 2526 | 2525 | rcu_segcblist_insert_done_cbs(&rdp->cblist, &rcl); |
| 2527 | 2526 | smp_mb(); /* List handling before counting for rcu_barrier(). */ |
| 2528 | − rcu_segcblist_insert_count(&rdp->cblist, &rcl); | |
| 2527 | + rcu_segcblist_add_len(&rdp->cblist, -count); | |
| 2529 | 2528 | |
| 2530 | 2529 | /* Reinstate batch limit if we have worked down the excess. */ |
| 2531 | 2530 | count = rcu_segcblist_n_cbs(&rdp->cblist); |