aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2023-04-10 14:15:17 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2023-04-10 14:15:17 -0700
commit0d3eb744aed40ffce820cded61d7eac515199165 (patch)
tree21469705de5f4dc3ee0d9fa7e5582e8828f2561a
parentdfc191544864601a056ce7691d600e70d60d3ca1 (diff)
parent5da7cb193db32da783a3f3e77d8b639989321d48 (diff)
downloadnvme-0d3eb744aed40ffce820cded61d7eac515199165.tar.gz
Merge tag 'urgent-rcu.2023.04.07a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu
Pull RCU fix from Paul McKenney: "This fixes a pair of bugs in which an improbable but very real sequence of events can cause kfree_rcu() to be a bit too quick about freeing the memory passed to it. It turns out that this pair of bugs is about two years old, and so this is not a v6.3 regression. However: (1) It just started showing up in the wild and (2) Its consequences are dire, so its fix needs to go in sooner rather than later. Testing is of course being upgraded, and the upgraded tests detect this situation very quickly. But to the best of my knowledge right now, the tests are not particularly urgent and will thus most likely show up in the v6.5 merge window (the one after this coming one). Kudos to Ziwei Dai and his group for tracking this one down the hard way!" * tag 'urgent-rcu.2023.04.07a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu: rcu/kvfree: Avoid freeing new kfree_rcu() memory after old grace period
-rw-r--r--kernel/rcu/tree.c27
1 files changed, 19 insertions, 8 deletions
diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 8e880c09ab59e..7b95ee98a1a53 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -3024,6 +3024,18 @@ need_offload_krc(struct kfree_rcu_cpu *krcp)
return !!READ_ONCE(krcp->head);
}
+static bool
+need_wait_for_krwp_work(struct kfree_rcu_cpu_work *krwp)
+{
+ int i;
+
+ for (i = 0; i < FREE_N_CHANNELS; i++)
+ if (!list_empty(&krwp->bulk_head_free[i]))
+ return true;
+
+ return !!krwp->head_free;
+}
+
static int krc_count(struct kfree_rcu_cpu *krcp)
{
int sum = atomic_read(&krcp->head_count);
@@ -3107,15 +3119,14 @@ static void kfree_rcu_monitor(struct work_struct *work)
for (i = 0; i < KFREE_N_BATCHES; i++) {
struct kfree_rcu_cpu_work *krwp = &(krcp->krw_arr[i]);
- // Try to detach bulk_head or head and attach it over any
- // available corresponding free channel. It can be that
- // a previous RCU batch is in progress, it means that
- // immediately to queue another one is not possible so
- // in that case the monitor work is rearmed.
- if ((!list_empty(&krcp->bulk_head[0]) && list_empty(&krwp->bulk_head_free[0])) ||
- (!list_empty(&krcp->bulk_head[1]) && list_empty(&krwp->bulk_head_free[1])) ||
- (READ_ONCE(krcp->head) && !krwp->head_free)) {
+ // Try to detach bulk_head or head and attach it, only when
+ // all channels are free. Any channel is not free means at krwp
+ // there is on-going rcu work to handle krwp's free business.
+ if (need_wait_for_krwp_work(krwp))
+ continue;
+ // kvfree_rcu_drain_ready() might handle this krcp, if so give up.
+ if (need_offload_krc(krcp)) {
// Channel 1 corresponds to the SLAB-pointer bulk path.
// Channel 2 corresponds to vmalloc-pointer bulk path.
for (j = 0; j < FREE_N_CHANNELS; j++) {