Commit c3cd4f6b authored by Alasdair G Kergon's avatar Alasdair G Kergon Committed by Linus Torvalds

[PATCH] device-mapper multipath: Fix pg initialisation races

Prevent more than one priority group initialisation function from being
outstanding at once.  Otherwise the completion functions interfere with each
other.  Also, reloading the table could reference a freed pointer.

Only reset queue_io in pg_init_complete if another pg_init isn't required.
Skip process_queued_ios if the queue is empty so that we only trigger a
pg_init if there's I/O.
Signed-off-by: default avatarLars Marowsky-Bree <lmb@suse.de>
Signed-off-by: default avatarAlasdair G Kergon <agk@redhat.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 436d4108
...@@ -63,6 +63,7 @@ struct multipath { ...@@ -63,6 +63,7 @@ struct multipath {
unsigned nr_priority_groups; unsigned nr_priority_groups;
struct list_head priority_groups; struct list_head priority_groups;
unsigned pg_init_required; /* pg_init needs calling? */ unsigned pg_init_required; /* pg_init needs calling? */
unsigned pg_init_in_progress; /* Only one pg_init allowed at once */
unsigned nr_valid_paths; /* Total number of usable paths */ unsigned nr_valid_paths; /* Total number of usable paths */
struct pgpath *current_pgpath; struct pgpath *current_pgpath;
...@@ -308,7 +309,8 @@ static int map_io(struct multipath *m, struct bio *bio, struct mpath_io *mpio, ...@@ -308,7 +309,8 @@ static int map_io(struct multipath *m, struct bio *bio, struct mpath_io *mpio,
/* Queue for the daemon to resubmit */ /* Queue for the daemon to resubmit */
bio_list_add(&m->queued_ios, bio); bio_list_add(&m->queued_ios, bio);
m->queue_size++; m->queue_size++;
if (m->pg_init_required || !m->queue_io) if ((m->pg_init_required && !m->pg_init_in_progress) ||
!m->queue_io)
queue_work(kmultipathd, &m->process_queued_ios); queue_work(kmultipathd, &m->process_queued_ios);
pgpath = NULL; pgpath = NULL;
r = 0; r = 0;
...@@ -335,7 +337,7 @@ static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path) ...@@ -335,7 +337,7 @@ static int queue_if_no_path(struct multipath *m, unsigned queue_if_no_path)
m->saved_queue_if_no_path = m->queue_if_no_path; m->saved_queue_if_no_path = m->queue_if_no_path;
m->queue_if_no_path = queue_if_no_path; m->queue_if_no_path = queue_if_no_path;
if (!m->queue_if_no_path) if (!m->queue_if_no_path && m->queue_size)
queue_work(kmultipathd, &m->process_queued_ios); queue_work(kmultipathd, &m->process_queued_ios);
spin_unlock_irqrestore(&m->lock, flags); spin_unlock_irqrestore(&m->lock, flags);
...@@ -380,25 +382,31 @@ static void process_queued_ios(void *data) ...@@ -380,25 +382,31 @@ static void process_queued_ios(void *data)
{ {
struct multipath *m = (struct multipath *) data; struct multipath *m = (struct multipath *) data;
struct hw_handler *hwh = &m->hw_handler; struct hw_handler *hwh = &m->hw_handler;
struct pgpath *pgpath; struct pgpath *pgpath = NULL;
unsigned init_required, must_queue = 0; unsigned init_required = 0, must_queue = 1;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&m->lock, flags); spin_lock_irqsave(&m->lock, flags);
if (!m->queue_size)
goto out;
if (!m->current_pgpath) if (!m->current_pgpath)
__choose_pgpath(m); __choose_pgpath(m);
pgpath = m->current_pgpath; pgpath = m->current_pgpath;
if ((pgpath && m->queue_io) || if ((pgpath && !m->queue_io) ||
(!pgpath && m->queue_if_no_path)) (!pgpath && !m->queue_if_no_path))
must_queue = 1; must_queue = 0;
init_required = m->pg_init_required; if (m->pg_init_required && !m->pg_init_in_progress) {
if (init_required)
m->pg_init_required = 0; m->pg_init_required = 0;
m->pg_init_in_progress = 1;
init_required = 1;
}
out:
spin_unlock_irqrestore(&m->lock, flags); spin_unlock_irqrestore(&m->lock, flags);
if (init_required) if (init_required)
...@@ -843,7 +851,7 @@ static int reinstate_path(struct pgpath *pgpath) ...@@ -843,7 +851,7 @@ static int reinstate_path(struct pgpath *pgpath)
pgpath->path.is_active = 1; pgpath->path.is_active = 1;
m->current_pgpath = NULL; m->current_pgpath = NULL;
if (!m->nr_valid_paths++) if (!m->nr_valid_paths++ && m->queue_size)
queue_work(kmultipathd, &m->process_queued_ios); queue_work(kmultipathd, &m->process_queued_ios);
queue_work(kmultipathd, &m->trigger_event); queue_work(kmultipathd, &m->trigger_event);
...@@ -969,12 +977,13 @@ void dm_pg_init_complete(struct path *path, unsigned err_flags) ...@@ -969,12 +977,13 @@ void dm_pg_init_complete(struct path *path, unsigned err_flags)
bypass_pg(m, pg, 1); bypass_pg(m, pg, 1);
spin_lock_irqsave(&m->lock, flags); spin_lock_irqsave(&m->lock, flags);
if (!err_flags) if (err_flags) {
m->queue_io = 0;
else {
m->current_pgpath = NULL; m->current_pgpath = NULL;
m->current_pg = NULL; m->current_pg = NULL;
} } else if (!m->pg_init_required)
m->queue_io = 0;
m->pg_init_in_progress = 0;
queue_work(kmultipathd, &m->process_queued_ios); queue_work(kmultipathd, &m->process_queued_ios);
spin_unlock_irqrestore(&m->lock, flags); spin_unlock_irqrestore(&m->lock, flags);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment