The existing code allow changing the data device when the dm-thin-pool
target is reloaded.

This can cause crashes like the one reported here:
        https://bugzilla.redhat.com/show_bug.cgi?id=1788596
where the kernel tries to issue a flush bio located in a structure that
was already freed.

Locking the data device would be quite tricky, so a better solution is to
disallow changing it - it is set in pool_create and it can never be
changed for the given pool.

Signed-off-by: Mikulas Patocka <[email protected]>
Cc: [email protected]
Fixes: 694cfe7f31db ("dm thin: Flush data device before committing metadata")

---
 drivers/md/dm-thin.c |   16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

Index: linux-2.6/drivers/md/dm-thin.c
===================================================================
--- linux-2.6.orig/drivers/md/dm-thin.c 2020-01-13 16:13:42.000000000 +0100
+++ linux-2.6/drivers/md/dm-thin.c      2020-01-13 18:47:50.000000000 +0100
@@ -231,6 +231,7 @@ struct pool {
        struct dm_target *ti;   /* Only set if a pool target is bound */
 
        struct mapped_device *pool_md;
+       struct block_device *data_dev;
        struct block_device *md_dev;
        struct dm_pool_metadata *pmd;
 
@@ -2933,6 +2934,7 @@ static struct kmem_cache *_new_mapping_c
 
 static struct pool *pool_create(struct mapped_device *pool_md,
                                struct block_device *metadata_dev,
+                               struct block_device *data_dev,
                                unsigned long block_size,
                                int read_only, char **error)
 {
@@ -3040,6 +3042,7 @@ static struct pool *pool_create(struct m
        pool->last_commit_jiffies = jiffies;
        pool->pool_md = pool_md;
        pool->md_dev = metadata_dev;
+       pool->data_dev = data_dev;
        __pool_table_insert(pool);
 
        return pool;
@@ -3081,6 +3084,7 @@ static void __pool_dec(struct pool *pool
 
 static struct pool *__pool_find(struct mapped_device *pool_md,
                                struct block_device *metadata_dev,
+                               struct block_device *data_dev,
                                unsigned long block_size, int read_only,
                                char **error, int *created)
 {
@@ -3091,6 +3095,10 @@ static struct pool *__pool_find(struct m
                        *error = "metadata device already in use by a pool";
                        return ERR_PTR(-EBUSY);
                }
+               if (pool->data_dev != data_dev) {
+                       *error = "data device already in use by a pool";
+                       return ERR_PTR(-EBUSY);
+               }
                __pool_inc(pool);
 
        } else {
@@ -3100,10 +3108,14 @@ static struct pool *__pool_find(struct m
                                *error = "different pool cannot replace a pool";
                                return ERR_PTR(-EINVAL);
                        }
+                       if (pool->data_dev != data_dev) {
+                               *error = "different pool cannot replace a pool";
+                               return ERR_PTR(-EINVAL);
+                       }
                        __pool_inc(pool);
 
                } else {
-                       pool = pool_create(pool_md, metadata_dev, block_size, 
read_only, error);
+                       pool = pool_create(pool_md, metadata_dev, data_dev, 
block_size, read_only, error);
                        *created = 1;
                }
        }
@@ -3356,7 +3368,7 @@ static int pool_ctr(struct dm_target *ti
                goto out;
        }
 
-       pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev,
+       pool = __pool_find(dm_table_get_md(ti->table), metadata_dev->bdev, 
data_dev->bdev,
                           block_size, pf.mode == PM_READ_ONLY, &ti->error, 
&pool_created);
        if (IS_ERR(pool)) {
                r = PTR_ERR(pool);

--
dm-devel mailing list
[email protected]
https://www.redhat.com/mailman/listinfo/dm-devel

Reply via email to