Skip to content

Commit

Permalink
Merge tag 'fsnotify_for_v5.19-rc1' of git://git.kernel.org/pub/scm/li…
Browse files Browse the repository at this point in the history
…nux/kernel/git/jack/linux-fs

Pull fsnotify updates from Jan Kara:
 "The biggest part of this is support for fsnotify inode marks that
  don't pin inodes in memory but rather get evicted together with the
  inode (they are useful if userspace needs to exclude receipt of events
  from potentially large subtrees using fanotify ignore marks).

  There is also a fix for more consistent handling of events sent to
  parent and a fix of sparse(1) complaints"

* tag 'fsnotify_for_v5.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fanotify: fix incorrect fmode_t casts
  fsnotify: consistent behavior for parent not watching children
  fsnotify: introduce mark type iterator
  fanotify: enable "evictable" inode marks
  fanotify: use fsnotify group lock helpers
  fanotify: implement "evictable" inode marks
  fanotify: factor out helper fanotify_mark_update_flags()
  fanotify: create helper fanotify_mark_user_flags()
  fsnotify: allow adding an inode mark without pinning inode
  dnotify: use fsnotify group lock helpers
  nfsd: use fsnotify group lock helpers
  audit: use fsnotify group lock helpers
  inotify: use fsnotify group lock helpers
  fsnotify: create helpers for group mark_mutex lock
  fsnotify: make allow_dups a property of the group
  fsnotify: pass flags argument to fsnotify_alloc_group()
  fsnotify: fix wrong lockdep annotations
  inotify: move control flags from mask to mark flags
  inotify: show inotify mask flags in proc fdinfo
  • Loading branch information
torvalds committed May 26, 2022
2 parents 8b728ed + dccd855 commit e375780
Show file tree
Hide file tree
Showing 18 changed files with 396 additions and 234 deletions.
14 changes: 8 additions & 6 deletions fs/nfsd/filecache.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,23 +119,24 @@ nfsd_file_mark_find_or_create(struct nfsd_file *nf)
struct inode *inode = nf->nf_inode;

do {
mutex_lock(&nfsd_file_fsnotify_group->mark_mutex);
fsnotify_group_lock(nfsd_file_fsnotify_group);
mark = fsnotify_find_mark(&inode->i_fsnotify_marks,
nfsd_file_fsnotify_group);
nfsd_file_fsnotify_group);
if (mark) {
nfm = nfsd_file_mark_get(container_of(mark,
struct nfsd_file_mark,
nfm_mark));
mutex_unlock(&nfsd_file_fsnotify_group->mark_mutex);
fsnotify_group_unlock(nfsd_file_fsnotify_group);
if (nfm) {
fsnotify_put_mark(mark);
break;
}
/* Avoid soft lockup race with nfsd_file_mark_put() */
fsnotify_destroy_mark(mark, nfsd_file_fsnotify_group);
fsnotify_put_mark(mark);
} else
mutex_unlock(&nfsd_file_fsnotify_group->mark_mutex);
} else {
fsnotify_group_unlock(nfsd_file_fsnotify_group);
}

/* allocate a new nfm */
new = kmem_cache_alloc(nfsd_file_mark_slab, GFP_KERNEL);
Expand Down Expand Up @@ -678,7 +679,8 @@ nfsd_file_cache_init(void)
goto out_shrinker;
}

nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops);
nfsd_file_fsnotify_group = fsnotify_alloc_group(&nfsd_file_fsnotify_ops,
FSNOTIFY_GROUP_NOFS);
if (IS_ERR(nfsd_file_fsnotify_group)) {
pr_err("nfsd: unable to create fsnotify group: %ld\n",
PTR_ERR(nfsd_file_fsnotify_group));
Expand Down
13 changes: 7 additions & 6 deletions fs/notify/dnotify/dnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
return;
dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);

mutex_lock(&dnotify_group->mark_mutex);
fsnotify_group_lock(dnotify_group);

spin_lock(&fsn_mark->lock);
prev = &dn_mark->dn;
Expand All @@ -191,7 +191,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id)
free = true;
}

