diff options
author | Jan Kara <jack@suse.cz> | 2014-01-21 18:48:14 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-21 19:19:41 -0500 |
commit | 7053aee26a3548ebaba046ae2e52396ccf56ac6c (patch) | |
tree | 1d21fa9409fede7b908ac08df2984766120448db /fs/notify/fanotify/fanotify_user.c | |
parent | e9fe69045bd648d75d8d8099b8658a4ee005a8e5 (diff) |
fsnotify: do not share events between notification groups
Currently fsnotify framework creates one event structure for each
notification event and links this event into all interested notification
groups. This is done so that we save memory when several notification
groups are interested in the event. However the need for event
structure shared between inotify & fanotify bloats the event structure
so the result is often higher memory consumption.
Another problem is that fsnotify framework keeps path references with
outstanding events so that fanotify can return open file descriptors
with its events. This has the undesirable effect that filesystem cannot
be unmounted while there are outstanding events - a regression for
inotify compared to a situation before it was converted to fsnotify
framework. For fanotify this problem is hard to avoid and users of
fanotify should kind of expect this behavior when they ask for file
descriptors from notified files.
This patch changes fsnotify and its users to create separate event
structure for each group. This allows for much simpler code (~400 lines
removed by this patch) and also smaller event structures. For example
on 64-bit system original struct fsnotify_event consumes 120 bytes, plus
additional space for file name, additional 24 bytes for second and each
subsequent group linking the event, and additional 32 bytes for each
inotify group for private data. After the conversion inotify event
consumes 48 bytes plus space for file name which is considerably less
memory unless file names are long and there are several groups
interested in the events (both of which are uncommon). Fanotify event
fits in 56 bytes after the conversion (fanotify doesn't care about file
names so its events don't have to have it allocated). A win unless
there are four or more fanotify groups interested in the event.
The conversion also solves the problem with unmount when only inotify is
used as we don't have to grab path references for inotify events.
[hughd@google.com: fanotify: fix corruption preventing startup]
Signed-off-by: Jan Kara <jack@suse.cz>
Reviewed-by: Christoph Hellwig <hch@lst.de>
Cc: Eric Paris <eparis@parisplace.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Hugh Dickins <hughd@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'fs/notify/fanotify/fanotify_user.c')
-rw-r--r-- | fs/notify/fanotify/fanotify_user.c | 41 |
1 files changed, 20 insertions, 21 deletions
diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index e44cb6427df3..57d7c083cb4b 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c | |||
@@ -19,6 +19,7 @@ | |||
19 | 19 | ||
20 | #include "../../mount.h" | 20 | #include "../../mount.h" |
21 | #include "../fdinfo.h" | 21 | #include "../fdinfo.h" |
22 | #include "fanotify.h" | ||
22 | 23 | ||
23 | #define FANOTIFY_DEFAULT_MAX_EVENTS 16384 | 24 | #define FANOTIFY_DEFAULT_MAX_EVENTS 16384 |
24 | #define FANOTIFY_DEFAULT_MAX_MARKS 8192 | 25 | #define FANOTIFY_DEFAULT_MAX_MARKS 8192 |
@@ -28,11 +29,12 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; | |||
28 | 29 | ||
29 | static struct kmem_cache *fanotify_mark_cache __read_mostly; | 30 | static struct kmem_cache *fanotify_mark_cache __read_mostly; |
30 | static struct kmem_cache *fanotify_response_event_cache __read_mostly; | 31 | static struct kmem_cache *fanotify_response_event_cache __read_mostly; |
32 | struct kmem_cache *fanotify_event_cachep __read_mostly; | ||
31 | 33 | ||
32 | struct fanotify_response_event { | 34 | struct fanotify_response_event { |
33 | struct list_head list; | 35 | struct list_head list; |
34 | __s32 fd; | 36 | __s32 fd; |
35 | struct fsnotify_event *event; | 37 | struct fanotify_event_info *event; |
36 | }; | 38 | }; |
37 | 39 | ||
38 | /* | 40 | /* |
@@ -61,8 +63,8 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, | |||
61 | } | 63 | } |
62 | 64 | ||
63 | static int create_fd(struct fsnotify_group *group, | 65 | static int create_fd(struct fsnotify_group *group, |
64 | struct fsnotify_event *event, | 66 | struct fanotify_event_info *event, |
65 | struct file **file) | 67 | struct file **file) |
66 | { | 68 | { |
67 | int client_fd; | 69 | int client_fd; |
68 | struct file *new_file; | 70 | struct file *new_file; |
@@ -73,12 +75,6 @@ static int create_fd(struct fsnotify_group *group, | |||
73 | if (client_fd < 0) | 75 | if (client_fd < 0) |
74 | return client_fd; | 76 | return client_fd; |
75 | 77 | ||
76 | if (event->data_type != FSNOTIFY_EVENT_PATH) { | ||
77 | WARN_ON(1); | ||
78 | put_unused_fd(client_fd); | ||
79 | return -EINVAL; | ||
80 | } | ||
81 | |||
82 | /* | 78 | /* |
83 | * we need a new file handle for the userspace program so it can read even if it was | 79 | * we need a new file handle for the userspace program so it can read even if it was |
84 | * originally opened O_WRONLY. | 80 | * originally opened O_WRONLY. |
@@ -109,23 +105,25 @@ static int create_fd(struct fsnotify_group *group, | |||
109 | } | 105 | } |
110 | 106 | ||
111 | static int fill_event_metadata(struct fsnotify_group *group, | 107 | static int fill_event_metadata(struct fsnotify_group *group, |
112 | struct fanotify_event_metadata *metadata, | 108 | struct fanotify_event_metadata *metadata, |
113 | struct fsnotify_event *event, | 109 | struct fsnotify_event *fsn_event, |
114 | struct file **file) | 110 | struct file **file) |
115 | { | 111 | { |
116 | int ret = 0; | 112 | int ret = 0; |
113 | struct fanotify_event_info *event; | ||
117 | 114 | ||
118 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, | 115 | pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, |
119 | group, metadata, event); | 116 | group, metadata, fsn_event); |
120 | 117 | ||
121 | *file = NULL; | 118 | *file = NULL; |
119 | event = container_of(fsn_event, struct fanotify_event_info, fse); | ||
122 | metadata->event_len = FAN_EVENT_METADATA_LEN; | 120 | metadata->event_len = FAN_EVENT_METADATA_LEN; |
123 | metadata->metadata_len = FAN_EVENT_METADATA_LEN; | 121 | metadata->metadata_len = FAN_EVENT_METADATA_LEN; |
124 | metadata->vers = FANOTIFY_METADATA_VERSION; | 122 | metadata->vers = FANOTIFY_METADATA_VERSION; |
125 | metadata->reserved = 0; | 123 | metadata->reserved = 0; |
126 | metadata->mask = event->mask & FAN_ALL_OUTGOING_EVENTS; | 124 | metadata->mask = fsn_event->mask & FAN_ALL_OUTGOING_EVENTS; |
127 | metadata->pid = pid_vnr(event->tgid); | 125 | metadata->pid = pid_vnr(event->tgid); |
128 | if (unlikely(event->mask & FAN_Q_OVERFLOW)) | 126 | if (unlikely(fsn_event->mask & FAN_Q_OVERFLOW)) |
129 | metadata->fd = FAN_NOFD; | 127 | metadata->fd = FAN_NOFD; |
130 | else { | 128 | else { |
131 | metadata->fd = create_fd(group, event, file); | 129 | metadata->fd = create_fd(group, event, file); |
@@ -209,7 +207,7 @@ static int prepare_for_access_response(struct fsnotify_group *group, | |||
209 | if (!re) | 207 | if (!re) |
210 | return -ENOMEM; | 208 | return -ENOMEM; |
211 | 209 | ||
212 | re->event = event; | 210 | re->event = FANOTIFY_E(event); |
213 | re->fd = fd; | 211 | re->fd = fd; |
214 | 212 | ||
215 | mutex_lock(&group->fanotify_data.access_mutex); | 213 | mutex_lock(&group->fanotify_data.access_mutex); |
@@ -217,7 +215,7 @@ static int prepare_for_access_response(struct fsnotify_group *group, | |||
217 | if (atomic_read(&group->fanotify_data.bypass_perm)) { | 215 | if (atomic_read(&group->fanotify_data.bypass_perm)) { |
218 | mutex_unlock(&group->fanotify_data.access_mutex); | 216 | mutex_unlock(&group->fanotify_data.access_mutex); |
219 | kmem_cache_free(fanotify_response_event_cache, re); | 217 | kmem_cache_free(fanotify_response_event_cache, re); |
220 | event->response = FAN_ALLOW; | 218 | FANOTIFY_E(event)->response = FAN_ALLOW; |
221 | return 0; | 219 | return 0; |
222 | } | 220 | } |
223 | 221 | ||
@@ -273,7 +271,7 @@ out_close_fd: | |||
273 | out: | 271 | out: |
274 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS | 272 | #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS |
275 | if (event->mask & FAN_ALL_PERM_EVENTS) { | 273 | if (event->mask & FAN_ALL_PERM_EVENTS) { |
276 | event->response = FAN_DENY; | 274 | FANOTIFY_E(event)->response = FAN_DENY; |
277 | wake_up(&group->fanotify_data.access_waitq); | 275 | wake_up(&group->fanotify_data.access_waitq); |
278 | } | 276 | } |
279 | #endif | 277 | #endif |
@@ -321,7 +319,7 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, | |||
321 | if (IS_ERR(kevent)) | 319 | if (IS_ERR(kevent)) |
322 | break; | 320 | break; |
323 | ret = copy_event_to_user(group, kevent, buf); | 321 | ret = copy_event_to_user(group, kevent, buf); |
324 | fsnotify_put_event(kevent); | 322 | fsnotify_destroy_event(group, kevent); |
325 | if (ret < 0) | 323 | if (ret < 0) |
326 | break; | 324 | break; |
327 | buf += ret; | 325 | buf += ret; |
@@ -409,7 +407,7 @@ static int fanotify_release(struct inode *ignored, struct file *file) | |||
409 | static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | 407 | static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg) |
410 | { | 408 | { |
411 | struct fsnotify_group *group; | 409 | struct fsnotify_group *group; |
412 | struct fsnotify_event_holder *holder; | 410 | struct fsnotify_event *fsn_event; |
413 | void __user *p; | 411 | void __user *p; |
414 | int ret = -ENOTTY; | 412 | int ret = -ENOTTY; |
415 | size_t send_len = 0; | 413 | size_t send_len = 0; |
@@ -421,7 +419,7 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar | |||
421 | switch (cmd) { | 419 | switch (cmd) { |
422 | case FIONREAD: | 420 | case FIONREAD: |
423 | mutex_lock(&group->notification_mutex); | 421 | mutex_lock(&group->notification_mutex); |
424 | list_for_each_entry(holder, &group->notification_list, event_list) | 422 | list_for_each_entry(fsn_event, &group->notification_list, list) |
425 | send_len += FAN_EVENT_METADATA_LEN; | 423 | send_len += FAN_EVENT_METADATA_LEN; |
426 | mutex_unlock(&group->notification_mutex); | 424 | mutex_unlock(&group->notification_mutex); |
427 | ret = put_user(send_len, (int __user *) p); | 425 | ret = put_user(send_len, (int __user *) p); |
@@ -906,6 +904,7 @@ static int __init fanotify_user_setup(void) | |||
906 | fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); | 904 | fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); |
907 | fanotify_response_event_cache = KMEM_CACHE(fanotify_response_event, | 905 | fanotify_response_event_cache = KMEM_CACHE(fanotify_response_event, |
908 | SLAB_PANIC); | 906 | SLAB_PANIC); |
907 | fanotify_event_cachep = KMEM_CACHE(fanotify_event_info, SLAB_PANIC); | ||
909 | 908 | ||
910 | return 0; | 909 | return 0; |
911 | } | 910 | } |