btrfs: combine device update operations during transaction commit
authorNikolay Borisov <nborisov@suse.com>
Mon, 25 Mar 2019 12:31:22 +0000 (14:31 +0200)
committerDavid Sterba <dsterba@suse.com>
Mon, 29 Apr 2019 17:02:36 +0000 (19:02 +0200)
We currently overload the pending_chunks list to handle updating
btrfs_device->commit_bytes used.  We don't actually care about the
extent mapping or even the device mapping for the chunk - we just need
the device, and we can end up processing it multiple times.  The
fs_devices->resized_list does more or less the same thing, but with the
disk size.  They are called consecutively during commit and have more or
less the same purpose.

We can combine the two lists into a single list that attaches to the
transaction and contains a list of devices that need updating.  Since we
always add the device to a list when we change bytes_used or
disk_total_size, there's no harm in copying both values at once.

Signed-off-by: Nikolay Borisov <nborisov@suse.com>
Reviewed-by: David Sterba <dsterba@suse.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/dev-replace.c
fs/btrfs/disk-io.c
fs/btrfs/transaction.c
fs/btrfs/transaction.h
fs/btrfs/volumes.c
fs/btrfs/volumes.h

index ee193c5222b2cd9c86e379fe98c2bd1918dbf92b..dba43ada41d1bcf582371afbc0deeca12d606360 100644 (file)
@@ -662,7 +662,7 @@ static int btrfs_dev_replace_finishing(struct btrfs_fs_info *fs_info,
        btrfs_device_set_disk_total_bytes(tgt_device,
                                          src_device->disk_total_bytes);
        btrfs_device_set_bytes_used(tgt_device, src_device->bytes_used);
-       ASSERT(list_empty(&src_device->resized_list));
+       ASSERT(list_empty(&src_device->post_commit_list));
        tgt_device->commit_total_bytes = src_device->commit_total_bytes;
        tgt_device->commit_bytes_used = src_device->bytes_used;
 
index 0fa65aca56a3b4d88a973c78466f7981e2dd4a4b..de1dcccc7c9d0b3507e7da333f6f5f26507d2cc3 100644 (file)
@@ -4479,10 +4479,17 @@ void btrfs_cleanup_dirty_bgs(struct btrfs_transaction *cur_trans,
 void btrfs_cleanup_one_transaction(struct btrfs_transaction *cur_trans,
                                   struct btrfs_fs_info *fs_info)
 {
+       struct btrfs_device *dev, *tmp;
+
        btrfs_cleanup_dirty_bgs(cur_trans, fs_info);
        ASSERT(list_empty(&cur_trans->dirty_bgs));
        ASSERT(list_empty(&cur_trans->io_bgs));
 
+       list_for_each_entry_safe(dev, tmp, &cur_trans->dev_update_list,
+                                post_commit_list) {
+               list_del_init(&dev->post_commit_list);
+       }
+
        btrfs_destroy_delayed_refs(cur_trans, fs_info);
 
        cur_trans->state = TRANS_STATE_COMMIT_START;
index f1732b77a379924324abe69ee81f522d70d147ef..4aa827a2e951fa04b6619cdade4194fed7f1ee5a 100644 (file)
@@ -75,6 +75,7 @@ void btrfs_put_transaction(struct btrfs_transaction *transaction)
                        btrfs_put_block_group_trimming(cache);
                        btrfs_put_block_group(cache);
                }
+               WARN_ON(!list_empty(&transaction->dev_update_list));
                kfree(transaction);
        }
 }
@@ -264,6 +265,7 @@ loop:
 
        INIT_LIST_HEAD(&cur_trans->pending_snapshots);
        INIT_LIST_HEAD(&cur_trans->pending_chunks);
+       INIT_LIST_HEAD(&cur_trans->dev_update_list);
        INIT_LIST_HEAD(&cur_trans->switch_commits);
        INIT_LIST_HEAD(&cur_trans->dirty_bgs);
        INIT_LIST_HEAD(&cur_trans->io_bgs);
@@ -2241,8 +2243,7 @@ int btrfs_commit_transaction(struct btrfs_trans_handle *trans)
        memcpy(fs_info->super_for_commit, fs_info->super_copy,
               sizeof(*fs_info->super_copy));
 
-       btrfs_update_commit_device_size(fs_info);
-       btrfs_update_commit_device_bytes_used(cur_trans);
+       btrfs_commit_device_sizes(cur_trans);
 
        clear_bit(BTRFS_FS_LOG1_ERR, &fs_info->flags);
        clear_bit(BTRFS_FS_LOG2_ERR, &fs_info->flags);
