Linux-Fsdevel Archive on lore.kernel.org
 help / color / Atom feed
* [RFC][PATCH] fanotify: introduce filesystem view mark
@ 2020-11-09 18:00 Amir Goldstein
  2020-11-10  5:07 ` Amir Goldstein
                   ` (2 more replies)
  0 siblings, 3 replies; 38+ messages in thread
From: Amir Goldstein @ 2020-11-09 18:00 UTC (permalink / raw)
  To: Jan Kara; +Cc: linux-fsdevel, Mark Fasheh

A filesystem view is a subtree of a filesystem accessible from a specific
mount point.  When marking an FS view, user expects to get events on all
inodes that are accessible from the marked mount, even if the events
were generated from another mount.

In particular, the events such as FAN_CREATE, FAN_MOVE, FAN_DELETE that
are not delivered to a mount mark can be delivered to an FS view mark.

One example of a filesystem view is btrfs subvolume, which cannot be
marked with a regular filesystem mark.

Another example of a filesystem view is a bind mount, not on the root of
the filesystem, such as the bind mounts used for containers.

A filesystem view mark is composed of a heads sb mark and an sb_view mark.
The filesystem view mark is connected to the head sb mark and the head
sb mark is connected to the sb object. The mask of the head sb mask is
a cumulative mask of all the associated sb_view mark masks.

Filesystem view marks cannot co-exist with a regular filesystem mark on
the same filesystem.

When an event is generated on the head sb mark, fsnotify iterates the
list of associated sb_view marks and filter events that happen outside
of the sb_view mount's root.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
---

Jan,

I started to play with ideas of filtering events by subtree.
My first approach was to do that in userspace.

This becomes quite easy if you are able to create a bind mount on the
subtree of interest - you use a 'mount_fd' from within the bind mount
for open_by_handle_at() of the dirfid and the magic symlink in /proc
resolves that fd to / for dirs outside the bind mount path.

Having done that, it seems pretty silly to go to userspace and back to
kernel for the filtering, so many wasted cycles when we can do the same
filtering much sooner.

The name "fs view" is inspired by Mark's proposal of the same name [1].
We do not have to restrict the "fs view" points to mount points, but it
created conventient semantics and I pretty much like the idea that
FAN_MARK_MOUNT|FAN_MARK_FILESYSTEM gives you this new hybrid thing.

This is just a POC that I sketched with obvious missing pieces, but at
least with a single fs view mark that I tested it works.

Thoughts?

Amir.


[1] https://lore.kernel.org/linux-fsdevel/20180508180436.716-2-mfasheh@suse.de/

 fs/notify/fanotify/fanotify_user.c | 115 ++++++++++++++++++++++++++---
 fs/notify/fdinfo.c                 |   9 +++
 fs/notify/fsnotify.c               |  37 +++++++++-
 fs/notify/fsnotify.h               |   8 ++
 fs/notify/group.c                  |   2 +-
 fs/notify/mark.c                   |  11 ++-
 include/linux/fanotify.h           |   1 +
 include/linux/fsnotify_backend.h   |  44 +++++++++--
 include/uapi/linux/fanotify.h      |   2 +
 9 files changed, 207 insertions(+), 22 deletions(-)

diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c
index 3e01d8f2ab90..1a46898a1bc8 100644
--- a/fs/notify/fanotify/fanotify_user.c
+++ b/fs/notify/fanotify/fanotify_user.c
@@ -760,7 +760,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group,
 
 	removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
 						 umask, &destroy_mark);
-	if (removed & fsnotify_conn_mask(fsn_mark->connector))
+	if (!mask || (removed & fsnotify_conn_mask(fsn_mark->connector)))
 		fsnotify_recalc_mask(fsn_mark->connector);
 	if (destroy_mark)
 		fsnotify_detach_mark(fsn_mark);
@@ -781,6 +781,35 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group,
 				    mask, flags, umask);
 }
 
