FSTRANS(9)

πŸ“¦ version 11.0, in nixpkgs unstable

🌸 FSTRANS(9) β€” the kernel dev girlies' guide to file system suspension ✨

okay so bestie, this one's from the Kernel Developer's Manual, and it's literally the whole fstrans subsystem β€” the file system suspension helper squad πŸ’…. Let me walk you through it, no cap.


πŸ’– NAME

fstrans, fstrans_setstate, fstrans_getstate, fstrans_start, fstrans_start_nowait, fstrans_start_lazy, fstrans_done, fstrans_is_owner, fscow_establish, fscow_disestablish, fscow_run β€” the file system suspension helper subsystem, periodt πŸŽ€


✨ SYNOPSIS

#include <sys/mount.h>
#include <sys/fstrans.h>

void
fstrans_start(struct mount *mp);

int
fstrans_start_nowait(struct mount *mp);

void
fstrans_start_lazy(struct mount *mp);

void
fstrans_done(struct mount *mp);

int
fstrans_setstate(struct mount *mp, enum fstrans_state new_state);

enum fstrans_state
fstrans_getstate(struct mount *mp);

int
fstrans_is_owner(struct mount *mp);

int
fscow_establish(struct mount *mp,
    int (*func)(void *, struct buf *, bool), void *cookie);

int
fscow_disestablish(struct mount *mp,
    int (*func)(void *, struct buf *, bool), void *cookie);

int
fscow_run(struct buf *bp, bool data_valid);

πŸ¦‹ DESCRIPTION

The fstrans subsystem is your bestie for two things: file system suspension and copy-on-write snapshots 🌈.

The file system's normal operations β€” think its vnodeops(9) β€” have to be bracketed by fstrans_start() and fstrans_done() in what's called a shared transaction. A shared transaction gets blocked by suspending the file system, and also while the file system is suspended. like, it's not invited to that party 🚫

Operations that are needed while you're suspending the file system must be bracketed by fstrans_start_lazy() and fstrans_done() in a lazy transaction. Lazy transactions are allowed while suspending the file system, but blocked while the file system is suspended. it's giving "you can come to the pregame but not the afterparty" πŸ’€

Transactions are per-thread and nestable: if a thread is already in a transaction, it can enter another transaction without blocking. Every fstrans_start() must be paired with fstrans_done() β€” that's the rule, bestie, don't leave your transaction hanging πŸ™ˆ. Also important: transactions for multiple distinct mount points may not be nested. nope. not a thing.

The file system's VFS_SUSPENDCTL(9) method can use fstrans_setstate() to:

  • enter the FSTRANS_SUSPENDING state β€” suspends all normal operations but allows lazy transactions,
  • enter the FSTRANS_SUSPENDED state β€” suspends literally all operations, and
  • restore to the FSTRANS_NORMAL state β€” resumes all operations.

A file system that supports fstrans may establish a copy-on-write callback with fscow_establish(). That callback gets called every time a buffer is written to a block device with VOP_STRATEGY(), and every time a buffer is read into the buffercache(9) with B_MODIFY set β€” which is the caller's little way of saying "bestie I'm about to modify this" πŸ‘€. Anyone modifying a buffer may additionally use fscow_run() to explicitly invoke the established callback. And when the file system is done with it, the copy-on-write callback must be disestablished with fscow_disestablish(). clean up after yourself, queen ✨


πŸ’… FUNCTIONS

πŸŽ€ fstrans_start(mp)

Enter a transaction on the file system mp in the current thread. If the file system is in a state that blocks such transactions, wait until it changes to a state that doesn't.

If the file system is suspended, wait until it's resumed.

But β€” plot twist β€” if the current thread is already in a transaction on mp, fstrans_start() will enter a nested transaction and return immediately without waiting. main character energy, honestly πŸ‘‘

May sleep.

πŸŽ€ fstrans_start_nowait(mp)

Like fstrans_start(), but return EBUSY immediately if transactions are blocked in its current state.

πŸ’€ Heads up: may sleep nevertheless on internal locks. so like, "nowait" is a vibe, not a promise.

πŸŽ€ fstrans_start_lazy(mp)

