fanotify: reduce event objectid to 29-bit hash
authorAmir Goldstein <amir73il@gmail.com>
Thu, 4 Mar 2021 10:48:23 +0000 (12:48 +0200)
committerJan Kara <jack@suse.cz>
Tue, 16 Mar 2021 15:14:28 +0000 (16:14 +0100)
objectid is only used by fanotify backend and it is just an optimization
for event merge before comparing all fields in event.

Move the objectid member from common struct fsnotify_event into struct
fanotify_event and reduce it to 29-bit hash to cram it together with the
3-bit event type.

Events of different types are never merged, so the combination of event
type and hash form a 32-bit key for fast compare of events.

This reduces the size of events by one pointer and paves the way for
adding hashed queue support for fanotify.

Link: https://lore.kernel.org/r/20210304104826.3993892-3-amir73il@gmail.com
Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Signed-off-by: Jan Kara <jack@suse.cz>
fs/notify/fanotify/fanotify.c
fs/notify/fanotify/fanotify.h
fs/notify/inotify/inotify_fsnotify.c
fs/notify/inotify/inotify_user.c
include/linux/fsnotify_backend.h

index 1192c99..8a2bb69 100644 (file)
@@ -88,16 +88,12 @@ static bool fanotify_name_event_equal(struct fanotify_name_event *fne1,
        return fanotify_info_equal(info1, info2);
 }
 
-static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
-                                 struct fsnotify_event *new_fsn)
+static bool fanotify_should_merge(struct fanotify_event *old,
+                                 struct fanotify_event *new)
 {
-       struct fanotify_event *old, *new;
+       pr_debug("%s: old=%p new=%p\n", __func__, old, new);
 
-       pr_debug("%s: old=%p new=%p\n", __func__, old_fsn, new_fsn);
-       old = FANOTIFY_E(old_fsn);
-       new = FANOTIFY_E(new_fsn);
-
-       if (old_fsn->objectid != new_fsn->objectid ||
+       if (old->hash != new->hash ||
            old->type != new->type || old->pid != new->pid)
                return false;
 
@@ -133,10 +129,9 @@ static bool fanotify_should_merge(struct fsnotify_event *old_fsn,
 static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
 {
        struct fsnotify_event *test_event;
-       struct fanotify_event *new;
+       struct fanotify_event *old, *new = FANOTIFY_E(event);
 
        pr_debug("%s: list=%p event=%p\n", __func__, list, event);
-       new = FANOTIFY_E(event);
 
        /*
         * Don't merge a permission event with any other event so that we know
@@ -147,8 +142,9 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event)
                return 0;
 
        list_for_each_entry_reverse(test_event, list, list) {
-               if (fanotify_should_merge(test_event, event)) {
-                       FANOTIFY_E(test_event)->mask |= new->mask;
+               old = FANOTIFY_E(test_event);
+               if (fanotify_should_merge(old, new)) {
+                       old->mask |= new->mask;
                        return 1;
                }
        }
@@ -533,6 +529,7 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
        struct mem_cgroup *old_memcg;
        struct inode *child = NULL;
        bool name_event = false;
+       unsigned int hash = 0;
 
        if ((fid_mode & FAN_REPORT_DIR_FID) && dirid) {
                /*
@@ -600,8 +597,10 @@ static struct fanotify_event *fanotify_alloc_event(struct fsnotify_group *group,
         * Use the victim inode instead of the watching inode as the id for
         * event queue, so event reported on parent is merged with event
         * reported on child when both directory and child watches exist.
+        * Hash object id for queue merge.
         */
-       fanotify_init_event(event, (unsigned long)id, mask);
+       hash = hash_ptr(id, FANOTIFY_EVENT_HASH_BITS);
+       fanotify_init_event(event, hash, mask);
        if (FAN_GROUP_FLAG(group, FAN_REPORT_TID))
                event->pid = get_pid(task_pid(current));
        else
index 896c819..d531f0c 100644 (file)
@@ -135,19 +135,29 @@ enum fanotify_event_type {
        FANOTIFY_EVENT_TYPE_PATH,
        FANOTIFY_EVENT_TYPE_PATH_PERM,
        FANOTIFY_EVENT_TYPE_OVERFLOW, /* struct fanotify_event */
+       __FANOTIFY_EVENT_TYPE_NUM
 };
 
+#define FANOTIFY_EVENT_TYPE_BITS \
+       (ilog2(__FANOTIFY_EVENT_TYPE_NUM - 1) + 1)
+#define FANOTIFY_EVENT_HASH_BITS \
+       (32 - FANOTIFY_EVENT_TYPE_BITS)
+
 struct fanotify_event {
        struct fsnotify_event fse;
        u32 mask;
-       enum fanotify_event_type type;
+       struct {
+               unsigned int type : FANOTIFY_EVENT_TYPE_BITS;
+               unsigned int hash : FANOTIFY_EVENT_HASH_BITS;
+       };
        struct pid *pid;
 };
 
 static inline void fanotify_init_event(struct fanotify_event *event,
-                                      unsigned long id, u32 mask)
+                                      unsigned int hash, u32 mask)
 {
-       fsnotify_init_event(&event->fse, id);
+       fsnotify_init_event(&event->fse);
+       event->hash = hash;
        event->mask = mask;
        event->pid = NULL;
 }
index 1901d79..0533bac 100644 (file)
@@ -107,7 +107,7 @@ int inotify_handle_inode_event(struct fsnotify_mark *inode_mark, u32 mask,
                mask &= ~IN_ISDIR;
 
        fsn_event = &event->fse;
-       fsnotify_init_event(fsn_event, 0);
+       fsnotify_init_event(fsn_event);
        event->mask = mask;
        event->wd = i_mark->wd;
        event->sync_cookie = cookie;
index a6c95bd..98f61b3 100644 (file)
@@ -641,7 +641,7 @@ static struct fsnotify_group *inotify_new_group(unsigned int max_events)
                return ERR_PTR(-ENOMEM);
        }
        group->overflow_event = &oevent->fse;
-       fsnotify_init_event(group->overflow_event, 0);
+       fsnotify_init_event(group->overflow_event);
        oevent->mask = FS_Q_OVERFLOW;
        oevent->wd = -1;
        oevent->sync_cookie = 0;
index 7eb979b..fc98f9f 100644 (file)
@@ -167,7 +167,6 @@ struct fsnotify_ops {
  */
 struct fsnotify_event {
        struct list_head list;
-       unsigned long objectid; /* identifier for queue merges */
 };
 
 /*
@@ -582,11 +581,9 @@ extern void fsnotify_put_mark(struct fsnotify_mark *mark);
 extern void fsnotify_finish_user_wait(struct fsnotify_iter_info *iter_info);
 extern bool fsnotify_prepare_user_wait(struct fsnotify_iter_info *iter_info);
 
-static inline void fsnotify_init_event(struct fsnotify_event *event,
-                                      unsigned long objectid)
+static inline void fsnotify_init_event(struct fsnotify_event *event)
 {
        INIT_LIST_HEAD(&event->list);
-       event->objectid = objectid;
 }
 
 #else