Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 694

kernel-2.6.18-238.el5.src.rpm

From: Milan Broz <mbroz@redhat.com>
Date: Tue, 14 Oct 2008 10:22:40 +0200
Subject: [dm] mpath: moving path activation to workqueue panics
Message-id: 48F456D0.4060308@redhat.com
O-Subject: [RHEL 5.3 PATCH] dm mpath: moving path activation to workqueue causes a panic
Bugzilla: 465570
RH-Acked-by: Alasdair G Kergon <agk@redhat.com>

RHEL5.3 dm mpath: moving path activation to workqueue causes a panic
Resolves: rhbz#465570
Patch is upstream, commit 7253a33434245ee8243897559188186df65f3611

Moving the path activation to workqueue along with scsi_dh patches introduced
a race. It is due to the fact that the current_pgpath (in the multipath data
structure) can be modified if changes happen in any of the paths leading to
the lun. If the changes lead to current_pgpath being set to NULL, then it
leads to the invalid access which results in the panic.

This patch fixes that by storing the pgpath to activate in the multipath data
structure and properly protecting it.

Note that if activate_path is called twice in succession with different pgpath,
with the second one being called before the first one is done, then activate
path will be called twice for the second pgpath, which is fine.

Patch provided and tested by customer.
Also test build and basic testing passed.

diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index b96d7c3..f09d272 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -87,6 +87,7 @@ struct multipath {
 
 	struct work_struct trigger_event;
 	struct work_struct activate_path;
+	struct pgpath *pgpath_to_activate;
 
 	/*
 	 * We must use a mempool of mpath_io structs so that we
@@ -151,6 +152,7 @@ static struct priority_group *alloc_priority_group(void)
 
 static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
 {
+	unsigned long flags;
 	struct pgpath *pgpath, *tmp;
 	struct multipath *m = (struct multipath *) ti->private;
 
@@ -159,6 +161,10 @@ static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
 		if (m->hw_handler_name)
 			scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
 		dm_put_device(ti, pgpath->path.dev);
+		spin_lock_irqsave(&m->lock, flags);
+		if (m->pgpath_to_activate == pgpath)
+			m->pgpath_to_activate = NULL;
+		spin_unlock_irqrestore(&m->lock, flags);
 		free_pgpath(pgpath);
 	}
 }
@@ -425,6 +431,7 @@ static void process_queued_ios(void *data)
 		__choose_pgpath(m);
 
 	pgpath = m->current_pgpath;
+	m->pgpath_to_activate = m->current_pgpath;
 
 	if ((pgpath && !m->queue_io) ||
 	    (!pgpath && !m->queue_if_no_path))
@@ -1174,7 +1181,15 @@ static void activate_path(void *data)
 {
 	int ret;
 	struct multipath *m = (struct multipath *) data;
-	struct path *path = &m->current_pgpath->path;
+	struct path *path;
+	unsigned long flags;
+
+	spin_lock_irqsave(&m->lock, flags);
+	path = &m->pgpath_to_activate->path;
+	m->pgpath_to_activate = NULL;
+	spin_unlock_irqrestore(&m->lock, flags);
+	if (!path)
+		return;
 
 	ret = scsi_dh_activate(bdev_get_queue(path->dev->bdev));
 	pg_init_done(path, ret);