Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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>

authored by

Mateusz Guzik and committed by
Christian Brauner
d8753f78 cb5db358

+76 -2
+76 -2
include/linux/fs.h
··· 759 759 /* reserved wait address bit 3 */ 760 760 }; 761 761 762 - enum inode_state_flags_t { 762 + enum inode_state_flags_enum { 763 763 I_NEW = (1U << __I_NEW), 764 764 I_SYNC = (1U << __I_SYNC), 765 765 I_LRU_ISOLATING = (1U << __I_LRU_ISOLATING), ··· 843 843 #endif 844 844 845 845 /* Misc */ 846 - enum inode_state_flags_t i_state; 846 + enum inode_state_flags_enum i_state; 847 847 /* 32-bit hole */ 848 848 struct rw_semaphore i_rwsem; 849 849 ··· 901 901 902 902 void *i_private; /* fs or device private pointer */ 903 903 } __randomize_layout; 904 + 905 + /* 906 + * i_state handling 907 + * 908 + * We hide all of it behind helpers so that we can validate consumers. 909 + */ 910 + static inline enum inode_state_flags_enum inode_state_read_once(struct inode *inode) 911 + { 912 + return READ_ONCE(inode->i_state); 913 + } 914 + 915 + static inline enum inode_state_flags_enum inode_state_read(struct inode *inode) 916 + { 917 + lockdep_assert_held(&inode->i_lock); 918 + return inode->i_state; 919 + } 920 + 921 + static inline void inode_state_set_raw(struct inode *inode, 922 + enum inode_state_flags_enum flags) 923 + { 924 + WRITE_ONCE(inode->i_state, inode->i_state | flags); 925 + } 926 + 927 + static inline void inode_state_set(struct inode *inode, 928 + enum inode_state_flags_enum flags) 929 + { 930 + lockdep_assert_held(&inode->i_lock); 931 + inode_state_set_raw(inode, flags); 932 + } 933 + 934 + static inline void inode_state_clear_raw(struct inode *inode, 935 + enum inode_state_flags_enum flags) 936 + { 937 + WRITE_ONCE(inode->i_state, inode->i_state & ~flags); 938 + } 939 + 940 + static inline void inode_state_clear(struct inode *inode, 941 + enum inode_state_flags_enum flags) 942 + { 943 + lockdep_assert_held(&inode->i_lock); 944 + inode_state_clear_raw(inode, flags); 945 + } 946 + 947 + static inline void inode_state_assign_raw(struct inode *inode, 948 + enum inode_state_flags_enum flags) 949 + { 950 + WRITE_ONCE(inode->i_state, flags); 951 + } 952 + 953 + static inline void inode_state_assign(struct inode *inode, 954 + enum inode_state_flags_enum flags) 955 + { 956 + lockdep_assert_held(&inode->i_lock); 957 + inode_state_assign_raw(inode, flags); 958 + } 959 + 960 + static inline void inode_state_replace_raw(struct inode *inode, 961 + enum inode_state_flags_enum clearflags, 962 + enum inode_state_flags_enum setflags) 963 + { 964 + enum inode_state_flags_enum flags; 965 + flags = inode->i_state; 966 + flags &= ~clearflags; 967 + flags |= setflags; 968 + inode_state_assign_raw(inode, flags); 969 + } 970 + 971 + static inline void inode_state_replace(struct inode *inode, 972 + enum inode_state_flags_enum clearflags, 973 + enum inode_state_flags_enum setflags) 974 + { 975 + lockdep_assert_held(&inode->i_lock); 976 + inode_state_replace_raw(inode, clearflags, setflags); 977 + } 904 978 905 979 static inline void inode_set_cached_link(struct inode *inode, char *link, int linklen) 906 980 {