fsnotify: allow marks to not pin inodes in core
authorEric Paris <eparis@redhat.com>
Fri, 18 Dec 2009 02:24:33 +0000 (21:24 -0500)
committerEric Paris <eparis@redhat.com>
Wed, 28 Jul 2010 13:58:59 +0000 (09:58 -0400)
inotify marks must pin inodes in core.  dnotify doesn't technically need to
since they are closed when the directory is closed.  fanotify also need to
pin inodes in core as it works today.  But the next step is to introduce
the concept of 'ignored masks' which is actually a mask of events for an
inode of no interest.  I claim that these should be liberally sent to the
kernel and should not pin the inode in core.  If the inode is brought back
in the listener will get an event it may have thought excluded, but this is
not a serious situation and one any listener should deal with.

This patch lays the ground work for non-pinning inode marks by using lazy
inode pinning.  We do not pin a mark until it has a non-zero mask entry.  If a
listener new sets a mask we never pin the inode.

Signed-off-by: Eric Paris <eparis@redhat.com>
fs/notify/dnotify/dnotify.c
fs/notify/fanotify/fanotify_user.c
fs/notify/fsnotify.h
fs/notify/inode_mark.c
fs/notify/inotify/inotify_user.c
fs/notify/mark.c
include/linux/fsnotify_backend.h

index 69f42df9ba457bcfb33ba6c3fc27560ceee39e28..6624c2ee8786fe9f804e5eb1dfb9c005d065abc9 100644 (file)
@@ -65,7 +65,7 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark)
        new_mask = 0;
        for (dn = dn_mark->dn; dn != NULL; dn = dn->dn_next)
                new_mask |= (dn->dn_mask & ~FS_DN_MULTISHOT);
-       fsn_mark->mask = new_mask;
+       fsnotify_set_mark_mask_locked(fsn_mark, new_mask);
 
        if (old_mask == new_mask)
                return;
index 84155841a0256fb6481d91557c67187e3f6c200a..3320f0c57e31901874afebbdf7826ccf7c3baf1f 100644 (file)
@@ -302,7 +302,7 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u3
 
        spin_lock(&fsn_mark->lock);
        oldmask = fsn_mark->mask;
-       fsn_mark->mask = oldmask & ~mask;
+       fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask));
        spin_unlock(&fsn_mark->lock);
 
        if (!(oldmask & ~mask))
@@ -359,7 +359,7 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mas
 
        spin_lock(&fsn_mark->lock);
        oldmask = fsn_mark->mask;
-       fsn_mark->mask = oldmask | mask;
+       fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask));
        spin_unlock(&fsn_mark->lock);
 
        return mask & ~oldmask;
index 204353c0f663957899d275e290aca242f0c0563d..1be54f6f9e7d7abc0c37b293e214f70e1d2173a9 100644 (file)
@@ -20,6 +20,8 @@ extern __u32 fsnotify_vfsmount_mask;
 /* destroy all events sitting in this groups notification queue */
 extern void fsnotify_flush_notify(struct fsnotify_group *group);
 
+extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark,
+                                               __u32 mask);
 /* add a mark to an inode */
 extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
                                   struct fsnotify_group *group, struct inode *inode,
index c925579ba0116993e0d02b2f5bb1938694479b15..4292f9e23ae85e99376ba32325a787d0a8b84bdd 100644 (file)
@@ -141,7 +141,32 @@ struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group,
 }
 
 /*
- * Attach an initialized mark mark to a given group and inode.
+ * If we are setting a mark mask on an inode mark we should pin the inode
+ * in memory.
+ */
+void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark,
+                                        __u32 mask)
+{
+       struct inode *inode;
+
+       assert_spin_locked(&mark->lock);
+
+       if (mask &&
+           mark->i.inode &&
+           !(mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED)) {
+               mark->flags |= FSNOTIFY_MARK_FLAG_OBJECT_PINNED;
+               inode = igrab(mark->i.inode);
+               /*
+                * we shouldn't be able to get here if the inode wasn't
+                * already safely held in memory.  But bug in case it
+                * ever is wrong.
+                */
+               BUG_ON(!inode);
+       }
+}
+
+/*
+ * Attach an initialized mark to a given group and inode.
  * These marks may be used for the fsnotify backend to determine which
  * event types should be delivered to which group and for which inodes.
  */
