}
int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
- void *ctx, bool ignore_offset)
+ void *ctx, bool ignore_offset,
+ bool search_commit_root)
{
struct btrfs_backref_walk_ctx walk_ctx = { 0 };
int ret;
walk_ctx.extent_item_pos = logical - found_key.objectid;
walk_ctx.fs_info = fs_info;
- return iterate_extent_inodes(&walk_ctx, false, build_ino_list, ctx);
+ /*
+ * A live-root walk attaches to the running transaction and holds a
+ * tree mod log sequence until it is done, and while any sequence is
+ * live every node-level tree change in the filesystem is logged, with
+ * nothing freeing the log below the oldest sequence. A caller that
+ * resolves extents shared by tens of thousands of snapshots, on a
+ * filesystem busy relocating them, can hold that sequence for as long
+ * as it waits behind the commit thread; the log then grows without
+ * bound. BTRFS_LOGICAL_INO_ARGS_COMMIT_ROOT lets such a caller take
+ * the commit-root walk scrub and send already use here instead.
+ */
+ return iterate_extent_inodes(&walk_ctx, search_commit_root, build_ino_list, ctx);
}
static int inode_to_path(u64 inum, u32 name_len, unsigned long name_off,
iterate_extent_inodes_t *iterate, void *user_ctx);
int iterate_inodes_from_logical(u64 logical, struct btrfs_fs_info *fs_info,
- void *ctx, bool ignore_offset);
+ void *ctx, bool ignore_offset,
+ bool search_commit_root);
int paths_from_inode(u64 inum, struct inode_fs_paths *ipath);
struct btrfs_ioctl_logical_ino_args *loi;
struct btrfs_data_container *inodes = NULL;
bool ignore_offset;
+ bool commit_root = false;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
goto out_loi;
}
/* Only accept flags we have defined so far */
- if (loi->flags & ~(BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET)) {
+ if (loi->flags & ~(BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET |
+ BTRFS_LOGICAL_INO_ARGS_COMMIT_ROOT)) {
ret = -EINVAL;
goto out_loi;
}
ignore_offset = loi->flags & BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET;
+ commit_root = loi->flags & BTRFS_LOGICAL_INO_ARGS_COMMIT_ROOT;
size = min_t(u32, loi->size, SZ_16M);
}
goto out_loi;
}
- ret = iterate_inodes_from_logical(loi->logical, fs_info, inodes, ignore_offset);
+ ret = iterate_inodes_from_logical(loi->logical, fs_info, inodes,
+ ignore_offset, commit_root);
if (ret == -EINVAL)
ret = -ENOENT;
if (ret < 0)
* Requires logical == extent bytenr.
*/
#define BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET (1ULL << 0)
+/*
+ * Resolve against the commit roots: the answer is as of the last committed
+ * transaction. The walk then takes no tree mod log sequence and does not
+ * contend with the running transaction for tree locks or delayed ref heads,
+ * which matters to a caller resolving extents shared by many snapshots while
+ * the filesystem is busy -- the live-root walk pins every node-level tree
+ * change made in the meantime in memory until it finishes.
+ */
+#define BTRFS_LOGICAL_INO_ARGS_COMMIT_ROOT (1ULL << 1)
enum btrfs_dev_stat_values {
/* disk I/O failure stats */