dm thin: don't allow changing data device during thin-pool reload
authorMikulas Patocka <mpatocka@redhat.com>
Mon, 13 Jan 2020 20:04:37 +0000 (15:04 -0500)
committerMike Snitzer <snitzer@redhat.com>
Wed, 15 Jan 2020 01:22:51 +0000 (20:22 -0500)
The existing code allows changing the data device when the thin-pool
target is reloaded.

This capability is not required and only complicates device lifetime
guarantees. 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.

Take the first step to simplifying the thin-pool's data device lifetime
by disallowing changing it. Like the thin-pool's metadata device, the
data device is now set in pool_create() and it cannot be changed for a
given thin-pool.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@redhat.com>
drivers/md/dm-thin.c

index a2bb262..4fb6e89 100644 (file)
@@ -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_cache;
 
 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 mapped_device *pool_md,
        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,19 +3095,23 @@ static struct pool *__pool_find(struct mapped_device *pool_md,
                        *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 {
                pool = __pool_table_lookup(pool_md);
                if (pool) {
-                       if (pool->md_dev != metadata_dev) {
+                       if (pool->md_dev != metadata_dev || 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 +3364,7 @@ static int pool_ctr(struct dm_target *ti, unsigned argc, char **argv)
                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);
@@ -4098,7 +4106,7 @@ static struct target_type pool_target = {
        .name = "thin-pool",
        .features = DM_TARGET_SINGLETON | DM_TARGET_ALWAYS_WRITEABLE |
                    DM_TARGET_IMMUTABLE,
-       .version = {1, 21, 0},
+       .version = {1, 22, 0},
        .module = THIS_MODULE,
        .ctr = pool_ctr,
        .dtr = pool_dtr,
@@ -4475,7 +4483,7 @@ static void thin_io_hints(struct dm_target *ti, struct queue_limits *limits)
 
 static struct target_type thin_target = {
        .name = "thin",
-       .version = {1, 21, 0},
+       .version = {1, 22, 0},
        .module = THIS_MODULE,
        .ctr = thin_ctr,
        .dtr = thin_dtr,