To avoid deadlock when suspending a multipath device when all its paths have failed, stop queueing any I/O that is about to fail *before* calling freeze_bdev instead of after. To facilitate this without adding a new 'resume' function corresponding to 'presuspend', instead of setting a multipath 'suspended' flag which must always be reset if an error occurs during the process, save the previous queueing state and leave userspace to restore if it wishes. [submitted to -mm] --- diff/drivers/md/dm-mpath.c 2005-07-06 17:19:59.000000000 +0100 +++ source/drivers/md/dm-mpath.c 2005-07-06 17:21:49.000000000 +0100 @@ -72,7 +72,7 @@ unsigned queue_io; /* Must we queue all I/O? */ unsigned queue_if_no_path; /* Queue I/O if last path fails? */ - unsigned suspended; /* Has dm core suspended our I/O? */ + unsigned saved_queue_if_no_path;/* Saved state during suspension */ struct work_struct process_queued_ios; struct bio_list queued_ios; @@ -304,7 +304,7 @@ m->queue_size--; if ((pgpath && m->queue_io) || - (!pgpath && m->queue_if_no_path && !m->suspended)) { + (!pgpath && m->queue_if_no_path)) { /* Queue for the daemon to resubmit */ bio_list_add(&m->queued_ios, bio); m->queue_size++; @@ -333,6 +333,7 @@ spin_lock_irqsave(&m->lock, flags); + m->saved_queue_if_no_path = m->queue_if_no_path; m->queue_if_no_path = queue_if_no_path; if (!m->queue_if_no_path) queue_work(kmultipathd, &m->process_queued_ios); @@ -391,7 +392,7 @@ pgpath = m->current_pgpath; if ((pgpath && m->queue_io) || - (!pgpath && m->queue_if_no_path && !m->suspended)) + (!pgpath && m->queue_if_no_path)) must_queue = 1; init_required = m->pg_init_required; @@ -1021,7 +1022,7 @@ if (!m->nr_valid_paths) { DMWARN("dm-multipath: no valid paths left"); - if (!m->queue_if_no_path || m->suspended) { + if (!m->queue_if_no_path) { spin_unlock(&m->lock); return -EIO; } else { @@ -1086,27 +1087,27 @@ /* * Suspend can't complete until all the I/O is processed so if - * the last path failed we will now error any queued I/O. + * the last path fails we must error any remaining I/O. + * Note that if the freeze_bdev fails while suspending, the + * queue_if_no_path state is lost - userspace should reset it. */ static void multipath_presuspend(struct dm_target *ti) { struct multipath *m = (struct multipath *) ti->private; - unsigned long flags; - spin_lock_irqsave(&m->lock, flags); - m->suspended = 1; - if (m->queue_if_no_path) - queue_work(kmultipathd, &m->process_queued_ios); - spin_unlock_irqrestore(&m->lock, flags); + queue_if_no_path(m, 0); } +/* + * Restore the queue_if_no_path setting. + */ static void multipath_resume(struct dm_target *ti) { struct multipath *m = (struct multipath *) ti->private; unsigned long flags; spin_lock_irqsave(&m->lock, flags); - m->suspended = 0; + m->queue_if_no_path = m->saved_queue_if_no_path; spin_unlock_irqrestore(&m->lock, flags); } --- diff/drivers/md/dm.c 2005-07-06 17:18:50.000000000 +0100 +++ source/drivers/md/dm.c 2005-07-06 17:21:49.000000000 +0100 @@ -1055,14 +1055,17 @@ if (test_bit(DMF_BLOCK_IO, &md->flags)) goto out_read_unlock; - error = __lock_fs(md); - if (error) - goto out_read_unlock; - map = dm_get_table(md); if (map) + /* This does not get reverted if there's an error later. */ dm_table_presuspend_targets(map); + error = __lock_fs(md); + if (error) { + dm_table_put(map); + goto out_read_unlock; + } + up_read(&md->lock); /* @@ -1121,7 +1124,6 @@ return 0; out_unfreeze: - /* FIXME Undo dm_table_presuspend_targets */ __unlock_fs(md); clear_bit(DMF_BLOCK_IO, &md->flags); out_write_unlock: