dm-devel.redhat.com archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/3] dm-delay synchronization cleanup
@ 2024-05-07 21:16 Benjamin Marzinski
  2024-05-07 21:16 ` [PATCH 1/3] dm-delay: fix workqueue delay_timer race Benjamin Marzinski
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Benjamin Marzinski @ 2024-05-07 21:16 UTC (permalink / raw)
  To: Mikulas Patocka, Mike Snitzer, Heinz Mauelshagen; +Cc: dm-devel

This patchset fixes a possible crash in delay_dtr() caused by the
delay_timer queueing work on a destroyed workqueue. It also cuts down
on the amount of locking and waiting the dm-delay does.

Benjamin Marzinski (3):
  dm-delay: fix workqueue delay_timer race
  dm-delay: change locking to avoid contention
  dm-delay: remove timer_lock

 drivers/md/dm-delay.c | 52 +++++++++++++++++++++++--------------------
 1 file changed, 28 insertions(+), 24 deletions(-)

-- 
2.45.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] dm-delay: fix workqueue delay_timer race
  2024-05-07 21:16 [PATCH 0/3] dm-delay synchronization cleanup Benjamin Marzinski
@ 2024-05-07 21:16 ` Benjamin Marzinski
  2024-05-07 21:16 ` [PATCH 2/3] dm-delay: change locking to avoid contention Benjamin Marzinski
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Benjamin Marzinski @ 2024-05-07 21:16 UTC (permalink / raw)
  To: Mikulas Patocka, Mike Snitzer, Heinz Mauelshagen; +Cc: dm-devel

delay_timer could be pending when delay_dtr() is called. It needs to be
shut down before kdelayd_wq is destroyed, so it won't try queueing more
work to kdelayd_wq while that's getting destroyed.

Also the del_timer_sync() call in delay_presuspend() doesn't protect
against the timer getting immediately rearmed by the queued call to
flush_delayed_bios(), but there's no real harm if that does happen.
timer_delete() is less work, and is basically just as likely to stop a
pointless call to flush_delayed_bios().

Fixes: 26b9f228703f ("dm: delay target")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 drivers/md/dm-delay.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c
index 1c424983c042..584e3b10802d 100644
--- a/drivers/md/dm-delay.c
+++ b/drivers/md/dm-delay.c
@@ -154,8 +154,10 @@ static void delay_dtr(struct dm_target *ti)
 {
 	struct delay_c *dc = ti->private;
 
-	if (dc->kdelayd_wq)
+	if (dc->kdelayd_wq) {
+		timer_shutdown_sync(&dc->delay_timer);
 		destroy_workqueue(dc->kdelayd_wq);
+	}
 
 	if (dc->read.dev)
 		dm_put_device(ti, dc->read.dev);
@@ -334,7 +336,7 @@ static void delay_presuspend(struct dm_target *ti)
 	mutex_unlock(&delayed_bios_lock);
 
 	if (!delay_is_fast(dc))
-		del_timer_sync(&dc->delay_timer);
+		timer_delete(&dc->delay_timer);
 	flush_delayed_bios(dc, true);
 }
 
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] dm-delay: change locking to avoid contention
  2024-05-07 21:16 [PATCH 0/3] dm-delay synchronization cleanup Benjamin Marzinski
  2024-05-07 21:16 ` [PATCH 1/3] dm-delay: fix workqueue delay_timer race Benjamin Marzinski
