]> git.kernelconcepts.de Git - karo-tx-linux.git/commitdiff
Merge branch 'for-next' of git://git.infradead.org/users/eparis/notify
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 Dec 2012 04:11:52 +0000 (20:11 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 21 Dec 2012 04:11:52 +0000 (20:11 -0800)
Pull filesystem notification updates from Eric Paris:
 "This pull mostly is about locking changes in the fsnotify system.  By
  switching the group lock from a spin_lock() to a mutex() we can now
  hold the lock across things like iput().  This fixes a problem
  involving unmounting a fs and having inodes be busy, first pointed out
  by FAT, but reproducible with tmpfs.

  This also restores signal driven I/O for inotify, which has been
  broken since about 2.6.32."

Ugh.  I *hate* the timing of this.  It was rebased after the merge
window opened, and then left to sit with the pull request coming the day
before the merge window closes.  That's just crap.  But apparently the
patches themselves have been around for over a year, just gathering
dust, so now it's suddenly critical.

Fixed up semantic conflict in fs/notify/fdinfo.c as per Stephen
Rothwell's fixes from -next.

* 'for-next' of git://git.infradead.org/users/eparis/notify:
  inotify: automatically restart syscalls
  inotify: dont skip removal of watch descriptor if creation of ignored event failed
  fanotify: dont merge permission events
  fsnotify: make fasync generic for both inotify and fanotify
  fsnotify: change locking order
  fsnotify: dont put marks on temporary list when clearing marks by group
  fsnotify: introduce locked versions of fsnotify_add_mark() and fsnotify_remove_mark()
  fsnotify: pass group to fsnotify_destroy_mark()
  fsnotify: use a mutex instead of a spinlock to protect a groups mark list
  fanotify: add an extra flag to mark_remove_from_mask that indicates wheather a mark should be destroyed
  fsnotify: take groups mark_lock before mark lock
  fsnotify: use reference counting for groups
  fsnotify: introduce fsnotify_get_group()
  inotify, fanotify: replace fsnotify_put_group() with fsnotify_destroy_group()

1  2 
fs/notify/fanotify/fanotify.c
fs/notify/fanotify/fanotify_user.c
fs/notify/fdinfo.c
fs/notify/inode_mark.c
fs/notify/inotify/inotify_user.c
fs/notify/notification.c
kernel/audit_watch.c

index a50636025364214176fe7cec0cb80757ddc27171,aeb5b5abbd4fcb9036b1b9e7391541c20d8b8d3c..0c2f9122b262da54392c8ed90a5a9d94ea22719c
@@@ -18,10 -18,15 +18,16 @@@ static bool should_merge(struct fsnotif
            old->tgid == new->tgid) {
                switch (old->data_type) {
                case (FSNOTIFY_EVENT_PATH):
+ #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
+                       /* dont merge two permission events */
+                       if ((old->mask & FAN_ALL_PERM_EVENTS) &&
+                           (new->mask & FAN_ALL_PERM_EVENTS))
+                               return false;
+ #endif
                        if ((old->path.mnt == new->path.mnt) &&
                            (old->path.dentry == new->path.dentry))
                                return true;
 +                      break;
                case (FSNOTIFY_EVENT_NONE):
                        return true;
                default:
index a5cd9bba022f4619021ddc0ef659e59b4a88e656,f0e7a57bc8990419e75b48988e523e1ed6efbea4..9ff4a5ee6e208fcccac9f3dc6393be18ffac3e1b
@@@ -17,7 -17,6 +17,7 @@@
  #include <asm/ioctls.h>
  
  #include "../../mount.h"
 +#include "../fdinfo.h"
  
  #define FANOTIFY_DEFAULT_MAX_EVENTS   16384
  #define FANOTIFY_DEFAULT_MAX_MARKS    8192
@@@ -59,9 -58,7 +59,9 @@@ static struct fsnotify_event *get_one_e
        return fsnotify_remove_notify_event(group);
  }
  
 -static int create_fd(struct fsnotify_group *group, struct fsnotify_event *event)
 +static int create_fd(struct fsnotify_group *group,
 +                      struct fsnotify_event *event,
 +                      struct file **file)
  {
        int client_fd;
        struct file *new_file;
                put_unused_fd(client_fd);
                client_fd = PTR_ERR(new_file);
        } else {
 -              fd_install(client_fd, new_file);
 +              *file = new_file;
        }
  
        return client_fd;
  
  static int fill_event_metadata(struct fsnotify_group *group,
                                   struct fanotify_event_metadata *metadata,
 -                                 struct fsnotify_event *event)
 +                                 struct fsnotify_event *event,
 +                                 struct file **file)
  {
        int ret = 0;
  
        pr_debug("%s: group=%p metadata=%p event=%p\n", __func__,
                 group, metadata, event);
  
 +      *file = NULL;
        metadata->event_len = FAN_EVENT_METADATA_LEN;
        metadata->metadata_len = FAN_EVENT_METADATA_LEN;
        metadata->vers = FANOTIFY_METADATA_VERSION;
        if (unlikely(event->mask & FAN_Q_OVERFLOW))
                metadata->fd = FAN_NOFD;
        else {
 -              metadata->fd = create_fd(group, event);
 +              metadata->fd = create_fd(group, event, file);
                if (metadata->fd < 0)
                        ret = metadata->fd;
        }
@@@ -225,6 -220,25 +225,6 @@@ static int prepare_for_access_response(
        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,
        return 0;
  }
  
 -static void remove_access_response(struct fsnotify_group *group,
 -                                 struct fsnotify_event *event,
 -                                 __s32 fd)
 -{
 -      return;
 -}
  #endif
  
  static ssize_t copy_event_to_user(struct fsnotify_group *group,
                                  char __user *buf)
  {
        struct fanotify_event_metadata fanotify_event_metadata;
 +      struct file *f;
        int fd, ret;
  
        pr_debug("%s: group=%p event=%p\n", __func__, group, event);
  
 -      ret = fill_event_metadata(group, &fanotify_event_metadata, event);
 +      ret = fill_event_metadata(group, &fanotify_event_metadata, event, &f);
        if (ret < 0)
                goto out;
  
        fd = fanotify_event_metadata.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,
                         fanotify_event_metadata.event_len))
 -              goto out_kill_access_response;
 +              goto out_close_fd;
  
 +      ret = prepare_for_access_response(group, event, fd);
 +      if (ret)
 +              goto out_close_fd;
 +
 +      if (fd != FAN_NOFD)
 +              fd_install(fd, f);
        return fanotify_event_metadata.event_len;
  
 -out_kill_access_response:
 -      remove_access_response(group, event, fd);
  out_close_fd:
 -      if (fd != FAN_NOFD)
 -              sys_close(fd);
 +      if (fd != FAN_NOFD) {
 +              put_unused_fd(fd);
 +              fput(f);
 +      }
  out:
  #ifdef CONFIG_FANOTIFY_ACCESS_PERMISSIONS
        if (event->mask & FAN_ALL_PERM_EVENTS) {
@@@ -397,8 -414,12 +397,12 @@@ static int fanotify_release(struct inod
  
        wake_up(&group->fanotify_data.access_waitq);
  #endif
+       if (file->f_flags & FASYNC)
+               fsnotify_fasync(-1, file, 0);
        /* matches the fanotify_init->fsnotify_alloc_group */
-       fsnotify_put_group(group);
+       fsnotify_destroy_group(group);
  
        return 0;
  }
@@@ -429,7 -450,6 +433,7 @@@ static long fanotify_ioctl(struct file 
  }
  
  static const struct file_operations fanotify_fops = {
 +      .show_fdinfo    = fanotify_show_fdinfo,
        .poll           = fanotify_poll,
        .read           = fanotify_read,
        .write          = fanotify_write,
@@@ -454,22 -474,24 +458,22 @@@ static int fanotify_find_path(int dfd, 
                 dfd, filename, flags);
  
        if (filename == NULL) {
 -              struct file *file;
 -              int fput_needed;
 +              struct fd f = fdget(dfd);
  
                ret = -EBADF;
 -              file = fget_light(dfd, &fput_needed);
 -              if (!file)
 +              if (!f.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);
 +                  !(S_ISDIR(f.file->f_path.dentry->d_inode->i_mode))) {
 +                      fdput(f);
                        goto out;
                }
  
 -              *path = file->f_path;
 +              *path = f.file->f_path;
                path_get(path);
 -              fput_light(file, fput_needed);
 +              fdput(f);
        } else {
                unsigned int lookup_flags = 0;
  
@@@ -493,7 -515,8 +497,8 @@@ out
  
  static __u32 fanotify_mark_remove_from_mask(struct fsnotify_mark *fsn_mark,
                                            __u32 mask,
-                                           unsigned int flags)
+                                           unsigned int flags,
+                                           int *destroy)
  {
        __u32 oldmask;
  
        }
        spin_unlock(&fsn_mark->lock);
  
-       if (!(oldmask & ~mask))
-               fsnotify_destroy_mark(fsn_mark);
+       *destroy = !(oldmask & ~mask);
  
        return mask & oldmask;
  }
@@@ -519,12 -541,17 +523,17 @@@ static int fanotify_remove_vfsmount_mar
  {
        struct fsnotify_mark *fsn_mark = NULL;
        __u32 removed;
+       int destroy_mark;
  
        fsn_mark = fsnotify_find_vfsmount_mark(group, mnt);
        if (!fsn_mark)
                return -ENOENT;
  
-       removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags);
+       removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
+                                                &destroy_mark);
+       if (destroy_mark)
+               fsnotify_destroy_mark(fsn_mark, group);
        fsnotify_put_mark(fsn_mark);
        if (removed & real_mount(mnt)->mnt_fsnotify_mask)
                fsnotify_recalc_vfsmount_mask(mnt);
