From b7ba83715317007962ee318587de92f14e9c3aaa Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:04 -0500 Subject: inotify: simplify the inotify idr handling This patch moves all of the idr editing operations into their own idr functions. It makes it easier to prove locking correctness and to to understand the code flow. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 194 ++++++++++++++++++++++++++++----------- 1 file changed, 139 insertions(+), 55 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index e46ca68..653c507 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -357,6 +357,77 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns return error; } +static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, + int last_wd, + struct inotify_inode_mark_entry *ientry) +{ + int ret; + + do { + if (unlikely(!idr_pre_get(idr, GFP_KERNEL))) + return -ENOMEM; + + spin_lock(idr_lock); + ret = idr_get_new_above(idr, ientry, last_wd + 1, + &ientry->wd); + /* we added the mark to the idr, take a reference */ + if (!ret) + fsnotify_get_mark(&ientry->fsn_entry); + spin_unlock(idr_lock); + } while (ret == -EAGAIN); + + return ret; +} + +static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_group *group, + int wd) +{ + struct idr *idr = &group->inotify_data.idr; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + struct inotify_inode_mark_entry *ientry; + + assert_spin_locked(idr_lock); + + ientry = idr_find(idr, wd); + if (ientry) { + struct fsnotify_mark_entry *fsn_entry = &ientry->fsn_entry; + + fsnotify_get_mark(fsn_entry); + /* One ref for being in the idr, one ref we just took */ + BUG_ON(atomic_read(&fsn_entry->refcnt) < 2); + } + + return ientry; +} + +static struct inotify_inode_mark_entry *inotify_idr_find(struct fsnotify_group *group, + int wd) +{ + struct inotify_inode_mark_entry *ientry; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + + spin_lock(idr_lock); + ientry = inotify_idr_find_locked(group, wd); + spin_unlock(idr_lock); + + return ientry; +} + +static void do_inotify_remove_from_idr(struct fsnotify_group *group, + struct inotify_inode_mark_entry *ientry) +{ + struct idr *idr = &group->inotify_data.idr; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + int wd = ientry->wd; + + assert_spin_locked(idr_lock); + + idr_remove(idr, wd); + + /* removed from the idr, drop that ref */ + fsnotify_put_mark(&ientry->fsn_entry); +} + /* * Remove the mark from the idr (if present) and drop the reference * on the mark because it was in the idr. @@ -364,42 +435,72 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns static void inotify_remove_from_idr(struct fsnotify_group *group, struct inotify_inode_mark_entry *ientry) { - struct idr *idr; - struct fsnotify_mark_entry *entry; - struct inotify_inode_mark_entry *found_ientry; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; + struct inotify_inode_mark_entry *found_ientry = NULL; int wd; - spin_lock(&group->inotify_data.idr_lock); - idr = &group->inotify_data.idr; + spin_lock(idr_lock); wd = ientry->wd; - if (wd == -1) + /* + * does this ientry think it is in the idr? we shouldn't get called + * if it wasn't.... + */ + if (wd == -1) { + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + " ientry->inode=%p\n", __func__, ientry, ientry->wd, + ientry->fsn_entry.group, ientry->fsn_entry.inode); + WARN_ON(1); goto out; + } - entry = idr_find(&group->inotify_data.idr, wd); - if (unlikely(!entry)) + /* Lets look in the idr to see if we find it */ + found_ientry = inotify_idr_find_locked(group, wd); + if (unlikely(!found_ientry)) { + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + " ientry->inode=%p\n", __func__, ientry, ientry->wd, + ientry->fsn_entry.group, ientry->fsn_entry.inode); + WARN_ON(1); goto out; + } - found_ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + /* + * We found an entry in the idr at the right wd, but it's + * not the entry we were told to remove. eparis seriously + * fucked up somewhere. + */ if (unlikely(found_ientry != ientry)) { - /* We found an entry in the idr with the right wd, but it's - * not the entry we were told to remove. eparis seriously - * fucked up somewhere. */ WARN_ON(1); - ientry->wd = -1; + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p " + "entry->inode=%p found_ientry=%p found_ientry->wd=%d " + "found_ientry->group=%p found_ientry->inode=%p\n", + __func__, ientry, ientry->wd, ientry->fsn_entry.group, + ientry->fsn_entry.inode, found_ientry, found_ientry->wd, + found_ientry->fsn_entry.group, + found_ientry->fsn_entry.inode); goto out; } - /* One ref for being in the idr, one ref held by the caller */ - BUG_ON(atomic_read(&entry->refcnt) < 2); - - idr_remove(idr, wd); - ientry->wd = -1; + /* + * One ref for being in the idr + * one ref held by the caller trying to kill us + * one ref grabbed by inotify_idr_find + */ + if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { + printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + " ientry->inode=%p\n", __func__, ientry, ientry->wd, + ientry->fsn_entry.group, ientry->fsn_entry.inode); + /* we can't really recover with bad ref cnting.. */ + BUG(); + } - /* removed from the idr, drop that ref */ - fsnotify_put_mark(entry); + do_inotify_remove_from_idr(group, ientry); out: - spin_unlock(&group->inotify_data.idr_lock); + /* match the ref taken by inotify_idr_find_locked() */ + if (found_ientry) + fsnotify_put_mark(&found_ientry->fsn_entry); + ientry->wd = -1; + spin_unlock(idr_lock); } /* @@ -524,6 +625,8 @@ static int inotify_new_watch(struct fsnotify_group *group, struct inotify_inode_mark_entry *tmp_ientry; __u32 mask; int ret; + struct idr *idr = &group->inotify_data.idr; + spinlock_t *idr_lock = &group->inotify_data.idr_lock; /* don't allow invalid bits: we don't want flags set */ mask = inotify_arg_to_mask(arg); @@ -541,28 +644,11 @@ static int inotify_new_watch(struct fsnotify_group *group, ret = -ENOSPC; if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) goto out_err; -retry: - ret = -ENOMEM; - if (unlikely(!idr_pre_get(&group->inotify_data.idr, GFP_KERNEL))) - goto out_err; - - /* we are putting the mark on the idr, take a reference */ - fsnotify_get_mark(&tmp_ientry->fsn_entry); - - spin_lock(&group->inotify_data.idr_lock); - ret = idr_get_new_above(&group->inotify_data.idr, &tmp_ientry->fsn_entry, - group->inotify_data.last_wd+1, - &tmp_ientry->wd); - spin_unlock(&group->inotify_data.idr_lock); - if (ret) { - /* we didn't get on the idr, drop the idr reference */ - fsnotify_put_mark(&tmp_ientry->fsn_entry); - /* idr was out of memory allocate and try again */ - if (ret == -EAGAIN) - goto retry; + ret = inotify_add_to_idr(idr, idr_lock, group->inotify_data.last_wd, + tmp_ientry); + if (ret) goto out_err; - } /* we are on the idr, now get on the inode */ ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); @@ -726,7 +812,7 @@ fput_and_out: SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) { struct fsnotify_group *group; - struct fsnotify_mark_entry *entry; + struct inotify_inode_mark_entry *ientry; struct file *filp; int ret = 0, fput_needed; @@ -735,25 +821,23 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) return -EBADF; /* verify that this is indeed an inotify instance */ - if (unlikely(filp->f_op != &inotify_fops)) { - ret = -EINVAL; + ret = -EINVAL; + if (unlikely(filp->f_op != &inotify_fops)) goto out; - } group = filp->private_data; - spin_lock(&group->inotify_data.idr_lock); - entry = idr_find(&group->inotify_data.idr, wd); - if (unlikely(!entry)) { - spin_unlock(&group->inotify_data.idr_lock); - ret = -EINVAL; + ret = -EINVAL; + ientry = inotify_idr_find(group, wd); + if (unlikely(!ientry)) goto out; - } - fsnotify_get_mark(entry); - spin_unlock(&group->inotify_data.idr_lock); - fsnotify_destroy_mark_by_entry(entry); - fsnotify_put_mark(entry); + ret = 0; + + fsnotify_destroy_mark_by_entry(&ientry->fsn_entry); + + /* match ref taken by inotify_idr_find */ + fsnotify_put_mark(&ientry->fsn_entry); out: fput_light(filp, fput_needed); -- cgit v1.1 From 9e1c74321d87a8b079f04d89e750b39a43365e1f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:05 -0500 Subject: fsnotify: duplicate fsnotify_mark_entry data between 2 marks Simple copy fsnotify information from one mark to another in preparation for the second mark to replace the first. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0399bcb..a13cf9e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -282,12 +282,20 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou return NULL; } +void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) +{ + assert_spin_locked(&old->lock); + new->inode = old->inode; + new->group = old->group; + new->mask = old->mask; + new->free_mark = old->free_mark; +} + /* * Nothing fancy, just initialize lists and locks and counters. */ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, void (*free_mark)(struct fsnotify_mark_entry *entry)) - { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); -- cgit v1.1 From 40554c3dae83bd892b7fbfaa2ea9de739cbcf065 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:05 -0500 Subject: fsnotify: allow addition of duplicate fsnotify marks This patch allows a task to add a second fsnotify mark to an inode for the same group. This mark will be added to the end of the inode's list and this will never be found by the stand fsnotify_find_mark() function. This is useful if a user wants to add a new mark before removing the old one. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/inode_mark.c | 8 +++++--- fs/notify/inotify/inotify_user.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 7e54e52..85b97fc 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -362,7 +362,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); spin_lock(&entry->lock); } else { - fsnotify_add_mark(new_entry, dnotify_group, inode); + fsnotify_add_mark(new_entry, dnotify_group, inode, 0); spin_lock(&new_entry->lock); entry = new_entry; dnentry = new_dnentry; diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index a13cf9e..7d2962e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -312,9 +312,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, * event types should be delivered to which group and for which inodes. */ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, - struct fsnotify_group *group, struct inode *inode) + struct fsnotify_group *group, struct inode *inode, + int allow_dups) { - struct fsnotify_mark_entry *lentry; + struct fsnotify_mark_entry *lentry = NULL; int ret = 0; inode = igrab(inode); @@ -331,7 +332,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - lentry = fsnotify_find_mark_entry(group, inode); + if (!allow_dups) + lentry = fsnotify_find_mark_entry(group, inode); if (!lentry) { entry->group = group; entry->inode = inode; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 653c507..f22a040 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -651,7 +651,7 @@ static int inotify_new_watch(struct fsnotify_group *group, goto out_err; /* we are on the idr, now get on the inode */ - ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode); + ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode, 0); if (ret) { /* we failed to get on the inode, get off the idr */ inotify_remove_from_idr(group, tmp_ientry); -- cgit v1.1 From b4277d3dd5a7400c1ea7fd4e7d64bda8899f84f5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:06 -0500 Subject: fsnotify: use fsnotify_create_event to allocate the q_overflow event Currently fsnotify defines a static fsnotify event which is sent when a group overflows its allotted queue length. This patch just allocates that event from the event cache rather than defining it statically. There is no known reason that the current implementation is wrong, but this makes sure the event is initialized and created like any other. Signed-off-by: Eric Paris --- fs/notify/notification.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b8bf53b..8481253 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -56,7 +56,7 @@ static struct kmem_cache *fsnotify_event_holder_cachep; * it is needed. It's refcnt is set 1 at kernel init time and will never * get set to 0 so it will never get 'freed' */ -static struct fsnotify_event q_overflow_event; +static struct fsnotify_event *q_overflow_event; static atomic_t fsnotify_sync_cookie = ATOMIC_INIT(0); /** @@ -195,7 +195,7 @@ alloc_holder: mutex_lock(&group->notification_mutex); if (group->q_len >= group->max_events) { - event = &q_overflow_event; + event = q_overflow_event; ret = -EOVERFLOW; /* sorry, no private data on the overflow event */ priv = NULL; @@ -412,8 +412,11 @@ __init int fsnotify_notification_init(void) fsnotify_event_cachep = KMEM_CACHE(fsnotify_event, SLAB_PANIC); fsnotify_event_holder_cachep = KMEM_CACHE(fsnotify_event_holder, SLAB_PANIC); - initialize_event(&q_overflow_event); - q_overflow_event.mask = FS_Q_OVERFLOW; + q_overflow_event = fsnotify_create_event(NULL, FS_Q_OVERFLOW, NULL, + FSNOTIFY_EVENT_NONE, NULL, 0, + GFP_KERNEL); + if (!q_overflow_event) + panic("unable to allocate fsnotify q_overflow_event\n"); return 0; } -- cgit v1.1 From 31ddd3268dcb6c1d70e9930a83be43bf86e4bf17 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:06 -0500 Subject: inotify: use container_of instead of casting inotify_free_mark casts directly from an fsnotify_mark_entry to an inotify_inode_mark_entry. This works, but should use container_of instead for future proofing. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f22a040..a0e40f7 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -550,7 +550,9 @@ skip_send_ignore: /* ding dong the mark is dead */ static void inotify_free_mark(struct fsnotify_mark_entry *entry) { - struct inotify_inode_mark_entry *ientry = (struct inotify_inode_mark_entry *)entry; + struct inotify_inode_mark_entry *ientry; + + ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); kmem_cache_free(inotify_inode_mark_cachep, ientry); } -- cgit v1.1 From f0553af054d31f48a75fddd3ef8beb5c39715019 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:06 -0500 Subject: fsnotify: kzalloc fsnotify groups Use kzalloc for fsnotify_groups so that none of the fields can leak any information accidentally. Signed-off-by: Eric Paris --- fs/notify/group.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index 0e16771..777ca82 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -207,7 +207,7 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, struct fsnotify_group *group, *tgroup; /* very low use, simpler locking if we just always alloc */ - group = kmalloc(sizeof(struct fsnotify_group), GFP_KERNEL); + group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL); if (!group) return ERR_PTR(-ENOMEM); -- cgit v1.1 From 6f3a539e3bd8ed2eafa532443723d56896153a00 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:12:07 -0500 Subject: fsnotify: use kmem_cache_zalloc to simplify event initialization fsnotify event initialization is done entry by entry with almost everything set to either 0 or NULL. Use kmem_cache_zalloc and only initialize things that need non-zero initialization. Also means we don't have to change initialization entries based on the config options. Signed-off-by: Eric Paris --- fs/notify/notification.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 8481253..b34ce7a 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -314,25 +314,14 @@ void fsnotify_flush_notify(struct fsnotify_group *group) static void initialize_event(struct fsnotify_event *event) { - event->holder.event = NULL; INIT_LIST_HEAD(&event->holder.event_list); atomic_set(&event->refcnt, 1); spin_lock_init(&event->lock); - event->path.dentry = NULL; - event->path.mnt = NULL; - event->inode = NULL; event->data_type = FSNOTIFY_EVENT_NONE; INIT_LIST_HEAD(&event->private_data_list); - - event->to_tell = NULL; - - event->file_name = NULL; - event->name_len = 0; - - event->sync_cookie = 0; } /* @@ -353,7 +342,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, { struct fsnotify_event *event; - event = kmem_cache_alloc(fsnotify_event_cachep, gfp); + event = kmem_cache_zalloc(fsnotify_event_cachep, gfp); if (!event) return NULL; -- cgit v1.1 From 7050c48826d5adb2210bddfb6a67aa13bbe984ed Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:27:10 -0500 Subject: inotify: do not reuse watch descriptors Prior to 2.6.31 inotify would not reuse watch descriptors until all of them had been used at least once. After the rewrite inotify would reuse watch descriptors. The selinux utility 'restorecond' was found to have problems when watch descriptors were reused. This patch reverts to the pre inotify rewrite behavior to not reuse watch descriptors. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a0e40f7..ce21eba 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -358,7 +358,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns } static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, - int last_wd, + int *last_wd, struct inotify_inode_mark_entry *ientry) { int ret; @@ -368,11 +368,13 @@ static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, return -ENOMEM; spin_lock(idr_lock); - ret = idr_get_new_above(idr, ientry, last_wd + 1, + ret = idr_get_new_above(idr, ientry, *last_wd + 1, &ientry->wd); /* we added the mark to the idr, take a reference */ - if (!ret) + if (!ret) { fsnotify_get_mark(&ientry->fsn_entry); + *last_wd = ientry->wd; + } spin_unlock(idr_lock); } while (ret == -EAGAIN); @@ -647,7 +649,7 @@ static int inotify_new_watch(struct fsnotify_group *group, if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) goto out_err; - ret = inotify_add_to_idr(idr, idr_lock, group->inotify_data.last_wd, + ret = inotify_add_to_idr(idr, idr_lock, &group->inotify_data.last_wd, tmp_ientry); if (ret) goto out_err; @@ -660,9 +662,6 @@ static int inotify_new_watch(struct fsnotify_group *group, goto out_err; } - /* update the idr hint, who cares about races, it's just a hint */ - group->inotify_data.last_wd = tmp_ientry->wd; - /* increment the number of watches the user has */ atomic_inc(&group->inotify_data.user->inotify_watches); -- cgit v1.1 From 2dfc1cae4c42b93b831b2417540df2b895ab7108 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 20:30:52 -0500 Subject: inotify: remove inotify in kernel interface nothing uses inotify in the kernel, drop it! Signed-off-by: Eric Paris --- fs/inode.c | 6 - fs/notify/inotify/Kconfig | 15 - fs/notify/inotify/Makefile | 1 - fs/notify/inotify/inotify.c | 873 -------------------------------------------- fs/open.c | 1 + 5 files changed, 1 insertion(+), 895 deletions(-) delete mode 100644 fs/notify/inotify/inotify.c (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 722860b..8e1bee9 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include @@ -264,10 +263,6 @@ void inode_init_once(struct inode *inode) INIT_RAW_PRIO_TREE_ROOT(&inode->i_data.i_mmap); INIT_LIST_HEAD(&inode->i_data.i_mmap_nonlinear); i_size_ordered_init(inode); -#ifdef CONFIG_INOTIFY - INIT_LIST_HEAD(&inode->inotify_watches); - mutex_init(&inode->inotify_mutex); -#endif #ifdef CONFIG_FSNOTIFY INIT_HLIST_HEAD(&inode->i_fsnotify_mark_entries); #endif @@ -413,7 +408,6 @@ int invalidate_inodes(struct super_block *sb) down_write(&iprune_sem); spin_lock(&inode_lock); - inotify_unmount_inodes(&sb->s_inodes); fsnotify_unmount_inodes(&sb->s_inodes); busy = invalidate_list(&sb->s_inodes, &throw_away); spin_unlock(&inode_lock); diff --git a/fs/notify/inotify/Kconfig b/fs/notify/inotify/Kconfig index b3a159b..b981fc0 100644 --- a/fs/notify/inotify/Kconfig +++ b/fs/notify/inotify/Kconfig @@ -1,18 +1,3 @@ -config INOTIFY - bool "Inotify file change notification support" - default n - ---help--- - Say Y here to enable legacy in kernel inotify support. Inotify is a - file change notification system. It is a replacement for dnotify. - This option only provides the legacy inotify in kernel API. There - are no in tree kernel users of this interface since it is deprecated. - You only need this if you are loading an out of tree kernel module - that uses inotify. - - For more information, see - - If unsure, say N. - config INOTIFY_USER bool "Inotify support for userspace" select ANON_INODES diff --git a/fs/notify/inotify/Makefile b/fs/notify/inotify/Makefile index 9438281..a380dab 100644 --- a/fs/notify/inotify/Makefile +++ b/fs/notify/inotify/Makefile @@ -1,2 +1 @@ -obj-$(CONFIG_INOTIFY) += inotify.o obj-$(CONFIG_INOTIFY_USER) += inotify_fsnotify.o inotify_user.o diff --git a/fs/notify/inotify/inotify.c b/fs/notify/inotify/inotify.c deleted file mode 100644 index 27b75eb..0000000 --- a/fs/notify/inotify/inotify.c +++ /dev/null @@ -1,873 +0,0 @@ -/* - * fs/inotify.c - inode-based file event notifications - * - * Authors: - * John McCutchan - * Robert Love - * - * Kernel API added by: Amy Griffis - * - * Copyright (C) 2005 John McCutchan - * Copyright 2006 Hewlett-Packard Development Company, L.P. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2, or (at your option) any - * later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static atomic_t inotify_cookie; - -/* - * Lock ordering: - * - * dentry->d_lock (used to keep d_move() away from dentry->d_parent) - * iprune_mutex (synchronize shrink_icache_memory()) - * inode_lock (protects the super_block->s_inodes list) - * inode->inotify_mutex (protects inode->inotify_watches and watches->i_list) - * inotify_handle->mutex (protects inotify_handle and watches->h_list) - * - * The inode->inotify_mutex and inotify_handle->mutex and held during execution - * of a caller's event handler. Thus, the caller must not hold any locks - * taken in their event handler while calling any of the published inotify - * interfaces. - */ - -/* - * Lifetimes of the three main data structures--inotify_handle, inode, and - * inotify_watch--are managed by reference count. - * - * inotify_handle: Lifetime is from inotify_init() to inotify_destroy(). - * Additional references can bump the count via get_inotify_handle() and drop - * the count via put_inotify_handle(). - * - * inotify_watch: for inotify's purposes, lifetime is from inotify_add_watch() - * to remove_watch_no_event(). Additional references can bump the count via - * get_inotify_watch() and drop the count via put_inotify_watch(). The caller - * is reponsible for the final put after receiving IN_IGNORED, or when using - * IN_ONESHOT after receiving the first event. Inotify does the final put if - * inotify_destroy() is called. - * - * inode: Pinned so long as the inode is associated with a watch, from - * inotify_add_watch() to the final put_inotify_watch(). - */ - -/* - * struct inotify_handle - represents an inotify instance - * - * This structure is protected by the mutex 'mutex'. - */ -struct inotify_handle { - struct idr idr; /* idr mapping wd -> watch */ - struct mutex mutex; /* protects this bad boy */ - struct list_head watches; /* list of watches */ - atomic_t count; /* reference count */ - u32 last_wd; /* the last wd allocated */ - const struct inotify_operations *in_ops; /* inotify caller operations */ -}; - -static inline void get_inotify_handle(struct inotify_handle *ih) -{ - atomic_inc(&ih->count); -} - -static inline void put_inotify_handle(struct inotify_handle *ih) -{ - if (atomic_dec_and_test(&ih->count)) { - idr_destroy(&ih->idr); - kfree(ih); - } -} - -/** - * get_inotify_watch - grab a reference to an inotify_watch - * @watch: watch to grab - */ -void get_inotify_watch(struct inotify_watch *watch) -{ - atomic_inc(&watch->count); -} -EXPORT_SYMBOL_GPL(get_inotify_watch); - -int pin_inotify_watch(struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - if (atomic_inc_not_zero(&sb->s_active)) { - atomic_inc(&watch->count); - return 1; - } - return 0; -} - -/** - * put_inotify_watch - decrements the ref count on a given watch. cleans up - * watch references if the count reaches zero. inotify_watch is freed by - * inotify callers via the destroy_watch() op. - * @watch: watch to release - */ -void put_inotify_watch(struct inotify_watch *watch) -{ - if (atomic_dec_and_test(&watch->count)) { - struct inotify_handle *ih = watch->ih; - - iput(watch->inode); - ih->in_ops->destroy_watch(watch); - put_inotify_handle(ih); - } -} -EXPORT_SYMBOL_GPL(put_inotify_watch); - -void unpin_inotify_watch(struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - put_inotify_watch(watch); - deactivate_super(sb); -} - -/* - * inotify_handle_get_wd - returns the next WD for use by the given handle - * - * Callers must hold ih->mutex. This function can sleep. - */ -static int inotify_handle_get_wd(struct inotify_handle *ih, - struct inotify_watch *watch) -{ - int ret; - - do { - if (unlikely(!idr_pre_get(&ih->idr, GFP_NOFS))) - return -ENOSPC; - ret = idr_get_new_above(&ih->idr, watch, ih->last_wd+1, &watch->wd); - } while (ret == -EAGAIN); - - if (likely(!ret)) - ih->last_wd = watch->wd; - - return ret; -} - -/* - * inotify_inode_watched - returns nonzero if there are watches on this inode - * and zero otherwise. We call this lockless, we do not care if we race. - */ -static inline int inotify_inode_watched(struct inode *inode) -{ - return !list_empty(&inode->inotify_watches); -} - -/* - * Get child dentry flag into synch with parent inode. - * Flag should always be clear for negative dentrys. - */ -static void set_dentry_child_flags(struct inode *inode, int watched) -{ - struct dentry *alias; - - spin_lock(&dcache_lock); - list_for_each_entry(alias, &inode->i_dentry, d_alias) { - struct dentry *child; - - list_for_each_entry(child, &alias->d_subdirs, d_u.d_child) { - if (!child->d_inode) - continue; - - spin_lock(&child->d_lock); - if (watched) - child->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; - else - child->d_flags &=~DCACHE_INOTIFY_PARENT_WATCHED; - spin_unlock(&child->d_lock); - } - } - spin_unlock(&dcache_lock); -} - -/* - * inotify_find_handle - find the watch associated with the given inode and - * handle - * - * Callers must hold inode->inotify_mutex. - */ -static struct inotify_watch *inode_find_handle(struct inode *inode, - struct inotify_handle *ih) -{ - struct inotify_watch *watch; - - list_for_each_entry(watch, &inode->inotify_watches, i_list) { - if (watch->ih == ih) - return watch; - } - - return NULL; -} - -/* - * remove_watch_no_event - remove watch without the IN_IGNORED event. - * - * Callers must hold both inode->inotify_mutex and ih->mutex. - */ -static void remove_watch_no_event(struct inotify_watch *watch, - struct inotify_handle *ih) -{ - list_del(&watch->i_list); - list_del(&watch->h_list); - - if (!inotify_inode_watched(watch->inode)) - set_dentry_child_flags(watch->inode, 0); - - idr_remove(&ih->idr, watch->wd); -} - -/** - * inotify_remove_watch_locked - Remove a watch from both the handle and the - * inode. Sends the IN_IGNORED event signifying that the inode is no longer - * watched. May be invoked from a caller's event handler. - * @ih: inotify handle associated with watch - * @watch: watch to remove - * - * Callers must hold both inode->inotify_mutex and ih->mutex. - */ -void inotify_remove_watch_locked(struct inotify_handle *ih, - struct inotify_watch *watch) -{ - remove_watch_no_event(watch, ih); - ih->in_ops->handle_event(watch, watch->wd, IN_IGNORED, 0, NULL, NULL); -} -EXPORT_SYMBOL_GPL(inotify_remove_watch_locked); - -/* Kernel API for producing events */ - -/* - * inotify_d_instantiate - instantiate dcache entry for inode - */ -void inotify_d_instantiate(struct dentry *entry, struct inode *inode) -{ - struct dentry *parent; - - if (!inode) - return; - - spin_lock(&entry->d_lock); - parent = entry->d_parent; - if (parent->d_inode && inotify_inode_watched(parent->d_inode)) - entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; - spin_unlock(&entry->d_lock); -} - -/* - * inotify_d_move - dcache entry has been moved - */ -void inotify_d_move(struct dentry *entry) -{ - struct dentry *parent; - - parent = entry->d_parent; - if (inotify_inode_watched(parent->d_inode)) - entry->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED; - else - entry->d_flags &= ~DCACHE_INOTIFY_PARENT_WATCHED; -} - -/** - * inotify_inode_queue_event - queue an event to all watches on this inode - * @inode: inode event is originating from - * @mask: event mask describing this event - * @cookie: cookie for synchronization, or zero - * @name: filename, if any - * @n_inode: inode associated with name - */ -void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie, - const char *name, struct inode *n_inode) -{ - struct inotify_watch *watch, *next; - - if (!inotify_inode_watched(inode)) - return; - - mutex_lock(&inode->inotify_mutex); - list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { - u32 watch_mask = watch->mask; - if (watch_mask & mask) { - struct inotify_handle *ih= watch->ih; - mutex_lock(&ih->mutex); - if (watch_mask & IN_ONESHOT) - remove_watch_no_event(watch, ih); - ih->in_ops->handle_event(watch, watch->wd, mask, cookie, - name, n_inode); - mutex_unlock(&ih->mutex); - } - } - mutex_unlock(&inode->inotify_mutex); -} -EXPORT_SYMBOL_GPL(inotify_inode_queue_event); - -/** - * inotify_dentry_parent_queue_event - queue an event to a dentry's parent - * @dentry: the dentry in question, we queue against this dentry's parent - * @mask: event mask describing this event - * @cookie: cookie for synchronization, or zero - * @name: filename, if any - */ -void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask, - u32 cookie, const char *name) -{ - struct dentry *parent; - struct inode *inode; - - if (!(dentry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED)) - return; - - spin_lock(&dentry->d_lock); - parent = dentry->d_parent; - inode = parent->d_inode; - - if (inotify_inode_watched(inode)) { - dget(parent); - spin_unlock(&dentry->d_lock); - inotify_inode_queue_event(inode, mask, cookie, name, - dentry->d_inode); - dput(parent); - } else - spin_unlock(&dentry->d_lock); -} -EXPORT_SYMBOL_GPL(inotify_dentry_parent_queue_event); - -/** - * inotify_get_cookie - return a unique cookie for use in synchronizing events. - */ -u32 inotify_get_cookie(void) -{ - return atomic_inc_return(&inotify_cookie); -} -EXPORT_SYMBOL_GPL(inotify_get_cookie); - -/** - * inotify_unmount_inodes - an sb is unmounting. handle any watched inodes. - * @list: list of inodes being unmounted (sb->s_inodes) - * - * Called with inode_lock held, protecting the unmounting super block's list - * of inodes, and with iprune_mutex held, keeping shrink_icache_memory() at bay. - * We temporarily drop inode_lock, however, and CAN block. - */ -void inotify_unmount_inodes(struct list_head *list) -{ - struct inode *inode, *next_i, *need_iput = NULL; - - list_for_each_entry_safe(inode, next_i, list, i_sb_list) { - struct inotify_watch *watch, *next_w; - struct inode *need_iput_tmp; - struct list_head *watches; - - /* - * We cannot __iget() an inode in state I_CLEAR, I_FREEING, - * I_WILL_FREE, or I_NEW which is fine because by that point - * the inode cannot have any associated watches. - */ - if (inode->i_state & (I_CLEAR|I_FREEING|I_WILL_FREE|I_NEW)) - continue; - - /* - * If i_count is zero, the inode cannot have any watches and - * doing an __iget/iput with MS_ACTIVE clear would actually - * evict all inodes with zero i_count from icache which is - * unnecessarily violent and may in fact be illegal to do. - */ - if (!atomic_read(&inode->i_count)) - continue; - - need_iput_tmp = need_iput; - need_iput = NULL; - /* In case inotify_remove_watch_locked() drops a reference. */ - if (inode != need_iput_tmp) - __iget(inode); - else - need_iput_tmp = NULL; - /* In case the dropping of a reference would nuke next_i. */ - if ((&next_i->i_sb_list != list) && - atomic_read(&next_i->i_count) && - !(next_i->i_state & (I_CLEAR | I_FREEING | - I_WILL_FREE))) { - __iget(next_i); - need_iput = next_i; - } - - /* - * We can safely drop inode_lock here because we hold - * references on both inode and next_i. Also no new inodes - * will be added since the umount has begun. Finally, - * iprune_mutex keeps shrink_icache_memory() away. - */ - spin_unlock(&inode_lock); - - if (need_iput_tmp) - iput(need_iput_tmp); - - /* for each watch, send IN_UNMOUNT and then remove it */ - mutex_lock(&inode->inotify_mutex); - watches = &inode->inotify_watches; - list_for_each_entry_safe(watch, next_w, watches, i_list) { - struct inotify_handle *ih= watch->ih; - get_inotify_watch(watch); - mutex_lock(&ih->mutex); - ih->in_ops->handle_event(watch, watch->wd, IN_UNMOUNT, 0, - NULL, NULL); - inotify_remove_watch_locked(ih, watch); - mutex_unlock(&ih->mutex); - put_inotify_watch(watch); - } - mutex_unlock(&inode->inotify_mutex); - iput(inode); - - spin_lock(&inode_lock); - } -} -EXPORT_SYMBOL_GPL(inotify_unmount_inodes); - -/** - * inotify_inode_is_dead - an inode has been deleted, cleanup any watches - * @inode: inode that is about to be removed - */ -void inotify_inode_is_dead(struct inode *inode) -{ - struct inotify_watch *watch, *next; - - mutex_lock(&inode->inotify_mutex); - list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) { - struct inotify_handle *ih = watch->ih; - mutex_lock(&ih->mutex); - inotify_remove_watch_locked(ih, watch); - mutex_unlock(&ih->mutex); - } - mutex_unlock(&inode->inotify_mutex); -} -EXPORT_SYMBOL_GPL(inotify_inode_is_dead); - -/* Kernel Consumer API */ - -/** - * inotify_init - allocate and initialize an inotify instance - * @ops: caller's inotify operations - */ -struct inotify_handle *inotify_init(const struct inotify_operations *ops) -{ - struct inotify_handle *ih; - - ih = kmalloc(sizeof(struct inotify_handle), GFP_KERNEL); - if (unlikely(!ih)) - return ERR_PTR(-ENOMEM); - - idr_init(&ih->idr); - INIT_LIST_HEAD(&ih->watches); - mutex_init(&ih->mutex); - ih->last_wd = 0; - ih->in_ops = ops; - atomic_set(&ih->count, 0); - get_inotify_handle(ih); - - return ih; -} -EXPORT_SYMBOL_GPL(inotify_init); - -/** - * inotify_init_watch - initialize an inotify watch - * @watch: watch to initialize - */ -void inotify_init_watch(struct inotify_watch *watch) -{ - INIT_LIST_HEAD(&watch->h_list); - INIT_LIST_HEAD(&watch->i_list); - atomic_set(&watch->count, 0); - get_inotify_watch(watch); /* initial get */ -} -EXPORT_SYMBOL_GPL(inotify_init_watch); - -/* - * Watch removals suck violently. To kick the watch out we need (in this - * order) inode->inotify_mutex and ih->mutex. That's fine if we have - * a hold on inode; however, for all other cases we need to make damn sure - * we don't race with umount. We can *NOT* just grab a reference to a - * watch - inotify_unmount_inodes() will happily sail past it and we'll end - * with reference to inode potentially outliving its superblock. Ideally - * we just want to grab an active reference to superblock if we can; that - * will make sure we won't go into inotify_umount_inodes() until we are - * done. Cleanup is just deactivate_super(). However, that leaves a messy - * case - what if we *are* racing with umount() and active references to - * superblock can't be acquired anymore? We can bump ->s_count, grab - * ->s_umount, which will wait until the superblock is shut down and the - * watch in question is pining for fjords. - * - * And yes, this is far beyond mere "not very pretty"; so's the entire - * concept of inotify to start with. - */ - -/** - * pin_to_kill - pin the watch down for removal - * @ih: inotify handle - * @watch: watch to kill - * - * Called with ih->mutex held, drops it. Possible return values: - * 0 - nothing to do, it has died - * 1 - remove it, drop the reference and deactivate_super() - */ -static int pin_to_kill(struct inotify_handle *ih, struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - - if (atomic_inc_not_zero(&sb->s_active)) { - get_inotify_watch(watch); - mutex_unlock(&ih->mutex); - return 1; /* the best outcome */ - } - spin_lock(&sb_lock); - sb->s_count++; - spin_unlock(&sb_lock); - mutex_unlock(&ih->mutex); /* can't grab ->s_umount under it */ - down_read(&sb->s_umount); - /* fs is already shut down; the watch is dead */ - drop_super(sb); - return 0; -} - -static void unpin_and_kill(struct inotify_watch *watch) -{ - struct super_block *sb = watch->inode->i_sb; - put_inotify_watch(watch); - deactivate_super(sb); -} - -/** - * inotify_destroy - clean up and destroy an inotify instance - * @ih: inotify handle - */ -void inotify_destroy(struct inotify_handle *ih) -{ - /* - * Destroy all of the watches for this handle. Unfortunately, not very - * pretty. We cannot do a simple iteration over the list, because we - * do not know the inode until we iterate to the watch. But we need to - * hold inode->inotify_mutex before ih->mutex. The following works. - * - * AV: it had to become even uglier to start working ;-/ - */ - while (1) { - struct inotify_watch *watch; - struct list_head *watches; - struct super_block *sb; - struct inode *inode; - - mutex_lock(&ih->mutex); - watches = &ih->watches; - if (list_empty(watches)) { - mutex_unlock(&ih->mutex); - break; - } - watch = list_first_entry(watches, struct inotify_watch, h_list); - sb = watch->inode->i_sb; - if (!pin_to_kill(ih, watch)) - continue; - - inode = watch->inode; - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* make sure we didn't race with another list removal */ - if (likely(idr_find(&ih->idr, watch->wd))) { - remove_watch_no_event(watch, ih); - put_inotify_watch(watch); - } - - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - unpin_and_kill(watch); - } - - /* free this handle: the put matching the get in inotify_init() */ - put_inotify_handle(ih); -} -EXPORT_SYMBOL_GPL(inotify_destroy); - -/** - * inotify_find_watch - find an existing watch for an (ih,inode) pair - * @ih: inotify handle - * @inode: inode to watch - * @watchp: pointer to existing inotify_watch - * - * Caller must pin given inode (via nameidata). - */ -s32 inotify_find_watch(struct inotify_handle *ih, struct inode *inode, - struct inotify_watch **watchp) -{ - struct inotify_watch *old; - int ret = -ENOENT; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - old = inode_find_handle(inode, ih); - if (unlikely(old)) { - get_inotify_watch(old); /* caller must put watch */ - *watchp = old; - ret = old->wd; - } - - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - - return ret; -} -EXPORT_SYMBOL_GPL(inotify_find_watch); - -/** - * inotify_find_update_watch - find and update the mask of an existing watch - * @ih: inotify handle - * @inode: inode's watch to update - * @mask: mask of events to watch - * - * Caller must pin given inode (via nameidata). - */ -s32 inotify_find_update_watch(struct inotify_handle *ih, struct inode *inode, - u32 mask) -{ - struct inotify_watch *old; - int mask_add = 0; - int ret; - - if (mask & IN_MASK_ADD) - mask_add = 1; - - /* don't allow invalid bits: we don't want flags set */ - mask &= IN_ALL_EVENTS | IN_ONESHOT; - if (unlikely(!mask)) - return -EINVAL; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* - * Handle the case of re-adding a watch on an (inode,ih) pair that we - * are already watching. We just update the mask and return its wd. - */ - old = inode_find_handle(inode, ih); - if (unlikely(!old)) { - ret = -ENOENT; - goto out; - } - - if (mask_add) - old->mask |= mask; - else - old->mask = mask; - ret = old->wd; -out: - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(inotify_find_update_watch); - -/** - * inotify_add_watch - add a watch to an inotify instance - * @ih: inotify handle - * @watch: caller allocated watch structure - * @inode: inode to watch - * @mask: mask of events to watch - * - * Caller must pin given inode (via nameidata). - * Caller must ensure it only calls inotify_add_watch() once per watch. - * Calls inotify_handle_get_wd() so may sleep. - */ -s32 inotify_add_watch(struct inotify_handle *ih, struct inotify_watch *watch, - struct inode *inode, u32 mask) -{ - int ret = 0; - int newly_watched; - - /* don't allow invalid bits: we don't want flags set */ - mask &= IN_ALL_EVENTS | IN_ONESHOT; - if (unlikely(!mask)) - return -EINVAL; - watch->mask = mask; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* Initialize a new watch */ - ret = inotify_handle_get_wd(ih, watch); - if (unlikely(ret)) - goto out; - ret = watch->wd; - - /* save a reference to handle and bump the count to make it official */ - get_inotify_handle(ih); - watch->ih = ih; - - /* - * Save a reference to the inode and bump the ref count to make it - * official. We hold a reference to nameidata, which makes this safe. - */ - watch->inode = igrab(inode); - - /* Add the watch to the handle's and the inode's list */ - newly_watched = !inotify_inode_watched(inode); - list_add(&watch->h_list, &ih->watches); - list_add(&watch->i_list, &inode->inotify_watches); - /* - * Set child flags _after_ adding the watch, so there is no race - * windows where newly instantiated children could miss their parent's - * watched flag. - */ - if (newly_watched) - set_dentry_child_flags(inode, 1); - -out: - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - return ret; -} -EXPORT_SYMBOL_GPL(inotify_add_watch); - -/** - * inotify_clone_watch - put the watch next to existing one - * @old: already installed watch - * @new: new watch - * - * Caller must hold the inotify_mutex of inode we are dealing with; - * it is expected to remove the old watch before unlocking the inode. - */ -s32 inotify_clone_watch(struct inotify_watch *old, struct inotify_watch *new) -{ - struct inotify_handle *ih = old->ih; - int ret = 0; - - new->mask = old->mask; - new->ih = ih; - - mutex_lock(&ih->mutex); - - /* Initialize a new watch */ - ret = inotify_handle_get_wd(ih, new); - if (unlikely(ret)) - goto out; - ret = new->wd; - - get_inotify_handle(ih); - - new->inode = igrab(old->inode); - - list_add(&new->h_list, &ih->watches); - list_add(&new->i_list, &old->inode->inotify_watches); -out: - mutex_unlock(&ih->mutex); - return ret; -} - -void inotify_evict_watch(struct inotify_watch *watch) -{ - get_inotify_watch(watch); - mutex_lock(&watch->ih->mutex); - inotify_remove_watch_locked(watch->ih, watch); - mutex_unlock(&watch->ih->mutex); -} - -/** - * inotify_rm_wd - remove a watch from an inotify instance - * @ih: inotify handle - * @wd: watch descriptor to remove - * - * Can sleep. - */ -int inotify_rm_wd(struct inotify_handle *ih, u32 wd) -{ - struct inotify_watch *watch; - struct super_block *sb; - struct inode *inode; - - mutex_lock(&ih->mutex); - watch = idr_find(&ih->idr, wd); - if (unlikely(!watch)) { - mutex_unlock(&ih->mutex); - return -EINVAL; - } - sb = watch->inode->i_sb; - if (!pin_to_kill(ih, watch)) - return 0; - - inode = watch->inode; - - mutex_lock(&inode->inotify_mutex); - mutex_lock(&ih->mutex); - - /* make sure that we did not race */ - if (likely(idr_find(&ih->idr, wd) == watch)) - inotify_remove_watch_locked(ih, watch); - - mutex_unlock(&ih->mutex); - mutex_unlock(&inode->inotify_mutex); - unpin_and_kill(watch); - - return 0; -} -EXPORT_SYMBOL_GPL(inotify_rm_wd); - -/** - * inotify_rm_watch - remove a watch from an inotify instance - * @ih: inotify handle - * @watch: watch to remove - * - * Can sleep. - */ -int inotify_rm_watch(struct inotify_handle *ih, - struct inotify_watch *watch) -{ - return inotify_rm_wd(ih, watch->wd); -} -EXPORT_SYMBOL_GPL(inotify_rm_watch); - -/* - * inotify_setup - core initialization function - */ -static int __init inotify_setup(void) -{ - BUILD_BUG_ON(IN_ACCESS != FS_ACCESS); - BUILD_BUG_ON(IN_MODIFY != FS_MODIFY); - BUILD_BUG_ON(IN_ATTRIB != FS_ATTRIB); - BUILD_BUG_ON(IN_CLOSE_WRITE != FS_CLOSE_WRITE); - BUILD_BUG_ON(IN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); - BUILD_BUG_ON(IN_OPEN != FS_OPEN); - BUILD_BUG_ON(IN_MOVED_FROM != FS_MOVED_FROM); - BUILD_BUG_ON(IN_MOVED_TO != FS_MOVED_TO); - BUILD_BUG_ON(IN_CREATE != FS_CREATE); - BUILD_BUG_ON(IN_DELETE != FS_DELETE); - BUILD_BUG_ON(IN_DELETE_SELF != FS_DELETE_SELF); - BUILD_BUG_ON(IN_MOVE_SELF != FS_MOVE_SELF); - BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW); - - BUILD_BUG_ON(IN_UNMOUNT != FS_UNMOUNT); - BUILD_BUG_ON(IN_ISDIR != FS_IN_ISDIR); - BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED); - BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT); - - atomic_set(&inotify_cookie, 0); - - return 0; -} - -module_init(inotify_setup); diff --git a/fs/open.c b/fs/open.c index 5463266..94d54d3 100644 --- a/fs/open.c +++ b/fs/open.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "internal.h" -- cgit v1.1 From d7f0ce4e436b6109527c51b0efe0deff53d215f7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Tue, 22 Dec 2009 23:16:33 -0500 Subject: inotify: do not spam console without limit inotify was supposed to have a dmesg printk ratelimitor which would cause inotify to only emit one message per boot. The static bool was never set so it kept firing messages. This patch correctly limits warnings in multiple places. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ce21eba..f2b5424 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -449,20 +449,18 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * if it wasn't.... */ if (wd == -1) { - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, ientry->fsn_entry.inode); - WARN_ON(1); goto out; } /* Lets look in the idr to see if we find it */ found_ientry = inotify_idr_find_locked(group, wd); if (unlikely(!found_ientry)) { - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, ientry->fsn_entry.inode); - WARN_ON(1); goto out; } @@ -472,8 +470,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * fucked up somewhere. */ if (unlikely(found_ientry != ientry)) { - WARN_ON(1); - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p " + WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p " "entry->inode=%p found_ientry=%p found_ientry->wd=%d " "found_ientry->group=%p found_ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, @@ -489,7 +486,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * one ref grabbed by inotify_idr_find */ if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { - printk(KERN_WARNING "%s: ientry=%p ientry->wd=%d ientry->group=%p" + printk(KERN_ERR "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, ientry->fsn_entry.inode); /* we can't really recover with bad ref cnting.. */ -- cgit v1.1 From 7b0a04fbfb35650941af87728d4891515b4fc179 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: provide the data type to should_send_event fanotify is only interested in event types which contain enough information to open the original file in the context of the fanotify listener. Since fanotify may not want to send events if that data isn't present we pass the data type to the should_send_event function call so fanotify can express its lack of interest. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 3 ++- fs/notify/fsnotify.c | 2 +- fs/notify/inotify/inotify_fsnotify.c | 3 ++- 3 files changed, 5 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 85b97fc..6f30f49 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -133,7 +133,8 @@ static int dnotify_handle_event(struct fsnotify_group *group, * userspace notification for that pair. */ static bool dnotify_should_send_event(struct fsnotify_group *group, - struct inode *inode, __u32 mask) + struct inode *inode, __u32 mask, + int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index fcc2f06..fc06e47 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -157,7 +157,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const idx = srcu_read_lock(&fsnotify_grp_srcu); list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { if (test_mask & group->mask) { - if (!group->ops->should_send_event(group, to_tell, mask)) + if (!group->ops->should_send_event(group, to_tell, mask, data_is)) continue; if (!event) { event = fsnotify_create_event(to_tell, mask, data, diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index e27960c..fc7c495 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -86,7 +86,8 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot inotify_ignored_and_remove_idr(entry, group); } -static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) +static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, + __u32 mask, int data_type) { struct fsnotify_mark_entry *entry; bool send; -- cgit v1.1 From 8112e2d6a7356e8c3ff1f7f3c86f375ed0305705 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: include data in should_send calls fanotify is going to need to look at file->private_data to know if an event should be sent or not. This passes the data (which might be a file, dentry, inode, or none) to the should_send function calls so fanotify can get that information when available Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/fsnotify.c | 3 ++- fs/notify/inotify/inotify_fsnotify.c | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 6f30f49..a213b83 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -134,7 +134,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, */ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, __u32 mask, - int data_type) + void *data, int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index fc06e47..523337b 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -157,7 +157,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const idx = srcu_read_lock(&fsnotify_grp_srcu); list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { if (test_mask & group->mask) { - if (!group->ops->should_send_event(group, to_tell, mask, data_is)) + if (!group->ops->should_send_event(group, to_tell, mask, + data, data_is)) continue; if (!event) { event = fsnotify_create_event(to_tell, mask, data, diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index fc7c495..1f33234 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -87,7 +87,7 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, int data_type) + __u32 mask, void *data, int data_type) { struct fsnotify_mark_entry *entry; bool send; -- cgit v1.1 From 2a12a9d7814631e918dec93abad856e692d5286d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: pass a file instead of an inode to open, read, and write fanotify, the upcoming notification system actually needs a struct path so it can do opens in the context of listeners, and it needs a file so it can get f_flags from the original process. Close was the only operation that already was passing a struct file to the notification hook. This patch passes a file for access, modify, and open as well as they are easily available to these hooks. Signed-off-by: Eric Paris --- fs/compat.c | 5 ++--- fs/exec.c | 4 ++-- fs/nfsd/vfs.c | 4 ++-- fs/open.c | 2 +- fs/read_write.c | 8 ++++---- 5 files changed, 11 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/compat.c b/fs/compat.c index 6490d21..ce02278 100644 --- a/fs/compat.c +++ b/fs/compat.c @@ -1193,11 +1193,10 @@ out: if (iov != iovstack) kfree(iov); if ((ret + (type == READ)) > 0) { - struct dentry *dentry = file->f_path.dentry; if (type == READ) - fsnotify_access(dentry); + fsnotify_access(file); else - fsnotify_modify(dentry); + fsnotify_modify(file); } return ret; } diff --git a/fs/exec.c b/fs/exec.c index e19de6a..f2de04a 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -129,7 +129,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) goto exit; - fsnotify_open(file->f_path.dentry); + fsnotify_open(file); error = -ENOEXEC; if(file->f_op) { @@ -683,7 +683,7 @@ struct file *open_exec(const char *name) if (file->f_path.mnt->mnt_flags & MNT_NOEXEC) goto exit; - fsnotify_open(file->f_path.dentry); + fsnotify_open(file); err = deny_write_access(file); if (err) diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 3c11112..16114a8 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c @@ -951,7 +951,7 @@ nfsd_vfs_read(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, nfsdstats.io_read += host_err; *count = host_err; err = 0; - fsnotify_access(file->f_path.dentry); + fsnotify_access(file); } else err = nfserrno(host_err); out: @@ -1062,7 +1062,7 @@ nfsd_vfs_write(struct svc_rqst *rqstp, struct svc_fh *fhp, struct file *file, goto out_nfserr; *cnt = host_err; nfsdstats.io_write += host_err; - fsnotify_modify(file->f_path.dentry); + fsnotify_modify(file); /* clear setuid/setgid flag after write */ if (inode->i_mode & (S_ISUID | S_ISGID)) diff --git a/fs/open.c b/fs/open.c index 94d54d3..bf08263 100644 --- a/fs/open.c +++ b/fs/open.c @@ -889,7 +889,7 @@ long do_sys_open(int dfd, const char __user *filename, int flags, int mode) put_unused_fd(fd); fd = PTR_ERR(f); } else { - fsnotify_open(f->f_path.dentry); + fsnotify_open(f); fd_install(fd, f); } } diff --git a/fs/read_write.c b/fs/read_write.c index 9c04852..74e3658 100644 --- a/fs/read_write.c +++ b/fs/read_write.c @@ -311,7 +311,7 @@ ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos) else ret = do_sync_read(file, buf, count, pos); if (ret > 0) { - fsnotify_access(file->f_path.dentry); + fsnotify_access(file); add_rchar(current, ret); } inc_syscr(current); @@ -367,7 +367,7 @@ ssize_t vfs_write(struct file *file, const char __user *buf, size_t count, loff_ else ret = do_sync_write(file, buf, count, pos); if (ret > 0) { - fsnotify_modify(file->f_path.dentry); + fsnotify_modify(file); add_wchar(current, ret); } inc_syscw(current); @@ -675,9 +675,9 @@ out: kfree(iov); if ((ret + (type == READ)) > 0) { if (type == READ) - fsnotify_access(file->f_path.dentry); + fsnotify_access(file); else - fsnotify_modify(file->f_path.dentry); + fsnotify_modify(file); } return ret; } -- cgit v1.1 From 28c60e37f874dcbb93c4afc839ba5e4911c4f4bc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: send struct file when sending events to parents when possible fanotify needs a path in order to open an fd to the object which changed. Currently notifications to inode's parents are done using only the inode. For some parental notification we have the entire file, send that so fanotify can use it. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 523337b..806beed 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -78,13 +78,16 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) } /* Notify this dentry's parent about a child's events. */ -void __fsnotify_parent(struct dentry *dentry, __u32 mask) +void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) { struct dentry *parent; struct inode *p_inode; bool send = false; bool should_update_children = false; + if (file) + dentry = file->f_path.dentry; + if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) return; @@ -115,8 +118,12 @@ void __fsnotify_parent(struct dentry *dentry, __u32 mask) * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; - fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, - dentry->d_name.name, 0); + if (file) + fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, + dentry->d_name.name, 0); + else + fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, + dentry->d_name.name, 0); dput(parent); } -- cgit v1.1 From 74766bbfa99adf8cb8119df6121851edba21c9d9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: per group notification queue merge types inotify only wishes to merge a new event with the last event on the notification fifo. fanotify is willing to merge any events including by means of bitwise OR masks of multiple events together. This patch moves the inotify event merging logic out of the generic fsnotify notification.c and into the inotify code. This allows each use of fsnotify to provide their own merge functionality. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 56 ++++++++++++++++++++++++++- fs/notify/inotify/inotify_user.c | 2 +- fs/notify/notification.c | 73 ++++++++++-------------------------- 3 files changed, 75 insertions(+), 56 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 1f33234..0a0f5d0 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -32,6 +32,60 @@ #include "inotify.h" +/* + * Check if 2 events contain the same information. We do not compare private data + * but at this moment that isn't a problem for any know fsnotify listeners. + */ +static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) +{ + if ((old->mask == new->mask) && + (old->to_tell == new->to_tell) && + (old->data_type == new->data_type) && + (old->name_len == new->name_len)) { + switch (old->data_type) { + case (FSNOTIFY_EVENT_INODE): + /* remember, after old was put on the wait_q we aren't + * allowed to look at the inode any more, only thing + * left to check was if the file_name is the same */ + if (!old->name_len || + !strcmp(old->file_name, new->file_name)) + return true; + break; + case (FSNOTIFY_EVENT_PATH): + if ((old->path.mnt == new->path.mnt) && + (old->path.dentry == new->path.dentry)) + return true; + break; + case (FSNOTIFY_EVENT_NONE): + if (old->mask & FS_Q_OVERFLOW) + return true; + else if (old->mask & FS_IN_IGNORED) + return false; + return true; + }; + } + return false; +} + +static int inotify_merge(struct list_head *list, struct fsnotify_event *event) +{ + struct fsnotify_event_holder *last_holder; + struct fsnotify_event *last_event; + int ret = 0; + + /* and the list better be locked by something too */ + spin_lock(&event->lock); + + last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); + last_event = last_holder->event; + if (event_compare(last_event, event)) + ret = -EEXIST; + + spin_unlock(&event->lock); + + return ret; +} + static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { struct fsnotify_mark_entry *entry; @@ -62,7 +116,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev fsn_event_priv->group = group; event_priv->wd = wd; - ret = fsnotify_add_notify_event(group, event, fsn_event_priv); + ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); if (ret) { inotify_free_event_priv(fsn_event_priv); /* EEXIST says we tail matched, EOVERFLOW isn't something diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f2b5424..cbe16df 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -531,7 +531,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, fsn_event_priv->group = group; event_priv->wd = ientry->wd; - ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv); + ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); if (ret) inotify_free_event_priv(fsn_event_priv); diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b34ce7a..6dc96b3 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -104,7 +104,8 @@ struct fsnotify_event_holder *fsnotify_alloc_event_holder(void) void fsnotify_destroy_event_holder(struct fsnotify_event_holder *holder) { - kmem_cache_free(fsnotify_event_holder_cachep, holder); + if (holder) + kmem_cache_free(fsnotify_event_holder_cachep, holder); } /* @@ -129,53 +130,17 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot } /* - * Check if 2 events contain the same information. We do not compare private data - * but at this moment that isn't a problem for any know fsnotify listeners. - */ -static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new) -{ - if ((old->mask == new->mask) && - (old->to_tell == new->to_tell) && - (old->data_type == new->data_type) && - (old->name_len == new->name_len)) { - switch (old->data_type) { - case (FSNOTIFY_EVENT_INODE): - /* remember, after old was put on the wait_q we aren't - * allowed to look at the inode any more, only thing - * left to check was if the file_name is the same */ - if (!old->name_len || - !strcmp(old->file_name, new->file_name)) - return true; - break; - case (FSNOTIFY_EVENT_PATH): - if ((old->path.mnt == new->path.mnt) && - (old->path.dentry == new->path.dentry)) - return true; - break; - case (FSNOTIFY_EVENT_NONE): - if (old->mask & FS_Q_OVERFLOW) - return true; - else if (old->mask & FS_IN_IGNORED) - return false; - return false; - }; - } - return false; -} - -/* * Add an event to the group notification queue. The group can later pull this * event off the queue to deal with. If the event is successfully added to the * group's notification queue, a reference is taken on event. */ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, - struct fsnotify_event_private_data *priv) + struct fsnotify_event_private_data *priv, + int (*merge)(struct list_head *, struct fsnotify_event *)) { struct fsnotify_event_holder *holder = NULL; struct list_head *list = &group->notification_list; - struct fsnotify_event_holder *last_holder; - struct fsnotify_event *last_event; - int ret = 0; + int rc = 0; /* * There is one fsnotify_event_holder embedded inside each fsnotify_event. @@ -196,11 +161,23 @@ alloc_holder: if (group->q_len >= group->max_events) { event = q_overflow_event; - ret = -EOVERFLOW; + rc = -EOVERFLOW; /* sorry, no private data on the overflow event */ priv = NULL; } + if (!list_empty(list) && merge) { + int ret; + + ret = merge(list, event); + if (ret) { + mutex_unlock(&group->notification_mutex); + if (holder != &event->holder) + fsnotify_destroy_event_holder(holder); + return ret; + } + } + spin_lock(&event->lock); if (list_empty(&event->holder.event_list)) { @@ -215,18 +192,6 @@ alloc_holder: goto alloc_holder; } - if (!list_empty(list)) { - last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); - last_event = last_holder->event; - if (event_compare(last_event, event)) { - spin_unlock(&event->lock); - mutex_unlock(&group->notification_mutex); - if (holder != &event->holder) - fsnotify_destroy_event_holder(holder); - return -EEXIST; - } - } - group->q_len++; holder->event = event; @@ -238,7 +203,7 @@ alloc_holder: mutex_unlock(&group->notification_mutex); wake_up(&group->notification_waitq); - return ret; + return rc; } /* -- cgit v1.1 From b4e4e1407312ae5a267ed7d716e6d4e7120a8430 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:21 -0500 Subject: fsnotify: clone existing events fsnotify_clone_event will take an event, clone it, and return the cloned event to the caller. Since events may be in use by multiple fsnotify groups simultaneously certain event entries (such as the mask) cannot be changed after the event was created. Since fanotify would like to merge events happening on the same file it needs a new clean event to work with so it can change any fields it wishes. Signed-off-by: Eric Paris --- fs/notify/notification.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 6dc96b3..bc9470c 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -284,11 +284,33 @@ static void initialize_event(struct fsnotify_event *event) spin_lock_init(&event->lock); - event->data_type = FSNOTIFY_EVENT_NONE; - INIT_LIST_HEAD(&event->private_data_list); } +struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) +{ + struct fsnotify_event *event; + + event = kmem_cache_alloc(fsnotify_event_cachep, GFP_KERNEL); + if (!event) + return NULL; + + memcpy(event, old_event, sizeof(*event)); + initialize_event(event); + + if (event->name_len) { + event->file_name = kstrdup(old_event->file_name, GFP_KERNEL); + if (!event->file_name) { + kmem_cache_free(fsnotify_event_cachep, event); + return NULL; + } + } + if (event->data_type == FSNOTIFY_EVENT_PATH) + path_get(&event->path); + + return event; +} + /* * fsnotify_create_event - Allocate a new event which will be sent to each * group's handle_event function if the group was interested in this @@ -324,6 +346,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->sync_cookie = cookie; event->to_tell = to_tell; + event->data_type = data_type; switch (data_type) { case FSNOTIFY_EVENT_FILE: { @@ -340,12 +363,10 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->path.dentry = path->dentry; event->path.mnt = path->mnt; path_get(&event->path); - event->data_type = FSNOTIFY_EVENT_PATH; break; } case FSNOTIFY_EVENT_INODE: event->inode = data; - event->data_type = FSNOTIFY_EVENT_INODE; break; case FSNOTIFY_EVENT_NONE: event->inode = NULL; -- cgit v1.1 From 1201a5361b9bd6512ae01e6f2b7aa79d458cafb1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: replace an event on a list fanotify would like to clone events already on its notification list, make changes to the new event, and then replace the old event on the list with the new event. This patch implements the replace functionality of that process. Signed-off-by: Eric Paris --- fs/notify/notification.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index bc9470c..b493c37 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -287,6 +287,62 @@ static void initialize_event(struct fsnotify_event *event) INIT_LIST_HEAD(&event->private_data_list); } +/* + * Caller damn well better be holding whatever mutex is protecting the + * old_holder->event_list. + */ +int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, + struct fsnotify_event *new_event) +{ + struct fsnotify_event *old_event = old_holder->event; + struct fsnotify_event_holder *new_holder = NULL; + + /* + * There is one fsnotify_event_holder embedded inside each fsnotify_event. + * Check if we expect to be able to use that holder. If not alloc a new + * holder. + * For the overflow event it's possible that something will use the in + * event holder before we get the lock so we may need to jump back and + * alloc a new holder, this can't happen for most events... + */ + if (!list_empty(&new_event->holder.event_list)) { +alloc_holder: + new_holder = fsnotify_alloc_event_holder(); + if (!new_holder) + return -ENOMEM; + } + + spin_lock(&old_event->lock); + spin_lock(&new_event->lock); + + if (list_empty(&new_event->holder.event_list)) { + if (unlikely(new_holder)) + fsnotify_destroy_event_holder(new_holder); + new_holder = &new_event->holder; + } else if (unlikely(!new_holder)) { + /* between the time we checked above and got the lock the in + * event holder was used, go back and get a new one */ + spin_unlock(&new_event->lock); + spin_unlock(&old_event->lock); + goto alloc_holder; + } + + new_holder->event = new_event; + list_replace_init(&old_holder->event_list, &new_holder->event_list); + + spin_unlock(&new_event->lock); + spin_unlock(&old_event->lock); + + /* event == holder means we are referenced through the in event holder */ + if (old_holder != &old_event->holder) + fsnotify_destroy_event_holder(old_holder); + + fsnotify_get_event(new_event); /* on the list take reference */ + fsnotify_put_event(old_event); /* off the list, drop reference */ + + return 0; +} + struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) { struct fsnotify_event *event; -- cgit v1.1 From cac69dad32899c6f4c66bb4f9baf69b0d3c7d3d1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: lock annotation for event replacement fsnotify_replace_event need to lock both the old and the new event. This causes lockdep to get all pissed off since it dosn't know this is safe. It's safe in this case since the new event is impossible to be reached from other places in the kernel. Signed-off-by: Eric Paris --- fs/notify/notification.c | 41 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 28 deletions(-) (limited to 'fs') diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b493c37..dafd0b7 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -289,43 +289,28 @@ static void initialize_event(struct fsnotify_event *event) /* * Caller damn well better be holding whatever mutex is protecting the - * old_holder->event_list. + * old_holder->event_list and the new_event must be a clean event which + * cannot be found anywhere else in the kernel. */ int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, struct fsnotify_event *new_event) { struct fsnotify_event *old_event = old_holder->event; - struct fsnotify_event_holder *new_holder = NULL; + struct fsnotify_event_holder *new_holder = &new_event->holder; + + enum event_spinlock_class { + SPINLOCK_OLD, + SPINLOCK_NEW, + }; /* - * There is one fsnotify_event_holder embedded inside each fsnotify_event. - * Check if we expect to be able to use that holder. If not alloc a new - * holder. - * For the overflow event it's possible that something will use the in - * event holder before we get the lock so we may need to jump back and - * alloc a new holder, this can't happen for most events... + * if the new_event's embedded holder is in use someone + * screwed up and didn't give us a clean new event. */ - if (!list_empty(&new_event->holder.event_list)) { -alloc_holder: - new_holder = fsnotify_alloc_event_holder(); - if (!new_holder) - return -ENOMEM; - } + BUG_ON(!list_empty(&new_holder->event_list)); - spin_lock(&old_event->lock); - spin_lock(&new_event->lock); - - if (list_empty(&new_event->holder.event_list)) { - if (unlikely(new_holder)) - fsnotify_destroy_event_holder(new_holder); - new_holder = &new_event->holder; - } else if (unlikely(!new_holder)) { - /* between the time we checked above and got the lock the in - * event holder was used, go back and get a new one */ - spin_unlock(&new_event->lock); - spin_unlock(&old_event->lock); - goto alloc_holder; - } + spin_lock_nested(&old_event->lock, SPINLOCK_OLD); + spin_lock_nested(&new_event->lock, SPINLOCK_NEW); new_holder->event = new_event; list_replace_init(&old_holder->event_list, &new_holder->event_list); -- cgit v1.1 From 74be0cc82835aecad332a29896b0f212ba893403 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: remove group_num altogether The original fsnotify interface has a group-num which was intended to be able to find a group after it was added. I no longer think this is a necessary thing to do and so we remove the group_num. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 3 +-- fs/notify/group.c | 48 ++-------------------------------------- fs/notify/inotify/inotify_user.c | 11 +-------- 3 files changed, 4 insertions(+), 58 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index a213b83..1f46aea 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -433,8 +433,7 @@ static int __init dnotify_init(void) dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); - dnotify_group = fsnotify_obtain_group(DNOTIFY_GROUP_NUM, - 0, &dnotify_fsnotify_ops); + dnotify_group = fsnotify_obtain_group(0, &dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) panic("unable to allocate fsnotify group for dnotify\n"); return 0; diff --git a/fs/notify/group.c b/fs/notify/group.c index 777ca82..62fb896 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -78,15 +78,6 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) } /* - * Take a reference to a group so things found under the fsnotify_grp_mutex - * can't get freed under us - */ -static void fsnotify_get_group(struct fsnotify_group *group) -{ - atomic_inc(&group->refcnt); -} - -/* * Final freeing of a group */ void fsnotify_final_destroy_group(struct fsnotify_group *group) @@ -171,40 +162,14 @@ void fsnotify_put_group(struct fsnotify_group *group) } /* - * Simply run the fsnotify_groups list and find a group which matches - * the given parameters. If a group is found we take a reference to that - * group. - */ -static struct fsnotify_group *fsnotify_find_group(unsigned int group_num, __u32 mask, - const struct fsnotify_ops *ops) -{ - struct fsnotify_group *group_iter; - struct fsnotify_group *group = NULL; - - BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); - - list_for_each_entry_rcu(group_iter, &fsnotify_groups, group_list) { - if (group_iter->group_num == group_num) { - if ((group_iter->mask == mask) && - (group_iter->ops == ops)) { - fsnotify_get_group(group_iter); - group = group_iter; - } else - group = ERR_PTR(-EEXIST); - } - } - return group; -} - -/* * Either finds an existing group which matches the group_num, mask, and ops or * creates a new group and adds it to the global group list. In either case we * take a reference for the group returned. */ -struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, +struct fsnotify_group *fsnotify_obtain_group(__u32 mask, const struct fsnotify_ops *ops) { - struct fsnotify_group *group, *tgroup; + struct fsnotify_group *group; /* very low use, simpler locking if we just always alloc */ group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL); @@ -214,7 +179,6 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, atomic_set(&group->refcnt, 1); group->on_group_list = 0; - group->group_num = group_num; group->mask = mask; mutex_init(&group->notification_mutex); @@ -230,14 +194,6 @@ struct fsnotify_group *fsnotify_obtain_group(unsigned int group_num, __u32 mask, group->ops = ops; mutex_lock(&fsnotify_grp_mutex); - tgroup = fsnotify_find_group(group_num, mask, ops); - if (tgroup) { - /* group already exists */ - mutex_unlock(&fsnotify_grp_mutex); - /* destroy the new one we made */ - fsnotify_put_group(group); - return tgroup; - } /* group not found, add a new one */ list_add_rcu(&group->group_list, &fsnotify_groups); diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index cbe16df..cae317f 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -51,12 +51,6 @@ int inotify_max_user_watches __read_mostly; static struct kmem_cache *inotify_inode_mark_cachep __read_mostly; struct kmem_cache *event_priv_cachep __read_mostly; -/* - * When inotify registers a new group it increments this and uses that - * value as an offset to set the fsnotify group "name" and priority. - */ -static atomic_t inotify_grp_num; - #ifdef CONFIG_SYSCTL #include @@ -700,11 +694,8 @@ retry: static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsigned int max_events) { struct fsnotify_group *group; - unsigned int grp_num; - /* fsnotify_obtain_group took a reference to group, we put this when we kill the file in the end */ - grp_num = (INOTIFY_GROUP_NUM - atomic_inc_return(&inotify_grp_num)); - group = fsnotify_obtain_group(grp_num, 0, &inotify_fsnotify_ops); + group = fsnotify_obtain_group(0, &inotify_fsnotify_ops); if (IS_ERR(group)) return group; -- cgit v1.1 From cd7752ce7cac5184ca35aecebffafae9662570bc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: fsnotify_obtain_group kzalloc cleanup fsnotify_obtain_group uses kzalloc but then proceedes to set things to 0. This patch just deletes those useless lines. Signed-off-by: Eric Paris --- fs/notify/group.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index 62fb896..934860e 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -178,17 +178,14 @@ struct fsnotify_group *fsnotify_obtain_group(__u32 mask, atomic_set(&group->refcnt, 1); - group->on_group_list = 0; group->mask = mask; mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); init_waitqueue_head(&group->notification_waitq); - group->q_len = 0; group->max_events = UINT_MAX; spin_lock_init(&group->mark_lock); - atomic_set(&group->num_marks, 0); INIT_LIST_HEAD(&group->mark_entries); group->ops = ops; -- cgit v1.1 From ffab83402f01555a5fa32efb48a4dd0ce8d12ef5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: fsnotify_obtain_group should be fsnotify_alloc_group fsnotify_obtain_group was intended to be able to find an already existing group. Nothing uses that functionality. This just renames it to fsnotify_alloc_group so it is clear what it is doing. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/group.c | 10 +++------- fs/notify/inotify/inotify_user.c | 2 +- 3 files changed, 5 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 1f46aea..51e4fe3 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -433,7 +433,7 @@ static int __init dnotify_init(void) dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); - dnotify_group = fsnotify_obtain_group(0, &dnotify_fsnotify_ops); + dnotify_group = fsnotify_alloc_group(0, &dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) panic("unable to allocate fsnotify group for dnotify\n"); return 0; diff --git a/fs/notify/group.c b/fs/notify/group.c index 934860e..1d20d26 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -162,16 +162,13 @@ void fsnotify_put_group(struct fsnotify_group *group) } /* - * Either finds an existing group which matches the group_num, mask, and ops or - * creates a new group and adds it to the global group list. In either case we - * take a reference for the group returned. + * Create a new fsnotify_group and hold a reference for the group returned. */ -struct fsnotify_group *fsnotify_obtain_group(__u32 mask, - const struct fsnotify_ops *ops) +struct fsnotify_group *fsnotify_alloc_group(__u32 mask, + const struct fsnotify_ops *ops) { struct fsnotify_group *group; - /* very low use, simpler locking if we just always alloc */ group = kzalloc(sizeof(struct fsnotify_group), GFP_KERNEL); if (!group) return ERR_PTR(-ENOMEM); @@ -192,7 +189,6 @@ struct fsnotify_group *fsnotify_obtain_group(__u32 mask, mutex_lock(&fsnotify_grp_mutex); - /* group not found, add a new one */ list_add_rcu(&group->group_list, &fsnotify_groups); group->on_group_list = 1; /* being on the fsnotify_groups list holds one num_marks */ diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index cae317f..25a2854 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -695,7 +695,7 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign { struct fsnotify_group *group; - group = fsnotify_obtain_group(0, &inotify_fsnotify_ops); + group = fsnotify_alloc_group(0, &inotify_fsnotify_ops); if (IS_ERR(group)) return group; -- cgit v1.1 From 0d2e2a1d00d7d23e5bd9bb0935cde7c3d5835c56 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:22 -0500 Subject: fsnotify: drop mask argument from fsnotify_alloc_group Nothing uses the mask argument to fsnotify_alloc_group. This patch drops that argument. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/group.c | 8 +------- fs/notify/inotify/inotify_user.c | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 51e4fe3..e0a847b 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -433,7 +433,7 @@ static int __init dnotify_init(void) dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); - dnotify_group = fsnotify_alloc_group(0, &dnotify_fsnotify_ops); + dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) panic("unable to allocate fsnotify group for dnotify\n"); return 0; diff --git a/fs/notify/group.c b/fs/notify/group.c index 1d20d26..1657349 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -164,8 +164,7 @@ void fsnotify_put_group(struct fsnotify_group *group) /* * Create a new fsnotify_group and hold a reference for the group returned. */ -struct fsnotify_group *fsnotify_alloc_group(__u32 mask, - const struct fsnotify_ops *ops) +struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) { struct fsnotify_group *group; @@ -175,8 +174,6 @@ struct fsnotify_group *fsnotify_alloc_group(__u32 mask, atomic_set(&group->refcnt, 1); - group->mask = mask; - mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); init_waitqueue_head(&group->notification_waitq); @@ -196,8 +193,5 @@ struct fsnotify_group *fsnotify_alloc_group(__u32 mask, mutex_unlock(&fsnotify_grp_mutex); - if (mask) - fsnotify_recalc_global_mask(); - return group; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 25a2854..a48d68a 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -695,7 +695,7 @@ static struct fsnotify_group *inotify_new_group(struct user_struct *user, unsign { struct fsnotify_group *group; - group = fsnotify_alloc_group(0, &inotify_fsnotify_ops); + group = fsnotify_alloc_group(&inotify_fsnotify_ops); if (IS_ERR(group)) return group; -- cgit v1.1 From 19c2a0e1a2f60112c158342ba5f568f72b741c2c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: rename fsnotify_groups to fsnotify_inode_groups Simple renaming patch. fsnotify is about to support mount point listeners so I am renaming fsnotify_groups and fsnotify_mask to indicate these are lists used only for groups which have watches on inodes. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 6 +++--- fs/notify/fsnotify.h | 8 ++++---- fs/notify/group.c | 30 +++++++++++++++++++----------- 3 files changed, 26 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 806beed..23b5cfb 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -148,10 +148,10 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (list_empty(&fsnotify_groups)) + if (list_empty(&fsnotify_inode_groups)) return; - if (!(test_mask & fsnotify_mask)) + if (!(test_mask & fsnotify_inode_mask)) return; if (!(test_mask & to_tell->i_fsnotify_mask)) @@ -162,7 +162,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const * anything other than walk the list so it's crazy to pre-allocate. */ idx = srcu_read_lock(&fsnotify_grp_srcu); - list_for_each_entry_rcu(group, &fsnotify_groups, group_list) { + list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { if (test_mask & group->mask) { if (!group->ops->should_send_event(group, to_tell, mask, data, data_is)) diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 4dc2408..ec5aee4 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -8,10 +8,10 @@ /* protects reads of fsnotify_groups */ extern struct srcu_struct fsnotify_grp_srcu; -/* all groups which receive fsnotify events */ -extern struct list_head fsnotify_groups; -/* all bitwise OR of all event types (FS_*) for all fsnotify_groups */ -extern __u32 fsnotify_mask; +/* all groups which receive inode fsnotify events */ +extern struct list_head fsnotify_inode_groups; +/* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ +extern __u32 fsnotify_inode_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 1657349..c808097 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -33,9 +33,9 @@ static DEFINE_MUTEX(fsnotify_grp_mutex); /* protects reads while running the fsnotify_groups list */ struct srcu_struct fsnotify_grp_srcu; /* all groups registered to receive filesystem notifications */ -LIST_HEAD(fsnotify_groups); +LIST_HEAD(fsnotify_inode_groups); /* bitwise OR of all events (FS_*) interesting to some group on this system */ -__u32 fsnotify_mask; +__u32 fsnotify_inode_mask; /* * When a new group registers or changes it's set of interesting events @@ -48,10 +48,10 @@ void fsnotify_recalc_global_mask(void) int idx; idx = srcu_read_lock(&fsnotify_grp_srcu); - list_for_each_entry_rcu(group, &fsnotify_groups, group_list) + list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) mask |= group->mask; srcu_read_unlock(&fsnotify_grp_srcu, idx); - fsnotify_mask = mask; + fsnotify_inode_mask = mask; } /* @@ -77,6 +77,17 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) fsnotify_recalc_global_mask(); } +static void fsnotify_add_group(struct fsnotify_group *group) +{ + BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); + + group->on_inode_group_list = 1; + /* being on the fsnotify_groups list holds one num_marks */ + atomic_inc(&group->num_marks); + + list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); +} + /* * Final freeing of a group */ @@ -118,9 +129,9 @@ static void __fsnotify_evict_group(struct fsnotify_group *group) { BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); - if (group->on_group_list) - list_del_rcu(&group->group_list); - group->on_group_list = 0; + if (group->on_inode_group_list) + list_del_rcu(&group->inode_group_list); + group->on_inode_group_list = 0; } /* @@ -186,10 +197,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) mutex_lock(&fsnotify_grp_mutex); - list_add_rcu(&group->group_list, &fsnotify_groups); - group->on_group_list = 1; - /* being on the fsnotify_groups list holds one num_marks */ - atomic_inc(&group->num_marks); + fsnotify_add_group(group); mutex_unlock(&fsnotify_grp_mutex); -- cgit v1.1 From 36fddebaa8a9186d4f5817ab798a83400b2fb2e7 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: initialize the group->num_marks in a better place Currently the comments say that group->num_marks is held because the group is on the fsnotify_group list. This isn't strictly the case, we really just hold the num_marks for the life of the group (any time group->refcnt is != 0) This patch moves the initialization stuff and makes it clear when it is really being held. Signed-off-by: Eric Paris --- fs/notify/group.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index c808097..656c534 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -82,9 +82,6 @@ static void fsnotify_add_group(struct fsnotify_group *group) BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); group->on_inode_group_list = 1; - /* being on the fsnotify_groups list holds one num_marks */ - atomic_inc(&group->num_marks); - list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); } @@ -183,7 +180,14 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) if (!group) return ERR_PTR(-ENOMEM); + /* set to 0 when there a no external references to this group */ atomic_set(&group->refcnt, 1); + /* + * hits 0 when there are no external references AND no marks for + * this group + */ + atomic_set(&group->num_marks, 1); + mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); -- cgit v1.1 From 4ca763523e040dc61191d4866a82981a5d30a4e9 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: add groups to fsnotify_inode_groups when registering inode watch Currently all fsnotify groups are added immediately to the fsnotify_inode_groups list upon creation. This means, even groups with no watches (common for audit) will be on the global tracking list and will get checked for every event. This patch adds groups to the global list on when the first inode mark is added to the group. Signed-of-by: Eric Paris --- fs/notify/fsnotify.h | 2 ++ fs/notify/group.c | 18 ++++++++---------- fs/notify/inode_mark.c | 7 +++++++ 3 files changed, 17 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index ec5aee4..5bd2241 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -16,6 +16,8 @@ extern __u32 fsnotify_inode_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); +/* add a group to the inode group list */ +extern void fsnotify_add_inode_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 656c534..34fccbd 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -77,12 +77,15 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) fsnotify_recalc_global_mask(); } -static void fsnotify_add_group(struct fsnotify_group *group) +void fsnotify_add_inode_group(struct fsnotify_group *group) { - BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); + mutex_lock(&fsnotify_grp_mutex); + if (!group->on_inode_group_list) + list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); group->on_inode_group_list = 1; - list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); + + mutex_unlock(&fsnotify_grp_mutex); } /* @@ -188,22 +191,17 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) */ atomic_set(&group->num_marks, 1); - mutex_init(&group->notification_mutex); INIT_LIST_HEAD(&group->notification_list); init_waitqueue_head(&group->notification_waitq); group->max_events = UINT_MAX; + INIT_LIST_HEAD(&group->inode_group_list); + spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->mark_entries); group->ops = ops; - mutex_lock(&fsnotify_grp_mutex); - - fsnotify_add_group(group); - - mutex_unlock(&fsnotify_grp_mutex); - return group; } diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 7d2962e..a3230c4 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -323,6 +323,13 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, return -EINVAL; /* + * if this group isn't being testing for inode type events we need + * to start testing + */ + if (unlikely(list_empty(&group->inode_group_list))) + fsnotify_add_inode_group(group); + + /* * LOCKING ORDER!!!! * entry->lock * group->mark_lock -- cgit v1.1 From 7131485a93679ff9a543b74df280cfd119eb03ca Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: mount point listeners list and global mask currently all of the notification systems implemented select which inodes they care about and receive messages only about those inodes (or the children of those inodes.) This patch begins to flesh out fsnotify support for the concept of listeners that want to hear notification for an inode accessed below a given monut point. This patch implements a second list of fsnotify groups to hold these types of groups and a second global mask to hold the events of interest for this type of group. The reason we want a second group list and mask is because the inode based notification should_send_event support which makes each group look for a mark on the given inode. With one nfsmount listener that means that every group would have to take the inode->i_lock, look for their mark, not find one, and return for every operation. By seperating vfsmount from inode listeners only when there is a inode listener will the inode groups have to look for their mark and take the inode lock. vfsmount listeners will have to grab the lock and look for a mark but there should be fewer of them, and one vfsmount listener won't cause the i_lock to be grabbed and released for every fsnotify group on every io operation. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 92 ++++++++++++++++++++++++++++++++++++++------------ fs/notify/fsnotify.h | 6 ++++ fs/notify/group.c | 33 +++++++++++++++--- fs/notify/inode_mark.c | 7 ++++ 4 files changed, 112 insertions(+), 26 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 23b5cfb..a61aaa7 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -134,6 +135,45 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); +static void send_to_group(__u32 mask, + struct fsnotify_group *group, + void *data, int data_is, const char *file_name, + u32 cookie, struct fsnotify_event **event, + struct inode *to_tell) +{ + if (!group->ops->should_send_event(group, to_tell, mask, + data, data_is)) + return; + if (!*event) { + *event = fsnotify_create_event(to_tell, mask, data, + data_is, file_name, + cookie, GFP_KERNEL); + /* + * shit, we OOM'd and now we can't tell, maybe + * someday someone else will want to do something + * here + */ + if (!*event) + return; + } + group->ops->handle_event(group, *event); +} + +static bool needed_by_vfsmount(__u32 test_mask, void *data, int data_is) +{ + struct path *path; + + if (data_is == FSNOTIFY_EVENT_PATH) + path = (struct path *)data; + else if (data_is == FSNOTIFY_EVENT_FILE) + path = &((struct file *)data)->f_path; + else + return false; + + /* hook in this when mnt->mnt_fsnotify_mask is defined */ + /* return (test_mask & path->mnt->mnt_fsnotify_mask); */ + return false; +} /* * This is the main call to fsnotify. The VFS calls into hook specific functions * in linux/fsnotify.h. Those functions then in turn call here. Here will call @@ -148,38 +188,46 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (list_empty(&fsnotify_inode_groups)) - return; + /* if no fsnotify listeners, nothing to do */ + if (list_empty(&fsnotify_inode_groups) && + list_empty(&fsnotify_vfsmount_groups)) + return; + + /* if none of the directed listeners or vfsmount listeners care */ + if (!(test_mask & fsnotify_inode_mask) && + !(test_mask & fsnotify_vfsmount_mask)) + return; + + /* if this inode's directed listeners don't care and nothing on the vfsmount + * listeners list cares, nothing to do */ + if (!(test_mask & to_tell->i_fsnotify_mask) && + !needed_by_vfsmount(test_mask, data, data_is)) + return; - if (!(test_mask & fsnotify_inode_mask)) - return; - - if (!(test_mask & to_tell->i_fsnotify_mask)) - return; /* * SRCU!! the groups list is very very much read only and the path is * very hot. The VAST majority of events are not going to need to do * anything other than walk the list so it's crazy to pre-allocate. */ idx = srcu_read_lock(&fsnotify_grp_srcu); - list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { - if (test_mask & group->mask) { - if (!group->ops->should_send_event(group, to_tell, mask, - data, data_is)) - continue; - if (!event) { - event = fsnotify_create_event(to_tell, mask, data, - data_is, file_name, cookie, - GFP_KERNEL); - /* shit, we OOM'd and now we can't tell, maybe - * someday someone else will want to do something - * here */ - if (!event) - break; + + if (test_mask & to_tell->i_fsnotify_mask) { + list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { + if (test_mask & group->mask) { + send_to_group(mask, group, data, data_is, + file_name, cookie, &event, to_tell); } - group->ops->handle_event(group, event); } } + if (needed_by_vfsmount(test_mask, data, data_is)) { + list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { + if (test_mask & group->mask) { + send_to_group(mask, group, data, data_is, + file_name, cookie, &event, to_tell); + } + } + } + srcu_read_unlock(&fsnotify_grp_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 5bd2241..2ba5915 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -10,14 +10,20 @@ extern struct srcu_struct fsnotify_grp_srcu; /* all groups which receive inode fsnotify events */ extern struct list_head fsnotify_inode_groups; +/* all groups which receive vfsmount fsnotify events */ +extern struct list_head fsnotify_vfsmount_groups; /* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ extern __u32 fsnotify_inode_mask; +/* all bitwise OR of all event types (FS_*) for all fsnotify_vfsmount_groups */ +extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); /* add a group to the inode group list */ extern void fsnotify_add_inode_group(struct fsnotify_group *group); +/* add a group to the vfsmount group list */ +extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 34fccbd..aa4654f 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -32,10 +32,14 @@ static DEFINE_MUTEX(fsnotify_grp_mutex); /* protects reads while running the fsnotify_groups list */ struct srcu_struct fsnotify_grp_srcu; -/* all groups registered to receive filesystem notifications */ +/* all groups registered to receive inode filesystem notifications */ LIST_HEAD(fsnotify_inode_groups); +/* all groups registered to receive mount point filesystem notifications */ +LIST_HEAD(fsnotify_vfsmount_groups); /* bitwise OR of all events (FS_*) interesting to some group on this system */ __u32 fsnotify_inode_mask; +/* bitwise OR of all events (FS_*) interesting to some group on this system */ +__u32 fsnotify_vfsmount_mask; /* * When a new group registers or changes it's set of interesting events @@ -44,14 +48,20 @@ __u32 fsnotify_inode_mask; void fsnotify_recalc_global_mask(void) { struct fsnotify_group *group; - __u32 mask = 0; + __u32 inode_mask = 0; + __u32 vfsmount_mask = 0; int idx; idx = srcu_read_lock(&fsnotify_grp_srcu); list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) - mask |= group->mask; + inode_mask |= group->mask; + list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) + vfsmount_mask |= group->mask; + srcu_read_unlock(&fsnotify_grp_srcu, idx); - fsnotify_inode_mask = mask; + + fsnotify_inode_mask = inode_mask; + fsnotify_vfsmount_mask = vfsmount_mask; } /* @@ -77,6 +87,17 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) fsnotify_recalc_global_mask(); } +void fsnotify_add_vfsmount_group(struct fsnotify_group *group) +{ + mutex_lock(&fsnotify_grp_mutex); + + if (!group->on_vfsmount_group_list) + list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups); + group->on_vfsmount_group_list = 1; + + mutex_unlock(&fsnotify_grp_mutex); +} + void fsnotify_add_inode_group(struct fsnotify_group *group) { mutex_lock(&fsnotify_grp_mutex); @@ -132,6 +153,9 @@ static void __fsnotify_evict_group(struct fsnotify_group *group) if (group->on_inode_group_list) list_del_rcu(&group->inode_group_list); group->on_inode_group_list = 0; + if (group->on_vfsmount_group_list) + list_del_rcu(&group->vfsmount_group_list); + group->on_vfsmount_group_list = 0; } /* @@ -197,6 +221,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) group->max_events = UINT_MAX; INIT_LIST_HEAD(&group->inode_group_list); + INIT_LIST_HEAD(&group->vfsmount_group_list); spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->mark_entries); diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index a3230c4..beffebb 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -328,6 +328,13 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, */ if (unlikely(list_empty(&group->inode_group_list))) fsnotify_add_inode_group(group); + /* + * XXX This is where we could also do the fsnotify_add_vfsmount_group + * if we are setting and vfsmount mark.... + + if (unlikely(list_empty(&group->vfsmount_group_list))) + fsnotify_add_vfsmount_group(group); + */ /* * LOCKING ORDER!!!! -- cgit v1.1 From 3a9fb89f4cd04c23e16397befba92efb5d989b74 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: include vfsmount in should_send_event when appropriate To ensure that a group will not duplicate events when it receives it based on the vfsmount and the inode should_send_event test we should distinguish those two cases. We pass a vfsmount to this function so groups can make their own determinations. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 ++-- fs/notify/fsnotify.c | 39 ++++++++++++++++++------------------ fs/notify/inotify/inotify_fsnotify.c | 3 ++- 3 files changed, 23 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e0a847b..9eddafa 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -133,8 +133,8 @@ static int dnotify_handle_event(struct fsnotify_group *group, * userspace notification for that pair. */ static bool dnotify_should_send_event(struct fsnotify_group *group, - struct inode *inode, __u32 mask, - void *data, int data_type) + struct inode *inode, struct vfsmount *mnt, + __u32 mask, void *data, int data_type) { struct fsnotify_mark_entry *entry; bool send; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index a61aaa7..78c440c 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -135,13 +135,12 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); -static void send_to_group(__u32 mask, - struct fsnotify_group *group, - void *data, int data_is, const char *file_name, - u32 cookie, struct fsnotify_event **event, - struct inode *to_tell) +static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, + struct vfsmount *mnt, __u32 mask, void *data, + int data_is, u32 cookie, const char *file_name, + struct fsnotify_event **event) { - if (!group->ops->should_send_event(group, to_tell, mask, + if (!group->ops->should_send_event(group, to_tell, mnt, mask, data, data_is)) return; if (!*event) { @@ -159,15 +158,9 @@ static void send_to_group(__u32 mask, group->ops->handle_event(group, *event); } -static bool needed_by_vfsmount(__u32 test_mask, void *data, int data_is) +static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) { - struct path *path; - - if (data_is == FSNOTIFY_EVENT_PATH) - path = (struct path *)data; - else if (data_is == FSNOTIFY_EVENT_FILE) - path = &((struct file *)data)->f_path; - else + if (!mnt) return false; /* hook in this when mnt->mnt_fsnotify_mask is defined */ @@ -184,6 +177,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const { struct fsnotify_group *group; struct fsnotify_event *event = NULL; + struct vfsmount *mnt = NULL; int idx; /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); @@ -198,10 +192,15 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const !(test_mask & fsnotify_vfsmount_mask)) return; + if (data_is == FSNOTIFY_EVENT_PATH) + mnt = ((struct path *)data)->mnt; + else if (data_is == FSNOTIFY_EVENT_FILE) + mnt = ((struct file *)data)->f_path.mnt; + /* if this inode's directed listeners don't care and nothing on the vfsmount * listeners list cares, nothing to do */ if (!(test_mask & to_tell->i_fsnotify_mask) && - !needed_by_vfsmount(test_mask, data, data_is)) + !needed_by_vfsmount(test_mask, mnt)) return; /* @@ -214,16 +213,16 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const if (test_mask & to_tell->i_fsnotify_mask) { list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { if (test_mask & group->mask) { - send_to_group(mask, group, data, data_is, - file_name, cookie, &event, to_tell); + send_to_group(group, to_tell, NULL, mask, data, data_is, + cookie, file_name, &event); } } } - if (needed_by_vfsmount(test_mask, data, data_is)) { + if (needed_by_vfsmount(test_mask, mnt)) { list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { if (test_mask & group->mask) { - send_to_group(mask, group, data, data_is, - file_name, cookie, &event, to_tell); + send_to_group(group, to_tell, mnt, mask, data, data_is, + cookie, file_name, &event); } } } diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 0a0f5d0..8075ae7 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -141,7 +141,8 @@ static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnot } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - __u32 mask, void *data, int data_type) + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) { struct fsnotify_mark_entry *entry; bool send; -- cgit v1.1 From 2823e04de4f1a49087b58ff2bb8f61361ffd9321 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:23 -0500 Subject: fsnotify: put inode specific fields in an fsnotify_mark in a union The addition of marks on vfs mounts will be simplified if the inode specific parts of a mark and the vfsmnt specific parts of a mark are actually in a union so naming can be easy. This patch just implements the inode struct and the union. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 ++-- fs/notify/inode_mark.c | 28 ++++++++++++++-------------- fs/notify/inotify/inotify_fsnotify.c | 2 +- fs/notify/inotify/inotify_user.c | 10 +++++----- 4 files changed, 22 insertions(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 9eddafa..fc3a9dc 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -70,8 +70,8 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark_entry *entry) if (old_mask == new_mask) return; - if (entry->inode) - fsnotify_recalc_inode_mask(entry->inode); + if (entry->i.inode) + fsnotify_recalc_inode_mask(entry->i.inode); } /* diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index beffebb..6731408 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -117,7 +117,7 @@ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i_list) + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) new_mask |= entry->mask; inode->i_fsnotify_mask = new_mask; } @@ -148,7 +148,7 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) spin_lock(&entry->lock); group = entry->group; - inode = entry->inode; + inode = entry->i.inode; BUG_ON(group && !inode); BUG_ON(!group && inode); @@ -165,8 +165,8 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - hlist_del_init(&entry->i_list); - entry->inode = NULL; + hlist_del_init(&entry->i.i_list); + entry->i.inode = NULL; list_del_init(&entry->g_list); entry->group = NULL; @@ -248,14 +248,14 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i_list) { - list_add(&entry->free_i_list, &free_list); - hlist_del_init(&entry->i_list); + hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i.i_list) { + list_add(&entry->i.free_i_list, &free_list); + hlist_del_init(&entry->i.i_list); fsnotify_get_mark(entry); } spin_unlock(&inode->i_lock); - list_for_each_entry_safe(entry, lentry, &free_list, free_i_list) { + list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { fsnotify_destroy_mark_by_entry(entry); fsnotify_put_mark(entry); } @@ -273,7 +273,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i_list) { + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) { if (entry->group == group) { fsnotify_get_mark(entry); return entry; @@ -285,7 +285,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) { assert_spin_locked(&old->lock); - new->inode = old->inode; + new->i.inode = old->i.inode; new->group = old->group; new->mask = old->mask; new->free_mark = old->free_mark; @@ -299,10 +299,10 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); - INIT_HLIST_NODE(&entry->i_list); + INIT_HLIST_NODE(&entry->i.i_list); entry->group = NULL; entry->mask = 0; - entry->inode = NULL; + entry->i.inode = NULL; entry->free_mark = free_mark; } @@ -350,9 +350,9 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, lentry = fsnotify_find_mark_entry(group, inode); if (!lentry) { entry->group = group; - entry->inode = inode; + entry->i.inode = inode; - hlist_add_head(&entry->i_list, &inode->i_fsnotify_mark_entries); + hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_mark_entries); list_add(&entry->g_list, &group->mark_entries); fsnotify_get_mark(entry); /* for i_list and g_list */ diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 8075ae7..3edb51c 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -193,7 +193,7 @@ static int idr_callback(int id, void *p, void *data) */ if (entry) printk(KERN_WARNING "entry->group=%p inode=%p wd=%d\n", - entry->group, entry->inode, ientry->wd); + entry->group, entry->i.inode, ientry->wd); return 0; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a48d68a..4b1587f 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -445,7 +445,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, if (wd == -1) { WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.inode); + ientry->fsn_entry.group, ientry->fsn_entry.i.inode); goto out; } @@ -454,7 +454,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, if (unlikely(!found_ientry)) { WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.inode); + ientry->fsn_entry.group, ientry->fsn_entry.i.inode); goto out; } @@ -468,9 +468,9 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, "entry->inode=%p found_ientry=%p found_ientry->wd=%d " "found_ientry->group=%p found_ientry->inode=%p\n", __func__, ientry, ientry->wd, ientry->fsn_entry.group, - ientry->fsn_entry.inode, found_ientry, found_ientry->wd, + ientry->fsn_entry.i.inode, found_ientry, found_ientry->wd, found_ientry->fsn_entry.group, - found_ientry->fsn_entry.inode); + found_ientry->fsn_entry.i.inode); goto out; } @@ -482,7 +482,7 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { printk(KERN_ERR "%s: ientry=%p ientry->wd=%d ientry->group=%p" " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.inode); + ientry->fsn_entry.group, ientry->fsn_entry.i.inode); /* we can't really recover with bad ref cnting.. */ BUG(); } -- cgit v1.1 From 098cf2fc77ee190c92bf9d08d69a13305f2487ec Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: add flags to fsnotify_mark_entries To differentiate between inode and vfsmount (or other future) types of marks we add a flags field and set the inode bit on inode marks (the only currently supported type of mark) Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 6731408..b000658 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -322,6 +322,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, if (unlikely(!inode)) return -EINVAL; + entry->flags = FSNOTIFY_MARK_FLAG_INODE; + /* * if this group isn't being testing for inode type events we need * to start testing -- cgit v1.1 From 72acc854427948efed7a83da27f7dc3239ac9afc Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: kill FSNOTIFY_EVENT_FILE Some fsnotify operations send a struct file. This is more information than we technically need. We instead send a struct path in all cases instead of sometimes a path and sometimes a file. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 12 +++++------- fs/notify/notification.c | 9 --------- 2 files changed, 5 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 78c440c..60e84fd 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -79,15 +79,15 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) } /* Notify this dentry's parent about a child's events. */ -void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) +void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) { struct dentry *parent; struct inode *p_inode; bool send = false; bool should_update_children = false; - if (file) - dentry = file->f_path.dentry; + if (!dentry) + dentry = path->dentry; if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) return; @@ -119,8 +119,8 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; - if (file) - fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, + if (path) + fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, dentry->d_name.name, 0); else fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, @@ -194,8 +194,6 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const if (data_is == FSNOTIFY_EVENT_PATH) mnt = ((struct path *)data)->mnt; - else if (data_is == FSNOTIFY_EVENT_FILE) - mnt = ((struct file *)data)->f_path.mnt; /* if this inode's directed listeners don't care and nothing on the vfsmount * listeners list cares, nothing to do */ diff --git a/fs/notify/notification.c b/fs/notify/notification.c index dafd0b7..066f1f9 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -390,15 +390,6 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->data_type = data_type; switch (data_type) { - case FSNOTIFY_EVENT_FILE: { - struct file *file = data; - struct path *path = &file->f_path; - event->path.dentry = path->dentry; - event->path.mnt = path->mnt; - path_get(&event->path); - event->data_type = FSNOTIFY_EVENT_PATH; - break; - } case FSNOTIFY_EVENT_PATH: { struct path *path = data; event->path.dentry = path->dentry; -- cgit v1.1 From e61ce86737b4d60521e4e71f9892fe4bdcfb688b Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename fsnotify_mark_entry to just fsnotify_mark The name is long and it serves no real purpose. So rename fsnotify_mark_entry to just fsnotify_mark. Signed-off-by: Eric Paris --- fs/inode.c | 2 +- fs/notify/dnotify/dnotify.c | 24 +++++++++--------- fs/notify/group.c | 8 +++--- fs/notify/inode_mark.c | 48 ++++++++++++++++++------------------ fs/notify/inotify/inotify.h | 6 ++--- fs/notify/inotify/inotify_fsnotify.c | 8 +++--- fs/notify/inotify/inotify_user.c | 8 +++--- 7 files changed, 52 insertions(+), 52 deletions(-) (limited to 'fs') diff --git a/fs/inode.c b/fs/inode.c index 8e1bee9..a2da778 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -264,7 +264,7 @@ void inode_init_once(struct inode *inode) INIT_LIST_HEAD(&inode->i_data.i_mmap_nonlinear); i_size_ordered_init(inode); #ifdef CONFIG_FSNOTIFY - INIT_HLIST_HEAD(&inode->i_fsnotify_mark_entries); + INIT_HLIST_HEAD(&inode->i_fsnotify_marks); #endif } EXPORT_SYMBOL(inode_init_once); diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index fc3a9dc..e6edae6 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -34,12 +34,12 @@ static struct fsnotify_group *dnotify_group __read_mostly; static DEFINE_MUTEX(dnotify_mark_mutex); /* - * dnotify will attach one of these to each inode (i_fsnotify_mark_entries) which + * dnotify will attach one of these to each inode (i_fsnotify_marks) which * is being watched by dnotify. If multiple userspace applications are watching * the same directory with dnotify their information is chained in dn */ struct dnotify_mark_entry { - struct fsnotify_mark_entry fsn_entry; + struct fsnotify_mark fsn_entry; struct dnotify_struct *dn; }; @@ -51,7 +51,7 @@ struct dnotify_mark_entry { * it calls the fsnotify function so it can update the set of all events relevant * to this inode. */ -static void dnotify_recalc_inode_mask(struct fsnotify_mark_entry *entry) +static void dnotify_recalc_inode_mask(struct fsnotify_mark *entry) { __u32 new_mask, old_mask; struct dnotify_struct *dn; @@ -85,7 +85,7 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark_entry *entry) static int dnotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark_entry *entry = NULL; + struct fsnotify_mark *entry = NULL; struct dnotify_mark_entry *dnentry; struct inode *to_tell; struct dnotify_struct *dn; @@ -136,7 +136,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; bool send; /* !dir_notify_enable should never get here, don't waste time checking @@ -163,7 +163,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, return send; } -static void dnotify_free_mark(struct fsnotify_mark_entry *entry) +static void dnotify_free_mark(struct fsnotify_mark *entry) { struct dnotify_mark_entry *dnentry = container_of(entry, struct dnotify_mark_entry, @@ -184,14 +184,14 @@ static struct fsnotify_ops dnotify_fsnotify_ops = { /* * Called every time a file is closed. Looks first for a dnotify mark on the - * inode. If one is found run all of the ->dn entries attached to that + * inode. If one is found run all of the ->dn structures attached to that * mark for one relevant to this process closing the file and remove that * dnotify_struct. If that was the last dnotify_struct also remove the - * fsnotify_mark_entry. + * fsnotify_mark. */ void dnotify_flush(struct file *filp, fl_owner_t id) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct dnotify_mark_entry *dnentry; struct dnotify_struct *dn; struct dnotify_struct **prev; @@ -260,7 +260,7 @@ static __u32 convert_arg(unsigned long arg) /* * If multiple processes watch the same inode with dnotify there is only one - * dnotify mark in inode->i_fsnotify_mark_entries but we chain a dnotify_struct + * dnotify mark in inode->i_fsnotify_marks but we chain a dnotify_struct * onto that mark. This function either attaches the new dnotify_struct onto * that list, or it |= the mask onto an existing dnofiy_struct. */ @@ -298,7 +298,7 @@ static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnent int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) { struct dnotify_mark_entry *new_dnentry, *dnentry; - struct fsnotify_mark_entry *new_entry, *entry; + struct fsnotify_mark *new_entry, *entry; struct dnotify_struct *dn; struct inode *inode; fl_owner_t id = current->files; @@ -378,7 +378,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* if (f != filp) means that we lost a race and another task/thread * actually closed the fd we are still playing with before we grabbed * the dnotify_mark_mutex and entry->lock. Since closing the fd is the - * only time we clean up the mark entries we need to get our mark off + * only time we clean up the marks we need to get our mark off * the list. */ if (f != filp) { /* if we added ourselves, shoot ourselves, it's possible that diff --git a/fs/notify/group.c b/fs/notify/group.c index aa4654f..b70e7d2 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -74,10 +74,10 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) { __u32 mask = 0; __u32 old_mask = group->mask; - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; spin_lock(&group->mark_lock); - list_for_each_entry(entry, &group->mark_entries, g_list) + list_for_each_entry(entry, &group->marks_list, g_list) mask |= entry->mask; spin_unlock(&group->mark_lock); @@ -133,7 +133,7 @@ void fsnotify_final_destroy_group(struct fsnotify_group *group) */ static void fsnotify_destroy_group(struct fsnotify_group *group) { - /* clear all inode mark entries for this group */ + /* clear all inode marks for this group */ fsnotify_clear_marks_by_group(group); /* past the point of no return, matches the initial value of 1 */ @@ -224,7 +224,7 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) INIT_LIST_HEAD(&group->vfsmount_group_list); spin_lock_init(&group->mark_lock); - INIT_LIST_HEAD(&group->mark_entries); + INIT_LIST_HEAD(&group->marks_list); group->ops = ops; diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index b000658..7e69f6b 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -38,12 +38,12 @@ * that lock to dereference either of these things (they could be NULL even with * the lock) * - * group->mark_lock protects the mark_entries list anchored inside a given group + * group->mark_lock protects the marks_list anchored inside a given group * and each entry is hooked via the g_list. It also sorta protects the * free_g_list, which when used is anchored by a private list on the stack of the * task which held the group->mark_lock. * - * inode->i_lock protects the i_fsnotify_mark_entries list anchored inside a + * inode->i_lock protects the i_fsnotify_marks list anchored inside a * given inode and each entry is hooked via the i_list. (and sorta the * free_i_list) * @@ -61,7 +61,7 @@ * need to be cleaned up. (fsnotify_clear_marks_by_group) * * Worst case we are given an inode and need to clean up all the marks on that - * inode. We take i_lock and walk the i_fsnotify_mark_entries safely. For each + * inode. We take i_lock and walk the i_fsnotify_marks safely. For each * mark on the list we take a reference (so the mark can't disappear under us). * We remove that mark form the inode's list of marks and we add this mark to a * private list anchored on the stack using i_free_list; At this point we no @@ -95,12 +95,12 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark_entry *entry) +void fsnotify_get_mark(struct fsnotify_mark *entry) { atomic_inc(&entry->refcnt); } -void fsnotify_put_mark(struct fsnotify_mark_entry *entry) +void fsnotify_put_mark(struct fsnotify_mark *entry) { if (atomic_dec_and_test(&entry->refcnt)) entry->free_mark(entry); @@ -111,13 +111,13 @@ void fsnotify_put_mark(struct fsnotify_mark_entry *entry) */ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct hlist_node *pos; __u32 new_mask = 0; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) new_mask |= entry->mask; inode->i_fsnotify_mask = new_mask; } @@ -140,7 +140,7 @@ void fsnotify_recalc_inode_mask(struct inode *inode) * The caller had better be holding a reference to this mark so we don't actually * do the final put under the entry->lock */ -void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) +void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry) { struct fsnotify_group *group; struct inode *inode; @@ -174,7 +174,7 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) fsnotify_put_mark(entry); /* for i_list and g_list */ /* - * this mark is now off the inode->i_fsnotify_mark_entries list and we + * this mark is now off the inode->i_fsnotify_marks list and we * hold the inode->i_lock, so this is the perfect time to update the * inode->i_fsnotify_mask */ @@ -221,11 +221,11 @@ void fsnotify_destroy_mark_by_entry(struct fsnotify_mark_entry *entry) */ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) { - struct fsnotify_mark_entry *lentry, *entry; + struct fsnotify_mark *lentry, *entry; LIST_HEAD(free_list); spin_lock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &group->mark_entries, g_list) { + list_for_each_entry_safe(entry, lentry, &group->marks_list, g_list) { list_add(&entry->free_g_list, &free_list); list_del_init(&entry->g_list); fsnotify_get_mark(entry); @@ -243,12 +243,12 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) */ void fsnotify_clear_marks_by_inode(struct inode *inode) { - struct fsnotify_mark_entry *entry, *lentry; + struct fsnotify_mark *entry, *lentry; struct hlist_node *pos, *n; LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_mark_entries, i.i_list) { + hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_marks, i.i_list) { list_add(&entry->i.free_i_list, &free_list); hlist_del_init(&entry->i.i_list); fsnotify_get_mark(entry); @@ -265,15 +265,15 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, + struct inode *inode) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct hlist_node *pos; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_mark_entries, i.i_list) { + hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) { if (entry->group == group) { fsnotify_get_mark(entry); return entry; @@ -282,7 +282,7 @@ struct fsnotify_mark_entry *fsnotify_find_mark_entry(struct fsnotify_group *grou return NULL; } -void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_mark_entry *old) +void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); new->i.inode = old->i.inode; @@ -294,8 +294,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark_entry *new, struct fsnotify_ma /* * Nothing fancy, just initialize lists and locks and counters. */ -void fsnotify_init_mark(struct fsnotify_mark_entry *entry, - void (*free_mark)(struct fsnotify_mark_entry *entry)) +void fsnotify_init_mark(struct fsnotify_mark *entry, + void (*free_mark)(struct fsnotify_mark *entry)) { spin_lock_init(&entry->lock); atomic_set(&entry->refcnt, 1); @@ -311,11 +311,11 @@ void fsnotify_init_mark(struct fsnotify_mark_entry *entry, * These marks may be used for the fsnotify backend to determine which * event types should be delivered to which group and for which inodes. */ -int fsnotify_add_mark(struct fsnotify_mark_entry *entry, +int fsnotify_add_mark(struct fsnotify_mark *entry, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark_entry *lentry = NULL; + struct fsnotify_mark *lentry = NULL; int ret = 0; inode = igrab(inode); @@ -354,8 +354,8 @@ int fsnotify_add_mark(struct fsnotify_mark_entry *entry, entry->group = group; entry->i.inode = inode; - hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_mark_entries); - list_add(&entry->g_list, &group->mark_entries); + hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_marks); + list_add(&entry->g_list, &group->marks_list); fsnotify_get_mark(entry); /* for i_list and g_list */ diff --git a/fs/notify/inotify/inotify.h b/fs/notify/inotify/inotify.h index f234f3a..07be6df 100644 --- a/fs/notify/inotify/inotify.h +++ b/fs/notify/inotify/inotify.h @@ -10,12 +10,12 @@ struct inotify_event_private_data { }; struct inotify_inode_mark_entry { - /* fsnotify_mark_entry MUST be the first thing */ - struct fsnotify_mark_entry fsn_entry; + /* fsnotify_mark MUST be the first thing */ + struct fsnotify_mark fsn_entry; int wd; }; -extern void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, +extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, struct fsnotify_group *group); extern void inotify_free_event_priv(struct fsnotify_event_private_data *event_priv); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 3edb51c..f33a9bd 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -88,7 +88,7 @@ static int inotify_merge(struct list_head *list, struct fsnotify_event *event) static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct inotify_inode_mark_entry *ientry; struct inode *to_tell; struct inotify_event_private_data *event_priv; @@ -135,7 +135,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev return ret; } -static void inotify_freeing_mark(struct fsnotify_mark_entry *entry, struct fsnotify_group *group) +static void inotify_freeing_mark(struct fsnotify_mark *entry, struct fsnotify_group *group) { inotify_ignored_and_remove_idr(entry, group); } @@ -144,7 +144,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; bool send; spin_lock(&inode->i_lock); @@ -171,7 +171,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode */ static int idr_callback(int id, void *p, void *data) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct inotify_inode_mark_entry *ientry; static bool warned = false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 4b1587f..7be5dcf 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -386,7 +386,7 @@ static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_ ientry = idr_find(idr, wd); if (ientry) { - struct fsnotify_mark_entry *fsn_entry = &ientry->fsn_entry; + struct fsnotify_mark *fsn_entry = &ientry->fsn_entry; fsnotify_get_mark(fsn_entry); /* One ref for being in the idr, one ref we just took */ @@ -499,7 +499,7 @@ out: /* * Send IN_IGNORED for this wd, remove this wd from the idr. */ -void inotify_ignored_and_remove_idr(struct fsnotify_mark_entry *entry, +void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, struct fsnotify_group *group) { struct inotify_inode_mark_entry *ientry; @@ -541,7 +541,7 @@ skip_send_ignore: } /* ding dong the mark is dead */ -static void inotify_free_mark(struct fsnotify_mark_entry *entry) +static void inotify_free_mark(struct fsnotify_mark *entry) { struct inotify_inode_mark_entry *ientry; @@ -554,7 +554,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, struct inode *inode, u32 arg) { - struct fsnotify_mark_entry *entry; + struct fsnotify_mark *entry; struct inotify_inode_mark_entry *ientry; __u32 old_mask, new_mask; __u32 mask; -- cgit v1.1 From d07754412f9cdc2f4a99318d5ee81ace6715ea99 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename fsnotify_find_mark_entry to fsnotify_find_mark the _entry portion of fsnotify functions is useless. Drop it. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 14 +++++++------- fs/notify/inode_mark.c | 14 +++++++------- fs/notify/inotify/inotify_fsnotify.c | 4 ++-- fs/notify/inotify/inotify_user.c | 6 +++--- 4 files changed, 19 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e6edae6..b202bc5 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -96,7 +96,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark_entry(group, to_tell); + entry = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* unlikely since we alreay passed dnotify_should_send_event() */ @@ -148,7 +148,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, return false; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(group, inode); + entry = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); /* no mark means no dnotify watch */ @@ -158,7 +158,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, mask = (mask & ~FS_EVENT_ON_CHILD); send = (mask & entry->mask); - fsnotify_put_mark(entry); /* matches fsnotify_find_mark_entry */ + fsnotify_put_mark(entry); /* matches fsnotify_find_mark */ return send; } @@ -202,7 +202,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) return; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(dnotify_group, inode); + entry = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); if (!entry) return; @@ -226,7 +226,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) /* nothing else could have found us thanks to the dnotify_mark_mutex */ if (dnentry->dn == NULL) - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_recalc_group_mask(dnotify_group); @@ -357,7 +357,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* add the new_entry or find an old one. */ spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(dnotify_group, inode); + entry = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); if (entry) { dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); @@ -414,7 +414,7 @@ out: spin_unlock(&entry->lock); if (destroy) - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_recalc_group_mask(dnotify_group); diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 7e69f6b..01c4263 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -56,7 +56,7 @@ * - The inode is unlinked for the last time. (fsnotify_inode_remove) * - The inode is being evicted from cache. (fsnotify_inode_delete) * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) - * - Something explicitly requests that it be removed. (fsnotify_destroy_mark_by_entry) + * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) * - The fsnotify_group associated with the mark is going away and all such marks * need to be cleaned up. (fsnotify_clear_marks_by_group) * @@ -140,7 +140,7 @@ void fsnotify_recalc_inode_mask(struct inode *inode) * The caller had better be holding a reference to this mark so we don't actually * do the final put under the entry->lock */ -void fsnotify_destroy_mark_by_entry(struct fsnotify_mark *entry) +void fsnotify_destroy_mark(struct fsnotify_mark *entry) { struct fsnotify_group *group; struct inode *inode; @@ -233,7 +233,7 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) spin_unlock(&group->mark_lock); list_for_each_entry_safe(entry, lentry, &free_list, free_g_list) { - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); } } @@ -256,7 +256,7 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) spin_unlock(&inode->i_lock); list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { - fsnotify_destroy_mark_by_entry(entry); + fsnotify_destroy_mark(entry); fsnotify_put_mark(entry); } } @@ -265,8 +265,8 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark *fsnotify_find_mark_entry(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *entry; struct hlist_node *pos; @@ -349,7 +349,7 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, spin_lock(&inode->i_lock); if (!allow_dups) - lentry = fsnotify_find_mark_entry(group, inode); + lentry = fsnotify_find_mark(group, inode); if (!lentry) { entry->group = group; entry->i.inode = inode; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index f33a9bd..f8a2a6e 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -98,7 +98,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark_entry(group, to_tell); + entry = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* race with watch removal? We already passes should_send */ if (unlikely(!entry)) @@ -148,7 +148,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode bool send; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(group, inode); + entry = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); if (!entry) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 7be5dcf..118085c 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -567,7 +567,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, return -EINVAL; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark_entry(group, inode); + entry = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); if (!entry) return -ENOENT; @@ -607,7 +607,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, /* return the wd */ ret = ientry->wd; - /* match the get from fsnotify_find_mark_entry() */ + /* match the get from fsnotify_find_mark() */ fsnotify_put_mark(entry); return ret; @@ -823,7 +823,7 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) ret = 0; - fsnotify_destroy_mark_by_entry(&ientry->fsn_entry); + fsnotify_destroy_mark(&ientry->fsn_entry); /* match ref taken by inotify_idr_find */ fsnotify_put_mark(&ientry->fsn_entry); -- cgit v1.1 From 841bdc10f573aa010dd5818d35a5690b7d9f73ce Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: fsnotify: rename mark_entry to just mark previously I used mark_entry when talking about marks on inodes. The _entry is pretty useless. Just use "mark" instead. Signed-off-by: Eric Paris --- fs/notify/group.c | 6 +- fs/notify/inode_mark.c | 148 ++++++++++++++++++++++++------------------------- 2 files changed, 77 insertions(+), 77 deletions(-) (limited to 'fs') diff --git a/fs/notify/group.c b/fs/notify/group.c index b70e7d2..9e9eb40 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -74,11 +74,11 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) { __u32 mask = 0; __u32 old_mask = group->mask; - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; spin_lock(&group->mark_lock); - list_for_each_entry(entry, &group->marks_list, g_list) - mask |= entry->mask; + list_for_each_entry(mark, &group->marks_list, g_list) + mask |= mark->mask; spin_unlock(&group->mark_lock); group->mask = mask; diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 01c4263..27c1b43 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -30,21 +30,21 @@ * There are 3 spinlocks involved with fsnotify inode marks and they MUST * be taken in order as follows: * - * entry->lock + * mark->lock * group->mark_lock * inode->i_lock * - * entry->lock protects 2 things, entry->group and entry->inode. You must hold + * mark->lock protects 2 things, mark->group and mark->inode. You must hold * that lock to dereference either of these things (they could be NULL even with * the lock) * * group->mark_lock protects the marks_list anchored inside a given group - * and each entry is hooked via the g_list. It also sorta protects the + * and each mark is hooked via the g_list. It also sorta protects the * free_g_list, which when used is anchored by a private list on the stack of the * task which held the group->mark_lock. * * inode->i_lock protects the i_fsnotify_marks list anchored inside a - * given inode and each entry is hooked via the i_list. (and sorta the + * given inode and each mark is hooked via the i_list. (and sorta the * free_i_list) * * @@ -95,15 +95,15 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark *entry) +void fsnotify_get_mark(struct fsnotify_mark *mark) { - atomic_inc(&entry->refcnt); + atomic_inc(&mark->refcnt); } -void fsnotify_put_mark(struct fsnotify_mark *entry) +void fsnotify_put_mark(struct fsnotify_mark *mark) { - if (atomic_dec_and_test(&entry->refcnt)) - entry->free_mark(entry); + if (atomic_dec_and_test(&mark->refcnt)) + mark->free_mark(mark); } /* @@ -111,14 +111,14 @@ void fsnotify_put_mark(struct fsnotify_mark *entry) */ static void fsnotify_recalc_inode_mask_locked(struct inode *inode) { - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; struct hlist_node *pos; __u32 new_mask = 0; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) - new_mask |= entry->mask; + hlist_for_each_entry(mark, pos, &inode->i_fsnotify_marks, i.i_list) + new_mask |= mark->mask; inode->i_fsnotify_mask = new_mask; } @@ -138,40 +138,40 @@ void fsnotify_recalc_inode_mask(struct inode *inode) /* * Any time a mark is getting freed we end up here. * The caller had better be holding a reference to this mark so we don't actually - * do the final put under the entry->lock + * do the final put under the mark->lock */ -void fsnotify_destroy_mark(struct fsnotify_mark *entry) +void fsnotify_destroy_mark(struct fsnotify_mark *mark) { struct fsnotify_group *group; struct inode *inode; - spin_lock(&entry->lock); + spin_lock(&mark->lock); - group = entry->group; - inode = entry->i.inode; + group = mark->group; + inode = mark->i.inode; BUG_ON(group && !inode); BUG_ON(!group && inode); /* if !group something else already marked this to die */ if (!group) { - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); return; } /* 1 from caller and 1 for being on i_list/g_list */ - BUG_ON(atomic_read(&entry->refcnt) < 2); + BUG_ON(atomic_read(&mark->refcnt) < 2); spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); - hlist_del_init(&entry->i.i_list); - entry->i.inode = NULL; + hlist_del_init(&mark->i.i_list); + mark->i.inode = NULL; - list_del_init(&entry->g_list); - entry->group = NULL; + list_del_init(&mark->g_list); + mark->group = NULL; - fsnotify_put_mark(entry); /* for i_list and g_list */ + fsnotify_put_mark(mark); /* for i_list and g_list */ /* * this mark is now off the inode->i_fsnotify_marks list and we @@ -182,21 +182,21 @@ void fsnotify_destroy_mark(struct fsnotify_mark *entry) spin_unlock(&inode->i_lock); spin_unlock(&group->mark_lock); - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); /* * Some groups like to know that marks are being freed. This is a - * callback to the group function to let it know that this entry + * callback to the group function to let it know that this mark * is being freed. */ if (group->ops->freeing_mark) - group->ops->freeing_mark(entry, group); + group->ops->freeing_mark(mark, group); /* * __fsnotify_update_child_dentry_flags(inode); * * I really want to call that, but we can't, we have no idea if the inode - * still exists the second we drop the entry->lock. + * still exists the second we drop the mark->lock. * * The next time an event arrive to this inode from one of it's children * __fsnotify_parent will see that the inode doesn't care about it's @@ -221,20 +221,20 @@ void fsnotify_destroy_mark(struct fsnotify_mark *entry) */ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) { - struct fsnotify_mark *lentry, *entry; + struct fsnotify_mark *lmark, *mark; LIST_HEAD(free_list); spin_lock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &group->marks_list, g_list) { - list_add(&entry->free_g_list, &free_list); - list_del_init(&entry->g_list); - fsnotify_get_mark(entry); + list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { + list_add(&mark->free_g_list, &free_list); + list_del_init(&mark->g_list); + fsnotify_get_mark(mark); } spin_unlock(&group->mark_lock); - list_for_each_entry_safe(entry, lentry, &free_list, free_g_list) { - fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); + list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); } } @@ -243,21 +243,21 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) */ void fsnotify_clear_marks_by_inode(struct inode *inode) { - struct fsnotify_mark *entry, *lentry; + struct fsnotify_mark *mark, *lmark; struct hlist_node *pos, *n; LIST_HEAD(free_list); spin_lock(&inode->i_lock); - hlist_for_each_entry_safe(entry, pos, n, &inode->i_fsnotify_marks, i.i_list) { - list_add(&entry->i.free_i_list, &free_list); - hlist_del_init(&entry->i.i_list); - fsnotify_get_mark(entry); + hlist_for_each_entry_safe(mark, pos, n, &inode->i_fsnotify_marks, i.i_list) { + list_add(&mark->i.free_i_list, &free_list); + hlist_del_init(&mark->i.i_list); + fsnotify_get_mark(mark); } spin_unlock(&inode->i_lock); - list_for_each_entry_safe(entry, lentry, &free_list, i.free_i_list) { - fsnotify_destroy_mark(entry); - fsnotify_put_mark(entry); + list_for_each_entry_safe(mark, lmark, &free_list, i.free_i_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); } } @@ -268,15 +268,15 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, struct inode *inode) { - struct fsnotify_mark *entry; + struct fsnotify_mark *mark; struct hlist_node *pos; assert_spin_locked(&inode->i_lock); - hlist_for_each_entry(entry, pos, &inode->i_fsnotify_marks, i.i_list) { - if (entry->group == group) { - fsnotify_get_mark(entry); - return entry; + hlist_for_each_entry(mark, pos, &inode->i_fsnotify_marks, i.i_list) { + if (mark->group == group) { + fsnotify_get_mark(mark); + return mark; } } return NULL; @@ -294,35 +294,35 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol /* * Nothing fancy, just initialize lists and locks and counters. */ -void fsnotify_init_mark(struct fsnotify_mark *entry, - void (*free_mark)(struct fsnotify_mark *entry)) +void fsnotify_init_mark(struct fsnotify_mark *mark, + void (*free_mark)(struct fsnotify_mark *mark)) { - spin_lock_init(&entry->lock); - atomic_set(&entry->refcnt, 1); - INIT_HLIST_NODE(&entry->i.i_list); - entry->group = NULL; - entry->mask = 0; - entry->i.inode = NULL; - entry->free_mark = free_mark; + spin_lock_init(&mark->lock); + atomic_set(&mark->refcnt, 1); + INIT_HLIST_NODE(&mark->i.i_list); + mark->group = NULL; + mark->mask = 0; + mark->i.inode = NULL; + mark->free_mark = free_mark; } /* - * Attach an initialized mark entry to a given group and inode. + * Attach an initialized mark 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. */ -int fsnotify_add_mark(struct fsnotify_mark *entry, +int fsnotify_add_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark *lentry = NULL; + struct fsnotify_mark *lmark = NULL; int ret = 0; inode = igrab(inode); if (unlikely(!inode)) return -EINVAL; - entry->flags = FSNOTIFY_MARK_FLAG_INODE; + mark->flags = FSNOTIFY_MARK_FLAG_INODE; /* * if this group isn't being testing for inode type events we need @@ -340,24 +340,24 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, /* * LOCKING ORDER!!!! - * entry->lock + * mark->lock * group->mark_lock * inode->i_lock */ - spin_lock(&entry->lock); + spin_lock(&mark->lock); spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); if (!allow_dups) - lentry = fsnotify_find_mark(group, inode); - if (!lentry) { - entry->group = group; - entry->i.inode = inode; + lmark = fsnotify_find_mark(group, inode); + if (!lmark) { + mark->group = group; + mark->i.inode = inode; - hlist_add_head(&entry->i.i_list, &inode->i_fsnotify_marks); - list_add(&entry->g_list, &group->marks_list); + hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + list_add(&mark->g_list, &group->marks_list); - fsnotify_get_mark(entry); /* for i_list and g_list */ + fsnotify_get_mark(mark); /* for i_list and g_list */ atomic_inc(&group->num_marks); @@ -366,12 +366,12 @@ int fsnotify_add_mark(struct fsnotify_mark *entry, spin_unlock(&inode->i_lock); spin_unlock(&group->mark_lock); - spin_unlock(&entry->lock); + spin_unlock(&mark->lock); - if (lentry) { + if (lmark) { ret = -EEXIST; iput(inode); - fsnotify_put_mark(lentry); + fsnotify_put_mark(lmark); } else { __fsnotify_update_child_dentry_flags(inode); } -- cgit v1.1 From 000285deb99a5e0636fdd3c6a2483a5d039ee2c2 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: inotify: rename mark_entry to just mark rename anything in inotify that deals with mark_entry to just be mark. It makes a lot more sense. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify.h | 7 +- fs/notify/inotify/inotify_fsnotify.c | 48 ++++----- fs/notify/inotify/inotify_user.c | 192 +++++++++++++++++------------------ 3 files changed, 123 insertions(+), 124 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify.h b/fs/notify/inotify/inotify.h index 07be6df..b6642e4 100644 --- a/fs/notify/inotify/inotify.h +++ b/fs/notify/inotify/inotify.h @@ -9,13 +9,12 @@ struct inotify_event_private_data { int wd; }; -struct inotify_inode_mark_entry { - /* fsnotify_mark MUST be the first thing */ - struct fsnotify_mark fsn_entry; +struct inotify_inode_mark { + struct fsnotify_mark fsn_mark; int wd; }; -extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, +extern void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group); extern void inotify_free_event_priv(struct fsnotify_event_private_data *event_priv); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index f8a2a6e..12dc72b 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -88,8 +88,8 @@ static int inotify_merge(struct list_head *list, struct fsnotify_event *event) static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark *entry; - struct inotify_inode_mark_entry *ientry; + struct fsnotify_mark *fsn_mark; + struct inotify_inode_mark *i_mark; struct inode *to_tell; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; @@ -98,14 +98,14 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* race with watch removal? We already passes should_send */ - if (unlikely(!entry)) + if (unlikely(!fsn_mark)) return 0; - ientry = container_of(entry, struct inotify_inode_mark_entry, - fsn_entry); - wd = ientry->wd; + i_mark = container_of(fsn_mark, struct inotify_inode_mark, + fsn_mark); + wd = i_mark->wd; event_priv = kmem_cache_alloc(event_priv_cachep, GFP_KERNEL); if (unlikely(!event_priv)) @@ -127,37 +127,37 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev } /* - * If we hold the entry until after the event is on the queue + * If we hold the fsn_mark until after the event is on the queue * IN_IGNORED won't be able to pass this event in the queue */ - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); return ret; } -static void inotify_freeing_mark(struct fsnotify_mark *entry, struct fsnotify_group *group) +static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) { - inotify_ignored_and_remove_idr(entry, group); + inotify_ignored_and_remove_idr(fsn_mark, group); } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *entry; + struct fsnotify_mark *fsn_mark; bool send; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); - if (!entry) + if (!fsn_mark) return false; mask = (mask & ~FS_EVENT_ON_CHILD); - send = (entry->mask & mask); + send = (fsn_mark->mask & mask); /* find took a reference */ - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); return send; } @@ -171,18 +171,18 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode */ static int idr_callback(int id, void *p, void *data) { - struct fsnotify_mark *entry; - struct inotify_inode_mark_entry *ientry; + struct fsnotify_mark *fsn_mark; + struct inotify_inode_mark *i_mark; static bool warned = false; if (warned) return 0; warned = true; - entry = p; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + fsn_mark = p; + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); - WARN(1, "inotify closing but id=%d for entry=%p in group=%p still in " + WARN(1, "inotify closing but id=%d for fsn_mark=%p in group=%p still in " "idr. Probably leaking memory\n", id, p, data); /* @@ -191,9 +191,9 @@ static int idr_callback(int id, void *p, void *data) * out why we got here and the panic is no worse than the original * BUG() that was here. */ - if (entry) - printk(KERN_WARNING "entry->group=%p inode=%p wd=%d\n", - entry->group, entry->i.inode, ientry->wd); + if (fsn_mark) + printk(KERN_WARNING "fsn_mark->group=%p inode=%p wd=%d\n", + fsn_mark->group, fsn_mark->i.inode, i_mark->wd); return 0; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 118085c..80d102a 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -353,7 +353,7 @@ static int inotify_find_inode(const char __user *dirname, struct path *path, uns static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, int *last_wd, - struct inotify_inode_mark_entry *ientry) + struct inotify_inode_mark *i_mark) { int ret; @@ -362,12 +362,12 @@ static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, return -ENOMEM; spin_lock(idr_lock); - ret = idr_get_new_above(idr, ientry, *last_wd + 1, - &ientry->wd); + ret = idr_get_new_above(idr, i_mark, *last_wd + 1, + &i_mark->wd); /* we added the mark to the idr, take a reference */ if (!ret) { - fsnotify_get_mark(&ientry->fsn_entry); - *last_wd = ientry->wd; + *last_wd = i_mark->wd; + fsnotify_get_mark(&i_mark->fsn_mark); } spin_unlock(idr_lock); } while (ret == -EAGAIN); @@ -375,53 +375,53 @@ static int inotify_add_to_idr(struct idr *idr, spinlock_t *idr_lock, return ret; } -static struct inotify_inode_mark_entry *inotify_idr_find_locked(struct fsnotify_group *group, +static struct inotify_inode_mark *inotify_idr_find_locked(struct fsnotify_group *group, int wd) { struct idr *idr = &group->inotify_data.idr; spinlock_t *idr_lock = &group->inotify_data.idr_lock; - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; assert_spin_locked(idr_lock); - ientry = idr_find(idr, wd); - if (ientry) { - struct fsnotify_mark *fsn_entry = &ientry->fsn_entry; + i_mark = idr_find(idr, wd); + if (i_mark) { + struct fsnotify_mark *fsn_mark = &i_mark->fsn_mark; - fsnotify_get_mark(fsn_entry); + fsnotify_get_mark(fsn_mark); /* One ref for being in the idr, one ref we just took */ - BUG_ON(atomic_read(&fsn_entry->refcnt) < 2); + BUG_ON(atomic_read(&fsn_mark->refcnt) < 2); } - return ientry; + return i_mark; } -static struct inotify_inode_mark_entry *inotify_idr_find(struct fsnotify_group *group, +static struct inotify_inode_mark *inotify_idr_find(struct fsnotify_group *group, int wd) { - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; spinlock_t *idr_lock = &group->inotify_data.idr_lock; spin_lock(idr_lock); - ientry = inotify_idr_find_locked(group, wd); + i_mark = inotify_idr_find_locked(group, wd); spin_unlock(idr_lock); - return ientry; + return i_mark; } static void do_inotify_remove_from_idr(struct fsnotify_group *group, - struct inotify_inode_mark_entry *ientry) + struct inotify_inode_mark *i_mark) { struct idr *idr = &group->inotify_data.idr; spinlock_t *idr_lock = &group->inotify_data.idr_lock; - int wd = ientry->wd; + int wd = i_mark->wd; assert_spin_locked(idr_lock); idr_remove(idr, wd); /* removed from the idr, drop that ref */ - fsnotify_put_mark(&ientry->fsn_entry); + fsnotify_put_mark(&i_mark->fsn_mark); } /* @@ -429,48 +429,48 @@ static void do_inotify_remove_from_idr(struct fsnotify_group *group, * on the mark because it was in the idr. */ static void inotify_remove_from_idr(struct fsnotify_group *group, - struct inotify_inode_mark_entry *ientry) + struct inotify_inode_mark *i_mark) { spinlock_t *idr_lock = &group->inotify_data.idr_lock; - struct inotify_inode_mark_entry *found_ientry = NULL; + struct inotify_inode_mark *found_i_mark = NULL; int wd; spin_lock(idr_lock); - wd = ientry->wd; + wd = i_mark->wd; /* - * does this ientry think it is in the idr? we shouldn't get called + * does this i_mark think it is in the idr? we shouldn't get called * if it wasn't.... */ if (wd == -1) { - WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" - " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.i.inode); + WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p" + " i_mark->inode=%p\n", __func__, i_mark, i_mark->wd, + i_mark->fsn_mark.group, i_mark->fsn_mark.i.inode); goto out; } /* Lets look in the idr to see if we find it */ - found_ientry = inotify_idr_find_locked(group, wd); - if (unlikely(!found_ientry)) { - WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p" - " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.i.inode); + found_i_mark = inotify_idr_find_locked(group, wd); + if (unlikely(!found_i_mark)) { + WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p" + " i_mark->inode=%p\n", __func__, i_mark, i_mark->wd, + i_mark->fsn_mark.group, i_mark->fsn_mark.i.inode); goto out; } /* - * We found an entry in the idr at the right wd, but it's - * not the entry we were told to remove. eparis seriously + * We found an mark in the idr at the right wd, but it's + * not the mark we were told to remove. eparis seriously * fucked up somewhere. */ - if (unlikely(found_ientry != ientry)) { - WARN_ONCE(1, "%s: ientry=%p ientry->wd=%d ientry->group=%p " - "entry->inode=%p found_ientry=%p found_ientry->wd=%d " - "found_ientry->group=%p found_ientry->inode=%p\n", - __func__, ientry, ientry->wd, ientry->fsn_entry.group, - ientry->fsn_entry.i.inode, found_ientry, found_ientry->wd, - found_ientry->fsn_entry.group, - found_ientry->fsn_entry.i.inode); + if (unlikely(found_i_mark != i_mark)) { + WARN_ONCE(1, "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p " + "mark->inode=%p found_i_mark=%p found_i_mark->wd=%d " + "found_i_mark->group=%p found_i_mark->inode=%p\n", + __func__, i_mark, i_mark->wd, i_mark->fsn_mark.group, + i_mark->fsn_mark.i.inode, found_i_mark, found_i_mark->wd, + found_i_mark->fsn_mark.group, + found_i_mark->fsn_mark.i.inode); goto out; } @@ -479,30 +479,30 @@ static void inotify_remove_from_idr(struct fsnotify_group *group, * one ref held by the caller trying to kill us * one ref grabbed by inotify_idr_find */ - if (unlikely(atomic_read(&ientry->fsn_entry.refcnt) < 3)) { - printk(KERN_ERR "%s: ientry=%p ientry->wd=%d ientry->group=%p" - " ientry->inode=%p\n", __func__, ientry, ientry->wd, - ientry->fsn_entry.group, ientry->fsn_entry.i.inode); + if (unlikely(atomic_read(&i_mark->fsn_mark.refcnt) < 3)) { + printk(KERN_ERR "%s: i_mark=%p i_mark->wd=%d i_mark->group=%p" + " i_mark->inode=%p\n", __func__, i_mark, i_mark->wd, + i_mark->fsn_mark.group, i_mark->fsn_mark.i.inode); /* we can't really recover with bad ref cnting.. */ BUG(); } - do_inotify_remove_from_idr(group, ientry); + do_inotify_remove_from_idr(group, i_mark); out: /* match the ref taken by inotify_idr_find_locked() */ - if (found_ientry) - fsnotify_put_mark(&found_ientry->fsn_entry); - ientry->wd = -1; + if (found_i_mark) + fsnotify_put_mark(&found_i_mark->fsn_mark); + i_mark->wd = -1; spin_unlock(idr_lock); } /* * Send IN_IGNORED for this wd, remove this wd from the idr. */ -void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, +void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) { - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; struct fsnotify_event *ignored_event; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; @@ -514,7 +514,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, if (!ignored_event) return; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); event_priv = kmem_cache_alloc(event_priv_cachep, GFP_NOFS); if (unlikely(!event_priv)) @@ -523,7 +523,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *entry, fsn_event_priv = &event_priv->fsnotify_event_priv_data; fsn_event_priv->group = group; - event_priv->wd = ientry->wd; + event_priv->wd = i_mark->wd; ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); if (ret) @@ -534,28 +534,28 @@ skip_send_ignore: /* matches the reference taken when the event was created */ fsnotify_put_event(ignored_event); - /* remove this entry from the idr */ - inotify_remove_from_idr(group, ientry); + /* remove this mark from the idr */ + inotify_remove_from_idr(group, i_mark); atomic_dec(&group->inotify_data.user->inotify_watches); } /* ding dong the mark is dead */ -static void inotify_free_mark(struct fsnotify_mark *entry) +static void inotify_free_mark(struct fsnotify_mark *fsn_mark) { - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); - kmem_cache_free(inotify_inode_mark_cachep, ientry); + kmem_cache_free(inotify_inode_mark_cachep, i_mark); } static int inotify_update_existing_watch(struct fsnotify_group *group, struct inode *inode, u32 arg) { - struct fsnotify_mark *entry; - struct inotify_inode_mark_entry *ientry; + struct fsnotify_mark *fsn_mark; + struct inotify_inode_mark *i_mark; __u32 old_mask, new_mask; __u32 mask; int add = (arg & IN_MASK_ADD); @@ -567,35 +567,35 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, return -EINVAL; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); - if (!entry) + if (!fsn_mark) return -ENOENT; - ientry = container_of(entry, struct inotify_inode_mark_entry, fsn_entry); + i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark); - spin_lock(&entry->lock); + spin_lock(&fsn_mark->lock); - old_mask = entry->mask; + old_mask = fsn_mark->mask; if (add) { - entry->mask |= mask; - new_mask = entry->mask; + fsn_mark->mask |= mask; + new_mask = fsn_mark->mask; } else { - entry->mask = mask; - new_mask = entry->mask; + fsn_mark->mask = mask; + new_mask = fsn_mark->mask; } - spin_unlock(&entry->lock); + spin_unlock(&fsn_mark->lock); if (old_mask != new_mask) { /* more bits in old than in new? */ int dropped = (old_mask & ~new_mask); - /* more bits in this entry than the inode's mask? */ + /* more bits in this fsn_mark than the inode's mask? */ int do_inode = (new_mask & ~inode->i_fsnotify_mask); - /* more bits in this entry than the group? */ + /* more bits in this fsn_mark than the group? */ int do_group = (new_mask & ~group->mask); - /* update the inode with this new entry */ + /* update the inode with this new fsn_mark */ if (dropped || do_inode) fsnotify_recalc_inode_mask(inode); @@ -605,10 +605,10 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, } /* return the wd */ - ret = ientry->wd; + ret = i_mark->wd; /* match the get from fsnotify_find_mark() */ - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); return ret; } @@ -617,7 +617,7 @@ static int inotify_new_watch(struct fsnotify_group *group, struct inode *inode, u32 arg) { - struct inotify_inode_mark_entry *tmp_ientry; + struct inotify_inode_mark *tmp_i_mark; __u32 mask; int ret; struct idr *idr = &group->inotify_data.idr; @@ -628,44 +628,44 @@ static int inotify_new_watch(struct fsnotify_group *group, if (unlikely(!mask)) return -EINVAL; - tmp_ientry = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); - if (unlikely(!tmp_ientry)) + tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); + if (unlikely(!tmp_i_mark)) return -ENOMEM; - fsnotify_init_mark(&tmp_ientry->fsn_entry, inotify_free_mark); - tmp_ientry->fsn_entry.mask = mask; - tmp_ientry->wd = -1; + fsnotify_init_mark(&tmp_i_mark->fsn_mark, inotify_free_mark); + tmp_i_mark->fsn_mark.mask = mask; + tmp_i_mark->wd = -1; ret = -ENOSPC; if (atomic_read(&group->inotify_data.user->inotify_watches) >= inotify_max_user_watches) goto out_err; ret = inotify_add_to_idr(idr, idr_lock, &group->inotify_data.last_wd, - tmp_ientry); + tmp_i_mark); if (ret) goto out_err; /* we are on the idr, now get on the inode */ - ret = fsnotify_add_mark(&tmp_ientry->fsn_entry, group, inode, 0); + ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, 0); if (ret) { /* we failed to get on the inode, get off the idr */ - inotify_remove_from_idr(group, tmp_ientry); + inotify_remove_from_idr(group, tmp_i_mark); goto out_err; } /* increment the number of watches the user has */ atomic_inc(&group->inotify_data.user->inotify_watches); - /* return the watch descriptor for this new entry */ - ret = tmp_ientry->wd; + /* return the watch descriptor for this new mark */ + ret = tmp_i_mark->wd; /* if this mark added a new event update the group mask */ if (mask & ~group->mask) fsnotify_recalc_group_mask(group); out_err: - /* match the ref from fsnotify_init_markentry() */ - fsnotify_put_mark(&tmp_ientry->fsn_entry); + /* match the ref from fsnotify_init_mark() */ + fsnotify_put_mark(&tmp_i_mark->fsn_mark); return ret; } @@ -801,7 +801,7 @@ fput_and_out: SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) { struct fsnotify_group *group; - struct inotify_inode_mark_entry *ientry; + struct inotify_inode_mark *i_mark; struct file *filp; int ret = 0, fput_needed; @@ -817,16 +817,16 @@ SYSCALL_DEFINE2(inotify_rm_watch, int, fd, __s32, wd) group = filp->private_data; ret = -EINVAL; - ientry = inotify_idr_find(group, wd); - if (unlikely(!ientry)) + i_mark = inotify_idr_find(group, wd); + if (unlikely(!i_mark)) goto out; ret = 0; - fsnotify_destroy_mark(&ientry->fsn_entry); + fsnotify_destroy_mark(&i_mark->fsn_mark); /* match ref taken by inotify_idr_find */ - fsnotify_put_mark(&ientry->fsn_entry); + fsnotify_put_mark(&i_mark->fsn_mark); out: fput_light(filp, fput_needed); @@ -840,7 +840,7 @@ out: */ static int __init inotify_user_setup(void) { - inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark_entry, SLAB_PANIC); + inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC); event_priv_cachep = KMEM_CACHE(inotify_event_private_data, SLAB_PANIC); inotify_max_queued_events = 16384; -- cgit v1.1 From ef5e2b785fb3216269e6d0656d38ec286b98dbe5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:24 -0500 Subject: dnotify: rename mark_entry to mark nomenclature change. Used to call things 'entries' but now we just call them 'marks.' Do those changes for dnotify. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 170 ++++++++++++++++++++++---------------------- 1 file changed, 85 insertions(+), 85 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index b202bc5..3efb8b9 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -29,7 +29,7 @@ int dir_notify_enable __read_mostly = 1; static struct kmem_cache *dnotify_struct_cache __read_mostly; -static struct kmem_cache *dnotify_mark_entry_cache __read_mostly; +static struct kmem_cache *dnotify_mark_cache __read_mostly; static struct fsnotify_group *dnotify_group __read_mostly; static DEFINE_MUTEX(dnotify_mark_mutex); @@ -38,8 +38,8 @@ static DEFINE_MUTEX(dnotify_mark_mutex); * is being watched by dnotify. If multiple userspace applications are watching * the same directory with dnotify their information is chained in dn */ -struct dnotify_mark_entry { - struct fsnotify_mark fsn_entry; +struct dnotify_mark { + struct fsnotify_mark fsn_mark; struct dnotify_struct *dn; }; @@ -51,27 +51,27 @@ struct dnotify_mark_entry { * it calls the fsnotify function so it can update the set of all events relevant * to this inode. */ -static void dnotify_recalc_inode_mask(struct fsnotify_mark *entry) +static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) { __u32 new_mask, old_mask; struct dnotify_struct *dn; - struct dnotify_mark_entry *dnentry = container_of(entry, - struct dnotify_mark_entry, - fsn_entry); + struct dnotify_mark *dn_mark = container_of(fsn_mark, + struct dnotify_mark, + fsn_mark); - assert_spin_locked(&entry->lock); + assert_spin_locked(&fsn_mark->lock); - old_mask = entry->mask; + old_mask = fsn_mark->mask; new_mask = 0; - for (dn = dnentry->dn; dn != NULL; dn = dn->dn_next) + for (dn = dn_mark->dn; dn != NULL; dn = dn->dn_next) new_mask |= (dn->dn_mask & ~FS_DN_MULTISHOT); - entry->mask = new_mask; + fsn_mark->mask = new_mask; if (old_mask == new_mask) return; - if (entry->i.inode) - fsnotify_recalc_inode_mask(entry->i.inode); + if (fsn_mark->i.inode) + fsnotify_recalc_inode_mask(fsn_mark->i.inode); } /* @@ -85,8 +85,8 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *entry) static int dnotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - struct fsnotify_mark *entry = NULL; - struct dnotify_mark_entry *dnentry; + struct fsnotify_mark *fsn_mark = NULL; + struct dnotify_mark *dn_mark; struct inode *to_tell; struct dnotify_struct *dn; struct dnotify_struct **prev; @@ -96,16 +96,16 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; spin_lock(&to_tell->i_lock); - entry = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_mark(group, to_tell); spin_unlock(&to_tell->i_lock); /* unlikely since we alreay passed dnotify_should_send_event() */ - if (unlikely(!entry)) + if (unlikely(!fsn_mark)) return 0; - dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); + dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); - spin_lock(&entry->lock); - prev = &dnentry->dn; + spin_lock(&fsn_mark->lock); + prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_mask & test_mask) == 0) { prev = &dn->dn_next; @@ -118,12 +118,12 @@ static int dnotify_handle_event(struct fsnotify_group *group, else { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(entry); + dnotify_recalc_inode_mask(fsn_mark); } } - spin_unlock(&entry->lock); - fsnotify_put_mark(entry); + spin_unlock(&fsn_mark->lock); + fsnotify_put_mark(fsn_mark); return 0; } @@ -136,7 +136,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *entry; + struct fsnotify_mark *fsn_mark; bool send; /* !dir_notify_enable should never get here, don't waste time checking @@ -148,30 +148,30 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, return false; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_mark(group, inode); spin_unlock(&inode->i_lock); /* no mark means no dnotify watch */ - if (!entry) + if (!fsn_mark) return false; mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mask & entry->mask); + send = (mask & fsn_mark->mask); - fsnotify_put_mark(entry); /* matches fsnotify_find_mark */ + fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_mark */ return send; } -static void dnotify_free_mark(struct fsnotify_mark *entry) +static void dnotify_free_mark(struct fsnotify_mark *fsn_mark) { - struct dnotify_mark_entry *dnentry = container_of(entry, - struct dnotify_mark_entry, - fsn_entry); + struct dnotify_mark *dn_mark = container_of(fsn_mark, + struct dnotify_mark, + fsn_mark); - BUG_ON(dnentry->dn); + BUG_ON(dn_mark->dn); - kmem_cache_free(dnotify_mark_entry_cache, dnentry); + kmem_cache_free(dnotify_mark_cache, dn_mark); } static struct fsnotify_ops dnotify_fsnotify_ops = { @@ -191,8 +191,8 @@ static struct fsnotify_ops dnotify_fsnotify_ops = { */ void dnotify_flush(struct file *filp, fl_owner_t id) { - struct fsnotify_mark *entry; - struct dnotify_mark_entry *dnentry; + struct fsnotify_mark *fsn_mark; + struct dnotify_mark *dn_mark; struct dnotify_struct *dn; struct dnotify_struct **prev; struct inode *inode; @@ -202,37 +202,37 @@ void dnotify_flush(struct file *filp, fl_owner_t id) return; spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); - if (!entry) + if (!fsn_mark) return; - dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); + dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); mutex_lock(&dnotify_mark_mutex); - spin_lock(&entry->lock); - prev = &dnentry->dn; + spin_lock(&fsn_mark->lock); + prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_owner == id) && (dn->dn_filp == filp)) { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(entry); + dnotify_recalc_inode_mask(fsn_mark); break; } prev = &dn->dn_next; } - spin_unlock(&entry->lock); + spin_unlock(&fsn_mark->lock); /* nothing else could have found us thanks to the dnotify_mark_mutex */ - if (dnentry->dn == NULL) - fsnotify_destroy_mark(entry); + if (dn_mark->dn == NULL) + fsnotify_destroy_mark(fsn_mark); fsnotify_recalc_group_mask(dnotify_group); mutex_unlock(&dnotify_mark_mutex); - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); } /* this conversion is done only at watch creation */ @@ -264,12 +264,12 @@ static __u32 convert_arg(unsigned long arg) * onto that mark. This function either attaches the new dnotify_struct onto * that list, or it |= the mask onto an existing dnofiy_struct. */ -static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnentry, +static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark *dn_mark, fl_owner_t id, int fd, struct file *filp, __u32 mask) { struct dnotify_struct *odn; - odn = dnentry->dn; + odn = dn_mark->dn; while (odn != NULL) { /* adding more events to existing dnofiy_struct? */ if ((odn->dn_owner == id) && (odn->dn_filp == filp)) { @@ -284,8 +284,8 @@ static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnent dn->dn_fd = fd; dn->dn_filp = filp; dn->dn_owner = id; - dn->dn_next = dnentry->dn; - dnentry->dn = dn; + dn->dn_next = dn_mark->dn; + dn_mark->dn = dn; return 0; } @@ -297,8 +297,8 @@ static int attach_dn(struct dnotify_struct *dn, struct dnotify_mark_entry *dnent */ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) { - struct dnotify_mark_entry *new_dnentry, *dnentry; - struct fsnotify_mark *new_entry, *entry; + struct dnotify_mark *new_dn_mark, *dn_mark; + struct fsnotify_mark *new_fsn_mark, *fsn_mark; struct dnotify_struct *dn; struct inode *inode; fl_owner_t id = current->files; @@ -307,7 +307,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) __u32 mask; /* we use these to tell if we need to kfree */ - new_entry = NULL; + new_fsn_mark = NULL; dn = NULL; if (!dir_notify_enable) { @@ -337,8 +337,8 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) } /* new fsnotify mark, we expect most fcntl calls to add a new mark */ - new_dnentry = kmem_cache_alloc(dnotify_mark_entry_cache, GFP_KERNEL); - if (!new_dnentry) { + new_dn_mark = kmem_cache_alloc(dnotify_mark_cache, GFP_KERNEL); + if (!new_dn_mark) { error = -ENOMEM; goto out_err; } @@ -346,29 +346,29 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* convert the userspace DN_* "arg" to the internal FS_* defines in fsnotify */ mask = convert_arg(arg); - /* set up the new_entry and new_dnentry */ - new_entry = &new_dnentry->fsn_entry; - fsnotify_init_mark(new_entry, dnotify_free_mark); - new_entry->mask = mask; - new_dnentry->dn = NULL; + /* set up the new_fsn_mark and new_dn_mark */ + new_fsn_mark = &new_dn_mark->fsn_mark; + fsnotify_init_mark(new_fsn_mark, dnotify_free_mark); + new_fsn_mark->mask = mask; + new_dn_mark->dn = NULL; /* this is needed to prevent the fcntl/close race described below */ mutex_lock(&dnotify_mark_mutex); - /* add the new_entry or find an old one. */ + /* add the new_fsn_mark or find an old one. */ spin_lock(&inode->i_lock); - entry = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_mark(dnotify_group, inode); spin_unlock(&inode->i_lock); - if (entry) { - dnentry = container_of(entry, struct dnotify_mark_entry, fsn_entry); - spin_lock(&entry->lock); + if (fsn_mark) { + dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); + spin_lock(&fsn_mark->lock); } else { - fsnotify_add_mark(new_entry, dnotify_group, inode, 0); - spin_lock(&new_entry->lock); - entry = new_entry; - dnentry = new_dnentry; - /* we used new_entry, so don't free it */ - new_entry = NULL; + fsnotify_add_mark(new_fsn_mark, dnotify_group, inode, 0); + spin_lock(&new_fsn_mark->lock); + fsn_mark = new_fsn_mark; + dn_mark = new_dn_mark; + /* we used new_fsn_mark, so don't free it */ + new_fsn_mark = NULL; } rcu_read_lock(); @@ -377,17 +377,17 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) /* if (f != filp) means that we lost a race and another task/thread * actually closed the fd we are still playing with before we grabbed - * the dnotify_mark_mutex and entry->lock. Since closing the fd is the + * the dnotify_mark_mutex and fsn_mark->lock. Since closing the fd is the * only time we clean up the marks we need to get our mark off * the list. */ if (f != filp) { /* if we added ourselves, shoot ourselves, it's possible that - * the flush actually did shoot this entry. That's fine too + * the flush actually did shoot this fsn_mark. That's fine too * since multiple calls to destroy_mark is perfectly safe, if - * we found a dnentry already attached to the inode, just sod + * we found a dn_mark already attached to the inode, just sod * off silently as the flush at close time dealt with it. */ - if (dnentry == new_dnentry) + if (dn_mark == new_dn_mark) destroy = 1; goto out; } @@ -395,13 +395,13 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) error = __f_setown(filp, task_pid(current), PIDTYPE_PID, 0); if (error) { /* if we added, we must shoot */ - if (dnentry == new_dnentry) + if (dn_mark == new_dn_mark) destroy = 1; goto out; } - error = attach_dn(dn, dnentry, id, fd, filp, mask); - /* !error means that we attached the dn to the dnentry, so don't free it */ + error = attach_dn(dn, dn_mark, id, fd, filp, mask); + /* !error means that we attached the dn to the dn_mark, so don't free it */ if (!error) dn = NULL; /* -EEXIST means that we didn't add this new dn and used an old one. @@ -409,20 +409,20 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) else if (error == -EEXIST) error = 0; - dnotify_recalc_inode_mask(entry); + dnotify_recalc_inode_mask(fsn_mark); out: - spin_unlock(&entry->lock); + spin_unlock(&fsn_mark->lock); if (destroy) - fsnotify_destroy_mark(entry); + fsnotify_destroy_mark(fsn_mark); fsnotify_recalc_group_mask(dnotify_group); mutex_unlock(&dnotify_mark_mutex); - fsnotify_put_mark(entry); + fsnotify_put_mark(fsn_mark); out_err: - if (new_entry) - fsnotify_put_mark(new_entry); + if (new_fsn_mark) + fsnotify_put_mark(new_fsn_mark); if (dn) kmem_cache_free(dnotify_struct_cache, dn); return error; @@ -431,7 +431,7 @@ out_err: static int __init dnotify_init(void) { dnotify_struct_cache = KMEM_CACHE(dnotify_struct, SLAB_PANIC); - dnotify_mark_entry_cache = KMEM_CACHE(dnotify_mark_entry, SLAB_PANIC); + dnotify_mark_cache = KMEM_CACHE(dnotify_mark, SLAB_PANIC); dnotify_group = fsnotify_alloc_group(&dnotify_fsnotify_ops); if (IS_ERR(dnotify_group)) -- cgit v1.1 From 35566087099c3ff8901d65ee98af56347ee66e5a Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fsnotify: take inode->i_lock inside fsnotify_find_mark_entry() All callers to fsnotify_find_mark_entry() except one take and release inode->i_lock around the call. Take the lock inside fsnotify_find_mark_entry() instead. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 12 ------------ fs/notify/inode_mark.c | 26 +++++++++++++++++++------- fs/notify/inotify/inotify_fsnotify.c | 4 ---- fs/notify/inotify/inotify_user.c | 2 -- 4 files changed, 19 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 3efb8b9..cac2eb8 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -95,11 +95,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - spin_lock(&to_tell->i_lock); fsn_mark = fsnotify_find_mark(group, to_tell); - spin_unlock(&to_tell->i_lock); - - /* unlikely since we alreay passed dnotify_should_send_event() */ if (unlikely(!fsn_mark)) return 0; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -147,11 +143,7 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, if (!S_ISDIR(inode->i_mode)) return false; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(group, inode); - spin_unlock(&inode->i_lock); - - /* no mark means no dnotify watch */ if (!fsn_mark) return false; @@ -201,9 +193,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) if (!S_ISDIR(inode->i_mode)) return; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(dnotify_group, inode); - spin_unlock(&inode->i_lock); if (!fsn_mark) return; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -356,9 +346,7 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) mutex_lock(&dnotify_mark_mutex); /* add the new_fsn_mark or find an old one. */ - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(dnotify_group, inode); - spin_unlock(&inode->i_lock); if (fsn_mark) { dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); spin_lock(&fsn_mark->lock); diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 27c1b43..ba6f983 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -261,12 +261,8 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } } -/* - * given a group and inode, find the mark associated with that combination. - * if found take a reference to that mark and return it, else return NULL - */ -struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, - struct inode *inode) +static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; struct hlist_node *pos; @@ -282,6 +278,22 @@ struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, return NULL; } +/* + * given a group and inode, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, + struct inode *inode) +{ + struct fsnotify_mark *mark; + + spin_lock(&inode->i_lock); + mark = fsnotify_find_mark_locked(group, inode); + spin_unlock(&inode->i_lock); + + return mark; +} + void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); @@ -349,7 +361,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, spin_lock(&inode->i_lock); if (!allow_dups) - lmark = fsnotify_find_mark(group, inode); + lmark = fsnotify_find_mark_locked(group, inode); if (!lmark) { mark->group = group; mark->i.inode = inode; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 12dc72b..cc8f6bc 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -97,9 +97,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; - spin_lock(&to_tell->i_lock); fsn_mark = fsnotify_find_mark(group, to_tell); - spin_unlock(&to_tell->i_lock); /* race with watch removal? We already passes should_send */ if (unlikely(!fsn_mark)) return 0; @@ -147,9 +145,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct fsnotify_mark *fsn_mark; bool send; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(group, inode); - spin_unlock(&inode->i_lock); if (!fsn_mark) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 80d102a..ad5a1ea 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -566,9 +566,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, if (unlikely(!mask)) return -EINVAL; - spin_lock(&inode->i_lock); fsn_mark = fsnotify_find_mark(group, inode); - spin_unlock(&inode->i_lock); if (!fsn_mark) return -ENOENT; -- cgit v1.1 From ff0b16a9850e8a240ad59e10b0a1291a8fcf7cbc Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: fscking all notification system fanotify is a novel file notification system which bases notification on giving userspace both an event type (open, close, read, write) and an open file descriptor to the object in question. This should address a number of races and problems with other notification systems like inotify and dnotify and should allow the future implementation of blocking or access controlled notification. These are useful for on access scanners or hierachical storage management schemes. This patch just implements the basics of the fsnotify functions. Signed-off-by: Eric Paris --- fs/notify/Kconfig | 1 + fs/notify/Makefile | 1 + fs/notify/fanotify/Kconfig | 11 ++++++ fs/notify/fanotify/Makefile | 1 + fs/notify/fanotify/fanotify.c | 78 +++++++++++++++++++++++++++++++++++++++++++ fs/notify/fanotify/fanotify.h | 12 +++++++ 6 files changed, 104 insertions(+) create mode 100644 fs/notify/fanotify/Kconfig create mode 100644 fs/notify/fanotify/Makefile create mode 100644 fs/notify/fanotify/fanotify.c create mode 100644 fs/notify/fanotify/fanotify.h (limited to 'fs') diff --git a/fs/notify/Kconfig b/fs/notify/Kconfig index dffbb09..22c629e 100644 --- a/fs/notify/Kconfig +++ b/fs/notify/Kconfig @@ -3,3 +3,4 @@ config FSNOTIFY source "fs/notify/dnotify/Kconfig" source "fs/notify/inotify/Kconfig" +source "fs/notify/fanotify/Kconfig" diff --git a/fs/notify/Makefile b/fs/notify/Makefile index 0922cc8..396a387 100644 --- a/fs/notify/Makefile +++ b/fs/notify/Makefile @@ -2,3 +2,4 @@ obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o obj-y += dnotify/ obj-y += inotify/ +obj-y += fanotify/ diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig new file mode 100644 index 0000000..f9d7ae0 --- /dev/null +++ b/fs/notify/fanotify/Kconfig @@ -0,0 +1,11 @@ +config FANOTIFY + bool "Filesystem wide access notification" + select FSNOTIFY + default y + ---help--- + Say Y here to enable fanotify suport. fanotify is a file access + notification system which differs from inotify in that it sends + and open file descriptor to the userspace listener along with + the event. + + If unsure, say Y. diff --git a/fs/notify/fanotify/Makefile b/fs/notify/fanotify/Makefile new file mode 100644 index 0000000..e7d39c0 --- /dev/null +++ b/fs/notify/fanotify/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_FANOTIFY) += fanotify.o diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c new file mode 100644 index 0000000..3ffb9db --- /dev/null +++ b/fs/notify/fanotify/fanotify.c @@ -0,0 +1,78 @@ +#include +#include +#include +#include /* UINT_MAX */ +#include + +#include "fanotify.h" + +static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +{ + int ret; + + + BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); + BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); + BUILD_BUG_ON(FAN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); + BUILD_BUG_ON(FAN_CLOSE_WRITE != FS_CLOSE_WRITE); + BUILD_BUG_ON(FAN_OPEN != FS_OPEN); + BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD); + BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW); + + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + + ret = fsnotify_add_notify_event(group, event, NULL, NULL); + + return ret; +} + +static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) +{ + struct fsnotify_mark *fsn_mark; + bool send; + + pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", + __func__, group, inode, mask, data, data_type); + + /* sorry, fanotify only gives a damn about files and dirs */ + if (!S_ISREG(inode->i_mode) && + !S_ISDIR(inode->i_mode)) + return false; + + /* if we don't have enough info to send an event to userspace say no */ + if (data_type != FSNOTIFY_EVENT_PATH) + return false; + + fsn_mark = fsnotify_find_mark(group, inode); + if (!fsn_mark) + return false; + + /* if the event is for a child and this inode doesn't care about + * events on the child, don't send it! */ + if ((mask & FS_EVENT_ON_CHILD) && + !(fsn_mark->mask & FS_EVENT_ON_CHILD)) { + send = false; + } else { + /* + * We care about children, but do we care about this particular + * type of event? + */ + mask = (mask & ~FS_EVENT_ON_CHILD); + send = (fsn_mark->mask & mask); + } + + /* find took a reference */ + fsnotify_put_mark(fsn_mark); + + return send; +} + +const struct fsnotify_ops fanotify_fsnotify_ops = { + .handle_event = fanotify_handle_event, + .should_send_event = fanotify_should_send_event, + .free_group_priv = NULL, + .free_event_priv = NULL, + .freeing_mark = NULL, +}; diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h new file mode 100644 index 0000000..50765eb --- /dev/null +++ b/fs/notify/fanotify/fanotify.h @@ -0,0 +1,12 @@ +#include +#include +#include +#include +#include + +static inline bool fanotify_mask_valid(__u32 mask) +{ + if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) + return false; + return true; +} -- cgit v1.1 From 767cd46c332d1360cdbe46ef18d80c3ade06fdc1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify:drop notification if they exist in the outgoing queue fanotify listeners get an open file descriptor to the object in question so the ordering of operations is not as important as in other notification systems. inotify will drop events if the last event in the event FIFO is the same as the current event. This patch will drop fanotify events if they are the same as another event anywhere in the event FIFO. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 45 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 3ffb9db..c35c117 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -6,6 +6,45 @@ #include "fanotify.h" +static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) +{ + pr_debug("%s: old=%p new=%p\n", __func__, old, new); + + if ((old->mask == new->mask) && + (old->to_tell == new->to_tell) && + (old->data_type == new->data_type)) { + switch (old->data_type) { + case (FSNOTIFY_EVENT_PATH): + if ((old->path.mnt == new->path.mnt) && + (old->path.dentry == new->path.dentry)) + return true; + case (FSNOTIFY_EVENT_NONE): + return true; + default: + BUG(); + }; + } + return false; +} + +static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) +{ + struct fsnotify_event_holder *holder; + struct fsnotify_event *test_event; + + pr_debug("%s: list=%p event=%p\n", __func__, list, event); + + /* and the list better be locked by something too! */ + + list_for_each_entry_reverse(holder, list, event_list) { + test_event = holder->event; + if (should_merge(test_event, event)) + return -EEXIST; + } + + return 0; +} + static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { int ret; @@ -21,8 +60,10 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, NULL); - + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge); + /* -EEXIST means this event was merged with another, not that it was an error */ + if (ret == -EEXIST) + ret = 0; return ret; } -- cgit v1.1 From a12a7dd3284f5644326af1ea53b35030f205dd29 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: merge notification events with different masks Instead of just merging fanotify events if they are exactly the same, merge notification events with different masks. To do this we have to clone the old event, update the mask in the new event with the new merged mask, and put the new event in place of the old event. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index c35c117..8e574d6 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -10,8 +10,7 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { pr_debug("%s: old=%p new=%p\n", __func__, old, new); - if ((old->mask == new->mask) && - (old->to_tell == new->to_tell) && + if ((old->to_tell == new->to_tell) && (old->data_type == new->data_type)) { switch (old->data_type) { case (FSNOTIFY_EVENT_PATH): @@ -29,20 +28,42 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) { - struct fsnotify_event_holder *holder; + struct fsnotify_event_holder *test_holder; struct fsnotify_event *test_event; + struct fsnotify_event *new_event; + int ret = 0; pr_debug("%s: list=%p event=%p\n", __func__, list, event); /* and the list better be locked by something too! */ - list_for_each_entry_reverse(holder, list, event_list) { - test_event = holder->event; - if (should_merge(test_event, event)) - return -EEXIST; + list_for_each_entry_reverse(test_holder, list, event_list) { + test_event = test_holder->event; + if (should_merge(test_event, event)) { + ret = -EEXIST; + + /* if they are exactly the same we are done */ + if (test_event->mask == event->mask) + goto out; + + /* can't allocate memory, merge was no possible */ + new_event = fsnotify_clone_event(test_event); + if (unlikely(!new_event)) { + ret = 0; + goto out; + } + + /* build new event and replace it on the list */ + new_event->mask = (test_event->mask | event->mask); + fsnotify_replace_event(test_holder, new_event); + /* match ref from fsnotify_clone_event() */ + fsnotify_put_event(new_event); + + break; + } } - - return 0; +out: + return ret; } static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) -- cgit v1.1 From 9dced01a0939f3e952eca8c21427ceec1f473dcf Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: do not clone on merge unless needed Currently if 2 events are going to be merged on the notication queue with different masks the second event will be cloned and will replace the first event. However if this notification queue is the only place referencing the event in question there is no reason not to just update the event in place. We can tell this if the event->refcnt == 1. Since we hold a reference for each queue this event is on we know that when refcnt == 1 this is the only queue. The other concern is that it might be about to be added to a new queue, but this can't be the case since fsnotify holds a reference on the event until it is finished adding it to queues. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 8e574d6..5b0b6b4 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -46,6 +46,16 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) if (test_event->mask == event->mask) goto out; + /* + * if the refcnt == 1 this is the only queue + * for this event and so we can update the mask + * in place. + */ + if (atomic_read(&test_event->refcnt) == 1) { + test_event->mask |= event->mask; + goto out; + } + /* can't allocate memory, merge was no possible */ new_event = fsnotify_clone_event(test_event); if (unlikely(!new_event)) { -- cgit v1.1 From 11637e4b7dc098e9a863f0a619d55ebc60f5949e Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:25 -0500 Subject: fanotify: fanotify_init syscall declaration This patch defines a new syscall fanotify_init() of the form: int sys_fanotify_init(unsigned int flags, unsigned int event_f_flags, unsigned int priority) This syscall is used to create and fanotify group. This is very similar to the inotify_init() syscall. Signed-off-by: Eric Paris --- fs/notify/fanotify/Makefile | 2 +- fs/notify/fanotify/fanotify_user.c | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 fs/notify/fanotify/fanotify_user.c (limited to 'fs') diff --git a/fs/notify/fanotify/Makefile b/fs/notify/fanotify/Makefile index e7d39c0..0999213 100644 --- a/fs/notify/fanotify/Makefile +++ b/fs/notify/fanotify/Makefile @@ -1 +1 @@ -obj-$(CONFIG_FANOTIFY) += fanotify.o +obj-$(CONFIG_FANOTIFY) += fanotify.o fanotify_user.o diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c new file mode 100644 index 0000000..cf176fc --- /dev/null +++ b/fs/notify/fanotify/fanotify_user.c @@ -0,0 +1,13 @@ +#include +#include +#include +#include +#include + +#include "fanotify.h" + +SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, + unsigned int, priority) +{ + return -ENOSYS; +} -- cgit v1.1 From 52c923dd079df49f58016a9e56df184b132611d6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: fanotify_init syscall implementation NAME fanotify_init - initialize an fanotify group SYNOPSIS int fanotify_init(unsigned int flags, unsigned int event_f_flags, int priority); DESCRIPTION fanotify_init() initializes a new fanotify instance and returns a file descriptor associated with the new fanotify event queue. The following values can be OR'd into the flags field: FAN_NONBLOCK Set the O_NONBLOCK file status flag on the new open file description. Using this flag saves extra calls to fcntl(2) to achieve the same result. FAN_CLOEXEC Set the close-on-exec (FD_CLOEXEC) flag on the new file descriptor. See the description of the O_CLOEXEC flag in open(2) for reasons why this may be useful. The event_f_flags argument is unused and must be set to 0 The priority argument is unused and must be set to 0 RETURN VALUE On success, this system call return a new file descriptor. On error, -1 is returned, and errno is set to indicate the error. ERRORS EINVAL An invalid value was specified in flags. EINVAL A non-zero valid was passed in event_f_flags or in priority ENFILE The system limit on the total number of file descriptors has been reached. ENOMEM Insufficient kernel memory is available. CONFORMING TO These system calls are Linux-specific. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 2 ++ fs/notify/fanotify/fanotify_user.c | 61 +++++++++++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 50765eb..dd656cf 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -4,6 +4,8 @@ #include #include +extern const struct fsnotify_ops fanotify_fsnotify_ops; + static inline bool fanotify_mask_valid(__u32 mask) { if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index cf176fc..67c0b5e 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1,13 +1,72 @@ #include #include +#include #include #include #include #include "fanotify.h" +static int fanotify_release(struct inode *ignored, struct file *file) +{ + struct fsnotify_group *group = file->private_data; + + pr_debug("%s: file=%p group=%p\n", __func__, file, group); + + /* matches the fanotify_init->fsnotify_alloc_group */ + fsnotify_put_group(group); + + return 0; +} + +static const struct file_operations fanotify_fops = { + .poll = NULL, + .read = NULL, + .fasync = NULL, + .release = fanotify_release, + .unlocked_ioctl = NULL, + .compat_ioctl = NULL, +}; + +/* fanotify syscalls */ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, unsigned int, priority) { - return -ENOSYS; + struct fsnotify_group *group; + int f_flags, fd; + + pr_debug("%s: flags=%d event_f_flags=%d priority=%d\n", + __func__, flags, event_f_flags, priority); + + if (event_f_flags) + return -EINVAL; + if (priority) + return -EINVAL; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + if (flags & ~FAN_ALL_INIT_FLAGS) + return -EINVAL; + + f_flags = (O_RDONLY | FMODE_NONOTIFY); + if (flags & FAN_CLOEXEC) + f_flags |= O_CLOEXEC; + if (flags & FAN_NONBLOCK) + f_flags |= O_NONBLOCK; + + /* fsnotify_alloc_group takes a ref. Dropped in fanotify_release */ + group = fsnotify_alloc_group(&fanotify_fsnotify_ops); + if (IS_ERR(group)) + return PTR_ERR(group); + + fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); + if (fd < 0) + goto out_put_group; + + return fd; + +out_put_group: + fsnotify_put_group(group); + return fd; } -- cgit v1.1 From bbaa4168b2d2d8cc674e6d35806e8426aef464b8 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: sys_fanotify_mark declartion This patch simply declares the new sys_fanotify_mark syscall int fanotify_mark(int fanotify_fd, unsigned int flags, u64_mask, int dfd const char *pathname) Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 67c0b5e..55d6e37 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -70,3 +70,9 @@ out_put_group: fsnotify_put_group(group); return fd; } + +SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, + __u64, mask, int, dfd, const char __user *, pathname) +{ + return -ENOSYS; +} -- cgit v1.1 From 2a3edf86040a7e15684525a2aadc29f532c51325 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: fanotify_mark syscall implementation NAME fanotify_mark - add, remove, or modify an fanotify mark on a filesystem object SYNOPSIS int fanotify_mark(int fanotify_fd, unsigned int flags, u64 mask, int dfd, const char *pathname) DESCRIPTION fanotify_mark() is used to add remove or modify a mark on a filesystem object. Marks are used to indicate that the fanotify group is interested in events which occur on that object. At this point in time marks may only be added to files and directories. fanotify_fd must be a file descriptor returned by fanotify_init() The flags field must contain exactly one of the following: FAN_MARK_ADD - or the bits in mask and ignored mask into the mark FAN_MARK_REMOVE - bitwise remove the bits in mask and ignored mark from the mark The following values can be OR'd into the flags field: FAN_MARK_DONT_FOLLOW - same meaning as O_NOFOLLOW as described in open(2) FAN_MARK_ONLYDIR - same meaning as O_DIRECTORY as described in open(2) dfd may be any of the following: AT_FDCWD: the object will be lookup up based on pathname similar to open(2) file descriptor of a directory: if pathname is not NULL the object to modify will be lookup up similar to openat(2) file descriptor of the final object: if pathname is NULL the object to modify will be the object referenced by dfd The mask is the bitwise OR of the set of events of interest such as: FAN_ACCESS - object was accessed (read) FAN_MODIFY - object was modified (write) FAN_CLOSE_WRITE - object was writable and was closed FAN_CLOSE_NOWRITE - object was read only and was closed FAN_OPEN - object was opened FAN_EVENT_ON_CHILD - interested in objected that happen to children. Only relavent when the object is a directory FAN_Q_OVERFLOW - event queue overflowed (not implemented) RETURN VALUE On success, this system call returns 0. On error, -1 is returned, and errno is set to indicate the error. ERRORS EINVAL An invalid value was specified in flags. EINVAL An invalid value was specified in mask. EINVAL An invalid value was specified in ignored_mask. EINVAL fanotify_fd is not a file descriptor as returned by fanotify_init() EBADF fanotify_fd is not a valid file descriptor EBADF dfd is not a valid file descriptor and path is NULL. ENOTDIR dfd is not a directory and path is not NULL EACCESS no search permissions on some part of the path ENENT file not found ENOMEM Insufficient kernel memory is available. CONFORMING TO These system calls are Linux-specific. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 18 +++ fs/notify/fanotify/fanotify_user.c | 239 ++++++++++++++++++++++++++++++++++++- 2 files changed, 256 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index dd656cf..59c3331 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -6,6 +6,24 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; +static inline bool fanotify_mark_flags_valid(unsigned int flags) +{ + /* must be either and add or a remove */ + if (!(flags & (FAN_MARK_ADD | FAN_MARK_REMOVE))) + return false; + + /* cannot be both add and remove */ + if ((flags & FAN_MARK_ADD) && + (flags & FAN_MARK_REMOVE)) + return false; + + /* cannot have more flags than we know about */ + if (flags & ~FAN_ALL_MARK_FLAGS) + return false; + + return true; +} + static inline bool fanotify_mask_valid(__u32 mask) { if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 55d6e37..bc4fa48 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1,12 +1,18 @@ #include +#include #include #include #include +#include +#include #include #include +#include #include "fanotify.h" +static struct kmem_cache *fanotify_mark_cache __read_mostly; + static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; @@ -28,6 +34,185 @@ static const struct file_operations fanotify_fops = { .compat_ioctl = NULL, }; +static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) +{ + kmem_cache_free(fanotify_mark_cache, fsn_mark); +} + +static int fanotify_find_path(int dfd, const char __user *filename, + struct path *path, unsigned int flags) +{ + int ret; + + pr_debug("%s: dfd=%d filename=%p flags=%x\n", __func__, + dfd, filename, flags); + + if (filename == NULL) { + struct file *file; + int fput_needed; + + ret = -EBADF; + file = fget_light(dfd, &fput_needed); + if (!file) + goto out; + + ret = -ENOTDIR; + if ((flags & FAN_MARK_ONLYDIR) && + !(S_ISDIR(file->f_path.dentry->d_inode->i_mode))) { + fput_light(file, fput_needed); + goto out; + } + + *path = file->f_path; + path_get(path); + fput_light(file, fput_needed); + } else { + unsigned int lookup_flags = 0; + + if (!(flags & FAN_MARK_DONT_FOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + if (flags & FAN_MARK_ONLYDIR) + lookup_flags |= LOOKUP_DIRECTORY; + + ret = user_path_at(dfd, filename, lookup_flags, path); + if (ret) + goto out; + } + + /* you can only watch an inode if you have read permissions on it */ + ret = inode_permission(path->dentry->d_inode, MAY_READ); + if (ret) + path_put(path); +out: + return ret; +} + +static int fanotify_remove_mark(struct fsnotify_group *group, + struct inode *inode, + __u32 mask) +{ + struct fsnotify_mark *fsn_mark; + __u32 new_mask; + + pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, + group, inode, mask); + + fsn_mark = fsnotify_find_mark(group, inode); + if (!fsn_mark) + return -ENOENT; + + spin_lock(&fsn_mark->lock); + fsn_mark->mask &= ~mask; + new_mask = fsn_mark->mask; + spin_unlock(&fsn_mark->lock); + + if (!new_mask) + fsnotify_destroy_mark(fsn_mark); + else + fsnotify_recalc_inode_mask(inode); + + fsnotify_recalc_group_mask(group); + + /* matches the fsnotify_find_mark() */ + fsnotify_put_mark(fsn_mark); + + return 0; +} + +static int fanotify_add_mark(struct fsnotify_group *group, + struct inode *inode, + __u32 mask) +{ + struct fsnotify_mark *fsn_mark; + __u32 old_mask, new_mask; + int ret; + + pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, + group, inode, mask); + + fsn_mark = fsnotify_find_mark(group, inode); + if (!fsn_mark) { + struct fsnotify_mark *new_fsn_mark; + + ret = -ENOMEM; + new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!new_fsn_mark) + goto out; + + fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(new_fsn_mark, group, inode, 0); + if (ret) { + fanotify_free_mark(new_fsn_mark); + goto out; + } + + fsn_mark = new_fsn_mark; + } + + ret = 0; + + spin_lock(&fsn_mark->lock); + old_mask = fsn_mark->mask; + fsn_mark->mask |= mask; + new_mask = fsn_mark->mask; + spin_unlock(&fsn_mark->lock); + + /* we made changes to a mask, update the group mask and the inode mask + * so things happen quickly. */ + if (old_mask != new_mask) { + /* more bits in old than in new? */ + int dropped = (old_mask & ~new_mask); + /* more bits in this mark than the inode's mask? */ + int do_inode = (new_mask & ~inode->i_fsnotify_mask); + /* more bits in this mark than the group? */ + int do_group = (new_mask & ~group->mask); + + /* update the inode with this new mark */ + if (dropped || do_inode) + fsnotify_recalc_inode_mask(inode); + + /* update the group mask with the new mask */ + if (dropped || do_group) + fsnotify_recalc_group_mask(group); + } + + /* match the init or the find.... */ + fsnotify_put_mark(fsn_mark); +out: + return ret; +} + +static int fanotify_update_mark(struct fsnotify_group *group, + struct inode *inode, int flags, + __u32 mask) +{ + pr_debug("%s: group=%p inode=%p flags=%x mask=%x\n", __func__, + group, inode, flags, mask); + + if (flags & FAN_MARK_ADD) + fanotify_add_mark(group, inode, mask); + else if (flags & FAN_MARK_REMOVE) + fanotify_remove_mark(group, inode, mask); + else + BUG(); + + return 0; +} + +static bool fanotify_mark_validate_input(int flags, + __u32 mask) +{ + pr_debug("%s: flags=%x mask=%x\n", __func__, flags, mask); + + /* are flags valid of this operation? */ + if (!fanotify_mark_flags_valid(flags)) + return false; + /* is the mask valid? */ + if (!fanotify_mask_valid(mask)) + return false; + return true; +} + /* fanotify syscalls */ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, unsigned int, priority) @@ -74,5 +259,57 @@ out_put_group: SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, __u64, mask, int, dfd, const char __user *, pathname) { - return -ENOSYS; + struct inode *inode; + struct fsnotify_group *group; + struct file *filp; + struct path path; + int ret, fput_needed; + + pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n", + __func__, fanotify_fd, flags, dfd, pathname, mask); + + /* we only use the lower 32 bits as of right now. */ + if (mask & ((__u64)0xffffffff << 32)) + return -EINVAL; + + if (!fanotify_mark_validate_input(flags, mask)) + return -EINVAL; + + filp = fget_light(fanotify_fd, &fput_needed); + if (unlikely(!filp)) + return -EBADF; + + /* verify that this is indeed an fanotify instance */ + ret = -EINVAL; + if (unlikely(filp->f_op != &fanotify_fops)) + goto fput_and_out; + + ret = fanotify_find_path(dfd, pathname, &path, flags); + if (ret) + goto fput_and_out; + + /* inode held in place by reference to path; group by fget on fd */ + inode = path.dentry->d_inode; + group = filp->private_data; + + /* create/update an inode mark */ + ret = fanotify_update_mark(group, inode, flags, mask); + + path_put(&path); +fput_and_out: + fput_light(filp, fput_needed); + return ret; +} + +/* + * fanotify_user_setup - Our initialization function. Note that we cannnot return + * error because we have compiled-in VFS hooks. So an (unlikely) failure here + * must result in panic(). + */ +static int __init fanotify_user_setup(void) +{ + fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); + + return 0; } +device_initcall(fanotify_user_setup); -- cgit v1.1 From a1014f102322398e67524b68b3300acf384e6c1f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: send events using read Send events to userspace by reading the file descriptor from fanotify_init(). One will get blocks of data which look like: struct fanotify_event_metadata { __u32 event_len; __u32 vers; __s32 fd; __u64 mask; __s64 pid; __u64 cookie; } __attribute__ ((packed)); Simple code to retrieve and deal with events is below while ((len = read(fan_fd, buf, sizeof(buf))) > 0) { struct fanotify_event_metadata *metadata; metadata = (void *)buf; while(FAN_EVENT_OK(metadata, len)) { [PROCESS HERE!!] if (metadata->fd >= 0 && close(metadata->fd) != 0) goto fail; metadata = FAN_EVENT_NEXT(metadata, len); } } Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 5 + fs/notify/fanotify/fanotify_user.c | 220 ++++++++++++++++++++++++++++++++++++- 2 files changed, 221 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 59c3331..5608783 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -30,3 +30,8 @@ static inline bool fanotify_mask_valid(__u32 mask) return false; return true; } + +static inline __u32 fanotify_outgoing_mask(__u32 mask) +{ + return mask & FAN_ALL_OUTGOING_EVENTS; +} diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index bc4fa48..a99550f 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -4,15 +4,202 @@ #include #include #include +#include #include +#include #include #include #include +#include + +#include #include "fanotify.h" static struct kmem_cache *fanotify_mark_cache __read_mostly; +/* + * Get an fsnotify notification event if one exists and is small + * enough to fit in "count". Return an error pointer if the count + * is not large enough. + * + * Called with the group->notification_mutex held. + */ +static struct fsnotify_event *get_one_event(struct fsnotify_group *group, + size_t count) +{ + BUG_ON(!mutex_is_locked(&group->notification_mutex)); + + pr_debug("%s: group=%p count=%zd\n", __func__, group, count); + + if (fsnotify_notify_queue_is_empty(group)) + return NULL; + + if (FAN_EVENT_METADATA_LEN > count) + return ERR_PTR(-EINVAL); + + /* held the notification_mutex the whole time, so this is the + * same event we peeked above */ + return fsnotify_remove_notify_event(group); +} + +static int create_and_fill_fd(struct fsnotify_group *group, + struct fanotify_event_metadata *metadata, + struct fsnotify_event *event) +{ + int client_fd; + struct dentry *dentry; + struct vfsmount *mnt; + struct file *new_file; + + pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group, + metadata, event); + + client_fd = get_unused_fd(); + if (client_fd < 0) + return client_fd; + + if (event->data_type != FSNOTIFY_EVENT_PATH) { + WARN_ON(1); + put_unused_fd(client_fd); + return -EINVAL; + } + + /* + * we need a new file handle for the userspace program so it can read even if it was + * originally opened O_WRONLY. + */ + dentry = dget(event->path.dentry); + mnt = mntget(event->path.mnt); + /* it's possible this event was an overflow event. in that case dentry and mnt + * are NULL; That's fine, just don't call dentry open */ + if (dentry && mnt) + new_file = dentry_open(dentry, mnt, + O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY, + current_cred()); + else + new_file = ERR_PTR(-EOVERFLOW); + if (IS_ERR(new_file)) { + /* + * we still send an event even if we can't open the file. this + * can happen when say tasks are gone and we try to open their + * /proc files or we try to open a WRONLY file like in sysfs + * we just send the errno to userspace since there isn't much + * else we can do. + */ + put_unused_fd(client_fd); + client_fd = PTR_ERR(new_file); + } else { + fd_install(client_fd, new_file); + } + + metadata->fd = client_fd; + + return 0; +} + +static ssize_t fill_event_metadata(struct fsnotify_group *group, + struct fanotify_event_metadata *metadata, + struct fsnotify_event *event) +{ + pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, + group, metadata, event); + + metadata->event_len = FAN_EVENT_METADATA_LEN; + metadata->vers = FANOTIFY_METADATA_VERSION; + metadata->mask = fanotify_outgoing_mask(event->mask); + + return create_and_fill_fd(group, metadata, event); + +} + +static ssize_t copy_event_to_user(struct fsnotify_group *group, + struct fsnotify_event *event, + char __user *buf) +{ + struct fanotify_event_metadata fanotify_event_metadata; + int ret; + + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + + ret = fill_event_metadata(group, &fanotify_event_metadata, event); + if (ret) + return ret; + + if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) + return -EFAULT; + + return FAN_EVENT_METADATA_LEN; +} + +/* intofiy userspace file descriptor functions */ +static unsigned int fanotify_poll(struct file *file, poll_table *wait) +{ + struct fsnotify_group *group = file->private_data; + int ret = 0; + + poll_wait(file, &group->notification_waitq, wait); + mutex_lock(&group->notification_mutex); + if (!fsnotify_notify_queue_is_empty(group)) + ret = POLLIN | POLLRDNORM; + mutex_unlock(&group->notification_mutex); + + return ret; +} + +static ssize_t fanotify_read(struct file *file, char __user *buf, + size_t count, loff_t *pos) +{ + struct fsnotify_group *group; + struct fsnotify_event *kevent; + char __user *start; + int ret; + DEFINE_WAIT(wait); + + start = buf; + group = file->private_data; + + pr_debug("%s: group=%p\n", __func__, group); + + while (1) { + prepare_to_wait(&group->notification_waitq, &wait, TASK_INTERRUPTIBLE); + + mutex_lock(&group->notification_mutex); + kevent = get_one_event(group, count); + mutex_unlock(&group->notification_mutex); + + if (kevent) { + ret = PTR_ERR(kevent); + if (IS_ERR(kevent)) + break; + ret = copy_event_to_user(group, kevent, buf); + fsnotify_put_event(kevent); + if (ret < 0) + break; + buf += ret; + count -= ret; + continue; + } + + ret = -EAGAIN; + if (file->f_flags & O_NONBLOCK) + break; + ret = -EINTR; + if (signal_pending(current)) + break; + + if (start != buf) + break; + + schedule(); + } + + finish_wait(&group->notification_waitq, &wait); + if (start != buf && ret != -EFAULT) + ret = buf - start; + return ret; +} + static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; @@ -25,13 +212,38 @@ static int fanotify_release(struct inode *ignored, struct file *file) return 0; } +static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct fsnotify_group *group; + struct fsnotify_event_holder *holder; + void __user *p; + int ret = -ENOTTY; + size_t send_len = 0; + + group = file->private_data; + + p = (void __user *) arg; + + switch (cmd) { + case FIONREAD: + mutex_lock(&group->notification_mutex); + list_for_each_entry(holder, &group->notification_list, event_list) + send_len += FAN_EVENT_METADATA_LEN; + mutex_unlock(&group->notification_mutex); + ret = put_user(send_len, (int __user *) p); + break; + } + + return ret; +} + static const struct file_operations fanotify_fops = { - .poll = NULL, - .read = NULL, + .poll = fanotify_poll, + .read = fanotify_read, .fasync = NULL, .release = fanotify_release, - .unlocked_ioctl = NULL, - .compat_ioctl = NULL, + .unlocked_ioctl = fanotify_ioctl, + .compat_ioctl = fanotify_ioctl, }; static void fanotify_free_mark(struct fsnotify_mark *fsn_mark) -- cgit v1.1 From ef601a9cfd21fe9ce57e0ee3f4a31552ffb96366 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: select ANON_INODES. fanotify references anon_inode_getfd(), which is only available with ANON_INODES enabled. Presently this bails out with the following: LD vmlinux fs/built-in.o: In function `sys_fanotify_init': (.text+0x26d1c): undefined reference to `anon_inode_getfd' make: *** [vmlinux] Error 1 which is trivially corrected by adding an ANON_INODES select. Signed-off-by: Paul Mundt Signed-off-by: Eric Paris --- fs/notify/fanotify/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'fs') diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig index f9d7ae0..668e5df 100644 --- a/fs/notify/fanotify/Kconfig +++ b/fs/notify/fanotify/Kconfig @@ -1,6 +1,7 @@ config FANOTIFY bool "Filesystem wide access notification" select FSNOTIFY + select ANON_INODES default y ---help--- Say Y here to enable fanotify suport. fanotify is a file access -- cgit v1.1 From 9bbfc964b89009d0cadcec7027afc92ee742e95e Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: CONFIG_HAVE_SYSCALL_WRAPPERS for sys_fanotify_mark Please note that you need the patch below in addition, otherwise the syscall wrapper stuff won't work on those 32 bit architectures which enable the wrappers. When enabled the syscall wrapper defines always take long parameters and then cast them to whatever is needed. This approach doesn't work for the 32 bit case where the original syscall takes a long long parameter, since we would lose the upper 32 bits. So syscalls with 64 bit arguments are special cases wrt to syscall wrappers and enp up in the ugliness below (see also sys_fallocate). In addition these special cased syscall wrappers have the drawback that ftrace syscall tracing doesn't work on them, since they don't get defined by using the usual macros. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a99550f..a9ced3f 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -468,8 +468,9 @@ out_put_group: return fd; } -SYSCALL_DEFINE5(fanotify_mark, int, fanotify_fd, unsigned int, flags, - __u64, mask, int, dfd, const char __user *, pathname) +SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, + __u64 mask, int dfd, + const char __user * pathname) { struct inode *inode; struct fsnotify_group *group; @@ -513,6 +514,17 @@ fput_and_out: return ret; } +#ifdef CONFIG_HAVE_SYSCALL_WRAPPERS +asmlinkage long SyS_fanotify_mark(long fanotify_fd, long flags, __u64 mask, + long dfd, long pathname) +{ + return SYSC_fanotify_mark((int) fanotify_fd, (unsigned int) flags, + mask, (int) dfd, + (const char __user *) pathname); +} +SYSCALL_ALIAS(sys_fanotify_mark, SyS_fanotify_mark); +#endif + /* * fanotify_user_setup - Our initialization function. Note that we cannnot return * error because we have compiled-in VFS hooks. So an (unlikely) failure here -- cgit v1.1 From 22aa425dec9e47051624714ae283eb2b6a473013 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:26 -0500 Subject: fanotify: create_fd cleanup Code cleanup which does the fd creation work seperately from the userspace metadata creation. It fits better with the other code. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index a9ced3f..cf9c300 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -43,17 +43,14 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, return fsnotify_remove_notify_event(group); } -static int create_and_fill_fd(struct fsnotify_group *group, - struct fanotify_event_metadata *metadata, - struct fsnotify_event *event) +static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) { int client_fd; struct dentry *dentry; struct vfsmount *mnt; struct file *new_file; - pr_debug("%s: group=%p metadata=%p event=%p\n", __func__, group, - metadata, event); + pr_debug("%s: group=%p event=%p\n", __func__, group, event); client_fd = get_unused_fd(); if (client_fd < 0) @@ -93,9 +90,7 @@ static int create_and_fill_fd(struct fsnotify_group *group, fd_install(client_fd, new_file); } - metadata->fd = client_fd; - - return 0; + return client_fd; } static ssize_t fill_event_metadata(struct fsnotify_group *group, @@ -108,9 +103,9 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->vers = FANOTIFY_METADATA_VERSION; metadata->mask = fanotify_outgoing_mask(event->mask); + metadata->fd = create_fd(group, event); - return create_and_fill_fd(group, metadata, event); - + return metadata->fd; } static ssize_t copy_event_to_user(struct fsnotify_group *group, @@ -123,7 +118,7 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, pr_debug("%s: group=%p event=%p\n", __func__, group, event); ret = fill_event_metadata(group, &fanotify_event_metadata, event); - if (ret) + if (ret < 0) return ret; if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) -- cgit v1.1 From 32c3263221bd63316815286dccacdc7abfd7f3c4 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fanotify: Add pids to events Pass the process identifiers of the triggering processes to fanotify listeners: this information is useful for event filtering and logging. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 5 +++-- fs/notify/fanotify/fanotify_user.c | 1 + fs/notify/notification.c | 3 +++ 3 files changed, 7 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 5b0b6b4..881067d 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -10,8 +10,9 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { pr_debug("%s: old=%p new=%p\n", __func__, old, new); - if ((old->to_tell == new->to_tell) && - (old->data_type == new->data_type)) { + if (old->to_tell == new->to_tell && + old->data_type == new->data_type && + old->tgid == new->tgid) { switch (old->data_type) { case (FSNOTIFY_EVENT_PATH): if ((old->path.mnt == new->path.mnt) && diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index cf9c300..66e38fc 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -103,6 +103,7 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->vers = FANOTIFY_METADATA_VERSION; metadata->mask = fanotify_outgoing_mask(event->mask); + metadata->pid = pid_vnr(event->tgid); metadata->fd = create_fd(group, event); return metadata->fd; diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 066f1f9..7fc8d00 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -93,6 +93,7 @@ void fsnotify_put_event(struct fsnotify_event *event) BUG_ON(!list_empty(&event->private_data_list)); kfree(event->file_name); + put_pid(event->tgid); kmem_cache_free(fsnotify_event_cachep, event); } } @@ -346,6 +347,7 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) return NULL; } } + event->tgid = get_pid(old_event->tgid); if (event->data_type == FSNOTIFY_EVENT_PATH) path_get(&event->path); @@ -385,6 +387,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->name_len = strlen(event->file_name); } + event->tgid = get_pid(task_tgid(current)); event->sync_cookie = cookie; event->to_tell = to_tell; event->data_type = data_type; -- cgit v1.1 From 5444e2981c31d0ed7465475e451b8437084337e5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: split generic and inode specific mark code currently all marking is done by functions in inode-mark.c. Some of this is pretty generic and should be instead done in a generic function and we should only put the inode specific code in inode-mark.c Signed-off-by: Eric Paris --- fs/notify/Makefile | 3 +- fs/notify/dnotify/dnotify.c | 12 +- fs/notify/fanotify/fanotify.c | 2 +- fs/notify/fanotify/fanotify_user.c | 8 +- fs/notify/fsnotify.h | 7 + fs/notify/inode_mark.c | 246 +++-------------------------- fs/notify/inotify/inotify_fsnotify.c | 4 +- fs/notify/inotify/inotify_user.c | 4 +- fs/notify/mark.c | 294 +++++++++++++++++++++++++++++++++++ 9 files changed, 337 insertions(+), 243 deletions(-) create mode 100644 fs/notify/mark.c (limited to 'fs') diff --git a/fs/notify/Makefile b/fs/notify/Makefile index 396a387..8f7f3b0 100644 --- a/fs/notify/Makefile +++ b/fs/notify/Makefile @@ -1,4 +1,5 @@ -obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o +obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \ + mark.o obj-y += dnotify/ obj-y += inotify/ diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index cac2eb8..69f42df 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -95,7 +95,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - fsn_mark = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_inode_mark(group, to_tell); if (unlikely(!fsn_mark)) return 0; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -143,14 +143,14 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, if (!S_ISDIR(inode->i_mode)) return false; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; mask = (mask & ~FS_EVENT_ON_CHILD); send = (mask & fsn_mark->mask); - fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_mark */ + fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_inode_mark */ return send; } @@ -193,7 +193,7 @@ void dnotify_flush(struct file *filp, fl_owner_t id) if (!S_ISDIR(inode->i_mode)) return; - fsn_mark = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_inode_mark(dnotify_group, inode); if (!fsn_mark) return; dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); @@ -346,12 +346,12 @@ int fcntl_dirnotify(int fd, struct file *filp, unsigned long arg) mutex_lock(&dnotify_mark_mutex); /* add the new_fsn_mark or find an old one. */ - fsn_mark = fsnotify_find_mark(dnotify_group, inode); + fsn_mark = fsnotify_find_inode_mark(dnotify_group, inode); if (fsn_mark) { dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); spin_lock(&fsn_mark->lock); } else { - fsnotify_add_mark(new_fsn_mark, dnotify_group, inode, 0); + fsnotify_add_mark(new_fsn_mark, dnotify_group, inode, NULL, 0); spin_lock(&new_fsn_mark->lock); fsn_mark = new_fsn_mark; dn_mark = new_dn_mark; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 881067d..aa5e926 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -118,7 +118,7 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod if (data_type != FSNOTIFY_EVENT_PATH) return false; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 66e38fc..0535193 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -305,7 +305,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return -ENOENT; @@ -321,7 +321,7 @@ static int fanotify_remove_mark(struct fsnotify_group *group, fsnotify_recalc_group_mask(group); - /* matches the fsnotify_find_mark() */ + /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); return 0; @@ -338,7 +338,7 @@ static int fanotify_add_mark(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { struct fsnotify_mark *new_fsn_mark; @@ -348,7 +348,7 @@ static int fanotify_add_mark(struct fsnotify_group *group, goto out; fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); - ret = fsnotify_add_mark(new_fsn_mark, group, inode, 0); + ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); if (ret) { fanotify_free_mark(new_fsn_mark); goto out; diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 2ba5915..7c7a904 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -20,6 +20,11 @@ extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); +/* add a mark to an inode */ +extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct inode *inode, + int allow_dups); + /* add a group to the inode group list */ extern void fsnotify_add_inode_group(struct fsnotify_group *group); /* add a group to the vfsmount group list */ @@ -27,6 +32,8 @@ extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); +/* inode specific destruction of a mark */ +extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark); /* run the list of all marks associated with inode and flag them to be freed */ extern void fsnotify_clear_marks_by_inode(struct inode *inode); /* diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index ba6f983..c925579 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -16,72 +16,6 @@ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ -/* - * fsnotify inode mark locking/lifetime/and refcnting - * - * REFCNT: - * The mark->refcnt tells how many "things" in the kernel currently are - * referencing this object. The object typically will live inside the kernel - * with a refcnt of 2, one for each list it is on (i_list, g_list). Any task - * which can find this object holding the appropriete locks, can take a reference - * and the object itself is guarenteed to survive until the reference is dropped. - * - * LOCKING: - * There are 3 spinlocks involved with fsnotify inode marks and they MUST - * be taken in order as follows: - * - * mark->lock - * group->mark_lock - * inode->i_lock - * - * mark->lock protects 2 things, mark->group and mark->inode. You must hold - * that lock to dereference either of these things (they could be NULL even with - * the lock) - * - * group->mark_lock protects the marks_list anchored inside a given group - * and each mark is hooked via the g_list. It also sorta protects the - * free_g_list, which when used is anchored by a private list on the stack of the - * task which held the group->mark_lock. - * - * inode->i_lock protects the i_fsnotify_marks list anchored inside a - * given inode and each mark is hooked via the i_list. (and sorta the - * free_i_list) - * - * - * LIFETIME: - * Inode marks survive between when they are added to an inode and when their - * refcnt==0. - * - * The inode mark can be cleared for a number of different reasons including: - * - The inode is unlinked for the last time. (fsnotify_inode_remove) - * - The inode is being evicted from cache. (fsnotify_inode_delete) - * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) - * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) - * - The fsnotify_group associated with the mark is going away and all such marks - * need to be cleaned up. (fsnotify_clear_marks_by_group) - * - * Worst case we are given an inode and need to clean up all the marks on that - * inode. We take i_lock and walk the i_fsnotify_marks safely. For each - * mark on the list we take a reference (so the mark can't disappear under us). - * We remove that mark form the inode's list of marks and we add this mark to a - * private list anchored on the stack using i_free_list; At this point we no - * longer fear anything finding the mark using the inode's list of marks. - * - * We can safely and locklessly run the private list on the stack of everything - * we just unattached from the original inode. For each mark on the private list - * we grab the mark-> and can thus dereference mark->group and mark->inode. If - * we see the group and inode are not NULL we take those locks. Now holding all - * 3 locks we can completely remove the mark from other tasks finding it in the - * future. Remember, 10 things might already be referencing this mark, but they - * better be holding a ref. We drop our reference we took before we unhooked it - * from the inode. When the ref hits 0 we can free the mark. - * - * Very similarly for freeing by group, except we use free_g_list. - * - * This has the very interesting property of being able to run concurrently with - * any (or all) other directions. - */ - #include #include #include @@ -95,17 +29,6 @@ #include #include "fsnotify.h" -void fsnotify_get_mark(struct fsnotify_mark *mark) -{ - atomic_inc(&mark->refcnt); -} - -void fsnotify_put_mark(struct fsnotify_mark *mark) -{ - if (atomic_dec_and_test(&mark->refcnt)) - mark->free_mark(mark); -} - /* * Recalculate the mask of events relevant to a given inode locked. */ @@ -135,44 +58,18 @@ void fsnotify_recalc_inode_mask(struct inode *inode) __fsnotify_update_child_dentry_flags(inode); } -/* - * Any time a mark is getting freed we end up here. - * The caller had better be holding a reference to this mark so we don't actually - * do the final put under the mark->lock - */ -void fsnotify_destroy_mark(struct fsnotify_mark *mark) +void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) { - struct fsnotify_group *group; - struct inode *inode; - - spin_lock(&mark->lock); + struct inode *inode = mark->i.inode; - group = mark->group; - inode = mark->i.inode; + assert_spin_locked(&mark->lock); + assert_spin_locked(&mark->group->mark_lock); - BUG_ON(group && !inode); - BUG_ON(!group && inode); - - /* if !group something else already marked this to die */ - if (!group) { - spin_unlock(&mark->lock); - return; - } - - /* 1 from caller and 1 for being on i_list/g_list */ - BUG_ON(atomic_read(&mark->refcnt) < 2); - - spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); hlist_del_init(&mark->i.i_list); mark->i.inode = NULL; - list_del_init(&mark->g_list); - mark->group = NULL; - - fsnotify_put_mark(mark); /* for i_list and g_list */ - /* * this mark is now off the inode->i_fsnotify_marks list and we * hold the inode->i_lock, so this is the perfect time to update the @@ -181,61 +78,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); - spin_unlock(&group->mark_lock); - spin_unlock(&mark->lock); - - /* - * Some groups like to know that marks are being freed. This is a - * callback to the group function to let it know that this mark - * is being freed. - */ - if (group->ops->freeing_mark) - group->ops->freeing_mark(mark, group); - - /* - * __fsnotify_update_child_dentry_flags(inode); - * - * I really want to call that, but we can't, we have no idea if the inode - * still exists the second we drop the mark->lock. - * - * The next time an event arrive to this inode from one of it's children - * __fsnotify_parent will see that the inode doesn't care about it's - * children and will update all of these flags then. So really this - * is just a lazy update (and could be a perf win...) - */ - - - iput(inode); - - /* - * it's possible that this group tried to destroy itself, but this - * this mark was simultaneously being freed by inode. If that's the - * case, we finish freeing the group here. - */ - if (unlikely(atomic_dec_and_test(&group->num_marks))) - fsnotify_final_destroy_group(group); -} - -/* - * Given a group, destroy all of the marks associated with that group. - */ -void fsnotify_clear_marks_by_group(struct fsnotify_group *group) -{ - struct fsnotify_mark *lmark, *mark; - LIST_HEAD(free_list); - - spin_lock(&group->mark_lock); - list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { - list_add(&mark->free_g_list, &free_list); - list_del_init(&mark->g_list); - fsnotify_get_mark(mark); - } - spin_unlock(&group->mark_lock); - - list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { - fsnotify_destroy_mark(mark); - fsnotify_put_mark(mark); - } } /* @@ -261,8 +103,12 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } } -static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *group, - struct inode *inode) +/* + * given a group and inode, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_inode_mark_locked(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; struct hlist_node *pos; @@ -282,50 +128,26 @@ static struct fsnotify_mark *fsnotify_find_mark_locked(struct fsnotify_group *gr * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ -struct fsnotify_mark *fsnotify_find_mark(struct fsnotify_group *group, - struct inode *inode) +struct fsnotify_mark *fsnotify_find_inode_mark(struct fsnotify_group *group, + struct inode *inode) { struct fsnotify_mark *mark; spin_lock(&inode->i_lock); - mark = fsnotify_find_mark_locked(group, inode); + mark = fsnotify_find_inode_mark_locked(group, inode); spin_unlock(&inode->i_lock); return mark; } -void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) -{ - assert_spin_locked(&old->lock); - new->i.inode = old->i.inode; - new->group = old->group; - new->mask = old->mask; - new->free_mark = old->free_mark; -} - -/* - * Nothing fancy, just initialize lists and locks and counters. - */ -void fsnotify_init_mark(struct fsnotify_mark *mark, - void (*free_mark)(struct fsnotify_mark *mark)) -{ - spin_lock_init(&mark->lock); - atomic_set(&mark->refcnt, 1); - INIT_HLIST_NODE(&mark->i.i_list); - mark->group = NULL; - mark->mask = 0; - mark->i.inode = NULL; - mark->free_mark = free_mark; -} - /* * Attach an initialized mark 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. */ -int fsnotify_add_mark(struct fsnotify_mark *mark, - struct fsnotify_group *group, struct inode *inode, - int allow_dups) +int fsnotify_add_inode_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct inode *inode, + int allow_dups) { struct fsnotify_mark *lmark = NULL; int ret = 0; @@ -336,56 +158,26 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, mark->flags = FSNOTIFY_MARK_FLAG_INODE; - /* - * if this group isn't being testing for inode type events we need - * to start testing - */ - if (unlikely(list_empty(&group->inode_group_list))) - fsnotify_add_inode_group(group); - /* - * XXX This is where we could also do the fsnotify_add_vfsmount_group - * if we are setting and vfsmount mark.... - - if (unlikely(list_empty(&group->vfsmount_group_list))) - fsnotify_add_vfsmount_group(group); - */ + assert_spin_locked(&mark->lock); + assert_spin_locked(&group->mark_lock); - /* - * LOCKING ORDER!!!! - * mark->lock - * group->mark_lock - * inode->i_lock - */ - spin_lock(&mark->lock); - spin_lock(&group->mark_lock); spin_lock(&inode->i_lock); if (!allow_dups) - lmark = fsnotify_find_mark_locked(group, inode); + lmark = fsnotify_find_inode_mark_locked(group, inode); if (!lmark) { - mark->group = group; mark->i.inode = inode; hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); - list_add(&mark->g_list, &group->marks_list); - - fsnotify_get_mark(mark); /* for i_list and g_list */ - - atomic_inc(&group->num_marks); fsnotify_recalc_inode_mask_locked(inode); } spin_unlock(&inode->i_lock); - spin_unlock(&group->mark_lock); - spin_unlock(&mark->lock); if (lmark) { ret = -EEXIST; iput(inode); - fsnotify_put_mark(lmark); - } else { - __fsnotify_update_child_dentry_flags(inode); } return ret; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index cc8f6bc..1d237e1 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -97,7 +97,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev to_tell = event->to_tell; - fsn_mark = fsnotify_find_mark(group, to_tell); + fsn_mark = fsnotify_find_inode_mark(group, to_tell); /* race with watch removal? We already passes should_send */ if (unlikely(!fsn_mark)) return 0; @@ -145,7 +145,7 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct fsnotify_mark *fsn_mark; bool send; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index ad5a1ea..a12315a 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -566,7 +566,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, if (unlikely(!mask)) return -EINVAL; - fsn_mark = fsnotify_find_mark(group, inode); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return -ENOENT; @@ -644,7 +644,7 @@ static int inotify_new_watch(struct fsnotify_group *group, goto out_err; /* we are on the idr, now get on the inode */ - ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, 0); + ret = fsnotify_add_mark(&tmp_i_mark->fsn_mark, group, inode, NULL, 0); if (ret) { /* we failed to get on the inode, get off the idr */ inotify_remove_from_idr(group, tmp_i_mark); diff --git a/fs/notify/mark.c b/fs/notify/mark.c new file mode 100644 index 0000000..e56e876 --- /dev/null +++ b/fs/notify/mark.c @@ -0,0 +1,294 @@ +/* + * Copyright (C) 2008 Red Hat, Inc., Eric Paris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * fsnotify inode mark locking/lifetime/and refcnting + * + * REFCNT: + * The mark->refcnt tells how many "things" in the kernel currently are + * referencing this object. The object typically will live inside the kernel + * with a refcnt of 2, one for each list it is on (i_list, g_list). Any task + * which can find this object holding the appropriete locks, can take a reference + * and the object itself is guarenteed to survive until the reference is dropped. + * + * LOCKING: + * There are 3 spinlocks involved with fsnotify inode marks and they MUST + * be taken in order as follows: + * + * mark->lock + * group->mark_lock + * inode->i_lock + * + * mark->lock protects 2 things, mark->group and mark->inode. You must hold + * that lock to dereference either of these things (they could be NULL even with + * the lock) + * + * group->mark_lock protects the marks_list anchored inside a given group + * and each mark is hooked via the g_list. It also sorta protects the + * free_g_list, which when used is anchored by a private list on the stack of the + * task which held the group->mark_lock. + * + * inode->i_lock protects the i_fsnotify_marks list anchored inside a + * given inode and each mark is hooked via the i_list. (and sorta the + * free_i_list) + * + * + * LIFETIME: + * Inode marks survive between when they are added to an inode and when their + * refcnt==0. + * + * The inode mark can be cleared for a number of different reasons including: + * - The inode is unlinked for the last time. (fsnotify_inode_remove) + * - The inode is being evicted from cache. (fsnotify_inode_delete) + * - The fs the inode is on is unmounted. (fsnotify_inode_delete/fsnotify_unmount_inodes) + * - Something explicitly requests that it be removed. (fsnotify_destroy_mark) + * - The fsnotify_group associated with the mark is going away and all such marks + * need to be cleaned up. (fsnotify_clear_marks_by_group) + * + * Worst case we are given an inode and need to clean up all the marks on that + * inode. We take i_lock and walk the i_fsnotify_marks safely. For each + * mark on the list we take a reference (so the mark can't disappear under us). + * We remove that mark form the inode's list of marks and we add this mark to a + * private list anchored on the stack using i_free_list; At this point we no + * longer fear anything finding the mark using the inode's list of marks. + * + * We can safely and locklessly run the private list on the stack of everything + * we just unattached from the original inode. For each mark on the private list + * we grab the mark-> and can thus dereference mark->group and mark->inode. If + * we see the group and inode are not NULL we take those locks. Now holding all + * 3 locks we can completely remove the mark from other tasks finding it in the + * future. Remember, 10 things might already be referencing this mark, but they + * better be holding a ref. We drop our reference we took before we unhooked it + * from the inode. When the ref hits 0 we can free the mark. + * + * Very similarly for freeing by group, except we use free_g_list. + * + * This has the very interesting property of being able to run concurrently with + * any (or all) other directions. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for inode_lock */ + +#include + +#include +#include "fsnotify.h" + +void fsnotify_get_mark(struct fsnotify_mark *mark) +{ + atomic_inc(&mark->refcnt); +} + +void fsnotify_put_mark(struct fsnotify_mark *mark) +{ + if (atomic_dec_and_test(&mark->refcnt)) + mark->free_mark(mark); +} + +/* + * Any time a mark is getting freed we end up here. + * The caller had better be holding a reference to this mark so we don't actually + * do the final put under the mark->lock + */ +void fsnotify_destroy_mark(struct fsnotify_mark *mark) +{ + struct fsnotify_group *group; + struct inode *inode; + + spin_lock(&mark->lock); + + group = mark->group; + inode = mark->i.inode; + + BUG_ON(group && !inode); + BUG_ON(!group && inode); + + /* if !group something else already marked this to die */ + if (!group) { + spin_unlock(&mark->lock); + return; + } + + /* 1 from caller and 1 for being on i_list/g_list */ + BUG_ON(atomic_read(&mark->refcnt) < 2); + + spin_lock(&group->mark_lock); + + if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) + fsnotify_destroy_inode_mark(mark); + else + BUG(); + + list_del_init(&mark->g_list); + mark->group = NULL; + + fsnotify_put_mark(mark); /* for i_list and g_list */ + + spin_unlock(&group->mark_lock); + spin_unlock(&mark->lock); + + /* + * Some groups like to know that marks are being freed. This is a + * callback to the group function to let it know that this mark + * is being freed. + */ + if (group->ops->freeing_mark) + group->ops->freeing_mark(mark, group); + + /* + * __fsnotify_update_child_dentry_flags(inode); + * + * I really want to call that, but we can't, we have no idea if the inode + * still exists the second we drop the mark->lock. + * + * The next time an event arrive to this inode from one of it's children + * __fsnotify_parent will see that the inode doesn't care about it's + * children and will update all of these flags then. So really this + * is just a lazy update (and could be a perf win...) + */ + + + iput(inode); + + /* + * it's possible that this group tried to destroy itself, but this + * this mark was simultaneously being freed by inode. If that's the + * case, we finish freeing the group here. + */ + if (unlikely(atomic_dec_and_test(&group->num_marks))) + fsnotify_final_destroy_group(group); +} + +/* + * Attach an initialized mark to a given group and fs object. + * These marks may be used for the fsnotify backend to determine which + * event types should be delivered to which group. + */ +int fsnotify_add_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, int allow_dups) +{ + int ret = 0; + + BUG_ON(mnt); + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); + + /* + * if this group isn't being testing for inode type events we need + * to start testing + */ + if (inode && unlikely(list_empty(&group->inode_group_list))) + fsnotify_add_inode_group(group); + else if (mnt && unlikely(list_empty(&group->vfsmount_group_list))) + fsnotify_add_vfsmount_group(group); + + /* + * LOCKING ORDER!!!! + * mark->lock + * group->mark_lock + * inode->i_lock + */ + spin_lock(&mark->lock); + spin_lock(&group->mark_lock); + + mark->group = group; + list_add(&mark->g_list, &group->marks_list); + atomic_inc(&group->num_marks); + fsnotify_get_mark(mark); /* for i_list and g_list */ + + if (inode) { + ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); + if (ret) + goto err; + } else { + BUG(); + } + + spin_unlock(&group->mark_lock); + spin_unlock(&mark->lock); + + if (inode) + __fsnotify_update_child_dentry_flags(inode); + + return ret; +err: + mark->group = NULL; + list_del_init(&mark->g_list); + atomic_dec(&group->num_marks); + fsnotify_put_mark(mark); + + spin_unlock(&group->mark_lock); + spin_unlock(&mark->lock); + + return ret; +} + +/* + * Given a group, destroy all of the marks associated with that group. + */ +void fsnotify_clear_marks_by_group(struct fsnotify_group *group) +{ + struct fsnotify_mark *lmark, *mark; + LIST_HEAD(free_list); + + spin_lock(&group->mark_lock); + list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { + list_add(&mark->free_g_list, &free_list); + list_del_init(&mark->g_list); + fsnotify_get_mark(mark); + } + spin_unlock(&group->mark_lock); + + list_for_each_entry_safe(mark, lmark, &free_list, free_g_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); + } +} + +void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) +{ + assert_spin_locked(&old->lock); + new->i.inode = old->i.inode; + new->m.mnt = old->m.mnt; + new->group = old->group; + new->mask = old->mask; + new->free_mark = old->free_mark; +} + +/* + * Nothing fancy, just initialize lists and locks and counters. + */ +void fsnotify_init_mark(struct fsnotify_mark *mark, + void (*free_mark)(struct fsnotify_mark *mark)) +{ + spin_lock_init(&mark->lock); + atomic_set(&mark->refcnt, 1); + INIT_HLIST_NODE(&mark->i.i_list); + mark->group = NULL; + mark->mask = 0; + mark->i.inode = NULL; + mark->free_mark = free_mark; +} -- cgit v1.1 From ba643f04cdda170215c8820acd3e201936fc512d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: clear marks to 0 in fsnotify_init_mark Currently fsnotify_init_mark sets some fields to 0/NULL. Some users already used some sorts of zalloc, some didn't. This patch uses memset to explicitly zero everything in the fsnotify_mark when it is initialized so we don't have to be careful if fields are later added to marks. Signed-off-by: Eric Paris --- fs/notify/mark.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index e56e876..57bb1d7 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -284,11 +284,8 @@ void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *ol void fsnotify_init_mark(struct fsnotify_mark *mark, void (*free_mark)(struct fsnotify_mark *mark)) { + memset(mark, 0, sizeof(*mark)); spin_lock_init(&mark->lock); atomic_set(&mark->refcnt, 1); - INIT_HLIST_NODE(&mark->i.i_list); - mark->group = NULL; - mark->mask = 0; - mark->i.inode = NULL; mark->free_mark = free_mark; } -- cgit v1.1 From 2504c5d63b811b71bbaa8d5d5af163e698f4df1f Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify/vfsmount: add fsnotify fields to struct vfsmount This patch adds the list and mask fields needed to support vfsmount marks. These are the same fields fsnotify needs on an inode. They are not used, just declared and we note where the cleanup hook should be (the function is not yet defined) Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/namespace.c | 4 ++++ fs/notify/fsnotify.c | 4 +--- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index 88058de..a2d681a 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "pnode.h" @@ -150,6 +151,9 @@ struct vfsmount *alloc_vfsmnt(const char *name) INIT_LIST_HEAD(&mnt->mnt_share); INIT_LIST_HEAD(&mnt->mnt_slave_list); INIT_LIST_HEAD(&mnt->mnt_slave); +#ifdef CONFIG_FSNOTIFY + INIT_HLIST_HEAD(&mnt->mnt_fsnotify_marks); +#endif #ifdef CONFIG_SMP mnt->mnt_writers = alloc_percpu(int); if (!mnt->mnt_writers) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 60e84fd..e0bf869 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -163,9 +163,7 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) if (!mnt) return false; - /* hook in this when mnt->mnt_fsnotify_mask is defined */ - /* return (test_mask & path->mnt->mnt_fsnotify_mask); */ - return false; + return (test_mask & mnt->mnt_fsnotify_mask); } /* * This is the main call to fsnotify. The VFS calls into hook specific functions -- cgit v1.1 From 0d48b7f01f442bc88a69aa98f3b6b015f2817608 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: vfsmount marks generic functions Much like inode-mark.c has all of the code dealing with marks on inodes this patch adds a vfsmount-mark.c which has similar code but is intended for marks on vfsmounts. Signed-off-by: Eric Paris --- fs/notify/Makefile | 2 +- fs/notify/fsnotify.h | 6 ++ fs/notify/mark.c | 20 +++--- fs/notify/vfsmount_mark.c | 171 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 189 insertions(+), 10 deletions(-) create mode 100644 fs/notify/vfsmount_mark.c (limited to 'fs') diff --git a/fs/notify/Makefile b/fs/notify/Makefile index 8f7f3b0..ae5f33a 100644 --- a/fs/notify/Makefile +++ b/fs/notify/Makefile @@ -1,5 +1,5 @@ obj-$(CONFIG_FSNOTIFY) += fsnotify.o notification.o group.o inode_mark.o \ - mark.o + mark.o vfsmount_mark.o obj-y += dnotify/ obj-y += inotify/ diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 7c7a904..38f3fb5 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -24,6 +24,10 @@ extern void fsnotify_flush_notify(struct fsnotify_group *group); extern int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups); +/* add a mark to a vfsmount */ +extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct vfsmount *mnt, + int allow_dups); /* add a group to the inode group list */ extern void fsnotify_add_inode_group(struct fsnotify_group *group); @@ -32,6 +36,8 @@ extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); +/* vfsmount specific destruction of a mark */ +extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark); /* inode specific destruction of a mark */ extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark); /* run the list of all marks associated with inode and flag them to be freed */ diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 57bb1d7..d296ec9 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -115,15 +115,11 @@ void fsnotify_put_mark(struct fsnotify_mark *mark) void fsnotify_destroy_mark(struct fsnotify_mark *mark) { struct fsnotify_group *group; - struct inode *inode; + struct inode *inode = NULL; spin_lock(&mark->lock); group = mark->group; - inode = mark->i.inode; - - BUG_ON(group && !inode); - BUG_ON(!group && inode); /* if !group something else already marked this to die */ if (!group) { @@ -136,8 +132,11 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) spin_lock(&group->mark_lock); - if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) + if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { fsnotify_destroy_inode_mark(mark); + inode = mark->i.inode; + } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) + fsnotify_destroy_vfsmount_mark(mark); else BUG(); @@ -169,8 +168,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) * is just a lazy update (and could be a perf win...) */ - - iput(inode); + if (inode) + iput(inode); /* * it's possible that this group tried to destroy itself, but this @@ -192,7 +191,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, { int ret = 0; - BUG_ON(mnt); BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); @@ -223,6 +221,10 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, ret = fsnotify_add_inode_mark(mark, group, inode, allow_dups); if (ret) goto err; + } else if (mnt) { + ret = fsnotify_add_vfsmount_mark(mark, group, mnt, allow_dups); + if (ret) + goto err; } else { BUG(); } diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c new file mode 100644 index 0000000..1b61d0a --- /dev/null +++ b/fs/notify/vfsmount_mark.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2008 Red Hat, Inc., Eric Paris + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for inode_lock */ + +#include + +#include +#include "fsnotify.h" + +void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) +{ + struct fsnotify_mark *mark, *lmark; + struct hlist_node *pos, *n; + LIST_HEAD(free_list); + + spin_lock(&mnt->mnt_root->d_lock); + hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) { + list_add(&mark->m.free_m_list, &free_list); + hlist_del_init(&mark->m.m_list); + fsnotify_get_mark(mark); + } + spin_unlock(&mnt->mnt_root->d_lock); + + list_for_each_entry_safe(mark, lmark, &free_list, m.free_m_list) { + fsnotify_destroy_mark(mark); + fsnotify_put_mark(mark); + } +} + +/* + * Recalculate the mask of events relevant to a given vfsmount locked. + */ +static void fsnotify_recalc_vfsmount_mask_locked(struct vfsmount *mnt) +{ + struct fsnotify_mark *mark; + struct hlist_node *pos; + __u32 new_mask = 0; + + assert_spin_locked(&mnt->mnt_root->d_lock); + + hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list) + new_mask |= mark->mask; + mnt->mnt_fsnotify_mask = new_mask; +} + +/* + * Recalculate the mnt->mnt_fsnotify_mask, or the mask of all FS_* event types + * any notifier is interested in hearing for this mount point + */ +void fsnotify_recalc_vfsmount_mask(struct vfsmount *mnt) +{ + spin_lock(&mnt->mnt_root->d_lock); + fsnotify_recalc_vfsmount_mask_locked(mnt); + spin_unlock(&mnt->mnt_root->d_lock); +} + +void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark) +{ + struct vfsmount *mnt = mark->m.mnt; + + assert_spin_locked(&mark->lock); + assert_spin_locked(&mark->group->mark_lock); + + spin_lock(&mnt->mnt_root->d_lock); + + hlist_del_init(&mark->m.m_list); + mark->m.mnt = NULL; + + fsnotify_recalc_vfsmount_mask_locked(mnt); + + spin_unlock(&mnt->mnt_root->d_lock); +} + +static struct fsnotify_mark *fsnotify_find_vfsmount_mark_locked(struct fsnotify_group *group, + struct vfsmount *mnt) +{ + struct fsnotify_mark *mark; + struct hlist_node *pos; + + assert_spin_locked(&mnt->mnt_root->d_lock); + + hlist_for_each_entry(mark, pos, &mnt->mnt_fsnotify_marks, m.m_list) { + if (mark->group == group) { + fsnotify_get_mark(mark); + return mark; + } + } + return NULL; +} + +/* + * given a group and vfsmount, find the mark associated with that combination. + * if found take a reference to that mark and return it, else return NULL + */ +struct fsnotify_mark *fsnotify_find_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt) +{ + struct fsnotify_mark *mark; + + spin_lock(&mnt->mnt_root->d_lock); + mark = fsnotify_find_vfsmount_mark_locked(group, mnt); + spin_unlock(&mnt->mnt_root->d_lock); + + return mark; +} + +/* + * Attach an initialized mark to a given group and vfsmount. + * These marks may be used for the fsnotify backend to determine which + * event types should be delivered to which groups. + */ +int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, + struct fsnotify_group *group, struct vfsmount *mnt, + int allow_dups) +{ + struct fsnotify_mark *lmark = NULL; + int ret = 0; + + mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT; + + /* + * LOCKING ORDER!!!! + * mark->lock + * group->mark_lock + * mnt->mnt_root->d_lock + */ + assert_spin_locked(&mark->lock); + assert_spin_locked(&group->mark_lock); + + spin_lock(&mnt->mnt_root->d_lock); + + if (!allow_dups) + lmark = fsnotify_find_vfsmount_mark_locked(group, mnt); + if (!lmark) { + mark->m.mnt = mnt; + + hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); + + fsnotify_recalc_vfsmount_mask_locked(mnt); + } else { + ret = -EEXIST; + } + + spin_unlock(&mnt->mnt_root->d_lock); + + return ret; +} -- cgit v1.1 From ca9c726eea013394d1e846331b117effb21ead83 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:27 -0500 Subject: fsnotify: Infrastructure for per-mount watches Per-mount watches allow groups to listen to fsnotify events on an entire mount. This patch simply adds and initializes the fields needed in the vfsmount struct to make this happen. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/namespace.c | 1 + fs/notify/fsnotify.c | 5 +++++ fs/notify/fsnotify.h | 2 ++ 3 files changed, 8 insertions(+) (limited to 'fs') diff --git a/fs/namespace.c b/fs/namespace.c index a2d681a..1969d6b 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -614,6 +614,7 @@ static inline void __mntput(struct vfsmount *mnt) * provides barriers, so count_mnt_writers() below is safe. AV */ WARN_ON(count_mnt_writers(mnt)); + fsnotify_vfsmount_delete(mnt); dput(mnt->mnt_root); free_vfsmnt(mnt); deactivate_super(sb); diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index e0bf869..7f14ddc 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -36,6 +36,11 @@ void __fsnotify_inode_delete(struct inode *inode) } EXPORT_SYMBOL_GPL(__fsnotify_inode_delete); +void __fsnotify_vfsmount_delete(struct vfsmount *mnt) +{ + fsnotify_clear_marks_by_mount(mnt); +} + /* * Given an inode, first check if we care what happens to our children. Inotify * and dnotify both tell their parents about events. If we care about any event diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 38f3fb5..204353c 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -42,6 +42,8 @@ extern void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark); extern void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark); /* run the list of all marks associated with inode and flag them to be freed */ extern void fsnotify_clear_marks_by_inode(struct inode *inode); +/* run the list of all marks associated with vfsmount and flag them to be freed */ +extern void fsnotify_clear_marks_by_mount(struct vfsmount *mnt); /* * update the dentry->d_flags of all of inode's children to indicate if inode cares * about events that happen to its children. -- cgit v1.1 From 1c529063a3e4c15eaae28db31326a7aaab7091b5 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: should_send_event needs to handle vfsmounts currently should_send_event in fanotify only cares about marks on inodes. This patch extends that interface to indicate that it cares about events that happened on vfsmounts. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 56 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 45 insertions(+), 11 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index aa5e926..202be8a 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -2,6 +2,7 @@ #include #include #include /* UINT_MAX */ +#include #include #include "fanotify.h" @@ -99,24 +100,35 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e return ret; } -static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) +static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt, + __u32 mask) { struct fsnotify_mark *fsn_mark; bool send; - pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", - __func__, group, inode, mask, data, data_type); + pr_debug("%s: group=%p vfsmount=%p mask=%x\n", + __func__, group, mnt, mask); - /* sorry, fanotify only gives a damn about files and dirs */ - if (!S_ISREG(inode->i_mode) && - !S_ISDIR(inode->i_mode)) + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!fsn_mark) return false; - /* if we don't have enough info to send an event to userspace say no */ - if (data_type != FSNOTIFY_EVENT_PATH) - return false; + send = (mask & fsn_mark->mask); + + /* find took a reference */ + fsnotify_put_mark(fsn_mark); + + return send; +} + +static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode, + __u32 mask) +{ + struct fsnotify_mark *fsn_mark; + bool send; + + pr_debug("%s: group=%p inode=%p mask=%x\n", + __func__, group, inode, mask); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) @@ -142,6 +154,28 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return send; } +static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, + struct vfsmount *mnt, __u32 mask, void *data, + int data_type) +{ + pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", + __func__, group, to_tell, mnt, mask, data, data_type); + + /* sorry, fanotify only gives a damn about files and dirs */ + if (!S_ISREG(to_tell->i_mode) && + !S_ISDIR(to_tell->i_mode)) + return false; + + /* if we don't have enough info to send an event to userspace say no */ + if (data_type != FSNOTIFY_EVENT_PATH) + return false; + + if (mnt) + return should_send_vfsmount_event(group, mnt, mask); + else + return should_send_inode_event(group, to_tell, mask); +} + const struct fsnotify_ops fanotify_fsnotify_ops = { .handle_event = fanotify_handle_event, .should_send_event = fanotify_should_send_event, -- cgit v1.1 From 88826276dcaf4cef9cc7c2695ff15c6d20d4a74d Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: infrastructure to add an remove marks on vfsmounts infrastructure work to add and remove marks on vfsmounts. This should get every set up except wiring the functions to the syscalls. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 185 ++++++++++++++++++++++++++----------- 1 file changed, 133 insertions(+), 52 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 0535193..cb7a0c5 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -295,31 +295,83 @@ out: return ret; } -static int fanotify_remove_mark(struct fsnotify_group *group, - struct inode *inode, - __u32 mask) +static void fanotify_update_object_mask(struct fsnotify_group *group, + struct inode *inode, + struct vfsmount *mnt, + struct fsnotify_mark *fsn_mark, + unsigned int flags, + __u32 mask) { - struct fsnotify_mark *fsn_mark; - __u32 new_mask; - - pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, - group, inode, mask); + __u32 old_mask, new_mask; - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return -ENOENT; + pr_debug("%s: group=%p inode=%p mnt=%p fsn_mark=%p flags=%x mask=%x\n", + __func__, group, inode, mnt, fsn_mark, flags, mask); spin_lock(&fsn_mark->lock); - fsn_mark->mask &= ~mask; + old_mask = fsn_mark->mask; + if (flags & FAN_MARK_ADD) + fsn_mark->mask |= mask; + else if (flags & FAN_MARK_REMOVE) + fsn_mark->mask &= ~mask; + else + BUG(); new_mask = fsn_mark->mask; spin_unlock(&fsn_mark->lock); if (!new_mask) fsnotify_destroy_mark(fsn_mark); + + /* we made changes to a mask, update the group mask and the object mask + * so things happen quickly. */ + if (old_mask != new_mask) { + __u32 dropped, do_object, do_group; + + /* more bits in old than in new? */ + dropped = (old_mask & ~new_mask); + /* more bits in this fsn_mark than the group? */ + do_group = (new_mask & ~group->mask); + + if (inode) { + /* more bits in this fsn_mark than the object's mask? */ + do_object = (new_mask & ~inode->i_fsnotify_mask); + /* update the object with this new fsn_mark */ + if (dropped || do_object) + fsnotify_recalc_inode_mask(inode); + } else if (mnt) { + /* more bits in this fsn_mark than the object's mask? */ + do_object = (new_mask & ~mnt->mnt_fsnotify_mask); + /* update the object with this new fsn_mark */ + if (dropped || do_object) + fsnotify_recalc_vfsmount_mask(mnt); + } else { + BUG(); + } + + /* update the group mask with the new mask */ + if (dropped || do_group) + fsnotify_recalc_group_mask(group); + } +} + +static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, unsigned int flags, __u32 mask) +{ + struct fsnotify_mark *fsn_mark = NULL; + + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); + + if (inode) + fsn_mark = fsnotify_find_inode_mark(group, inode); + else if (mnt) + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); else - fsnotify_recalc_inode_mask(inode); + BUG(); - fsnotify_recalc_group_mask(group); + if (!fsn_mark) + return -ENOENT; + + fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); @@ -327,22 +379,48 @@ static int fanotify_remove_mark(struct fsnotify_group *group, return 0; } -static int fanotify_add_mark(struct fsnotify_group *group, - struct inode *inode, - __u32 mask) +static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt) { struct fsnotify_mark *fsn_mark; - __u32 old_mask, new_mask; - int ret; - pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, - group, inode, mask); + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!fsn_mark) { + struct fsnotify_mark *new_fsn_mark; + int ret; + + fsn_mark = ERR_PTR(-ENOMEM); + new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!new_fsn_mark) + goto out; + + fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(new_fsn_mark, group, NULL, mnt, 0); + if (ret) { + fsn_mark = ERR_PTR(ret); + fanotify_free_mark(new_fsn_mark); + goto out; + } + + fsn_mark = new_fsn_mark; + } +out: + return fsn_mark; +} + +static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group, + struct inode *inode) +{ + struct fsnotify_mark *fsn_mark; + + pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { struct fsnotify_mark *new_fsn_mark; + int ret; - ret = -ENOMEM; + fsn_mark = ERR_PTR(-ENOMEM); new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!new_fsn_mark) goto out; @@ -350,57 +428,60 @@ static int fanotify_add_mark(struct fsnotify_group *group, fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); if (ret) { + fsn_mark = ERR_PTR(ret); fanotify_free_mark(new_fsn_mark); goto out; } fsn_mark = new_fsn_mark; } +out: + return fsn_mark; +} - ret = 0; +static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, + struct vfsmount *mnt, unsigned int flags, __u32 mask) +{ + struct fsnotify_mark *fsn_mark; - spin_lock(&fsn_mark->lock); - old_mask = fsn_mark->mask; - fsn_mark->mask |= mask; - new_mask = fsn_mark->mask; - spin_unlock(&fsn_mark->lock); + pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", + __func__, group, inode, mnt, flags, mask); - /* we made changes to a mask, update the group mask and the inode mask - * so things happen quickly. */ - if (old_mask != new_mask) { - /* more bits in old than in new? */ - int dropped = (old_mask & ~new_mask); - /* more bits in this mark than the inode's mask? */ - int do_inode = (new_mask & ~inode->i_fsnotify_mask); - /* more bits in this mark than the group? */ - int do_group = (new_mask & ~group->mask); + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); - /* update the inode with this new mark */ - if (dropped || do_inode) - fsnotify_recalc_inode_mask(inode); + if (inode) + fsn_mark = fanotify_add_inode_mark(group, inode); + else if (mnt) + fsn_mark = fanotify_add_vfsmount_mark(group, mnt); + else + BUG(); - /* update the group mask with the new mask */ - if (dropped || do_group) - fsnotify_recalc_group_mask(group); - } + if (IS_ERR(fsn_mark)) + goto out; + + fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); /* match the init or the find.... */ fsnotify_put_mark(fsn_mark); out: - return ret; + return PTR_ERR(fsn_mark); } static int fanotify_update_mark(struct fsnotify_group *group, - struct inode *inode, int flags, - __u32 mask) + struct inode *inode, struct vfsmount *mnt, + int flags, __u32 mask) { - pr_debug("%s: group=%p inode=%p flags=%x mask=%x\n", __func__, - group, inode, flags, mask); + pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", + __func__, group, inode, mnt, flags, mask); + + BUG_ON(inode && mnt); + BUG_ON(!inode && !mnt); if (flags & FAN_MARK_ADD) - fanotify_add_mark(group, inode, mask); + fanotify_add_mark(group, inode, mnt, flags, mask); else if (flags & FAN_MARK_REMOVE) - fanotify_remove_mark(group, inode, mask); + fanotify_remove_mark(group, inode, mnt, flags, mask); else BUG(); @@ -502,7 +583,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, group = filp->private_data; /* create/update an inode mark */ - ret = fanotify_update_mark(group, inode, flags, mask); + ret = fanotify_update_mark(group, inode, NULL, flags, mask); path_put(&path); fput_and_out: -- cgit v1.1 From c6223f464927cab9f4b10169b78c51d84228faf8 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: remove fanotify_update_mark fanotify_update_mark() doesn't do much useful; remove it. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index cb7a0c5..0f25fc2 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -468,26 +468,6 @@ out: return PTR_ERR(fsn_mark); } -static int fanotify_update_mark(struct fsnotify_group *group, - struct inode *inode, struct vfsmount *mnt, - int flags, __u32 mask) -{ - pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", - __func__, group, inode, mnt, flags, mask); - - BUG_ON(inode && mnt); - BUG_ON(!inode && !mnt); - - if (flags & FAN_MARK_ADD) - fanotify_add_mark(group, inode, mnt, flags, mask); - else if (flags & FAN_MARK_REMOVE) - fanotify_remove_mark(group, inode, mnt, flags, mask); - else - BUG(); - - return 0; -} - static bool fanotify_mark_validate_input(int flags, __u32 mask) { @@ -583,7 +563,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, group = filp->private_data; /* create/update an inode mark */ - ret = fanotify_update_mark(group, inode, NULL, flags, mask); + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + case FAN_MARK_ADD: + ret = fanotify_add_mark(group, inode, NULL, flags, mask); + break; + case FAN_MARK_REMOVE: + ret = fanotify_remove_mark(group, inode, NULL, flags, mask); + break; + default: + ret = -EINVAL; + } path_put(&path); fput_and_out: -- cgit v1.1 From 088b09b0ac7a866a35962eeaea5d5607bd1840b7 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: do not call fanotify_update_object_mask in fanotify_remove_mark Recalculate masks in fanotify_remove_mark, don't use fanotify_update_object_mask. This gets us one step closers to readable code. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 0f25fc2..96d4ffd 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -353,10 +353,26 @@ static void fanotify_update_object_mask(struct fsnotify_group *group, } } +static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +{ + __u32 oldmask; + + spin_lock(&fsn_mark->lock); + oldmask = fsn_mark->mask; + fsn_mark->mask = oldmask & ~mask; + spin_unlock(&fsn_mark->lock); + + if (!(oldmask & ~mask)) + fsnotify_destroy_mark(fsn_mark); + + return mask & oldmask; +} + static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, unsigned int flags, __u32 mask) + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark = NULL; + __u32 removed; BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); @@ -371,11 +387,20 @@ static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inod if (!fsn_mark) return -ENOENT; - fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); - + removed = fanotify_mark_remove_from_mask(fsn_mark, mask); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); + if (removed & group->mask) + fsnotify_recalc_group_mask(group); + if (inode) { + if (removed & inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); + } else if (mnt) { + if (removed & mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + } + return 0; } @@ -568,7 +593,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, ret = fanotify_add_mark(group, inode, NULL, flags, mask); break; case FAN_MARK_REMOVE: - ret = fanotify_remove_mark(group, inode, NULL, flags, mask); + ret = fanotify_remove_mark(group, inode, NULL, mask); break; default: ret = -EINVAL; -- cgit v1.1 From 912ee3946c5e57de0d05baf3b60b65ce6bf3ff96 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: do not call fanotify_update_object_mask in fanotify_add_mark Recalculate masks in fanotify_add_mark, don't use fanotify_update_object_mask. This gets us one step closers to readable code. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 144 +++++++++++++------------------------ 1 file changed, 50 insertions(+), 94 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 96d4ffd..db74677 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -295,64 +295,6 @@ out: return ret; } -static void fanotify_update_object_mask(struct fsnotify_group *group, - struct inode *inode, - struct vfsmount *mnt, - struct fsnotify_mark *fsn_mark, - unsigned int flags, - __u32 mask) -{ - __u32 old_mask, new_mask; - - pr_debug("%s: group=%p inode=%p mnt=%p fsn_mark=%p flags=%x mask=%x\n", - __func__, group, inode, mnt, fsn_mark, flags, mask); - - spin_lock(&fsn_mark->lock); - old_mask = fsn_mark->mask; - if (flags & FAN_MARK_ADD) - fsn_mark->mask |= mask; - else if (flags & FAN_MARK_REMOVE) - fsn_mark->mask &= ~mask; - else - BUG(); - new_mask = fsn_mark->mask; - spin_unlock(&fsn_mark->lock); - - if (!new_mask) - fsnotify_destroy_mark(fsn_mark); - - /* we made changes to a mask, update the group mask and the object mask - * so things happen quickly. */ - if (old_mask != new_mask) { - __u32 dropped, do_object, do_group; - - /* more bits in old than in new? */ - dropped = (old_mask & ~new_mask); - /* more bits in this fsn_mark than the group? */ - do_group = (new_mask & ~group->mask); - - if (inode) { - /* more bits in this fsn_mark than the object's mask? */ - do_object = (new_mask & ~inode->i_fsnotify_mask); - /* update the object with this new fsn_mark */ - if (dropped || do_object) - fsnotify_recalc_inode_mask(inode); - } else if (mnt) { - /* more bits in this fsn_mark than the object's mask? */ - do_object = (new_mask & ~mnt->mnt_fsnotify_mask); - /* update the object with this new fsn_mark */ - if (dropped || do_object) - fsnotify_recalc_vfsmount_mask(mnt); - } else { - BUG(); - } - - /* update the group mask with the new mask */ - if (dropped || do_group) - fsnotify_recalc_group_mask(group); - } -} - static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u32 mask) { __u32 oldmask; @@ -404,89 +346,103 @@ static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inod return 0; } +static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +{ + __u32 oldmask; + + spin_lock(&fsn_mark->lock); + oldmask = fsn_mark->mask; + fsn_mark->mask = oldmask | mask; + spin_unlock(&fsn_mark->lock); + + return mask & ~oldmask; +} + static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt) + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark; + __u32 added; fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); if (!fsn_mark) { - struct fsnotify_mark *new_fsn_mark; int ret; - fsn_mark = ERR_PTR(-ENOMEM); - new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); - if (!new_fsn_mark) - goto out; + fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!fsn_mark) + return ERR_PTR(-ENOMEM); - fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); - ret = fsnotify_add_mark(new_fsn_mark, group, NULL, mnt, 0); + fsnotify_init_mark(fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); if (ret) { - fsn_mark = ERR_PTR(ret); - fanotify_free_mark(new_fsn_mark); - goto out; + fanotify_free_mark(fsn_mark); + return ERR_PTR(ret); } - - fsn_mark = new_fsn_mark; } -out: + added = fanotify_mark_add_to_mask(fsn_mark, mask); + if (added) { + if (added & ~group->mask) + fsnotify_recalc_group_mask(group); + if (added & ~mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + } return fsn_mark; } static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group, - struct inode *inode) + struct inode *inode, __u32 mask) { struct fsnotify_mark *fsn_mark; + __u32 added; pr_debug("%s: group=%p inode=%p\n", __func__, group, inode); fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) { - struct fsnotify_mark *new_fsn_mark; int ret; - fsn_mark = ERR_PTR(-ENOMEM); - new_fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); - if (!new_fsn_mark) - goto out; + fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); + if (!fsn_mark) + return ERR_PTR(-ENOMEM); - fsnotify_init_mark(new_fsn_mark, fanotify_free_mark); - ret = fsnotify_add_mark(new_fsn_mark, group, inode, NULL, 0); + fsnotify_init_mark(fsn_mark, fanotify_free_mark); + ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); if (ret) { - fsn_mark = ERR_PTR(ret); - fanotify_free_mark(new_fsn_mark); - goto out; + fanotify_free_mark(fsn_mark); + return ERR_PTR(ret); } - - fsn_mark = new_fsn_mark; } -out: + added = fanotify_mark_add_to_mask(fsn_mark, mask); + if (added) { + if (added & ~group->mask) + fsnotify_recalc_group_mask(group); + if (added & ~inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); + } return fsn_mark; } static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, unsigned int flags, __u32 mask) + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark; - pr_debug("%s: group=%p inode=%p mnt=%p flags=%x mask=%x\n", - __func__, group, inode, mnt, flags, mask); + pr_debug("%s: group=%p inode=%p mnt=%p mask=%x\n", + __func__, group, inode, mnt, mask); BUG_ON(inode && mnt); BUG_ON(!inode && !mnt); if (inode) - fsn_mark = fanotify_add_inode_mark(group, inode); + fsn_mark = fanotify_add_inode_mark(group, inode, mask); else if (mnt) - fsn_mark = fanotify_add_vfsmount_mark(group, mnt); + fsn_mark = fanotify_add_vfsmount_mark(group, mnt, mask); else BUG(); if (IS_ERR(fsn_mark)) goto out; - fanotify_update_object_mask(group, inode, mnt, fsn_mark, flags, mask); - /* match the init or the find.... */ fsnotify_put_mark(fsn_mark); out: @@ -590,7 +546,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - ret = fanotify_add_mark(group, inode, NULL, flags, mask); + ret = fanotify_add_mark(group, inode, NULL, mask); break; case FAN_MARK_REMOVE: ret = fanotify_remove_mark(group, inode, NULL, mask); -- cgit v1.1 From 52202dfbd9107787dc68a2019cc7be4e79f52e5c Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: do not return pointer from fanotify_add_*_mark No need to return the mark from fanotify_add_*_mark to fanotify_add_mark Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index db74677..7d7c138 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -358,8 +358,8 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mas return mask & ~oldmask; } -static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt, __u32 mask) +static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -370,27 +370,28 @@ static struct fsnotify_mark *fanotify_add_vfsmount_mark(struct fsnotify_group *g fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!fsn_mark) - return ERR_PTR(-ENOMEM); + return -ENOMEM; fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, NULL, mnt, 0); if (ret) { fanotify_free_mark(fsn_mark); - return ERR_PTR(ret); + return ret; } } added = fanotify_mark_add_to_mask(fsn_mark, mask); + fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) fsnotify_recalc_group_mask(group); if (added & ~mnt->mnt_fsnotify_mask) fsnotify_recalc_vfsmount_mask(mnt); } - return fsn_mark; + return 0; } -static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *group, - struct inode *inode, __u32 mask) +static int fanotify_add_inode_mark(struct fsnotify_group *group, + struct inode *inode, __u32 mask) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -403,29 +404,30 @@ static struct fsnotify_mark *fanotify_add_inode_mark(struct fsnotify_group *grou fsn_mark = kmem_cache_alloc(fanotify_mark_cache, GFP_KERNEL); if (!fsn_mark) - return ERR_PTR(-ENOMEM); + return -ENOMEM; fsnotify_init_mark(fsn_mark, fanotify_free_mark); ret = fsnotify_add_mark(fsn_mark, group, inode, NULL, 0); if (ret) { fanotify_free_mark(fsn_mark); - return ERR_PTR(ret); + return ret; } } added = fanotify_mark_add_to_mask(fsn_mark, mask); + fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) fsnotify_recalc_group_mask(group); if (added & ~inode->i_fsnotify_mask) fsnotify_recalc_inode_mask(inode); } - return fsn_mark; + return 0; } static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, __u32 mask) { - struct fsnotify_mark *fsn_mark; + int ret; pr_debug("%s: group=%p inode=%p mnt=%p mask=%x\n", __func__, group, inode, mnt, mask); @@ -434,19 +436,13 @@ static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, BUG_ON(!inode && !mnt); if (inode) - fsn_mark = fanotify_add_inode_mark(group, inode, mask); + ret = fanotify_add_inode_mark(group, inode, mask); else if (mnt) - fsn_mark = fanotify_add_vfsmount_mark(group, mnt, mask); + ret = fanotify_add_vfsmount_mark(group, mnt, mask); else BUG(); - if (IS_ERR(fsn_mark)) - goto out; - - /* match the init or the find.... */ - fsnotify_put_mark(fsn_mark); -out: - return PTR_ERR(fsn_mark); + return ret; } static bool fanotify_mark_validate_input(int flags, -- cgit v1.1 From 90dd201d1ab064512078a77762a793e0bf5f3040 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:28 -0500 Subject: fanotify: remove fanotify_add_mark fanotify_add_mark now does nothing useful anymore, drop it. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 7d7c138..db80a0d 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -424,27 +424,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, return 0; } -static int fanotify_add_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask) -{ - int ret; - - pr_debug("%s: group=%p inode=%p mnt=%p mask=%x\n", - __func__, group, inode, mnt, mask); - - BUG_ON(inode && mnt); - BUG_ON(!inode && !mnt); - - if (inode) - ret = fanotify_add_inode_mark(group, inode, mask); - else if (mnt) - ret = fanotify_add_vfsmount_mark(group, mnt, mask); - else - BUG(); - - return ret; -} - static bool fanotify_mark_validate_input(int flags, __u32 mask) { @@ -542,7 +521,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - ret = fanotify_add_mark(group, inode, NULL, mask); + ret = fanotify_add_inode_mark(group, inode, mask); break; case FAN_MARK_REMOVE: ret = fanotify_remove_mark(group, inode, NULL, mask); -- cgit v1.1 From 0ff21db9fcc39042b814dad8a4b7508710a75235 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: hooks the fanotify_mark syscall to the vfsmount code Create a new fanotify_mark flag which indicates we should attach the mark to the vfsmount holding the object referenced by dfd and pathname rather than the inode itself. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index db80a0d..8126726 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -485,7 +485,8 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, __u64 mask, int dfd, const char __user * pathname) { - struct inode *inode; + struct inode *inode = NULL; + struct vfsmount *mnt = NULL; struct fsnotify_group *group; struct file *filp; struct path path; @@ -515,16 +516,22 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, goto fput_and_out; /* inode held in place by reference to path; group by fget on fd */ - inode = path.dentry->d_inode; + if (!(flags & FAN_MARK_ON_VFSMOUNT)) + inode = path.dentry->d_inode; + else + mnt = path.mnt; group = filp->private_data; /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - ret = fanotify_add_inode_mark(group, inode, mask); + if (flags & FAN_MARK_ON_VFSMOUNT) + ret = fanotify_add_vfsmount_mark(group, mnt, mask); + else + ret = fanotify_add_inode_mark(group, inode, mask); break; case FAN_MARK_REMOVE: - ret = fanotify_remove_mark(group, inode, NULL, mask); + ret = fanotify_remove_mark(group, inode, mnt, mask); break; default: ret = -EINVAL; -- cgit v1.1 From eac8e9e80ccbd30801b7b76a2ee4c6c5a681e53c Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: rename FAN_MARK_ON_VFSMOUNT to FAN_MARK_MOUNT the term 'vfsmount' isn't sensicle to userspace. instead call is 'mount. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 8126726..091371e 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -516,7 +516,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, goto fput_and_out; /* inode held in place by reference to path; group by fget on fd */ - if (!(flags & FAN_MARK_ON_VFSMOUNT)) + if (!(flags & FAN_MARK_MOUNT)) inode = path.dentry->d_inode; else mnt = path.mnt; @@ -525,7 +525,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, /* create/update an inode mark */ switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: - if (flags & FAN_MARK_ON_VFSMOUNT) + if (flags & FAN_MARK_MOUNT) ret = fanotify_add_vfsmount_mark(group, mnt, mask); else ret = fanotify_add_inode_mark(group, inode, mask); -- cgit v1.1 From f3640192c0a177506ec08ab07ed3178b912574da Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: split fanotify_remove_mark split fanotify_remove_mark into fanotify_remove_inode_mark and fanotify_remove_vfsmount_mark. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 45 +++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 091371e..00628d3 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -310,22 +310,33 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u3 return mask & oldmask; } -static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask) +static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, + struct vfsmount *mnt, __u32 mask) { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; - BUG_ON(inode && mnt); - BUG_ON(!inode && !mnt); + fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!fsn_mark) + return -ENOENT; - if (inode) - fsn_mark = fsnotify_find_inode_mark(group, inode); - else if (mnt) - fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); - else - BUG(); + removed = fanotify_mark_remove_from_mask(fsn_mark, mask); + fsnotify_put_mark(fsn_mark); + if (removed & group->mask) + fsnotify_recalc_group_mask(group); + if (removed & mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + + return 0; +} +static int fanotify_remove_inode_mark(struct fsnotify_group *group, + struct inode *inode, __u32 mask) +{ + struct fsnotify_mark *fsn_mark = NULL; + __u32 removed; + + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return -ENOENT; @@ -335,13 +346,8 @@ static int fanotify_remove_mark(struct fsnotify_group *group, struct inode *inod if (removed & group->mask) fsnotify_recalc_group_mask(group); - if (inode) { - if (removed & inode->i_fsnotify_mask) - fsnotify_recalc_inode_mask(inode); - } else if (mnt) { - if (removed & mnt->mnt_fsnotify_mask) - fsnotify_recalc_vfsmount_mask(mnt); - } + if (removed & inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); return 0; } @@ -531,7 +537,10 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, ret = fanotify_add_inode_mark(group, inode, mask); break; case FAN_MARK_REMOVE: - ret = fanotify_remove_mark(group, inode, mnt, mask); + if (flags & FAN_MARK_MOUNT) + ret = fanotify_remove_vfsmount_mark(group, mnt, mask); + else + ret = fanotify_remove_inode_mark(group, inode, mask); break; default: ret = -EINVAL; -- cgit v1.1 From 88380fe66e0ac22529f5426ab27d67da00ed2628 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: remove fanotify.h declarations fanotify_mark_validate functions are all needlessly declared in headers as static inlines. Instead just do the checks where they are needed for code readability. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.h | 25 ------------------------- fs/notify/fanotify/fanotify_user.c | 25 ++++++++++--------------- 2 files changed, 10 insertions(+), 40 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h index 5608783..4d5723a 100644 --- a/fs/notify/fanotify/fanotify.h +++ b/fs/notify/fanotify/fanotify.h @@ -6,31 +6,6 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; -static inline bool fanotify_mark_flags_valid(unsigned int flags) -{ - /* must be either and add or a remove */ - if (!(flags & (FAN_MARK_ADD | FAN_MARK_REMOVE))) - return false; - - /* cannot be both add and remove */ - if ((flags & FAN_MARK_ADD) && - (flags & FAN_MARK_REMOVE)) - return false; - - /* cannot have more flags than we know about */ - if (flags & ~FAN_ALL_MARK_FLAGS) - return false; - - return true; -} - -static inline bool fanotify_mask_valid(__u32 mask) -{ - if (mask & ~((__u32)FAN_ALL_INCOMING_EVENTS)) - return false; - return true; -} - static inline __u32 fanotify_outgoing_mask(__u32 mask) { return mask & FAN_ALL_OUTGOING_EVENTS; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 00628d3..618867e 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -430,20 +430,6 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, return 0; } -static bool fanotify_mark_validate_input(int flags, - __u32 mask) -{ - pr_debug("%s: flags=%x mask=%x\n", __func__, flags, mask); - - /* are flags valid of this operation? */ - if (!fanotify_mark_flags_valid(flags)) - return false; - /* is the mask valid? */ - if (!fanotify_mask_valid(mask)) - return false; - return true; -} - /* fanotify syscalls */ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, unsigned int, priority) @@ -505,7 +491,16 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, if (mask & ((__u64)0xffffffff << 32)) return -EINVAL; - if (!fanotify_mark_validate_input(flags, mask)) + if (flags & ~FAN_ALL_MARK_FLAGS) + return -EINVAL; + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + case FAN_MARK_ADD: + case FAN_MARK_REMOVE: + break; + default: + return -EINVAL; + } + if (mask & ~(FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD)) return -EINVAL; filp = fget_light(fanotify_fd, &fput_needed); -- cgit v1.1 From 33d3dfff451a2ab6fe2f6aaabed9b24e91aad109 Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Thu, 17 Dec 2009 21:24:29 -0500 Subject: fanotify: remove outgoing function checks in fanotify.h A number of validity checks on outgoing data are done in static inlines but are only used in one place. Instead just do them where they are used for readability. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 3 +-- fs/notify/fanotify/fanotify.h | 12 ------------ fs/notify/fanotify/fanotify_user.c | 5 +++-- 3 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 fs/notify/fanotify/fanotify.h (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 202be8a..f690002 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,8 +6,6 @@ #include #include -#include "fanotify.h" - static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { pr_debug("%s: old=%p new=%p\n", __func__, old, new); diff --git a/fs/notify/fanotify/fanotify.h b/fs/notify/fanotify/fanotify.h deleted file mode 100644 index 4d5723a..0000000 --- a/fs/notify/fanotify/fanotify.h +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include -#include -#include -#include - -extern const struct fsnotify_ops fanotify_fsnotify_ops; - -static inline __u32 fanotify_outgoing_mask(__u32 mask) -{ - return mask & FAN_ALL_OUTGOING_EVENTS; -} diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 618867e..8415584 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -14,7 +15,7 @@ #include -#include "fanotify.h" +extern const struct fsnotify_ops fanotify_fsnotify_ops; static struct kmem_cache *fanotify_mark_cache __read_mostly; @@ -102,7 +103,7 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, metadata->event_len = FAN_EVENT_METADATA_LEN; metadata->vers = FANOTIFY_METADATA_VERSION; - metadata->mask = fanotify_outgoing_mask(event->mask); + metadata->mask = event->mask & FAN_ALL_OUTGOING_EVENTS; metadata->pid = pid_vnr(event->tgid); metadata->fd = create_fd(group, event); -- cgit v1.1 From 90b1e7a57880fb66437ab7db39e1e65ca0372822 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: allow marks to not pin inodes in core 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 --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/fanotify/fanotify_user.c | 4 ++-- fs/notify/fsnotify.h | 2 ++ fs/notify/inode_mark.c | 35 +++++++++++++++++++++++++++-------- fs/notify/inotify/inotify_user.c | 12 +++++------- fs/notify/mark.c | 17 ++++++++++++++++- 6 files changed, 53 insertions(+), 19 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 69f42df..6624c2e 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -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; diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 8415584..3320f0c 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -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; diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 204353c..1be54f6 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -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, diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index c925579..4292f9e 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -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; } diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a12315a..19d2740 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -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); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index d296ec9..0ebc3fd 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -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) -- cgit v1.1 From 33af5e32e0bb73c704b5e156f4411cdb53e6cc59 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: ignored_mask - excluding notification The ignored_mask is a new mask which is part of fsnotify marks. A group's should_send_event() function can use the ignored mask to determine that certain events are not of interest. In particular if a group registers a mask including FS_OPEN on a vfsmount they could add FS_OPEN to the ignored_mask for individual inodes and not send open events for those inodes. Signed-off-by: Eric Paris --- fs/notify/mark.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 0ebc3fd..cb1d822 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -190,6 +190,12 @@ void fsnotify_set_mark_mask_locked(struct fsnotify_mark *mark, __u32 mask) fsnotify_set_inode_mark_mask_locked(mark, mask); } +void fsnotify_set_mark_ignored_mask_locked(struct fsnotify_mark *mark, __u32 mask) +{ + assert_spin_locked(&mark->lock); + + mark->ignored_mask = mask; +} /* * Attach an initialized mark to a given group and fs object. -- cgit v1.1 From 32a4df13b88afef2a7d869bb7586a7beba90961f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fanotify: ignored_mask to ignore events When fanotify receives an event it will check event->mask & ~ignored_mask. If no bits are left the event will not be sent. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index f690002..060b177 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -100,31 +100,39 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e } static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt, - __u32 mask) + struct inode *inode, __u32 mask) { - struct fsnotify_mark *fsn_mark; - bool send; + struct fsnotify_mark *mnt_mark; + struct fsnotify_mark *inode_mark; pr_debug("%s: group=%p vfsmount=%p mask=%x\n", __func__, group, mnt, mask); - fsn_mark = fsnotify_find_vfsmount_mark(group, mnt); - if (!fsn_mark) + mnt_mark = fsnotify_find_vfsmount_mark(group, mnt); + if (!mnt_mark) return false; - send = (mask & fsn_mark->mask); + mask &= mnt_mark->mask; + mask &= ~mnt_mark->ignored_mask; + + if (mask) { + inode_mark = fsnotify_find_inode_mark(group, inode); + if (inode_mark) { + mask &= ~inode_mark->ignored_mask; + fsnotify_put_mark(inode_mark); + } + } /* find took a reference */ - fsnotify_put_mark(fsn_mark); + fsnotify_put_mark(mnt_mark); - return send; + return mask; } static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode, __u32 mask) { struct fsnotify_mark *fsn_mark; - bool send; pr_debug("%s: group=%p inode=%p mask=%x\n", __func__, group, inode, mask); @@ -137,20 +145,21 @@ static bool should_send_inode_event(struct fsnotify_group *group, struct inode * * events on the child, don't send it! */ if ((mask & FS_EVENT_ON_CHILD) && !(fsn_mark->mask & FS_EVENT_ON_CHILD)) { - send = false; + mask = 0; } else { /* * We care about children, but do we care about this particular * type of event? */ - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (fsn_mark->mask & mask); + mask &= ~FS_EVENT_ON_CHILD; + mask &= fsn_mark->mask; + mask &= ~fsn_mark->ignored_mask; } /* find took a reference */ fsnotify_put_mark(fsn_mark); - return send; + return mask; } static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, @@ -170,7 +179,7 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; if (mnt) - return should_send_vfsmount_event(group, mnt, mask); + return should_send_vfsmount_event(group, mnt, to_tell, mask); else return should_send_inode_event(group, to_tell, mask); } -- cgit v1.1 From b9e4e3bd0495fea9e8f8e712889c9cd8ffa43c94 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fanotify: allow users to set an ignored_mask Change the sys_fanotify_mark() system call so users can set ignored_masks on inodes. Remember, if a user new sets a real mask, and only sets ignored masks, the ignore will never be pinned in memory. Thus ignored_masks can be lost under memory pressure and the user may again get events they previously thought were ignored. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 54 +++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 18 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 3320f0c..ad02d47 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -296,13 +296,20 @@ out: return ret; } -static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, + __u32 mask, + unsigned int flags) { __u32 oldmask; spin_lock(&fsn_mark->lock); - oldmask = fsn_mark->mask; - fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask)); + if (!(flags & FAN_MARK_IGNORED_MASK)) { + oldmask = fsn_mark->mask; + fsnotify_set_mark_mask_locked(fsn_mark, (oldmask & ~mask)); + } else { + oldmask = fsn_mark->ignored_mask; + fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask & ~mask)); + } spin_unlock(&fsn_mark->lock); if (!(oldmask & ~mask)) @@ -312,7 +319,8 @@ static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark, __u3 } static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt, __u32 mask) + struct vfsmount *mnt, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; @@ -321,7 +329,7 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, if (!fsn_mark) return -ENOENT; - removed = fanotify_mark_remove_from_mask(fsn_mark, mask); + removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); if (removed & group->mask) fsnotify_recalc_group_mask(group); @@ -332,7 +340,8 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, } static int fanotify_remove_inode_mark(struct fsnotify_group *group, - struct inode *inode, __u32 mask) + struct inode *inode, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark = NULL; __u32 removed; @@ -341,7 +350,7 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, if (!fsn_mark) return -ENOENT; - removed = fanotify_mark_remove_from_mask(fsn_mark, mask); + removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); @@ -353,20 +362,28 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, return 0; } -static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, __u32 mask) +static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, + __u32 mask, + unsigned int flags) { __u32 oldmask; spin_lock(&fsn_mark->lock); - oldmask = fsn_mark->mask; - fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask)); + if (!(flags & FAN_MARK_IGNORED_MASK)) { + oldmask = fsn_mark->mask; + fsnotify_set_mark_mask_locked(fsn_mark, (oldmask | mask)); + } else { + oldmask = fsn_mark->ignored_mask; + fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask)); + } spin_unlock(&fsn_mark->lock); return mask & ~oldmask; } static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, - struct vfsmount *mnt, __u32 mask) + struct vfsmount *mnt, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -386,7 +403,7 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, return ret; } } - added = fanotify_mark_add_to_mask(fsn_mark, mask); + added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) @@ -398,7 +415,8 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, } static int fanotify_add_inode_mark(struct fsnotify_group *group, - struct inode *inode, __u32 mask) + struct inode *inode, __u32 mask, + unsigned int flags) { struct fsnotify_mark *fsn_mark; __u32 added; @@ -420,7 +438,7 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, return ret; } } - added = fanotify_mark_add_to_mask(fsn_mark, mask); + added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); if (added) { if (added & ~group->mask) @@ -528,15 +546,15 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { case FAN_MARK_ADD: if (flags & FAN_MARK_MOUNT) - ret = fanotify_add_vfsmount_mark(group, mnt, mask); + ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags); else - ret = fanotify_add_inode_mark(group, inode, mask); + ret = fanotify_add_inode_mark(group, inode, mask, flags); break; case FAN_MARK_REMOVE: if (flags & FAN_MARK_MOUNT) - ret = fanotify_remove_vfsmount_mark(group, mnt, mask); + ret = fanotify_remove_vfsmount_mark(group, mnt, mask, flags); else - ret = fanotify_remove_inode_mark(group, inode, mask); + ret = fanotify_remove_inode_mark(group, inode, mask, flags); break; default: ret = -EINVAL; -- cgit v1.1 From e898386146deb49a0b45ff1887d9da149c003209 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: clear ignored mask on modify On inode modification we clear the ignored mask for all of the marks on the inode. This allows userspace to ignore accesses to inodes until there is something different. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 7f14ddc..3ad940d 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -140,6 +140,33 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); +void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) +{ + struct fsnotify_mark *mark; + struct hlist_node *node; + + if (!hlist_empty(&inode->i_fsnotify_marks)) { + spin_lock(&inode->i_lock); + hlist_for_each_entry(mark, node, &inode->i_fsnotify_marks, i.i_list) { + mark->ignored_mask = 0; + } + spin_unlock(&inode->i_lock); + } + + if (data_is == FSNOTIFY_EVENT_PATH) { + struct vfsmount *mnt; + + mnt = ((struct path *)data)->mnt; + if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { + spin_lock(&mnt->mnt_root->d_lock); + hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + mark->ignored_mask = 0; + } + spin_unlock(&mnt->mnt_root->d_lock); + } + } +} + static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, struct vfsmount *mnt, __u32 mask, void *data, int data_is, u32 cookie, const char *file_name, @@ -170,6 +197,7 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) return (test_mask & mnt->mnt_fsnotify_mask); } + /* * This is the main call to fsnotify. The VFS calls into hook specific functions * in linux/fsnotify.h. Those functions then in turn call here. Here will call @@ -190,6 +218,9 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const list_empty(&fsnotify_vfsmount_groups)) return; + if (mask & FS_MODIFY) + __fsnotify_flush_ignored_mask(to_tell, data, data_is); + /* if none of the directed listeners or vfsmount listeners care */ if (!(test_mask & fsnotify_inode_mask) && !(test_mask & fsnotify_vfsmount_mask)) -- cgit v1.1 From c908370fc1ac27fd7e1fc0f34c693047b26564ce Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fsnotify: allow ignored_mask to survive modification Some inodes a group may want to never hear about a set of events even if the inode is modified. We add a new mark flag which indicates that these marks should not have their ignored_mask cleared on modification. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 3ad940d..54d58d5 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -148,7 +148,8 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) if (!hlist_empty(&inode->i_fsnotify_marks)) { spin_lock(&inode->i_lock); hlist_for_each_entry(mark, node, &inode->i_fsnotify_marks, i.i_list) { - mark->ignored_mask = 0; + if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; } spin_unlock(&inode->i_lock); } @@ -160,7 +161,8 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { spin_lock(&mnt->mnt_root->d_lock); hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { - mark->ignored_mask = 0; + if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; } spin_unlock(&mnt->mnt_root->d_lock); } -- cgit v1.1 From c9778a98e7440fb73e0d27b8155a688663a0d493 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:33 -0500 Subject: fanotify: allow ignored_masks to survive modify Some users may want to truely ignore an inode even if it has been modified. Say you are wanting a mount which contains a log file and you really don't want any notification about that file. This patch allows the listener to do that. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index ad02d47..3e275f1 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -375,6 +375,8 @@ static __u32 fanotify_mark_add_to_mask(struct fsnotify_mark *fsn_mark, } else { oldmask = fsn_mark->ignored_mask; fsnotify_set_mark_ignored_mask_locked(fsn_mark, (oldmask | mask)); + if (flags & FAN_MARK_IGNORED_SURV_MODIFY) + fsn_mark->flags |= FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY; } spin_unlock(&fsn_mark->lock); -- cgit v1.1 From 4d92604cc90aa18bbbe0f6e23b7a9fdb612836d3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: clear all fanotify marks fanotify listeners may want to clear all marks. They may want to do this to destroy all of their inode marks which have nothing but ignores. Realistically this is useful for av vendors who update policy and want to clear all of their cached allows. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 12 ++++++++++-- fs/notify/inode_mark.c | 8 ++++++++ fs/notify/mark.c | 21 ++++++++++++++++----- fs/notify/vfsmount_mark.c | 5 +++++ 4 files changed, 39 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 3e275f1..9fe760b 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -514,9 +514,10 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, if (flags & ~FAN_ALL_MARK_FLAGS) return -EINVAL; - switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { case FAN_MARK_ADD: case FAN_MARK_REMOVE: + case FAN_MARK_FLUSH: break; default: return -EINVAL; @@ -545,7 +546,7 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, group = filp->private_data; /* create/update an inode mark */ - switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE)) { + switch (flags & (FAN_MARK_ADD | FAN_MARK_REMOVE | FAN_MARK_FLUSH)) { case FAN_MARK_ADD: if (flags & FAN_MARK_MOUNT) ret = fanotify_add_vfsmount_mark(group, mnt, mask, flags); @@ -558,6 +559,13 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, else ret = fanotify_remove_inode_mark(group, inode, mask, flags); break; + case FAN_MARK_FLUSH: + if (flags & FAN_MARK_MOUNT) + fsnotify_clear_vfsmount_marks_by_group(group); + else + fsnotify_clear_inode_marks_by_group(group); + fsnotify_recalc_group_mask(group); + break; default: ret = -EINVAL; } diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 4292f9e..0c0a48b 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -104,6 +104,14 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) } /* + * Given a group clear all of the inode marks associated with that group. + */ +void fsnotify_clear_inode_marks_by_group(struct fsnotify_group *group) +{ + fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_INODE); +} + +/* * given a group and inode, find the mark associated with that combination. * if found take a reference to that mark and return it, else return NULL */ diff --git a/fs/notify/mark.c b/fs/notify/mark.c index cb1d822..1e824e6 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -270,18 +270,21 @@ err: } /* - * Given a group, destroy all of the marks associated with that group. + * clear any marks in a group in which mark->flags & flags is true */ -void fsnotify_clear_marks_by_group(struct fsnotify_group *group) +void fsnotify_clear_marks_by_group_flags(struct fsnotify_group *group, + unsigned int flags) { struct fsnotify_mark *lmark, *mark; LIST_HEAD(free_list); spin_lock(&group->mark_lock); list_for_each_entry_safe(mark, lmark, &group->marks_list, g_list) { - list_add(&mark->free_g_list, &free_list); - list_del_init(&mark->g_list); - fsnotify_get_mark(mark); + if (mark->flags & flags) { + list_add(&mark->free_g_list, &free_list); + list_del_init(&mark->g_list); + fsnotify_get_mark(mark); + } } spin_unlock(&group->mark_lock); @@ -291,6 +294,14 @@ void fsnotify_clear_marks_by_group(struct fsnotify_group *group) } } +/* + * Given a group, destroy all of the marks associated with that group. + */ +void fsnotify_clear_marks_by_group(struct fsnotify_group *group) +{ + fsnotify_clear_marks_by_group_flags(group, (unsigned int)-1); +} + void fsnotify_duplicate_mark(struct fsnotify_mark *new, struct fsnotify_mark *old) { assert_spin_locked(&old->lock); diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index 1b61d0a..8f1aa02 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -51,6 +51,11 @@ void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) } } +void fsnotify_clear_vfsmount_marks_by_group(struct fsnotify_group *group) +{ + fsnotify_clear_marks_by_group_flags(group, FSNOTIFY_MARK_FLAG_VFSMOUNT); +} + /* * Recalculate the mask of events relevant to a given vfsmount locked. */ -- cgit v1.1 From cb2d429faf2cae62d3c51e28099a181d5fe8c244 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fsnotify: add group priorities This introduces an ordering to fsnotify groups. With purely asynchronous notification based "things" implementing fsnotify (inotify, dnotify) ordering isn't particularly important. But if people want to use fsnotify for the basis of sycronous notification or blocking notification ordering becomes important. eg. A Hierarchical Storage Management listener would need to get its event before an AV scanner could get its event (since the HSM would need to bring the data in for the AV scanner to scan.) Typically asynchronous notification would want to run after the AV scanner made any relevant access decisions so as to not send notification about an event that was denied. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 4 ++-- fs/notify/group.c | 40 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 9fe760b..84d3e20 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -463,8 +463,6 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (event_f_flags) return -EINVAL; - if (priority) - return -EINVAL; if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -483,6 +481,8 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (IS_ERR(group)) return PTR_ERR(group); + group->priority = priority; + fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); if (fd < 0) goto out_put_group; diff --git a/fs/notify/group.c b/fs/notify/group.c index 9e9eb40..ada913f 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -89,10 +89,27 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) void fsnotify_add_vfsmount_group(struct fsnotify_group *group) { + struct fsnotify_group *group_iter; + unsigned int priority = group->priority; + mutex_lock(&fsnotify_grp_mutex); - if (!group->on_vfsmount_group_list) + if (!group->on_vfsmount_group_list) { + list_for_each_entry(group_iter, &fsnotify_vfsmount_groups, + vfsmount_group_list) { + /* insert in front of this one? */ + if (priority < group_iter->priority) { + /* list_add_tail() insert in front of group_iter */ + list_add_tail_rcu(&group->inode_group_list, + &group_iter->inode_group_list); + goto out; + } + } + + /* apparently we need to be the last entry */ list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups); + } +out: group->on_vfsmount_group_list = 1; mutex_unlock(&fsnotify_grp_mutex); @@ -100,10 +117,27 @@ void fsnotify_add_vfsmount_group(struct fsnotify_group *group) void fsnotify_add_inode_group(struct fsnotify_group *group) { + struct fsnotify_group *group_iter; + unsigned int priority = group->priority; + mutex_lock(&fsnotify_grp_mutex); - if (!group->on_inode_group_list) + /* add to global group list, priority 0 first, UINT_MAX last */ + if (!group->on_inode_group_list) { + list_for_each_entry(group_iter, &fsnotify_inode_groups, + inode_group_list) { + if (priority < group_iter->priority) { + /* list_add_tail() insert in front of group_iter */ + list_add_tail_rcu(&group->inode_group_list, + &group_iter->inode_group_list); + goto out; + } + } + + /* apparently we need to be the last entry */ list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); + } +out: group->on_inode_group_list = 1; mutex_unlock(&fsnotify_grp_mutex); @@ -226,6 +260,8 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->marks_list); + group->priority = UINT_MAX; + group->ops = ops; return group; -- cgit v1.1 From 6e5f77b32e9097a8a68a8d453799676cacf70cad Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fsnotify: intoduce a notification merge argument Each group can define their own notification (and secondary_q) merge function. Inotify does tail drop, fanotify does matching and drop which can actually allocate a completely new event. But for fanotify to properly deal with permissions events it needs to know the new event which was ultimately added to the notification queue. This patch just implements a void ** argument which is passed to the merge function. fanotify can use this field to pass the new event back to higher layers. Signed-off-by: Eric Paris for fanotify to properly deal with permissions events --- fs/notify/fanotify/fanotify.c | 6 ++++-- fs/notify/inotify/inotify_fsnotify.c | 6 ++++-- fs/notify/inotify/inotify_user.c | 2 +- fs/notify/notification.c | 7 +++++-- 4 files changed, 14 insertions(+), 7 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 060b177..95a330d 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -27,7 +27,9 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) return false; } -static int fanotify_merge(struct list_head *list, struct fsnotify_event *event) +static int fanotify_merge(struct list_head *list, + struct fsnotify_event *event, + void **arg) { struct fsnotify_event_holder *test_holder; struct fsnotify_event *test_event; @@ -92,7 +94,7 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge); + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, NULL); /* -EEXIST means this event was merged with another, not that it was an error */ if (ret == -EEXIST) ret = 0; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 1d237e1..daa666a 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -67,7 +67,9 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new return false; } -static int inotify_merge(struct list_head *list, struct fsnotify_event *event) +static int inotify_merge(struct list_head *list, + struct fsnotify_event *event, + void **arg) { struct fsnotify_event_holder *last_holder; struct fsnotify_event *last_event; @@ -114,7 +116,7 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev fsn_event_priv->group = group; event_priv->wd = wd; - ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); + ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge, NULL); if (ret) { inotify_free_event_priv(fsn_event_priv); /* EEXIST says we tail matched, EOVERFLOW isn't something diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 19d2740..1ce71f5 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -525,7 +525,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, fsn_event_priv->group = group; event_priv->wd = i_mark->wd; - ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); + ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL, NULL); if (ret) inotify_free_event_priv(fsn_event_priv); diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 7fc8d00..2d50a40 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -137,7 +137,10 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot */ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, struct fsnotify_event_private_data *priv, - int (*merge)(struct list_head *, struct fsnotify_event *)) + int (*merge)(struct list_head *, + struct fsnotify_event *, + void **arg), + void **arg) { struct fsnotify_event_holder *holder = NULL; struct list_head *list = &group->notification_list; @@ -170,7 +173,7 @@ alloc_holder: if (!list_empty(list) && merge) { int ret; - ret = merge(list, event); + ret = merge(list, event, arg); if (ret) { mutex_unlock(&group->notification_mutex); if (holder != &event->holder) -- cgit v1.1 From 43ed7e16a8b47059d7f6ff67ba76f383a2421de3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: use merge argument to determine actual event added to queue fanotify needs to know the actual event added to queues so it can be correctly checked for return values from userspace. To do this we need to pass that information from the merger code back to the main even handling routine. Currently that information is unused, but it will be. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 95a330d..4feed86 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -27,6 +27,7 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) return false; } +/* Note, if we return an event in *arg that a reference is being held... */ static int fanotify_merge(struct list_head *list, struct fsnotify_event *event, void **arg) @@ -34,17 +35,22 @@ static int fanotify_merge(struct list_head *list, struct fsnotify_event_holder *test_holder; struct fsnotify_event *test_event; struct fsnotify_event *new_event; + struct fsnotify_event **return_event = (struct fsnotify_event **)arg; int ret = 0; pr_debug("%s: list=%p event=%p\n", __func__, list, event); + *return_event = NULL; + /* and the list better be locked by something too! */ list_for_each_entry_reverse(test_holder, list, event_list) { test_event = test_holder->event; if (should_merge(test_event, event)) { - ret = -EEXIST; + fsnotify_get_event(test_event); + *return_event = test_event; + ret = -EEXIST; /* if they are exactly the same we are done */ if (test_event->mask == event->mask) goto out; @@ -66,11 +72,14 @@ static int fanotify_merge(struct list_head *list, goto out; } + /* we didn't return the test_event, so drop that ref */ + fsnotify_put_event(test_event); + /* the reference we return on new_event is from clone */ + *return_event = new_event; + /* build new event and replace it on the list */ new_event->mask = (test_event->mask | event->mask); fsnotify_replace_event(test_holder, new_event); - /* match ref from fsnotify_clone_event() */ - fsnotify_put_event(new_event); break; } @@ -82,7 +91,7 @@ out: static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { int ret; - + struct fsnotify_event *used_event; BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); @@ -94,10 +103,12 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, NULL); + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, (void **)&used_event); /* -EEXIST means this event was merged with another, not that it was an error */ if (ret == -EEXIST) ret = 0; + if (used_event) + fsnotify_put_event(used_event); return ret; } -- cgit v1.1 From 59b0df211bd9699d7e0d01fcf9345a149f75b033 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Mon, 8 Feb 2010 12:53:52 -0500 Subject: fsnotify: use unsigned char * for dentry->d_name.name fsnotify was using char * when it passed around the d_name.name string internally but it is actually an unsigned char *. This patch switches fsnotify to use unsigned and should silence some pointer signess warnings which have popped out of xfs. I do not add -Wpointer-sign to the fsnotify code as there are still issues with kstrdup and strlen which would pop out needless warnings. Signed-off-by: Eric Paris --- fs/namei.c | 2 +- fs/notify/fsnotify.c | 5 +++-- fs/notify/notification.c | 4 ++-- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'fs') diff --git a/fs/namei.c b/fs/namei.c index 868d0cb..3479b17 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -2635,7 +2635,7 @@ int vfs_rename(struct inode *old_dir, struct dentry *old_dentry, { int error; int is_dir = S_ISDIR(old_dentry->d_inode->i_mode); - const char *old_name; + const unsigned char *old_name; if (old_dentry->d_inode == new_dentry->d_inode) return 0; diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 54d58d5..c5adf83 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -171,7 +171,7 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, struct vfsmount *mnt, __u32 mask, void *data, - int data_is, u32 cookie, const char *file_name, + int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { if (!group->ops->should_send_event(group, to_tell, mnt, mask, @@ -206,7 +206,8 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) * out to all of the registered fsnotify_group. Those groups can then use the * notification event in whatever means they feel necessary. */ -void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const char *file_name, u32 cookie) +void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, + const unsigned char *file_name, u32 cookie) { struct fsnotify_group *group; struct fsnotify_event *event = NULL; diff --git a/fs/notify/notification.c b/fs/notify/notification.c index 2d50a40..b35faaf 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -370,8 +370,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) * @name the filename, if available */ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, void *data, - int data_type, const char *name, u32 cookie, - gfp_t gfp) + int data_type, const unsigned char *name, + u32 cookie, gfp_t gfp) { struct fsnotify_event *event; -- cgit v1.1 From c4ec54b40d33f8016fea970a383cc584dd0e6019 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fsnotify: new fsnotify hooks and events types for access decisions introduce a new fsnotify hook, fsnotify_perm(), which is called from the security code. This hook is used to allow fsnotify groups to make access control decisions about events on the system. We also must change the generic fsnotify function to return an error code if we intend these hooks to be in any way useful. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index c5adf83..6682686 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -169,27 +169,22 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) } } -static void send_to_group(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, __u32 mask, void *data, - int data_is, u32 cookie, const unsigned char *file_name, - struct fsnotify_event **event) +static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, + struct vfsmount *mnt, __u32 mask, void *data, + int data_is, u32 cookie, const unsigned char *file_name, + struct fsnotify_event **event) { if (!group->ops->should_send_event(group, to_tell, mnt, mask, data, data_is)) - return; + return 0; if (!*event) { *event = fsnotify_create_event(to_tell, mask, data, data_is, file_name, cookie, GFP_KERNEL); - /* - * shit, we OOM'd and now we can't tell, maybe - * someday someone else will want to do something - * here - */ if (!*event) - return; + return -ENOMEM; } - group->ops->handle_event(group, *event); + return group->ops->handle_event(group, *event); } static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) @@ -206,20 +201,20 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) * out to all of the registered fsnotify_group. Those groups can then use the * notification event in whatever means they feel necessary. */ -void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, - const unsigned char *file_name, u32 cookie) +int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, + const unsigned char *file_name, u32 cookie) { struct fsnotify_group *group; struct fsnotify_event *event = NULL; struct vfsmount *mnt = NULL; - int idx; + int idx, ret = 0; /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); /* if no fsnotify listeners, nothing to do */ if (list_empty(&fsnotify_inode_groups) && list_empty(&fsnotify_vfsmount_groups)) - return; + return 0; if (mask & FS_MODIFY) __fsnotify_flush_ignored_mask(to_tell, data, data_is); @@ -227,7 +222,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, /* if none of the directed listeners or vfsmount listeners care */ if (!(test_mask & fsnotify_inode_mask) && !(test_mask & fsnotify_vfsmount_mask)) - return; + return 0; if (data_is == FSNOTIFY_EVENT_PATH) mnt = ((struct path *)data)->mnt; @@ -236,7 +231,7 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, * listeners list cares, nothing to do */ if (!(test_mask & to_tell->i_fsnotify_mask) && !needed_by_vfsmount(test_mask, mnt)) - return; + return 0; /* * SRCU!! the groups list is very very much read only and the path is @@ -248,20 +243,24 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if (test_mask & to_tell->i_fsnotify_mask) { list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { if (test_mask & group->mask) { - send_to_group(group, to_tell, NULL, mask, data, data_is, - cookie, file_name, &event); + ret = send_to_group(group, to_tell, NULL, mask, data, data_is, + cookie, file_name, &event); + if (ret) + goto out; } } } if (needed_by_vfsmount(test_mask, mnt)) { list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { if (test_mask & group->mask) { - send_to_group(group, to_tell, mnt, mask, data, data_is, - cookie, file_name, &event); + ret = send_to_group(group, to_tell, mnt, mask, data, data_is, + cookie, file_name, &event); + if (ret) + goto out; } } } - +out: srcu_read_unlock(&fsnotify_grp_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned @@ -269,6 +268,8 @@ void fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, */ if (event) fsnotify_put_event(event); + + return 0; } EXPORT_SYMBOL_GPL(fsnotify); -- cgit v1.1 From 9e66e4233db9c7e31e9ee706be2c9ddd54cf99b3 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: permissions and blocking This is the backend work needed for fanotify to support the new FS_OPEN_PERM and FS_ACCESS_PERM fsnotify events. This is done using the new fsnotify secondary queue. No userspace interface is provided actually respond to or request these events. Signed-off-by: Eric Paris --- fs/notify/fanotify/Kconfig | 14 ++++++++++ fs/notify/fanotify/fanotify.c | 54 +++++++++++++++++++++++++++++++++++--- fs/notify/fanotify/fanotify_user.c | 5 ++++ 3 files changed, 69 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig index 668e5df..566de30 100644 --- a/fs/notify/fanotify/Kconfig +++ b/fs/notify/fanotify/Kconfig @@ -10,3 +10,17 @@ config FANOTIFY the event. If unsure, say Y. + +config FANOTIFY_ACCESS_PERMISSIONS + bool "fanotify permissions checking" + depends on FANOTIFY + depends on SECURITY + default n + ---help--- + Say Y here is you want fanotify listeners to be able to make permissions + decisions concerning filesystem events. This is used by some fanotify + listeners which need to scan files before allowing the system access to + use those files. This is used by some anti-malware vendors and by some + hierarchical storage managent systems. + + If unsure, say N. diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 4feed86..52d0a55 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -2,9 +2,12 @@ #include #include #include +#include #include /* UINT_MAX */ #include +#include #include +#include static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) { @@ -88,10 +91,37 @@ out: return ret; } +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +static int fanotify_get_response_from_access(struct fsnotify_group *group, + struct fsnotify_event *event) +{ + int ret; + + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + + wait_event(group->fanotify_data.access_waitq, event->response); + + /* userspace responded, convert to something usable */ + spin_lock(&event->lock); + switch (event->response) { + case FAN_ALLOW: + ret = 0; + break; + case FAN_DENY: + default: + ret = -EPERM; + } + event->response = 0; + spin_unlock(&event->lock); + + return ret; +} +#endif + static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { int ret; - struct fsnotify_event *used_event; + struct fsnotify_event *notify_event = NULL; BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); BUILD_BUG_ON(FAN_MODIFY != FS_MODIFY); @@ -100,15 +130,31 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e BUILD_BUG_ON(FAN_OPEN != FS_OPEN); BUILD_BUG_ON(FAN_EVENT_ON_CHILD != FS_EVENT_ON_CHILD); BUILD_BUG_ON(FAN_Q_OVERFLOW != FS_Q_OVERFLOW); + BUILD_BUG_ON(FAN_OPEN_PERM != FS_OPEN_PERM); + BUILD_BUG_ON(FAN_ACCESS_PERM != FS_ACCESS_PERM); pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, (void **)&used_event); + ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, + (void **)¬ify_event); /* -EEXIST means this event was merged with another, not that it was an error */ if (ret == -EEXIST) ret = 0; - if (used_event) - fsnotify_put_event(used_event); + if (ret) + goto out; + +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (event->mask & FAN_ALL_PERM_EVENTS) { + /* if we merged we need to wait on the new event */ + if (notify_event) + event = notify_event; + ret = fanotify_get_response_from_access(group, event); + } +#endif + +out: + if (notify_event) + fsnotify_put_event(notify_event); return ret; } diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 84d3e20..09d9bdb 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -482,6 +482,11 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, return PTR_ERR(group); group->priority = priority; +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + mutex_init(&group->fanotify_data.access_mutex); + init_waitqueue_head(&group->fanotify_data.access_waitq); + INIT_LIST_HEAD(&group->fanotify_data.access_list); +#endif fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags); if (fd < 0) -- cgit v1.1 From b2d879096ac799722e6017ee82c0586f0d101c9c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 17 Dec 2009 21:24:34 -0500 Subject: fanotify: userspace interface for permission responses fanotify groups need to respond to events which include permissions types. To do so groups will send a response using write() on the fanotify_fd they have open. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 3 + fs/notify/fanotify/fanotify_user.c | 182 +++++++++++++++++++++++++++++++++++-- 2 files changed, 179 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 52d0a55..bbcfccd 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -114,6 +114,9 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, event->response = 0; spin_unlock(&event->lock); + pr_debug("%s: group=%p event=%p about to return ret=%d\n", __func__, + group, event, ret); + return ret; } #endif diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 09d9bdb..87f0be8 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -18,6 +18,13 @@ extern const struct fsnotify_ops fanotify_fsnotify_ops; static struct kmem_cache *fanotify_mark_cache __read_mostly; +static struct kmem_cache *fanotify_response_event_cache __read_mostly; + +struct fanotify_response_event { + struct list_head list; + __s32 fd; + struct fsnotify_event *event; +}; /* * Get an fsnotify notification event if one exists and is small @@ -110,23 +117,152 @@ static ssize_t fill_event_metadata(struct fsnotify_group *group, return metadata->fd; } +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS +static struct fanotify_response_event *dequeue_re(struct fsnotify_group *group, + __s32 fd) +{ + struct fanotify_response_event *re, *return_re = NULL; + + mutex_lock(&group->fanotify_data.access_mutex); + list_for_each_entry(re, &group->fanotify_data.access_list, list) { + if (re->fd != fd) + continue; + + list_del_init(&re->list); + return_re = re; + break; + } + mutex_unlock(&group->fanotify_data.access_mutex); + + pr_debug("%s: found return_re=%p\n", __func__, return_re); + + return return_re; +} + +static int process_access_response(struct fsnotify_group *group, + struct fanotify_response *response_struct) +{ + struct fanotify_response_event *re; + __s32 fd = response_struct->fd; + __u32 response = response_struct->response; + + pr_debug("%s: group=%p fd=%d response=%d\n", __func__, group, + fd, response); + /* + * make sure the response is valid, if invalid we do nothing and either + * userspace can send a valid responce or we will clean it up after the + * timeout + */ + switch (response) { + case FAN_ALLOW: + case FAN_DENY: + break; + default: + return -EINVAL; + } + + if (fd < 0) + return -EINVAL; + + re = dequeue_re(group, fd); + if (!re) + return -ENOENT; + + re->event->response = response; + + wake_up(&group->fanotify_data.access_waitq); + + kmem_cache_free(fanotify_response_event_cache, re); + + return 0; +} + +static int prepare_for_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + struct fanotify_response_event *re; + + if (!(event->mask & FAN_ALL_PERM_EVENTS)) + return 0; + + re = kmem_cache_alloc(fanotify_response_event_cache, GFP_KERNEL); + if (!re) + return -ENOMEM; + + re->event = event; + re->fd = fd; + + mutex_lock(&group->fanotify_data.access_mutex); + list_add_tail(&re->list, &group->fanotify_data.access_list); + mutex_unlock(&group->fanotify_data.access_mutex); + + return 0; +} + +static void remove_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + struct fanotify_response_event *re; + + if (!(event->mask & FAN_ALL_PERM_EVENTS)) + return; + + re = dequeue_re(group, fd); + if (!re) + return; + + BUG_ON(re->event != event); + + kmem_cache_free(fanotify_response_event_cache, re); + + return; +} +#else +static int prepare_for_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + return 0; +} + +static void remove_access_response(struct fsnotify_group *group, + struct fsnotify_event *event, + __s32 fd) +{ + return 0; +} +#endif + static ssize_t copy_event_to_user(struct fsnotify_group *group, struct fsnotify_event *event, char __user *buf) { struct fanotify_event_metadata fanotify_event_metadata; - int ret; + int fd, ret; pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fill_event_metadata(group, &fanotify_event_metadata, event); - if (ret < 0) - return ret; + fd = fill_event_metadata(group, &fanotify_event_metadata, event); + if (fd < 0) + return fd; + + ret = prepare_for_access_response(group, event, fd); + if (ret) + goto out_close_fd; + ret = -EFAULT; if (copy_to_user(buf, &fanotify_event_metadata, FAN_EVENT_METADATA_LEN)) - return -EFAULT; + goto out_kill_access_response; return FAN_EVENT_METADATA_LEN; + +out_kill_access_response: + remove_access_response(group, event, fd); +out_close_fd: + sys_close(fd); + return ret; } /* intofiy userspace file descriptor functions */ @@ -197,6 +333,33 @@ static ssize_t fanotify_read(struct file *file, char __user *buf, return ret; } +static ssize_t fanotify_write(struct file *file, const char __user *buf, size_t count, loff_t *pos) +{ +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + struct fanotify_response response = { .fd = -1, .response = -1 }; + struct fsnotify_group *group; + int ret; + + group = file->private_data; + + if (count > sizeof(response)) + count = sizeof(response); + + pr_debug("%s: group=%p count=%zu\n", __func__, group, count); + + if (copy_from_user(&response, buf, count)) + return -EFAULT; + + ret = process_access_response(group, &response); + if (ret < 0) + count = ret; + + return count; +#else + return -EINVAL; +#endif +} + static int fanotify_release(struct inode *ignored, struct file *file) { struct fsnotify_group *group = file->private_data; @@ -237,6 +400,7 @@ static long fanotify_ioctl(struct file *file, unsigned int cmd, unsigned long ar static const struct file_operations fanotify_fops = { .poll = fanotify_poll, .read = fanotify_read, + .write = fanotify_write, .fasync = NULL, .release = fanotify_release, .unlocked_ioctl = fanotify_ioctl, @@ -470,7 +634,7 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (flags & ~FAN_ALL_INIT_FLAGS) return -EINVAL; - f_flags = (O_RDONLY | FMODE_NONOTIFY); + f_flags = O_RDWR | FMODE_NONOTIFY; if (flags & FAN_CLOEXEC) f_flags |= O_CLOEXEC; if (flags & FAN_NONBLOCK) @@ -527,7 +691,11 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, default: return -EINVAL; } +#ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS + if (mask & ~(FAN_ALL_EVENTS | FAN_ALL_PERM_EVENTS | FAN_EVENT_ON_CHILD)) +#else if (mask & ~(FAN_ALL_EVENTS | FAN_EVENT_ON_CHILD)) +#endif return -EINVAL; filp = fget_light(fanotify_fd, &fput_needed); @@ -600,6 +768,8 @@ SYSCALL_ALIAS(sys_fanotify_mark, SyS_fanotify_mark); static int __init fanotify_user_setup(void) { fanotify_mark_cache = KMEM_CACHE(fsnotify_mark, SLAB_PANIC); + fanotify_response_event_cache = KMEM_CACHE(fanotify_response_event, + SLAB_PANIC); return 0; } -- cgit v1.1 From 8860f060e473dce1a0873d92105d536f72b05908 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 23 Dec 2009 00:10:25 -0500 Subject: fanotify: do not return 0 in a void function remove_access_response() is supposed to have a void return, but was returning 0; Reported-by: Stephen Rothwell Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 87f0be8..7c869fa 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -231,7 +231,7 @@ static void remove_access_response(struct fsnotify_group *group, struct fsnotify_event *event, __s32 fd) { - return 0; + return; } #endif -- cgit v1.1 From 98b5c10d320adfa250c1c18f3ccaec2f78e5e11d Mon Sep 17 00:00:00 2001 From: Jean-Christophe Dubois Date: Tue, 23 Mar 2010 08:08:09 +0100 Subject: fanotify: do not always return 0 in fsnotify It seems to me you are always returning 0 in fsnotify, when you should return the error (EPERM) returned by fanotify. Signed-off-by: Jean-Christophe DUBOIS Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 6682686..9810bab 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -269,7 +269,7 @@ out: if (event) fsnotify_put_event(event); - return 0; + return ret; } EXPORT_SYMBOL_GPL(fsnotify); -- cgit v1.1 From b31d397e430a90cbe9d3656929a7d5f96e986666 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 21 Apr 2010 16:49:38 -0400 Subject: fsnotify: call iput on inodes when no longer marked fsnotify takes an igrab on an inode when it adds a mark. The code was supposed to drop the reference when the mark was removed but didn't. This caused problems when an fs was unmounted because those inodes would clearly not be gone. Thus resulting in the most devistating of messages: VFS: Busy inodes after unmount of loop0. Self-destruct in 5 seconds. >>> Have a nice day... Jiri Slaby bisected the problem to a patch in the fsnotify tree. The code snippets below show my stupidity quite clearly. void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) { ... mark->inode = NULL; ... } void fsnotify_destroy_mark(struct fsnotify_mark *mark) { struct inode *inode = NULL; ... if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { fsnotify_destroy_inode_mark(mark); inode = mark->i.inode; } ... if (inode) iput(inode); ... } Obviously the intent was to capture the inode before it was set to NULL in fsnotify_destory_inode_mark() so we wouldn't be leaking inodes forever. Instead we leaked them (and exploded on umount) Reported-by: Jiri Slaby Signed-off-by: Eric Paris --- fs/notify/mark.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 1e824e6..8f3b0e7 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -133,8 +133,8 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) spin_lock(&group->mark_lock); if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) { - fsnotify_destroy_inode_mark(mark); inode = mark->i.inode; + fsnotify_destroy_inode_mark(mark); } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) fsnotify_destroy_vfsmount_mark(mark); else -- cgit v1.1 From 0a24887afacefbe2c44e0eee4150b43959a60665 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 14 May 2010 15:35:21 -0500 Subject: inotify_user.c: make local symbol static The symbol inotify_max_user_watches is not used outside this file and should be static. Signed-off-by: H Hartley Sweeten Cc: John McCutchan Cc: Robert Love Cc: Eric Paris Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 1ce71f5..44aeb0f 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -46,7 +46,7 @@ /* these are configurable via /proc/sys/fs/inotify/ */ static int inotify_max_user_instances __read_mostly; static int inotify_max_queued_events __read_mostly; -int inotify_max_user_watches __read_mostly; +static int inotify_max_user_watches __read_mostly; static struct kmem_cache *inotify_inode_mark_cachep __read_mostly; struct kmem_cache *event_priv_cachep __read_mostly; -- cgit v1.1 From 269ed32a9ce00132b9372e9c00014532e054d6b2 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 27 May 2010 09:29:37 -0400 Subject: fanotify: default Kconfig to n fanotify has default to y in linux-next since it's inception but default to n in the final push to Linus. Signed-off-by: Eric Paris --- fs/notify/fanotify/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/Kconfig b/fs/notify/fanotify/Kconfig index 566de30..3ac36b7 100644 --- a/fs/notify/fanotify/Kconfig +++ b/fs/notify/fanotify/Kconfig @@ -2,7 +2,7 @@ config FANOTIFY bool "Filesystem wide access notification" select FSNOTIFY select ANON_INODES - default y + default n ---help--- Say Y here to enable fanotify suport. fanotify is a file access notification system which differs from inotify in that it sends -- cgit v1.1 From 08ae89380a8210a9965d04083e1de78cb8bca4b1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Thu, 27 May 2010 09:41:40 -0400 Subject: fanotify: drop the useless priority argument The priority argument in fanotify is useless. Kill it. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 8 +++----- fs/notify/group.c | 10 +++------- 2 files changed, 6 insertions(+), 12 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 7c869fa..6641020 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -616,14 +616,13 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, } /* fanotify syscalls */ -SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, - unsigned int, priority) +SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) { struct fsnotify_group *group; int f_flags, fd; - pr_debug("%s: flags=%d event_f_flags=%d priority=%d\n", - __func__, flags, event_f_flags, priority); + pr_debug("%s: flags=%d event_f_flags=%d\n", + __func__, flags, event_f_flags); if (event_f_flags) return -EINVAL; @@ -645,7 +644,6 @@ SYSCALL_DEFINE3(fanotify_init, unsigned int, flags, unsigned int, event_f_flags, if (IS_ERR(group)) return PTR_ERR(group); - group->priority = priority; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS mutex_init(&group->fanotify_data.access_mutex); init_waitqueue_head(&group->fanotify_data.access_waitq); diff --git a/fs/notify/group.c b/fs/notify/group.c index ada913f..7ac65ed 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -90,7 +90,6 @@ void fsnotify_recalc_group_mask(struct fsnotify_group *group) void fsnotify_add_vfsmount_group(struct fsnotify_group *group) { struct fsnotify_group *group_iter; - unsigned int priority = group->priority; mutex_lock(&fsnotify_grp_mutex); @@ -98,7 +97,7 @@ void fsnotify_add_vfsmount_group(struct fsnotify_group *group) list_for_each_entry(group_iter, &fsnotify_vfsmount_groups, vfsmount_group_list) { /* insert in front of this one? */ - if (priority < group_iter->priority) { + if (group < group_iter) { /* list_add_tail() insert in front of group_iter */ list_add_tail_rcu(&group->inode_group_list, &group_iter->inode_group_list); @@ -118,15 +117,14 @@ out: void fsnotify_add_inode_group(struct fsnotify_group *group) { struct fsnotify_group *group_iter; - unsigned int priority = group->priority; mutex_lock(&fsnotify_grp_mutex); - /* add to global group list, priority 0 first, UINT_MAX last */ + /* add to global group list */ if (!group->on_inode_group_list) { list_for_each_entry(group_iter, &fsnotify_inode_groups, inode_group_list) { - if (priority < group_iter->priority) { + if (group < group_iter) { /* list_add_tail() insert in front of group_iter */ list_add_tail_rcu(&group->inode_group_list, &group_iter->inode_group_list); @@ -260,8 +258,6 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->marks_list); - group->priority = UINT_MAX; - group->ops = ops; return group; -- cgit v1.1 From e4e047a22058f48544b16728e0f15a3fc12bb0cf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 20 May 2010 01:36:28 +1000 Subject: fsnotify: update gfp/slab.h includes Implicit slab.h inclusion via percpu.h is about to go away. Make sure gfp.h or slab.h is included as necessary. Signed-off-by: Tejun Heo Cc: Stephen Rothwell Cc: Eric Paris Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 1 + fs/notify/vfsmount_mark.c | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 6641020..da01091 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index 8f1aa02..ec580a2 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -22,7 +22,6 @@ #include #include #include -#include #include #include /* for inode_lock */ -- cgit v1.1 From ff311008ab8d2f2cfdbbefd407d1b05acc8164b2 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: fix inotify oneshot support During the large inotify rewrite to fsnotify I completely dropped support for IN_ONESHOT. Reimplement that support. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index daa666a..388a150 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -126,6 +126,9 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev ret = 0; } + if (fsn_mark->mask & IN_ONESHOT) + fsnotify_destroy_mark(fsn_mark); + /* * If we hold the fsn_mark until after the event is on the queue * IN_IGNORED won't be able to pass this event in the queue -- cgit v1.1 From 611da04f7a31b2208e838be55a42c7a1310ae321 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: send IN_UNMOUNT events Since the .31 or so notify rewrite inotify has not sent events about inodes which are unmounted. This patch restores those events. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 44aeb0f..f381daf 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -90,8 +90,11 @@ static inline __u32 inotify_arg_to_mask(u32 arg) { __u32 mask; - /* everything should accept their own ignored and cares about children */ - mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD); + /* + * everything should accept their own ignored, cares about children, + * and should receive events when the inode is unmounted + */ + mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD | FS_UNMOUNT); /* mask off the flags used to open the fd */ mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT)); -- cgit v1.1 From 8c1934c8d70b22ca8333b216aec6c7d09fdbd6a6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: allow users to request not to recieve events on unlinked children An inotify watch on a directory will send events for children even if those children have been unlinked. This patch add a new inotify flag IN_EXCL_UNLINK which allows a watch to specificy they don't care about unlinked children. This should fix performance problems seen by tasks which add a watch to /tmp and then are overrun with events when other processes are reading and writing to unlinked files they created in /tmp. https://bugzilla.kernel.org/show_bug.cgi?id=16296 Requested-by: Matthias Clasen Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 9 +++++++++ fs/notify/inotify/inotify_user.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 388a150..9d332e7 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -22,6 +22,7 @@ * General Public License for more details. */ +#include /* d_unlinked */ #include /* struct inode */ #include #include @@ -157,6 +158,14 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode mask = (mask & ~FS_EVENT_ON_CHILD); send = (fsn_mark->mask & mask); + if (send && (fsn_mark->mask & FS_EXCL_UNLINK) && + (data_type == FSNOTIFY_EVENT_PATH)) { + struct path *path = data; + + if (d_unlinked(path->dentry)) + send = false; + } + /* find took a reference */ fsnotify_put_mark(fsn_mark); diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index f381daf..dfc80f7 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -97,7 +97,7 @@ static inline __u32 inotify_arg_to_mask(u32 arg) mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD | FS_UNMOUNT); /* mask off the flags used to open the fd */ - mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT)); + mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT | IN_EXCL_UNLINK)); return mask; } -- cgit v1.1 From f874e1ac21d7708464dc656a10312542c54719f1 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: force inotify and fsnotify use same bits inotify uses bits called IN_* and fsnotify uses bits called FS_*. These need to line up. This patch adds build time checks to make sure noone can change these bits so they are not the same. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index dfc80f7..c8203ce 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -839,6 +839,27 @@ out: */ static int __init inotify_user_setup(void) { + BUILD_BUG_ON(IN_ACCESS != FS_ACCESS); + BUILD_BUG_ON(IN_MODIFY != FS_MODIFY); + BUILD_BUG_ON(IN_ATTRIB != FS_ATTRIB); + BUILD_BUG_ON(IN_CLOSE_WRITE != FS_CLOSE_WRITE); + BUILD_BUG_ON(IN_CLOSE_NOWRITE != FS_CLOSE_NOWRITE); + BUILD_BUG_ON(IN_OPEN != FS_OPEN); + BUILD_BUG_ON(IN_MOVED_FROM != FS_MOVED_FROM); + BUILD_BUG_ON(IN_MOVED_TO != FS_MOVED_TO); + BUILD_BUG_ON(IN_CREATE != FS_CREATE); + BUILD_BUG_ON(IN_DELETE != FS_DELETE); + BUILD_BUG_ON(IN_DELETE_SELF != FS_DELETE_SELF); + BUILD_BUG_ON(IN_MOVE_SELF != FS_MOVE_SELF); + BUILD_BUG_ON(IN_UNMOUNT != FS_UNMOUNT); + BUILD_BUG_ON(IN_Q_OVERFLOW != FS_Q_OVERFLOW); + BUILD_BUG_ON(IN_IGNORED != FS_IN_IGNORED); + BUILD_BUG_ON(IN_EXCL_UNLINK != FS_EXCL_UNLINK); + BUILD_BUG_ON(IN_ISDIR != FS_IN_ISDIR); + BUILD_BUG_ON(IN_ONESHOT != FS_IN_ONESHOT); + + BUG_ON(hweight32(ALL_INOTIFY_BITS) != 21); + inotify_inode_mark_cachep = KMEM_CACHE(inotify_inode_mark, SLAB_PANIC); event_priv_cachep = KMEM_CACHE(inotify_event_private_data, SLAB_PANIC); -- cgit v1.1 From 44b350fc23e36e95c8e042b7ded66217ea2b9d72 Mon Sep 17 00:00:00 2001 From: Jerome Marchand Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: inotify: Fix mask checks The mask checks in inotify_update_existing_watch() and inotify_new_watch() are useless because inotify_arg_to_mask() sets FS_IN_IGNORED and FS_EVENT_ON_CHILD bits anyway. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index c8203ce..7dc940c 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -566,7 +566,7 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, /* don't allow invalid bits: we don't want flags set */ mask = inotify_arg_to_mask(arg); - if (unlikely(!mask)) + if (unlikely(!(mask & IN_ALL_EVENTS))) return -EINVAL; fsn_mark = fsnotify_find_inode_mark(group, inode); @@ -624,7 +624,7 @@ static int inotify_new_watch(struct fsnotify_group *group, /* don't allow invalid bits: we don't want flags set */ mask = inotify_arg_to_mask(arg); - if (unlikely(!mask)) + if (unlikely(!(mask & IN_ALL_EVENTS))) return -EINVAL; tmp_i_mark = kmem_cache_alloc(inotify_inode_mark_cachep, GFP_KERNEL); -- cgit v1.1 From 20dee624ca40db227aa70cb3f44d2d6cb4fdbab4 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: check to make sure all fsnotify bits are unique This patch adds a check to make sure that all fsnotify bits are unique and we cannot accidentally use the same bit for 2 different fsnotify event types. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 9810bab..076c10e 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -275,6 +275,8 @@ EXPORT_SYMBOL_GPL(fsnotify); static __init int fsnotify_init(void) { + BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23); + return init_srcu_struct(&fsnotify_grp_srcu); } subsys_initcall(fsnotify_init); -- cgit v1.1 From 80af2588676483ac4e998b5092e9d008dab3ab62 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fanotify: groups can specify their f_flags for new fd Currently fanotify fds opened for thier listeners are done with f_flags equal to O_RDONLY | O_LARGEFILE. This patch instead takes f_flags from the fanotify_init syscall and uses those when opening files in the context of the listener. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify_user.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index da01091..7182c83 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -81,7 +81,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) * are NULL; That's fine, just don't call dentry open */ if (dentry && mnt) new_file = dentry_open(dentry, mnt, - O_RDONLY | O_LARGEFILE | FMODE_NONOTIFY, + group->fanotify_data.f_flags | FMODE_NONOTIFY, current_cred()); else new_file = ERR_PTR(-EOVERFLOW); @@ -625,9 +625,6 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) pr_debug("%s: flags=%d event_f_flags=%d\n", __func__, flags, event_f_flags); - if (event_f_flags) - return -EINVAL; - if (!capable(CAP_SYS_ADMIN)) return -EACCES; @@ -645,6 +642,7 @@ SYSCALL_DEFINE2(fanotify_init, unsigned int, flags, unsigned int, event_f_flags) if (IS_ERR(group)) return PTR_ERR(group); + group->fanotify_data.f_flags = event_f_flags; #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS mutex_init(&group->fanotify_data.access_mutex); init_waitqueue_head(&group->fanotify_data.access_waitq); -- cgit v1.1 From 5ba08e2eeb06355f66ed62ae97bb87d145973a9a Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: add pr_debug throughout It can be hard to debug fsnotify since there are so few printks. Use pr_debug to allow for dynamic debugging. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 4 ++++ fs/notify/inotify/inotify_fsnotify.c | 6 ++++++ fs/notify/inotify/inotify_user.c | 10 ++++++++++ fs/notify/notification.c | 13 +++++++++++++ 4 files changed, 33 insertions(+) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 076c10e..72aae40 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -174,6 +174,10 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { + pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_is=%d" + " cookie=%d event=%p\n", __func__, group, to_tell, mnt, + mask, data, data_is, cookie, *event); + if (!group->ops->should_send_event(group, to_tell, mnt, mask, data, data_is)) return 0; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 9d332e7..906b727 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -98,6 +98,9 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev struct fsnotify_event_private_data *fsn_event_priv; int wd, ret; + pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, + event, event->to_tell, event->mask); + to_tell = event->to_tell; fsn_mark = fsnotify_find_inode_mark(group, to_tell); @@ -151,6 +154,9 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct fsnotify_mark *fsn_mark; bool send; + pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", + __func__, group, inode, mask, data, data_type); + fsn_mark = fsnotify_find_inode_mark(group, inode); if (!fsn_mark) return false; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 7dc940c..1068e1c 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -141,6 +141,8 @@ static struct fsnotify_event *get_one_event(struct fsnotify_group *group, event = fsnotify_peek_notify_event(group); + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + if (event->name_len) event_size += roundup(event->name_len + 1, event_size); @@ -170,6 +172,8 @@ static ssize_t copy_event_to_user(struct fsnotify_group *group, size_t event_size = sizeof(struct inotify_event); size_t name_len = 0; + pr_debug("%s: group=%p event=%p\n", __func__, group, event); + /* we get the inotify watch descriptor from the event private data */ spin_lock(&event->lock); fsn_priv = fsnotify_remove_priv_from_event(group, event); @@ -242,6 +246,8 @@ static ssize_t inotify_read(struct file *file, char __user *buf, kevent = get_one_event(group, count); mutex_unlock(&group->notification_mutex); + pr_debug("%s: group=%p kevent=%p\n", __func__, group, kevent); + if (kevent) { ret = PTR_ERR(kevent); if (IS_ERR(kevent)) @@ -286,6 +292,8 @@ static int inotify_release(struct inode *ignored, struct file *file) struct fsnotify_group *group = file->private_data; struct user_struct *user = group->inotify_data.user; + pr_debug("%s: group=%p\n", __func__, group); + fsnotify_clear_marks_by_group(group); /* free this group, matching get was inotify_init->fsnotify_obtain_group */ @@ -309,6 +317,8 @@ static long inotify_ioctl(struct file *file, unsigned int cmd, group = file->private_data; p = (void __user *) arg; + pr_debug("%s: group=%p cmd=%u\n", __func__, group, cmd); + switch (cmd) { case FIONREAD: mutex_lock(&group->notification_mutex); diff --git a/fs/notify/notification.c b/fs/notify/notification.c index b35faaf..e6dde25 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -87,6 +87,8 @@ void fsnotify_put_event(struct fsnotify_event *event) return; if (atomic_dec_and_test(&event->refcnt)) { + pr_debug("%s: event=%p\n", __func__, event); + if (event->data_type == FSNOTIFY_EVENT_PATH) path_put(&event->path); @@ -146,6 +148,8 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even struct list_head *list = &group->notification_list; int rc = 0; + pr_debug("%s: group=%p event=%p priv=%p\n", __func__, group, event, priv); + /* * There is one fsnotify_event_holder embedded inside each fsnotify_event. * Check if we expect to be able to use that holder. If not alloc a new @@ -222,6 +226,8 @@ struct fsnotify_event *fsnotify_remove_notify_event(struct fsnotify_group *group BUG_ON(!mutex_is_locked(&group->notification_mutex)); + pr_debug("%s: group=%p\n", __func__, group); + holder = list_first_entry(&group->notification_list, struct fsnotify_event_holder, event_list); event = holder->event; @@ -307,6 +313,8 @@ int fsnotify_replace_event(struct fsnotify_event_holder *old_holder, SPINLOCK_NEW, }; + pr_debug("%s: old_event=%p new_event=%p\n", __func__, old_event, new_event); + /* * if the new_event's embedded holder is in use someone * screwed up and didn't give us a clean new event. @@ -340,6 +348,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) if (!event) return NULL; + pr_debug("%s: old_event=%p new_event=%p\n", __func__, old_event, event); + memcpy(event, old_event, sizeof(*event)); initialize_event(event); @@ -379,6 +389,9 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, if (!event) return NULL; + pr_debug("%s: event=%p to_tell=%p mask=%x data=%p data_type=%d\n", + __func__, event, to_tell, mask, data, data_type); + initialize_event(event); if (name) { -- cgit v1.1 From f70ab54cc6c3907b0727ba332b3976f80f3846d0 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: fsnotify_add_notify_event should return an event Rather than the horrific void ** argument and such just to pass the fanotify_merge event back to the caller of fsnotify_add_notify_event() have those things return an event if it was different than the event suggusted to be added. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 103 ++++++++++++++++------------------- fs/notify/inotify/inotify_fsnotify.c | 28 +++++----- fs/notify/inotify/inotify_user.c | 11 +++- fs/notify/notification.c | 42 +++++++++----- 4 files changed, 96 insertions(+), 88 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index bbcfccd..f3c40c0 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -30,65 +30,58 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) return false; } -/* Note, if we return an event in *arg that a reference is being held... */ -static int fanotify_merge(struct list_head *list, - struct fsnotify_event *event, - void **arg) +/* and the list better be locked by something too! */ +static struct fsnotify_event *fanotify_merge(struct list_head *list, + struct fsnotify_event *event) { struct fsnotify_event_holder *test_holder; - struct fsnotify_event *test_event; + struct fsnotify_event *test_event = NULL; struct fsnotify_event *new_event; - struct fsnotify_event **return_event = (struct fsnotify_event **)arg; - int ret = 0; pr_debug("%s: list=%p event=%p\n", __func__, list, event); - *return_event = NULL; - - /* and the list better be locked by something too! */ list_for_each_entry_reverse(test_holder, list, event_list) { - test_event = test_holder->event; - if (should_merge(test_event, event)) { - fsnotify_get_event(test_event); - *return_event = test_event; - - ret = -EEXIST; - /* if they are exactly the same we are done */ - if (test_event->mask == event->mask) - goto out; - - /* - * if the refcnt == 1 this is the only queue - * for this event and so we can update the mask - * in place. - */ - if (atomic_read(&test_event->refcnt) == 1) { - test_event->mask |= event->mask; - goto out; - } - - /* can't allocate memory, merge was no possible */ - new_event = fsnotify_clone_event(test_event); - if (unlikely(!new_event)) { - ret = 0; - goto out; - } - - /* we didn't return the test_event, so drop that ref */ - fsnotify_put_event(test_event); - /* the reference we return on new_event is from clone */ - *return_event = new_event; - - /* build new event and replace it on the list */ - new_event->mask = (test_event->mask | event->mask); - fsnotify_replace_event(test_holder, new_event); - + if (should_merge(test_holder->event, event)) { + test_event = test_holder->event; break; } } -out: - return ret; + + if (!test_event) + return NULL; + + fsnotify_get_event(test_event); + + /* if they are exactly the same we are done */ + if (test_event->mask == event->mask) + return test_event; + + /* + * if the refcnt == 2 this is the only queue + * for this event and so we can update the mask + * in place. + */ + if (atomic_read(&test_event->refcnt) == 2) { + test_event->mask |= event->mask; + return test_event; + } + + new_event = fsnotify_clone_event(test_event); + + /* done with test_event */ + fsnotify_put_event(test_event); + + /* couldn't allocate memory, merge was not possible */ + if (unlikely(!new_event)) + return ERR_PTR(-ENOMEM); + + /* build new event and replace it on the list */ + new_event->mask = (test_event->mask | event->mask); + fsnotify_replace_event(test_holder, new_event); + + /* we hold a reference on new_event from clone_event */ + return new_event; } #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS @@ -123,7 +116,7 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) { - int ret; + int ret = 0; struct fsnotify_event *notify_event = NULL; BUILD_BUG_ON(FAN_ACCESS != FS_ACCESS); @@ -138,13 +131,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e pr_debug("%s: group=%p event=%p\n", __func__, group, event); - ret = fsnotify_add_notify_event(group, event, NULL, fanotify_merge, - (void **)¬ify_event); - /* -EEXIST means this event was merged with another, not that it was an error */ - if (ret == -EEXIST) - ret = 0; - if (ret) - goto out; + notify_event = fsnotify_add_notify_event(group, event, NULL, fanotify_merge); + if (IS_ERR(notify_event)) + return PTR_ERR(notify_event); #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS if (event->mask & FAN_ALL_PERM_EVENTS) { @@ -155,9 +144,9 @@ static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_e } #endif -out: if (notify_event) fsnotify_put_event(notify_event); + return ret; } diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 906b727..73a1106 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -68,13 +68,11 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new return false; } -static int inotify_merge(struct list_head *list, - struct fsnotify_event *event, - void **arg) +static struct fsnotify_event *inotify_merge(struct list_head *list, + struct fsnotify_event *event) { struct fsnotify_event_holder *last_holder; struct fsnotify_event *last_event; - int ret = 0; /* and the list better be locked by something too */ spin_lock(&event->lock); @@ -82,11 +80,13 @@ static int inotify_merge(struct list_head *list, last_holder = list_entry(list->prev, struct fsnotify_event_holder, event_list); last_event = last_holder->event; if (event_compare(last_event, event)) - ret = -EEXIST; + fsnotify_get_event(last_event); + else + last_event = NULL; spin_unlock(&event->lock); - return ret; + return last_event; } static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) @@ -96,7 +96,8 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev struct inode *to_tell; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; - int wd, ret; + struct fsnotify_event *added_event; + int wd, ret = 0; pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, event, event->to_tell, event->mask); @@ -120,14 +121,13 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev fsn_event_priv->group = group; event_priv->wd = wd; - ret = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge, NULL); - if (ret) { + added_event = fsnotify_add_notify_event(group, event, fsn_event_priv, inotify_merge); + if (added_event) { inotify_free_event_priv(fsn_event_priv); - /* EEXIST says we tail matched, EOVERFLOW isn't something - * to report up the stack. */ - if ((ret == -EEXIST) || - (ret == -EOVERFLOW)) - ret = 0; + if (!IS_ERR(added_event)) + fsnotify_put_event(added_event); + else + ret = PTR_ERR(added_event); } if (fsn_mark->mask & IN_ONESHOT) diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index 1068e1c..a4cd227 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -516,7 +516,7 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, struct fsnotify_group *group) { struct inotify_inode_mark *i_mark; - struct fsnotify_event *ignored_event; + struct fsnotify_event *ignored_event, *notify_event; struct inotify_event_private_data *event_priv; struct fsnotify_event_private_data *fsn_event_priv; int ret; @@ -538,9 +538,14 @@ void inotify_ignored_and_remove_idr(struct fsnotify_mark *fsn_mark, fsn_event_priv->group = group; event_priv->wd = i_mark->wd; - ret = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL, NULL); - if (ret) + notify_event = fsnotify_add_notify_event(group, ignored_event, fsn_event_priv, NULL); + if (notify_event) { + if (IS_ERR(notify_event)) + ret = PTR_ERR(notify_event); + else + fsnotify_put_event(notify_event); inotify_free_event_priv(fsn_event_priv); + } skip_send_ignore: diff --git a/fs/notify/notification.c b/fs/notify/notification.c index e6dde25..f39260f 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -137,16 +137,14 @@ struct fsnotify_event_private_data *fsnotify_remove_priv_from_event(struct fsnot * event off the queue to deal with. If the event is successfully added to the * group's notification queue, a reference is taken on event. */ -int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, - struct fsnotify_event_private_data *priv, - int (*merge)(struct list_head *, - struct fsnotify_event *, - void **arg), - void **arg) +struct fsnotify_event *fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_event *event, + struct fsnotify_event_private_data *priv, + struct fsnotify_event *(*merge)(struct list_head *, + struct fsnotify_event *)) { + struct fsnotify_event *return_event = NULL; struct fsnotify_event_holder *holder = NULL; struct list_head *list = &group->notification_list; - int rc = 0; pr_debug("%s: group=%p event=%p priv=%p\n", __func__, group, event, priv); @@ -162,27 +160,37 @@ int fsnotify_add_notify_event(struct fsnotify_group *group, struct fsnotify_even alloc_holder: holder = fsnotify_alloc_event_holder(); if (!holder) - return -ENOMEM; + return ERR_PTR(-ENOMEM); } mutex_lock(&group->notification_mutex); if (group->q_len >= group->max_events) { event = q_overflow_event; - rc = -EOVERFLOW; + + /* + * we need to return the overflow event + * which means we need a ref + */ + fsnotify_get_event(event); + return_event = event; + /* sorry, no private data on the overflow event */ priv = NULL; } if (!list_empty(list) && merge) { - int ret; + struct fsnotify_event *tmp; - ret = merge(list, event, arg); - if (ret) { + tmp = merge(list, event); + if (tmp) { mutex_unlock(&group->notification_mutex); + + if (return_event) + fsnotify_put_event(return_event); if (holder != &event->holder) fsnotify_destroy_event_holder(holder); - return ret; + return tmp; } } @@ -197,6 +205,12 @@ alloc_holder: * event holder was used, go back and get a new one */ spin_unlock(&event->lock); mutex_unlock(&group->notification_mutex); + + if (return_event) { + fsnotify_put_event(return_event); + return_event = NULL; + } + goto alloc_holder; } @@ -211,7 +225,7 @@ alloc_holder: mutex_unlock(&group->notification_mutex); wake_up(&group->notification_waitq); - return rc; + return return_event; } /* -- cgit v1.1 From 3bcf3860a4ff9bbc522820b4b765e65e4deceb3e Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:37 -0400 Subject: fsnotify: store struct file not struct path Al explains that calling dentry_open() with a mnt/dentry pair is only garunteed to be safe if they are already used in an open struct file. To make sure this is the case don't store and use a struct path in fsnotify, always use a struct file. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 8 ++++---- fs/notify/fanotify/fanotify_user.c | 6 +++--- fs/notify/fsnotify.c | 16 ++++++++-------- fs/notify/inotify/inotify_fsnotify.c | 12 ++++++------ fs/notify/notification.c | 20 +++++++++----------- 5 files changed, 30 insertions(+), 32 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index f3c40c0..c2a3029 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -17,9 +17,9 @@ static bool should_merge(struct fsnotify_event *old, struct fsnotify_event *new) old->data_type == new->data_type && old->tgid == new->tgid) { switch (old->data_type) { - case (FSNOTIFY_EVENT_PATH): - if ((old->path.mnt == new->path.mnt) && - (old->path.dentry == new->path.dentry)) + case (FSNOTIFY_EVENT_FILE): + if ((old->file->f_path.mnt == new->file->f_path.mnt) && + (old->file->f_path.dentry == new->file->f_path.dentry)) return true; case (FSNOTIFY_EVENT_NONE): return true; @@ -226,7 +226,7 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; /* if we don't have enough info to send an event to userspace say no */ - if (data_type != FSNOTIFY_EVENT_PATH) + if (data_type != FSNOTIFY_EVENT_FILE) return false; if (mnt) diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 7182c83..50cea74 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -65,7 +65,7 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) if (client_fd < 0) return client_fd; - if (event->data_type != FSNOTIFY_EVENT_PATH) { + if (event->data_type != FSNOTIFY_EVENT_FILE) { WARN_ON(1); put_unused_fd(client_fd); return -EINVAL; @@ -75,8 +75,8 @@ static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event) * we need a new file handle for the userspace program so it can read even if it was * originally opened O_WRONLY. */ - dentry = dget(event->path.dentry); - mnt = mntget(event->path.mnt); + dentry = dget(event->file->f_path.dentry); + mnt = mntget(event->file->f_path.mnt); /* it's possible this event was an overflow event. in that case dentry and mnt * are NULL; That's fine, just don't call dentry open */ if (dentry && mnt) diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 72aae40..4788c86 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -84,7 +84,7 @@ void __fsnotify_update_child_dentry_flags(struct inode *inode) } /* Notify this dentry's parent about a child's events. */ -void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) +void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) { struct dentry *parent; struct inode *p_inode; @@ -92,7 +92,7 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) bool should_update_children = false; if (!dentry) - dentry = path->dentry; + dentry = file->f_path.dentry; if (!(dentry->d_flags & DCACHE_FSNOTIFY_PARENT_WATCHED)) return; @@ -124,8 +124,8 @@ void __fsnotify_parent(struct path *path, struct dentry *dentry, __u32 mask) * specifies these are events which came from a child. */ mask |= FS_EVENT_ON_CHILD; - if (path) - fsnotify(p_inode, mask, path, FSNOTIFY_EVENT_PATH, + if (file) + fsnotify(p_inode, mask, file, FSNOTIFY_EVENT_FILE, dentry->d_name.name, 0); else fsnotify(p_inode, mask, dentry->d_inode, FSNOTIFY_EVENT_INODE, @@ -154,10 +154,10 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) spin_unlock(&inode->i_lock); } - if (data_is == FSNOTIFY_EVENT_PATH) { + if (data_is == FSNOTIFY_EVENT_FILE) { struct vfsmount *mnt; - mnt = ((struct path *)data)->mnt; + mnt = ((struct file *)data)->f_path.mnt; if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { spin_lock(&mnt->mnt_root->d_lock); hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { @@ -228,8 +228,8 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, !(test_mask & fsnotify_vfsmount_mask)) return 0; - if (data_is == FSNOTIFY_EVENT_PATH) - mnt = ((struct path *)data)->mnt; + if (data_is == FSNOTIFY_EVENT_FILE) + mnt = ((struct file *)data)->f_path.mnt; /* if this inode's directed listeners don't care and nothing on the vfsmount * listeners list cares, nothing to do */ diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 73a1106..3c506e0 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -52,9 +52,9 @@ static bool event_compare(struct fsnotify_event *old, struct fsnotify_event *new !strcmp(old->file_name, new->file_name)) return true; break; - case (FSNOTIFY_EVENT_PATH): - if ((old->path.mnt == new->path.mnt) && - (old->path.dentry == new->path.dentry)) + case (FSNOTIFY_EVENT_FILE): + if ((old->file->f_path.mnt == new->file->f_path.mnt) && + (old->file->f_path.dentry == new->file->f_path.dentry)) return true; break; case (FSNOTIFY_EVENT_NONE): @@ -165,10 +165,10 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode send = (fsn_mark->mask & mask); if (send && (fsn_mark->mask & FS_EXCL_UNLINK) && - (data_type == FSNOTIFY_EVENT_PATH)) { - struct path *path = data; + (data_type == FSNOTIFY_EVENT_FILE)) { + struct file *file = data; - if (d_unlinked(path->dentry)) + if (d_unlinked(file->f_path.dentry)) send = false; } diff --git a/fs/notify/notification.c b/fs/notify/notification.c index f39260f..c106cdd 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -31,6 +31,7 @@ * allocated and used. */ +#include #include #include #include @@ -89,8 +90,8 @@ void fsnotify_put_event(struct fsnotify_event *event) if (atomic_dec_and_test(&event->refcnt)) { pr_debug("%s: event=%p\n", __func__, event); - if (event->data_type == FSNOTIFY_EVENT_PATH) - path_put(&event->path); + if (event->data_type == FSNOTIFY_EVENT_FILE) + fput(event->file); BUG_ON(!list_empty(&event->private_data_list)); @@ -375,8 +376,8 @@ struct fsnotify_event *fsnotify_clone_event(struct fsnotify_event *old_event) } } event->tgid = get_pid(old_event->tgid); - if (event->data_type == FSNOTIFY_EVENT_PATH) - path_get(&event->path); + if (event->data_type == FSNOTIFY_EVENT_FILE) + get_file(event->file); return event; } @@ -423,11 +424,9 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, event->data_type = data_type; switch (data_type) { - case FSNOTIFY_EVENT_PATH: { - struct path *path = data; - event->path.dentry = path->dentry; - event->path.mnt = path->mnt; - path_get(&event->path); + case FSNOTIFY_EVENT_FILE: { + event->file = data; + get_file(event->file); break; } case FSNOTIFY_EVENT_INODE: @@ -435,8 +434,7 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, break; case FSNOTIFY_EVENT_NONE: event->inode = NULL; - event->path.dentry = NULL; - event->path.mnt = NULL; + event->file = NULL; break; default: BUG(); -- cgit v1.1 From c1e5c954020e123d30b4abf4038ce501861bcf9f Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: vfs/fsnotify: fsnotify_close can delay the final work in fput fanotify almost works like so: user context calls fsnotify_* function with a struct file. fsnotify takes a reference on the struct path user context goes about it's buissiness at some later point in time the fsnotify listener gets the struct path fanotify listener calls dentry_open() to create a file which userspace can deal with listener drops the reference on the struct path at some later point the listener calls close() on it's new file With the switch from struct path to struct file this presents a problem for fput() and fsnotify_close(). fsnotify_close() is called when the filp has already reached 0 and __fput() wants to do it's cleanup. The solution presented here is a bit odd. If an event is created from a struct file we take a reference on the file. We check however if the f_count was already 0 and if so we take an EXTRA reference EVEN THOUGH IT WAS ZERO. In __fput() (where we know the f_count hit 0 once) we check if the f_count is non-zero and if so we drop that 'extra' ref and return without destroying the file. Signed-off-by: Eric Paris --- fs/file_table.c | 9 +++++++++ fs/notify/notification.c | 13 +++++++++++++ 2 files changed, 22 insertions(+) (limited to 'fs') diff --git a/fs/file_table.c b/fs/file_table.c index 5c7d10e..b8a0bb6 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -230,6 +230,15 @@ static void __fput(struct file *file) might_sleep(); fsnotify_close(file); + + /* + * fsnotify_create_event may have taken one or more references on this + * file. If it did so it left one reference for us to drop to make sure + * its calls to fput could not prematurely destroy the file. + */ + if (atomic_long_read(&file->f_count)) + return fput(file); + /* * The function eventpoll_release() should be the first called * in the file cleanup chain. diff --git a/fs/notify/notification.c b/fs/notify/notification.c index c106cdd..d6c435a 100644 --- a/fs/notify/notification.c +++ b/fs/notify/notification.c @@ -426,6 +426,19 @@ struct fsnotify_event *fsnotify_create_event(struct inode *to_tell, __u32 mask, switch (data_type) { case FSNOTIFY_EVENT_FILE: { event->file = data; + /* + * if this file is about to disappear hold an extra reference + * until we return to __fput so we don't have to worry about + * future get/put destroying the file under us or generating + * additional events. Notice that we change f_mode without + * holding f_lock. This is safe since this is the only possible + * reference to this object in the kernel (it was about to be + * freed, remember?) + */ + if (!atomic_long_read(&event->file->f_count)) { + event->file->f_mode |= FMODE_NONOTIFY; + get_file(event->file); + } get_file(event->file); break; } -- cgit v1.1 From 0c6532e4e3b0c8bd18dd0a5cc1894a1944997cc6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: place marks on object in order of group memory address fsnotify_marks currently are placed on objects (inodes or vfsmounts) in arbitrary order. This patch places them in order of the group memory address. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 40 +++++++++++++++++++++++++++++----------- fs/notify/vfsmount_mark.c | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 55 insertions(+), 25 deletions(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 0c0a48b..83ce6db 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -174,15 +174,17 @@ void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *mark, } /* - * Attach an initialized mark to a given group and inode. + * Attach an initialized mark to a given 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. + * event types should be delivered to which group and for which inodes. These + * marks are ordered according to the group's location in memory. */ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct inode *inode, int allow_dups) { - struct fsnotify_mark *lmark = NULL; + struct fsnotify_mark *lmark; + struct hlist_node *node, *last = NULL; int ret = 0; mark->flags = FSNOTIFY_MARK_FLAG_INODE; @@ -192,21 +194,37 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, spin_lock(&inode->i_lock); - if (!allow_dups) - lmark = fsnotify_find_inode_mark_locked(group, inode); - if (!lmark) { - mark->i.inode = inode; + mark->i.inode = inode; + /* is mark the first mark? */ + if (hlist_empty(&inode->i_fsnotify_marks)) { hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + goto out; + } + + /* should mark be in the middle of the current list? */ + hlist_for_each_entry(lmark, node, &inode->i_fsnotify_marks, i.i_list) { + last = node; + + if ((lmark->group == group) && !allow_dups) { + ret = -EEXIST; + goto out; + } - fsnotify_recalc_inode_mask_locked(inode); + if (mark->group < lmark->group) + continue; + + hlist_add_before(&mark->i.i_list, &lmark->i.i_list); + goto out; } + BUG_ON(last == NULL); + /* mark should be the last entry. last is the current last entry */ + hlist_add_after(last, &mark->i.i_list); +out: + fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); - if (lmark) - ret = -EEXIST; - return ret; } diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index ec580a2..c4b3f14 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -141,34 +141,46 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct vfsmount *mnt, int allow_dups) { - struct fsnotify_mark *lmark = NULL; + struct fsnotify_mark *lmark; + struct hlist_node *node, *last = NULL; int ret = 0; mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT; - /* - * LOCKING ORDER!!!! - * mark->lock - * group->mark_lock - * mnt->mnt_root->d_lock - */ assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); spin_lock(&mnt->mnt_root->d_lock); - if (!allow_dups) - lmark = fsnotify_find_vfsmount_mark_locked(group, mnt); - if (!lmark) { - mark->m.mnt = mnt; + mark->m.mnt = mnt; + /* is mark the first mark? */ + if (hlist_empty(&mnt->mnt_fsnotify_marks)) { hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); + goto out; + } + + /* should mark be in the middle of the current list? */ + hlist_for_each_entry(lmark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + last = node; + + if ((lmark->group == group) && !allow_dups) { + ret = -EEXIST; + goto out; + } + + if (mark->group < lmark->group) + continue; - fsnotify_recalc_vfsmount_mask_locked(mnt); - } else { - ret = -EEXIST; + hlist_add_before(&mark->m.m_list, &lmark->m.m_list); + goto out; } + BUG_ON(last == NULL); + /* mark should be the last entry. last is the current last entry */ + hlist_add_after(last, &mark->m.m_list); +out: + fsnotify_recalc_vfsmount_mask_locked(mnt); spin_unlock(&mnt->mnt_root->d_lock); return ret; -- cgit v1.1 From a4c6e9961fcb9da54648d98978d33c6fdcb7bb45 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: use _rcu functions for mark list traversal In preparation for srcu locking use all _rcu appropiete functions for mark list addition, removal, and traversal. The operations are still done under a spinlock at the end of this patch. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 10 +++++----- fs/notify/vfsmount_mark.c | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 83ce6db..455cb41 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -67,7 +67,7 @@ void fsnotify_destroy_inode_mark(struct fsnotify_mark *mark) spin_lock(&inode->i_lock); - hlist_del_init(&mark->i.i_list); + hlist_del_init_rcu(&mark->i.i_list); mark->i.inode = NULL; /* @@ -92,7 +92,7 @@ void fsnotify_clear_marks_by_inode(struct inode *inode) spin_lock(&inode->i_lock); hlist_for_each_entry_safe(mark, pos, n, &inode->i_fsnotify_marks, i.i_list) { list_add(&mark->i.free_i_list, &free_list); - hlist_del_init(&mark->i.i_list); + hlist_del_init_rcu(&mark->i.i_list); fsnotify_get_mark(mark); } spin_unlock(&inode->i_lock); @@ -198,7 +198,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, /* is mark the first mark? */ if (hlist_empty(&inode->i_fsnotify_marks)) { - hlist_add_head(&mark->i.i_list, &inode->i_fsnotify_marks); + hlist_add_head_rcu(&mark->i.i_list, &inode->i_fsnotify_marks); goto out; } @@ -214,13 +214,13 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, if (mark->group < lmark->group) continue; - hlist_add_before(&mark->i.i_list, &lmark->i.i_list); + hlist_add_before_rcu(&mark->i.i_list, &lmark->i.i_list); goto out; } BUG_ON(last == NULL); /* mark should be the last entry. last is the current last entry */ - hlist_add_after(last, &mark->i.i_list); + hlist_add_after_rcu(last, &mark->i.i_list); out: fsnotify_recalc_inode_mask_locked(inode); spin_unlock(&inode->i_lock); diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index c4b3f14..b7ae640 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -39,7 +39,7 @@ void fsnotify_clear_marks_by_mount(struct vfsmount *mnt) spin_lock(&mnt->mnt_root->d_lock); hlist_for_each_entry_safe(mark, pos, n, &mnt->mnt_fsnotify_marks, m.m_list) { list_add(&mark->m.free_m_list, &free_list); - hlist_del_init(&mark->m.m_list); + hlist_del_init_rcu(&mark->m.m_list); fsnotify_get_mark(mark); } spin_unlock(&mnt->mnt_root->d_lock); @@ -91,7 +91,7 @@ void fsnotify_destroy_vfsmount_mark(struct fsnotify_mark *mark) spin_lock(&mnt->mnt_root->d_lock); - hlist_del_init(&mark->m.m_list); + hlist_del_init_rcu(&mark->m.m_list); mark->m.mnt = NULL; fsnotify_recalc_vfsmount_mask_locked(mnt); @@ -156,7 +156,7 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, /* is mark the first mark? */ if (hlist_empty(&mnt->mnt_fsnotify_marks)) { - hlist_add_head(&mark->m.m_list, &mnt->mnt_fsnotify_marks); + hlist_add_head_rcu(&mark->m.m_list, &mnt->mnt_fsnotify_marks); goto out; } @@ -172,13 +172,13 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, if (mark->group < lmark->group) continue; - hlist_add_before(&mark->m.m_list, &lmark->m.m_list); + hlist_add_before_rcu(&mark->m.m_list, &lmark->m.m_list); goto out; } BUG_ON(last == NULL); /* mark should be the last entry. last is the current last entry */ - hlist_add_after(last, &mark->m.m_list); + hlist_add_after_rcu(last, &mark->m.m_list); out: fsnotify_recalc_vfsmount_mask_locked(mnt); spin_unlock(&mnt->mnt_root->d_lock); -- cgit v1.1 From 700307a29ad61090dcf1d45f8f4a135f5e9211ae Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: use an explicit flag to indicate fsnotify_destroy_mark has been called Currently fsnotify check is mark->group is NULL to decide if fsnotify_destroy_mark() has already been called or not. With the upcoming rcu work it is a heck of a lot easier to use an explicit flag than worry about group being set to NULL. Signed-off-by: Eric Paris --- fs/notify/inode_mark.c | 2 +- fs/notify/mark.c | 11 +++++++---- fs/notify/vfsmount_mark.c | 2 +- 3 files changed, 9 insertions(+), 6 deletions(-) (limited to 'fs') diff --git a/fs/notify/inode_mark.c b/fs/notify/inode_mark.c index 455cb41..37b460f 100644 --- a/fs/notify/inode_mark.c +++ b/fs/notify/inode_mark.c @@ -187,7 +187,7 @@ int fsnotify_add_inode_mark(struct fsnotify_mark *mark, struct hlist_node *node, *last = NULL; int ret = 0; - mark->flags = FSNOTIFY_MARK_FLAG_INODE; + mark->flags |= FSNOTIFY_MARK_FLAG_INODE; assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 8f3b0e7..69c5a16 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -121,12 +121,14 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) group = mark->group; - /* if !group something else already marked this to die */ - if (!group) { + /* something else already called this function on this mark */ + if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE)) { spin_unlock(&mark->lock); return; } + mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; + /* 1 from caller and 1 for being on i_list/g_list */ BUG_ON(atomic_read(&mark->refcnt) < 2); @@ -141,7 +143,6 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) BUG(); list_del_init(&mark->g_list); - mark->group = NULL; fsnotify_put_mark(mark); /* for i_list and g_list */ @@ -229,6 +230,8 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, spin_lock(&mark->lock); spin_lock(&group->mark_lock); + mark->flags |= FSNOTIFY_MARK_FLAG_ALIVE; + mark->group = group; list_add(&mark->g_list, &group->marks_list); atomic_inc(&group->num_marks); @@ -258,7 +261,7 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, return ret; err: - mark->group = NULL; + mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; list_del_init(&mark->g_list); atomic_dec(&group->num_marks); fsnotify_put_mark(mark); diff --git a/fs/notify/vfsmount_mark.c b/fs/notify/vfsmount_mark.c index b7ae640..56772b5 100644 --- a/fs/notify/vfsmount_mark.c +++ b/fs/notify/vfsmount_mark.c @@ -145,7 +145,7 @@ int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, struct hlist_node *node, *last = NULL; int ret = 0; - mark->flags = FSNOTIFY_MARK_FLAG_VFSMOUNT; + mark->flags |= FSNOTIFY_MARK_FLAG_VFSMOUNT; assert_spin_locked(&mark->lock); assert_spin_locked(&group->mark_lock); -- cgit v1.1 From 75c1be487a690db43da2c1234fcacd84c982803c Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: srcu to protect read side of inode and vfsmount locks Currently reading the inode->i_fsnotify_marks or vfsmount->mnt_fsnotify_marks lists are protected by a spinlock on both the read and the write side. This patch protects the read side of those lists with a new single srcu. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 69 ++++++++++++++++++++++++++++++++++------------------ fs/notify/fsnotify.h | 5 ++-- fs/notify/group.c | 16 ++++-------- fs/notify/mark.c | 60 ++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 110 insertions(+), 40 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 4788c86..4678b41 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -144,14 +144,15 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) { struct fsnotify_mark *mark; struct hlist_node *node; + int idx; + + idx = srcu_read_lock(&fsnotify_mark_srcu); if (!hlist_empty(&inode->i_fsnotify_marks)) { - spin_lock(&inode->i_lock); - hlist_for_each_entry(mark, node, &inode->i_fsnotify_marks, i.i_list) { + hlist_for_each_entry_rcu(mark, node, &inode->i_fsnotify_marks, i.i_list) { if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) mark->ignored_mask = 0; } - spin_unlock(&inode->i_lock); } if (data_is == FSNOTIFY_EVENT_FILE) { @@ -159,14 +160,14 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) mnt = ((struct file *)data)->f_path.mnt; if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { - spin_lock(&mnt->mnt_root->d_lock); - hlist_for_each_entry(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) mark->ignored_mask = 0; } - spin_unlock(&mnt->mnt_root->d_lock); } } + + srcu_read_unlock(&fsnotify_mark_srcu, idx); } static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, @@ -208,8 +209,10 @@ static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const unsigned char *file_name, u32 cookie) { + struct fsnotify_mark *mark; struct fsnotify_group *group; struct fsnotify_event *event = NULL; + struct hlist_node *node; struct vfsmount *mnt = NULL; int idx, ret = 0; /* global tests shouldn't care about events on child only the specific event */ @@ -237,35 +240,47 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, !needed_by_vfsmount(test_mask, mnt)) return 0; - /* - * SRCU!! the groups list is very very much read only and the path is - * very hot. The VAST majority of events are not going to need to do - * anything other than walk the list so it's crazy to pre-allocate. - */ - idx = srcu_read_lock(&fsnotify_grp_srcu); + idx = srcu_read_lock(&fsnotify_mark_srcu); if (test_mask & to_tell->i_fsnotify_mask) { - list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) { - if (test_mask & group->mask) { - ret = send_to_group(group, to_tell, NULL, mask, data, data_is, - cookie, file_name, &event); + hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { + + pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", + __func__, mark, mark->mask, mark->ignored_mask); + + if (test_mask & mark->mask & ~mark->ignored_mask) { + group = mark->group; + if (!group) + continue; + ret = send_to_group(group, to_tell, NULL, mask, + data, data_is, cookie, file_name, + &event); if (ret) goto out; } } } - if (needed_by_vfsmount(test_mask, mnt)) { - list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) { - if (test_mask & group->mask) { - ret = send_to_group(group, to_tell, mnt, mask, data, data_is, - cookie, file_name, &event); + + if (mnt && (test_mask & mnt->mnt_fsnotify_mask)) { + hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { + + pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", + __func__, mark, mark->mask, mark->ignored_mask); + + if (test_mask & mark->mask & ~mark->ignored_mask) { + group = mark->group; + if (!group) + continue; + ret = send_to_group(group, to_tell, mnt, mask, + data, data_is, cookie, file_name, + &event); if (ret) goto out; } } } out: - srcu_read_unlock(&fsnotify_grp_srcu, idx); + srcu_read_unlock(&fsnotify_mark_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned * up while we are still trying to add it to lists, drop that one. @@ -279,8 +294,14 @@ EXPORT_SYMBOL_GPL(fsnotify); static __init int fsnotify_init(void) { + int ret; + BUG_ON(hweight32(ALL_FSNOTIFY_EVENTS) != 23); - return init_srcu_struct(&fsnotify_grp_srcu); + ret = init_srcu_struct(&fsnotify_mark_srcu); + if (ret) + panic("initializing fsnotify_mark_srcu"); + + return 0; } -subsys_initcall(fsnotify_init); +core_initcall(fsnotify_init); diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 1be54f6..7eed86f 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -6,8 +6,6 @@ #include #include -/* protects reads of fsnotify_groups */ -extern struct srcu_struct fsnotify_grp_srcu; /* all groups which receive inode fsnotify events */ extern struct list_head fsnotify_inode_groups; /* all groups which receive vfsmount fsnotify events */ @@ -20,6 +18,9 @@ extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); +/* protects reads of inode and vfsmount marks list */ +extern struct srcu_struct fsnotify_mark_srcu; + extern void fsnotify_set_inode_mark_mask_locked(struct fsnotify_mark *fsn_mark, __u32 mask); /* add a mark to an inode */ diff --git a/fs/notify/group.c b/fs/notify/group.c index 7ac65ed..48d3a6d 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -30,8 +30,6 @@ /* protects writes to fsnotify_groups and fsnotify_mask */ static DEFINE_MUTEX(fsnotify_grp_mutex); -/* protects reads while running the fsnotify_groups list */ -struct srcu_struct fsnotify_grp_srcu; /* all groups registered to receive inode filesystem notifications */ LIST_HEAD(fsnotify_inode_groups); /* all groups registered to receive mount point filesystem notifications */ @@ -50,18 +48,17 @@ void fsnotify_recalc_global_mask(void) struct fsnotify_group *group; __u32 inode_mask = 0; __u32 vfsmount_mask = 0; - int idx; - idx = srcu_read_lock(&fsnotify_grp_srcu); + mutex_lock(&fsnotify_grp_mutex); list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) inode_mask |= group->mask; list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) vfsmount_mask |= group->mask; - - srcu_read_unlock(&fsnotify_grp_srcu, idx); fsnotify_inode_mask = inode_mask; fsnotify_vfsmount_mask = vfsmount_mask; + + mutex_unlock(&fsnotify_grp_mutex); } /* @@ -168,6 +165,8 @@ static void fsnotify_destroy_group(struct fsnotify_group *group) /* clear all inode marks for this group */ fsnotify_clear_marks_by_group(group); + synchronize_srcu(&fsnotify_mark_srcu); + /* past the point of no return, matches the initial value of 1 */ if (atomic_dec_and_test(&group->num_marks)) fsnotify_final_destroy_group(group); @@ -216,12 +215,7 @@ void fsnotify_put_group(struct fsnotify_group *group) */ __fsnotify_evict_group(group); - /* - * now it's off the list, so the only thing we might care about is - * srcu access.... - */ mutex_unlock(&fsnotify_grp_mutex); - synchronize_srcu(&fsnotify_grp_srcu); /* and now it is really dead. _Nothing_ could be seeing it */ fsnotify_recalc_global_mask(); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 69c5a16..41f3990 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -85,10 +85,12 @@ #include #include #include +#include #include #include #include #include +#include #include /* for inode_lock */ #include @@ -96,6 +98,11 @@ #include #include "fsnotify.h" +struct srcu_struct fsnotify_mark_srcu; +static DEFINE_SPINLOCK(destroy_lock); +static LIST_HEAD(destroy_list); +static DECLARE_WAIT_QUEUE_HEAD(destroy_waitq); + void fsnotify_get_mark(struct fsnotify_mark *mark) { atomic_inc(&mark->refcnt); @@ -144,11 +151,14 @@ void fsnotify_destroy_mark(struct fsnotify_mark *mark) list_del_init(&mark->g_list); - fsnotify_put_mark(mark); /* for i_list and g_list */ - spin_unlock(&group->mark_lock); spin_unlock(&mark->lock); + spin_lock(&destroy_lock); + list_add(&mark->destroy_list, &destroy_list); + spin_unlock(&destroy_lock); + wake_up(&destroy_waitq); + /* * Some groups like to know that marks are being freed. This is a * callback to the group function to let it know that this mark @@ -263,12 +273,17 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, err: mark->flags &= ~FSNOTIFY_MARK_FLAG_ALIVE; list_del_init(&mark->g_list); + mark->group = NULL; atomic_dec(&group->num_marks); - fsnotify_put_mark(mark); spin_unlock(&group->mark_lock); spin_unlock(&mark->lock); + spin_lock(&destroy_lock); + list_add(&mark->destroy_list, &destroy_list); + spin_unlock(&destroy_lock); + wake_up(&destroy_waitq); + return ret; } @@ -326,3 +341,42 @@ void fsnotify_init_mark(struct fsnotify_mark *mark, atomic_set(&mark->refcnt, 1); mark->free_mark = free_mark; } + +static int fsnotify_mark_destroy(void *ignored) +{ + struct fsnotify_mark *mark, *next; + LIST_HEAD(private_destroy_list); + + for (;;) { + spin_lock(&destroy_lock); + list_for_each_entry_safe(mark, next, &destroy_list, destroy_list) { + list_del(&mark->destroy_list); + list_add(&mark->destroy_list, &private_destroy_list); + } + spin_unlock(&destroy_lock); + + synchronize_srcu(&fsnotify_mark_srcu); + + list_for_each_entry_safe(mark, next, &private_destroy_list, destroy_list) { + list_del_init(&mark->destroy_list); + fsnotify_put_mark(mark); + } + + wait_event_interruptible(destroy_waitq, !list_empty(&destroy_list)); + } + + return 0; +} + +static int __init fsnotify_mark_init(void) +{ + struct task_struct *thread; + + thread = kthread_run(fsnotify_mark_destroy, NULL, + "fsnotify_mark"); + if (IS_ERR(thread)) + panic("unable to start fsnotify mark destruction thread."); + + return 0; +} +device_initcall(fsnotify_mark_init); -- cgit v1.1 From 8778abb9a88fc4a74d8776ffaadf7214cf33c61e Mon Sep 17 00:00:00 2001 From: Andreas Gruenbacher Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: Exchange list heads instead of moving elements Instead of moving list elements from destroy_list to &private_destroy_list, exchange the list heads. Signed-off-by: Andreas Gruenbacher Signed-off-by: Eric Paris --- fs/notify/mark.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'fs') diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 41f3990..236f29b 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -349,10 +349,8 @@ static int fsnotify_mark_destroy(void *ignored) for (;;) { spin_lock(&destroy_lock); - list_for_each_entry_safe(mark, next, &destroy_list, destroy_list) { - list_del(&mark->destroy_list); - list_add(&mark->destroy_list, &private_destroy_list); - } + /* exchange the list head */ + list_replace_init(&destroy_list, &private_destroy_list); spin_unlock(&destroy_lock); synchronize_srcu(&fsnotify_mark_srcu); -- cgit v1.1 From 3a9b16b407f10b2a771bcae13fb5791e527d6bcf Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fsnotify: send fsnotify_mark to groups in event handling functions With the change of fsnotify to use srcu walking the marks list instead of walking the global groups list we now know the mark in question. The code can send the mark to the group's handling functions and the groups won't have to find those marks themselves. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 +++- fs/notify/fanotify/fanotify.c | 8 +++++--- fs/notify/fsnotify.c | 19 ++++++++++--------- fs/notify/inotify/inotify_fsnotify.c | 8 +++++--- 4 files changed, 23 insertions(+), 16 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 6624c2e..2cae9be 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -83,6 +83,7 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) * events. */ static int dnotify_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, struct fsnotify_event *event) { struct fsnotify_mark *fsn_mark = NULL; @@ -130,7 +131,8 @@ static int dnotify_handle_event(struct fsnotify_group *group, */ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, - __u32 mask, void *data, int data_type) + struct fsnotify_mark *mark, __u32 mask, + void *data, int data_type) { struct fsnotify_mark *fsn_mark; bool send; diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index c2a3029..abfba45 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -114,7 +114,9 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, } #endif -static int fanotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +static int fanotify_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, + struct fsnotify_event *event) { int ret = 0; struct fsnotify_event *notify_event = NULL; @@ -214,8 +216,8 @@ static bool should_send_inode_event(struct fsnotify_group *group, struct inode * } static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_type) { pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", __func__, group, to_tell, mnt, mask, data, data_type); diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 4678b41..59d6399 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -171,15 +171,16 @@ void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) } static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, __u32 mask, void *data, - int data_is, u32 cookie, const unsigned char *file_name, + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_is, u32 cookie, + const unsigned char *file_name, struct fsnotify_event **event) { - pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_is=%d" - " cookie=%d event=%p\n", __func__, group, to_tell, mnt, - mask, data, data_is, cookie, *event); + pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" + " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, + mnt, mark, mask, data, data_is, cookie, *event); - if (!group->ops->should_send_event(group, to_tell, mnt, mask, + if (!group->ops->should_send_event(group, to_tell, mnt, mark, mask, data, data_is)) return 0; if (!*event) { @@ -189,7 +190,7 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, if (!*event) return -ENOMEM; } - return group->ops->handle_event(group, *event); + return group->ops->handle_event(group, mark, *event); } static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) @@ -252,7 +253,7 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, group = mark->group; if (!group) continue; - ret = send_to_group(group, to_tell, NULL, mask, + ret = send_to_group(group, to_tell, NULL, mark, mask, data, data_is, cookie, file_name, &event); if (ret) @@ -271,7 +272,7 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, group = mark->group; if (!group) continue; - ret = send_to_group(group, to_tell, mnt, mask, + ret = send_to_group(group, to_tell, mnt, mark, mask, data, data_is, cookie, file_name, &event); if (ret) diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 3c506e0..dbd76bb 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -89,7 +89,9 @@ static struct fsnotify_event *inotify_merge(struct list_head *list, return last_event; } -static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *event) +static int inotify_handle_event(struct fsnotify_group *group, + struct fsnotify_mark *mark, + struct fsnotify_event *event) { struct fsnotify_mark *fsn_mark; struct inotify_inode_mark *i_mark; @@ -148,8 +150,8 @@ static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, __u32 mask, void *data, - int data_type) + struct vfsmount *mnt, struct fsnotify_mark *mark, + __u32 mask, void *data, int data_type) { struct fsnotify_mark *fsn_mark; bool send; -- cgit v1.1 From 7f6b6117e1803777fcf48fe31bd236a7fbf740db Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: inotify: use the mark in handler functions inotify now gets a mark in the should_send_event and handle_event functions. Rather than look up the mark themselves inotify should just use the mark it was handed. Signed-off-by: Eric Paris --- fs/notify/inotify/inotify_fsnotify.c | 29 +++++------------------------ 1 file changed, 5 insertions(+), 24 deletions(-) (limited to 'fs') diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index dbd76bb..aa3f93c 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -93,7 +93,6 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_mark *mark, struct fsnotify_event *event) { - struct fsnotify_mark *fsn_mark; struct inotify_inode_mark *i_mark; struct inode *to_tell; struct inotify_event_private_data *event_priv; @@ -106,11 +105,7 @@ static int inotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - fsn_mark = fsnotify_find_inode_mark(group, to_tell); - /* race with watch removal? We already passes should_send */ - if (unlikely(!fsn_mark)) - return 0; - i_mark = container_of(fsn_mark, struct inotify_inode_mark, + i_mark = container_of(mark, struct inotify_inode_mark, fsn_mark); wd = i_mark->wd; @@ -132,14 +127,8 @@ static int inotify_handle_event(struct fsnotify_group *group, ret = PTR_ERR(added_event); } - if (fsn_mark->mask & IN_ONESHOT) - fsnotify_destroy_mark(fsn_mark); - - /* - * If we hold the fsn_mark until after the event is on the queue - * IN_IGNORED won't be able to pass this event in the queue - */ - fsnotify_put_mark(fsn_mark); + if (mark->mask & IN_ONESHOT) + fsnotify_destroy_mark(mark); return ret; } @@ -153,20 +142,15 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *fsn_mark; bool send; pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", __func__, group, inode, mask, data, data_type); - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return false; - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (fsn_mark->mask & mask); + send = (mark->mask & mask); - if (send && (fsn_mark->mask & FS_EXCL_UNLINK) && + if (send && (mark->mask & FS_EXCL_UNLINK) && (data_type == FSNOTIFY_EVENT_FILE)) { struct file *file = data; @@ -174,9 +158,6 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode send = false; } - /* find took a reference */ - fsnotify_put_mark(fsn_mark); - return send; } -- cgit v1.1 From c496313fcc35a41e176e3f19cdda2544ea3a32a6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: dnotify: use the mark in handler functions dnotify now gets a mark in the should_send_event and handle_event functions. Rather than look up the mark themselves dnotify should just use the mark it was handed. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index 2cae9be..e3e855f 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -86,7 +86,6 @@ static int dnotify_handle_event(struct fsnotify_group *group, struct fsnotify_mark *mark, struct fsnotify_event *event) { - struct fsnotify_mark *fsn_mark = NULL; struct dnotify_mark *dn_mark; struct inode *to_tell; struct dnotify_struct *dn; @@ -96,12 +95,9 @@ static int dnotify_handle_event(struct fsnotify_group *group, to_tell = event->to_tell; - fsn_mark = fsnotify_find_inode_mark(group, to_tell); - if (unlikely(!fsn_mark)) - return 0; - dn_mark = container_of(fsn_mark, struct dnotify_mark, fsn_mark); + dn_mark = container_of(mark, struct dnotify_mark, fsn_mark); - spin_lock(&fsn_mark->lock); + spin_lock(&mark->lock); prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_mask & test_mask) == 0) { @@ -115,12 +111,11 @@ static int dnotify_handle_event(struct fsnotify_group *group, else { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(fsn_mark); + dnotify_recalc_inode_mask(mark); } } - spin_unlock(&fsn_mark->lock); - fsnotify_put_mark(fsn_mark); + spin_unlock(&mark->lock); return 0; } @@ -134,7 +129,6 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - struct fsnotify_mark *fsn_mark; bool send; /* !dir_notify_enable should never get here, don't waste time checking @@ -145,14 +139,8 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, if (!S_ISDIR(inode->i_mode)) return false; - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return false; - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mask & fsn_mark->mask); - - fsnotify_put_mark(fsn_mark); /* matches fsnotify_find_inode_mark */ + send = (mask & mark->mask); return send; } -- cgit v1.1 From 0215054f377ce5ac4ffc27b26b13b3f10e6410e6 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:38 -0400 Subject: fanotify: use the mark in handler functions fanotify now gets a mark in the should_send_event and handle_event functions. Rather than look up the mark themselves fanotify should just use the mark it was handed. Signed-off-by: Eric Paris --- fs/notify/fanotify/fanotify.c | 46 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) (limited to 'fs') diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index abfba45..666ccb7 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -152,18 +152,16 @@ static int fanotify_handle_event(struct fsnotify_group *group, return ret; } -static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsmount *mnt, - struct inode *inode, __u32 mask) +static bool should_send_vfsmount_event(struct fsnotify_group *group, + struct vfsmount *mnt, + struct inode *inode, + struct fsnotify_mark *mnt_mark, + __u32 mask) { - struct fsnotify_mark *mnt_mark; struct fsnotify_mark *inode_mark; - pr_debug("%s: group=%p vfsmount=%p mask=%x\n", - __func__, group, mnt, mask); - - mnt_mark = fsnotify_find_vfsmount_mark(group, mnt); - if (!mnt_mark) - return false; + pr_debug("%s: group=%p vfsmount=%p mark=%p mask=%x\n", + __func__, group, mnt, mnt_mark, mask); mask &= mnt_mark->mask; mask &= ~mnt_mark->ignored_mask; @@ -176,28 +174,21 @@ static bool should_send_vfsmount_event(struct fsnotify_group *group, struct vfsm } } - /* find took a reference */ - fsnotify_put_mark(mnt_mark); - return mask; } -static bool should_send_inode_event(struct fsnotify_group *group, struct inode *inode, +static bool should_send_inode_event(struct fsnotify_group *group, + struct inode *inode, + struct fsnotify_mark *mark, __u32 mask) { - struct fsnotify_mark *fsn_mark; - - pr_debug("%s: group=%p inode=%p mask=%x\n", - __func__, group, inode, mask); - - fsn_mark = fsnotify_find_inode_mark(group, inode); - if (!fsn_mark) - return false; + pr_debug("%s: group=%p inode=%p mark=%p mask=%x\n", + __func__, group, inode, mark, mask); /* if the event is for a child and this inode doesn't care about * events on the child, don't send it! */ if ((mask & FS_EVENT_ON_CHILD) && - !(fsn_mark->mask & FS_EVENT_ON_CHILD)) { + !(mark->mask & FS_EVENT_ON_CHILD)) { mask = 0; } else { /* @@ -205,13 +196,10 @@ static bool should_send_inode_event(struct fsnotify_group *group, struct inode * * type of event? */ mask &= ~FS_EVENT_ON_CHILD; - mask &= fsn_mark->mask; - mask &= ~fsn_mark->ignored_mask; + mask &= mark->mask; + mask &= ~mark->ignored_mask; } - /* find took a reference */ - fsnotify_put_mark(fsn_mark); - return mask; } @@ -232,9 +220,9 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; if (mnt) - return should_send_vfsmount_event(group, mnt, to_tell, mask); + return should_send_vfsmount_event(group, mnt, to_tell, mark, mask); else - return should_send_inode_event(group, to_tell, mask); + return should_send_inode_event(group, to_tell, mark, mask); } const struct fsnotify_ops fanotify_fsnotify_ops = { -- cgit v1.1 From 2612abb51b11ffd2d75c472b11178115f5808909 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: cleanup should_send_event The change to use srcu and walk the object list rather than the global fsnotify_group list means that should_send_event is no longer needed for a number of groups and can be simplified for others. Do that. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 11 +---------- fs/notify/fanotify/fanotify.c | 23 ++++++++--------------- fs/notify/fsnotify.c | 4 ++-- fs/notify/inotify/inotify_fsnotify.c | 14 +++----------- 4 files changed, 14 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e3e855f..c3dc158 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -129,20 +129,11 @@ static bool dnotify_should_send_event(struct fsnotify_group *group, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - bool send; - - /* !dir_notify_enable should never get here, don't waste time checking - if (!dir_notify_enable) - return 0; */ - /* not a dir, dnotify doesn't care */ if (!S_ISDIR(inode->i_mode)) return false; - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mask & mark->mask); - - return send; + return true; } static void dnotify_free_mark(struct fsnotify_mark *fsn_mark) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index 666ccb7..fbd7f35 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -185,22 +185,15 @@ static bool should_send_inode_event(struct fsnotify_group *group, pr_debug("%s: group=%p inode=%p mark=%p mask=%x\n", __func__, group, inode, mark, mask); - /* if the event is for a child and this inode doesn't care about - * events on the child, don't send it! */ + /* + * if the event is for a child and this inode doesn't care about + * events on the child, don't send it! + */ if ((mask & FS_EVENT_ON_CHILD) && - !(mark->mask & FS_EVENT_ON_CHILD)) { - mask = 0; - } else { - /* - * We care about children, but do we care about this particular - * type of event? - */ - mask &= ~FS_EVENT_ON_CHILD; - mask &= mark->mask; - mask &= ~mark->ignored_mask; - } - - return mask; + !(mark->mask & FS_EVENT_ON_CHILD)) + return false; + else + return true; } static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 59d6399..53b31f4 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -180,8 +180,8 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, mnt, mark, mask, data, data_is, cookie, *event); - if (!group->ops->should_send_event(group, to_tell, mnt, mark, mask, - data, data_is)) + if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, + data, data_is) == false) return 0; if (!*event) { *event = fsnotify_create_event(to_tell, mask, data, diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index aa3f93c..7cf518b 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -142,23 +142,15 @@ static bool inotify_should_send_event(struct fsnotify_group *group, struct inode struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_type) { - bool send; - - pr_debug("%s: group=%p inode=%p mask=%x data=%p data_type=%d\n", - __func__, group, inode, mask, data, data_type); - - mask = (mask & ~FS_EVENT_ON_CHILD); - send = (mark->mask & mask); - - if (send && (mark->mask & FS_EXCL_UNLINK) && + if ((mark->mask & FS_EXCL_UNLINK) && (data_type == FSNOTIFY_EVENT_FILE)) { struct file *file = data; if (d_unlinked(file->f_path.dentry)) - send = false; + return false; } - return send; + return true; } /* -- cgit v1.1 From 03930979afa63e079e9aefd4d3dd429240711027 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: remove the global masks Because we walk the object->fsnotify_marks list instead of the global fsnotify groups list we don't need the fsnotify_inode_mask and fsnotify_vfsmount_mask as these were simply shortcuts in fsnotify() for performance. They are now extra checks, rip them out. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 5 ----- fs/notify/fsnotify.h | 4 ---- fs/notify/group.c | 39 ++------------------------------------- 3 files changed, 2 insertions(+), 46 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 53b31f4..9ba29ee 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -227,11 +227,6 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if (mask & FS_MODIFY) __fsnotify_flush_ignored_mask(to_tell, data, data_is); - /* if none of the directed listeners or vfsmount listeners care */ - if (!(test_mask & fsnotify_inode_mask) && - !(test_mask & fsnotify_vfsmount_mask)) - return 0; - if (data_is == FSNOTIFY_EVENT_FILE) mnt = ((struct file *)data)->f_path.mnt; diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index 7eed86f..b41dbf5 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -10,10 +10,6 @@ extern struct list_head fsnotify_inode_groups; /* all groups which receive vfsmount fsnotify events */ extern struct list_head fsnotify_vfsmount_groups; -/* all bitwise OR of all event types (FS_*) for all fsnotify_inode_groups */ -extern __u32 fsnotify_inode_mask; -/* all bitwise OR of all event types (FS_*) for all fsnotify_vfsmount_groups */ -extern __u32 fsnotify_vfsmount_mask; /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index 48d3a6d..8da532d 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -34,54 +34,21 @@ static DEFINE_MUTEX(fsnotify_grp_mutex); LIST_HEAD(fsnotify_inode_groups); /* all groups registered to receive mount point filesystem notifications */ LIST_HEAD(fsnotify_vfsmount_groups); -/* bitwise OR of all events (FS_*) interesting to some group on this system */ -__u32 fsnotify_inode_mask; -/* bitwise OR of all events (FS_*) interesting to some group on this system */ -__u32 fsnotify_vfsmount_mask; - -/* - * When a new group registers or changes it's set of interesting events - * this function updates the fsnotify_mask to contain all interesting events - */ -void fsnotify_recalc_global_mask(void) -{ - struct fsnotify_group *group; - __u32 inode_mask = 0; - __u32 vfsmount_mask = 0; - - mutex_lock(&fsnotify_grp_mutex); - list_for_each_entry_rcu(group, &fsnotify_inode_groups, inode_group_list) - inode_mask |= group->mask; - list_for_each_entry_rcu(group, &fsnotify_vfsmount_groups, vfsmount_group_list) - vfsmount_mask |= group->mask; - - fsnotify_inode_mask = inode_mask; - fsnotify_vfsmount_mask = vfsmount_mask; - - mutex_unlock(&fsnotify_grp_mutex); -} /* * Update the group->mask by running all of the marks associated with this - * group and finding the bitwise | of all of the mark->mask. If we change - * the group->mask we need to update the global mask of events interesting - * to the system. + * group and finding the bitwise | of all of the mark->mask. */ void fsnotify_recalc_group_mask(struct fsnotify_group *group) { __u32 mask = 0; - __u32 old_mask = group->mask; struct fsnotify_mark *mark; spin_lock(&group->mark_lock); list_for_each_entry(mark, &group->marks_list, g_list) mask |= mark->mask; - spin_unlock(&group->mark_lock); - group->mask = mask; - - if (old_mask != mask) - fsnotify_recalc_global_mask(); + spin_unlock(&group->mark_lock); } void fsnotify_add_vfsmount_group(struct fsnotify_group *group) @@ -217,8 +184,6 @@ void fsnotify_put_group(struct fsnotify_group *group) mutex_unlock(&fsnotify_grp_mutex); - /* and now it is really dead. _Nothing_ could be seeing it */ - fsnotify_recalc_global_mask(); fsnotify_destroy_group(group); } -- cgit v1.1 From 43709a288ed03aa0e2979ab63dd089b3889645c4 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: remove group->mask group->mask is now useless. It was originally a shortcut for fsnotify to save on performance. These checks are now redundant, so we remove them. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 4 ---- fs/notify/fanotify/fanotify_user.c | 23 +++++------------------ fs/notify/group.c | 16 ---------------- fs/notify/inotify/inotify_user.c | 9 --------- 4 files changed, 5 insertions(+), 47 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index c3dc158..e92b2c8 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -199,8 +199,6 @@ void dnotify_flush(struct file *filp, fl_owner_t id) if (dn_mark->dn == NULL) fsnotify_destroy_mark(fsn_mark); - fsnotify_recalc_group_mask(dnotify_group); - mutex_unlock(&dnotify_mark_mutex); fsnotify_put_mark(fsn_mark); @@ -385,8 +383,6 @@ out: if (destroy) fsnotify_destroy_mark(fsn_mark); - fsnotify_recalc_group_mask(dnotify_group); - mutex_unlock(&dnotify_mark_mutex); fsnotify_put_mark(fsn_mark); out_err: diff --git a/fs/notify/fanotify/fanotify_user.c b/fs/notify/fanotify/fanotify_user.c index 50cea74..25a3b4d 100644 --- a/fs/notify/fanotify/fanotify_user.c +++ b/fs/notify/fanotify/fanotify_user.c @@ -496,8 +496,6 @@ static int fanotify_remove_vfsmount_mark(struct fsnotify_group *group, removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); - if (removed & group->mask) - fsnotify_recalc_group_mask(group); if (removed & mnt->mnt_fsnotify_mask) fsnotify_recalc_vfsmount_mask(mnt); @@ -518,9 +516,6 @@ static int fanotify_remove_inode_mark(struct fsnotify_group *group, removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags); /* matches the fsnotify_find_inode_mark() */ fsnotify_put_mark(fsn_mark); - - if (removed & group->mask) - fsnotify_recalc_group_mask(group); if (removed & inode->i_fsnotify_mask) fsnotify_recalc_inode_mask(inode); @@ -572,12 +567,9 @@ static int fanotify_add_vfsmount_mark(struct fsnotify_group *group, } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); - if (added) { - if (added & ~group->mask) - fsnotify_recalc_group_mask(group); - if (added & ~mnt->mnt_fsnotify_mask) - fsnotify_recalc_vfsmount_mask(mnt); - } + if (added & ~mnt->mnt_fsnotify_mask) + fsnotify_recalc_vfsmount_mask(mnt); + return 0; } @@ -607,12 +599,8 @@ static int fanotify_add_inode_mark(struct fsnotify_group *group, } added = fanotify_mark_add_to_mask(fsn_mark, mask, flags); fsnotify_put_mark(fsn_mark); - if (added) { - if (added & ~group->mask) - fsnotify_recalc_group_mask(group); - if (added & ~inode->i_fsnotify_mask) - fsnotify_recalc_inode_mask(inode); - } + if (added & ~inode->i_fsnotify_mask) + fsnotify_recalc_inode_mask(inode); return 0; } @@ -734,7 +722,6 @@ SYSCALL_DEFINE(fanotify_mark)(int fanotify_fd, unsigned int flags, fsnotify_clear_vfsmount_marks_by_group(group); else fsnotify_clear_inode_marks_by_group(group); - fsnotify_recalc_group_mask(group); break; default: ret = -EINVAL; diff --git a/fs/notify/group.c b/fs/notify/group.c index 8da532d..fc0d966 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -35,22 +35,6 @@ LIST_HEAD(fsnotify_inode_groups); /* all groups registered to receive mount point filesystem notifications */ LIST_HEAD(fsnotify_vfsmount_groups); -/* - * Update the group->mask by running all of the marks associated with this - * group and finding the bitwise | of all of the mark->mask. - */ -void fsnotify_recalc_group_mask(struct fsnotify_group *group) -{ - __u32 mask = 0; - struct fsnotify_mark *mark; - - spin_lock(&group->mark_lock); - list_for_each_entry(mark, &group->marks_list, g_list) - mask |= mark->mask; - group->mask = mask; - spin_unlock(&group->mark_lock); -} - void fsnotify_add_vfsmount_group(struct fsnotify_group *group) { struct fsnotify_group *group_iter; diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c index a4cd227..bf7f6d7 100644 --- a/fs/notify/inotify/inotify_user.c +++ b/fs/notify/inotify/inotify_user.c @@ -606,16 +606,11 @@ static int inotify_update_existing_watch(struct fsnotify_group *group, int dropped = (old_mask & ~new_mask); /* more bits in this fsn_mark than the inode's mask? */ int do_inode = (new_mask & ~inode->i_fsnotify_mask); - /* more bits in this fsn_mark than the group? */ - int do_group = (new_mask & ~group->mask); /* update the inode with this new fsn_mark */ if (dropped || do_inode) fsnotify_recalc_inode_mask(inode); - /* update the group mask with the new mask */ - if (dropped || do_group) - fsnotify_recalc_group_mask(group); } /* return the wd */ @@ -673,10 +668,6 @@ static int inotify_new_watch(struct fsnotify_group *group, /* return the watch descriptor for this new mark */ ret = tmp_i_mark->wd; - /* if this mark added a new event update the group mask */ - if (mask & ~group->mask) - fsnotify_recalc_group_mask(group); - out_err: /* match the ref from fsnotify_init_mark() */ fsnotify_put_mark(&tmp_i_mark->fsn_mark); -- cgit v1.1 From 02436668d98385f5b5d9ffb695a37dadf98ed8a8 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: remove global fsnotify groups lists The global fsnotify groups lists were invented as a way to increase the performance of fsnotify by shortcutting events which were not interesting. With the changes to walk the object lists rather than global groups lists these shortcuts are not useful. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 5 --- fs/notify/fsnotify.h | 9 ----- fs/notify/group.c | 107 +-------------------------------------------------- fs/notify/mark.c | 9 ----- 4 files changed, 2 insertions(+), 128 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 9ba29ee..1dd1fde 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -219,11 +219,6 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - /* if no fsnotify listeners, nothing to do */ - if (list_empty(&fsnotify_inode_groups) && - list_empty(&fsnotify_vfsmount_groups)) - return 0; - if (mask & FS_MODIFY) __fsnotify_flush_ignored_mask(to_tell, data, data_is); diff --git a/fs/notify/fsnotify.h b/fs/notify/fsnotify.h index b41dbf5..85e7d2b 100644 --- a/fs/notify/fsnotify.h +++ b/fs/notify/fsnotify.h @@ -6,11 +6,6 @@ #include #include -/* all groups which receive inode fsnotify events */ -extern struct list_head fsnotify_inode_groups; -/* all groups which receive vfsmount fsnotify events */ -extern struct list_head fsnotify_vfsmount_groups; - /* destroy all events sitting in this groups notification queue */ extern void fsnotify_flush_notify(struct fsnotify_group *group); @@ -28,10 +23,6 @@ extern int fsnotify_add_vfsmount_mark(struct fsnotify_mark *mark, struct fsnotify_group *group, struct vfsmount *mnt, int allow_dups); -/* add a group to the inode group list */ -extern void fsnotify_add_inode_group(struct fsnotify_group *group); -/* add a group to the vfsmount group list */ -extern void fsnotify_add_vfsmount_group(struct fsnotify_group *group); /* final kfree of a group */ extern void fsnotify_final_destroy_group(struct fsnotify_group *group); diff --git a/fs/notify/group.c b/fs/notify/group.c index fc0d966..d309f38 100644 --- a/fs/notify/group.c +++ b/fs/notify/group.c @@ -28,67 +28,6 @@ #include -/* protects writes to fsnotify_groups and fsnotify_mask */ -static DEFINE_MUTEX(fsnotify_grp_mutex); -/* all groups registered to receive inode filesystem notifications */ -LIST_HEAD(fsnotify_inode_groups); -/* all groups registered to receive mount point filesystem notifications */ -LIST_HEAD(fsnotify_vfsmount_groups); - -void fsnotify_add_vfsmount_group(struct fsnotify_group *group) -{ - struct fsnotify_group *group_iter; - - mutex_lock(&fsnotify_grp_mutex); - - if (!group->on_vfsmount_group_list) { - list_for_each_entry(group_iter, &fsnotify_vfsmount_groups, - vfsmount_group_list) { - /* insert in front of this one? */ - if (group < group_iter) { - /* list_add_tail() insert in front of group_iter */ - list_add_tail_rcu(&group->inode_group_list, - &group_iter->inode_group_list); - goto out; - } - } - - /* apparently we need to be the last entry */ - list_add_tail_rcu(&group->vfsmount_group_list, &fsnotify_vfsmount_groups); - } -out: - group->on_vfsmount_group_list = 1; - - mutex_unlock(&fsnotify_grp_mutex); -} - -void fsnotify_add_inode_group(struct fsnotify_group *group) -{ - struct fsnotify_group *group_iter; - - mutex_lock(&fsnotify_grp_mutex); - - /* add to global group list */ - if (!group->on_inode_group_list) { - list_for_each_entry(group_iter, &fsnotify_inode_groups, - inode_group_list) { - if (group < group_iter) { - /* list_add_tail() insert in front of group_iter */ - list_add_tail_rcu(&group->inode_group_list, - &group_iter->inode_group_list); - goto out; - } - } - - /* apparently we need to be the last entry */ - list_add_tail_rcu(&group->inode_group_list, &fsnotify_inode_groups); - } -out: - group->on_inode_group_list = 1; - - mutex_unlock(&fsnotify_grp_mutex); -} - /* * Final freeing of a group */ @@ -124,51 +63,12 @@ static void fsnotify_destroy_group(struct fsnotify_group *group) } /* - * Remove this group from the global list of groups that will get events - * this can be done even if there are still references and things still using - * this group. This just stops the group from getting new events. - */ -static void __fsnotify_evict_group(struct fsnotify_group *group) -{ - BUG_ON(!mutex_is_locked(&fsnotify_grp_mutex)); - - if (group->on_inode_group_list) - list_del_rcu(&group->inode_group_list); - group->on_inode_group_list = 0; - if (group->on_vfsmount_group_list) - list_del_rcu(&group->vfsmount_group_list); - group->on_vfsmount_group_list = 0; -} - -/* - * Called when a group is no longer interested in getting events. This can be - * used if a group is misbehaving or if for some reason a group should no longer - * get any filesystem events. - */ -void fsnotify_evict_group(struct fsnotify_group *group) -{ - mutex_lock(&fsnotify_grp_mutex); - __fsnotify_evict_group(group); - mutex_unlock(&fsnotify_grp_mutex); -} - -/* * Drop a reference to a group. Free it if it's through. */ void fsnotify_put_group(struct fsnotify_group *group) { - if (!atomic_dec_and_mutex_lock(&group->refcnt, &fsnotify_grp_mutex)) - return; - - /* - * OK, now we know that there's no other users *and* we hold mutex, - * so no new references will appear - */ - __fsnotify_evict_group(group); - - mutex_unlock(&fsnotify_grp_mutex); - - fsnotify_destroy_group(group); + if (atomic_dec_and_test(&group->refcnt)) + fsnotify_destroy_group(group); } /* @@ -195,9 +95,6 @@ struct fsnotify_group *fsnotify_alloc_group(const struct fsnotify_ops *ops) init_waitqueue_head(&group->notification_waitq); group->max_events = UINT_MAX; - INIT_LIST_HEAD(&group->inode_group_list); - INIT_LIST_HEAD(&group->vfsmount_group_list); - spin_lock_init(&group->mark_lock); INIT_LIST_HEAD(&group->marks_list); diff --git a/fs/notify/mark.c b/fs/notify/mark.c index 236f29b..325185e 100644 --- a/fs/notify/mark.c +++ b/fs/notify/mark.c @@ -223,15 +223,6 @@ int fsnotify_add_mark(struct fsnotify_mark *mark, BUG_ON(!inode && !mnt); /* - * if this group isn't being testing for inode type events we need - * to start testing - */ - if (inode && unlikely(list_empty(&group->inode_group_list))) - fsnotify_add_inode_group(group); - else if (mnt && unlikely(list_empty(&group->vfsmount_group_list))) - fsnotify_add_vfsmount_group(group); - - /* * LOCKING ORDER!!!! * mark->lock * group->mark_lock -- cgit v1.1 From 84a5b68e8da1490906c11129756490a556ae2c19 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: rework ignored mark flushing currently ignored_mark clearing is done in a seperate list traversal before the actual list traversal to send events. There is no need for this. Do them at the same time. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 60 ++++++++++------------------------------------------ 1 file changed, 11 insertions(+), 49 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 1dd1fde..0bb4aeb 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -140,36 +140,6 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); -void __fsnotify_flush_ignored_mask(struct inode *inode, void *data, int data_is) -{ - struct fsnotify_mark *mark; - struct hlist_node *node; - int idx; - - idx = srcu_read_lock(&fsnotify_mark_srcu); - - if (!hlist_empty(&inode->i_fsnotify_marks)) { - hlist_for_each_entry_rcu(mark, node, &inode->i_fsnotify_marks, i.i_list) { - if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - } - } - - if (data_is == FSNOTIFY_EVENT_FILE) { - struct vfsmount *mnt; - - mnt = ((struct file *)data)->f_path.mnt; - if (mnt && !hlist_empty(&mnt->mnt_fsnotify_marks)) { - hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { - if (!(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - } - } - } - - srcu_read_unlock(&fsnotify_mark_srcu, idx); -} - static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, struct vfsmount *mnt, struct fsnotify_mark *mark, __u32 mask, void *data, int data_is, u32 cookie, @@ -193,14 +163,6 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, return group->ops->handle_event(group, mark, *event); } -static bool needed_by_vfsmount(__u32 test_mask, struct vfsmount *mnt) -{ - if (!mnt) - return false; - - return (test_mask & mnt->mnt_fsnotify_mask); -} - /* * This is the main call to fsnotify. The VFS calls into hook specific functions * in linux/fsnotify.h. Those functions then in turn call here. Here will call @@ -219,26 +181,21 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); - if (mask & FS_MODIFY) - __fsnotify_flush_ignored_mask(to_tell, data, data_is); - if (data_is == FSNOTIFY_EVENT_FILE) mnt = ((struct file *)data)->f_path.mnt; - /* if this inode's directed listeners don't care and nothing on the vfsmount - * listeners list cares, nothing to do */ - if (!(test_mask & to_tell->i_fsnotify_mask) && - !needed_by_vfsmount(test_mask, mnt)) - return 0; - idx = srcu_read_lock(&fsnotify_mark_srcu); - if (test_mask & to_tell->i_fsnotify_mask) { + if ((test_mask & to_tell->i_fsnotify_mask) || (mask & FS_MODIFY)) { hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", __func__, mark, mark->mask, mark->ignored_mask); + if ((mask & FS_MODIFY) && + !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; + if (test_mask & mark->mask & ~mark->ignored_mask) { group = mark->group; if (!group) @@ -252,12 +209,17 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, } } - if (mnt && (test_mask & mnt->mnt_fsnotify_mask)) { + if (mnt && ((test_mask & mnt->mnt_fsnotify_mask) || + (mask & FS_MODIFY))) { hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", __func__, mark, mark->mask, mark->ignored_mask); + if ((mask & FS_MODIFY) && + !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; + if (test_mask & mark->mask & ~mark->ignored_mask) { group = mark->group; if (!group) -- cgit v1.1 From 613a807fe7c793ceb7d6f059773527a5a6c84a96 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: walk the inode and vfsmount lists simultaneously We currently walk the list of marks on an inode followed by the list of marks on the vfsmount. These are in order (by the memory address of the group) so lets walk them both together. Eventually we can pass both the inode mark and the vfsmount mark to helpers simultaneously. Signed-off-by: Eric Paris --- fs/notify/fsnotify.c | 134 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 84 insertions(+), 50 deletions(-) (limited to 'fs') diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 0bb4aeb..cdaa51c 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -140,19 +140,31 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) } EXPORT_SYMBOL_GPL(__fsnotify_parent); -static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, struct fsnotify_mark *mark, - __u32 mask, void *data, int data_is, u32 cookie, +static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, + struct fsnotify_mark *mark, + __u32 mask, void *data, + int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { + struct fsnotify_group *group = mark->group; + __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); + pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, mnt, mark, mask, data, data_is, cookie, *event); + if ((mask & FS_MODIFY) && + !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + mark->ignored_mask = 0; + + if (!(test_mask & mark->mask & ~mark->ignored_mask)) + return 0; + if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, data, data_is) == false) return 0; + if (!*event) { *event = fsnotify_create_event(to_tell, mask, data, data_is, file_name, @@ -172,67 +184,89 @@ static int send_to_group(struct fsnotify_group *group, struct inode *to_tell, int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, const unsigned char *file_name, u32 cookie) { - struct fsnotify_mark *mark; - struct fsnotify_group *group; + struct hlist_node *inode_node, *vfsmount_node; + struct fsnotify_mark *inode_mark = NULL, *vfsmount_mark = NULL; + struct fsnotify_group *inode_group, *vfsmount_group; struct fsnotify_event *event = NULL; - struct hlist_node *node; - struct vfsmount *mnt = NULL; + struct vfsmount *mnt; int idx, ret = 0; + bool used_inode = false, used_vfsmount = false; /* global tests shouldn't care about events on child only the specific event */ __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); if (data_is == FSNOTIFY_EVENT_FILE) mnt = ((struct file *)data)->f_path.mnt; + else + mnt = NULL; + + /* + * if this is a modify event we may need to clear the ignored masks + * otherwise return if neither the inode nor the vfsmount care about + * this type of event. + */ + if (!(mask & FS_MODIFY) && + !(test_mask & to_tell->i_fsnotify_mask) && + !(mnt && test_mask & mnt->mnt_fsnotify_mask)) + return 0; idx = srcu_read_lock(&fsnotify_mark_srcu); - if ((test_mask & to_tell->i_fsnotify_mask) || (mask & FS_MODIFY)) { - hlist_for_each_entry_rcu(mark, node, &to_tell->i_fsnotify_marks, i.i_list) { - - pr_debug("%s: inode_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", - __func__, mark, mark->mask, mark->ignored_mask); - - if ((mask & FS_MODIFY) && - !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - - if (test_mask & mark->mask & ~mark->ignored_mask) { - group = mark->group; - if (!group) - continue; - ret = send_to_group(group, to_tell, NULL, mark, mask, - data, data_is, cookie, file_name, - &event); - if (ret) - goto out; - } - } + if ((mask & FS_MODIFY) || + (test_mask & to_tell->i_fsnotify_mask)) + inode_node = to_tell->i_fsnotify_marks.first; + else + inode_node = NULL; + + if (mnt) { + if ((mask & FS_MODIFY) || + (test_mask & mnt->mnt_fsnotify_mask)) + vfsmount_node = mnt->mnt_fsnotify_marks.first; + else + vfsmount_node = NULL; + } else { + mnt = NULL; + vfsmount_node = NULL; } - if (mnt && ((test_mask & mnt->mnt_fsnotify_mask) || - (mask & FS_MODIFY))) { - hlist_for_each_entry_rcu(mark, node, &mnt->mnt_fsnotify_marks, m.m_list) { - - pr_debug("%s: mnt_loop: mark=%p mark->mask=%x mark->ignored_mask=%x\n", - __func__, mark, mark->mask, mark->ignored_mask); - - if ((mask & FS_MODIFY) && - !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; - - if (test_mask & mark->mask & ~mark->ignored_mask) { - group = mark->group; - if (!group) - continue; - ret = send_to_group(group, to_tell, mnt, mark, mask, - data, data_is, cookie, file_name, - &event); - if (ret) - goto out; - } + while (inode_node || vfsmount_node) { + if (inode_node) { + inode_mark = hlist_entry(srcu_dereference(inode_node, &fsnotify_mark_srcu), + struct fsnotify_mark, i.i_list); + inode_group = inode_mark->group; + } else + inode_group = (void *)-1; + + if (vfsmount_node) { + vfsmount_mark = hlist_entry(srcu_dereference(vfsmount_node, &fsnotify_mark_srcu), + struct fsnotify_mark, m.m_list); + vfsmount_group = vfsmount_mark->group; + } else + vfsmount_group = (void *)-1; + + if (inode_group < vfsmount_group) { + /* handle inode */ + send_to_group(to_tell, NULL, inode_mark, mask, data, + data_is, cookie, file_name, &event); + used_inode = true; + } else if (vfsmount_group < inode_group) { + send_to_group(to_tell, mnt, vfsmount_mark, mask, data, + data_is, cookie, file_name, &event); + used_vfsmount = true; + } else { + send_to_group(to_tell, mnt, vfsmount_mark, mask, data, + data_is, cookie, file_name, &event); + used_vfsmount = true; + send_to_group(to_tell, NULL, inode_mark, mask, data, + data_is, cookie, file_name, &event); + used_inode = true; } + + if (used_inode) + inode_node = inode_node->next; + if (used_vfsmount) + vfsmount_node = vfsmount_node->next; } -out: + srcu_read_unlock(&fsnotify_mark_srcu, idx); /* * fsnotify_create_event() took a reference so the event can't be cleaned -- cgit v1.1 From ce8f76fb7320297ccbe7c950fd9a2d727dd6a5a0 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fsnotify: pass both the vfsmount mark and inode mark should_send_event() and handle_event() will both need to look up the inode event if they get a vfsmount event. Lets just pass both at the same time since we have them both after walking the lists in lockstep. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 18 ++++++---- fs/notify/fanotify/fanotify.c | 15 +++++--- fs/notify/fsnotify.c | 70 ++++++++++++++++++++++++------------ fs/notify/inotify/inotify_fsnotify.c | 12 ++++--- 4 files changed, 77 insertions(+), 38 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index e92b2c8..bda588b 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -83,7 +83,8 @@ static void dnotify_recalc_inode_mask(struct fsnotify_mark *fsn_mark) * events. */ static int dnotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct dnotify_mark *dn_mark; @@ -93,11 +94,13 @@ static int dnotify_handle_event(struct fsnotify_group *group, struct fown_struct *fown; __u32 test_mask = event->mask & ~FS_EVENT_ON_CHILD; + BUG_ON(vfsmount_mark); + to_tell = event->to_tell; - dn_mark = container_of(mark, struct dnotify_mark, fsn_mark); + dn_mark = container_of(inode_mark, struct dnotify_mark, fsn_mark); - spin_lock(&mark->lock); + spin_lock(&inode_mark->lock); prev = &dn_mark->dn; while ((dn = *prev) != NULL) { if ((dn->dn_mask & test_mask) == 0) { @@ -111,11 +114,11 @@ static int dnotify_handle_event(struct fsnotify_group *group, else { *prev = dn->dn_next; kmem_cache_free(dnotify_struct_cache, dn); - dnotify_recalc_inode_mask(mark); + dnotify_recalc_inode_mask(inode_mark); } } - spin_unlock(&mark->lock); + spin_unlock(&inode_mark->lock); return 0; } @@ -126,8 +129,9 @@ static int dnotify_handle_event(struct fsnotify_group *group, */ static bool dnotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, - struct fsnotify_mark *mark, __u32 mask, - void *data, int data_type) + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, + __u32 mask, void *data, int data_type) { /* not a dir, dnotify doesn't care */ if (!S_ISDIR(inode->i_mode)) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index fbd7f35..ef4fa4a 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -115,7 +115,8 @@ static int fanotify_get_response_from_access(struct fsnotify_group *group, #endif static int fanotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *fanotify_mark, struct fsnotify_event *event) { int ret = 0; @@ -196,8 +197,11 @@ static bool should_send_inode_event(struct fsnotify_group *group, return true; } -static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, struct fsnotify_mark *mark, +static bool fanotify_should_send_event(struct fsnotify_group *group, + struct inode *to_tell, + struct vfsmount *mnt, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", @@ -213,9 +217,10 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, struct inod return false; if (mnt) - return should_send_vfsmount_event(group, mnt, to_tell, mark, mask); + return should_send_vfsmount_event(group, mnt, to_tell, + vfsmount_mark, mask); else - return should_send_inode_event(group, to_tell, mark, mask); + return should_send_inode_event(group, to_tell, inode_mark, mask); } const struct fsnotify_ops fanotify_fsnotify_ops = { diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index cdaa51c..090b64c 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -141,28 +141,51 @@ void __fsnotify_parent(struct file *file, struct dentry *dentry, __u32 mask) EXPORT_SYMBOL_GPL(__fsnotify_parent); static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, - struct fsnotify_mark *mark, - __u32 mask, void *data, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, + __u32 mask, void *data, int data_is, u32 cookie, const unsigned char *file_name, struct fsnotify_event **event) { - struct fsnotify_group *group = mark->group; - __u32 test_mask = (mask & ~FS_EVENT_ON_CHILD); + struct fsnotify_group *group = inode_mark->group; + __u32 inode_test_mask = (mask & ~FS_EVENT_ON_CHILD); + __u32 vfsmount_test_mask = (mask & ~FS_EVENT_ON_CHILD); pr_debug("%s: group=%p to_tell=%p mnt=%p mark=%p mask=%x data=%p" " data_is=%d cookie=%d event=%p\n", __func__, group, to_tell, - mnt, mark, mask, data, data_is, cookie, *event); + mnt, inode_mark, mask, data, data_is, cookie, *event); + + /* clear ignored on inode modification */ + if (mask & FS_MODIFY) { + if (inode_mark && + !(inode_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + inode_mark->ignored_mask = 0; + if (vfsmount_mark && + !(vfsmount_mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) + vfsmount_mark->ignored_mask = 0; + } - if ((mask & FS_MODIFY) && - !(mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)) - mark->ignored_mask = 0; + /* does the inode mark tell us to do something? */ + if (inode_mark) { + inode_test_mask &= inode_mark->mask; + inode_test_mask &= ~inode_mark->ignored_mask; + } - if (!(test_mask & mark->mask & ~mark->ignored_mask)) + /* does the vfsmount_mark tell us to do something? */ + if (vfsmount_mark) { + vfsmount_test_mask &= vfsmount_mark->mask; + vfsmount_test_mask &= ~vfsmount_mark->ignored_mask; + if (inode_mark) + vfsmount_test_mask &= ~inode_mark->ignored_mask; + } + + if (!inode_test_mask && !vfsmount_test_mask) return 0; - if (group->ops->should_send_event(group, to_tell, mnt, mark, mask, - data, data_is) == false) + if (group->ops->should_send_event(group, to_tell, mnt, inode_mark, + vfsmount_mark, mask, data, + data_is) == false) return 0; if (!*event) { @@ -172,7 +195,7 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, if (!*event) return -ENOMEM; } - return group->ops->handle_event(group, mark, *event); + return group->ops->handle_event(group, inode_mark, vfsmount_mark, *event); } /* @@ -213,14 +236,16 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if ((mask & FS_MODIFY) || (test_mask & to_tell->i_fsnotify_mask)) - inode_node = to_tell->i_fsnotify_marks.first; + inode_node = srcu_dereference(to_tell->i_fsnotify_marks.first, + &fsnotify_mark_srcu); else inode_node = NULL; if (mnt) { if ((mask & FS_MODIFY) || (test_mask & mnt->mnt_fsnotify_mask)) - vfsmount_node = mnt->mnt_fsnotify_marks.first; + vfsmount_node = srcu_dereference(mnt->mnt_fsnotify_marks.first, + &fsnotify_mark_srcu); else vfsmount_node = NULL; } else { @@ -245,26 +270,27 @@ int fsnotify(struct inode *to_tell, __u32 mask, void *data, int data_is, if (inode_group < vfsmount_group) { /* handle inode */ - send_to_group(to_tell, NULL, inode_mark, mask, data, + send_to_group(to_tell, NULL, inode_mark, NULL, mask, data, data_is, cookie, file_name, &event); used_inode = true; } else if (vfsmount_group < inode_group) { - send_to_group(to_tell, mnt, vfsmount_mark, mask, data, + send_to_group(to_tell, mnt, NULL, vfsmount_mark, mask, data, data_is, cookie, file_name, &event); used_vfsmount = true; } else { - send_to_group(to_tell, mnt, vfsmount_mark, mask, data, - data_is, cookie, file_name, &event); + send_to_group(to_tell, mnt, inode_mark, vfsmount_mark, + mask, data, data_is, cookie, file_name, + &event); used_vfsmount = true; - send_to_group(to_tell, NULL, inode_mark, mask, data, - data_is, cookie, file_name, &event); used_inode = true; } if (used_inode) - inode_node = inode_node->next; + inode_node = srcu_dereference(inode_node->next, + &fsnotify_mark_srcu); if (used_vfsmount) - vfsmount_node = vfsmount_node->next; + vfsmount_node = srcu_dereference(vfsmount_node->next, + &fsnotify_mark_srcu); } srcu_read_unlock(&fsnotify_mark_srcu, idx); diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index 7cf518b..e53f497 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -90,7 +90,8 @@ static struct fsnotify_event *inotify_merge(struct list_head *list, } static int inotify_handle_event(struct fsnotify_group *group, - struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, struct fsnotify_event *event) { struct inotify_inode_mark *i_mark; @@ -100,12 +101,14 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_event *added_event; int wd, ret = 0; + BUG_ON(vfsmount_mark); + pr_debug("%s: group=%p event=%p to_tell=%p mask=%x\n", __func__, group, event, event->to_tell, event->mask); to_tell = event->to_tell; - i_mark = container_of(mark, struct inotify_inode_mark, + i_mark = container_of(inode_mark, struct inotify_inode_mark, fsn_mark); wd = i_mark->wd; @@ -127,8 +130,8 @@ static int inotify_handle_event(struct fsnotify_group *group, ret = PTR_ERR(added_event); } - if (mark->mask & IN_ONESHOT) - fsnotify_destroy_mark(mark); + if (inode_mark->mask & IN_ONESHOT) + fsnotify_destroy_mark(inode_mark); return ret; } @@ -140,6 +143,7 @@ static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, struct vfsmount *mnt, struct fsnotify_mark *mark, + struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { if ((mark->mask & FS_EXCL_UNLINK) && -- cgit v1.1 From 1968f5eed54ce47bde488fd9a450912e4a2d7138 Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 28 Jul 2010 10:18:39 -0400 Subject: fanotify: use both marks when possible fanotify currently, when given a vfsmount_mark will look up (if it exists) the corresponding inode mark. This patch drops that lookup and uses the mark provided. Signed-off-by: Eric Paris --- fs/notify/dnotify/dnotify.c | 2 +- fs/notify/fanotify/fanotify.c | 88 ++++++++++++++---------------------- fs/notify/fsnotify.c | 2 +- fs/notify/inotify/inotify_fsnotify.c | 4 +- 4 files changed, 38 insertions(+), 58 deletions(-) (limited to 'fs') diff --git a/fs/notify/dnotify/dnotify.c b/fs/notify/dnotify/dnotify.c index bda588b..3344bdd 100644 --- a/fs/notify/dnotify/dnotify.c +++ b/fs/notify/dnotify/dnotify.c @@ -128,7 +128,7 @@ static int dnotify_handle_event(struct fsnotify_group *group, * userspace notification for that pair. */ static bool dnotify_should_send_event(struct fsnotify_group *group, - struct inode *inode, struct vfsmount *mnt, + struct inode *inode, struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) diff --git a/fs/notify/fanotify/fanotify.c b/fs/notify/fanotify/fanotify.c index ef4fa4a..eb8f73c 100644 --- a/fs/notify/fanotify/fanotify.c +++ b/fs/notify/fanotify/fanotify.c @@ -153,59 +153,20 @@ static int fanotify_handle_event(struct fsnotify_group *group, return ret; } -static bool should_send_vfsmount_event(struct fsnotify_group *group, - struct vfsmount *mnt, - struct inode *inode, - struct fsnotify_mark *mnt_mark, - __u32 mask) -{ - struct fsnotify_mark *inode_mark; - - pr_debug("%s: group=%p vfsmount=%p mark=%p mask=%x\n", - __func__, group, mnt, mnt_mark, mask); - - mask &= mnt_mark->mask; - mask &= ~mnt_mark->ignored_mask; - - if (mask) { - inode_mark = fsnotify_find_inode_mark(group, inode); - if (inode_mark) { - mask &= ~inode_mark->ignored_mask; - fsnotify_put_mark(inode_mark); - } - } - - return mask; -} - -static bool should_send_inode_event(struct fsnotify_group *group, - struct inode *inode, - struct fsnotify_mark *mark, - __u32 mask) -{ - pr_debug("%s: group=%p inode=%p mark=%p mask=%x\n", - __func__, group, inode, mark, mask); - - /* - * if the event is for a child and this inode doesn't care about - * events on the child, don't send it! - */ - if ((mask & FS_EVENT_ON_CHILD) && - !(mark->mask & FS_EVENT_ON_CHILD)) - return false; - else - return true; -} - static bool fanotify_should_send_event(struct fsnotify_group *group, struct inode *to_tell, - struct vfsmount *mnt, struct fsnotify_mark *inode_mark, - struct fsnotify_mark *vfsmount_mark, - __u32 mask, void *data, int data_type) + struct fsnotify_mark *vfsmnt_mark, + __u32 event_mask, void *data, int data_type) { - pr_debug("%s: group=%p to_tell=%p mnt=%p mask=%x data=%p data_type=%d\n", - __func__, group, to_tell, mnt, mask, data, data_type); + __u32 marks_mask, marks_ignored_mask; + + pr_debug("%s: group=%p to_tell=%p inode_mark=%p vfsmnt_mark=%p " + "mask=%x data=%p data_type=%d\n", __func__, group, to_tell, + inode_mark, vfsmnt_mark, event_mask, data, data_type); + + pr_debug("%s: group=%p vfsmount_mark=%p inode_mark=%p mask=%x\n", + __func__, group, vfsmnt_mark, inode_mark, event_mask); /* sorry, fanotify only gives a damn about files and dirs */ if (!S_ISREG(to_tell->i_mode) && @@ -216,11 +177,30 @@ static bool fanotify_should_send_event(struct fsnotify_group *group, if (data_type != FSNOTIFY_EVENT_FILE) return false; - if (mnt) - return should_send_vfsmount_event(group, mnt, to_tell, - vfsmount_mark, mask); - else - return should_send_inode_event(group, to_tell, inode_mark, mask); + if (inode_mark && vfsmnt_mark) { + marks_mask = (vfsmnt_mark->mask | inode_mark->mask); + marks_ignored_mask = (vfsmnt_mark->ignored_mask | inode_mark->ignored_mask); + } else if (inode_mark) { + /* + * if the event is for a child and this inode doesn't care about + * events on the child, don't send it! + */ + if ((event_mask & FS_EVENT_ON_CHILD) && + !(inode_mark->mask & FS_EVENT_ON_CHILD)) + return false; + marks_mask = inode_mark->mask; + marks_ignored_mask = inode_mark->ignored_mask; + } else if (vfsmnt_mark) { + marks_mask = vfsmnt_mark->mask; + marks_ignored_mask = vfsmnt_mark->ignored_mask; + } else { + BUG(); + } + + if (event_mask & marks_mask & ~marks_ignored_mask) + return true; + + return false; } const struct fsnotify_ops fanotify_fsnotify_ops = { diff --git a/fs/notify/fsnotify.c b/fs/notify/fsnotify.c index 090b64c..4d2a82c 100644 --- a/fs/notify/fsnotify.c +++ b/fs/notify/fsnotify.c @@ -183,7 +183,7 @@ static int send_to_group(struct inode *to_tell, struct vfsmount *mnt, if (!inode_test_mask && !vfsmount_test_mask) return 0; - if (group->ops->should_send_event(group, to_tell, mnt, inode_mark, + if (group->ops->should_send_event(group, to_tell, inode_mark, vfsmount_mark, mask, data, data_is) == false) return 0; diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c index e53f497..5e73eeb 100644 --- a/fs/notify/inotify/inotify_fsnotify.c +++ b/fs/notify/inotify/inotify_fsnotify.c @@ -142,11 +142,11 @@ static void inotify_freeing_mark(struct fsnotify_mark *fsn_mark, struct fsnotify } static bool inotify_should_send_event(struct fsnotify_group *group, struct inode *inode, - struct vfsmount *mnt, struct fsnotify_mark *mark, + struct fsnotify_mark *inode_mark, struct fsnotify_mark *vfsmount_mark, __u32 mask, void *data, int data_type) { - if ((mark->mask & FS_EXCL_UNLINK) && + if ((inode_mark->mask & FS_EXCL_UNLINK) && (data_type == FSNOTIFY_EVENT_FILE)) { struct file *file = data; -- cgit v1.1