Skip to content

Commit 33304b7

Browse files
bmarzinsMikulas Patocka
authored andcommitted
dm-delay: don't busy-wait in kthread
When using a kthread to delay the IOs, dm-delay would continuously loop, checking if IOs were ready to submit. It had a cond_resched() call in the loop, but might still loop hundreds of millions of times waiting for an IO that was scheduled to be submitted 10s of ms in the future. With the change to make dm-delay over zoned devices always use kthreads regardless of the length of the delay, this wasted work only gets worse. To solve this and still keep roughly the same precision for very short delays, dm-delay now calls fsleep() for 1/8th of the smallest non-zero delay it will place on IOs, or 1 ms, whichever is smaller. The reason that dm-delay doesn't just use the actual expiration time of the next delayed IO to calculated the sleep time is that delay_dtr() must wait for the kthread to finish before deleting the table. If a zoned device with a long delay queued an IO shortly before being suspended and removed, the IO would be flushed in delay_presuspend(), but the removing the device would still have to wait for the remainder of the long delay. This time is now capped at 1 ms. Signed-off-by: Benjamin Marzinski <[email protected]> Reviewed-by: Damien Le Moal <[email protected]> Tested-by: Damien Le Moal <[email protected]> Signed-off-by: Mikulas Patocka <[email protected]>
1 parent ad320ae commit 33304b7

File tree

1 file changed

+14
-3
lines changed

1 file changed

+14
-3
lines changed

drivers/md/dm-delay.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,14 @@
1414
#include <linux/bio.h>
1515
#include <linux/slab.h>
1616
#include <linux/kthread.h>
17+
#include <linux/delay.h>
1718

1819
#include <linux/device-mapper.h>
1920

2021
#define DM_MSG_PREFIX "delay"
2122

23+
#define SLEEP_SHIFT 3
24+
2225
struct delay_class {
2326
struct dm_dev *dev;
2427
sector_t start;
@@ -34,6 +37,7 @@ struct delay_c {
3437
struct work_struct flush_expired_bios;
3538
struct list_head delayed_bios;
3639
struct task_struct *worker;
40+
unsigned int worker_sleep_us;
3741
bool may_delay;
3842

3943
struct delay_class read;
@@ -136,6 +140,7 @@ static int flush_worker_fn(void *data)
136140
schedule();
137141
} else {
138142
spin_unlock(&dc->delayed_bios_lock);
143+
fsleep(dc->worker_sleep_us);
139144
cond_resched();
140145
}
141146
}
@@ -212,7 +217,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
212217
{
213218
struct delay_c *dc;
214219
int ret;
215-
unsigned int max_delay;
220+
unsigned int max_delay, min_delay;
216221

217222
if (argc != 3 && argc != 6 && argc != 9) {
218223
ti->error = "Requires exactly 3, 6 or 9 arguments";
@@ -235,7 +240,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
235240
ret = delay_class_ctr(ti, &dc->read, argv);
236241
if (ret)
237242
goto bad;
238-
max_delay = dc->read.delay;
243+
min_delay = max_delay = dc->read.delay;
239244

240245
if (argc == 3) {
241246
ret = delay_class_ctr(ti, &dc->write, argv);
@@ -251,6 +256,7 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
251256
if (ret)
252257
goto bad;
253258
max_delay = max(max_delay, dc->write.delay);
259+
min_delay = min_not_zero(min_delay, dc->write.delay);
254260

255261
if (argc == 6) {
256262
ret = delay_class_ctr(ti, &dc->flush, argv + 3);
@@ -263,9 +269,14 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
263269
if (ret)
264270
goto bad;
265271
max_delay = max(max_delay, dc->flush.delay);
272+
min_delay = min_not_zero(min_delay, dc->flush.delay);
266273

267274
out:
268275
if (max_delay < 50) {
276+
if (min_delay >> SLEEP_SHIFT)
277+
dc->worker_sleep_us = 1000;
278+
else
279+
dc->worker_sleep_us = (min_delay * 1000) >> SLEEP_SHIFT;
269280
/*
270281
* In case of small requested delays, use kthread instead of
271282
* timers and workqueue to achieve better latency.
@@ -438,7 +449,7 @@ static int delay_iterate_devices(struct dm_target *ti,
438449

439450
static struct target_type delay_target = {
440451
.name = "delay",
441-
.version = {1, 4, 0},
452+
.version = {1, 5, 0},
442453
.features = DM_TARGET_PASSES_INTEGRITY | DM_TARGET_ZONED_HM,
443454
.module = THIS_MODULE,
444455
.ctr = delay_ctr,

0 commit comments

Comments
 (0)