btrfs: add BTRFS_LOGICAL_INO_ARGS_COMMIT_ROOT to resolve against the commit roots
The LOGICAL_INO ioctl is the only caller of iterate_extent_inodes() that
walks the live roots. Doing so attaches to the running transaction and
holds a tree mod log sequence for the whole walk, and while any sequence
is live every node-level tree change in the filesystem is recorded in the
tree mod log. The log is trimmed only below the oldest live sequence and
has no shrinker, so its size is the modification rate times the lifetime
of the slowest walker.
That product has no bound. A walk over an extent shared by 32047
snapshots takes most of a second on its own, and when the walker then has
to wait for the commit thread -- on the extent root, or on the mutex of a
delayed ref head with tens of thousands of pending refs -- it holds its
sequence for as long as the wait lasts. Meanwhile relocation of a block
group shared by all those snapshots COWs a path in every reloc tree for
every extent, and each root COW logs one element per key of the old root.
On a 16 GiB machine running a dedupe daemon that issues LOGICAL_INO
continuously, unreclaimable slab reached 13.6 GiB about an hour into such
a relocation, with four ioctl callers still holding their sequences behind
the commit thread; every allocation then went to direct reclaim with tree
locks held and the machine stopped responding.
Add a v2 flag, alongside BTRFS_LOGICAL_INO_ARGS_IGNORE_OFFSET, that makes
the walk use the commit roots, as scrub and send already do through the
same function. The answer lags the live tree by at most one transaction,
which a caller of this ioctl cannot distinguish from the live tree changing
after the call returns, and the walk no longer takes a sequence, logs
nothing, and does not contend with the commit thread for tree locks or
delayed ref heads. Callers that want the live view keep it by default.