+static int fanotify_remove_sb_view_mark(struct fsnotify_group *group,
+					struct vfsmount *mnt, __u32 mask,
+					unsigned int flags, __u32 umask)
+{
+	struct fsnotify_mark *sb_mark;
+	int ret;
+
+	/* Find the head sb mark */
+	mutex_lock(&group->mark_mutex);
+	sb_mark = fsnotify_find_mark(&mnt->mnt_sb->s_fsnotify_marks, group);
+	mutex_unlock(&group->mark_mutex);
+	/* Cannot have both sb mark and sb_view marks on the same sb */
+	if (!sb_mark || !(sb_mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD)) {
+		if (sb_mark)
+			fsnotify_put_mark(sb_mark);
+		return -ENOENT;
+	}
+
+	/* Update or destroy the sb_view mark */
+	ret = fanotify_remove_mark(group, &fsnotify_sbv_mark(sb_mark)->sbv_marks,
+				   mask, flags, umask);
+	fsnotify_put_mark(sb_mark);
+	if (ret)
+		return ret;
+
+	/* Remove mask 0 to update or destroy the head sb mark */
+	return fanotify_remove_mark(group, &mnt->mnt_sb->s_fsnotify_marks, 0, flags, umask);
+}
+
 static int fanotify_remove_sb_mark(struct fsnotify_group *group,
 				   struct super_block *sb, __u32 mask,
 				   unsigned int flags, __u32 umask)
@@ -833,6 +862,7 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
 		return ERR_PTR(-ENOMEM);
 
 	fsnotify_init_mark(mark, group);
