Skip to content

Commit 6fc45b6

Browse files
Mikulas PatockaMike Snitzer
authored andcommitted
dm-delay: fix a race between delay_presuspend and delay_bio
In delay_presuspend, we set the atomic variable may_delay and then stop the timer and flush pending bios. The intention here is to prevent the delay target from re-arming the timer again. However, this test is racy. Suppose that one thread goes to delay_bio, sees that dc->may_delay is one and proceeds; now, another thread executes delay_presuspend, it sets dc->may_delay to zero, deletes the timer and flushes pending bios. Then, the first thread continues and adds the bio to delayed->list despite the fact that dc->may_delay is false. Fix this bug by changing may_delay's type from atomic_t to bool and only access it while holding the delayed_bios_lock mutex. Note that we don't have to grab the mutex in delay_resume because there are no bios in flight at this point. Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> Cc: stable@vger.kernel.org Signed-off-by: Mike Snitzer <snitzer@kernel.org>
1 parent b85ea95 commit 6fc45b6

File tree

1 file changed

+11
-5
lines changed

1 file changed

+11
-5
lines changed

drivers/md/dm-delay.c

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ struct delay_c {
3333
struct work_struct flush_expired_bios;
3434
struct list_head delayed_bios;
3535
struct task_struct *worker;
36-
atomic_t may_delay;
36+
bool may_delay;
3737

3838
struct delay_class read;
3939
struct delay_class write;
@@ -236,7 +236,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
236236

237237
ti->private = dc;
238238
INIT_LIST_HEAD(&dc->delayed_bios);
239-
atomic_set(&dc->may_delay, 1);
239+
dc->may_delay = true;
240240
dc->argc = argc;
241241

242242
ret = delay_class_ctr(ti, &dc->read, argv);
@@ -312,7 +312,7 @@ static int delay_bio(struct delay_c *dc, struct delay_class *c, struct bio *bio)
312312
struct dm_delay_info *delayed;
313313
unsigned long expires = 0;
314314

315-
if (!c->delay || !atomic_read(&dc->may_delay))
315+
if (!c->delay)
316316
return DM_MAPIO_REMAPPED;
317317

318318
delayed = dm_per_bio_data(bio, sizeof(struct dm_delay_info));
@@ -321,6 +321,10 @@ static int delay_bio(struct delay_c *dc, struct delay_class *c, struct bio *bio)
321321
delayed->expires = expires = jiffies + msecs_to_jiffies(c->delay);
322322

323323
mutex_lock(&delayed_bios_lock);
324+
if (unlikely(!dc->may_delay)) {
325+
mutex_unlock(&delayed_bios_lock);
326+
return DM_MAPIO_REMAPPED;
327+
}
324328
c->ops++;
325329
list_add_tail(&delayed->list, &dc->delayed_bios);
326330
mutex_unlock(&delayed_bios_lock);
@@ -337,7 +341,9 @@ static void delay_presuspend(struct dm_target *ti)
337341
{
338342
struct delay_c *dc = ti->private;
339343

340-
atomic_set(&dc->may_delay, 0);
344+
mutex_lock(&delayed_bios_lock);
345+
dc->may_delay = false;
346+
mutex_unlock(&delayed_bios_lock);
341347

342348
if (delay_is_fast(dc))
343349
flush_delayed_bios_fast(dc, true);
@@ -351,7 +357,7 @@ static void delay_resume(struct dm_target *ti)
351357
{
352358
struct delay_c *dc = ti->private;
353359

354-
atomic_set(&dc->may_delay, 1);
360+
dc->may_delay = true;
355361
}
356362

357363
static int delay_map(struct dm_target *ti, struct bio *bio)

0 commit comments

Comments
 (0)