@@@ -538,12 -565,16 +547,16 @@@ static int fanotify_remove_inode_mark(s
  {
        struct fsnotify_mark *fsn_mark = NULL;
        __u32 removed;
+       int destroy_mark;
  
        fsn_mark = fsnotify_find_inode_mark(group, inode);
        if (!fsn_mark)
                return -ENOENT;
  
-       removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags);
+       removed = fanotify_mark_remove_from_mask(fsn_mark, mask, flags,
+                                                &destroy_mark);
+       if (destroy_mark)
+               fsnotify_destroy_mark(fsn_mark, group);
        /* matches the fsnotify_find_inode_mark() */
        fsnotify_put_mark(fsn_mark);
        if (removed & inode->i_fsnotify_mask)
@@@ -710,13 -741,13 +723,13 @@@ SYSCALL_DEFINE2(fanotify_init, unsigne
                break;
        default:
                fd = -EINVAL;
-               goto out_put_group;
+               goto out_destroy_group;
        }
  
        if (flags & FAN_UNLIMITED_QUEUE) {
                fd = -EPERM;
                if (!capable(CAP_SYS_ADMIN))
-                       goto out_put_group;
+                       goto out_destroy_group;
                group->max_events = UINT_MAX;
        } else {
                group->max_events = FANOTIFY_DEFAULT_MAX_EVENTS;
        if (flags & FAN_UNLIMITED_MARKS) {
                fd = -EPERM;
                if (!capable(CAP_SYS_ADMIN))
-                       goto out_put_group;
+                       goto out_destroy_group;
                group->fanotify_data.max_marks = UINT_MAX;
        } else {
                group->fanotify_data.max_marks = FANOTIFY_DEFAULT_MAX_MARKS;
  
        fd = anon_inode_getfd("[fanotify]", &fanotify_fops, group, f_flags);
        if (fd < 0)
-               goto out_put_group;
+               goto out_destroy_group;
  
        return fd;
  
- out_put_group:
-       fsnotify_put_group(group);
+ out_destroy_group:
+       fsnotify_destroy_group(group);
        return fd;
  }
  
@@@ -749,9 -780,9 +762,9 @@@ SYSCALL_DEFINE(fanotify_mark)(int fanot
        struct inode *inode = NULL;
        struct vfsmount *mnt = NULL;
        struct fsnotify_group *group;
 -      struct file *filp;
 +      struct fd f;
        struct path path;
 -      int ret, fput_needed;
 +      int ret;
  
        pr_debug("%s: fanotify_fd=%d flags=%x dfd=%d pathname=%p mask=%llx\n",
                 __func__, fanotify_fd, flags, dfd, pathname, mask);
  #endif
                return -EINVAL;
  
 -      filp = fget_light(fanotify_fd, &fput_needed);
 -      if (unlikely(!filp))
 +      f = fdget(fanotify_fd);
 +      if (unlikely(!f.file))
                return -EBADF;
  
        /* verify that this is indeed an fanotify instance */
        ret = -EINVAL;
 -      if (unlikely(filp->f_op != &fanotify_fops))
 +      if (unlikely(f.file->f_op != &fanotify_fops))
                goto fput_and_out;
 -      group = filp->private_data;
 +      group = f.file->private_data;
  
        /*
         * group->priority == FS_PRIO_0 == FAN_CLASS_NOTIF.  These are not
  
        path_put(&path);
  fput_and_out:
 -      fput_light(filp, fput_needed);
 +      fdput(f);
        return ret;
  }
  
diff --combined fs/notify/fdinfo.c
index 514c4b81483d618adda933b5e63b93bb9e529324,0000000000000000000000000000000000000000..238a5930cb3c7d16e1c76952e66c6bf24f5299ae
mode 100644,000000..100644
--- /dev/null
@@@ -1,179 -1,0 +1,179 @@@
-       spin_lock(&group->mark_lock);
 +#include <linux/file.h>
 +#include <linux/fs.h>
 +#include <linux/fsnotify_backend.h>
 +#include <linux/idr.h>
 +#include <linux/init.h>
 +#include <linux/inotify.h>
 +#include <linux/fanotify.h>
 +#include <linux/kernel.h>
 +#include <linux/namei.h>
 +#include <linux/sched.h>
 +#include <linux/types.h>
 +#include <linux/seq_file.h>
 +#include <linux/proc_fs.h>
 +#include <linux/exportfs.h>
 +
 +#include "inotify/inotify.h"
 +#include "../fs/mount.h"
 +
 +#if defined(CONFIG_PROC_FS)
 +
 +#if defined(CONFIG_INOTIFY_USER) || defined(CONFIG_FANOTIFY)
 +
 +static int show_fdinfo(struct seq_file *m, struct file *f,
 +                     int (*show)(struct seq_file *m, struct fsnotify_mark *mark))
 +{
 +      struct fsnotify_group *group = f->private_data;
 +      struct fsnotify_mark *mark;
 +      int ret = 0;
 +
-       spin_unlock(&group->mark_lock);
++      mutex_lock(&group->mark_mutex);
 +      list_for_each_entry(mark, &group->marks_list, g_list) {
 +              ret = show(m, mark);
 +              if (ret)
 +                      break;
 +      }
++      mutex_unlock(&group->mark_mutex);
 +      return ret;
 +}
 +
 +#if defined(CONFIG_EXPORTFS)
 +static int show_mark_fhandle(struct seq_file *m, struct inode *inode)
 +{
 +      struct {
 +              struct file_handle handle;
 +              u8 pad[64];
 +      } f;
 +      int size, ret, i;
 +
 +      f.handle.handle_bytes = sizeof(f.pad);
 +      size = f.handle.handle_bytes >> 2;
 +
 +      ret = exportfs_encode_inode_fh(inode, (struct fid *)f.handle.f_handle, &size, 0);
 +      if ((ret == 255) || (ret == -ENOSPC)) {
 +              WARN_ONCE(1, "Can't encode file handler for inotify: %d\n", ret);
 +              return 0;
 +      }
 +
 +      f.handle.handle_type = ret;
 +      f.handle.handle_bytes = size * sizeof(u32);
 +
 +      ret = seq_printf(m, "fhandle-bytes:%x fhandle-type:%x f_handle:",
 +                       f.handle.handle_bytes, f.handle.handle_type);
 +
 +      for (i = 0; i < f.handle.handle_bytes; i++)
 +              ret |= seq_printf(m, "%02x", (int)f.handle.f_handle[i]);
 +
 +      return ret;
 +}
 +#else
 +static int show_mark_fhandle(struct seq_file *m, struct inode *inode)
 +{
 +      return 0;
 +}
 +#endif
 +
 +#ifdef CONFIG_INOTIFY_USER
 +
 +static int inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
 +{
 +      struct inotify_inode_mark *inode_mark;
 +      struct inode *inode;
 +      int ret = 0;
 +
 +      if (!(mark->flags & (FSNOTIFY_MARK_FLAG_ALIVE | FSNOTIFY_MARK_FLAG_INODE)))
 +              return 0;
 +
 +      inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
 +      inode = igrab(mark->i.inode);
 +      if (inode) {
 +              ret = seq_printf(m, "inotify wd:%x ino:%lx sdev:%x "
 +                               "mask:%x ignored_mask:%x ",
 +                               inode_mark->wd, inode->i_ino,
 +                               inode->i_sb->s_dev,
 +                               mark->mask, mark->ignored_mask);
 +              ret |= show_mark_fhandle(m, inode);
 +              ret |= seq_putc(m, '\n');
 +              iput(inode);
 +      }
 +
 +      return ret;
 +}
 +
 +int inotify_show_fdinfo(struct seq_file *m, struct file *f)
 +{
 +      return show_fdinfo(m, f, inotify_fdinfo);
 +}
 +
 +#endif /* CONFIG_INOTIFY_USER */
 +
 +#ifdef CONFIG_FANOTIFY
 +
 +static int fanotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
 +{
 +      unsigned int mflags = 0;
 +      struct inode *inode;
 +      int ret = 0;
 +
 +      if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE))
 +              return 0;
 +
 +      if (mark->flags & FSNOTIFY_MARK_FLAG_IGNORED_SURV_MODIFY)
 +              mflags |= FAN_MARK_IGNORED_SURV_MODIFY;
 +
 +      if (mark->flags & FSNOTIFY_MARK_FLAG_INODE) {
 +              inode = igrab(mark->i.inode);
 +              if (!inode)
 +                      goto out;
 +              ret = seq_printf(m, "fanotify ino:%lx sdev:%x "
 +                               "mflags:%x mask:%x ignored_mask:%x ",
 +                               inode->i_ino, inode->i_sb->s_dev,
 +                               mflags, mark->mask, mark->ignored_mask);
 +              ret |= show_mark_fhandle(m, inode);
 +              ret |= seq_putc(m, '\n');
 +              iput(inode);
 +      } else if (mark->flags & FSNOTIFY_MARK_FLAG_VFSMOUNT) {
 +              struct mount *mnt = real_mount(mark->m.mnt);
 +
 +              ret = seq_printf(m, "fanotify mnt_id:%x mflags:%x mask:%x "
 +                               "ignored_mask:%x\n", mnt->mnt_id, mflags,
 +                               mark->mask, mark->ignored_mask);
 +      }
 +out:
 +      return ret;
 +}
 +
 +int fanotify_show_fdinfo(struct seq_file *m, struct file *f)
 +{
 +      struct fsnotify_group *group = f->private_data;
 +      unsigned int flags = 0;
 +
 +      switch (group->priority) {
 +      case FS_PRIO_0:
 +              flags |= FAN_CLASS_NOTIF;
 +              break;
 +      case FS_PRIO_1:
 +              flags |= FAN_CLASS_CONTENT;
 +              break;
 +      case FS_PRIO_2:
 +              flags |= FAN_CLASS_PRE_CONTENT;
 +              break;
 +      }
 +
 +      if (group->max_events == UINT_MAX)
 +              flags |= FAN_UNLIMITED_QUEUE;
 +
 +      if (group->fanotify_data.max_marks == UINT_MAX)
 +              flags |= FAN_UNLIMITED_MARKS;
 +
 +      seq_printf(m, "fanotify flags:%x event-flags:%x\n",
 +                 flags, group->fanotify_data.f_flags);
 +
 +      return show_fdinfo(m, f, fanotify_fdinfo);
 +}
 +
 +#endif /* CONFIG_FANOTIFY */
 +
 +#endif /* CONFIG_INOTIFY_USER || CONFIG_FANOTIFY */
 +
 +#endif /* CONFIG_PROC_FS */
diff --combined fs/notify/inode_mark.c
index f3035691f528db31f41eb8b6db034525b6619aaa,21230209c9579c2ed88a9f9cdfcf30a7dd54d6bd..f31e90fc050d1edb282be6194f91812f66eeb3fa
@@@ -63,8 -63,8 +63,8 @@@ void fsnotify_destroy_inode_mark(struc
  {
        struct inode *inode = mark->i.inode;
  
+       BUG_ON(!mutex_is_locked(&mark->group->mark_mutex));
        assert_spin_locked(&mark->lock);
-       assert_spin_locked(&mark->group->mark_lock);
  
        spin_lock(&inode->i_lock);
  
@@@ -99,8 -99,16 +99,16 @@@ void fsnotify_clear_marks_by_inode(stru
        spin_unlock(&inode->i_lock);
  
        list_for_each_entry_safe(mark, lmark, &free_list, i.free_i_list) {
-               fsnotify_destroy_mark(mark);
+               struct fsnotify_group *group;
+               spin_lock(&mark->lock);
+               fsnotify_get_group(mark->group);
+               group = mark->group;
+               spin_unlock(&mark->lock);
+               fsnotify_destroy_mark(mark, group);
                fsnotify_put_mark(mark);
+               fsnotify_put_group(group);
        }
  }
  
@@@ -116,9 -124,8 +124,9 @@@ void fsnotify_clear_inode_marks_by_grou
   * 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)
 +static struct fsnotify_mark *fsnotify_find_inode_mark_locked(
 +              struct fsnotify_group *group,
 +              struct inode *inode)
  {
        struct fsnotify_mark *mark;
        struct hlist_node *pos;
@@@ -192,8 -199,8 +200,8 @@@ int fsnotify_add_inode_mark(struct fsno
  
        mark->flags |= FSNOTIFY_MARK_FLAG_INODE;
  
+       BUG_ON(!mutex_is_locked(&group->mark_mutex));
        assert_spin_locked(&mark->lock);
-       assert_spin_locked(&group->mark_lock);
  
        spin_lock(&inode->i_lock);
  
index 36cb013c7c13e7972405c2f7df81120770c3fdd0,463e828f1f312e0a73e5fb86123a979147f74d5c..228a2c2ad8d7e4abb953dd9f094fb762d97d6668
@@@ -40,7 -40,6 +40,7 @@@
  #include <linux/wait.h>
  
  #include "inotify.h"
 +#include "../fdinfo.h"
  
  #include <asm/ioctls.h>
  
@@@ -265,7 -264,7 +265,7 @@@ static ssize_t inotify_read(struct fil
                ret = -EAGAIN;
                if (file->f_flags & O_NONBLOCK)
                        break;
-               ret = -EINTR;
+               ret = -ERESTARTSYS;
                if (signal_pending(current))
                        break;
  
        return ret;
  }
  
- static int inotify_fasync(int fd, struct file *file, int on)
- {
-       struct fsnotify_group *group = file->private_data;
-       return fasync_helper(fd, file, on, &group->inotify_data.fa) >= 0 ? 0 : -EIO;
- }
  static int inotify_release(struct inode *ignored, struct file *file)
  {
        struct fsnotify_group *group = file->private_data;
  
        pr_debug("%s: group=%p\n", __func__, group);
  
-       fsnotify_clear_marks_by_group(group);
+       if (file->f_flags & FASYNC)
+               fsnotify_fasync(-1, file, 0);
  
        /* free this group, matching get was inotify_init->fsnotify_obtain_group */
-       fsnotify_put_group(group);
+       fsnotify_destroy_group(group);
  
        return 0;
  }
@@@ -336,10 -329,9 +330,10 @@@ static long inotify_ioctl(struct file *
  }
  
  static const struct file_operations inotify_fops = {
 +      .show_fdinfo    = inotify_show_fdinfo,
        .poll           = inotify_poll,
        .read           = inotify_read,
-       .fasync         = inotify_fasync,
+       .fasync         = fsnotify_fasync,
        .release        = inotify_release,
        .unlocked_ioctl = inotify_ioctl,
        .compat_ioctl   = inotify_ioctl,
@@@ -521,13 -513,13 +515,13 @@@ void inotify_ignored_and_remove_idr(str
        struct fsnotify_event_private_data *fsn_event_priv;
        int ret;
  
+       i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
        ignored_event = fsnotify_create_event(NULL, FS_IN_IGNORED, NULL,
                                              FSNOTIFY_EVENT_NONE, NULL, 0,
                                              GFP_NOFS);
        if (!ignored_event)
-               return;
-       i_mark = container_of(fsn_mark, struct inotify_inode_mark, fsn_mark);
+               goto skip_send_ignore;
  
        event_priv = kmem_cache_alloc(event_priv_cachep, GFP_NOFS);
        if (unlikely(!event_priv))
  
        fsn_event_priv = &event_priv->fsnotify_event_priv_data;
  
+       fsnotify_get_group(group);
        fsn_event_priv->group = group;
        event_priv->wd = i_mark->wd;
  
        }
  
  skip_send_ignore:
        /* matches the reference taken when the event was created */
-       fsnotify_put_event(ignored_event);
+       if (ignored_event)
+               fsnotify_put_event(ignored_event);
  
        /* remove this mark from the idr */
        inotify_remove_from_idr(group, i_mark);
@@@ -709,12 -702,11 +704,11 @@@ static struct fsnotify_group *inotify_n
        spin_lock_init(&group->inotify_data.idr_lock);
        idr_init(&group->inotify_data.idr);
        group->inotify_data.last_wd = 0;
-       group->inotify_data.fa = NULL;
        group->inotify_data.user = get_current_user();
  
        if (atomic_inc_return(&group->inotify_data.user->inotify_devs) >
            inotify_max_user_instances) {
-               fsnotify_put_group(group);
+               fsnotify_destroy_group(group);
                return ERR_PTR(-EMFILE);
        }
  
@@@ -743,7 -735,7 +737,7 @@@ SYSCALL_DEFINE1(inotify_init1, int, fla
        ret = anon_inode_getfd("inotify", &inotify_fops, group,
                                  O_RDONLY | flags);
        if (ret < 0)
-               fsnotify_put_group(group);
+               fsnotify_destroy_group(group);
  
        return ret;
  }
@@@ -759,16 -751,16 +753,16 @@@ SYSCALL_DEFINE3(inotify_add_watch, int
        struct fsnotify_group *group;
        struct inode *inode;
        struct path path;
 -      struct file *filp;
 -      int ret, fput_needed;
 +      struct fd f;
 +      int ret;
        unsigned flags = 0;
  
 -      filp = fget_light(fd, &fput_needed);
 -      if (unlikely(!filp))
 +      f = fdget(fd);
 +      if (unlikely(!f.file))
                return -EBADF;
  
        /* verify that this is indeed an inotify instance */
 -      if (unlikely(filp->f_op != &inotify_fops)) {
 +      if (unlikely(f.file->f_op != &inotify_fops)) {
                ret = -EINVAL;
                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;
 +      group = f.file->private_data;
  
        /* create/update an inode mark */
        ret = inotify_update_watch(group, inode, mask);
        path_put(&path);
  fput_and_out:
 -      fput_light(filp, fput_needed);
 +      fdput(f);
        return ret;
  }
  
@@@ -798,19 -790,19 +792,19 @@@ SYSCALL_DEFINE2(inotify_rm_watch, int, 
  {
        struct fsnotify_group *group;
        struct inotify_inode_mark *i_mark;
 -      struct file *filp;
 -      int ret = 0, fput_needed;
 +      struct fd f;
 +      int ret = 0;
  
 -      filp = fget_light(fd, &fput_needed);
 -      if (unlikely(!filp))
 +      f = fdget(fd);
 +      if (unlikely(!f.file))
                return -EBADF;
  
        /* verify that this is indeed an inotify instance */
        ret = -EINVAL;
 -      if (unlikely(filp->f_op != &inotify_fops))
 +      if (unlikely(f.file->f_op != &inotify_fops))
                goto out;
  
 -      group = filp->private_data;
 +      group = f.file->private_data;
  
        ret = -EINVAL;
        i_mark = inotify_idr_find(group, wd);
  
        ret = 0;
  
-       fsnotify_destroy_mark(&i_mark->fsn_mark);
+       fsnotify_destroy_mark(&i_mark->fsn_mark, group);
  
        /* match ref taken by inotify_idr_find */
        fsnotify_put_mark(&i_mark->fsn_mark);
  
  out:
 -      fput_light(filp, fput_needed);
 +      fdput(f);
        return ret;
  }
  
diff --combined fs/notify/notification.c
index 48cb994e4922cf0d32fb3f61ad1eb05291231a7c,b3963d8c998846207eb8a0c48590c14d566630f4..7b51b05f160c36846e47a8f1b7cb417bed9ed914
@@@ -18,7 -18,7 +18,7 @@@
  
  /*
   * Basic idea behind the notification queue: An fsnotify group (like inotify)
 - * sends the userspace notification about events asyncronously some time after
 + * sends the userspace notification about events asynchronously some time after
   * the event happened.  When inotify gets an event it will need to add that
   * event to the group notify queue.  Since a single event might need to be on
   * multiple group's notification queues we can't add the event directly to each
@@@ -225,6 -225,7 +225,7 @@@ alloc_holder
        mutex_unlock(&group->notification_mutex);
  
        wake_up(&group->notification_waitq);
+       kill_fasync(&group->fsn_fa, SIGIO, POLL_IN);
        return return_event;
  }
  
diff --combined kernel/audit_watch.c
index 9a9ae6e3d290025179a964c577de517f24fbe75c,a66affc1c12c26db52b3f1ea6dd9c2f22f4fc04f..4a599f699adcfeca242e34f1a9691385a500450e
@@@ -241,7 -241,7 +241,7 @@@ static void audit_watch_log_rule_change
                struct audit_buffer *ab;
                ab = audit_log_start(NULL, GFP_NOFS, AUDIT_CONFIG_CHANGE);
                audit_log_format(ab, "auid=%u ses=%u op=",
 -                               audit_get_loginuid(current),
 +                               from_kuid(&init_user_ns, audit_get_loginuid(current)),
                                 audit_get_sessionid(current));
                audit_log_string(ab, op);
                audit_log_format(ab, " path=");
@@@ -265,8 -265,7 +265,8 @@@ static void audit_update_watch(struct a
        /* Run all of the watches on this parent looking for the one that
         * matches the given dname */
        list_for_each_entry_safe(owatch, nextw, &parent->watches, wlist) {
 -              if (audit_compare_dname_path(dname, owatch->path, NULL))
 +              if (audit_compare_dname_path(dname, owatch->path,
 +                                           AUDIT_NAME_FULL))
                        continue;
  
                /* If the update involves invalidating rules, do the inode-based
@@@ -350,7 -349,7 +350,7 @@@ static void audit_remove_parent_watches
        }
        mutex_unlock(&audit_filter_mutex);
  
-       fsnotify_destroy_mark(&parent->mark);
+       fsnotify_destroy_mark(&parent->mark, audit_watch_group);
  }
  
  /* Get path information necessary for adding watches. */
@@@ -457,7 -456,7 +457,7 @@@ void audit_remove_watch_rule(struct aud
  
                if (list_empty(&parent->watches)) {
                        audit_get_parent(parent);
-                       fsnotify_destroy_mark(&parent->mark);
+                       fsnotify_destroy_mark(&parent->mark, audit_watch_group);
                        audit_put_parent(parent);
                }
        }