diff mbox

dm-multipath: Accept failed paths for multipath maps

Message ID 20140718002319.GA20071@redhat.com (mailing list archive)
State Rejected, archived
Delegated to: Mike Snitzer
Headers show

Commit Message

Mike Snitzer July 18, 2014, 12:23 a.m. UTC
On Thu, Jul 17 2014 at  8:04pm -0400,
Mike Snitzer <snitzer@redhat.com> wrote:
 
> Revisiting this can of worms...
> 
> As part of full due-diligence on the approach that SUSE and NetApp have
> seemingly enjoyed "for years" I reviewed Hannes' v3 patch, fixed one
> issue and did some cleanup.  I then converted over to using a slightly
> different approach where-in the DM core becomes a more willing
> co-conspirator in this hack by introducing the ability to have
> place-holder devices (dm_dev without an opened bdev) referenced in a DM
> table.  The work is here:
> http://git.kernel.org/cgit/linux/kernel/git/snitzer/linux.git/log/?h=throwaway-dm-mpath-placeholder-devs

Here is the rolled up patch (the individual commits in the above branch
are rather noisy given the sequencing):

 drivers/md/dm-mpath.c | 51 +++++++++++++++++++++++++++++++++----------------
 drivers/md/dm-table.c | 53 ++++++++++++++++++++++++++++++++++++++-------------
 drivers/md/dm.c       |  5 ++---
 drivers/md/dm.h       | 12 ++++++++++++
 4 files changed, 89 insertions(+), 32 deletions(-)


--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel

Comments

Hannes Reinecke July 18, 2014, 6 a.m. UTC | #1
On 07/18/2014 02:23 AM, Mike Snitzer wrote:
> On Thu, Jul 17 2014 at  8:04pm -0400,
> Mike Snitzer <snitzer@redhat.com> wrote:
>
>> Revisiting this can of worms...
>>
>> As part of full due-diligence on the approach that SUSE and NetApp have
>> seemingly enjoyed "for years" I reviewed Hannes' v3 patch, fixed one
>> issue and did some cleanup.  I then converted over to using a slightly
>> different approach where-in the DM core becomes a more willing
>> co-conspirator in this hack by introducing the ability to have
>> place-holder devices (dm_dev without an opened bdev) referenced in a DM
>> table.  The work is here:
>> http://git.kernel.org/cgit/linux/kernel/git/snitzer/linux.git/log/?h=throwaway-dm-mpath-placeholder-devs
>
> Here is the rolled up patch (the individual commits in the above branch
> are rather noisy given the sequencing):
>
>   drivers/md/dm-mpath.c | 51 +++++++++++++++++++++++++++++++++----------------
>   drivers/md/dm-table.c | 53 ++++++++++++++++++++++++++++++++++++++-------------
>   drivers/md/dm.c       |  5 ++---
>   drivers/md/dm.h       | 12 ++++++++++++
>   4 files changed, 89 insertions(+), 32 deletions(-)
>
These patches look quite okay; I'll be cross-checking with my 
version and do some testing there.

Will be sending some update once the testing is done.

Cheers,

Hannes
Mike Snitzer July 18, 2014, 4:04 p.m. UTC | #2
On Fri, Jul 18 2014 at  2:00am -0400,
Hannes Reinecke <hare@suse.de> wrote:

> On 07/18/2014 02:23 AM, Mike Snitzer wrote:
> >On Thu, Jul 17 2014 at  8:04pm -0400,
> >Mike Snitzer <snitzer@redhat.com> wrote:
> >
> >>Revisiting this can of worms...
> >>
> >>As part of full due-diligence on the approach that SUSE and NetApp have
> >>seemingly enjoyed "for years" I reviewed Hannes' v3 patch, fixed one
> >>issue and did some cleanup.  I then converted over to using a slightly
> >>different approach where-in the DM core becomes a more willing
> >>co-conspirator in this hack by introducing the ability to have
> >>place-holder devices (dm_dev without an opened bdev) referenced in a DM
> >>table.  The work is here:
> >>http://git.kernel.org/cgit/linux/kernel/git/snitzer/linux.git/log/?h=throwaway-dm-mpath-placeholder-devs
> >
> >Here is the rolled up patch (the individual commits in the above branch
> >are rather noisy given the sequencing):
> >
> >  drivers/md/dm-mpath.c | 51 +++++++++++++++++++++++++++++++++----------------
> >  drivers/md/dm-table.c | 53 ++++++++++++++++++++++++++++++++++++++-------------
> >  drivers/md/dm.c       |  5 ++---
> >  drivers/md/dm.h       | 12 ++++++++++++
> >  4 files changed, 89 insertions(+), 32 deletions(-)
> >
> These patches look quite okay; I'll be cross-checking with my
> version and do some testing there.

