Skip to content

Commit

Permalink
Merge tag 'fsnotify_for_v4.18-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:
 "fsnotify cleanups unifying handling of different watch types.

  This is the shortened fsnotify series from Amir with the last five
  patches pulled out. Amir has modified those patches to not change
  struct inode but obviously it's too late for those to go into this
  merge window"

* tag 'fsnotify_for_v4.18-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jack/linux-fs:
  fsnotify: add fsnotify_add_inode_mark() wrappers
  fanotify: generalize fanotify_should_send_event()
  fsnotify: generalize send_to_group()
  fsnotify: generalize iteration of marks by object type
  fsnotify: introduce marks iteration helpers
  fsnotify: remove redundant arguments to handle_event()
  fsnotify: use type id to identify connector object type
  • Loading branch information
torvalds committed Jun 16, 2018
2 parents 644f263 + b249f5b commit dbb2816
Show file tree
Hide file tree
Showing 14 changed files with 230 additions and 147 deletions.
8 changes: 4 additions & 4 deletions fs/notify/dnotify/dnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,11 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark)
*/
static int dnotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct dnotify_mark *dn_mark;
struct dnotify_struct *dn;
struct dnotify_struct **prev;
Expand All @@ -95,7 +94,8 @@ static int dnotify_handle_event(struct fsnotify_group *group,
if (!S_ISDIR(inode->i_mode))
return 0;

BUG_ON(vfsmount_mark);
if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
return 0;

dn_mark = container_of(inode_mark, struct dnotify_mark, fsn_mark);

Expand Down Expand Up @@ -319,7 +319,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg)
dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark);
spin_lock(&fsn_mark->lock);
} else {
error = fsnotify_add_mark_locked(new_fsn_mark, inode, NULL, 0);
error = fsnotify_add_inode_mark_locked(new_fsn_mark, inode, 0);
if (error) {
mutex_unlock(&dnotify_group->mark_mutex);
goto out_err;
Expand Down
46 changes: 22 additions & 24 deletions fs/notify/fanotify/fanotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,17 @@ static int fanotify_get_response(struct fsnotify_group *group,
return ret;
}

static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmnt_mark,
u32 event_mask,
const void *data, int data_type)
static bool fanotify_should_send_event(struct fsnotify_iter_info *iter_info,
u32 event_mask, const void *data,
int data_type)
{
__u32 marks_mask = 0, marks_ignored_mask = 0;
const struct path *path = data;
struct fsnotify_mark *mark;
int type;

pr_debug("%s: inode_mark=%p vfsmnt_mark=%p mask=%x data=%p"
" data_type=%d\n", __func__, inode_mark, vfsmnt_mark,
event_mask, data, data_type);
pr_debug("%s: report_mask=%x mask=%x data=%p data_type=%d\n",
__func__, iter_info->report_mask, event_mask, data, data_type);

/* if we don't have enough info to send an event to userspace say no */
if (data_type != FSNOTIFY_EVENT_PATH)
Expand All @@ -108,20 +108,21 @@ static bool fanotify_should_send_event(struct fsnotify_mark *inode_mark,
!d_can_lookup(path->dentry))
return false;

/*
* if the event is for a child and this inode doesn't care about
* events on the child, don't send it!
*/
if (inode_mark &&
(!(event_mask & FS_EVENT_ON_CHILD) ||
(inode_mark->mask & FS_EVENT_ON_CHILD))) {
marks_mask |= inode_mark->mask;
marks_ignored_mask |= inode_mark->ignored_mask;
}
fsnotify_foreach_obj_type(type) {
if (!fsnotify_iter_should_report_type(iter_info, type))
continue;
mark = iter_info->marks[type];
/*
* if the event is for a child and this inode doesn't care about
* events on the child, don't send it!
*/
if (type == FSNOTIFY_OBJ_TYPE_INODE &&
(event_mask & FS_EVENT_ON_CHILD) &&
!(mark->mask & FS_EVENT_ON_CHILD))
continue;

if (vfsmnt_mark) {
marks_mask |= vfsmnt_mark->mask;
marks_ignored_mask |= vfsmnt_mark->ignored_mask;
marks_mask |= mark->mask;
marks_ignored_mask |= mark->ignored_mask;
}

if (d_is_dir(path->dentry) &&
Expand Down Expand Up @@ -178,8 +179,6 @@ init: __maybe_unused

static int fanotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *fanotify_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
Expand All @@ -199,8 +198,7 @@ static int fanotify_handle_event(struct fsnotify_group *group,
BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM);
BUILD_BUG_ON(FAN_ONDIR != FS_ISDIR);

if (!fanotify_should_send_event(inode_mark, fanotify_mark, mask, data,
data_type))
if (!fanotify_should_send_event(iter_info, mask, data, data_type))
return 0;

pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode,
Expand Down
6 changes: 3 additions & 3 deletions fs/notify/fdinfo.c
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
struct inotify_inode_mark *inode_mark;
struct inode *inode;

if (!(mark->connector->flags & FSNOTIFY_OBJ_TYPE_INODE))
if (mark->connector->type != FSNOTIFY_OBJ_TYPE_INODE)
return;

inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
Expand Down Expand Up @@ -116,7 +116,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
mflags |= FAN_MARK_IGNORED_SURV_MODIFY;

if (mark->connector->flags & FSNOTIFY_OBJ_TYPE_INODE) {
if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) {
inode = igrab(mark->connector->inode);
if (!inode)
return;
Expand All @@ -126,7 +126,7 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
show_mark_fhandle(m, inode);
seq_putc(m, '\n');
iput(inode);
} else if (mark->connector->flags & FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
} else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
struct mount *mnt = real_mount(mark->connector->mnt);

seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
Expand Down
138 changes: 83 additions & 55 deletions fs/notify/fsnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,8 +184,6 @@ int __fsnotify_parent(const struct path *path, struct dentry *dentry, __u32 mask
EXPORT_SYMBOL_GPL(__fsnotify_parent);

static int send_to_group(struct inode *to_tell,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
__u32 mask, const void *data,
int data_is, u32 cookie,
const unsigned char *file_name,
Expand All @@ -195,48 +193,45 @@ static int send_to_group(struct inode *to_tell,
__u32 test_mask = (mask & ~FS_EVENT_ON_CHILD);
__u32 marks_mask = 0;
__u32 marks_ignored_mask = 0;
struct fsnotify_mark *mark;
int type;

if (unlikely(!inode_mark && !vfsmount_mark)) {
BUG();
if (WARN_ON(!iter_info->report_mask))
return 0;
}

/* clear ignored on inode modification */
if (mask & FS_MODIFY) {
if (inode_mark &&
!(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
inode_mark->ignored_mask = 0;
if (vfsmount_mark &&
!(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
vfsmount_mark->ignored_mask = 0;
}

/* does the inode mark tell us to do something? */
if (inode_mark) {
group = inode_mark->group;
marks_mask |= inode_mark->mask;
marks_ignored_mask |= inode_mark->ignored_mask;
fsnotify_foreach_obj_type(type) {
if (!fsnotify_iter_should_report_type(iter_info, type))
continue;
mark = iter_info->marks[type];
if (mark &&
!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY))
mark->ignored_mask = 0;
}
}

/* does the vfsmount_mark tell us to do something? */
if (vfsmount_mark) {
group = vfsmount_mark->group;
marks_mask |= vfsmount_mark->mask;
marks_ignored_mask |= vfsmount_mark->ignored_mask;
fsnotify_foreach_obj_type(type) {
if (!fsnotify_iter_should_report_type(iter_info, type))
continue;
mark = iter_info->marks[type];
/* does the object mark tell us to do something? */
if (mark) {
group = mark->group;
marks_mask |= mark->mask;
marks_ignored_mask |= mark->ignored_mask;
}
}

pr_debug("%s: group=%p to_tell=%p mask=%x inode_mark=%p"
" vfsmount_mark=%p marks_mask=%x marks_ignored_mask=%x"
pr_debug("%s: group=%p to_tell=%p mask=%x marks_mask=%x marks_ignored_mask=%x"
" data=%p data_is=%d cookie=%d\n",
__func__, group, to_tell, mask, inode_mark, vfsmount_mark,
marks_mask, marks_ignored_mask, data,
data_is, cookie);
__func__, group, to_tell, mask, marks_mask, marks_ignored_mask,
data, data_is, cookie);

if (!(test_mask & marks_mask & ~marks_ignored_mask))
return 0;

return group->ops->handle_event(group, to_tell, inode_mark,
vfsmount_mark, mask, data, data_is,
return group->ops->handle_event(group, to_tell, mask, data, data_is,
file_name, cookie, iter_info);
}

Expand All @@ -263,6 +258,57 @@ static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark)
return hlist_entry_safe(node, struct fsnotify_mark, obj_list);
}

/*
* iter_info is a multi head priority queue of marks.
* Pick a subset of marks from queue heads, all with the
* same group and set the report_mask for selected subset.
* Returns the report_mask of the selected subset.
*/
static unsigned int fsnotify_iter_select_report_types(
struct fsnotify_iter_info *iter_info)
{
struct fsnotify_group *max_prio_group = NULL;
struct fsnotify_mark *mark;
int type;

/* Choose max prio group among groups of all queue heads */
fsnotify_foreach_obj_type(type) {
mark = iter_info->marks[type];
if (mark &&
fsnotify_compare_groups(max_prio_group, mark->group) > 0)
max_prio_group = mark->group;
}

if (!max_prio_group)
return 0;

/* Set the report mask for marks from same group as max prio group */
iter_info->report_mask = 0;
fsnotify_foreach_obj_type(type) {
mark = iter_info->marks[type];
if (mark &&
fsnotify_compare_groups(max_prio_group, mark->group) == 0)
fsnotify_iter_set_report_type(iter_info, type);
}

return iter_info->report_mask;
}

/*
* Pop from iter_info multi head queue, the marks that were iterated in the
* current iteration step.
*/
static void fsnotify_iter_next(struct fsnotify_iter_info *iter_info)
{
int type;

fsnotify_foreach_obj_type(type) {
if (fsnotify_iter_should_report_type(iter_info, type))
iter_info->marks[type] =
fsnotify_next_mark(iter_info->marks[type]);
}
}

/*
* This is the main call to fsnotify. The VFS calls into hook specific functions
* in linux/fsnotify.h. Those functions then in turn call here. Here will call
Expand Down Expand Up @@ -307,15 +353,15 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,

if ((mask & FS_MODIFY) ||
(test_mask & to_tell->i_fsnotify_mask)) {
iter_info.inode_mark =
iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
fsnotify_first_mark(&to_tell->i_fsnotify_marks);
}

if (mnt && ((mask & FS_MODIFY) ||
(test_mask & mnt->mnt_fsnotify_mask))) {
iter_info.inode_mark =
iter_info.marks[FSNOTIFY_OBJ_TYPE_INODE] =
fsnotify_first_mark(&to_tell->i_fsnotify_marks);
iter_info.vfsmount_mark =
iter_info.marks[FSNOTIFY_OBJ_TYPE_VFSMOUNT] =
fsnotify_first_mark(&mnt->mnt_fsnotify_marks);
}

Expand All @@ -324,32 +370,14 @@ int fsnotify(struct inode *to_tell, __u32 mask, const void *data, int data_is,
* ignore masks are properly reflected for mount mark notifications.
* That's why this traversal is so complicated...
*/
while (iter_info.inode_mark || iter_info.vfsmount_mark) {
struct fsnotify_mark *inode_mark = iter_info.inode_mark;
struct fsnotify_mark *vfsmount_mark = iter_info.vfsmount_mark;

if (inode_mark && vfsmount_mark) {
int cmp = fsnotify_compare_groups(inode_mark->group,
vfsmount_mark->group);
if (cmp > 0)
inode_mark = NULL;
else if (cmp < 0)
vfsmount_mark = NULL;
}

ret = send_to_group(to_tell, inode_mark, vfsmount_mark, mask,
data, data_is, cookie, file_name,
&iter_info);
while (fsnotify_iter_select_report_types(&iter_info)) {
ret = send_to_group(to_tell, mask, data, data_is, cookie,
file_name, &iter_info);

if (ret && (mask & ALL_FSNOTIFY_PERM_EVENTS))
goto out;

if (inode_mark)
iter_info.inode_mark =
fsnotify_next_mark(iter_info.inode_mark);
if (vfsmount_mark)
iter_info.vfsmount_mark =
fsnotify_next_mark(iter_info.vfsmount_mark);
fsnotify_iter_next(&iter_info);
}
ret = 0;
out:
Expand Down
6 changes: 0 additions & 6 deletions fs/notify/fsnotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,6 @@

#include "../mount.h"

struct fsnotify_iter_info {
struct fsnotify_mark *inode_mark;
struct fsnotify_mark *vfsmount_mark;
int srcu_idx;
};

/* destroy all events sitting in this groups notification queue */
extern void fsnotify_flush_notify(struct fsnotify_group *group);

Expand Down
2 changes: 1 addition & 1 deletion fs/notify/group.c
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ void fsnotify_destroy_group(struct fsnotify_group *group)
fsnotify_group_stop_queueing(group);

/* Clear all marks for this group and queue them for destruction */
fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES);
fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES_MASK);

/*
* Some marks can still be pinned when waiting for response from
Expand Down
2 changes: 0 additions & 2 deletions fs/notify/inotify/inotify.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@ extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark,
struct fsnotify_group *group);
extern int inotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info);
Expand Down
6 changes: 3 additions & 3 deletions fs/notify/inotify/inotify_fsnotify.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,20 +65,20 @@ static int inotify_merge(struct list_head *list,

int inotify_handle_event(struct fsnotify_group *group,
struct inode *inode,
struct fsnotify_mark *inode_mark,
struct fsnotify_mark *vfsmount_mark,
u32 mask, const void *data, int data_type,
const unsigned char *file_name, u32 cookie,
struct fsnotify_iter_info *iter_info)
{
struct fsnotify_mark *inode_mark = fsnotify_iter_inode_mark(iter_info);
struct inotify_inode_mark *i_mark;
struct inotify_event_info *event;
struct fsnotify_event *fsn_event;
int ret;
int len = 0;
int alloc_len = sizeof(struct inotify_event_info);

BUG_ON(vfsmount_mark);
if (WARN_ON(fsnotify_iter_vfsmount_mark(iter_info)))
return 0;

if ((inode_mark->mask & FS_EXCL_UNLINK) &&
(data_type == FSNOTIFY_EVENT_PATH)) {
Expand Down
Loading

0 comments on commit dbb2816

Please sign in to comment.