+	fsnotify_sbv_mark(mark)->mnt = NULL;
 	ret = fsnotify_add_mark_locked(mark, connp, type, 0, fsid);
 	if (ret) {
 		fsnotify_put_mark(mark);
@@ -843,13 +873,19 @@ static struct fsnotify_mark *fanotify_add_new_mark(struct fsnotify_group *group,
 }
 
 
-static int fanotify_add_mark(struct fsnotify_group *group,
+/* Returns fsnotify_mark with elevated refcount */
+static struct fsnotify_mark *__fanotify_add_mark(struct fsnotify_group *group,
 			     fsnotify_connp_t *connp, unsigned int type,
 			     __u32 mask, unsigned int flags,
-			     __kernel_fsid_t *fsid)
+			     __kernel_fsid_t *fsid, struct vfsmount *mnt)
 {
 	struct fsnotify_mark *fsn_mark;
 	__u32 added;
+	unsigned int sb_view_head = 0;
+
+	if (type == FSNOTIFY_OBJ_TYPE_SB &&
+	    (flags & FAN_MARK_FS_VIEW) == FAN_MARK_FS_VIEW)
+		sb_view_head = FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD;
 
 	mutex_lock(&group->mark_mutex);
 	fsn_mark = fsnotify_find_mark(connp, group);
@@ -857,14 +893,38 @@ static int fanotify_add_mark(struct fsnotify_group *group,
 		fsn_mark = fanotify_add_new_mark(group, connp, type, fsid);
 		if (IS_ERR(fsn_mark)) {
 			mutex_unlock(&group->mark_mutex);
-			return PTR_ERR(fsn_mark);
+			return fsn_mark;
 		}
+		if (type == FSNOTIFY_OBJ_TYPE_SB_VIEW)
+			fsnotify_sbv_mark(fsn_mark)->mnt = mnt;
+		else
+			fsn_mark->flags |= sb_view_head;
+
+	} else if ((fsn_mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD) != sb_view_head) {
+		/* Cannot have both sb mark and sb_view marks on the same sb */
+		mutex_unlock(&group->mark_mutex);
+		fsnotify_put_mark(fsn_mark);
+		return ERR_PTR(-EEXIST);
 	}
 	added = fanotify_mark_add_to_mask(fsn_mark, mask, flags);
-	if (added & ~fsnotify_conn_mask(fsn_mark->connector))
+	if (!mask || (added & ~fsnotify_conn_mask(fsn_mark->connector)))
 		fsnotify_recalc_mask(fsn_mark->connector);
 	mutex_unlock(&group->mark_mutex);
 
+	return fsn_mark;
+}
+
+static int fanotify_add_mark(struct fsnotify_group *group,
+			     fsnotify_connp_t *connp, unsigned int type,
+			     __u32 mask, unsigned int flags,
+			     __kernel_fsid_t *fsid, struct vfsmount *mnt)
+{
+	struct fsnotify_mark *fsn_mark;
+
+	fsn_mark = __fanotify_add_mark(group, connp, type, mask, flags, fsid, mnt);
+	if (IS_ERR(fsn_mark))
+		return PTR_ERR(fsn_mark);
+
 	fsnotify_put_mark(fsn_mark);
 	return 0;
 }
@@ -874,7 +934,31 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group,
 				      unsigned int flags, __kernel_fsid_t *fsid)
 {
 	return fanotify_add_mark(group, &real_mount(mnt)->mnt_fsnotify_marks,
-				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid);
+				 FSNOTIFY_OBJ_TYPE_VFSMOUNT, mask, flags, fsid, NULL);
+}
+
+static int fanotify_add_sb_view_mark(struct fsnotify_group *group,
+				     struct vfsmount *mnt, __u32 mask,
+				     unsigned int flags, __kernel_fsid_t *fsid)
+{
+	struct fsnotify_mark *sb_mark;
+	int ret;
+
+	sb_mark = __fanotify_add_mark(group, &mnt->mnt_sb->s_fsnotify_marks,
+				      FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid, mnt);
+	if (IS_ERR(sb_mark))
+		return PTR_ERR(sb_mark);
+
+	/* Add to sb_view mark */
+	ret = fanotify_add_mark(group, &fsnotify_sbv_mark(sb_mark)->sbv_marks,
+				FSNOTIFY_OBJ_TYPE_SB_VIEW, mask, flags, fsid, mnt);
+	fsnotify_put_mark(sb_mark);
+	if (ret)
+		return ret;
+
+	/* Add mask 0 to re-calc the sb object mask after updating the sb view head mark mask */
+	return fanotify_add_mark(group, &mnt->mnt_sb->s_fsnotify_marks, FSNOTIFY_OBJ_TYPE_SB, 0,
+				 flags, fsid, mnt);
 }
 
 static int fanotify_add_sb_mark(struct fsnotify_group *group,
@@ -882,7 +966,7 @@ static int fanotify_add_sb_mark(struct fsnotify_group *group,
 				unsigned int flags, __kernel_fsid_t *fsid)
 {
 	return fanotify_add_mark(group, &sb->s_fsnotify_marks,
-				 FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid);
+				 FSNOTIFY_OBJ_TYPE_SB, mask, flags, fsid, NULL);
 }
 
 static int fanotify_add_inode_mark(struct fsnotify_group *group,
@@ -902,7 +986,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group,
 		return 0;
 
 	return fanotify_add_mark(group, &inode->i_fsnotify_marks,
-				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid);
+				 FSNOTIFY_OBJ_TYPE_INODE, mask, flags, fsid, NULL);
 }
 
 static struct fsnotify_event *fanotify_alloc_overflow_event(void)
@@ -1116,7 +1200,7 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 	struct path path;
 	__kernel_fsid_t __fsid, *fsid = NULL;
 	u32 valid_mask = FANOTIFY_EVENTS | FANOTIFY_EVENT_FLAGS;
-	unsigned int mark_type = flags & FANOTIFY_MARK_TYPE_BITS;
+	unsigned int mark_type = FANOTIFY_MARK_TYPE(flags);
 	bool ignored = flags & FAN_MARK_IGNORED_MASK;
 	unsigned int obj_type, fid_mode;
 	u32 umask = 0;
@@ -1139,6 +1223,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 	case FAN_MARK_MOUNT:
 		obj_type = FSNOTIFY_OBJ_TYPE_VFSMOUNT;
 		break;
+	case FAN_MARK_FS_VIEW:
+		obj_type = FSNOTIFY_OBJ_TYPE_SB_VIEW;
+		break;
 	case FAN_MARK_FILESYSTEM:
 		obj_type = FSNOTIFY_OBJ_TYPE_SB;
 		break;
