dm: allow place-holder devices to be added to the internal dev list
authorMike Snitzer <snitzer@redhat.com>
Thu, 17 Jul 2014 22:58:24 +0000 (18:58 -0400)
committerMike Snitzer <snitzer@redhat.com>
Fri, 18 Jul 2014 15:56:06 +0000 (11:56 -0400)
drivers/md/dm-ioctl.c
drivers/md/dm-table.c
drivers/md/dm.c
drivers/md/dm.h
include/linux/device-mapper.h

index 51521429fb59d01ea65e8d1243a1151272f9beca..33eb68174a1546206e72797a59cb49c7b6955a4f 100644 (file)
@@ -1418,7 +1418,7 @@ static void retrieve_deps(struct dm_table *table,
        deps->count = count;
        count = 0;
        list_for_each_entry (dd, dm_table_get_devices(table), list)
-               deps->dev[count++] = huge_encode_dev(dd->dm_dev.bdev->bd_dev);
+               deps->dev[count++] = huge_encode_dev(dd->dm_dev.bd_dev);
 
        param->data_size = param->data_start + needed;
 }
index 145a0406f65afe6696ab215d2042df6313e6dee8..298f1810025b161f380adf1a3dd1b792ab596aa7 100644 (file)
@@ -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.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,16 +450,24 @@ 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;
                }
 
                format_dev_t(dd->dm_dev.name, dev);
+               dd->dm_dev.bd_dev = dev;
 
                atomic_set(&dd->count, 0);
                list_add(&dd->list, &t->devices);
 
+       } else if (!dd->dm_dev.bdev) {
+               dd->dm_dev.mode = mode;
+               r = open_dev(dd, dev, t->md);
+               if (r && !open_may_fail)
+                       return r;
+
        } else if (dd->dm_dev.mode != (mode | dd->dm_dev.mode)) {
                r = upgrade_mode(dd, mode, t->md);
                if (r)
@@ -463,6 +478,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 +492,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));
@@ -505,12 +531,9 @@ static int dm_set_device_limits(struct dm_target *ti, struct dm_dev *dev,
  */
 void dm_put_device(struct dm_target *ti, struct dm_dev *d)
 {
-       struct dm_dev_internal *dd;
+       struct dm_dev_internal *dd = container_of(d, struct dm_dev_internal,
+                                                 dm_dev);
 
-       if (!d)
-               return;
-
-       dd = container_of(d, struct dm_dev_internal, dm_dev);
        if (atomic_dec_and_test(&dd->count)) {
                close_dev(dd, ti->table->md);
                list_del(&dd->list);
@@ -909,6 +932,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");
@@ -1046,6 +1071,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;
@@ -1324,7 +1351,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);
 }
@@ -1376,7 +1403,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);
 }
@@ -1384,7 +1411,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);
 }
@@ -1409,7 +1436,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;
 }
@@ -1436,7 +1463,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);
 }
@@ -1619,9 +1646,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
index 32b958dbc499212bfcaceb6afbf72e58c5b4af66..60989fa9b09bbb8cb2decdb1c37406c206dcf55e 100644 (file)
@@ -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);
 }
 
 /*
index e81d2152fa684198899b1998efe3e5b19554b778..aa30a05255a162a24d4b77f6036adc9987d6c26d 100644 (file)
@@ -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().
  */
index e1707de043ae7bad7f6165ee34841f39abd5cc6c..6ecb7fa788a903a789460c004cf078a72c718a73 100644 (file)
@@ -119,6 +119,7 @@ struct dm_dev {
        struct block_device *bdev;
        fmode_t mode;
        char name[16];
+       dev_t bd_dev;
 };
 
 /*