Merge tag 'for-5.18-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 6 May 2022 21:32:16 +0000 (14:32 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 6 May 2022 21:32:16 +0000 (14:32 -0700)
Pull btrfs fixes from David Sterba:
 "Regression fixes in zone activation:

   - move a loop invariant out of the loop to avoid checking space
     status

   - properly handle unlimited activation

  Other fixes:

   - for subpage, force the free space v2 mount to avoid a warning and
     make it easy to switch a filesystem on different page size systems

   - export sysfs status of exclusive operation 'balance paused', so the
     user space tools can recognize it and allow adding a device with
     paused balance

   - fix assertion failure when logging directory key range item"

* tag 'for-5.18-rc5-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave/linux:
  btrfs: sysfs: export the balance paused state of exclusive operation
  btrfs: fix assertion failure when logging directory key range item
  btrfs: zoned: activate block group properly on unlimited active zone device
  btrfs: zoned: move non-changing condition check out of the loop
  btrfs: force v2 space cache usage for subpage mount

fs/btrfs/disk-io.c
fs/btrfs/sysfs.c
fs/btrfs/tree-log.c
fs/btrfs/zoned.c

index ed8e288cc3694117bef9cb6ed8dfdb3dd4abab79..31c3f592e5875ec8f10279e1c6a3a0b65e627dd4 100644 (file)
@@ -3658,6 +3658,17 @@ int __cold open_ctree(struct super_block *sb, struct btrfs_fs_devices *fs_device
        if (sectorsize < PAGE_SIZE) {
                struct btrfs_subpage_info *subpage_info;
 
+               /*
+                * V1 space cache has some hardcoded PAGE_SIZE usage, and is
+                * going to be deprecated.
+                *
+                * Force to use v2 cache for subpage case.
+                */
+               btrfs_clear_opt(fs_info->mount_opt, SPACE_CACHE);
+               btrfs_set_and_info(fs_info, FREE_SPACE_TREE,
+                       "forcing free space tree for sector size %u with page size %lu",
+                       sectorsize, PAGE_SIZE);
+
                btrfs_warn(fs_info,
                "read-write for sector size %u with page size %lu is experimental",
                           sectorsize, PAGE_SIZE);
index 17389a42a3ab7214971f6f02c3883c8caef31cb0..ba78ca5aabbb251a3bccb36da094ef3866fa17f4 100644 (file)
@@ -922,6 +922,9 @@ static ssize_t btrfs_exclusive_operation_show(struct kobject *kobj,
                case BTRFS_EXCLOP_BALANCE:
                        str = "balance\n";
                        break;
+               case BTRFS_EXCLOP_BALANCE_PAUSED:
+                       str = "balance paused\n";
+                       break;
                case BTRFS_EXCLOP_DEV_ADD:
                        str = "device add\n";
                        break;
index 11399c8eed87dd273993398940197626c6da6e91..e65633686378cc8b3ff13d4f6cde9a15dc3548ca 100644 (file)
@@ -3721,11 +3721,29 @@ static noinline int insert_dir_log_key(struct btrfs_trans_handle *trans,
        key.offset = first_offset;
        key.type = BTRFS_DIR_LOG_INDEX_KEY;
        ret = btrfs_insert_empty_item(trans, log, path, &key, sizeof(*item));
-       if (ret)
+       /*
+        * -EEXIST is fine and can happen sporadically when we are logging a
+        * directory and have concurrent insertions in the subvolume's tree for
+        * items from other inodes and that result in pushing off some dir items
+        * from one leaf to another in order to accommodate for the new items.
+        * This results in logging the same dir index range key.
+        */
+       if (ret && ret != -EEXIST)
                return ret;
 
        item = btrfs_item_ptr(path->nodes[0], path->slots[0],
                              struct btrfs_dir_log_item);
+       if (ret == -EEXIST) {
+               const u64 curr_end = btrfs_dir_log_end(path->nodes[0], item);
+
+               /*
+                * btrfs_del_dir_entries_in_log() might have been called during
+                * an unlink between the initial insertion of this key and the
+                * current update, or we might be logging a single entry deletion
+                * during a rename, so set the new last_offset to the max value.
+                */
+               last_offset = max(last_offset, curr_end);
+       }
        btrfs_set_dir_log_end(path->nodes[0], item, last_offset);
        btrfs_mark_buffer_dirty(path->nodes[0]);
        btrfs_release_path(path);
@@ -3849,13 +3867,6 @@ static int process_dir_items_leaf(struct btrfs_trans_handle *trans,
                                ret = insert_dir_log_key(trans, log, dst_path,
                                                 ino, *last_old_dentry_offset + 1,
                                                 key.offset - 1);
-                               /*
-                                * -EEXIST should never happen because when we
-                                * log a directory in full mode (LOG_INODE_ALL)
-                                * we drop all BTRFS_DIR_LOG_INDEX_KEY keys from
-                                * the log tree.
-                                */
-                               ASSERT(ret != -EEXIST);
                                if (ret < 0)
                                        return ret;
                        }
@@ -7031,12 +7042,12 @@ void btrfs_log_new_name(struct btrfs_trans_handle *trans,
                /*
                 * Other concurrent task might be logging the old directory,
                 * as it can be triggered when logging other inode that had or
-                * still has a dentry in the old directory. So take the old
-                * directory's log_mutex to prevent getting an -EEXIST when
-                * logging a key to record the deletion, or having that other
-                * task logging the old directory get an -EEXIST if it attempts
-                * to log the same key after we just did it. In both cases that
-                * would result in falling back to a transaction commit.
+                * still has a dentry in the old directory. We lock the old
+                * directory's log_mutex to ensure the deletion of the old
+                * name is persisted, because during directory logging we
+                * delete all BTRFS_DIR_LOG_INDEX_KEY keys and the deletion of
+                * the old name's dir index item is in the delayed items, so
+                * it could be missed by an in progress directory logging.
                 */
                mutex_lock(&old_dir->log_mutex);
                ret = del_logged_dentry(trans, log, path, btrfs_ino(old_dir),
index 1b1b310c3c510c60202e792791501272375a90d3..d31b0eda210f1eeb1a7c25667ab6a8a3816f1ef3 100644 (file)
@@ -1835,6 +1835,12 @@ bool btrfs_zone_activate(struct btrfs_block_group *block_group)
                goto out_unlock;
        }
 
+       /* No space left */
+       if (block_group->alloc_offset == block_group->zone_capacity) {
+               ret = false;
+               goto out_unlock;
+       }
+
        for (i = 0; i < map->num_stripes; i++) {
                device = map->stripes[i].dev;
                physical = map->stripes[i].physical;
@@ -1842,35 +1848,23 @@ bool btrfs_zone_activate(struct btrfs_block_group *block_group)
                if (device->zone_info->max_active_zones == 0)
                        continue;
 
-               /* No space left */
-               if (block_group->alloc_offset == block_group->zone_capacity) {
-                       ret = false;
-                       goto out_unlock;
-               }
-
                if (!btrfs_dev_set_active_zone(device, physical)) {
                        /* Cannot activate the zone */
                        ret = false;
                        goto out_unlock;
                }
-
-               /* Successfully activated all the zones */
-               if (i == map->num_stripes - 1)
-                       block_group->zone_is_active = 1;
-
-
        }
+
+       /* Successfully activated all the zones */
+       block_group->zone_is_active = 1;
        spin_unlock(&block_group->lock);
 
-       if (block_group->zone_is_active) {
-               /* For the active block group list */
-               btrfs_get_block_group(block_group);
+       /* For the active block group list */
+       btrfs_get_block_group(block_group);
 
-               spin_lock(&fs_info->zone_active_bgs_lock);
-               list_add_tail(&block_group->active_bg_list,
-                             &fs_info->zone_active_bgs);
-               spin_unlock(&fs_info->zone_active_bgs_lock);
-       }
+       spin_lock(&fs_info->zone_active_bgs_lock);
+       list_add_tail(&block_group->active_bg_list, &fs_info->zone_active_bgs);
+       spin_unlock(&fs_info->zone_active_bgs_lock);
 
        return true;