mutex_unlock(&dnotify_group->mark_mutex);
fsnotify_group_unlock(dnotify_group);

if (free)
fsnotify_free_mark(fsn_mark);
Expand Down Expand Up @@ -324,7 +324,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
new_dn_mark->dn = NULL;

/* this is needed to prevent the fcntl/close race described below */
mutex_lock(&dnotify_group->mark_mutex);
fsnotify_group_lock(dnotify_group);

/* add the new_fsn_mark or find an old one. */
fsn_mark = fsnotify_find_mark(&inode->i_fsnotify_marks, dnotify_group);
Expand All @@ -334,7 +334,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
} else {
error = fsnotify_add_inode_mark_locked(new_fsn_mark, inode, 0);
if (error) {
mutex_unlock(&dnotify_group->mark_mutex);
fsnotify_group_unlock(dnotify_group);
goto out_err;
}
spin_lock(&new_fsn_mark->lock);
Expand Down Expand Up @@ -383,7 +383,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)

if (destroy)
fsnotify_detach_mark(fsn_mark);
mutex_unlock(&dnotify_group->mark_mutex);
fsnotify_group_unlock(dnotify_group);
if (destroy)
fsnotify_free_mark(fsn_mark);
fsnotify_put_mark(fsn_mark);
Expand All @@ -401,7 +401,8 @@ static int __init dnotify_init(void)
SLAB_PANIC|SLAB_ACCOUNT);
dnotify_mark_cache = KMEM_CACHE(dnotify_mark, SLAB_PANIC|SLAB_ACCOUNT);

dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops);
dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops,
FSNOTIFY_GROUP_NOFS);
if (IS_ERR(dnotify_group))
panic("unable to allocate fsnotify group for dnotify\n");
dnotify_sysctl_init();
Expand Down
24 changes: 5 additions & 19 deletions fs/notify/fanotify/fanotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,12 +319,8 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
return 0;
}