Thanks, the intent was to make your approach more "official".  I did
have a couple fixes to the DM core patch so I rebased, see:
https://git.kernel.org/cgit/linux/kernel/git/snitzer/linux.git/log/?h=throwaway-dm-mpath-placeholder-devs

But as I said in earlier posts, I'm really not in love with using
place-holder devices (mainly because allowing references to dead devices
_seems_ to go against proper device stacking).  But I do think that DM
core _should_ acknowledge that everything isn't always going to be
perfect for all targets.  Especially a target like multipath whose
purpose is to cope with the potential for paths coming/going.

So there definitely is a case to be made for DM being more tolerate of
the potential for devices being inaccessible.

Now this is the last I'm going to channel Alasdair's argument for him on
this thread but: Alasdair would prefer to see userspace multipathd fixed
(more intelligent batched processing of uevents, etc).  As a second 
option, his preferred kernel change is a "handover" style approach that
hands the path group state over from the old table to the new.

I agree that userspace multipathd should be fixed to process events in
batches but I see that even then the kernel should accommodate potential
for imperfect table loads for targets like multipath.  I happen to
disagree with handover (I flipped and now flopped back on this point).
Handover would be serious churn (that agk somehow thinks more
worthwhile).  While I appreciate that mechanically handover is a
different means to the same end of having references to dm_devs that are
"removed failed devices" handover would be overly complicated and
brittle (and very disruptive to DM core because the dm_devs aren't
hanging directly off of a multipath structure, they are associated with
the DM table!)

In that light I do actually prefer the blunt/simple approach taken in my
'throwaway-dm-mpath-placeholder-devs' branch.  But I'm concerned that
accepting a workaround like this will just enable multipathd to never
get the attention it needs for improved uevent processing.  If others
commit to actually making multipathd's uevent processing better that'd
go a long way.

> Will be sending some update once the testing is done.

OK.  The stronger case that can be made for the stability of this change
(no missing checks for dev->bdev, etc) the better.

I did just do some basic testing, of both dm-mpath and dm-thinp, seems
fine.  The DM core changes really are simple (just some extra negative
checks that won't ever apply for the normal case -- where "normal case"
is DM targets that use dm_get_device rather than __dm_get_device, and
had to add a dev_t to struct dm_dev).

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
Mike Snitzer July 18, 2014, 4:15 p.m. UTC | #3
On Fri, Jul 18 2014 at 12:04pm -0400,
Mike Snitzer <snitzer@redhat.com> wrote:

> On Fri, Jul 18 2014 at  2:00am -0400,
> Hannes Reinecke <hare@suse.de> wrote:
> 
> > Will be sending some update once the testing is done.
> 
> OK.  The stronger case that can be made for the stability of this change
> (no missing checks for dev->bdev, etc) the better.
> 
> I did just do some basic testing, of both dm-mpath and dm-thinp, seems
> fine.  The DM core changes really are simple (just some extra negative
> checks that won't ever apply for the normal case -- where "normal case"
> is DM targets that use dm_get_device rather than __dm_get_device, and
> had to add a dev_t to struct dm_dev).

BTW, I noticed one thing that I didn't think of as logical fallout, but
it makes sense: if configure multipthd to not use find_multipaths then
multipathd will successfully push down an mpath table that covers the
system disk (sda) -- but mpatha isn't able to dm_get_device:

# lsblk /dev/sda /dev/mapper/mpatha
NAME                            MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
sda                               8:0    0 930.5G  0 disk  
??sda1                            8:1    0   500M  0 part  /boot
??sda2                            8:2    0   930G  0 part  
  ??rhel_rhel--storage--02-swap 253:0    0     4G  0 lvm   [SWAP]
  ??rhel_rhel--storage--02-root 253:1    0   350G  0 lvm   /
mpatha                          253:6    0 930.5G  0 mpath 