@@ -1205,6 +1292,8 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 		ret = 0;
 		if (mark_type == FAN_MARK_MOUNT)
 			fsnotify_clear_vfsmount_marks_by_group(group);
+		else if (mark_type == FAN_MARK_FS_VIEW)
+			fsnotify_clear_sb_view_marks_by_group(group);
 		else if (mark_type == FAN_MARK_FILESYSTEM)
 			fsnotify_clear_sb_marks_by_group(group);
 		else
@@ -1256,6 +1345,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 		if (mark_type == FAN_MARK_MOUNT)
 			ret = fanotify_add_vfsmount_mark(group, mnt, mask,
 							 flags, fsid);
+		else if (mark_type == FAN_MARK_FS_VIEW)
+			ret = fanotify_add_sb_view_mark(group, mnt, mask,
+							flags, fsid);
 		else if (mark_type == FAN_MARK_FILESYSTEM)
 			ret = fanotify_add_sb_mark(group, mnt->mnt_sb, mask,
 						   flags, fsid);
@@ -1267,6 +1359,9 @@ static int do_fanotify_mark(int fanotify_fd, unsigned int flags, __u64 mask,
 		if (mark_type == FAN_MARK_MOUNT)
 			ret = fanotify_remove_vfsmount_mark(group, mnt, mask,
 							    flags, umask);
+		else if (mark_type == FAN_MARK_FS_VIEW)
+			ret = fanotify_remove_sb_view_mark(group, mnt, mask,
+							   flags, umask);
 		else if (mark_type == FAN_MARK_FILESYSTEM)
 			ret = fanotify_remove_sb_mark(group, mnt->mnt_sb, mask,
 						      flags, umask);
@@ -1318,7 +1413,7 @@ static int __init fanotify_user_setup(void)
 	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_INIT_FLAGS) != 10);
 	BUILD_BUG_ON(HWEIGHT32(FANOTIFY_MARK_FLAGS) != 9);
 
-	fanotify_mark_cache = KMEM_CACHE(fsnotify_mark,
+	fanotify_mark_cache = KMEM_CACHE(fsnotify_sb_view_mark,
 					 SLAB_PANIC|SLAB_ACCOUNT);
 	fanotify_fid_event_cachep = KMEM_CACHE(fanotify_fid_event,
 					       SLAB_PANIC);
diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c
index f0d6b54be412..4b00575e9bd1 100644
--- a/fs/notify/fdinfo.c
+++ b/fs/notify/fdinfo.c
@@ -115,6 +115,9 @@ 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->type == FSNOTIFY_OBJ_TYPE_SB_VIEW ||
+	    (mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD))
+		mflags |= FAN_MARK_FS_VIEW;
 
 	if (mark->connector->type == FSNOTIFY_OBJ_TYPE_INODE) {
 		inode = igrab(fsnotify_conn_inode(mark->connector));
@@ -131,6 +134,12 @@ static void fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
 
 		seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
 			   mnt->mnt_id, mflags, mark->mask, mark->ignored_mask);
