diff options
author | Paul E. McKenney <paul.mckenney@linaro.org> | 2012-01-06 17:11:30 -0500 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2012-02-21 12:03:25 -0500 |
commit | 486e259340fc4c60474f2c14703e3b3634bb58ca (patch) | |
tree | 70a58702194588fa0773463523f72c682785d040 /kernel/rcutree.c | |
parent | 0bb7b59d6e2b8440cd7097097dd4bbfc4d76ed07 (diff) |
rcu: Avoid waking up CPUs having only kfree_rcu() callbacks
When CONFIG_RCU_FAST_NO_HZ is enabled, RCU will allow a given CPU to
enter dyntick-idle mode even if it still has RCU callbacks queued.
RCU avoids system hangs in this case by scheduling a timer for several
jiffies in the future. However, if all of the callbacks on that CPU
are from kfree_rcu(), there is no reason to wake the CPU up, as it is
not a problem to defer freeing of memory.
This commit therefore tracks the number of callbacks on a given CPU
that are from kfree_rcu(), and avoids scheduling the timer if all of
a given CPU's callbacks are from kfree_rcu().
Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'kernel/rcutree.c')
-rw-r--r-- | kernel/rcutree.c | 29 |
1 files changed, 18 insertions, 11 deletions
diff --git a/kernel/rcutree.c b/kernel/rcutree.c index 570f7530f4b3..acf2d67ad2f4 100644 --- a/kernel/rcutree.c +++ b/kernel/rcutree.c | |||
@@ -1261,6 +1261,7 @@ static void rcu_send_cbs_to_online(struct rcu_state *rsp) | |||
1261 | 1261 | ||
1262 | *receive_rdp->nxttail[RCU_NEXT_TAIL] = rdp->nxtlist; | 1262 | *receive_rdp->nxttail[RCU_NEXT_TAIL] = rdp->nxtlist; |
1263 | receive_rdp->nxttail[RCU_NEXT_TAIL] = rdp->nxttail[RCU_NEXT_TAIL]; | 1263 | receive_rdp->nxttail[RCU_NEXT_TAIL] = rdp->nxttail[RCU_NEXT_TAIL]; |
1264 | receive_rdp->qlen_lazy += rdp->qlen_lazy; | ||
1264 | receive_rdp->qlen += rdp->qlen; | 1265 | receive_rdp->qlen += rdp->qlen; |
1265 | receive_rdp->n_cbs_adopted += rdp->qlen; | 1266 | receive_rdp->n_cbs_adopted += rdp->qlen; |
1266 | rdp->n_cbs_orphaned += rdp->qlen; | 1267 | rdp->n_cbs_orphaned += rdp->qlen; |
@@ -1268,6 +1269,7 @@ static void rcu_send_cbs_to_online(struct rcu_state *rsp) | |||
1268 | rdp->nxtlist = NULL; | 1269 | rdp->nxtlist = NULL; |
1269 | for (i = 0; i < RCU_NEXT_SIZE; i++) | 1270 | for (i = 0; i < RCU_NEXT_SIZE; i++) |
1270 | rdp->nxttail[i] = &rdp->nxtlist; | 1271 | rdp->nxttail[i] = &rdp->nxtlist; |
1272 | rdp->qlen_lazy = 0; | ||
1271 | rdp->qlen = 0; | 1273 | rdp->qlen = 0; |
1272 | } | 1274 | } |
1273 | 1275 | ||
@@ -1368,11 +1370,11 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) | |||
1368 | { | 1370 | { |
1369 | unsigned long flags; | 1371 | unsigned long flags; |
1370 | struct rcu_head *next, *list, **tail; | 1372 | struct rcu_head *next, *list, **tail; |
1371 | int bl, count; | 1373 | int bl, count, count_lazy; |
1372 | 1374 | ||
1373 | /* If no callbacks are ready, just return.*/ | 1375 | /* If no callbacks are ready, just return.*/ |
1374 | if (!cpu_has_callbacks_ready_to_invoke(rdp)) { | 1376 | if (!cpu_has_callbacks_ready_to_invoke(rdp)) { |
1375 | trace_rcu_batch_start(rsp->name, 0, 0); | 1377 | trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, 0); |
1376 | trace_rcu_batch_end(rsp->name, 0, !!ACCESS_ONCE(rdp->nxtlist), | 1378 | trace_rcu_batch_end(rsp->name, 0, !!ACCESS_ONCE(rdp->nxtlist), |
1377 | need_resched(), is_idle_task(current), | 1379 | need_resched(), is_idle_task(current), |
1378 | rcu_is_callbacks_kthread()); | 1380 | rcu_is_callbacks_kthread()); |
@@ -1385,7 +1387,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) | |||
1385 | */ | 1387 | */ |
1386 | local_irq_save(flags); | 1388 | local_irq_save(flags); |
1387 | bl = rdp->blimit; | 1389 | bl = rdp->blimit; |
1388 | trace_rcu_batch_start(rsp->name, rdp->qlen, bl); | 1390 | trace_rcu_batch_start(rsp->name, rdp->qlen_lazy, rdp->qlen, bl); |
1389 | list = rdp->nxtlist; | 1391 | list = rdp->nxtlist; |
1390 | rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL]; | 1392 | rdp->nxtlist = *rdp->nxttail[RCU_DONE_TAIL]; |
1391 | *rdp->nxttail[RCU_DONE_TAIL] = NULL; | 1393 | *rdp->nxttail[RCU_DONE_TAIL] = NULL; |
@@ -1396,12 +1398,13 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) | |||
1396 | local_irq_restore(flags); | 1398 | local_irq_restore(flags); |
1397 | 1399 | ||
1398 | /* Invoke callbacks. */ | 1400 | /* Invoke callbacks. */ |
1399 | count = 0; | 1401 | count = count_lazy = 0; |
1400 | while (list) { | 1402 | while (list) { |
1401 | next = list->next; | 1403 | next = list->next; |
1402 | prefetch(next); | 1404 | prefetch(next); |
1403 | debug_rcu_head_unqueue(list); | 1405 | debug_rcu_head_unqueue(list); |
1404 | __rcu_reclaim(rsp->name, list); | 1406 | if (__rcu_reclaim(rsp->name, list)) |
1407 | count_lazy++; | ||
1405 | list = next; | 1408 | list = next; |
1406 | /* Stop only if limit reached and CPU has something to do. */ | 1409 | /* Stop only if limit reached and CPU has something to do. */ |
1407 | if (++count >= bl && | 1410 | if (++count >= bl && |
@@ -1416,6 +1419,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp) | |||
1416 | rcu_is_callbacks_kthread()); | 1419 | rcu_is_callbacks_kthread()); |
1417 | 1420 | ||
1418 | /* Update count, and requeue any remaining callbacks. */ | 1421 | /* Update count, and requeue any remaining callbacks. */ |
1422 | rdp->qlen_lazy -= count_lazy; | ||
1419 | rdp->qlen -= count; | 1423 | rdp->qlen -= count; |
1420 | rdp->n_cbs_invoked += count; | 1424 | rdp->n_cbs_invoked += count; |
1421 | if (list != NULL) { | 1425 | if (list != NULL) { |
@@ -1702,7 +1706,7 @@ static void invoke_rcu_core(void) | |||
1702 | 1706 | ||
1703 | static void | 1707 | static void |
1704 | __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), | 1708 | __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), |
1705 | struct rcu_state *rsp) | 1709 | struct rcu_state *rsp, bool lazy) |
1706 | { | 1710 | { |
1707 | unsigned long flags; | 1711 | unsigned long flags; |
1708 | struct rcu_data *rdp; | 1712 | struct rcu_data *rdp; |
@@ -1727,12 +1731,14 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), | |||
1727 | *rdp->nxttail[RCU_NEXT_TAIL] = head; | 1731 | *rdp->nxttail[RCU_NEXT_TAIL] = head; |
1728 | rdp->nxttail[RCU_NEXT_TAIL] = &head->next; | 1732 | rdp->nxttail[RCU_NEXT_TAIL] = &head->next; |
1729 | rdp->qlen++; | 1733 | rdp->qlen++; |
1734 | if (lazy) | ||
1735 | rdp->qlen_lazy++; | ||
1730 | 1736 | ||
1731 | if (__is_kfree_rcu_offset((unsigned long)func)) | 1737 | if (__is_kfree_rcu_offset((unsigned long)func)) |
1732 | trace_rcu_kfree_callback(rsp->name, head, (unsigned long)func, | 1738 | trace_rcu_kfree_callback(rsp->name, head, (unsigned long)func, |
1733 | rdp->qlen); | 1739 | rdp->qlen_lazy, rdp->qlen); |
1734 | else | 1740 | else |
1735 | trace_rcu_callback(rsp->name, head, rdp->qlen); | 1741 | trace_rcu_callback(rsp->name, head, rdp->qlen_lazy, rdp->qlen); |
1736 | 1742 | ||
1737 | /* If interrupts were disabled, don't dive into RCU core. */ | 1743 | /* If interrupts were disabled, don't dive into RCU core. */ |
1738 | if (irqs_disabled_flags(flags)) { | 1744 | if (irqs_disabled_flags(flags)) { |
@@ -1779,16 +1785,16 @@ __call_rcu(struct rcu_head *head, void (*func)(struct rcu_head *rcu), | |||
1779 | */ | 1785 | */ |
1780 | void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) | 1786 | void call_rcu_sched(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) |
1781 | { | 1787 | { |
1782 | __call_rcu(head, func, &rcu_sched_state); | 1788 | __call_rcu(head, func, &rcu_sched_state, 0); |
1783 | } | 1789 | } |
1784 | EXPORT_SYMBOL_GPL(call_rcu_sched); | 1790 | EXPORT_SYMBOL_GPL(call_rcu_sched); |
1785 | 1791 | ||
1786 | /* | 1792 | /* |
1787 | * Queue an RCU for invocation after a quicker grace period. | 1793 | * Queue an RCU callback for invocation after a quicker grace period. |
1788 | */ | 1794 | */ |
1789 | void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) | 1795 | void call_rcu_bh(struct rcu_head *head, void (*func)(struct rcu_head *rcu)) |
1790 | { | 1796 | { |
1791 | __call_rcu(head, func, &rcu_bh_state); | 1797 | __call_rcu(head, func, &rcu_bh_state, 0); |
1792 | } | 1798 | } |
1793 | EXPORT_SYMBOL_GPL(call_rcu_bh); | 1799 | EXPORT_SYMBOL_GPL(call_rcu_bh); |
1794 | 1800 | ||
@@ -2036,6 +2042,7 @@ rcu_boot_init_percpu_data(int cpu, struct rcu_state *rsp) | |||
2036 | rdp->nxtlist = NULL; | 2042 | rdp->nxtlist = NULL; |
2037 | for (i = 0; i < RCU_NEXT_SIZE; i++) | 2043 | for (i = 0; i < RCU_NEXT_SIZE; i++) |
2038 | rdp->nxttail[i] = &rdp->nxtlist; | 2044 | rdp->nxttail[i] = &rdp->nxtlist; |
2045 | rdp->qlen_lazy = 0; | ||
2039 | rdp->qlen = 0; | 2046 | rdp->qlen = 0; |
2040 | rdp->dynticks = &per_cpu(rcu_dynticks, cpu); | 2047 | rdp->dynticks = &per_cpu(rcu_dynticks, cpu); |
2041 | WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_NESTING); | 2048 | WARN_ON_ONCE(rdp->dynticks->dynticks_nesting != DYNTICK_TASK_NESTING); |