]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
dm cache policy smq: check allocation under invalidate lock
authorGuangshuo Li <lgs201920130244@gmail.com>
Fri, 29 May 2026 15:57:45 +0000 (23:57 +0800)
committerMikulas Patocka <mpatocka@redhat.com>
Mon, 1 Jun 2026 15:30:24 +0000 (17:30 +0200)
commit 2d1f7b65f5de ("dm cache policy smq: fix missing locks in
invalidating cache blocks") added mq->lock around the destructive part of
smq_invalidate_mapping(), but left the e->allocated check outside the
critical section.

That leaves a check-then-act race. Two concurrent invalidators can both
observe e->allocated as true before either of them takes mq->lock. The
first invalidator that acquires the lock removes the entry from the
queues and hash table and then calls free_entry(), which clears
e->allocated and puts the entry back on the free list. The second
invalidator can then acquire mq->lock and continue with the stale result
of the unlocked check.

This can corrupt the SMQ queues or hash table by deleting an entry that
is no longer on those structures. It can also hit the allocation check in
free_entry() when the same entry is freed again.

Move the allocation check under mq->lock so the predicate and the
destructive operations are serialized by the same lock.

Fixes: 2d1f7b65f5de ("dm cache policy smq: fix missing locks in invalidating cache blocks")
Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
drivers/md/dm-cache-policy-smq.c

index dd77a93fd68d2d12e0e0158e4be2be73dc4bdde9..1ae304c2f5737cfdb7c3bd02b7c6ce12b5d7ea90 100644 (file)
@@ -1590,18 +1590,22 @@ static int smq_invalidate_mapping(struct dm_cache_policy *p, dm_cblock_t cblock)
        struct smq_policy *mq = to_smq_policy(p);
        struct entry *e = get_entry(&mq->cache_alloc, from_cblock(cblock));
        unsigned long flags;
-
-       if (!e->allocated)
-               return -ENODATA;
+       int r = 0;
 
        spin_lock_irqsave(&mq->lock, flags);
+       if (!e->allocated) {
+               r = -ENODATA;
+               goto out;
+       }
        // FIXME: what if this block has pending background work?
        del_queue(mq, e);
        h_remove(&mq->table, e);
        free_entry(&mq->cache_alloc, e);
+
+out:
        spin_unlock_irqrestore(&mq->lock, flags);
 
-       return 0;
+       return r;
 }
 
 static uint32_t smq_get_hint(struct dm_cache_policy *p, dm_cblock_t cblock)