index b34678e7968ed2e704db2fc18ccfc8e2f288aaa7..2bd76f681520f6b316efb0cfe4320fcaed62a353 100644 (file)
@@ -52,6 +52,7 @@ struct btrfs_transaction {
        wait_queue_head_t commit_wait;
        struct list_head pending_snapshots;
        struct list_head pending_chunks;
+       struct list_head dev_update_list;
        struct list_head switch_commits;
        struct list_head dirty_bgs;
 
index 77bca3a61e2605646a30b462a14624363b2c3389..72e069c227ab3a6ebf52d85024152b4a8615009a 100644 (file)
@@ -319,7 +319,6 @@ static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid,
        mutex_init(&fs_devs->device_list_mutex);
 
        INIT_LIST_HEAD(&fs_devs->devices);
-       INIT_LIST_HEAD(&fs_devs->resized_devices);
        INIT_LIST_HEAD(&fs_devs->alloc_list);
        INIT_LIST_HEAD(&fs_devs->fs_list);
        if (fsid)
@@ -335,6 +334,7 @@ static struct btrfs_fs_devices *alloc_fs_devices(const u8 *fsid,
 
 void btrfs_free_device(struct btrfs_device *device)
 {
+       WARN_ON(!list_empty(&device->post_commit_list));
        rcu_string_free(device->name);
        bio_put(device->flush_bio);
        kfree(device);
@@ -403,7 +403,7 @@ static struct btrfs_device *__alloc_device(void)
 
        INIT_LIST_HEAD(&dev->dev_list);
        INIT_LIST_HEAD(&dev->dev_alloc_list);
-       INIT_LIST_HEAD(&dev->resized_list);
+       INIT_LIST_HEAD(&dev->post_commit_list);
 
        spin_lock_init(&dev->io_lock);
 
@@ -2853,7 +2853,6 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
 {
        struct btrfs_fs_info *fs_info = device->fs_info;
        struct btrfs_super_block *super_copy = fs_info->super_copy;
-       struct btrfs_fs_devices *fs_devices;
        u64 old_total;
        u64 diff;
 
@@ -2872,8 +2871,6 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
                return -EINVAL;
        }
 
-       fs_devices = fs_info->fs_devices;
-
        btrfs_set_super_total_bytes(super_copy,
                        round_down(old_total + diff, fs_info->sectorsize));
        device->fs_devices->total_rw_bytes += diff;
@@ -2881,9 +2878,9 @@ int btrfs_grow_device(struct btrfs_trans_handle *trans,
        btrfs_device_set_total_bytes(device, new_size);
        btrfs_device_set_disk_total_bytes(device, new_size);
        btrfs_clear_space_info_full(device->fs_info);
-       if (list_empty(&device->resized_list))
-               list_add_tail(&device->resized_list,
-                             &fs_devices->resized_devices);
+       if (list_empty(&device->post_commit_list))
+               list_add_tail(&device->post_commit_list,
+                             &trans->transaction->dev_update_list);
        mutex_unlock(&fs_info->chunk_mutex);
 
        return btrfs_update_device(trans, device);
@@ -4872,9 +4869,9 @@ again:
        }
 
        btrfs_device_set_disk_total_bytes(device, new_size);
-       if (list_empty(&device->resized_list))
-               list_add_tail(&device->resized_list,
-                             &fs_info->fs_devices->resized_devices);
+       if (list_empty(&device->post_commit_list))
+               list_add_tail(&device->post_commit_list,
+                             &trans->transaction->dev_update_list);
 
        WARN_ON(diff > old_total);
        btrfs_set_super_total_bytes(super_copy,
@@ -5214,9 +5211,14 @@ static int __btrfs_alloc_chunk(struct btrfs_trans_handle *trans,
        if (ret)
                goto error_del_extent;
 
-       for (i = 0; i < map->num_stripes; i++)
-               btrfs_device_set_bytes_used(map->stripes[i].dev,
-                               map->stripes[i].dev->bytes_used + stripe_size);
+       for (i = 0; i < map->num_stripes; i++) {
+               struct btrfs_device *dev = map->stripes[i].dev;
+
+               btrfs_device_set_bytes_used(dev, dev->bytes_used + stripe_size);
+               if (list_empty(&dev->post_commit_list))
+                       list_add_tail(&dev->post_commit_list,
+                                     &trans->transaction->dev_update_list);
+       }
 
        atomic64_sub(stripe_size * map->num_stripes, &info->free_chunk_space);
 
@@ -7579,51 +7581,34 @@ void btrfs_scratch_superblocks(struct block_device *bdev, const char *device_pat
 }
 
 /*
- * Update the size of all devices, which is used for writing out the
- * super blocks.
+ * Update the size and bytes used for each device where it changed.  This is
+ * delayed since we would otherwise get errors while writing out the
+ * superblocks.
+ *
+ * Must be invoked during transaction commit.
  */
-void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info)
+void btrfs_commit_device_sizes(struct btrfs_transaction *trans)
 {
-       struct btrfs_fs_devices *fs_devices = fs_info->fs_devices;
        struct btrfs_device *curr, *next;
 
-       if (list_empty(&fs_devices->resized_devices))
-               return;
-
-       mutex_lock(&fs_devices->device_list_mutex);
-       mutex_lock(&fs_info->chunk_mutex);
-       list_for_each_entry_safe(curr, next, &fs_devices->resized_devices,
-                                resized_list) {
-               list_del_init(&curr->resized_list);
-               curr->commit_total_bytes = curr->disk_total_bytes;
-       }
-       mutex_unlock(&fs_info->chunk_mutex);
-       mutex_unlock(&fs_devices->device_list_mutex);
-}
-
-/* Must be invoked during the transaction commit */
-void btrfs_update_commit_device_bytes_used(struct btrfs_transaction *trans)
-{
-       struct btrfs_fs_info *fs_info = trans->fs_info;
-       struct extent_map *em;
-       struct map_lookup *map;
-       struct btrfs_device *dev;
-       int i;
+       ASSERT(trans->state == TRANS_STATE_COMMIT_DOING);
 
-       if (list_empty(&trans->pending_chunks))
+       if (list_empty(&trans->dev_update_list))
                return;
 
-       /* In order to kick the device replace finish process */
-       mutex_lock(&fs_info->chunk_mutex);
-       list_for_each_entry(em, &trans->pending_chunks, list) {
-               map = em->map_lookup;
-
-               for (i = 0; i < map->num_stripes; i++) {
-                       dev = map->stripes[i].dev;
-                       dev->commit_bytes_used = dev->bytes_used;
-               }
+       /*
+        * We don't need the device_list_mutex here.  This list is owned by the
+        * transaction and the transaction must complete before the device is
+        * released.
+        */
+       mutex_lock(&trans->fs_info->chunk_mutex);
+       list_for_each_entry_safe(curr, next, &trans->dev_update_list,
+                                post_commit_list) {
+               list_del_init(&curr->post_commit_list);
+               curr->commit_total_bytes = curr->disk_total_bytes;
+               curr->commit_bytes_used = curr->bytes_used;
        }
-       mutex_unlock(&fs_info->chunk_mutex);
+       mutex_unlock(&trans->fs_info->chunk_mutex);
 }
 
 void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info)
index 38ed94b77202767d1a65502b8918d55927049361..b9912b910d6d69785d617df5747deb1ed78d5e09 100644 (file)
@@ -45,6 +45,7 @@ struct btrfs_pending_bios {
 struct btrfs_device {
        struct list_head dev_list;
        struct list_head dev_alloc_list;
+       struct list_head post_commit_list; /* chunk mutex */
        struct btrfs_fs_devices *fs_devices;
        struct btrfs_fs_info *fs_info;
 
@@ -102,18 +103,12 @@ struct btrfs_device {
         * size of the device on the current transaction
         *
         * This variant is update when committing the transaction,
-        * and protected by device_list_mutex
+        * and protected by chunk mutex
         */
        u64 commit_total_bytes;
 
        /* bytes used on the current transaction */
        u64 commit_bytes_used;
-       /*
-        * used to manage the device which is resized
-        *
-        * It is protected by chunk_lock.
-        */
-       struct list_head resized_list;
 
        /* for sending down flush barriers */
        struct bio *flush_bio;
@@ -235,7 +230,6 @@ struct btrfs_fs_devices {
        struct mutex device_list_mutex;
        struct list_head devices;
 
-       struct list_head resized_devices;
        /* devices not currently being allocated */
        struct list_head alloc_list;
 
@@ -567,8 +561,7 @@ static inline enum btrfs_raid_types btrfs_bg_flags_to_raid_index(u64 flags)
 
 const char *get_raid_name(enum btrfs_raid_types type);
 
-void btrfs_update_commit_device_size(struct btrfs_fs_info *fs_info);
-void btrfs_update_commit_device_bytes_used(struct btrfs_transaction *trans);
+void btrfs_commit_device_sizes(struct btrfs_transaction *trans);
 
 struct list_head *btrfs_get_fs_uuids(void);
 void btrfs_set_fs_info_ptr(struct btrfs_fs_info *fs_info);