• Darrick J. Wong's avatar
    xfs: allow scrub to hook metadata updates in other writers · 4e98cc90
    Darrick J. Wong authored
    Certain types of filesystem metadata can only be checked by scanning
    every file in the entire filesystem.  Specific examples of this include
    quota counts, file link counts, and reverse mappings of file extents.
    Directory and parent pointer reconstruction may also fall into this
    category.  File scanning is much trickier than scanning AG metadata
    because we have to take inode locks in the same order as the rest of
    [VX]FS, we can't be holding buffer locks when we do that, and scanning
    the whole filesystem takes time.
    
    Earlier versions of the online repair patchset relied heavily on
    fsfreeze as a means to quiesce the filesystem so that we could take
    locks in the proper order without worrying about concurrent updates from
    other writers.  Reviewers of those patches opined that freezing the
    entire fs to check and repair something was not sufficiently better than
    unmounting to run fsck offline.  I don't agree with that 100%, but the
    message was clear: find a way to repair things that minimizes the
    quiet period where nobody can write to the filesystem.
    
    Generally, building btree indexes online can be split into two phases: a
    collection phase where we compute the records that will be put into the
    new btree; and a construction phase, where we construct the physical
    btree blocks and persist them.  While it's simple to hold resource locks
    for the entirety of the two phases to ensure that the new index is
    consistent with the rest of the system, we don't need to hold resource
    locks during the collection phase if we have a means to receive live
    updates of other work going on elsewhere in the system.
    
    The goal of this patch, then, is to enable online fsck to learn about
    metadata updates going on in other threads while it constructs a shadow
    copy of the metadata records to verify or correct the real metadata.  To
    minimize the overhead when online fsck isn't running, we use srcu
    notifiers because they prioritize fast access to the notifier call chain
    (particularly when the chain is empty) at a cost to configuring
    notifiers.  Online fsck should be relatively infrequent, so this is
    acceptable.
    
    The intended usage model is fairly simple.  Code that modifies a
    metadata structure of interest should declare a xfs_hook_chain structure
    in some well defined place, and call xfs_hook_call whenever an update
    happens.  Online fsck code should define a struct notifier_block and use
    xfs_hook_add to attach the block to the chain, along with a function to
    be called.  This function should synchronize with the fsck scanner to
    update whatever in-memory data the scanner is collecting.  When
    finished, xfs_hook_del removes the notifier from the list and waits for
    them all to complete.
    
    Originally, I selected srcu notifiers over blocking notifiers to
    implement live hooks because they seemed to have fewer impacts to
    scalability.  The per-call cost of srcu_notifier_call_chain is higher
    (19ns) than blocking_notifier_ (4ns) in the single threaded case, but
    blocking notifiers use an rwsem to stabilize the list.  Cacheline
    bouncing for that rwsem is costly to runtime code when there are a lot
    of CPUs running regular filesystem operations.  If there are no hooks
    installed, this is a total waste of CPU time.
    
    Therefore, I stuck with srcu notifiers, despite trading off single
    threaded performance for multithreaded performance.  I also wasn't
    thrilled with the very high teardown time for srcu notifiers, since the
    caller has to wait for the next rcu grace period.  This can take a long
    time if there are a lot of CPUs.
    
    Then I discovered the jump label implementation of static keys.
    
    Jump labels use kernel code patching to replace a branch with a nop sled
    when the key is disabled.  IOWs, they can eliminate the overhead of
    _call_chain when there are no hooks enabled.  This makes blocking
    notifiers competitive again -- scrub runs faster because teardown of the
    chain is a lot cheaper, and runtime code only pays the rwsem locking
    overhead when scrub is actually running.
    
    With jump labels enabled, calls to empty notifier chains are elided from
    the call sites when there are no hooks registered, which means that the
    overhead is 0.36ns when fsck is not running.  This is perfect for most
    of the architectures that XFS is expected to run on (e.g. x86, powerpc,
    arm64, s390x, riscv).
    
    For architectures that don't support jump labels (e.g. m68k) the runtime
    overhead of checking the static key is an atomic counter read.  This
    isn't great, but it's still cheaper than taking a shared rwsem.
    Signed-off-by: default avatarDarrick J. Wong <djwong@kernel.org>
    Reviewed-by: default avatarChristoph Hellwig <hch@lst.de>
    4e98cc90
xfs_hooks.h 2.1 KB