@ 2024-05-07 21:16 ` Benjamin Marzinski
  2024-05-07 21:16 ` [PATCH 3/3] dm-delay: remove timer_lock Benjamin Marzinski
  2024-05-09 14:24 ` [PATCH 0/3] dm-delay synchronization cleanup Mike Snitzer
  3 siblings, 0 replies; 6+ messages in thread
From: Benjamin Marzinski @ 2024-05-07 21:16 UTC (permalink / raw)
  To: Mikulas Patocka, Mike Snitzer, Heinz Mauelshagen; +Cc: dm-devel

The delayed_bios list is protected by one mutex shared by all dm-delay
devices. This mutex must be held whenever a bio is added or expired bios
are removed from the list.  Since a large number of expired bios could
be on the list, flush_delayed_bios() can schedule while holding the
mutex. This means a flush_delayed_bios() call on any dm-delay device can
slow down delay_map() calls on any other dm-delay device.

To keep dm-delay devices from slowing each other down and keep
processing delay bios from slowing adding delayed bios, the global mutex
has been removed, and each dm-delay device now has two locks.
delayed_bios_lock is a spinlock that must be held whenever the
delayed_bios list is accessed. process_bios_lock is a mutex that must be
held whenever a process has temporarily pulled bios off the delayed_bios
list to check which ones should be processed. It must be held until all
the bios that won't be processed are returned to the list. This is what
flush_delayed_bios() now does. The mutex is necessary to guarantee that
delay_presuspend() sees the entire list of delayed bios when it calls
flush_delayed_bios().

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 drivers/md/dm-delay.c | 36 +++++++++++++++++++++++-------------
 1 file changed, 23 insertions(+), 13 deletions(-)

diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c
index 584e3b10802d..b1829c106a37 100644
--- a/drivers/md/dm-delay.c
+++ b/drivers/md/dm-delay.c
@@ -29,6 +29,8 @@ struct delay_class {
 struct delay_c {
 	struct timer_list delay_timer;
 	struct mutex timer_lock;
+	struct mutex process_bios_lock; /* hold while removing bios to be processed from list */
+	spinlock_t delayed_bios_lock; /* hold on all accesses to delayed_bios list */
 	struct workqueue_struct *kdelayd_wq;
 	struct work_struct flush_expired_bios;
 	struct list_head delayed_bios;
@@ -49,8 +51,6 @@ struct dm_delay_info {
 	unsigned long expires;
 };
 
-static DEFINE_MUTEX(delayed_bios_lock);
-
 static void handle_delayed_timer(struct timer_list *t)
 {
 	struct delay_c *dc = from_timer(dc, t, delay_timer);
@@ -89,12 +89,16 @@ static void flush_delayed_bios(struct delay_c *dc, bool flush_all)
 {
 	struct dm_delay_info *delayed, *next;
 	struct bio_list flush_bio_list;
+	LIST_HEAD(local_list);
 	unsigned long next_expires = 0;
 	bool start_timer = false;
 	bio_list_init(&flush_bio_list);
 
-	mutex_lock(&delayed_bios_lock);
-	list_for_each_entry_safe(delayed, next, &dc->delayed_bios, list) {
+	mutex_lock(&dc->process_bios_lock);
+	spin_lock(&dc->delayed_bios_lock);
+	list_replace_init(&dc->delayed_bios, &local_list);
+	spin_unlock(&dc->delayed_bios_lock);
+	list_for_each_entry_safe(delayed, next, &local_list, list) {
 		cond_resched();
 		if (flush_all || time_after_eq(jiffies, delayed->expires)) {
 			struct bio *bio = dm_bio_from_per_bio_data(delayed,
@@ -114,7 +118,10 @@ static void flush_delayed_bios(struct delay_c *dc, bool flush_all)
 			}
 		}
 	}
-	mutex_unlock(&delayed_bios_lock);
+	spin_lock(&dc->delayed_bios_lock);
+	list_splice(&local_list, &dc->delayed_bios);
+	spin_unlock(&dc->delayed_bios_lock);
+	mutex_unlock(&dc->process_bios_lock);
 
 	if (start_timer)
 		queue_timeout(dc, next_expires);
@@ -128,13 +135,13 @@ static int flush_worker_fn(void *data)
 
 	while (!kthread_should_stop()) {
 		flush_delayed_bios(dc, false);
-		mutex_lock(&delayed_bios_lock);
+		spin_lock(&dc->delayed_bios_lock);
 		if (unlikely(list_empty(&dc->delayed_bios))) {
 			set_current_state(TASK_INTERRUPTIBLE);
-			mutex_unlock(&delayed_bios_lock);
+			spin_unlock(&dc->delayed_bios_lock);
 			schedule();
 		} else {
-			mutex_unlock(&delayed_bios_lock);
+			spin_unlock(&dc->delayed_bios_lock);
 			cond_resched();
 		}
 	}
@@ -168,6 +175,7 @@ static void delay_dtr(struct dm_target *ti)
 	if (dc->worker)
 		kthread_stop(dc->worker);
 
+	mutex_destroy(&dc->process_bios_lock);
 	mutex_destroy(&dc->timer_lock);
 
 	kfree(dc);
@@ -227,6 +235,8 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 	ti->private = dc;
 	INIT_LIST_HEAD(&dc->delayed_bios);
 	mutex_init(&dc->timer_lock);
+	mutex_init(&dc->process_bios_lock);
+	spin_lock_init(&dc->delayed_bios_lock);
 	dc->may_delay = true;
 	dc->argc = argc;
 
@@ -310,14 +320,14 @@ static int delay_bio(struct delay_c *dc, struct delay_class *c, struct bio *bio)
 	delayed->context = dc;
 	delayed->expires = expires = jiffies + msecs_to_jiffies(c->delay);
 
-	mutex_lock(&delayed_bios_lock);
+	spin_lock(&dc->delayed_bios_lock);
 	if (unlikely(!dc->may_delay)) {
-		mutex_unlock(&delayed_bios_lock);
+		spin_unlock(&dc->delayed_bios_lock);
 		return DM_MAPIO_REMAPPED;
 	}
 	c->ops++;
 	list_add_tail(&delayed->list, &dc->delayed_bios);
-	mutex_unlock(&delayed_bios_lock);
+	spin_unlock(&dc->delayed_bios_lock);
 
 	if (delay_is_fast(dc))
 		wake_up_process(dc->worker);
@@ -331,9 +341,9 @@ static void delay_presuspend(struct dm_target *ti)
 {
 	struct delay_c *dc = ti->private;
 
-	mutex_lock(&delayed_bios_lock);
+	spin_lock(&dc->delayed_bios_lock);
 	dc->may_delay = false;
-	mutex_unlock(&delayed_bios_lock);
+	spin_unlock(&dc->delayed_bios_lock);
 
 	if (!delay_is_fast(dc))
 		timer_delete(&dc->delay_timer);
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] dm-delay: remove timer_lock
  2024-05-07 21:16 [PATCH 0/3] dm-delay synchronization cleanup Benjamin Marzinski
  2024-05-07 21:16 ` [PATCH 1/3] dm-delay: fix workqueue delay_timer race Benjamin Marzinski
  2024-05-07 21:16 ` [PATCH 2/3] dm-delay: change locking to avoid contention Benjamin Marzinski
@ 2024-05-07 21:16 ` Benjamin Marzinski
  2024-05-09 14:24 ` [PATCH 0/3] dm-delay synchronization cleanup Mike Snitzer
  3 siblings, 0 replies; 6+ messages in thread
From: Benjamin Marzinski @ 2024-05-07 21:16 UTC (permalink / raw)
  To: Mikulas Patocka, Mike Snitzer, Heinz Mauelshagen; +Cc: dm-devel

Instead of manually checking the timer details in queue_timeout(), call
timer_reduce() to start the timer or reduce the expiration time. This
avoids needing a lock.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
---
 drivers/md/dm-delay.c | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/drivers/md/dm-delay.c b/drivers/md/dm-delay.c
index b1829c106a37..297ae7ffcd0b 100644
--- a/drivers/md/dm-delay.c
+++ b/drivers/md/dm-delay.c
@@ -28,7 +28,6 @@ struct delay_class {
 
 struct delay_c {
 	struct timer_list delay_timer;
-	struct mutex timer_lock;
 	struct mutex process_bios_lock; /* hold while removing bios to be processed from list */
 	spinlock_t delayed_bios_lock; /* hold on all accesses to delayed_bios list */
 	struct workqueue_struct *kdelayd_wq;
@@ -60,12 +59,7 @@ static void handle_delayed_timer(struct timer_list *t)
 
 static void queue_timeout(struct delay_c *dc, unsigned long expires)
 {
-	mutex_lock(&dc->timer_lock);
-
-	if (!timer_pending(&dc->delay_timer) || expires < dc->delay_timer.expires)
-		mod_timer(&dc->delay_timer, expires);
-
-	mutex_unlock(&dc->timer_lock);
+	timer_reduce(&dc->delay_timer, expires);
 }
 
 static inline bool delay_is_fast(struct delay_c *dc)
@@ -176,7 +170,6 @@ static void delay_dtr(struct dm_target *ti)
 		kthread_stop(dc->worker);
 
 	mutex_destroy(&dc->process_bios_lock);
-	mutex_destroy(&dc->timer_lock);
 
 	kfree(dc);
 }
@@ -234,7 +227,6 @@ static int delay_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 
 	ti->private = dc;
 	INIT_LIST_HEAD(&dc->delayed_bios);
-	mutex_init(&dc->timer_lock);
 	mutex_init(&dc->process_bios_lock);
 	spin_lock_init(&dc->delayed_bios_lock);
 	dc->may_delay = true;
-- 
2.45.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/3] dm-delay synchronization cleanup
  2024-05-07 21:16 [PATCH 0/3] dm-delay synchronization cleanup Benjamin Marzinski
                   ` (2 preceding siblings ...)
  2024-05-07 21:16 ` [PATCH 3/3] dm-delay: remove timer_lock Benjamin Marzinski
@ 2024-05-09 14:24 ` Mike Snitzer
  2024-05-09 16:07   ` Benjamin Marzinski
  3 siblings, 1 reply; 6+ messages in thread
From: Mike Snitzer @ 2024-05-09 14:24 UTC (permalink / raw)
  To: Benjamin Marzinski
  Cc: Mikulas Patocka, Heinz Mauelshagen, dm-devel, Joel Colledge

On Tue, May 07, 2024 at 05:16:22PM -0400, Benjamin Marzinski wrote:
> This patchset fixes a possible crash in delay_dtr() caused by the
> delay_timer queueing work on a destroyed workqueue. It also cuts down
> on the amount of locking and waiting the dm-delay does.
> 
> Benjamin Marzinski (3):
>   dm-delay: fix workqueue delay_timer race
>   dm-delay: change locking to avoid contention
>   dm-delay: remove timer_lock
> 
>  drivers/md/dm-delay.c | 52 +++++++++++++++++++++++--------------------
>  1 file changed, 28 insertions(+), 24 deletions(-)
> 
> -- 
> 2.45.0
> 
> 

Hey Ben,

I just staged 5 dm-delay patches in dm-6.10. 1 from Joel and 4 from
you.  Please confirm this is what you expected and looks good:
https://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm.git/log/?h=dm-6.10

Thanks,
Mike

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/3] dm-delay synchronization cleanup
  2024-05-09 14:24 ` [PATCH 0/3] dm-delay synchronization cleanup Mike Snitzer
