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.
Showing the changes to kernel (3 of 3 files). Show all
Unified · Split
| @@ -95,7 +95,7 @@ static void rcu_segcblist_set_len(struct rcu_segcblist *rsclp, long v) · 94 unmodified lines | |||
| 95 | * This increase is fully ordered with respect to the callers accesses | 95 | * This increase is fully ordered with respect to the callers accesses |
| 96 | * both before and after. | 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 | #ifdef CONFIG_RCU_NOCB_CPU | 100 | #ifdef CONFIG_RCU_NOCB_CPU |
| 101 | smp_mb__before_atomic(); /* Up to the caller! */ | 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 | void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp); | 78 | void rcu_segcblist_inc_len(struct rcu_segcblist *rsclp); |
| 79 | void rcu_segcblist_add_len(struct rcu_segcblist *rsclp, long v); | ||
| 79 | void rcu_segcblist_init(struct rcu_segcblist *rsclp); | 80 | void rcu_segcblist_init(struct rcu_segcblist *rsclp); |
| 80 | void rcu_segcblist_disable(struct rcu_segcblist *rsclp); | 81 | void rcu_segcblist_disable(struct rcu_segcblist *rsclp); |
| 81 | void rcu_segcblist_offload(struct rcu_segcblist *rsclp); | 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 | const bool offloaded = rcu_segcblist_is_offloaded(&rdp->cblist); | 2434 | const bool offloaded = rcu_segcblist_is_offloaded(&rdp->cblist); |
| 2435 | struct rcu_head *rhp; | 2435 | struct rcu_head *rhp; |
| 2436 | struct rcu_cblist rcl = RCU_CBLIST_INITIALIZER(rcl); | 2436 | struct rcu_cblist rcl = RCU_CBLIST_INITIALIZER(rcl); |
| 2437 | long bl, count; | 2437 | long bl, count = 0; |
| 2438 | long pending, tlimit = 0; | 2438 | long pending, tlimit = 0; |
| 2439 | 2439 | ||
| 2440 | /* If no callbacks are ready, just return. */ | 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 | for (; rhp; rhp = rcu_cblist_dequeue(&rcl)) { | 2479 | for (; rhp; rhp = rcu_cblist_dequeue(&rcl)) { |
| 2480 | rcu_callback_t f; | 2480 | rcu_callback_t f; |
| 2481 | 2481 | ||
| 2482 | count++; | ||
| 2482 | debug_rcu_head_unqueue(rhp); | 2483 | debug_rcu_head_unqueue(rhp); |
| 2483 | 2484 | ||
| 2484 | rcu_lock_acquire(&rcu_callback_map); | 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 | * Stop only if limit reached and CPU has something to do. | 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 | (need_resched() || | 2498 | (need_resched() || |
| 2499 | (!is_idle_task(current) && !rcu_is_callbacks_kthread()))) | 2499 | (!is_idle_task(current) && !rcu_is_callbacks_kthread()))) |
| 2500 | break; | 2500 | break; |
| 2501 | if (unlikely(tlimit)) { | 2501 | if (unlikely(tlimit)) { |
| 2502 | /* only call local_clock() every 32 callbacks */ | 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 | continue; | 2504 | continue; |
| 2505 | /* Exceeded the time limit, so leave. */ | 2505 | /* Exceeded the time limit, so leave. */ |
| 2506 | break; | 2506 | break; |
| @@ -2517,7 +2517,6 @@ static void rcu_do_batch(struct rcu_data *rdp) · 10 unmodified lines | |||
| 2517 | 2517 | ||
| 2518 | local_irq_save(flags); | 2518 | local_irq_save(flags); |
| 2519 | rcu_nocb_lock(rdp); | 2519 | rcu_nocb_lock(rdp); |
| 2520 | count = -rcl.len; | ||
| 2521 | rdp->n_cbs_invoked += count; | 2520 | rdp->n_cbs_invoked += count; |
| 2522 | trace_rcu_batch_end(rcu_state.name, count, !!rcl.head, need_resched(), | 2521 | trace_rcu_batch_end(rcu_state.name, count, !!rcl.head, need_resched(), |
| 2523 | is_idle_task(current), rcu_is_callbacks_kthread()); | 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 | /* Update counts and requeue any remaining callbacks. */ | 2524 | /* Update counts and requeue any remaining callbacks. */ |
| 2526 | rcu_segcblist_insert_done_cbs(&rdp->cblist, &rcl); | 2525 | rcu_segcblist_insert_done_cbs(&rdp->cblist, &rcl); |
| 2527 | smp_mb(); /* List handling before counting for rcu_barrier(). */ | 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 | /* Reinstate batch limit if we have worked down the excess. */ | 2529 | /* Reinstate batch limit if we have worked down the excess. */ |
| 2531 | count = rcu_segcblist_n_cbs(&rdp->cblist); | 2530 | count = rcu_segcblist_n_cbs(&rdp->cblist); |