# multipath -ll mpatha
mpatha (36003005700ec189015d42b92237a76d1) dm-6 LSI     ,RAID 5/6 SAS 6G 
size=930G features='0' hwhandler='0' wp=rw
`-+- policy='service-time 0' prio=1 status=enabled
  `- 0:2:0:0 sda  8:0   failed ready running

And multipathd keeps failing to reinstate_path:
...
[ 1906.647322] device-mapper: multipath: message: error getting device 8:0
[ 1912.652652] device-mapper: multipath: message: error getting device 8:0
[ 1918.657949] device-mapper: multipath: message: error getting device 8:0
[ 1924.664118] device-mapper: multipath: message: error getting device 8:0
[ 1930.669635] device-mapper: multipath: message: error getting device 8:0

Again, this all makes sense... but certainly shows that blacklisting the
relevant devices is a must.

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel
Hannes Reinecke July 21, 2014, 6:05 a.m. UTC | #4
On 07/18/2014 06:15 PM, Mike Snitzer wrote:
> On Fri, Jul 18 2014 at 12:04pm -0400,
> Mike Snitzer <snitzer@redhat.com> wrote:
>
>> On Fri, Jul 18 2014 at  2:00am -0400,
>> Hannes Reinecke <hare@suse.de> wrote:
>>
>>> Will be sending some update once the testing is done.
>>
>> OK.  The stronger case that can be made for the stability of this change
>> (no missing checks for dev->bdev, etc) the better.
>>
>> I did just do some basic testing, of both dm-mpath and dm-thinp, seems
>> fine.  The DM core changes really are simple (just some extra negative
>> checks that won't ever apply for the normal case -- where "normal case"
>> is DM targets that use dm_get_device rather than __dm_get_device, and
>> had to add a dev_t to struct dm_dev).
>
> BTW, I noticed one thing that I didn't think of as logical fallout, but
> it makes sense: if configure multipthd to not use find_multipaths then
> multipathd will successfully push down an mpath table that covers the
> system disk (sda) -- but mpatha isn't able to dm_get_device:
>
> # lsblk /dev/sda /dev/mapper/mpatha
> NAME                            MAJ:MIN RM   SIZE RO TYPE  MOUNTPOINT
> sda                               8:0    0 930.5G  0 disk
> ??sda1                            8:1    0   500M  0 part  /boot
> ??sda2                            8:2    0   930G  0 part
>    ??rhel_rhel--storage--02-swap 253:0    0     4G  0 lvm   [SWAP]
>    ??rhel_rhel--storage--02-root 253:1    0   350G  0 lvm   /
> mpatha                          253:6    0 930.5G  0 mpath
>
> # multipath -ll mpatha
> mpatha (36003005700ec189015d42b92237a76d1) dm-6 LSI     ,RAID 5/6 SAS 6G
> size=930G features='0' hwhandler='0' wp=rw
> `-+- policy='service-time 0' prio=1 status=enabled
>    `- 0:2:0:0 sda  8:0   failed ready running
>
> And multipathd keeps failing to reinstate_path:
> ...
> [ 1906.647322] device-mapper: multipath: message: error getting device 8:0
> [ 1912.652652] device-mapper: multipath: message: error getting device 8:0
> [ 1918.657949] device-mapper: multipath: message: error getting device 8:0
> [ 1924.664118] device-mapper: multipath: message: error getting device 8:0
> [ 1930.669635] device-mapper: multipath: message: error getting device 8:0
>
> Again, this all makes sense... but certainly shows that blacklisting the
> relevant devices is a must.
>
Precisely.
We have been taking the approach that starting multipath will 
attempt to take over _any_ (eligible) device.
So this error would constitute a misconfiguration from our POV.
Either the device needs to be blacklisted or the process accessing 
the device needs to be reconfigured to use multipathing.

Cheers,

Hannes
diff mbox

Patch

diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 833d7e7..6c3ddd2 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -162,7 +162,7 @@  static void free_pgpaths(struct list_head *pgpaths, struct dm_target *ti)
 
 	list_for_each_entry_safe(pgpath, tmp, pgpaths, list) {
 		list_del(&pgpath->list);
-		if (m->hw_handler_name)
+		if (m->hw_handler_name && pgpath->path.dev->bdev)
 			scsi_dh_detach(bdev_get_queue(pgpath->path.dev->bdev));
 		dm_put_device(ti, pgpath->path.dev);
 		free_pgpath(pgpath);
@@ -306,6 +306,11 @@  static int __choose_path_in_pg(struct multipath *m, struct priority_group *pg,
 
 	m->current_pgpath = path_to_pgpath(path);
 
+	if (!m->current_pgpath->path.dev->bdev) {
+		m->current_pgpath = NULL;
+		return -ENODEV;
+	}
+
 	if (m->current_pg != pg)
 		__switch_pg(m, m->current_pgpath);
 
@@ -509,6 +514,9 @@  static int parse_path_selector(struct dm_arg_set *as, struct priority_group *pg,
 	return 0;
 }
 
+static void __fail_path(struct pgpath *pgpath, struct multipath *m,
+			struct path_selector *ps);
+
 static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps,
 			       struct dm_target *ti)
 {
@@ -528,17 +536,19 @@  static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
 	if (!p)
 		return ERR_PTR(-ENOMEM);
 
-	r = dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
-			  &p->path.dev);
+	r = __dm_get_device(ti, dm_shift_arg(as), dm_table_get_mode(ti->table),
+			    &p->path.dev, true);
 	if (r) {
 		ti->error = "error getting device";
 		goto bad;
 	}
 
-	if (m->retain_attached_hw_handler || m->hw_handler_name)
+	if (p->path.dev->bdev)
 		q = bdev_get_queue(p->path.dev->bdev);
+	else
+		p->is_active = 0;
 
-	if (m->retain_attached_hw_handler) {
+	if (q && m->retain_attached_hw_handler) {
 		attached_handler_name = scsi_dh_attached_handler_name(q, GFP_KERNEL);
 		if (attached_handler_name) {
 			/*
@@ -557,7 +567,7 @@  static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
 		}
 	}
 
-	if (m->hw_handler_name) {
+	if (q && m->hw_handler_name) {
 		/*
 		 * Increments scsi_dh reference, even when using an
 		 * already-attached handler.
@@ -596,6 +606,9 @@  static struct pgpath *parse_path(struct dm_arg_set *as, struct path_selector *ps
 		goto bad;
 	}
 
+	if (!p->is_active)
+		__fail_path(p, m, ps);
+
 	return p;
 
  bad:
@@ -912,6 +925,15 @@  static void multipath_dtr(struct dm_target *ti)
 	free_multipath(m);
 }
 
+static void __fail_path(struct pgpath *pgpath, struct multipath *m,
+			struct path_selector *ps)
+{
+	ps->type->fail_path(ps, &pgpath->path);
+	pgpath->fail_count++;
+
+	m->nr_valid_paths--;
+}
+
 /*
  * Take a path out of use.
  */
@@ -927,17 +949,14 @@  static int fail_path(struct pgpath *pgpath)
 
 	DMWARN("Failing path %s.", pgpath->path.dev->name);
 
-	pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path);
 	pgpath->is_active = 0;
-	pgpath->fail_count++;
-
-	m->nr_valid_paths--;
+	__fail_path(pgpath, m, &pgpath->pg->ps);
 
 	if (pgpath == m->current_pgpath)
 		m->current_pgpath = NULL;
 
 	dm_path_uevent(DM_UEVENT_PATH_FAILED, m->ti,
-		      pgpath->path.dev->name, m->nr_valid_paths);
+		       pgpath->path.dev->name, m->nr_valid_paths);
 
 	schedule_work(&m->trigger_event);
 
@@ -983,7 +1002,7 @@  static int reinstate_path(struct pgpath *pgpath)
 	}
 
 	dm_path_uevent(DM_UEVENT_PATH_REINSTATED, m->ti,
-		      pgpath->path.dev->name, m->nr_valid_paths);
+		       pgpath->path.dev->name, m->nr_valid_paths);
 
 	schedule_work(&m->trigger_event);
 
@@ -1195,7 +1214,7 @@  static void activate_path(struct work_struct *work)
 	struct pgpath *pgpath =
 		container_of(work, struct pgpath, activate_path.work);
 
-	if (pgpath->is_active)
+	if (pgpath->is_active && pgpath->path.dev->bdev)
 		scsi_dh_activate(bdev_get_queue(pgpath->path.dev->bdev),
 				 pg_init_done, pgpath);
 	else