+	} else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB_VIEW) {
+		struct vfsmount *mnt = fsnotify_sbv_mark(mark)->mnt;
+
+		seq_printf(m, "fanotify sdev:%x mnt_id:%x mflags:%x mask:%x ignored_mask:%x\n",
+			   mnt->mnt_sb->s_dev, real_mount(mnt)->mnt_id, mflags, mark->mask,
+			   mark->ignored_mask);
 	} else if (mark->connector->type == FSNOTIFY_OBJ_TYPE_SB) {
 		struct super_block *sb = fsnotify_conn_sb(mark->connector);
 
diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c
index 8d3ad5ef2925..7af2132372fc 100644
--- a/fs/notify/fsnotify.c
+++ b/fs/notify/fsnotify.c
@@ -275,6 +275,9 @@ static int fsnotify_handle_event(struct fsnotify_group *group, __u32 mask,
 	return ops->handle_inode_event(child_mark, mask, inode, NULL, NULL);
 }
 
+static struct fsnotify_mark *fsnotify_first_mark(struct fsnotify_mark_connector **connp);
+static struct fsnotify_mark *fsnotify_next_mark(struct fsnotify_mark *mark);
+
 static int send_to_group(__u32 mask, const void *data, int data_type,
 			 struct inode *dir, const struct qstr *file_name,
 			 u32 cookie, struct fsnotify_iter_info *iter_info)
@@ -305,9 +308,39 @@ static int send_to_group(__u32 mask, const void *data, int data_type,
 		if (!fsnotify_iter_should_report_type(iter_info, type))
 			continue;
 		mark = iter_info->marks[type];
+		if (!mark)
+			continue;
+
 		/* does the object mark tell us to do something? */
-		if (mark) {
-			group = mark->group;
+		group = mark->group;
+		if (type == FSNOTIFY_OBJ_TYPE_SB && mark &&
+		     (mark->flags & FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD)) {
+			const struct path *path = fsnotify_data_path(data, data_type);
+			struct fsnotify_sb_view_mark *sbv_mark = (void *)mark;
+
+			/* TODO: pass FSNOTIFY_EVENT_DENTRY data type for dir modify events */
+			if (!path || !sbv_mark->sbv_marks)
+				continue;
+
+			/*
+			 * Iterate sb_view marks and apply masks if victim is under mnt root.
+			 * We already have rcu read lock and d_ancestor is accurate enough
+			 * for our needs - if any of the ancestors have been moved in or out
+			 * of the mnt root path, we may either send the event or not.
+			 * The important thing is that if ancestry was always under mnt root
+			 * we will send the event.
+			 */
+			for (sbv_mark = (void *)fsnotify_first_mark(&sbv_mark->sbv_marks);
+			     sbv_mark; sbv_mark = (void *)fsnotify_next_mark((void *)sbv_mark)) {
+				if ((!(test_mask & sbv_mark->fsn_mark.mask) &&
+				     !(test_mask & sbv_mark->fsn_mark.ignored_mask)) ||
+				    !d_ancestor(sbv_mark->mnt->mnt_root, path->dentry))
+					continue;
+
+				marks_mask |= sbv_mark->fsn_mark.mask;
+				marks_ignored_mask |= sbv_mark->fsn_mark.ignored_mask;
+			}
+		} else {
 			marks_mask |= mark->mask;
 			marks_ignored_mask |= mark->ignored_mask;
 		}
diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h
index ff2063ec6b0f..5aaabf2bee31 100644
--- a/fs/notify/fsnotify.h
+++ b/fs/notify/fsnotify.h
@@ -21,6 +21,12 @@ static inline struct mount *fsnotify_conn_mount(
 	return container_of(conn->obj, struct mount, mnt_fsnotify_marks);
 }
 
+static inline struct fsnotify_sb_view_mark *fsnotify_conn_sb_view_mark(
+				struct fsnotify_mark_connector *conn)
+{
+	return container_of(conn->obj, struct fsnotify_sb_view_mark, sbv_marks);
+}
+
 static inline struct super_block *fsnotify_conn_sb(
 				struct fsnotify_mark_connector *conn)
 {
@@ -48,11 +54,13 @@ static inline void fsnotify_clear_marks_by_inode(struct inode *inode)
 static inline void fsnotify_clear_marks_by_mount(struct vfsmount *mnt)
 {
 	fsnotify_destroy_marks(&real_mount(mnt)->mnt_fsnotify_marks);
+	/* TODO: clear sb_view marks associated with this mnt */
 }
 /* run the list of all marks associated with sb and destroy them */
 static inline void fsnotify_clear_marks_by_sb(struct super_block *sb)
 {
 	fsnotify_destroy_marks(&sb->s_fsnotify_marks);
+	/* TODO: clear sb_view marks associated with this sb */
 }
 
 /*
diff --git a/fs/notify/group.c b/fs/notify/group.c
index a4a4b1c64d32..a5367b66f33a 100644
--- a/fs/notify/group.c
+++ b/fs/notify/group.c
@@ -58,7 +58,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_MASK);
+	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_ALL_TYPES_MASK, 0);
 
 	/*
 	 * Some marks can still be pinned when waiting for response from
diff --git a/fs/notify/mark.c b/fs/notify/mark.c
index 8387937b9d01..81825de6a6a9 100644
--- a/fs/notify/mark.c
+++ b/fs/notify/mark.c
@@ -103,6 +103,8 @@ static __u32 *fsnotify_conn_mask_p(struct fsnotify_mark_connector *conn)
 		return &fsnotify_conn_inode(conn)->i_fsnotify_mask;
 	else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT)
 		return &fsnotify_conn_mount(conn)->mnt_fsnotify_mask;
+	else if (conn->type == FSNOTIFY_OBJ_TYPE_SB_VIEW)
+		return &fsnotify_conn_sb_view_mark(conn)->fsn_mark.mask;
 	else if (conn->type == FSNOTIFY_OBJ_TYPE_SB)
 		return &fsnotify_conn_sb(conn)->s_fsnotify_mask;
 	return NULL;
@@ -185,6 +187,8 @@ static void *fsnotify_detach_connector_from_object(
 		atomic_long_inc(&inode->i_sb->s_fsnotify_inode_refs);
 	} else if (conn->type == FSNOTIFY_OBJ_TYPE_VFSMOUNT) {
 		fsnotify_conn_mount(conn)->mnt_fsnotify_mask = 0;
+	} else if (conn->type == FSNOTIFY_OBJ_TYPE_SB_VIEW) {
+		fsnotify_conn_sb_view_mark(conn)->fsn_mark.mask = 0;
 	} else if (conn->type == FSNOTIFY_OBJ_TYPE_SB) {
 		fsnotify_conn_sb(conn)->s_fsnotify_mask = 0;
 	}
@@ -720,9 +724,9 @@ struct fsnotify_mark *fsnotify_find_mark(fsnotify_connp_t *connp,
 }
 EXPORT_SYMBOL_GPL(fsnotify_find_mark);
 
-/* Clear any marks in a group with given type mask */
+/* Clear any marks in a group with given type mask or given flags */
 void fsnotify_clear_marks_by_group(struct fsnotify_group *group,
-				   unsigned int type_mask)
+				   unsigned int type_mask, unsigned int flags_mask)
 {
 	struct fsnotify_mark *lmark, *mark;
 	LIST_HEAD(to_free);
@@ -744,7 +748,8 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group,
 	 */
 	mutex_lock_nested(&group->mark_mutex, SINGLE_DEPTH_NESTING);
 	list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) {
-		if ((1U << mark->connector->type) & type_mask)
+		if (((1U << mark->connector->type) & type_mask) ||
+		    (mark->flags & flags_mask))
 			list_move(&mark->g_list, &to_free);
 	}
 	mutex_unlock(&group->mark_mutex);
diff --git a/include/linux/fanotify.h b/include/linux/fanotify.h
index 3e9c56ee651f..6f36bece5518 100644
--- a/include/linux/fanotify.h
+++ b/include/linux/fanotify.h
@@ -27,6 +27,7 @@
 
 #define FANOTIFY_MARK_TYPE_BITS	(FAN_MARK_INODE | FAN_MARK_MOUNT | \
 				 FAN_MARK_FILESYSTEM)
+#define FANOTIFY_MARK_TYPE(flags) ((flags) & FANOTIFY_MARK_TYPE_BITS)
 
 #define FANOTIFY_MARK_FLAGS	(FANOTIFY_MARK_TYPE_BITS | \
 				 FAN_MARK_ADD | \
diff --git a/include/linux/fsnotify_backend.h b/include/linux/fsnotify_backend.h
index f8529a3a2923..441b3d5d9241 100644
--- a/include/linux/fsnotify_backend.h
+++ b/include/linux/fsnotify_backend.h
@@ -279,6 +279,7 @@ enum fsnotify_obj_type {
 	FSNOTIFY_OBJ_TYPE_INODE,
 	FSNOTIFY_OBJ_TYPE_CHILD,
 	FSNOTIFY_OBJ_TYPE_VFSMOUNT,
+	FSNOTIFY_OBJ_TYPE_SB_VIEW,
 	FSNOTIFY_OBJ_TYPE_SB,
 	FSNOTIFY_OBJ_TYPE_COUNT,
 	FSNOTIFY_OBJ_TYPE_DETACHED = FSNOTIFY_OBJ_TYPE_COUNT
@@ -287,6 +288,7 @@ enum fsnotify_obj_type {
 #define FSNOTIFY_OBJ_TYPE_INODE_FL	(1U << FSNOTIFY_OBJ_TYPE_INODE)
 #define FSNOTIFY_OBJ_TYPE_CHILD_FL	(1U << FSNOTIFY_OBJ_TYPE_CHILD)
 #define FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL	(1U << FSNOTIFY_OBJ_TYPE_VFSMOUNT)
+#define FSNOTIFY_OBJ_TYPE_SB_VIEW_FL	(1U << FSNOTIFY_OBJ_TYPE_SB_VIEW)
 #define FSNOTIFY_OBJ_TYPE_SB_FL		(1U << FSNOTIFY_OBJ_TYPE_SB)
 #define FSNOTIFY_OBJ_ALL_TYPES_MASK	((1U << FSNOTIFY_OBJ_TYPE_COUNT) - 1)
 
@@ -403,9 +405,30 @@ struct fsnotify_mark {
 #define FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY	0x01
 #define FSNOTIFY_MARK_FLAG_ALIVE		0x02
 #define FSNOTIFY_MARK_FLAG_ATTACHED		0x04
+#define FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD		0x08
 	unsigned int flags;		/* flags [mark->lock] */
 };
 
+/*
+ * The head sb_view mark is connected to an sb objects and the rest of the
+ * sb_view marks are connected to the head mark.
+ * The mask on the head mark is the cumulative mask of all sb_view marks.
+ */
+struct fsnotify_sb_view_mark {
+	struct fsnotify_mark fsn_mark;
+	union {
+		/* sb_view marks connected to this head sb mark */
+		struct fsnotify_mark_connector __rcu *sbv_marks;
+		/* mntpoint associated with this sb view mark */
+		struct vfsmount *mnt;
+	};
+};
+
+static inline struct fsnotify_sb_view_mark *fsnotify_sbv_mark(struct fsnotify_mark *fsn_mark)
+{
+	return container_of(fsn_mark, struct fsnotify_sb_view_mark, fsn_mark);
+}
+
 #ifdef CONFIG_FSNOTIFY
 
 /* called from the vfs helpers */
@@ -552,22 +575,31 @@ extern void fsnotify_detach_mark(struct fsnotify_mark *mark);
 extern void fsnotify_free_mark(struct fsnotify_mark *mark);
 /* Wait until all marks queued for destruction are destroyed */
 extern void fsnotify_wait_marks_destroyed(void);
-/* run all the marks in a group, and clear all of the marks attached to given object type */
-extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group, unsigned int type);
+/* run all the marks in a group, and clear all of the marks by object type and flags */
+extern void fsnotify_clear_marks_by_group(struct fsnotify_group *group, unsigned int type,
+					  unsigned int flags);
 /* run all the marks in a group, and clear all of the vfsmount marks */
 static inline void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group)
 {
-	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL);
+	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_VFSMOUNT_FL, 0);
 }
 /* run all the marks in a group, and clear all of the inode marks */
 static inline void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group)
 {
-	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_INODE_FL);
+	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_INODE_FL, 0);
+}
+/* run all the marks in a group, and clear all of the sb view marks */
+static inline void fsnotify_clear_sb_view_marks_by_group(struct fsnotify_group *group)
+{
+	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_VIEW_FL, 0);
+	/* Clear all sb marks associated with sb view marks */
+	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_FL,
+				      FSNOTIFY_MARK_FLAG_SB_VIEW_HEAD);
 }
