]> git.ipfire.org Git - thirdparty/kernel/linux.git/commit
fs: provide accessors for ->i_state
authorMateusz Guzik <mjguzik@gmail.com>
Thu, 9 Oct 2025 07:59:17 +0000 (09:59 +0200)
committerChristian Brauner <brauner@kernel.org>
Mon, 20 Oct 2025 18:22:26 +0000 (20:22 +0200)
commitd8753f788ab4916341d9fab81795be9f2f49c264
tree21b96c6d5ec48b0623c090cfcc8d80727b59e376
parentcb5db358ab5769cbd3e8e864f14af321126cccdb
fs: provide accessors for ->i_state

Open-coded accesses prevent asserting they are done correctly. One
obvious aspect is locking, but significantly more can checked. For
example it can be detected when the code is clearing flags which are
already missing, or is setting flags when it is illegal (e.g., I_FREEING
when ->i_count > 0).

In order to keep things manageable this patchset merely gets the thing
off the ground with only lockdep checks baked in.

Current consumers can be trivially converted.

Suppose flags I_A and I_B are to be handled.

If ->i_lock is held, then:

state = inode->i_state   => state = inode_state_read(inode)
inode->i_state |= (I_A | I_B)  => inode_state_set(inode, I_A | I_B)
inode->i_state &= ~(I_A | I_B)  => inode_state_clear(inode, I_A | I_B)
inode->i_state = I_A | I_B => inode_state_assign(inode, I_A | I_B)

If ->i_lock is not held or only held conditionally:

state = inode->i_state   => state = inode_state_read_once(inode)
inode->i_state |= (I_A | I_B)  => inode_state_set_raw(inode, I_A | I_B)
inode->i_state &= ~(I_A | I_B)  => inode_state_clear_raw(inode, I_A | I_B)
inode->i_state = I_A | I_B => inode_state_assign_raw(inode, I_A | I_B)

The "_once" vs "_raw" discrepancy stems from the read variant differing
by READ_ONCE as opposed to just lockdep checks.

Finally, if you want to atomically clear flags and set new ones, the
following:

state = inode->i_state;
state &= ~I_A;
state |= I_B;
inode->i_state = state;

turns into:

inode_state_replace(inode, I_A, I_B);

Signed-off-by: Mateusz Guzik <mjguzik@gmail.com>
Signed-off-by: Christian Brauner <brauner@kernel.org>
include/linux/fs.h