@ 2024-05-09 16:07   ` Benjamin Marzinski
  0 siblings, 0 replies; 6+ messages in thread
From: Benjamin Marzinski @ 2024-05-09 16:07 UTC (permalink / raw)
  To: Mike Snitzer; +Cc: Mikulas Patocka, Heinz Mauelshagen, dm-devel, Joel Colledge

On Thu, May 09, 2024 at 10:24:52AM -0400, Mike Snitzer wrote:
> On Tue, May 07, 2024 at 05:16:22PM -0400, Benjamin Marzinski wrote:
> > This patchset fixes a possible crash in delay_dtr() caused by the
> > delay_timer queueing work on a destroyed workqueue. It also cuts down
> > on the amount of locking and waiting the dm-delay does.
> > 
> > Benjamin Marzinski (3):
> >   dm-delay: fix workqueue delay_timer race
> >   dm-delay: change locking to avoid contention
> >   dm-delay: remove timer_lock
> > 
> >  drivers/md/dm-delay.c | 52 +++++++++++++++++++++++--------------------
> >  1 file changed, 28 insertions(+), 24 deletions(-)
> > 
> > -- 
> > 2.45.0
> > 
> > 
> 
> Hey Ben,
> 
> I just staged 5 dm-delay patches in dm-6.10. 1 from Joel and 4 from
> you.  Please confirm this is what you expected and looks good:
> https://git.kernel.org/pub/scm/linux/kernel/git/device-mapper/linux-dm.git/log/?h=dm-6.10

Well, I was half expecting to be told that it's kinda pointless to
optimize a target whoses purpose is to slow things down, but yeah, this
matches my dev branch.

Thanks.
-Ben

> 
> Thanks,
> Mike


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-05-09 16:07 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-05-07 21:16 [PATCH 0/3] dm-delay synchronization cleanup Benjamin Marzinski
2024-05-07 21:16 ` [PATCH 1/3] dm-delay: fix workqueue delay_timer race Benjamin Marzinski
2024-05-07 21:16 ` [PATCH 2/3] dm-delay: change locking to avoid contention Benjamin Marzinski
2024-05-07 21:16 ` [PATCH 3/3] dm-delay: remove timer_lock Benjamin Marzinski
2024-05-09 14:24 ` [PATCH 0/3] dm-delay synchronization cleanup Mike Snitzer
2024-05-09 16:07   ` Benjamin Marzinski

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).