From: Mike Snitzer <[email-masked]>

DM recalculates queue limits based only on devices which currently exist
in the table.  This creates a problem in the event all devices are
temporarily removed such as all fibre channel paths being lost in
multipath.  DM will reset the limits to the maximum permissible, which
can then assemble requests which exceed the limits of the paths when the
paths are restored.  The request will fail the blk_rq_check_limits()
test when sent to a path with lower limits, and will be retried without
end by multipath.  This became a much bigger issue after commit
fe86cdcef ("block: do not artificially constrain max_sectors for
stacking drivers").

Add a safety net that will establish safe default limits, via
blk_set_default_limits, in the event that a table temporarily doesn't
have any component devices.  The default limits may be too conservative
once paths are reinstated but adding a path to a multipath device
requires a table reload.  Once the table is reloaded proper limits
will be established based on the available component device(s).

Reported-by: David Jeffery <[email-masked]>
Signed-off-by: Mike Snitzer <[email-masked]>

---
drivers/md/dm-table.c |    9 +++++++++
 drivers/md/dm-table.c |    9 +++++++++
 1 file changed, 9 insertions(+)

v2: adjust the patch header to be more succinct

FIXME Still incoherent logic.
  - Sort out the reasoning and fix both the comments and header.
    - in general terms, not mpath terms
  - Where does UINT_MAX come from?

Index: linux/drivers/md/dm-table.c
===================================================================
--- linux.orig/drivers/md/dm-table.c
+++ linux/drivers/md/dm-table.c
@@ -1294,6 +1294,15 @@ combine_limits:
 			       (unsigned long long) ti->len);
 	}
 
+	/*
+	 * If a table doesn't have any component devices (e.g. multipath
+	 * loses all paths) don't allow the queue_limits to be left at
+	 * their maximum (as established by blk_set_stacking_limits() so
+	 * limits could be inherited from component devices).
+	 */
+	if (limits->max_sectors == UINT_MAX)
+		blk_set_default_limits(limits);
+
 	return validate_hardware_logical_block_alignment(table, limits);
 }