The 'noexcl' option allow to open underlying block-device
without FMODE_EXCL.

Signed-off-by: Sergei Shtepa <sergei.sht...@veeam.com>
---
 drivers/md/dm-linear.c        | 14 +++++++++++++-
 drivers/md/dm-table.c         | 12 +++++++-----
 drivers/md/dm.c               | 24 +++++++++++++++++-------
 drivers/md/dm.h               |  2 +-
 include/linux/device-mapper.h |  7 +++++++
 5 files changed, 45 insertions(+), 14 deletions(-)

diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 00774b5d7668..b16d89802b9d 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -33,7 +33,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int 
argc, char **argv)
        char dummy;
        int ret;
 
-       if (argc != 2) {
+       if ((argc < 2) || (argc > 3)) {
                ti->error = "Invalid argument count";
                return -EINVAL;
        }
@@ -51,6 +51,18 @@ static int linear_ctr(struct dm_target *ti, unsigned int 
argc, char **argv)
        }
        lc->start = tmp;
 
+       ti->non_exclusive = false;
+       if (argc > 2) {
+               if (strcmp("noexcl", argv[2]) == 0)
+                       ti->non_exclusive = true;
+               else if (strcmp("excl", argv[2]) == 0)
+                       ti->non_exclusive = false;
+               else {
+                       ti->error = "Invalid exclusive option";
+                       return -EINVAL;
+               }
+       }
+
        ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), 