@@ -1528,7 +1547,7 @@  static int multipath_ioctl(struct dm_target *ti, unsigned int cmd,
 
 	pgpath = m->current_pgpath;
 
-	if (pgpath) {
+	if (pgpath && pgpath->path.dev->bdev) {
 		bdev = pgpath->path.dev->bdev;
 		mode = pgpath->path.dev->mode;
 	}
@@ -1586,9 +1605,9 @@  out:
 
 static int __pgpath_busy(struct pgpath *pgpath)
 {
-	struct request_queue *q = bdev_get_queue(pgpath->path.dev->bdev);
+	struct request_queue *q = dm_dev_get_queue(pgpath->path.dev);
 
-	return dm_underlying_device_busy(q);
+	return q && dm_underlying_device_busy(q);
 }
 
 /*
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 3c72bf1..1ef1601 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -262,7 +262,7 @@  static struct dm_dev_internal *find_device(struct list_head *l, dev_t dev)
 	struct dm_dev_internal *dd;
 
 	list_for_each_entry (dd, l, list)
-		if (dd->dm_dev.bdev->bd_dev == dev)
+		if (dd->dm_dev.bdev && dd->dm_dev.bdev->bd_dev == dev)
 			return dd;
 
 	return NULL;
@@ -317,12 +317,16 @@  static int device_area_is_invalid(struct dm_target *ti, struct dm_dev *dev,
 	struct request_queue *q;
 	struct queue_limits *limits = data;
 	struct block_device *bdev = dev->bdev;
-	sector_t dev_size =
-		i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
+	sector_t dev_size;
 	unsigned short logical_block_size_sectors =
 		limits->logical_block_size >> SECTOR_SHIFT;
 	char b[BDEVNAME_SIZE];
 
+	if (!bdev)
+		return 0;
+
+	dev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
+
 	/*
 	 * Some devices exist without request functions,
 	 * such as loop devices not yet bound to backing files.
@@ -393,6 +397,9 @@  static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
 	dd_new.dm_dev.mode |= new_mode;
 	dd_new.dm_dev.bdev = NULL;
 
+	if (!dd->dm_dev.bdev)
+		return 0;
+
 	r = open_dev(&dd_new, dd->dm_dev.bdev->bd_dev, md);
 	if (r)
 		return r;
@@ -407,8 +414,8 @@  static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
  * Add a device to the list, or just increment the usage count if
  * it's already present.
  */
-int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
-		  struct dm_dev **result)
+int __dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+		    struct dm_dev **result, bool open_may_fail)
 {
 	int r;
 	dev_t uninitialized_var(dev);
@@ -443,7 +450,8 @@  int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
 		dd->dm_dev.mode = mode;
 		dd->dm_dev.bdev = NULL;
 
-		if ((r = open_dev(dd, dev, t->md))) {
+		r = open_dev(dd, dev, t->md);
+		if (r && !open_may_fail) {
 			kfree(dd);
 			return r;
 		}
@@ -463,6 +471,13 @@  int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
 	*result = &dd->dm_dev;
 	return 0;
 }
+EXPORT_SYMBOL_GPL(__dm_get_device);
+
+int dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+		  struct dm_dev **result)
+{
+	return __dm_get_device(ti, path, mode, result, false);
+}
 EXPORT_SYMBOL(dm_get_device);
 
 static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