-/* run all the marks in a group, and clear all of the sn marks */
+/* run all the marks in a group, and clear all of the sb marks */
 static inline void fsnotify_clear_sb_marks_by_group(struct fsnotify_group *group)
 {
-	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_FL);
+	fsnotify_clear_marks_by_group(group, FSNOTIFY_OBJ_TYPE_SB_FL, 0);
 }
 extern void fsnotify_get_mark(struct fsnotify_mark *mark);
 extern void fsnotify_put_mark(struct fsnotify_mark *mark);
diff --git a/include/uapi/linux/fanotify.h b/include/uapi/linux/fanotify.h
index fbf9c5c7dd59..c8e43c0ed89b 100644
--- a/include/uapi/linux/fanotify.h
+++ b/include/uapi/linux/fanotify.h
@@ -79,6 +79,8 @@
 #define FAN_MARK_INODE		0x00000000
 #define FAN_MARK_MOUNT		0x00000010
 #define FAN_MARK_FILESYSTEM	0x00000100
+/* FS View is a subtree of a filesystem accessible from a specific mount point */
+#define FAN_MARK_FS_VIEW	(FAN_MARK_FILESYSTEM | FAN_MARK_MOUNT)
 
 /* Deprecated - do not use this in programs and do not add new flags here! */
 #define FAN_ALL_MARK_FLAGS	(FAN_MARK_ADD |\
