dm thin metadata: introduce dm_pool_abort_metadata
[deliverable/linux.git] / drivers / md / dm-thin-metadata.c
index 0ef10f70565e2dc863a21060faec5856e5d15fe2..693e149e97271dbe3fa3b646cfac018533892835 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2011 Red Hat, Inc.
+ * Copyright (C) 2011-2012 Red Hat, Inc.
  *
  * This file is released under the GPL.
  */
@@ -184,6 +184,14 @@ struct dm_pool_metadata {
        uint64_t trans_id;
        unsigned long flags;
        sector_t data_block_size;
+       bool read_only:1;
+
+       /*
+        * Set if a transaction has to be aborted but the attempt to roll back
+        * to the previous (good) transaction failed.  The only pool metadata
+        * operation possible in this state is the closing of the device.
+        */
+       bool fail_io:1;
 };
 
 struct dm_thin_device {
@@ -192,7 +200,8 @@ struct dm_thin_device {
        dm_thin_id id;
 
        int open_count;
-       int changed;
+       bool changed:1;
+       bool aborted_with_changes:1;
        uint64_t mapped_blocks;
        uint64_t transaction_id;
        uint32_t creation_time;
@@ -357,7 +366,7 @@ static int superblock_lock(struct dm_pool_metadata *pmd,
                                &sb_validator, sblock);
 }
 
-static int superblock_all_zeroes(struct dm_block_manager *bm, int *result)
+static int __superblock_all_zeroes(struct dm_block_manager *bm, int *result)
 {
        int r;
        unsigned i;
@@ -422,97 +431,253 @@ static void __setup_btree_details(struct dm_pool_metadata *pmd)
        pmd->details_info.value_type.equal = NULL;
 }
 
-static int __create_persistent_data_objects(struct dm_pool_metadata *pmd,
-                                           struct dm_block_manager *bm,
-                                           dm_block_t nr_blocks, int create)
+static int __write_initial_superblock(struct dm_pool_metadata *pmd)
 {
        int r;
-       struct dm_space_map *sm, *data_sm;
-       struct dm_transaction_manager *tm;
        struct dm_block *sblock;
+       size_t metadata_len, data_len;
+       struct thin_disk_superblock *disk_super;
+       sector_t bdev_size = i_size_read(pmd->bdev->bd_inode) >> SECTOR_SHIFT;
 
-       if (create) {
-               r = dm_tm_create_with_sm(bm, THIN_SUPERBLOCK_LOCATION, &tm, &sm);
-               if (r < 0) {
-                       DMERR("tm_create_with_sm failed");
-                       return r;
-               }
+       if (bdev_size > THIN_METADATA_MAX_SECTORS)
+               bdev_size = THIN_METADATA_MAX_SECTORS;
 
-               data_sm = dm_sm_disk_create(tm, nr_blocks);
-               if (IS_ERR(data_sm)) {
-                       DMERR("sm_disk_create failed");
-                       r = PTR_ERR(data_sm);
-                       goto bad;
-               }
-       } else {
-               struct thin_disk_superblock *disk_super;
+       r = dm_sm_root_size(pmd->metadata_sm, &metadata_len);
+       if (r < 0)
+               return r;
 
-               r = dm_bm_read_lock(bm, THIN_SUPERBLOCK_LOCATION,
-                                   &sb_validator, &sblock);
-               if (r < 0) {
-                       DMERR("couldn't read superblock");
-                       return r;
-               }
+       r = dm_sm_root_size(pmd->data_sm, &data_len);
+       if (r < 0)
+               return r;
 
-               disk_super = dm_block_data(sblock);
-               r = dm_tm_open_with_sm(bm, THIN_SUPERBLOCK_LOCATION,
-                                      disk_super->metadata_space_map_root,
-                                      sizeof(disk_super->metadata_space_map_root),
-                                      &tm, &sm);
-               if (r < 0) {
-                       DMERR("tm_open_with_sm failed");
-                       dm_bm_unlock(sblock);
-                       return r;
-               }
+       r = dm_sm_commit(pmd->data_sm);
+       if (r < 0)
+               return r;
 
-               data_sm = dm_sm_disk_open(tm, disk_super->data_space_map_root,
-                                         sizeof(disk_super->data_space_map_root));
-               if (IS_ERR(data_sm)) {
-                       DMERR("sm_disk_open failed");
-                       dm_bm_unlock(sblock);
-                       r = PTR_ERR(data_sm);
-                       goto bad;
-               }
+       r = dm_tm_pre_commit(pmd->tm);
+       if (r < 0)
+               return r;
+
+       r = superblock_lock_zero(pmd, &sblock);
+       if (r)
+               return r;
+
+       disk_super = dm_block_data(sblock);
+       disk_super->flags = 0;
+       memset(disk_super->uuid, 0, sizeof(disk_super->uuid));
+       disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
+       disk_super->version = cpu_to_le32(THIN_VERSION);
+       disk_super->time = 0;
+       disk_super->trans_id = 0;
+       disk_super->held_root = 0;
+
+       r = dm_sm_copy_root(pmd->metadata_sm, &disk_super->metadata_space_map_root,
+                           metadata_len);
+       if (r < 0)
+               goto bad_locked;
+
+       r = dm_sm_copy_root(pmd->data_sm, &disk_super->data_space_map_root,
+                           data_len);
+       if (r < 0)
+               goto bad_locked;
+
+       disk_super->data_mapping_root = cpu_to_le64(pmd->root);
+       disk_super->device_details_root = cpu_to_le64(pmd->details_root);
+       disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
+       disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
+       disk_super->data_block_size = cpu_to_le32(pmd->data_block_size);
+
+       return dm_tm_commit(pmd->tm, sblock);
+
+bad_locked:
+       dm_bm_unlock(sblock);
+       return r;
+}
+
+static int __format_metadata(struct dm_pool_metadata *pmd)
+{
+       int r;
+
+       r = dm_tm_create_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                                &pmd->tm, &pmd->metadata_sm);
+       if (r < 0) {
+               DMERR("tm_create_with_sm failed");
+               return r;
+       }
 
-               dm_bm_unlock(sblock);
+       pmd->data_sm = dm_sm_disk_create(pmd->tm, 0);
+       if (IS_ERR(pmd->data_sm)) {
+               DMERR("sm_disk_create failed");
+               r = PTR_ERR(pmd->data_sm);
+               goto bad_cleanup_tm;
        }
 
-       pmd->bm = bm;
-       pmd->metadata_sm = sm;
-       pmd->data_sm = data_sm;
-       pmd->tm = tm;
-       pmd->nb_tm = dm_tm_create_non_blocking_clone(tm);
+       pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
        if (!pmd->nb_tm) {
-               DMERR("could not create clone tm");
+               DMERR("could not create non-blocking clone tm");
                r = -ENOMEM;
-               goto bad_data_sm;
+               goto bad_cleanup_data_sm;
        }
 
        __setup_btree_details(pmd);
-       pmd->root = 0;
 
-       init_rwsem(&pmd->root_lock);
-       pmd->time = 0;
-       pmd->details_root = 0;
-       pmd->trans_id = 0;
-       pmd->flags = 0;
-       INIT_LIST_HEAD(&pmd->thin_devices);
+       r = dm_btree_empty(&pmd->info, &pmd->root);
+       if (r < 0)
+               goto bad_cleanup_nb_tm;
+
+       r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
+       if (r < 0) {
+               DMERR("couldn't create devices root");
+               goto bad_cleanup_nb_tm;
+       }
+
+       r = __write_initial_superblock(pmd);
+       if (r)
+               goto bad_cleanup_nb_tm;
 
        return 0;
 
-bad_data_sm:
-       dm_sm_destroy(data_sm);
-bad:
-       dm_tm_destroy(tm);
-       dm_sm_destroy(sm);
+bad_cleanup_nb_tm:
+       dm_tm_destroy(pmd->nb_tm);
+bad_cleanup_data_sm:
+       dm_sm_destroy(pmd->data_sm);
+bad_cleanup_tm:
+       dm_tm_destroy(pmd->tm);
+       dm_sm_destroy(pmd->metadata_sm);
 
        return r;
 }
 
+static int __check_incompat_features(struct thin_disk_superblock *disk_super,
+                                    struct dm_pool_metadata *pmd)
+{
+       uint32_t features;
+
+       features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
+       if (features) {
+               DMERR("could not access metadata due to unsupported optional features (%lx).",
+                     (unsigned long)features);
+               return -EINVAL;
+       }
+
+       /*
+        * Check for read-only metadata to skip the following RDWR checks.
+        */
+       if (get_disk_ro(pmd->bdev->bd_disk))
+               return 0;
+
+       features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
+       if (features) {
+               DMERR("could not access metadata RDWR due to unsupported optional features (%lx).",
+                     (unsigned long)features);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int __open_metadata(struct dm_pool_metadata *pmd)
+{
+       int r;
+       struct dm_block *sblock;
+       struct thin_disk_superblock *disk_super;
+
+       r = dm_bm_read_lock(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                           &sb_validator, &sblock);
+       if (r < 0) {
+               DMERR("couldn't read superblock");
+               return r;
+       }
+
+       disk_super = dm_block_data(sblock);
+
+       r = __check_incompat_features(disk_super, pmd);
+       if (r < 0)
+               goto bad_unlock_sblock;
+
+       r = dm_tm_open_with_sm(pmd->bm, THIN_SUPERBLOCK_LOCATION,
+                              disk_super->metadata_space_map_root,
+                              sizeof(disk_super->metadata_space_map_root),
+                              &pmd->tm, &pmd->metadata_sm);
+       if (r < 0) {
+               DMERR("tm_open_with_sm failed");
+               goto bad_unlock_sblock;
+       }
+
+       pmd->data_sm = dm_sm_disk_open(pmd->tm, disk_super->data_space_map_root,
+                                      sizeof(disk_super->data_space_map_root));
+       if (IS_ERR(pmd->data_sm)) {
+               DMERR("sm_disk_open failed");
+               r = PTR_ERR(pmd->data_sm);
+               goto bad_cleanup_tm;
+       }
+
+       pmd->nb_tm = dm_tm_create_non_blocking_clone(pmd->tm);
+       if (!pmd->nb_tm) {
+               DMERR("could not create non-blocking clone tm");
+               r = -ENOMEM;
+               goto bad_cleanup_data_sm;
+       }
+
+       __setup_btree_details(pmd);
+       return dm_bm_unlock(sblock);
+
+bad_cleanup_data_sm:
+       dm_sm_destroy(pmd->data_sm);
+bad_cleanup_tm:
+       dm_tm_destroy(pmd->tm);
+       dm_sm_destroy(pmd->metadata_sm);
+bad_unlock_sblock:
+       dm_bm_unlock(sblock);
+
+       return r;
+}
+
+static int __open_or_format_metadata(struct dm_pool_metadata *pmd, bool format_device)
+{
+       int r, unformatted;
+
+       r = __superblock_all_zeroes(pmd->bm, &unformatted);
+       if (r)
+               return r;
+
+       if (unformatted)
+               return format_device ? __format_metadata(pmd) : -EPERM;
+
+       return __open_metadata(pmd);
+}
+
+static int __create_persistent_data_objects(struct dm_pool_metadata *pmd, bool format_device)
+{
+       int r;
+
+       pmd->bm = dm_block_manager_create(pmd->bdev, THIN_METADATA_BLOCK_SIZE,
+                                         THIN_METADATA_CACHE_SIZE,
+                                         THIN_MAX_CONCURRENT_LOCKS);
+       if (IS_ERR(pmd->bm)) {
+               DMERR("could not create block manager");
+               return PTR_ERR(pmd->bm);
+       }
+
+       r = __open_or_format_metadata(pmd, format_device);
+       if (r)
+               dm_block_manager_destroy(pmd->bm);
+
+       return r;
+}
+
+static void __destroy_persistent_data_objects(struct dm_pool_metadata *pmd)
+{
+       dm_sm_destroy(pmd->data_sm);
+       dm_sm_destroy(pmd->metadata_sm);
+       dm_tm_destroy(pmd->nb_tm);
+       dm_tm_destroy(pmd->tm);
+       dm_block_manager_destroy(pmd->bm);
+}
+
 static int __begin_transaction(struct dm_pool_metadata *pmd)
 {
        int r;
-       u32 features;
        struct thin_disk_superblock *disk_super;
        struct dm_block *sblock;
 
@@ -533,32 +698,8 @@ static int __begin_transaction(struct dm_pool_metadata *pmd)
        pmd->flags = le32_to_cpu(disk_super->flags);
        pmd->data_block_size = le32_to_cpu(disk_super->data_block_size);
 
-       features = le32_to_cpu(disk_super->incompat_flags) & ~THIN_FEATURE_INCOMPAT_SUPP;
-       if (features) {
-               DMERR("could not access metadata due to "
-                     "unsupported optional features (%lx).",
-                     (unsigned long)features);
-               r = -EINVAL;
-               goto out;
-       }
-
-       /*
-        * Check for read-only metadata to skip the following RDWR checks.
-        */
-       if (get_disk_ro(pmd->bdev->bd_disk))
-               goto out;
-
-       features = le32_to_cpu(disk_super->compat_ro_flags) & ~THIN_FEATURE_COMPAT_RO_SUPP;
-       if (features) {
-               DMERR("could not access metadata RDWR due to "
-                     "unsupported optional features (%lx).",
-                     (unsigned long)features);
-               r = -EINVAL;
-       }
-
-out:
        dm_bm_unlock(sblock);
-       return r;
+       return 0;
 }
 
 static int __write_changed_details(struct dm_pool_metadata *pmd)
@@ -598,9 +739,6 @@ static int __write_changed_details(struct dm_pool_metadata *pmd)
 
 static int __commit_transaction(struct dm_pool_metadata *pmd)
 {
-       /*
-        * FIXME: Associated pool should be made read-only on failure.
-        */
        int r;
        size_t metadata_len, data_len;
        struct thin_disk_superblock *disk_super;
@@ -660,15 +798,11 @@ out_locked:
 }
 
 struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
-                                              sector_t data_block_size)
+                                              sector_t data_block_size,
+                                              bool format_device)
 {
        int r;
-       struct thin_disk_superblock *disk_super;
        struct dm_pool_metadata *pmd;
-       sector_t bdev_size = i_size_read(bdev->bd_inode) >> SECTOR_SHIFT;
-       struct dm_block_manager *bm;
-       int create;
-       struct dm_block *sblock;
 
        pmd = kmalloc(sizeof(*pmd), GFP_KERNEL);
        if (!pmd) {
@@ -676,84 +810,28 @@ struct dm_pool_metadata *dm_pool_metadata_open(struct block_device *bdev,
                return ERR_PTR(-ENOMEM);
        }
 
-       bm = dm_block_manager_create(bdev, THIN_METADATA_BLOCK_SIZE,
-                                    THIN_METADATA_CACHE_SIZE,
-                                    THIN_MAX_CONCURRENT_LOCKS);
-       if (IS_ERR(bm)) {
-               r = PTR_ERR(bm);
-               DMERR("could not create block manager");
-               kfree(pmd);
-               return ERR_PTR(r);
-       }
-
-       r = superblock_all_zeroes(bm, &create);
-       if (r) {
-               dm_block_manager_destroy(bm);
-               kfree(pmd);
-               return ERR_PTR(r);
-       }
+       init_rwsem(&pmd->root_lock);
+       pmd->time = 0;
+       INIT_LIST_HEAD(&pmd->thin_devices);
+       pmd->read_only = false;
+       pmd->fail_io = false;
+       pmd->bdev = bdev;
+       pmd->data_block_size = data_block_size;
 
-       r = __create_persistent_data_objects(pmd, bm, 0, create);
+       r = __create_persistent_data_objects(pmd, format_device);
        if (r) {
-               dm_block_manager_destroy(bm);
                kfree(pmd);
                return ERR_PTR(r);
        }
-       pmd->bdev = bdev;
-
-       if (!create) {
-               r = __begin_transaction(pmd);
-               if (r < 0)
-                       goto bad;
-               return pmd;
-       }
 
-       /*
-        * Create.
-        */
-       r = superblock_lock_zero(pmd, &sblock);
-       if (r)
-               goto bad;
-
-       if (bdev_size > THIN_METADATA_MAX_SECTORS)
-               bdev_size = THIN_METADATA_MAX_SECTORS;
-
-       disk_super = dm_block_data(sblock);
-       disk_super->magic = cpu_to_le64(THIN_SUPERBLOCK_MAGIC);
-       disk_super->version = cpu_to_le32(THIN_VERSION);
-       disk_super->time = 0;
-       disk_super->metadata_block_size = cpu_to_le32(THIN_METADATA_BLOCK_SIZE >> SECTOR_SHIFT);
-       disk_super->metadata_nr_blocks = cpu_to_le64(bdev_size >> SECTOR_TO_BLOCK_SHIFT);
-       disk_super->data_block_size = cpu_to_le32(data_block_size);
-
-       r = dm_bm_unlock(sblock);
-       if (r < 0)
-               goto bad;
-
-       r = dm_btree_empty(&pmd->info, &pmd->root);
-       if (r < 0)
-               goto bad;
-
-       r = dm_btree_empty(&pmd->details_info, &pmd->details_root);
-       if (r < 0) {
-               DMERR("couldn't create devices root");
-               goto bad;
-       }
-
-       pmd->flags = 0;
-       r = dm_pool_commit_metadata(pmd);
+       r = __begin_transaction(pmd);
        if (r < 0) {
-               DMERR("%s: dm_pool_commit_metadata() failed, error = %d",
-                     __func__, r);
-               goto bad;
+               if (dm_pool_metadata_close(pmd) < 0)
+                       DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
+               return ERR_PTR(r);
        }
 
        return pmd;
-
-bad:
-       if (dm_pool_metadata_close(pmd) < 0)
-               DMWARN("%s: dm_pool_metadata_close() failed.", __func__);
-       return ERR_PTR(r);
 }
 
 int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
@@ -779,18 +857,17 @@ int dm_pool_metadata_close(struct dm_pool_metadata *pmd)
                return -EBUSY;
        }
 
-       r = __commit_transaction(pmd);
-       if (r < 0)
-               DMWARN("%s: __commit_transaction() failed, error = %d",
-                      __func__, r);
+       if (!pmd->read_only && !pmd->fail_io) {
+               r = __commit_transaction(pmd);
+               if (r < 0)
+                       DMWARN("%s: __commit_transaction() failed, error = %d",
+                              __func__, r);
+       }
 
-       dm_tm_destroy(pmd->tm);
-       dm_tm_destroy(pmd->nb_tm);
-       dm_block_manager_destroy(pmd->bm);
-       dm_sm_destroy(pmd->metadata_sm);
-       dm_sm_destroy(pmd->data_sm);
-       kfree(pmd);
+       if (!pmd->fail_io)
+               __destroy_persistent_data_objects(pmd);
 
+       kfree(pmd);
        return 0;
 }
 