&lc->dev);
        if (ret) {
                ti->error = "Device lookup failed";
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 4acf2342f7ad..f15bc2171f25 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -322,7 +322,7 @@ static int device_area_is_invalid(struct dm_target *ti, 
struct dm_dev *dev,
  * device and not to touch the existing bdev field in case
  * it is accessed concurrently.
  */
-static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode,
+static int upgrade_mode(struct dm_dev_internal *dd, fmode_t new_mode, bool 
non_exclusive,
                        struct mapped_device *md)
 {
        int r;
@@ -330,7 +330,7 @@ static int upgrade_mode(struct dm_dev_internal *dd, fmode_t 
new_mode,
 
        old_dev = dd->dm_dev;
 
-       r = dm_get_table_device(md, dd->dm_dev->bdev->bd_dev,
+       r = dm_get_table_device(md, dd->dm_dev->bdev->bd_dev, non_exclusive,
                                dd->dm_dev->mode | new_mode, &new_dev);
        if (r)
                return r;
@@ -387,7 +387,8 @@ int dm_get_device(struct dm_target *ti, const char *path, 
fmode_t mode,
                if (!dd)
                        return -ENOMEM;
 
-               if ((r = dm_get_table_device(t->md, dev, mode, &dd->dm_dev))) {
+               r = dm_get_table_device(t->md, dev, mode, ti->non_exclusive, 
&dd->dm_dev);
+               if (r) {
                        kfree(dd);
                        return r;
                }
@@ -396,8 +397,9 @@ int dm_get_device(struct dm_target *ti, const char *path, 
fmode_t mode,
                list_add(&dd->list, &t->devices);
                goto out;
 
-       } else if (dd->dm_dev->mode != (mode | dd->dm_dev->mode)) {
-               r = upgrade_mode(dd, mode, t->md);
+       } else if ((dd->dm_dev->mode != (mode | dd->dm_dev->mode)) &&
+                  (dd->dm_dev->non_exclusive != ti->non_exclusive)) {
+               r = upgrade_mode(dd, mode, ti->non_exclusive, t->md);
                if (r)
                        return r;
        }
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 0b184dfec233..c34c75467399 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1107,6 +1107,7 @@ static int open_table_device(struct table_device *td, 
dev_t dev,
 
        td->dm_dev.bdev = bdev;
        td->dm_dev.dax_dev = dax_get_by_host(bdev->bd_disk->disk_name);
+       td->dm_dev.non_exclusive = non_exclusive;
        return 0;
 }
 
@@ -1118,33 +1119,41 @@ static void close_table_device(struct table_device *td, 
struct mapped_device *md
        if (!td->dm_dev.bdev)
                return;
 
-       bd_unlink_disk_holder(td->dm_dev.bdev, dm_disk(md));
-       blkdev_put(td->dm_dev.bdev, td->dm_dev.mode | FMODE_EXCL);
+       if (td->dm_dev.non_exclusive)
+               blkdev_put(td->dm_dev.bdev, td->dm_dev.mode);
+       else {
+               bd_unlink_disk_holder(td->dm_dev.bdev, dm_disk(md));
+               blkdev_put(td->dm_dev.bdev, td->dm_dev.mode | FMODE_EXCL);
+       }
+
        put_dax(td->dm_dev.dax_dev);
        td->dm_dev.bdev = NULL;
        td->dm_dev.dax_dev = NULL;
+       td->dm_dev.non_exclusive = false;
 }
 
 static struct table_device *find_table_device(struct list_head *l, dev_t dev,
-                                             fmode_t mode)
+                                             fmode_t mode, bool non_exclusive)
 {
        struct table_device *td;
 
        list_for_each_entry(td, l, list)
-               if (td->dm_dev.bdev->bd_dev == dev && td->dm_dev.mode == mode)
+               if (td->dm_dev.bdev->bd_dev == dev &&
+                   td->dm_dev.mode == mode &&
+                   td->dm_dev.non_exclusive == non_exclusive)
                        return td;
 
        return NULL;
 }
 
-int dm_get_table_device(struct mapped_device *md, dev_t dev, fmode_t mode,
+int dm_get_table_device(struct mapped_device *md, dev_t dev, fmode_t mode, 
bool non_exclusive,
                        struct dm_dev **result)
 {
        int r;
        struct table_device *td;
 
        mutex_lock(&md->table_devices_lock);
-       td = find_table_device(&md->table_devices, dev, mode);
+       td = find_table_device(&md->table_devices, dev, mode, non_exclusive);
        if (!td) {
                td = kmalloc_node(sizeof(*td), GFP_KERNEL, md->numa_node_id);
                if (!td) {
@@ -1155,7 +1164,8 @@ int dm_get_table_device(struct mapped_device *md, dev_t 
dev, fmode_t mode,
                td->dm_dev.mode = mode;
                td->dm_dev.bdev = NULL;
 
-               if ((r = open_table_device(td, dev, md))) {
+               r = open_table_device(td, dev, md, non_exclusive);
+               if (r) {
                        mutex_unlock(&md->table_devices_lock);
                        kfree(td);
                        return r;
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index fffe1e289c53..7bf20fb2de74 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -179,7 +179,7 @@ int dm_open_count(struct mapped_device *md);
 int dm_lock_for_deletion(struct mapped_device *md, bool mark_deferred, bool 
only_deferred);
 int dm_cancel_deferred_remove(struct mapped_device *md);
 int dm_request_based(struct mapped_device *md);
-int dm_get_table_device(struct mapped_device *md, dev_t dev, fmode_t mode,
+int dm_get_table_device(struct mapped_device *md, dev_t dev, fmode_t mode, 
bool non_exclusive,
                        struct dm_dev **result);
 void dm_put_table_device(struct mapped_device *md, struct dm_dev *d);
 
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 61a66fb8ebb3..70002363bfc0 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -150,6 +150,7 @@ struct dm_dev {
        struct block_device *bdev;
        struct dax_device *dax_dev;
        fmode_t mode;
+       bool non_exclusive;
        char name[16];
 };
 
@@ -325,6 +326,12 @@ struct dm_target {
         * whether or not its underlying devices have support.
         */
        bool discards_supported:1;
+
+       /*
+        * Set if this target needs to open device without FMODE_EXCL
+        * mode.
+        */
+       bool non_exclusive:1;
 };
 
 void *dm_per_bio_data(struct bio *bio, size_t data_size);
-- 
2.20.1

Reply via email to