Like fstrans_start(), but it will not block while suspending.

May sleep.

πŸŽ€ fstrans_done(mp)

End the current transaction on mp. the exhale after the inhale 🫢

πŸŽ€ fstrans_getstate(mp)

Return the current state of the file system mp.

⚠️ Must be called within a transaction for the answer to be stable. otherwise you're just reading vibes.

πŸŽ€ fstrans_setstate(mp, new_state)

Change the state of the file system mp to new_state, and wait for all transactions not allowed in new_state to complete.

  • FSTRANS_NORMAL β€” Allow all transactions.
  • FSTRANS_SUSPENDING β€” Block FSTRANS_SHARED transactions but allow FSTRANS_LAZY transactions.
  • FSTRANS_SUSPENDED β€” Block all transactions.

A thread that changes a file system to a state other than FSTRANS_NORMAL enters a transaction for the purposes of fstrans_getstate() until it changes the state back to FSTRANS_NORMAL.

Additionally, a thread that changes a file system to a state other than FSTRANS_NORMAL acquires an exclusive lock on the file system state β€” so that fstrans_is_owner() will return true in that thread, and no other thread can change the file system's state until the owner restores it to FSTRANS_NORMAL. it's giving "I'm holding the aux cord until further notice" 🎧

May sleep, and may be interrupted by a signal. On success, return zero. On failure, restore the file system to the FSTRANS_NORMAL state and return an error code. fstrans_setstate() never fails if new_state is FSTRANS_NORMAL. iconic behavior honestly.

πŸŽ€ fstrans_is_owner(mp)

Return true if the current thread is currently suspending the file system mp.

Establish a copy-on-write callback for the file system mp. The function func will be called for every buffer bp written through this file system as:

func(cookie, bp, data_valid)

where data_valid is true if and only if the buffer bp has not yet been modified.

May sleep.

Disestablish a copy-on-write callback established with fscow_establish(). breaking up (amicably) πŸ’”

May sleep.

πŸŽ€ fscow_run(bp, data_valid)

Run all copy-on-write callbacks established for the file system this buffer belongs to, if they have not already been run for this buffer. If data_valid is true, the buffer data has not yet been modified.

May sleep.


πŸ”₯ EXAMPLES

Okay so here's an example of a file system suspend operation, look at her go:

int
xxx_suspendctl(struct mount *mp, int cmd)
{
        int error;

        switch (cmd) {
        case SUSPEND_SUSPEND:
                error = fstrans_setstate(mp, FSTRANS_SUSPENDING);
                if (error)
                        return error;
                return fstrans_setstate(mp, FSTRANS_SUSPENDED);

        case SUSPEND_RESUME:
                return fstrans_setstate(mp, FSTRANS_NORMAL);

        default:
                return EINVAL;
        }
}

And this is an example of a file system operation πŸ¦‹:

int
xxx_create(void *v)
{
        struct vop_create_args *ap = v;
        struct mount *mp = ap->a_dvp->v_mount;
        int error;

        fstrans_start(mp);

        /* Actually create the node. */

        fstrans_done(mp);

        return 0;
}

⭐ CODE REFERENCES

The fstrans subsystem is implemented in the file sys/kern/vfs_trans.c. go read it if you're nosy (affectionate) πŸ‘€


🌈 SEE ALSO

vfs_resume(9), vfs_suspend(9)


πŸ’œ HISTORY

The fstrans subsystem appeared in NetBSD 5.0. a classic, honestly.


πŸ‘‘ AUTHORS

The fstrans subsystem was written by JΓΌrgen Hannken-Illjes ⟨hannken@NetBSD.org⟩. we stan πŸ’–


πŸ™ˆ BUGS

fstrans is useful only for temporary suspension β€” it does not help to permanently block file system operations for unmounting, because fstrans_start() cannot fail. like, it literally has no failure mode, so don't ask it to do that, bestie 😭


and that's the whole page, mwah πŸ’‹ β€” this cutie is from the Debian build of the manpage, dated October 4, 2018, filed under FSTRANS(9). go forth and suspend responsibly πŸŽ€βœ¨

πŸ“¦ where this page comes from