btrfs: add helper for inline owner ref lookup
authorBoris Burkov <boris@bur.io>
Wed, 28 Jun 2023 17:45:29 +0000 (10:45 -0700)
committerDavid Sterba <dsterba@suse.com>
Thu, 12 Oct 2023 14:44:11 +0000 (16:44 +0200)
Inline ref parsing is a bit tricky and relies on a decent amount of
implicit information, so I think it is beneficial to have a helper
function for reading the owner ref, if only to "document" the format,
along with the write path.

The main subtlety of note which I was missing by open-coding this was
that it is important to check whether or not inline refs are present
*at all*. i.e., if we are writing out a new extent under squotas, we
will always use a big enough item for the inline ref and have it.
However, it is possible that some random item predating squotas will not
have any inline refs. In that case, trying to read the "type" field of
the first inline ref will just be reading garbage in the form of
whatever is in the next item.

This will be used by the extent free-ing path, which looks up data
extent owners as well as a relocation path which needs to grab the owner
before relocating an extent.

Signed-off-by: Boris Burkov <boris@bur.io>
Reviewed-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: David Sterba <dsterba@suse.com>
fs/btrfs/extent-tree.c
fs/btrfs/extent-tree.h

index 8bd556c8bca4fc25bce78a54af0d09a9157a54e4..8226fb9d8f7bd84294ff224afaff662fb9f7b64b 100644 (file)
@@ -2867,6 +2867,54 @@ int btrfs_finish_extent_commit(struct btrfs_trans_handle *trans)
        return 0;
 }
 
+/*
+ * Parse an extent item's inline extents looking for a simple quotas owner ref.
+ *
+ * @fs_info:   the btrfs_fs_info for this mount
+ * @leaf:      a leaf in the extent tree containing the extent item
+ * @slot:      the slot in the leaf where the extent item is found
+ *
+ * Returns the objectid of the root that originally allocated the extent item
+ * if the inline owner ref is expected and present, otherwise 0.
+ *
+ * If an extent item has an owner ref item, it will be the first inline ref
+ * item. Therefore the logic is to check whether there are any inline ref
+ * items, then check the type of the first one.
+ */
+u64 btrfs_get_extent_owner_root(struct btrfs_fs_info *fs_info,
+                               struct extent_buffer *leaf, int slot)
+{
+       struct btrfs_extent_item *ei;
+       struct btrfs_extent_inline_ref *iref;
+       struct btrfs_extent_owner_ref *oref;
+       unsigned long ptr;
+       unsigned long end;
+       int type;
+
+       if (!btrfs_fs_incompat(fs_info, SIMPLE_QUOTA))
+               return 0;
+
+       ei = btrfs_item_ptr(leaf, slot, struct btrfs_extent_item);
+       ptr = (unsigned long)(ei + 1);
+       end = (unsigned long)ei + btrfs_item_size(leaf, slot);
+
+       /* No inline ref items of any kind, can't check type. */
+       if (ptr == end)
+               return 0;
+
+       iref = (struct btrfs_extent_inline_ref *)ptr;
+       type = btrfs_get_extent_inline_ref_type(leaf, iref, BTRFS_REF_TYPE_ANY);
+
+       /* We found an owner ref, get the root out of it. */
+       if (type == BTRFS_EXTENT_OWNER_REF_KEY) {
+               oref = (struct btrfs_extent_owner_ref *)(&iref->offset);
+               return btrfs_extent_owner_ref_root_id(leaf, oref);
+       }
+
+       /* We have inline refs, but not an owner ref. */
+       return 0;
+}
+
 static int do_free_extent_accounting(struct btrfs_trans_handle *trans,
                                     u64 bytenr, u64 num_bytes, bool is_data)
 {
index 397cccafc8859c6093b9fad155b1448806d4dc01..3c435395db066dfdcf3755fc8868c9e73735bc58 100644 (file)
@@ -137,6 +137,8 @@ int btrfs_set_disk_extent_flags(struct btrfs_trans_handle *trans,
                                struct extent_buffer *eb, u64 flags);
 int btrfs_free_extent(struct btrfs_trans_handle *trans, struct btrfs_ref *ref);
 
+u64 btrfs_get_extent_owner_root(struct btrfs_fs_info *fs_info,
+                               struct extent_buffer *leaf, int slot);
 int btrfs_free_reserved_extent(struct btrfs_fs_info *fs_info,
                               u64 start, u64 len, int delalloc);
 int btrfs_pin_reserved_extent(struct btrfs_trans_handle *trans,