fsnotify_foreach_iter_type(type) {
if (!fsnotify_iter_should_report_type(iter_info, type))
continue;
mark = iter_info->marks[type];

/* Apply ignore mask regardless of ISDIR and ON_CHILD flags */
fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
/* Apply ignore mask regardless of mark's ISDIR flag */
marks_ignored_mask |= mark->ignored_mask;

/*
Expand All @@ -334,14 +330,6 @@ static u32 fanotify_group_event_mask(struct fsnotify_group *group,
if (event_mask & FS_ISDIR && !(mark->mask & FS_ISDIR))
continue;

/*
* If the event is on a child and this mark is on a parent not
* watching children, don't send it!
*/
if (type == FSNOTIFY_ITER_TYPE_PARENT &&
!(mark->mask & FS_EVENT_ON_CHILD))
continue;

marks_mask |= mark->mask;

/* Record the mark types of this group that matched the event */
Expand Down Expand Up @@ -849,16 +837,14 @@ static struct fanotify_event *fanotify_alloc_event(
*/
static __kernel_fsid_t fanotify_get_fsid(struct fsnotify_iter_info *iter_info)
{
struct fsnotify_mark *mark;
int type;
__kernel_fsid_t fsid = {};

fsnotify_foreach_iter_type(type) {
fsnotify_foreach_iter_mark_type(iter_info, mark, type) {
struct fsnotify_mark_connector *conn;

if (!fsnotify_iter_should_report_type(iter_info, type))
continue;

conn = READ_ONCE(iter_info->marks[type]->connector);
conn = READ_ONCE(mark->connector);
/* Mark is just getting destroyed or created? */
if (!conn)
continue;
Expand Down
12 changes: 12 additions & 0 deletions fs/notify/fanotify/fanotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -490,3 +490,15 @@ static inline unsigned int fanotify_event_hash_bucket(
{
return event->hash & FANOTIFY_HTABLE_MASK;
}

static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
{
unsigned int mflags = 0;

if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
if (mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)
mflags |= FAN_MARK_EVICTABLE;

return mflags;
}
104 changes: 71 additions & 33 deletions fs/notify/fanotify/fanotify_user.c
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ static int create_fd(struct fsnotify_group *group, struct path *path,
* originally opened O_WRONLY.
*/
new_file = dentry_open(path,
group->fanotify_data.f_flags | FMODE_NONOTIFY,
group->fanotify_data.f_flags | __FMODE_NONOTIFY,
current_cred());
if (IS_ERR(new_file)) {
/*
Expand Down Expand Up @@ -1035,10 +1035,10 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
__u32 removed;
int destroy_mark;

mutex_lock(&group->mark_mutex);
fsnotify_group_lock(group);
fsn_mark = fsnotify_find_mark(connp, group);
if (!fsn_mark) {
mutex_unlock(&group->mark_mutex);
fsnotify_group_unlock(group);
return -ENOENT;
}

Expand All @@ -1048,7 +1048,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
fsnotify_recalc_mask(fsn_mark->connector);
if (destroy_mark)
fsnotify_detach_mark(fsn_mark);
mutex_unlock(&group->mark_mutex);
fsnotify_group_unlock(group);
if (destroy_mark)
fsnotify_free_mark(fsn_mark);

Expand Down Expand Up @@ -1081,47 +1081,63 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
flags, umask);
}

static void fanotify_mark_add_ignored_mask(struct fsnotify_mark *fsn_mark,
__u32 mask, unsigned int flags,
__u32 *removed)
static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
unsigned int fan_flags)
{
fsn_mark->ignored_mask |= mask;
bool want_iref = !(fan_flags & FAN_MARK_EVICTABLE);
bool recalc = false;

/*
* Setting FAN_MARK_IGNORED_SURV_MODIFY for the first time may lead to
* the removal of the FS_MODIFY bit in calculated mask if it was set
* because of an ignored mask that is now going to survive FS_MODIFY.
*/
if ((flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
if ((fan_flags & FAN_MARK_IGNORED_MASK) &&
(fan_flags & FAN_MARK_IGNORED_SURV_MODIFY) &&
!(fsn_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) {
fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY;
if (!(fsn_mark->mask & FS_MODIFY))
*removed = FS_MODIFY;
recalc = true;
}

if (fsn_mark->connector->type != FSNOTIFY_OBJ_TYPE_INODE ||
want_iref == !(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF))
return recalc;

/*
* NO_IREF may be removed from a mark, but not added.
* When removed, fsnotify_recalc_mask() will take the inode ref.
*/
WARN_ON_ONCE(!want_iref);
fsn_mark->flags &= ~FSNOTIFY_MARK_FLAG_NO_IREF;

return true;
}

static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
__u32 mask, unsigned int flags,
__u32 *removed)
static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
__u32 mask, unsigned int fan_flags)
{
__u32 oldmask, newmask;
bool recalc;

spin_lock(&fsn_mark->lock);
oldmask = fsnotify_calc_mask(fsn_mark);
if (!(flags & FAN_MARK_IGNORED_MASK)) {
if (!(fan_flags & FAN_MARK_IGNORED_MASK))
fsn_mark->mask |= mask;
} else {
fanotify_mark_add_ignored_mask(fsn_mark, mask, flags, removed);
}
newmask = fsnotify_calc_mask(fsn_mark);
else
fsn_mark->ignored_mask |= mask;

recalc = fsnotify_calc_mask(fsn_mark) &
~fsnotify_conn_mask(fsn_mark->connector);

recalc |= fanotify_mark_update_flags(fsn_mark, fan_flags);
spin_unlock(&fsn_mark->lock);

return newmask & ~oldmask;
return recalc;
}

static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp,
unsigned int obj_type,
unsigned int fan_flags,
__kernel_fsid_t *fsid)
{
struct ucounts *ucounts = group->fanotify_data.ucounts;
Expand All @@ -1144,6 +1160,9 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
}

fsnotify_init_mark(mark, group);
if (fan_flags & FAN_MARK_EVICTABLE)
mark->flags |= FSNOTIFY_MARK_FLAG_NO_IREF;

ret = fsnotify_add_mark_locked(mark, connp, obj_type, 0, fsid);
if (ret) {
fsnotify_put_mark(mark);
Expand All @@ -1170,39 +1189,49 @@ static int fanotify_group_init_error_pool(struct fsnotify_group *group)

static int fanotify_add_mark(struct fsnotify_group *group,
fsnotify_connp_t *connp, unsigned int obj_type,
__u32 mask, unsigned int flags,
__u32 mask, unsigned int fan_flags,
__kernel_fsid_t *fsid)
{
struct fsnotify_mark *fsn_mark;
__u32 added, removed = 0;
bool recalc;
int ret = 0;

mutex_lock(&group->mark_mutex);
fsnotify_group_lock(group);
fsn_mark = fsnotify_find_mark(connp, group);
if (!fsn_mark) {
fsn_mark = fanotify_add_new_mark(group, connp, obj_type, fsid);
fsn_mark = fanotify_add_new_mark(group, connp, obj_type,
fan_flags, fsid);
if (IS_ERR(fsn_mark)) {
mutex_unlock(&group->mark_mutex);
fsnotify_group_unlock(group);
return PTR_ERR(fsn_mark);
}
}

/*
* Non evictable mark cannot be downgraded to evictable mark.
*/
if (fan_flags & FAN_MARK_EVICTABLE &&
!(fsn_mark->flags & FSNOTIFY_MARK_FLAG_NO_IREF)) {
ret = -EEXIST;
goto out;
}

/*
* Error events are pre-allocated per group, only if strictly
* needed (i.e. FAN_FS_ERROR was requested).
*/
if (!(flags & FAN_MARK_IGNORED_MASK) && (mask & FAN_FS_ERROR)) {
if (!(fan_flags & FAN_MARK_IGNORED_MASK) && (mask & FAN_FS_ERROR)) {
ret = fanotify_group_init_error_pool(group);
if (ret)
goto out;
}

added = fanotify_mark_add_to_mask(fsn_mark, mask, flags, &removed);
if (removed || (added & ~fsnotify_conn_mask(fsn_mark->connector)))
recalc = fanotify_mark_add_to_mask(fsn_mark, mask, fan_flags);
if (recalc)
fsnotify_recalc_mask(fsn_mark->connector);

out:
mutex_unlock(&group->mark_mutex);
fsnotify_group_unlock(group);

fsnotify_put_mark(fsn_mark);
return ret;
Expand Down Expand Up @@ -1348,14 +1377,15 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags)
(!(fid_mode & FAN_REPORT_NAME) || !(fid_mode & FAN_REPORT_FID)))
return -EINVAL;

f_flags = O_RDWR | FMODE_NONOTIFY;
f_flags = O_RDWR | __FMODE_NONOTIFY;
if (flags & FAN_CLOEXEC)
f_flags |= O_CLOEXEC;
if (flags & FAN_NONBLOCK)
f_flags |= O_NONBLOCK;

/* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */
group = fsnotify_alloc_user_group(&fanotify_fsnotify_ops);
group = fsnotify_alloc_group(&fanotify_fsnotify_ops,
FSNOTIFY_GROUP_USER | FSNOTIFY_GROUP_NOFS);
if (IS_ERR(group)) {
return PTR_ERR(group);
}
Expand Down Expand Up @@ -1597,6 +1627,14 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
mark_type != FAN_MARK_FILESYSTEM)
goto fput_and_out;

/*
* Evictable is only relevant for inode marks, because only inode object
* can be evicted on memory pressure.
*/
if (flags & FAN_MARK_EVICTABLE &&
mark_type != FAN_MARK_INODE)
goto fput_and_out;

/*
* Events that do not carry enough information to report
* event->fd require a group that supports reporting fid. Those
Expand Down Expand Up @@ -1762,7 +1800,7 @@ static int __init fanotify_user_setup(void)

BUILD_BUG_ON(FANOTIFY_INIT_FLAGS & FANOTIFY_INTERNAL_GROUP_FLAGS);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 12);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 10);

fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
SLAB_PANIC|SLAB_ACCOUNT);
Expand Down
Loading

0 comments on commit e375780

Please sign in to comment.