-- 
2.25.1


^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, back to index

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-09 18:00 [RFC][PATCH] fanotify: introduce filesystem view mark Amir Goldstein
2020-11-10  5:07 ` Amir Goldstein
2020-11-17  7:09 ` [fanotify] a23a7dc576: unixbench.score -3.7% regression kernel test robot
2020-11-24 13:49 ` [RFC][PATCH] fanotify: introduce filesystem view mark Jan Kara
2020-11-24 14:47   ` Amir Goldstein
2020-11-25 11:01     ` Jan Kara
2020-11-25 12:34       ` Amir Goldstein
2020-11-26 11:10         ` Jan Kara
2020-11-26 11:50           ` Amir Goldstein
2020-11-26  3:42       ` Amir Goldstein
2020-11-26 11:17         ` Jan Kara
2021-04-28 18:28           ` Amir Goldstein
2021-05-03 16:53             ` Jan Kara
2021-05-03 18:44               ` Amir Goldstein
2021-05-05 12:28                 ` Jan Kara
2021-05-05 14:24                   ` Christian Brauner
2021-05-05 14:42                     ` Amir Goldstein
2021-05-05 14:56                       ` Christian Brauner
2021-05-10 10:13                     ` Jan Kara
2021-05-10 11:37                       ` Amir Goldstein
2021-05-10 14:21                         ` Jan Kara
2021-05-10 15:08                           ` Amir Goldstein
2021-05-10 15:27                             ` Jan Kara
2021-05-12 13:07                             ` Christian Brauner
2021-05-12 13:34                               ` Jan Kara
2021-05-12 16:15                                 ` Christian Brauner
2021-05-12 15:26                         ` Christian Brauner
2021-05-13 10:55                           ` Jan Kara
2021-05-14 13:56                             ` Christian Brauner
2021-05-15 14:28                               ` Amir Goldstein
2021-05-17  9:09                                 ` Jan Kara
2021-05-17 12:45                                   ` Amir Goldstein
2021-05-17 13:07                                     ` Jan Kara
2021-05-18 10:11                                 ` Christian Brauner
2021-05-18 16:02                                   ` Amir Goldstein
2021-05-19  9:31                                     ` Christian Brauner
2021-05-12 16:11                         ` Christian Brauner
2021-05-05 13:25               ` Christian Brauner

Linux-Fsdevel Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-fsdevel/0 linux-fsdevel/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-fsdevel linux-fsdevel/ https://lore.kernel.org/linux-fsdevel \
		linux-fsdevel@vger.kernel.org
	public-inbox-index linux-fsdevel

Example config snippet for mirrors

Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-fsdevel


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git