@@ -851,6 +928,7 @@ static int __open_device(struct dm_pool_metadata *pmd,
        (*td)->id = dev;
        (*td)->open_count = 1;
        (*td)->changed = changed;
+       (*td)->aborted_with_changes = false;
        (*td)->mapped_blocks = le64_to_cpu(details_le.mapped_blocks);
        (*td)->transaction_id = le64_to_cpu(details_le.transaction_id);
        (*td)->creation_time = le32_to_cpu(details_le.creation_time);
@@ -912,10 +990,11 @@ static int __create_thin(struct dm_pool_metadata *pmd,
 
 int dm_pool_create_thin(struct dm_pool_metadata *pmd, dm_thin_id dev)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __create_thin(pmd, dev);
+       if (!pmd->fail_io)
+               r = __create_thin(pmd, dev);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1002,10 +1081,11 @@ int dm_pool_create_snap(struct dm_pool_metadata *pmd,
                                 dm_thin_id dev,
                                 dm_thin_id origin)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __create_snap(pmd, dev, origin);
+       if (!pmd->fail_io)
+               r = __create_snap(pmd, dev, origin);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1044,10 +1124,11 @@ static int __delete_device(struct dm_pool_metadata *pmd, dm_thin_id dev)
 int dm_pool_delete_thin_device(struct dm_pool_metadata *pmd,
                               dm_thin_id dev)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __delete_device(pmd, dev);
+       if (!pmd->fail_io)
+               r = __delete_device(pmd, dev);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1057,27 +1138,40 @@ int dm_pool_set_metadata_transaction_id(struct dm_pool_metadata *pmd,
                                        uint64_t current_id,
                                        uint64_t new_id)
 {
+       int r = -EINVAL;
+
        down_write(&pmd->root_lock);
+
+       if (pmd->fail_io)
+               goto out;
+
        if (pmd->trans_id != current_id) {
-               up_write(&pmd->root_lock);
                DMERR("mismatched transaction id");
-               return -EINVAL;
+               goto out;
        }
 
        pmd->trans_id = new_id;
+       r = 0;
+
+out:
        up_write(&pmd->root_lock);
 
-       return 0;
+       return r;
 }
 
 int dm_pool_get_metadata_transaction_id(struct dm_pool_metadata *pmd,
                                        uint64_t *result)
 {
+       int r = -EINVAL;
+
        down_read(&pmd->root_lock);
-       *result = pmd->trans_id;
+       if (!pmd->fail_io) {
+               *result = pmd->trans_id;
+               r = 0;
+       }
        up_read(&pmd->root_lock);
 
-       return 0;
+       return r;
 }
 
 static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
@@ -1141,10 +1235,11 @@ static int __reserve_metadata_snap(struct dm_pool_metadata *pmd)
 
 int dm_pool_reserve_metadata_snap(struct dm_pool_metadata *pmd)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __reserve_metadata_snap(pmd);
+       if (!pmd->fail_io)
+               r = __reserve_metadata_snap(pmd);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1186,10 +1281,11 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd)
 
 int dm_pool_release_metadata_snap(struct dm_pool_metadata *pmd)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __release_metadata_snap(pmd);
+       if (!pmd->fail_io)
+               r = __release_metadata_snap(pmd);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1216,10 +1312,11 @@ static int __get_metadata_snap(struct dm_pool_metadata *pmd,
 int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd,
                              dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = __get_metadata_snap(pmd, result);
+       if (!pmd->fail_io)
+               r = __get_metadata_snap(pmd, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1228,10 +1325,11 @@ int dm_pool_get_metadata_snap(struct dm_pool_metadata *pmd,
 int dm_pool_open_thin_device(struct dm_pool_metadata *pmd, dm_thin_id dev,
                             struct dm_thin_device **td)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __open_device(pmd, dev, 0, td);
+       if (!pmd->fail_io)
+               r = __open_device(pmd, dev, 0, td);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1259,28 +1357,31 @@ static bool __snapshotted_since(struct dm_thin_device *td, uint32_t time)
 int dm_thin_find_block(struct dm_thin_device *td, dm_block_t block,
                       int can_block, struct dm_thin_lookup_result *result)
 {
-       int r;
+       int r = -EINVAL;
        uint64_t block_time = 0;
        __le64 value;
        struct dm_pool_metadata *pmd = td->pmd;
        dm_block_t keys[2] = { td->id, block };
+       struct dm_btree_info *info;
 
        if (can_block) {
                down_read(&pmd->root_lock);
-               r = dm_btree_lookup(&pmd->info, pmd->root, keys, &value);
-               if (!r)
-                       block_time = le64_to_cpu(value);
-               up_read(&pmd->root_lock);
-
-       } else if (down_read_trylock(&pmd->root_lock)) {
-               r = dm_btree_lookup(&pmd->nb_info, pmd->root, keys, &value);
-               if (!r)
-                       block_time = le64_to_cpu(value);
-               up_read(&pmd->root_lock);
-
-       } else
+               info = &pmd->info;
+       } else if (down_read_trylock(&pmd->root_lock))
+               info = &pmd->nb_info;
+       else
                return -EWOULDBLOCK;
 
+       if (pmd->fail_io)
+               goto out;
+
+       r = dm_btree_lookup(info, pmd->root, keys, &value);
+       if (!r)
+               block_time = le64_to_cpu(value);
+
+out:
+       up_read(&pmd->root_lock);
+
        if (!r) {
                dm_block_t exception_block;
                uint32_t exception_time;
@@ -1309,10 +1410,9 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
        if (r)
                return r;
 
-       if (inserted) {
+       td->changed = 1;
+       if (inserted)
                td->mapped_blocks++;
-               td->changed = 1;
-       }
 
        return 0;
 }
@@ -1320,10 +1420,11 @@ static int __insert(struct dm_thin_device *td, dm_block_t block,
 int dm_thin_insert_block(struct dm_thin_device *td, dm_block_t block,
                         dm_block_t data_block)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&td->pmd->root_lock);
-       r = __insert(td, block, data_block);
+       if (!td->pmd->fail_io)
+               r = __insert(td, block, data_block);
        up_write(&td->pmd->root_lock);
 
        return r;
@@ -1347,21 +1448,45 @@ static int __remove(struct dm_thin_device *td, dm_block_t block)
 
 int dm_thin_remove_block(struct dm_thin_device *td, dm_block_t block)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&td->pmd->root_lock);
-       r = __remove(td, block);
+       if (!td->pmd->fail_io)
+               r = __remove(td, block);
        up_write(&td->pmd->root_lock);
 
        return r;
 }
 
-int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
+bool dm_thin_changed_this_transaction(struct dm_thin_device *td)
 {
        int r;
 
+       down_read(&td->pmd->root_lock);
+       r = td->changed;
+       up_read(&td->pmd->root_lock);
+
+       return r;
+}
+
+bool dm_thin_aborted_changes(struct dm_thin_device *td)
+{
+       bool r;
+
+       down_read(&td->pmd->root_lock);
+       r = td->aborted_with_changes;
+       up_read(&td->pmd->root_lock);
+
+       return r;
+}
+
+int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
+{
+       int r = -EINVAL;
+
        down_write(&pmd->root_lock);
-       r = dm_sm_new_block(pmd->data_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_new_block(pmd->data_sm, result);
        up_write(&pmd->root_lock);
 
        return r;
@@ -1369,9 +1494,11 @@ int dm_pool_alloc_data_block(struct dm_pool_metadata *pmd, dm_block_t *result)
 
 int dm_pool_commit_metadata(struct dm_pool_metadata *pmd)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
+       if (pmd->fail_io)
+               goto out;
 
        r = __commit_transaction(pmd);
        if (r <= 0)
@@ -1386,12 +1513,41 @@ out:
        return r;
 }
 
+static void __set_abort_with_changes_flags(struct dm_pool_metadata *pmd)
+{
+       struct dm_thin_device *td;
+
+       list_for_each_entry(td, &pmd->thin_devices, list)
+               td->aborted_with_changes = td->changed;
+}
+
+int dm_pool_abort_metadata(struct dm_pool_metadata *pmd)
+{
+       int r = -EINVAL;
+
+       down_write(&pmd->root_lock);
+       if (pmd->fail_io)
+               goto out;
+
+       __set_abort_with_changes_flags(pmd);
+       __destroy_persistent_data_objects(pmd);
+       r = __create_persistent_data_objects(pmd, false);
+       if (r)
+               pmd->fail_io = true;
+
+out:
+       up_write(&pmd->root_lock);
+
+       return r;
+}
+
 int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_free(pmd->data_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_free(pmd->data_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1400,10 +1556,11 @@ int dm_pool_get_free_block_count(struct dm_pool_metadata *pmd, dm_block_t *resul
 int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
                                          dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_free(pmd->metadata_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_free(pmd->metadata_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1412,10 +1569,11 @@ int dm_pool_get_free_metadata_block_count(struct dm_pool_metadata *pmd,
 int dm_pool_get_metadata_dev_size(struct dm_pool_metadata *pmd,
                                  dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_blocks(pmd->metadata_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1432,10 +1590,11 @@ int dm_pool_get_data_block_size(struct dm_pool_metadata *pmd, sector_t *result)
 
 int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
 
        down_read(&pmd->root_lock);
-       r = dm_sm_get_nr_blocks(pmd->data_sm, result);
+       if (!pmd->fail_io)
+               r = dm_sm_get_nr_blocks(pmd->data_sm, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1443,13 +1602,17 @@ int dm_pool_get_data_dev_size(struct dm_pool_metadata *pmd, dm_block_t *result)
 
 int dm_thin_get_mapped_count(struct dm_thin_device *td, dm_block_t *result)
 {
+       int r = -EINVAL;
        struct dm_pool_metadata *pmd = td->pmd;
 
        down_read(&pmd->root_lock);
-       *result = td->mapped_blocks;
+       if (!pmd->fail_io) {
+               *result = td->mapped_blocks;
+               r = 0;
+       }
        up_read(&pmd->root_lock);
 
-       return 0;
+       return r;
 }
 
 static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
@@ -1471,11 +1634,12 @@ static int __highest_block(struct dm_thin_device *td, dm_block_t *result)
 int dm_thin_get_highest_mapped_block(struct dm_thin_device *td,
                                     dm_block_t *result)
 {
-       int r;
+       int r = -EINVAL;
        struct dm_pool_metadata *pmd = td->pmd;
 
        down_read(&pmd->root_lock);
-       r = __highest_block(td, result);
+       if (!pmd->fail_io)
+               r = __highest_block(td, result);
        up_read(&pmd->root_lock);
 
        return r;
@@ -1503,11 +1667,20 @@ static int __resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
 
 int dm_pool_resize_data_dev(struct dm_pool_metadata *pmd, dm_block_t new_count)
 {
-       int r;
+       int r = -EINVAL;
 
        down_write(&pmd->root_lock);
-       r = __resize_data_dev(pmd, new_count);
+       if (!pmd->fail_io)
+               r = __resize_data_dev(pmd, new_count);
        up_write(&pmd->root_lock);
 
        return r;
 }
+
+void dm_pool_metadata_read_only(struct dm_pool_metadata *pmd)
+{
+       down_write(&pmd->root_lock);
+       pmd->read_only = true;
+       dm_bm_set_read_only(pmd->bm);
+       up_write(&pmd->root_lock);
+}
This page took 0.043578 seconds and 5 git commands to generate.