@@ -152,10 +177,6 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
        struct fsnotify_mark *lmark = NULL;
        int ret = 0;
 
-       inode = igrab(inode);
-       if (unlikely(!inode))
-               return -EINVAL;
-
        mark->flags = FSNOTIFY_MARK_FLAG_INODE;
 
        assert_spin_locked(&mark->lock);
@@ -175,10 +196,8 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark,
 
        spin_unlock(&inode->i_lock);
 
-       if (lmark) {
+       if (lmark)
                ret = -EEXIST;
-               iput(inode);
-       }
 
        return ret;
 }
index a12315a7553ddcab9cb5555639a0889fa03d0d94..19d274057bfa165406ebe7234dc60de2884362aa 100644 (file)
@@ -575,13 +575,11 @@ static int inotify_update_existing_watch(struct fsnotify_group *group,
        spin_lock(&fsn_mark->lock);
 
        old_mask = fsn_mark->mask;
-       if (add) {
-               fsn_mark->mask |= mask;
-               new_mask = fsn_mark->mask;
-       } else {
-               fsn_mark->mask = mask;
-               new_mask = fsn_mark->mask;
-       }
+       if (add)
+               fsnotify_set_mark_mask_locked(fsn_mark, (fsn_mark->mask | mask));
+       else
+               fsnotify_set_mark_mask_locked(fsn_mark, mask);
+       new_mask = fsn_mark->mask;
 
        spin_unlock(&fsn_mark->lock);
 
index d296ec9ffb2a09154c43e8d2dcf7747fd31ba531..0ebc3fd7089bb641ae5a77ddeac00bbd5aff96af 100644 (file)
@@ -168,7 +168,7 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
         * is just a lazy update (and could be a perf win...)
         */
 
-       if (inode)
+       if (inode && (mark->flags & FSNOTIFY_MARK_FLAG_OBJECT_PINNED))
                iput(inode);
 
        /*
@@ -180,6 +180,17 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark)
                fsnotify_final_destroy_group(group);
 }
 
+void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask)
+{
+       assert_spin_locked(&mark->lock);
+
+       mark->mask = mask;
+
+       if (mark->flags & FSNOTIFY_MARK_FLAG_INODE)
+               fsnotify_set_inode_mark_mask_locked(mark, mask);
+}
+
+
 /*
  * Attach an initialized mark to a given group and fs object.
  * These marks may be used for the fsnotify backend to determine which
@@ -230,6 +241,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark,
        }
 
        spin_unlock(&group->mark_lock);
+
+       /* this will pin the object if appropriate */
+       fsnotify_set_mark_mask_locked(mark, mark->mask);
+
        spin_unlock(&mark->lock);
 
        if (inode)
index 2d2f015fb7002270c5fb85fc871491e28501ff27..489c881ed4ec47a31455d31c85fed1bade40592c 100644 (file)
@@ -267,8 +267,9 @@ struct fsnotify_mark {
                struct fsnotify_vfsmount_mark m;
        };
        struct list_head free_g_list;   /* tmp list used when freeing this mark */
-#define FSNOTIFY_MARK_FLAG_INODE       0x01
-#define FSNOTIFY_MARK_FLAG_VFSMOUNT    0x02
+#define FSNOTIFY_MARK_FLAG_INODE               0x01
+#define FSNOTIFY_MARK_FLAG_VFSMOUNT            0x02
+#define FSNOTIFY_MARK_FLAG_OBJECT_PINNED       0x04
        unsigned int flags;             /* vfsmount or inode mark? */
        void (*free_mark)(struct fsnotify_mark *mark); /* called on final put+free */
 };
@@ -372,6 +373,8 @@ extern struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *gro
 extern struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group, struct vfsmount *mnt);
 /* copy the values from old into new */
 extern void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old);
+/* set the mask of a mark (might pin the object into memory */
+extern void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask);
 /* attach the mark to both the group and the inode */
 extern int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group,
                             struct inode *inode, struct vfsmount *mnt, int allow_dups);