@@ -470,9 +485,13 @@  static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
 {
 	struct queue_limits *limits = data;
 	struct block_device *bdev = dev->bdev;
-	struct request_queue *q = bdev_get_queue(bdev);
+	struct request_queue *q;
 	char b[BDEVNAME_SIZE];
 
+	if (!bdev)
+		return 0;
+
+	q = bdev_get_queue(bdev);
 	if (unlikely(!q)) {
 		DMWARN("%s: Cannot set limits for nonexistent device %s",
 		       dm_device_name(ti->table->md), bdevname(bdev, b));
@@ -906,6 +925,8 @@  static int dm_table_set_type(struct dm_table *t)
 	/* Non-request-stackable devices can't be used for request-based dm */
 	devices = dm_table_get_devices(t);
 	list_for_each_entry(dd, devices, list) {
+		if (!dd->dm_dev.bdev)
+			continue;
 		if (!blk_queue_stackable(bdev_get_queue(dd->dm_dev.bdev))) {
 			DMWARN("table load rejected: including"
 			       " non-request-stackable devices");
@@ -1043,6 +1064,8 @@  static struct gendisk * dm_table_get_integrity_disk(struct dm_table *t,
 	struct gendisk *prev_disk = NULL, *template_disk = NULL;
 
 	list_for_each_entry(dd, devices, list) {
+		if (!dd->dm_dev.bdev)
+			continue;
 		template_disk = dd->dm_dev.bdev->bd_disk;
 		if (!blk_get_integrity(template_disk))
 			goto no_integrity;
@@ -1321,7 +1344,7 @@  static int device_flush_capable(struct dm_target *ti, struct dm_dev *dev,
 				sector_t start, sector_t len, void *data)
 {
 	unsigned flush = (*(unsigned *)data);
-	struct request_queue *q = bdev_get_queue(dev->bdev);
+	struct request_queue *q = dm_dev_get_queue(dev);
 
 	return q && (q->flush_flags & flush);
 }
@@ -1373,7 +1396,7 @@  static bool dm_table_discard_zeroes_data(struct dm_table *t)
 static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
 			    sector_t start, sector_t len, void *data)
 {
-	struct request_queue *q = bdev_get_queue(dev->bdev);
+	struct request_queue *q = dm_dev_get_queue(dev);
 
 	return q && blk_queue_nonrot(q);
 }
@@ -1381,7 +1404,7 @@  static int device_is_nonrot(struct dm_target *ti, struct dm_dev *dev,
 static int device_is_not_random(struct dm_target *ti, struct dm_dev *dev,
 			     sector_t start, sector_t len, void *data)
 {
-	struct request_queue *q = bdev_get_queue(dev->bdev);
+	struct request_queue *q = dm_dev_get_queue(dev);
 
 	return q && !blk_queue_add_random(q);
 }
@@ -1406,7 +1429,7 @@  static bool dm_table_all_devices_attribute(struct dm_table *t,
 static int device_not_write_same_capable(struct dm_target *ti, struct dm_dev *dev,
 					 sector_t start, sector_t len, void *data)
 {
-	struct request_queue *q = bdev_get_queue(dev->bdev);
+	struct request_queue *q = dm_dev_get_queue(dev);
 
 	return q && !q->limits.max_write_same_sectors;
 }
@@ -1433,7 +1456,7 @@  static bool dm_table_supports_write_same(struct dm_table *t)
 static int device_discard_capable(struct dm_target *ti, struct dm_dev *dev,
 				  sector_t start, sector_t len, void *data)
 {
-	struct request_queue *q = bdev_get_queue(dev->bdev);
+	struct request_queue *q = dm_dev_get_queue(dev);
 
 	return q && blk_queue_discard(q);
 }
@@ -1616,9 +1639,13 @@  int dm_table_any_congested(struct dm_table *t, int bdi_bits)
 	int r = 0;
 
 	list_for_each_entry(dd, devices, list) {
-		struct request_queue *q = bdev_get_queue(dd->dm_dev.bdev);
+		struct request_queue *q;
 		char b[BDEVNAME_SIZE];
 
+		if (!dd->dm_dev.bdev)
+			continue;
+
+		q = bdev_get_queue(dd->dm_dev.bdev);
 		if (likely(q))
 			r |= bdi_congested(&q->backing_dev_info, bdi_bits);
 		else
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 32b958d..60989fa 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -2148,10 +2148,9 @@  static int dm_device_merge_is_compulsory(struct dm_target *ti,
 					 struct dm_dev *dev, sector_t start,
 					 sector_t len, void *data)
 {
-	struct block_device *bdev = dev->bdev;
-	struct request_queue *q = bdev_get_queue(bdev);
+	struct request_queue *q = dm_dev_get_queue(dev);
 
-	return dm_queue_merge_is_compulsory(q);
+	return q && dm_queue_merge_is_compulsory(q);
 }
 
 /*
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index e81d215..aa30a05 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -85,6 +85,18 @@  struct target_type *dm_get_immutable_target_type(struct mapped_device *md);
 
 int dm_setup_md_queue(struct mapped_device *md);
 
+static inline struct request_queue *dm_dev_get_queue(struct dm_dev *dev)
+{
+	return dev->bdev ? bdev_get_queue(dev->bdev) : NULL;
+}
+
+/*
+ * Variant of dm_get_device that allows place-holder devices to be referenced,
+ * but you _really_ shouldn't need to use this!
+ */
+int __dm_get_device(struct dm_target *ti, const char *path, fmode_t mode,
+		    struct dm_dev **result, bool open_may_fail);
+
 /*
  * To check the return value from dm_table_find_target().
  */