fanotify: implement "evictable" inode marks
authorAmir Goldstein <amir73il@gmail.com>
Fri, 22 Apr 2022 12:03:25 +0000 (15:03 +0300)
committerJan Kara <jack@suse.cz>
Mon, 25 Apr 2022 12:43:03 +0000 (14:43 +0200)
When an inode mark is created with flag FAN_MARK_EVICTABLE, it will not
pin the marked inode to inode cache, so when inode is evicted from cache
due to memory pressure, the mark will be lost.

When an inode mark with flag FAN_MARK_EVICATBLE is updated without using
this flag, the marked inode is pinned to inode cache.

When an inode mark is updated with flag FAN_MARK_EVICTABLE but an
existing mark already has the inode pinned, the mark update fails with
error EEXIST.

Evictable inode marks can be used to setup inode marks with ignored mask
to suppress events from uninteresting files or directories in a lazy
manner, upon receiving the first event, without having to iterate all
the uninteresting files or directories before hand.

The evictbale inode mark feature allows performing this lazy marks setup
without exhausting the system memory with pinned inodes.

This change does not enable the feature yet.

Link: https://lore.kernel.org/linux-fsdevel/CAOQ4uxiRDpuS=2uA6+ZUM7yG9vVU-u212tkunBmSnP_u=mkv=Q@mail.gmail.com/
Link: https://lore.kernel.org/r/20220422120327.3459282-15-amir73il@gmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
fs/notify/fanotify/fanotify.h
fs/notify/fanotify/fanotify_user.c
include/uapi/linux/fanotify.h

index 87142bc0131a432636828452b1a72361907b9cff..80e0ec95b11310179c00fa2c0eb31ea9f0006451 100644 (file)
@@ -497,6 +497,8 @@ static inline unsigned int fanotify_mark_user_flags(struct fsnotify_mark *mark)
 
        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;
 }
index 4005ee8e6e2c1f27821e30702e9d7c54257ae5f9..ae36138afead0b938dd95bd7b91f473a03c0b5cc 100644 (file)
@@ -1084,6 +1084,7 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group,
 static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
                                       unsigned int fan_flags)
 {
+       bool want_iref = !(fan_flags & FAN_MARK_EVICTABLE);
        bool recalc = false;
 
        /*
@@ -1099,7 +1100,18 @@ static bool fanotify_mark_update_flags(struct fsnotify_mark *fsn_mark,
                        recalc = true;
        }
 
-       return recalc;
+       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 bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
@@ -1125,6 +1137,7 @@ static bool fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark,
 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;
@@ -1147,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);
@@ -1183,13 +1199,23 @@ static int fanotify_add_mark(struct fsnotify_group *group,
        mutex_lock(&group->mark_mutex);
        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);
                        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).
@@ -1601,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
index e8ac38cc2fd6d8f64dbedac5ecf83cefef3c645e..f1f89132d60e25c25b3b54e88e3c45cb06416bba 100644 (file)
@@ -82,6 +82,7 @@
 #define FAN_MARK_IGNORED_SURV_MODIFY   0x00000040
 #define FAN_MARK_FLUSH         0x00000080
 /* FAN_MARK_FILESYSTEM is      0x00000100 */
+#define FAN_MARK_EVICTABLE     0x00000200
 
 /* These are NOT bitwise flags.  Both bits can be used togther.  */
 #define FAN_MARK_INODE         0x00000000