Merge 6b5b72ac2c ("Merge tag 'vfs-7.2-rc1.directory.delegations' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs") into android-mainline

Steps on the way to v7.2-rc1

Change-Id: Iba77b2977abbd83e55ef728fddf3c32b24d0b9e6
Signed-off-by: Carlos Llamas <cmllamas@google.com>
This commit is contained in:
Carlos Llamas committed 2026-06-29 17:39:49 +00:00
commit a67280de4c
37 files changed
+623 -187

No files matched your search

+1 -1
View File
@@ -9916,7 +9916,7 @@ S: Supported
F: Documentation/filesystems/nfs/exporting.rst
F: fs/exportfs/
F: fs/fhandle.c
F: include/linux/exportfs.h
F: include/linux/exportfs*.h
FILESYSTEMS [IDMAPPED MOUNTS]
M: Christian Brauner <brauner@kernel.org>
+1 -1
View File
@@ -1430,7 +1430,7 @@ static int spufs_mfc_open(struct inode *inode, struct file *file)
if (ctx->owner != current->mm)
return -EINVAL;
if (icount_read(inode) != 1)
if (icount_read_once(inode) != 1)
return -EBUSY;
mutex_lock(&ctx->mapping_lock);
+1 -1
View File
@@ -547,7 +547,7 @@ int notify_change(struct mnt_idmap *idmap, struct dentry *dentry,
* breaking the delegation in this case.
*/
if (!(ia_valid & ATTR_DELEG)) {
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (error)
return error;
}
+8 -2
View File
@@ -100,7 +100,7 @@ static bool match_security_bpf_prefix(const char *name__str)
static int bpf_xattr_read_permission(const char *name, struct inode *inode)
{
if (WARN_ON(!inode))
if (!inode)
return -EINVAL;
/* Allow reading xattr with user. and security.bpf. prefix */
@@ -170,7 +170,7 @@ __bpf_kfunc_end_defs();
static int bpf_xattr_write_permission(const char *name, struct inode *inode)
{
if (WARN_ON(!inode))
if (!inode)
return -EINVAL;
/* Only allow setting and removing security.bpf. xattrs */
@@ -289,6 +289,9 @@ __bpf_kfunc int bpf_set_dentry_xattr(struct dentry *dentry, const char *name__st
struct inode *inode = d_inode(dentry);
int ret;
if (!inode)
return -EINVAL;
inode_lock(inode);
ret = bpf_set_dentry_xattr_locked(dentry, name__str, value_p, flags);
inode_unlock(inode);
@@ -314,6 +317,9 @@ __bpf_kfunc int bpf_remove_dentry_xattr(struct dentry *dentry, const char *name_
struct inode *inode = d_inode(dentry);
int ret;
if (!inode)
return -EINVAL;
inode_lock(inode);
ret = bpf_remove_dentry_xattr_locked(dentry, name__str);
inode_unlock(inode);
+1 -1
View File
@@ -4745,7 +4745,7 @@ static void btrfs_prune_dentries(struct btrfs_root *root)
inode = btrfs_find_first_inode(root, min_ino);
while (inode) {
if (icount_read(&inode->vfs_inode) > 1)
if (icount_read_once(&inode->vfs_inode) > 1)
d_prune_aliases(&inode->vfs_inode);
min_ino = btrfs_ino(inode) + 1;
+1 -1
View File
@@ -2267,7 +2267,7 @@ static int trim_caps_cb(struct inode *inode, int mds, void *arg)
int count;
dput(dentry);
d_prune_aliases(inode);
count = icount_read(inode);
count = icount_read_once(inode);
if (count == 1)
(*remaining)--;
doutc(cl, "%p %llx.%llx cap %p pruned, count now %d\n",
+4
View File
@@ -2100,6 +2100,10 @@ void d_instantiate_new(struct dentry *entry, struct inode *inode)
__d_instantiate(entry, inode);
spin_unlock(&entry->d_lock);
WARN_ON(!(inode_state_read(inode) & I_NEW));
/*
* Paired with igrab_from_hash()
*/
smp_wmb();
inode_state_clear(inode, I_NEW | I_CREATING);
inode_wake_up_bit(inode, __I_NEW);
spin_unlock(&inode->i_lock);
+2 -2
View File
@@ -252,10 +252,10 @@ void ext4_free_inode(handle_t *handle, struct inode *inode)
"nonexistent device\n", __func__, __LINE__);
return;
}
if (icount_read(inode) > 1) {
if (icount_read_once(inode) > 1) {
ext4_msg(sb, KERN_ERR, "%s:%d: inode #%llu: count=%d",
__func__, __LINE__, inode->i_ino,
icount_read(inode));
icount_read_once(inode));
return;
}
if (inode->i_nlink) {
+1 -1
View File
@@ -184,7 +184,7 @@ void hpfs_write_inode(struct inode *i)
struct hpfs_inode_info *hpfs_inode = hpfs_i(i);
struct inode *parent;
if (i->i_ino == hpfs_sb(i->i_sb)->sb_root) return;
if (hpfs_inode->i_rddir_off && !icount_read(i)) {
if (hpfs_inode->i_rddir_off && !icount_read_once(i)) {
if (*hpfs_inode->i_rddir_off)
pr_err("write_inode: some position still there\n");
kfree(hpfs_inode->i_rddir_off);
+81 -19
View File
@@ -53,11 +53,7 @@
* inode->i_lock
*
* inode_hash_lock
* inode->i_sb->s_inode_list_lock
* inode->i_lock
*
* iunique_lock
* inode_hash_lock
*/
static unsigned int i_hash_mask __ro_after_init;
@@ -518,15 +514,6 @@ static void init_once(void *foo)
inode_init_once(inode);
}
/*
* get additional reference to inode; caller must already hold one.
*/
void ihold(struct inode *inode)
{
WARN_ON(atomic_inc_return(&inode->i_count) < 2);
}
EXPORT_SYMBOL(ihold);
struct wait_queue_head *inode_bit_waitqueue(struct wait_bit_queue_entry *wqe,
struct inode *inode, u32 bit)
{
@@ -902,7 +889,7 @@ void evict_inodes(struct super_block *sb)
again:
spin_lock(&sb->s_inode_list_lock);
list_for_each_entry(inode, &sb->s_inodes, i_sb_list) {
if (icount_read(inode))
if (icount_read_once(inode))
continue;
spin_lock(&inode->i_lock);
@@ -1032,6 +1019,7 @@ long prune_icache_sb(struct super_block *sb, struct shrink_control *sc)
}
static void __wait_on_freeing_inode(struct inode *inode, bool hash_locked, bool rcu_locked);
static bool igrab_from_hash(struct inode *inode);
/*
* Called with the inode lock held.
@@ -1056,6 +1044,11 @@ repeat:
continue;
if (!test(inode, data))
continue;
if (igrab_from_hash(inode)) {
rcu_read_unlock();
*isnew = false;
return inode;
}
spin_lock(&inode->i_lock);
if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE)) {
__wait_on_freeing_inode(inode, hash_locked, true);
@@ -1098,6 +1091,11 @@ repeat:
continue;
if (inode->i_sb != sb)
continue;
if (igrab_from_hash(inode)) {
rcu_read_unlock();
*isnew = false;
return inode;
}
spin_lock(&inode->i_lock);
if (inode_state_read(inode) & (I_FREEING | I_WILL_FREE)) {
__wait_on_freeing_inode(inode, hash_locked, true);
@@ -1215,6 +1213,10 @@ void unlock_new_inode(struct inode *inode)
lockdep_annotate_inode_mutex_key(inode);
spin_lock(&inode->i_lock);
WARN_ON(!(inode_state_read(inode) & I_NEW));
/*
* Paired with igrab_from_hash()
*/
smp_wmb();
inode_state_clear(inode, I_NEW | I_CREATING);
inode_wake_up_bit(inode, __I_NEW);
spin_unlock(&inode->i_lock);
@@ -1226,6 +1228,10 @@ void discard_new_inode(struct inode *inode)
lockdep_annotate_inode_mutex_key(inode);
spin_lock(&inode->i_lock);
WARN_ON(!(inode_state_read(inode) & I_NEW));
/*
* Paired with igrab_from_hash()
*/
smp_wmb();
inode_state_clear(inode, I_NEW);
inode_wake_up_bit(inode, __I_NEW);
spin_unlock(&inode->i_lock);
@@ -1572,8 +1578,27 @@ ino_t iunique(struct super_block *sb, ino_t max_reserved)
}
EXPORT_SYMBOL(iunique);
/**
* ihold - get a reference on the inode, provided you already have one
* @inode: inode to operate on
*/
void ihold(struct inode *inode)
{
VFS_BUG_ON_INODE(icount_read_once(inode) < 1, inode);
WARN_ON(atomic_inc_return(&inode->i_count) < 2);
}
EXPORT_SYMBOL(ihold);
struct inode *igrab(struct inode *inode)
{
/*
* Read commentary above igrab_from_hash() for an explanation why this works.
*/
if (atomic_add_unless(&inode->i_count, 1, 0)) {
VFS_BUG_ON_INODE(inode_state_read_once(inode) & (I_FREEING | I_WILL_FREE), inode);
return inode;
}
spin_lock(&inode->i_lock);
if (!(inode_state_read(inode) & (I_FREEING | I_WILL_FREE))) {
__iget(inode);
@@ -1591,6 +1616,43 @@ struct inode *igrab(struct inode *inode)
}
EXPORT_SYMBOL(igrab);
/*
* igrab_from_hash - special inode refcount acquire primitive for the inode hash
*
* It provides lockless refcount acquire in the common case of no problematic
* flags being set and the count being > 0.
*
* There are 4 state flags to worry about and the routine makes sure to not bump the
* ref if any of them is present.
*
* I_NEW and I_CREATING can only legally get set *before* the inode becomes visible
* during lookup. Thus if the flags are not spotted, they are guaranteed to not be
* a factor. However, we need an acquire fence before returning the inode just
* in case we raced against clearing the state to make sure our consumer picks up
* any other changes made prior. atomic_add_unless provides a full fence, which
* takes care of it.
*
* I_FREEING and I_WILL_FREE can only legally get set if ->i_count == 0 and it is
* illegal to bump the ref if either is present. Consequently if atomic_add_unless
* managed to replace a non-0 value with a bigger one, we have a guarantee neither
* of these flags is set. Note this means explicitly checking of these flags below
* is not necessary, it is only done because it does not cost anything on top of the
* load which already needs to be done to handle the other flags.
*/
static bool igrab_from_hash(struct inode *inode)
{
if (inode_state_read_once(inode) & (I_NEW | I_CREATING | I_FREEING | I_WILL_FREE))
return false;
/*
* Paired with routines clearing I_NEW
*/
if (atomic_add_unless(&inode->i_count, 1, 0)) {
VFS_BUG_ON_INODE(inode_state_read_once(inode) & (I_FREEING | I_WILL_FREE), inode);
return true;
}
return false;
}
/**
* ilookup5_nowait - search for an inode in the inode cache
* @sb: super block of file system to search
@@ -1920,7 +1982,7 @@ static void iput_final(struct inode *inode)
int drop;
WARN_ON(inode_state_read(inode) & I_NEW);
VFS_BUG_ON_INODE(atomic_read(&inode->i_count) != 0, inode);
VFS_BUG_ON_INODE(icount_read(inode) != 0, inode);
if (op->drop_inode)
drop = op->drop_inode(inode);
@@ -1939,7 +2001,7 @@ static void iput_final(struct inode *inode)
* Re-check ->i_count in case the ->drop_inode() hooks played games.
* Note we only execute this if the verdict was to drop the inode.
*/
VFS_BUG_ON_INODE(atomic_read(&inode->i_count) != 0, inode);
VFS_BUG_ON_INODE(icount_read(inode) != 0, inode);
if (drop) {
inode_state_set(inode, I_FREEING);
@@ -1983,7 +2045,7 @@ retry:
* equal to one, then two CPUs racing to further drop it can both
* conclude it's fine.
*/
VFS_BUG_ON_INODE(atomic_read(&inode->i_count) < 1, inode);
VFS_BUG_ON_INODE(icount_read_once(inode) < 1, inode);
if (atomic_add_unless(&inode->i_count, -1, 1))
return;
@@ -2017,7 +2079,7 @@ EXPORT_SYMBOL(iput);
void iput_not_last(struct inode *inode)
{
VFS_BUG_ON_INODE(inode_state_read_once(inode) & (I_FREEING | I_CLEAR), inode);
VFS_BUG_ON_INODE(atomic_read(&inode->i_count) < 2, inode);
VFS_BUG_ON_INODE(icount_read_once(inode) < 2, inode);
WARN_ON(atomic_sub_return(1, &inode->i_count) == 0);
}
@@ -3046,7 +3108,7 @@ void dump_inode(struct inode *inode, const char *reason)
}
state = inode_state_read_once(inode);
count = atomic_read(&inode->i_count);
count = icount_read_once(inode);
if (!sb ||
get_kernel_nofault(s_type, &sb->s_type) || !s_type ||
+92 -26
View File
@@ -1582,30 +1582,96 @@ trace:
return rc;
}
static bool
any_leases_conflict(struct inode *inode, struct file_lease *breaker)
#define IGNORE_MASK (FL_IGN_DIR_CREATE | FL_IGN_DIR_DELETE | FL_IGN_DIR_RENAME)
/**
* inode_lease_ignore_mask - return union of all ignored inode events for this inode
* @inode: inode of which to get ignore mask
*
* Walk the list of leases, and return the result of all of
* their FL_IGN_DIR_* bits or'ed together.
*/
u32
inode_lease_ignore_mask(struct inode *inode)
{
struct file_lock_context *ctx = inode->i_flctx;
struct file_lock_context *ctx;
struct file_lock_core *flc;
u32 mask = 0;
lockdep_assert_held(&ctx->flc_lock);
ctx = locks_inode_context(inode);
if (!ctx)
return 0;
spin_lock(&ctx->flc_lock);
list_for_each_entry(flc, &ctx->flc_lease, flc_list) {
if (leases_conflict(flc, &breaker->c))
return true;
mask |= flc->flc_flags & IGNORE_MASK;
/* If we already have everything, we can stop */
if (mask == IGNORE_MASK)
break;
}
spin_unlock(&ctx->flc_lock);
return mask;
}
EXPORT_SYMBOL_GPL(inode_lease_ignore_mask);
static bool
ignore_dir_deleg_break(struct file_lease *fl, unsigned int flags)
{
if ((flags & LEASE_BREAK_DIR_CREATE) && (fl->c.flc_flags & FL_IGN_DIR_CREATE))
return true;
if ((flags & LEASE_BREAK_DIR_DELETE) && (fl->c.flc_flags & FL_IGN_DIR_DELETE))
return true;
if ((flags & LEASE_BREAK_DIR_RENAME) && (fl->c.flc_flags & FL_IGN_DIR_RENAME))
return true;
return false;
}
static unsigned int
break_lease_flags_to_type(unsigned int flags)
{
if (flags & LEASE_BREAK_LEASE)
return FL_LEASE;
else if (flags & LEASE_BREAK_DELEG)
return FL_DELEG;
else if (flags & LEASE_BREAK_LAYOUT)
return FL_LAYOUT;
else
return 0;
}
static struct file_lease *
first_visible_lease(struct inode *inode, struct file_lease *new_fl, unsigned int flags)
{
struct file_lock_context *ctx = locks_inode_context(inode);
struct file_lease *fl;
lockdep_assert_held(&ctx->flc_lock);
list_for_each_entry(fl, &ctx->flc_lease, c.flc_list) {
if (!leases_conflict(&fl->c, &new_fl->c))
continue;
if (S_ISDIR(inode->i_mode) && ignore_dir_deleg_break(fl, flags))
continue;
return fl;
}
return NULL;
}
/**
* __break_lease - revoke all outstanding leases on file
* @inode: the inode of the file to return
* @flags: LEASE_BREAK_* flags
* __break_lease - revoke all outstanding leases on file
* @inode: the inode of the file to return
* @flags: LEASE_BREAK_* flags
*
* break_lease (inlined for speed) has checked there already is at least
* some kind of lock (maybe a lease) on this file. Leases are broken on
* a call to open() or truncate(). This function can block waiting for the
* lease break unless you specify LEASE_BREAK_NONBLOCK.
* break_lease (inlined for speed) has checked there already is at least
* some kind of lock (maybe a lease) on this file. Leases and Delegations
* are broken on a call to open() or truncate(). Delegations are also
* broken on any event that would change the ctime. Directory delegations
* are broken whenever the directory changes (unless the delegation is set
* up to ignore the event). This function can block waiting for the lease
* break unless you specify LEASE_BREAK_NONBLOCK.
*/
int __break_lease(struct inode *inode, unsigned int flags)
{
@@ -1617,13 +1683,10 @@ int __break_lease(struct inode *inode, unsigned int flags)
bool want_write = !(flags & LEASE_BREAK_OPEN_RDONLY);
int error = 0;
if (flags & LEASE_BREAK_LEASE)
type = FL_LEASE;
else if (flags & LEASE_BREAK_DELEG)
type = FL_DELEG;
else if (flags & LEASE_BREAK_LAYOUT)
type = FL_LAYOUT;
else
trace_break_lease(inode, flags);
type = break_lease_flags_to_type(flags);
if (!type)
return -EINVAL;
new_fl = lease_alloc(NULL, type, want_write ? F_WRLCK : F_RDLCK);
@@ -1642,7 +1705,7 @@ int __break_lease(struct inode *inode, unsigned int flags)
time_out_leases(inode, &dispose);
if (!any_leases_conflict(inode, new_fl))
if (!first_visible_lease(inode, new_fl, flags))
goto out;
break_time = 0;
@@ -1655,6 +1718,8 @@ int __break_lease(struct inode *inode, unsigned int flags)
list_for_each_entry_safe(fl, tmp, &ctx->flc_lease, c.flc_list) {
if (!leases_conflict(&fl->c, &new_fl->c))
continue;
if (S_ISDIR(inode->i_mode) && ignore_dir_deleg_break(fl, flags))
continue;
if (want_write) {
if (fl->c.flc_flags & FL_UNLOCK_PENDING)
continue;
@@ -1670,7 +1735,8 @@ int __break_lease(struct inode *inode, unsigned int flags)
locks_delete_lock_ctx(&fl->c, &dispose);
}
if (list_empty(&ctx->flc_lease))
fl = first_visible_lease(inode, new_fl, flags);
if (!fl)
goto out;
if (flags & LEASE_BREAK_NONBLOCK) {
@@ -1680,7 +1746,6 @@ int __break_lease(struct inode *inode, unsigned int flags)
}
restart:
fl = list_first_entry(&ctx->flc_lease, struct file_lease, c.flc_list);
break_time = fl->fl_break_time;
if (break_time != 0) {
if (time_after(jiffies, break_time)) {
@@ -1691,7 +1756,7 @@ restart:
} else
break_time++;
locks_insert_block(&fl->c, &new_fl->c, leases_conflict);
trace_break_lease_block(inode, new_fl);
trace_break_lease_block(inode, fl);
spin_unlock(&ctx->flc_lock);
percpu_up_read(&file_rwsem);
@@ -1702,7 +1767,7 @@ restart:
percpu_down_read(&file_rwsem);
spin_lock(&ctx->flc_lock);
trace_break_lease_unblock(inode, new_fl);
trace_break_lease_unblock(inode, NULL);
__locks_delete_block(&new_fl->c);
if (error >= 0) {
/*
@@ -1711,7 +1776,8 @@ restart:
*/
if (error == 0)
time_out_leases(inode, &dispose);
if (any_leases_conflict(inode, new_fl))
fl = first_visible_lease(inode, new_fl, flags);
if (fl)
goto restart;
error = 0;
}
+17 -14
View File
@@ -4198,7 +4198,7 @@ int vfs_create(struct mnt_idmap *idmap, struct dentry *dentry, umode_t mode,
error = security_inode_create(dir, dentry, mode);
if (error)
return error;
error = try_break_deleg(dir, di);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, di);
if (error)
return error;
error = dir->i_op->create(idmap, dir, dentry, mode, true);
@@ -4497,7 +4497,7 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
/* Negative dentry, just create the file */
if (!dentry->d_inode && (open_flag & O_CREAT)) {
/* but break the directory lease first! */
error = try_break_deleg(dir_inode, delegated_inode);
error = try_break_deleg(dir_inode, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto out_dput;
@@ -5123,7 +5123,7 @@ int vfs_mknod(struct mnt_idmap *idmap, struct inode *dir,
if (error)
return error;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
return error;
@@ -5264,7 +5264,7 @@ struct dentry *vfs_mkdir(struct mnt_idmap *idmap, struct inode *dir,
if (max_links && dir->i_nlink >= max_links)
goto err;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto err;
@@ -5369,7 +5369,7 @@ int vfs_rmdir(struct mnt_idmap *idmap, struct inode *dir,
if (error)
goto out;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_DELETE, delegated_inode);
if (error)
goto out;
@@ -5499,10 +5499,10 @@ int vfs_unlink(struct mnt_idmap *idmap, struct inode *dir,
else {
error = security_inode_unlink(dir, dentry);
if (!error) {
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_DELETE, delegated_inode);
if (error)
goto out;
error = try_break_deleg(target, delegated_inode);
error = try_break_deleg(target, 0, delegated_inode);
if (error)
goto out;
error = dir->i_op->unlink(dir, dentry);
@@ -5646,7 +5646,7 @@ int vfs_symlink(struct mnt_idmap *idmap, struct inode *dir,
if (error)
return error;
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
return error;
@@ -5777,9 +5777,9 @@ int vfs_link(struct dentry *old_dentry, struct mnt_idmap *idmap,
else if (max_links && inode->i_nlink >= max_links)
error = -EMLINK;
else {
error = try_break_deleg(dir, delegated_inode);
error = try_break_deleg(dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (!error)
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (!error)
error = dir->i_op->link(old_dentry, dir, new_dentry);
}
@@ -6043,21 +6043,24 @@ int vfs_rename(struct renamedata *rd)
old_dir->i_nlink >= max_links)
goto out;
}
error = try_break_deleg(old_dir, delegated_inode);
error = try_break_deleg(old_dir,
old_dir == new_dir ? LEASE_BREAK_DIR_RENAME :
LEASE_BREAK_DIR_DELETE,
delegated_inode);
if (error)
goto out;
if (new_dir != old_dir) {
error = try_break_deleg(new_dir, delegated_inode);
error = try_break_deleg(new_dir, LEASE_BREAK_DIR_CREATE, delegated_inode);
if (error)
goto out;
}
if (!is_dir) {
error = try_break_deleg(source, delegated_inode);
error = try_break_deleg(source, 0, delegated_inode);
if (error)
goto out;
}
if (target && !new_is_dir) {
error = try_break_deleg(target, delegated_inode);
error = try_break_deleg(target, 0, delegated_inode);
if (error)
goto out;
}
+2 -2
View File
@@ -608,7 +608,7 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr)
inode->i_sb->s_id,
(unsigned long long)NFS_FILEID(inode),
nfs_display_fhandle_hash(fh),
icount_read(inode));
icount_read_once(inode));
out:
return inode;
@@ -2255,7 +2255,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
dfprintk(VFS, "NFS: %s(%s/%llu fh_crc=0x%08x ct=%d info=0x%llx)\n",
__func__, inode->i_sb->s_id, inode->i_ino,
nfs_display_fhandle_hash(NFS_FH(inode)),
icount_read(inode), fattr->valid);
icount_read_once(inode), fattr->valid);
if (!(fattr->valid & NFS_ATTR_FATTR_FILEID)) {
/* Only a mounted-on-fileid? Just exit */
+17 -20
View File
@@ -2,7 +2,7 @@
/*
* Copyright (c) 2014-2016 Christoph Hellwig.
*/
#include <linux/exportfs.h>
#include <linux/exportfs_block.h>
#include <linux/iomap.h>
#include <linux/slab.h>
#include <linux/pr.h>
@@ -32,8 +32,8 @@ nfsd4_block_map_extent(struct inode *inode, const struct svc_fh *fhp,
u32 device_generation = 0;
int error;
error = sb->s_export_op->map_blocks(inode, offset, length, &iomap,
iomode != IOMODE_READ, &device_generation);
error = sb->s_export_op->block_ops->map_blocks(inode, offset, length,
&iomap, iomode != IOMODE_READ, &device_generation);
if (error) {
if (error == -ENXIO)
return nfserr_layoutunavailable;
@@ -179,23 +179,20 @@ static __be32
nfsd4_block_commit_blocks(struct inode *inode, struct nfsd4_layoutcommit *lcp,
struct iomap *iomaps, int nr_iomaps)
{
struct timespec64 mtime = inode_get_mtime(inode);
struct iattr iattr = { .ia_valid = 0 };
int error;
if (lcp->lc_mtime.tv_nsec == UTIME_NOW ||
timespec64_compare(&lcp->lc_mtime, &mtime) < 0)
lcp->lc_mtime = current_time(inode);
iattr.ia_valid |= ATTR_ATIME | ATTR_CTIME | ATTR_MTIME;
iattr.ia_atime = iattr.ia_ctime = iattr.ia_mtime = lcp->lc_mtime;
if (lcp->lc_size_chg) {
iattr.ia_valid |= ATTR_SIZE;
iattr.ia_size = lcp->lc_newsize;
}
error = inode->i_sb->s_export_op->commit_blocks(inode, iomaps,
nr_iomaps, &iattr);
/*
* This ignores the client provided mtime in loca_time_modify, as a
* fully client specified mtime doesn't really fit into the Linux
* multi-grain timestamp architecture.
*
* RFC 8881 Section 18.42 makes it clear that the client provided
* timestamp is a "may" condition, and clients that want to force a
* specific timestamp should send a separate SETATTR in the compound.
*/
error = inode->i_sb->s_export_op->block_ops->commit_blocks(inode,
iomaps, nr_iomaps,
lcp->lc_size_chg ? lcp->lc_newsize : 0);
kfree(iomaps);
return nfserrno(error);
}
@@ -218,8 +215,8 @@ nfsd4_block_get_device_info_simple(struct super_block *sb,
b->type = PNFS_BLOCK_VOLUME_SIMPLE;
b->simple.sig_len = PNFS_BLOCK_UUID_LEN;
return sb->s_export_op->get_uuid(sb, b->simple.sig, &b->simple.sig_len,
&b->simple.offset);
return sb->s_export_op->block_ops->get_uuid(sb, b->simple.sig,
&b->simple.sig_len, &b->simple.offset);
}
static __be32
+2 -1
View File
@@ -735,7 +735,8 @@ static int svc_export_parse(struct cache_detail *cd, char *mesg, int mlen)
goto out4;
err = 0;
nfsd4_setup_layout_type(&exp);
if (exp.ex_flags & NFSEXP_PNFS)
nfsd4_setup_layout_type(&exp);
}
expp = svc_export_lookup(&exp);
+8 -21
View File
@@ -2,7 +2,7 @@
/*
* Copyright (c) 2014 Christoph Hellwig.
*/
#include <linux/blkdev.h>
#include <linux/exportfs_block.h>
#include <linux/kmod.h>
#include <linux/file.h>
#include <linux/jhash.h>
@@ -127,30 +127,17 @@ nfsd4_set_deviceid(struct nfsd4_deviceid *id, const struct svc_fh *fhp,
void nfsd4_setup_layout_type(struct svc_export *exp)
{
#if defined(CONFIG_NFSD_BLOCKLAYOUT) || defined(CONFIG_NFSD_SCSILAYOUT)
struct super_block *sb = exp->ex_path.mnt->mnt_sb;
#endif
expfs_block_layouts_t block_supported = exportfs_layouts_supported(sb);
if (!(exp->ex_flags & NFSEXP_PNFS))
return;
#ifdef CONFIG_NFSD_FLEXFILELAYOUT
exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
#endif
#ifdef CONFIG_NFSD_BLOCKLAYOUT
if (sb->s_export_op->get_uuid &&
sb->s_export_op->map_blocks &&
sb->s_export_op->commit_blocks)
if (IS_ENABLED(CONFIG_NFSD_FLEXFILELAYOUT))
exp->ex_layout_types |= 1 << LAYOUT_FLEX_FILES;
if (IS_ENABLED(CONFIG_NFSD_BLOCKLAYOUT) &&
(block_supported & EXPFS_BLOCK_IN_BAND_ID))
exp->ex_layout_types |= 1 << LAYOUT_BLOCK_VOLUME;
#endif
#ifdef CONFIG_NFSD_SCSILAYOUT
if (sb->s_export_op->map_blocks &&
sb->s_export_op->commit_blocks &&
sb->s_bdev &&
sb->s_bdev->bd_disk->fops->pr_ops &&
sb->s_bdev->bd_disk->fops->get_unique_id)
if (IS_ENABLED(CONFIG_NFSD_SCSILAYOUT) &&
(block_supported & EXPFS_BLOCK_OUT_OF_BAND_ID))
exp->ex_layout_types |= 1 << LAYOUT_SCSI;
#endif
}
void nfsd4_close_layout(struct nfs4_layout_stateid *ls)
+5
View File
@@ -14,6 +14,9 @@
#include <linux/fsnotify_backend.h>
#include "fsnotify.h"
#define CREATE_TRACE_POINTS
#include <trace/events/fsnotify.h>
/*
* Clear all of the marks on an inode when it is being evicted from core
*/
@@ -504,6 +507,8 @@ int fsnotify(__u32 mask, const void *data, int data_type, struct inode *dir,
int ret = 0;
__u32 test_mask, marks_mask = 0;
trace_fsnotify(mask, data, data_type, dir, file_name, inode, cookie);
if (path)
mnt = real_mount(path->mnt);
+29
View File
@@ -343,6 +343,35 @@ void fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
fsnotify_conn_set_children_dentry_flags(conn);
}
/**
* fsnotify_modify_mark_mask - set and/or clear flags in a mark's mask
* @mark: mark to be modified
* @set: bits to be set in mask
* @clear: bits to be cleared in mask
*
* Modify a fsnotify_mark mask as directed, and update its associated conn.
* The caller is expected to hold a reference to the mark.
*/
void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear)
{
bool recalc = false;
u32 mask;
WARN_ON_ONCE(clear & set);
spin_lock(&mark->lock);
mask = mark->mask;
mark->mask |= set;
mark->mask &= ~clear;
if (mark->mask != mask)
recalc = true;
spin_unlock(&mark->lock);
if (recalc)
fsnotify_recalc_mask(mark->connector);
}
EXPORT_SYMBOL_GPL(fsnotify_modify_mark_mask);
/* Free all connectors queued for freeing once SRCU period ends */
static void fsnotify_connector_destroy_workfn(struct work_struct *work)
{
+2 -2
View File
@@ -1126,7 +1126,7 @@ retry_deleg:
if (error)
goto out_inode_unlock;
error = try_break_deleg(inode, &delegated_inode);
error = try_break_deleg(inode, 0, &delegated_inode);
if (error)
goto out_inode_unlock;
@@ -1234,7 +1234,7 @@ retry_deleg:
if (error)
goto out_inode_unlock;
error = try_break_deleg(inode, &delegated_inode);
error = try_break_deleg(inode, 0, &delegated_inode);
if (error)
goto out_inode_unlock;
+1 -1
View File
@@ -2845,7 +2845,7 @@ int cifs_revalidate_dentry_attr(struct dentry *dentry)
}
cifs_dbg(FYI, "Update attributes: %s inode 0x%p count %d dentry: 0x%p d_time %ld jiffies %ld\n",
full_path, inode, icount_read(inode),
full_path, inode, icount_read_once(inode),
dentry, cifs_get_time(dentry), jiffies);
again:
+1 -1
View File
@@ -358,7 +358,7 @@ static void ubifs_evict_inode(struct inode *inode)
goto out;
dbg_gen("inode %llu, mode %#x", inode->i_ino, (int)inode->i_mode);
ubifs_assert(c, !icount_read(inode));
ubifs_assert(c, !icount_read_once(inode));
truncate_inode_pages_final(&inode->i_data);
+2 -2
View File
@@ -306,7 +306,7 @@ __vfs_setxattr_locked(struct mnt_idmap *idmap, struct dentry *dentry,
if (error)
goto out;
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (error)
goto out;
@@ -564,7 +564,7 @@ __vfs_removexattr_locked(struct mnt_idmap *idmap,
if (error)
goto out;
error = try_break_deleg(inode, delegated_inode);
error = try_break_deleg(inode, 0, delegated_inode);
if (error)
goto out;
+1 -3
View File
@@ -244,8 +244,6 @@ const struct export_operations xfs_export_operations = {
.get_parent = xfs_fs_get_parent,
.commit_metadata = xfs_fs_nfs_commit_metadata,
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
.get_uuid = xfs_fs_get_uuid,
.map_blocks = xfs_fs_map_blocks,
.commit_blocks = xfs_fs_commit_blocks,
.block_ops = &xfs_export_block_ops,
#endif
};
+1 -1
View File
@@ -1040,7 +1040,7 @@ xfs_itruncate_extents_flags(
int error = 0;
xfs_assert_ilocked(ip, XFS_ILOCK_EXCL);
if (icount_read(VFS_I(ip)))
if (icount_read_once(VFS_I(ip)))
xfs_assert_ilocked(ip, XFS_IOLOCK_EXCL);
if (whichfork == XFS_DATA_FORK)
ASSERT(new_size <= XFS_ISIZE(ip));
+32 -12
View File
@@ -13,6 +13,7 @@
#include "xfs_bmap.h"
#include "xfs_iomap.h"
#include "xfs_pnfs.h"
#include <linux/exportfs_block.h>
/*
* Ensure that we do not have any outstanding pNFS layouts that can be used by
@@ -45,11 +46,22 @@ xfs_break_leased_layouts(
return error;
}
static expfs_block_layouts_t
xfs_fs_layouts_supported(
struct super_block *sb)
{
expfs_block_layouts_t supported = EXPFS_BLOCK_IN_BAND_ID;
if (exportfs_bdev_supports_out_of_band_id(sb->s_bdev))
supported |= EXPFS_BLOCK_OUT_OF_BAND_ID;
return supported;
}
/*
* Get a unique ID including its location so that the client can identify
* the exported device.
*/
int
static int
xfs_fs_get_uuid(
struct super_block *sb,
u8 *buf,
@@ -104,7 +116,7 @@ xfs_fs_map_update_inode(
/*
* Get a layout for the pNFS client.
*/
int
static int
xfs_fs_map_blocks(
struct inode *inode,
loff_t offset,
@@ -255,28 +267,27 @@ xfs_pnfs_validate_isize(
* to manually flush the cache here similar to what the fsync code path does
* for datasyncs on files that have no dirty metadata.
*/
int
static int
xfs_fs_commit_blocks(
struct inode *inode,
struct iomap *maps,
int nr_maps,
struct iattr *iattr)
loff_t new_size)
{
struct xfs_inode *ip = XFS_I(inode);
struct xfs_mount *mp = ip->i_mount;
struct xfs_trans *tp;
struct timespec64 now;
bool update_isize = false;
int error, i;
loff_t size;
ASSERT(iattr->ia_valid & (ATTR_ATIME|ATTR_CTIME|ATTR_MTIME));
xfs_ilock(ip, XFS_IOLOCK_EXCL);
size = i_size_read(inode);
if ((iattr->ia_valid & ATTR_SIZE) && iattr->ia_size > size) {
if (new_size > size) {
update_isize = true;
size = iattr->ia_size;
size = new_size;
}
for (i = 0; i < nr_maps; i++) {
@@ -321,11 +332,13 @@ xfs_fs_commit_blocks(
xfs_trans_ijoin(tp, ip, XFS_ILOCK_EXCL);
xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE);
ASSERT(!(iattr->ia_valid & (ATTR_UID | ATTR_GID)));
setattr_copy(&nop_mnt_idmap, inode, iattr);
now = inode_set_ctime_current(inode);
inode_set_atime_to_ts(inode, now);
inode_set_mtime_to_ts(inode, now);
if (update_isize) {
i_size_write(inode, iattr->ia_size);
ip->i_disk_size = iattr->ia_size;
i_size_write(inode, new_size);
ip->i_disk_size = new_size;
}
xfs_trans_set_sync(tp);
@@ -335,3 +348,10 @@ out_drop_iolock:
xfs_iunlock(ip, XFS_IOLOCK_EXCL);
return error;
}
const struct exportfs_block_ops xfs_export_block_ops = {
.layouts_supported = xfs_fs_layouts_supported,
.get_uuid = xfs_fs_get_uuid,
.map_blocks = xfs_fs_map_blocks,
.commit_blocks = xfs_fs_commit_blocks,
};
+5 -6
View File
@@ -2,13 +2,9 @@
#ifndef _XFS_PNFS_H
#define _XFS_PNFS_H 1
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
int xfs_fs_get_uuid(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
int xfs_fs_map_blocks(struct inode *inode, loff_t offset, u64 length,
struct iomap *iomap, bool write, u32 *device_generation);
int xfs_fs_commit_blocks(struct inode *inode, struct iomap *maps, int nr_maps,
struct iattr *iattr);
#include <linux/exportfs_block.h>
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
int xfs_break_leased_layouts(struct inode *inode, uint *iolock,
bool *did_unlock);
#else
@@ -18,4 +14,7 @@ xfs_break_leased_layouts(struct inode *inode, uint *iolock, bool *did_unlock)
return 0;
}
#endif /* CONFIG_EXPORTFS_BLOCK_OPS */
extern const struct exportfs_block_ops xfs_export_block_ops;
#endif /* _XFS_PNFS_H */
+1 -1
View File
@@ -1158,7 +1158,7 @@ DECLARE_EVENT_CLASS(xfs_iref_class,
TP_fast_assign(
__entry->dev = VFS_I(ip)->i_sb->s_dev;
__entry->ino = ip->i_ino;
__entry->count = icount_read(VFS_I(ip));
__entry->count = icount_read_once(VFS_I(ip));
__entry->pincount = atomic_read(&ip->i_pincount);
__entry->iflags = ip->i_flags;
__entry->caller_ip = caller_ip;
+8 -17
View File
@@ -6,9 +6,8 @@
#include <linux/path.h>
struct dentry;
struct iattr;
struct exportfs_block_ops;
struct inode;
struct iomap;
struct super_block;
struct vfsmount;
@@ -260,19 +259,13 @@ struct handle_to_path_ctx {
* @commit_metadata:
* @commit_metadata should commit metadata changes to stable storage.
*
* @get_uuid:
* Get a filesystem unique signature exposed to clients.
*
* @map_blocks:
* Map and, if necessary, allocate blocks for a layout.
*
* @commit_blocks:
* Commit blocks in a layout once the client is done with them.
*
* @flags:
* Allows the filesystem to communicate to nfsd that it may want to do things
* differently when dealing with it.
*
* @block_ops:
* Operations for layout grants to block on the underlying device.
*
* Locking rules:
* get_parent is called with child->d_inode->i_rwsem down
* get_name is not (which is possibly inconsistent)
@@ -290,12 +283,6 @@ struct export_operations {
struct dentry * (*get_parent)(struct dentry *child);
int (*commit_metadata)(struct inode *inode);
int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
int (*map_blocks)(struct inode *inode, loff_t offset,
u64 len, struct iomap *iomap,
bool write, u32 *device_generation);
int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
int nr_iomaps, struct iattr *iattr);
int (*permission)(struct handle_to_path_ctx *ctx, unsigned int oflags);
struct file * (*open)(const struct path *path, unsigned int oflags);
#define EXPORT_OP_NOWCC (0x1) /* don't collect v3 wcc data */
@@ -308,6 +295,10 @@ struct export_operations {
#define EXPORT_OP_FLUSH_ON_CLOSE (0x20) /* fs flushes file data on close */
#define EXPORT_OP_NOLOCKS (0x40) /* no file locking support */
unsigned long flags;
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
const struct exportfs_block_ops *block_ops;
#endif
};
/**
+88
View File
@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2014-2026 Christoph Hellwig.
*
* Support for exportfs-based layout grants for direct block device access.
*/
#ifndef LINUX_EXPORTFS_BLOCK_H
#define LINUX_EXPORTFS_BLOCK_H 1
#include <linux/blkdev.h>
#include <linux/exportfs.h>
#include <linux/fs.h>
struct inode;
struct iomap;
struct super_block;
/*
* There are the two types of block-style layout support:
* - In-band implies a device identified by a unique cookie inside the actual
* device address space checked by the ->get_uuid method as used by the pNFS
* block layout. This is a bit dangerous and deprecated.
* - Out of band implies identification by out of band unique identifiers
* specified by the storage protocol, which is much safer and used by the
* pNFS SCSI/NVMe layouts.
*/
typedef unsigned int __bitwise expfs_block_layouts_t;
#define EXPFS_BLOCK_FLAG(__bit) \
((__force expfs_block_layouts_t)(1u << __bit))
#define EXPFS_BLOCK_IN_BAND_ID EXPFS_BLOCK_FLAG(0)
#define EXPFS_BLOCK_OUT_OF_BAND_ID EXPFS_BLOCK_FLAG(1)
struct exportfs_block_ops {
/*
* Returns the EXPFS_BLOCK_* bitmap of supported layout types.
*/
expfs_block_layouts_t (*layouts_supported)(struct super_block *sb);
/*
* Get the in-band device unique signature exposed to clients.
*/
int (*get_uuid)(struct super_block *sb, u8 *buf, u32 *len, u64 *offset);
/*
* Map blocks for direct block access.
* If @write is %true, also allocate the blocks for the range if needed.
*/
int (*map_blocks)(struct inode *inode, loff_t offset, u64 len,
struct iomap *iomap, bool write,
u32 *device_generation);
/*
* Commit blocks previously handed out by ->map_blocks and written to by
* the client.
*/
int (*commit_blocks)(struct inode *inode, struct iomap *iomaps,
int nr_iomaps, loff_t new_size);
};
static inline bool
exportfs_bdev_supports_out_of_band_id(struct block_device *bdev)
{
return bdev->bd_disk->fops->pr_ops &&
bdev->bd_disk->fops->get_unique_id;
}
#ifdef CONFIG_EXPORTFS_BLOCK_OPS
static inline expfs_block_layouts_t
exportfs_layouts_supported(struct super_block *sb)
{
const struct exportfs_block_ops *bops = sb->s_export_op->block_ops;
if (!bops ||
!bops->layouts_supported ||
WARN_ON_ONCE(!bops->map_blocks) ||
WARN_ON_ONCE(!bops->commit_blocks))
return 0;
return bops->layouts_supported(sb);
}
#else
static inline expfs_block_layouts_t
exportfs_layouts_supported(struct super_block *sb)
{
return 0;
}
#endif /* CONFIG_EXPORTFS_BLOCK_OPS */
#endif /* LINUX_EXPORTFS_BLOCK_H */
+43 -23
View File
@@ -4,19 +4,22 @@
#include <linux/fs.h>
#define FL_POSIX 1
#define FL_FLOCK 2
#define FL_DELEG 4 /* NFSv4 delegation */
#define FL_ACCESS 8 /* not trying to lock, just looking */
#define FL_EXISTS 16 /* when unlocking, test for existence */
#define FL_LEASE 32 /* lease held on this file */
#define FL_CLOSE 64 /* unlock on close */
#define FL_SLEEP 128 /* A blocking lock */
#define FL_DOWNGRADE_PENDING 256 /* Lease is being downgraded */
#define FL_UNLOCK_PENDING 512 /* Lease is being broken */
#define FL_OFDLCK 1024 /* lock is "owned" by struct file */
#define FL_LAYOUT 2048 /* outstanding pNFS layout */
#define FL_RECLAIM 4096 /* reclaiming from a reboot server */
#define FL_POSIX BIT(0) /* POSIX lock */
#define FL_FLOCK BIT(1) /* BSD lock */
#define FL_DELEG BIT(2) /* NFSv4 delegation */
#define FL_ACCESS BIT(3) /* not trying to lock, just looking */
#define FL_EXISTS BIT(4) /* when unlocking, test for existence */
#define FL_LEASE BIT(5) /* file lease */
#define FL_CLOSE BIT(6) /* unlock on close */
#define FL_SLEEP BIT(7) /* A blocking lock */
#define FL_DOWNGRADE_PENDING BIT(8) /* Lease is being downgraded */
#define FL_UNLOCK_PENDING BIT(9) /* Lease is being broken */
#define FL_OFDLCK BIT(10) /* POSIX lock "owned" by struct file */
#define FL_LAYOUT BIT(11) /* outstanding pNFS layout */
#define FL_RECLAIM BIT(12) /* reclaiming from a reboot server */
#define FL_IGN_DIR_CREATE BIT(13) /* ignore DIR_CREATE events */
#define FL_IGN_DIR_DELETE BIT(14) /* ignore DIR_DELETE events */
#define FL_IGN_DIR_RENAME BIT(15) /* ignore DIR_RENAME events */
#define FL_CLOSE_POSIX (FL_POSIX | FL_CLOSE)
@@ -26,6 +29,15 @@
*/
#define FILE_LOCK_DEFERRED 1
#define LEASE_BREAK_LEASE BIT(0) // break leases and delegations
#define LEASE_BREAK_DELEG BIT(1) // break delegations only
#define LEASE_BREAK_LAYOUT BIT(2) // break layouts only
#define LEASE_BREAK_NONBLOCK BIT(3) // non-blocking break
#define LEASE_BREAK_OPEN_RDONLY BIT(4) // readonly open event
#define LEASE_BREAK_DIR_CREATE BIT(5) // dir deleg create event
#define LEASE_BREAK_DIR_DELETE BIT(6) // dir deleg delete event
#define LEASE_BREAK_DIR_RENAME BIT(7) // dir deleg rename event
struct file_lock;
struct file_lease;
@@ -216,19 +228,13 @@ int locks_lock_inode_wait(struct inode *inode, struct file_lock *fl);
void locks_init_lease(struct file_lease *);
void locks_free_lease(struct file_lease *fl);
struct file_lease *locks_alloc_lease(void);
#define LEASE_BREAK_LEASE BIT(0) // break leases and delegations
#define LEASE_BREAK_DELEG BIT(1) // break delegations only
#define LEASE_BREAK_LAYOUT BIT(2) // break layouts only
#define LEASE_BREAK_NONBLOCK BIT(3) // non-blocking break
#define LEASE_BREAK_OPEN_RDONLY BIT(4) // readonly open event
int __break_lease(struct inode *inode, unsigned int flags);
void lease_get_mtime(struct inode *, struct timespec64 *time);
int generic_setlease(struct file *, int, struct file_lease **, void **priv);
int kernel_setlease(struct file *, int, struct file_lease **, void **);
int vfs_setlease(struct file *, int, struct file_lease **, void **);
int lease_modify(struct file_lease *, int, struct list_head *);
u32 inode_lease_ignore_mask(struct inode *inode);
struct notifier_block;
int lease_register_notifier(struct notifier_block *);
@@ -516,12 +522,26 @@ static inline bool is_delegated(struct delegated_inode *di)
return di->di_inode;
}
static inline int try_break_deleg(struct inode *inode,
/**
* try_break_deleg - do a non-blocking delegation break
* @inode: inode that should have its delegations broken
* @flags: extra LEASE_BREAK_* flags to pass to break_deleg()
* @di: returns pointer to delegated inode (may be NULL)
*
* Break delegations in a non-blocking fashion. If there are
* outstanding delegations and @di is set, then an extra reference
* will be taken on @inode and @di->di_inode will be populated so
* that it may be waited upon.
*
* Returns 0 if there is no need to wait or an error. If -EWOULDBLOCK
* is returned, then @di will be populated (if non-NULL).
*/
static inline int try_break_deleg(struct inode *inode, unsigned int flags,
struct delegated_inode *di)
{
int ret;
ret = break_deleg(inode, LEASE_BREAK_NONBLOCK);
ret = break_deleg(inode, flags | LEASE_BREAK_NONBLOCK);
if (ret == -EWOULDBLOCK && di) {
di->di_inode = inode;
ihold(inode);
@@ -574,7 +594,7 @@ static inline int break_deleg(struct inode *inode, unsigned int flags)
return 0;
}
static inline int try_break_deleg(struct inode *inode,
static inline int try_break_deleg(struct inode *inode, unsigned int flags,
struct delegated_inode *delegated_inode)
{
return 0;
+13
View File
@@ -2218,8 +2218,21 @@ static inline void mark_inode_dirty_sync(struct inode *inode)
__mark_inode_dirty(inode, I_DIRTY_SYNC);
}
/*
* returns the refcount on the inode. it can change arbitrarily.
*/
static inline int icount_read_once(const struct inode *inode)
{
return atomic_read(&inode->i_count);
}
/*
* returns the refcount on the inode. The lock guarantees no 0->1 or 1->0 transitions
* of the count are going to take place, otherwise it changes arbitrarily.
*/
static inline int icount_read(const struct inode *inode)
{
lockdep_assert_held(&inode->i_lock);
return atomic_read(&inode->i_count);
}
+6 -2
View File
@@ -257,6 +257,10 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
__u32 new_dir_mask = FS_MOVED_TO;
__u32 rename_mask = FS_RENAME;
const struct qstr *new_name = &moved->d_name;
struct fsnotify_rename_data rd = {
.moved = moved,
.target = target,
};
if (isdir) {
old_dir_mask |= FS_ISDIR;
@@ -265,12 +269,12 @@ static inline void fsnotify_move(struct inode *old_dir, struct inode *new_dir,
}
/* Event with information about both old and new parent+name */
fsnotify_name(rename_mask, moved, FSNOTIFY_EVENT_DENTRY,
fsnotify_name(rename_mask, &rd, FSNOTIFY_EVENT_RENAME,
old_dir, old_name, 0);
fsnotify_name(old_dir_mask, source, FSNOTIFY_EVENT_INODE,
old_dir, old_name, fs_cookie);
fsnotify_name(new_dir_mask, source, FSNOTIFY_EVENT_INODE,
fsnotify_name(new_dir_mask, &rd, FSNOTIFY_EVENT_RENAME,
new_dir, new_name, fs_cookie);
if (target)
+21
View File
@@ -311,6 +311,7 @@ enum fsnotify_data_type {
FSNOTIFY_EVENT_DENTRY,
FSNOTIFY_EVENT_MNT,
FSNOTIFY_EVENT_ERROR,
FSNOTIFY_EVENT_RENAME,
};
struct fs_error_report {
@@ -335,6 +336,11 @@ struct fsnotify_mnt {
u64 mnt_id;
};
struct fsnotify_rename_data {
struct dentry *moved; /* the dentry that was renamed */
struct inode *target; /* inode overwritten by rename, or NULL */
};
static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
{
switch (data_type) {
@@ -348,6 +354,8 @@ static inline struct inode *fsnotify_data_inode(const void *data, int data_type)
return d_inode(file_range_path(data)->dentry);
case FSNOTIFY_EVENT_ERROR:
return ((struct fs_error_report *)data)->inode;
case FSNOTIFY_EVENT_RENAME:
return d_inode(((const struct fsnotify_rename_data *)data)->moved);
default:
return NULL;
}
@@ -363,6 +371,8 @@ static inline struct dentry *fsnotify_data_dentry(const void *data, int data_typ
return ((const struct path *)data)->dentry;
case FSNOTIFY_EVENT_FILE_RANGE:
return file_range_path(data)->dentry;
case FSNOTIFY_EVENT_RENAME:
return ((struct fsnotify_rename_data *)data)->moved;
default:
return NULL;
}
@@ -395,6 +405,8 @@ static inline struct super_block *fsnotify_data_sb(const void *data,
return file_range_path(data)->dentry->d_sb;
case FSNOTIFY_EVENT_ERROR:
return ((struct fs_error_report *) data)->sb;
case FSNOTIFY_EVENT_RENAME:
return ((const struct fsnotify_rename_data *)data)->moved->d_sb;
default:
return NULL;
}
@@ -430,6 +442,14 @@ static inline struct fs_error_report *fsnotify_data_error_report(
}
}
static inline struct inode *fsnotify_data_rename_target(const void *data,
int data_type)
{
if (data_type == FSNOTIFY_EVENT_RENAME)
return ((const struct fsnotify_rename_data *)data)->target;
return NULL;
}
static inline const struct file_range *fsnotify_data_file_range(
const void *data,
int data_type)
@@ -918,6 +938,7 @@ extern void fsnotify_put_mark(struct fsnotify_mark *mark);
struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark);
extern void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info);
extern bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info);
extern void fsnotify_modify_mark_mask(struct fsnotify_mark *mark, u32 set, u32 clear);
static inline void fsnotify_init_event(struct fsnotify_event *event)
{
+38 -2
View File
@@ -28,7 +28,10 @@
{ FL_DOWNGRADE_PENDING, "FL_DOWNGRADE_PENDING" }, \
{ FL_UNLOCK_PENDING, "FL_UNLOCK_PENDING" }, \
{ FL_OFDLCK, "FL_OFDLCK" }, \
{ FL_RECLAIM, "FL_RECLAIM"})
{ FL_RECLAIM, "FL_RECLAIM" }, \
{ FL_IGN_DIR_CREATE, "FL_IGN_DIR_CREATE" }, \
{ FL_IGN_DIR_DELETE, "FL_IGN_DIR_DELETE" }, \
{ FL_IGN_DIR_RENAME, "FL_IGN_DIR_RENAME" })
#define show_fl_type(val) \
__print_symbolic(val, \
@@ -117,6 +120,39 @@ DEFINE_EVENT(filelock_lock, flock_lock_inode,
TP_PROTO(struct inode *inode, struct file_lock *fl, int ret),
TP_ARGS(inode, fl, ret));
#define show_lease_break_flags(val) \
__print_flags(val, "|", \
{ LEASE_BREAK_LEASE, "LEASE" }, \
{ LEASE_BREAK_DELEG, "DELEG" }, \
{ LEASE_BREAK_LAYOUT, "LAYOUT" }, \
{ LEASE_BREAK_NONBLOCK, "NONBLOCK" }, \
{ LEASE_BREAK_OPEN_RDONLY, "OPEN_RDONLY" }, \
{ LEASE_BREAK_DIR_CREATE, "DIR_CREATE" }, \
{ LEASE_BREAK_DIR_DELETE, "DIR_DELETE" }, \
{ LEASE_BREAK_DIR_RENAME, "DIR_RENAME" })
TRACE_EVENT(break_lease,
TP_PROTO(struct inode *inode, unsigned int flags),
TP_ARGS(inode, flags),
TP_STRUCT__entry(
__field(unsigned long, i_ino)
__field(dev_t, s_dev)
__field(unsigned int, flags)
),
TP_fast_assign(
__entry->s_dev = inode->i_sb->s_dev;
__entry->i_ino = inode->i_ino;
__entry->flags = flags;
),
TP_printk("dev=0x%x:0x%x ino=0x%lx flags=%s",
MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
__entry->i_ino, show_lease_break_flags(__entry->flags))
);
DECLARE_EVENT_CLASS(filelock_lease,
TP_PROTO(struct inode *inode, struct file_lease *fl),
@@ -190,7 +226,7 @@ TRACE_EVENT(generic_add_lease,
__entry->i_ino = inode->i_ino;
__entry->wcount = atomic_read(&inode->i_writecount);
__entry->rcount = atomic_read(&inode->i_readcount);
__entry->icount = icount_read(inode);
__entry->icount = icount_read_once(inode);
__entry->owner = fl->c.flc_owner;
__entry->flags = fl->c.flc_flags;
__entry->type = fl->c.flc_type;
+51
View File
@@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM fsnotify
#if !defined(_TRACE_FSNOTIFY_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_FSNOTIFY_H
#include <linux/tracepoint.h>
#include <trace/misc/fsnotify.h>
TRACE_EVENT(fsnotify,
TP_PROTO(__u32 mask, const void *data, int data_type,
struct inode *dir, const struct qstr *file_name,
struct inode *inode, u32 cookie),
TP_ARGS(mask, data, data_type, dir, file_name, inode, cookie),
TP_STRUCT__entry(
__field(__u32, mask)
__field(unsigned long, dir_ino)
__field(unsigned long, ino)
__field(dev_t, s_dev)
__field(int, data_type)
__field(u32, cookie)
__string(file_name, file_name ? (const char *)file_name->name : "")
),
TP_fast_assign(
__entry->mask = mask;
__entry->dir_ino = dir ? dir->i_ino : 0;
__entry->ino = inode ? inode->i_ino : 0;
__entry->s_dev = dir ? dir->i_sb->s_dev :
inode ? inode->i_sb->s_dev : 0;
__entry->data_type = data_type;
__entry->cookie = cookie;
__assign_str(file_name);
),
TP_printk("dev=%d:%d dir=%lu ino=%lu data_type=%d cookie=0x%x mask=0x%x %s name=%s",
MAJOR(__entry->s_dev), MINOR(__entry->s_dev),
__entry->dir_ino, __entry->ino,
__entry->data_type, __entry->cookie,
__entry->mask, show_fsnotify_mask(__entry->mask),
__get_str(file_name))
);
#endif /* _TRACE_FSNOTIFY_H */
/* This part must be outside protection */
#include <trace/define_trace.h>
+35
View File
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Display helpers for fsnotify events
*/
#include <linux/fsnotify_backend.h>
#define show_fsnotify_mask(mask) \
__print_flags(mask, "|", \
{ FS_ACCESS, "ACCESS" }, \
{ FS_MODIFY, "MODIFY" }, \
{ FS_ATTRIB, "ATTRIB" }, \
{ FS_CLOSE_WRITE, "CLOSE_WRITE" }, \
{ FS_CLOSE_NOWRITE, "CLOSE_NOWRITE" }, \
{ FS_OPEN, "OPEN" }, \
{ FS_MOVED_FROM, "MOVED_FROM" }, \
{ FS_MOVED_TO, "MOVED_TO" }, \
{ FS_CREATE, "CREATE" }, \
{ FS_DELETE, "DELETE" }, \
{ FS_DELETE_SELF, "DELETE_SELF" }, \
{ FS_MOVE_SELF, "MOVE_SELF" }, \
{ FS_OPEN_EXEC, "OPEN_EXEC" }, \
{ FS_UNMOUNT, "UNMOUNT" }, \
{ FS_Q_OVERFLOW, "Q_OVERFLOW" }, \
{ FS_ERROR, "ERROR" }, \
{ FS_OPEN_PERM, "OPEN_PERM" }, \
{ FS_ACCESS_PERM, "ACCESS_PERM" }, \
{ FS_OPEN_EXEC_PERM, "OPEN_EXEC_PERM" }, \
{ FS_PRE_ACCESS, "PRE_ACCESS" }, \
{ FS_MNT_ATTACH, "MNT_ATTACH" }, \
{ FS_MNT_DETACH, "MNT_DETACH" }, \
{ FS_EVENT_ON_CHILD, "EVENT_ON_CHILD" }, \
{ FS_RENAME, "RENAME" }, \
{ FS_DN_MULTISHOT, "DN_MULTISHOT" }, \
{ FS_ISDIR, "ISDIR" })
+1 -1
View File
@@ -1278,7 +1278,7 @@ static void hook_sb_delete(struct super_block *const sb)
struct landlock_object *object;
/* Only handles referenced inodes. */
if (!icount_read(inode))
if (!icount_read_once(inode))
continue;
/*