fsnotify: Move mark list head from object into dedicated structure
[sfrench/cifs-2.6.git] / fs / notify / mark.c
index 44836e539169ac4d531a026b3d4b174999ed45b2..24b6191bd6c607ee9ddb7c2c39af9ad000bcc456 100644 (file)
@@ -83,6 +83,8 @@
 #define FSNOTIFY_REAPER_DELAY  (1)     /* 1 jiffy */
 
 struct srcu_struct fsnotify_mark_srcu;
+struct kmem_cache *fsnotify_mark_connector_cachep;
+
 static DEFINE_SPINLOCK(destroy_lock);
 static LIST_HEAD(destroy_list);
 
@@ -104,12 +106,15 @@ void fsnotify_put_mark(struct fsnotify_mark *mark)
 }
 
 /* Calculate mask of events for a list of marks */
-u32 fsnotify_recalc_mask(struct hlist_head *head)
+u32 fsnotify_recalc_mask(struct fsnotify_mark_connector *conn)
 {
        u32 new_mask = 0;
        struct fsnotify_mark *mark;
 
-       hlist_for_each_entry(mark, head, obj_list)
+       if (!conn)
+               return 0;
+
+       hlist_for_each_entry(mark, &conn->list, obj_list)
                new_mask |= mark->mask;
        return new_mask;
 }
@@ -220,10 +225,14 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark,
        fsnotify_free_mark(mark);
 }
 
-void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
+void fsnotify_destroy_marks(struct fsnotify_mark_connector *conn,
+                           spinlock_t *lock)
 {
        struct fsnotify_mark *mark;
 
+       if (!conn)
+               return;
+
        while (1) {
                /*
                 * We have to be careful since we can race with e.g.
@@ -233,11 +242,12 @@ void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
                 * calling fsnotify_destroy_mark() more than once is fine.
                 */
                spin_lock(lock);
-               if (hlist_empty(head)) {
+               if (hlist_empty(&conn->list)) {
                        spin_unlock(lock);
                        break;
                }
-               mark = hlist_entry(head->first, struct fsnotify_mark, obj_list);
+               mark = hlist_entry(conn->list.first, struct fsnotify_mark,
+                                  obj_list);
                /*
                 * We don't update i_fsnotify_mask / mnt_fsnotify_mask here
                 * since inode / mount is going away anyway. So just remove
@@ -251,6 +261,14 @@ void fsnotify_destroy_marks(struct hlist_head *head, spinlock_t *lock)
        }
 }
 
+void fsnotify_connector_free(struct fsnotify_mark_connector **connp)
+{
+       if (*connp) {
+               kmem_cache_free(fsnotify_mark_connector_cachep, *connp);
+               *connp = NULL;
+       }
+}
+
 void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask)
 {
        assert_spin_locked(&mark->lock);
@@ -304,21 +322,54 @@ int fsnotify_compare_groups(struct fsnotify_group *a, struct fsnotify_group *b)
        return -1;
 }
 
-/* Add mark into proper place in given list of marks */
-int fsnotify_add_mark_list(struct hlist_head *head, struct fsnotify_mark *mark,
-                          int allow_dups)
+static int fsnotify_attach_connector_to_object(
+                                       struct fsnotify_mark_connector **connp)
+{
+       struct fsnotify_mark_connector *conn;
+
+       conn = kmem_cache_alloc(fsnotify_mark_connector_cachep, GFP_ATOMIC);
+       if (!conn)
+               return -ENOMEM;
+       INIT_HLIST_HEAD(&conn->list);
+       /*
+        * Make sure 'conn' initialization is visible. Matches
+        * lockless_dereference() in fsnotify().
+        */
+       smp_wmb();
+       *connp = conn;
+
+       return 0;
+}
+
+/*
+ * Add mark into proper place in given list of marks. These marks may be used
+ * for the fsnotify backend to determine which event types should be delivered
+ * to which group and for which inodes. These marks are ordered according to
+ * priority, highest number first, and then by the group's location in memory.
+ */
+int fsnotify_add_mark_list(struct fsnotify_mark_connector **connp,
+                          struct fsnotify_mark *mark, int allow_dups)
 {
        struct fsnotify_mark *lmark, *last = NULL;
+       struct fsnotify_mark_connector *conn;
        int cmp;
+       int err;
+
+       if (!*connp) {
+               err = fsnotify_attach_connector_to_object(connp);
+               if (err)
+                       return err;
+       }
+       conn = *connp;
 
        /* is mark the first mark? */
-       if (hlist_empty(head)) {
-               hlist_add_head_rcu(&mark->obj_list, head);
+       if (hlist_empty(&conn->list)) {
+               hlist_add_head_rcu(&mark->obj_list, &conn->list);
                return 0;
        }
 
        /* should mark be in the middle of the current list? */
-       hlist_for_each_entry(lmark, head, obj_list) {
+       hlist_for_each_entry(lmark, &conn->list, obj_list) {
                last = lmark;
 
                if ((lmark->group == mark->group) && !allow_dups)
@@ -419,12 +470,15 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group,
  * Given a list of marks, find the mark associated with given group. If found
  * take a reference to that mark and return it, else return NULL.
  */
-struct fsnotify_mark *fsnotify_find_mark(struct hlist_head *head,
+struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_mark_connector *conn,
                                         struct fsnotify_group *group)
 {
        struct fsnotify_mark *mark;
 
-       hlist_for_each_entry(mark, head, obj_list) {
+       if (!conn)
+               return NULL;
+
+       hlist_for_each_entry(mark, &conn->list, obj_list) {
                if (mark->group == group) {
                        fsnotify_get_mark(mark);
                        return mark;