linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / fs / inotify.c
index 732ec4b..3041503 100644 (file)
 #include <asm/ioctls.h>
 
 static atomic_t inotify_cookie;
+static atomic_t inotify_watches;
 
-static kmem_cache_t *watch_cachep __read_mostly;
-static kmem_cache_t *event_cachep __read_mostly;
+static kmem_cache_t *watch_cachep;
+static kmem_cache_t *event_cachep;
 
-static struct vfsmount *inotify_mnt __read_mostly;
+static struct vfsmount *inotify_mnt;
 
 /* these are configurable via /proc/sys/fs/inotify/ */
-int inotify_max_user_instances __read_mostly;
-int inotify_max_user_watches __read_mostly;
-int inotify_max_queued_events __read_mostly;
+int inotify_max_user_instances;
+int inotify_max_user_watches;
+int inotify_max_queued_events;
 
 /*
  * Lock ordering:
  *
  * dentry->d_lock (used to keep d_move() away from dentry->d_parent)
- * iprune_mutex (synchronize shrink_icache_memory())
+ * iprune_sem (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_dev->mutex (protects inotify_device and watches->d_list)
+ *     inode->inotify_sem (protects inode->inotify_watches and watches->i_list)
+ *             inotify_dev->sem (protects inotify_device and watches->d_list)
  */
 
 /*
@@ -78,12 +79,12 @@ int inotify_max_queued_events __read_mostly;
 /*
  * struct inotify_device - represents an inotify instance
  *
- * This structure is protected by the mutex 'mutex'.
+ * This structure is protected by the semaphore 'sem'.
  */
 struct inotify_device {
        wait_queue_head_t       wq;             /* wait queue for i/o */
        struct idr              idr;            /* idr mapping wd -> watch */
-       struct mutex            mutex;          /* protects this bad boy */
+       struct semaphore        sem;            /* protects this bad boy */
        struct list_head        events;         /* list of queued events */
        struct list_head        watches;        /* list of watches */
        atomic_t                count;          /* reference count */
@@ -100,7 +101,7 @@ struct inotify_device {
  * device.  In read(), this list is walked and all events that can fit in the
  * buffer are returned.
  *
- * Protected by dev->mutex of the device in which we are queued.
+ * Protected by dev->sem of the device in which we are queued.
  */
 struct inotify_kernel_event {
        struct inotify_event    event;  /* the user-space event */
@@ -111,8 +112,8 @@ struct inotify_kernel_event {
 /*
  * struct inotify_watch - represents a watch request on a specific inode
  *
- * d_list is protected by dev->mutex of the associated watch->dev.
- * i_list and mask are protected by inode->inotify_mutex of the associated inode.
+ * d_list is protected by dev->sem of the associated watch->dev.
+ * i_list and mask are protected by inode->inotify_sem of the associated inode.
  * dev, inode, and wd are never written to once the watch is created.
  */
 struct inotify_watch {
@@ -260,7 +261,7 @@ static struct inotify_kernel_event * kernel_event(s32 wd, u32 mask, u32 cookie,
 /*
  * inotify_dev_get_event - return the next event in the given dev's queue
  *
- * Caller must hold dev->mutex.
+ * Caller must hold dev->sem.
  */
 static inline struct inotify_kernel_event *
 inotify_dev_get_event(struct inotify_device *dev)
@@ -271,7 +272,7 @@ inotify_dev_get_event(struct inotify_device *dev)
 /*
  * inotify_dev_queue_event - add a new event to the given device
  *
- * Caller must hold dev->mutex.  Can sleep (calls kernel_event()).
+ * Caller must hold dev->sem.  Can sleep (calls kernel_event()).
  */
 static void inotify_dev_queue_event(struct inotify_device *dev,
                                    struct inotify_watch *watch, u32 mask,
@@ -314,7 +315,7 @@ static void inotify_dev_queue_event(struct inotify_device *dev,
 /*
  * remove_kevent - cleans up and ultimately frees the given kevent
  *
- * Caller must hold dev->mutex.
+ * Caller must hold dev->sem.
  */
 static void remove_kevent(struct inotify_device *dev,
                          struct inotify_kernel_event *kevent)
@@ -331,7 +332,7 @@ static void remove_kevent(struct inotify_device *dev,
 /*
  * inotify_dev_event_dequeue - destroy an event on the given device
  *
- * Caller must hold dev->mutex.
+ * Caller must hold dev->sem.
  */
 static void inotify_dev_event_dequeue(struct inotify_device *dev)
 {
@@ -345,7 +346,7 @@ static void inotify_dev_event_dequeue(struct inotify_device *dev)
 /*
  * inotify_dev_get_wd - returns the next WD for use by the given dev
  *
- * Callers must hold dev->mutex.  This function can sleep.
+ * Callers must hold dev->sem.  This function can sleep.
  */
 static int inotify_dev_get_wd(struct inotify_device *dev,
                              struct inotify_watch *watch)
@@ -379,52 +380,10 @@ static int find_inode(const char __user *dirname, struct nameidata *nd,
        return error;
 }
 
-/*
- * 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) {
-                               WARN_ON(child->d_flags & DCACHE_INOTIFY_PARENT_WATCHED);
-                               continue;
-                       }
-                       spin_lock(&child->d_lock);
-                       if (watched) {
-                               WARN_ON(child->d_flags &
-                                               DCACHE_INOTIFY_PARENT_WATCHED);
-                               child->d_flags |= DCACHE_INOTIFY_PARENT_WATCHED;
-                       } else {
-                               WARN_ON(!(child->d_flags &
-                                       DCACHE_INOTIFY_PARENT_WATCHED));
-                               child->d_flags&=~DCACHE_INOTIFY_PARENT_WATCHED;
-                       }
-                       spin_unlock(&child->d_lock);
-               }
-       }
-       spin_unlock(&dcache_lock);
-}
-
 /*
  * create_watch - creates a watch on the given device.
  *
- * Callers must hold dev->mutex.  Calls inotify_dev_get_wd() so may sleep.
+ * Callers must hold dev->sem.  Calls inotify_dev_get_wd() so may sleep.
  * Both 'dev' and 'inode' (by way of nameidata) need to be pinned.
  */
 static struct inotify_watch *create_watch(struct inotify_device *dev,
@@ -467,6 +426,7 @@ static struct inotify_watch *create_watch(struct inotify_device *dev,
        get_inotify_watch(watch);
 
        atomic_inc(&dev->user->inotify_watches);
+       atomic_inc(&inotify_watches);
 
        return watch;
 }
@@ -474,7 +434,7 @@ static struct inotify_watch *create_watch(struct inotify_device *dev,
 /*
  * inotify_find_dev - find the watch associated with the given inode and dev
  *
- * Callers must hold inode->inotify_mutex.
+ * Callers must hold inode->inotify_sem.
  */
 static struct inotify_watch *inode_find_dev(struct inode *inode,
                                            struct inotify_device *dev)
@@ -498,10 +458,8 @@ static void remove_watch_no_event(struct inotify_watch *watch,
        list_del(&watch->i_list);
        list_del(&watch->d_list);
 
-       if (!inotify_inode_watched(watch->inode))
-               set_dentry_child_flags(watch->inode, 0);
-
        atomic_dec(&dev->user->inotify_watches);
+       atomic_dec(&inotify_watches);
        idr_remove(&dev->idr, watch->wd);
        put_inotify_watch(watch);
 }
@@ -511,7 +469,7 @@ static void remove_watch_no_event(struct inotify_watch *watch,
  * the IN_IGNORED event to the given device signifying that the inode is no
  * longer watched.
  *
- * Callers must hold both inode->inotify_mutex and dev->mutex.  We drop a
+ * Callers must hold both inode->inotify_sem and dev->sem.  We drop a
  * reference to the inode before returning.
  *
  * The inode is not iput() so as to remain atomic.  If the inode needs to be
@@ -523,39 +481,16 @@ static void remove_watch(struct inotify_watch *watch,struct inotify_device *dev)
        remove_watch_no_event(watch, dev);
 }
 
-/* Kernel API */
-
 /*
- * inotify_d_instantiate - instantiate dcache entry for inode
+ * 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.
  */
-void inotify_d_instantiate(struct dentry *entry, struct inode *inode)
+static inline int inotify_inode_watched(struct inode *inode)
 {
-       struct dentry *parent;
-
-       if (!inode)
-               return;
-
-       WARN_ON(entry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED);
-       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);
+       return !list_empty(&inode->inotify_watches);
 }
 
-/*
- * 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;
-}
+/* Kernel API */
 
 /**
  * inotify_inode_queue_event - queue an event to all watches on this inode
@@ -572,21 +507,21 @@ void inotify_inode_queue_event(struct inode *inode, u32 mask, u32 cookie,
        if (!inotify_inode_watched(inode))
                return;
 
-       mutex_lock(&inode->inotify_mutex);
+       down(&inode->inotify_sem);
        list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
                u32 watch_mask = watch->mask;
                if (watch_mask & mask) {
                        struct inotify_device *dev = watch->dev;
                        get_inotify_watch(watch);
-                       mutex_lock(&dev->mutex);
+                       down(&dev->sem);
                        inotify_dev_queue_event(dev, watch, mask, cookie, name);
                        if (watch_mask & IN_ONESHOT)
                                remove_watch_no_event(watch, dev);
-                       mutex_unlock(&dev->mutex);
+                       up(&dev->sem);
                        put_inotify_watch(watch);
                }
        }
-       mutex_unlock(&inode->inotify_mutex);
+       up(&inode->inotify_sem);
 }
 EXPORT_SYMBOL_GPL(inotify_inode_queue_event);
 
@@ -603,7 +538,7 @@ void inotify_dentry_parent_queue_event(struct dentry *dentry, u32 mask,
        struct dentry *parent;
        struct inode *inode;
 
-       if (!(dentry->d_flags & DCACHE_INOTIFY_PARENT_WATCHED))
+       if (!atomic_read (&inotify_watches))
                return;
 
        spin_lock(&dentry->d_lock);
@@ -634,7 +569,7 @@ EXPORT_SYMBOL_GPL(inotify_get_cookie);
  * @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.
+ * of inodes, and with iprune_sem held, keeping shrink_icache_memory() at bay.
  * We temporarily drop inode_lock, however, and CAN block.
  */
 void inotify_unmount_inodes(struct list_head *list)
@@ -683,7 +618,7 @@ void inotify_unmount_inodes(struct list_head *list)
                 * 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.
+                * iprune_sem keeps shrink_icache_memory() away.
                 */
                spin_unlock(&inode_lock);
 
@@ -691,16 +626,16 @@ void inotify_unmount_inodes(struct list_head *list)
                        iput(need_iput_tmp);
 
                /* for each watch, send IN_UNMOUNT and then remove it */
-               mutex_lock(&inode->inotify_mutex);
+               down(&inode->inotify_sem);
                watches = &inode->inotify_watches;
                list_for_each_entry_safe(watch, next_w, watches, i_list) {
                        struct inotify_device *dev = watch->dev;
-                       mutex_lock(&dev->mutex);
+                       down(&dev->sem);
                        inotify_dev_queue_event(dev, watch, IN_UNMOUNT,0,NULL);
                        remove_watch(watch, dev);
-                       mutex_unlock(&dev->mutex);
+                       up(&dev->sem);
                }
-               mutex_unlock(&inode->inotify_mutex);
+               up(&inode->inotify_sem);
                iput(inode);            
 
                spin_lock(&inode_lock);
@@ -716,14 +651,14 @@ void inotify_inode_is_dead(struct inode *inode)
 {
        struct inotify_watch *watch, *next;
 
-       mutex_lock(&inode->inotify_mutex);
+       down(&inode->inotify_sem);
        list_for_each_entry_safe(watch, next, &inode->inotify_watches, i_list) {
                struct inotify_device *dev = watch->dev;
-               mutex_lock(&dev->mutex);
+               down(&dev->sem);
                remove_watch(watch, dev);
-               mutex_unlock(&dev->mutex);
+               up(&dev->sem);
        }
-       mutex_unlock(&inode->inotify_mutex);
+       up(&inode->inotify_sem);
 }
 EXPORT_SYMBOL_GPL(inotify_inode_is_dead);
 
@@ -735,10 +670,10 @@ static unsigned int inotify_poll(struct file *file, poll_table *wait)
        int ret = 0;
 
        poll_wait(file, &dev->wq, wait);
-       mutex_lock(&dev->mutex);
+       down(&dev->sem);
        if (!list_empty(&dev->events))
                ret = POLLIN | POLLRDNORM;
-       mutex_unlock(&dev->mutex);
+       up(&dev->sem);
 
        return ret;
 }
@@ -760,9 +695,9 @@ static ssize_t inotify_read(struct file *file, char __user *buf,
 
                prepare_to_wait(&dev->wq, &wait, TASK_INTERRUPTIBLE);
 
-               mutex_lock(&dev->mutex);
+               down(&dev->sem);
                events = !list_empty(&dev->events);
-               mutex_unlock(&dev->mutex);
+               up(&dev->sem);
                if (events) {
                        ret = 0;
                        break;
@@ -785,7 +720,7 @@ static ssize_t inotify_read(struct file *file, char __user *buf,
        if (ret)
                return ret;
 
-       mutex_lock(&dev->mutex);
+       down(&dev->sem);
        while (1) {
                struct inotify_kernel_event *kevent;
 
@@ -815,7 +750,7 @@ static ssize_t inotify_read(struct file *file, char __user *buf,
 
                remove_kevent(dev, kevent);
        }
-       mutex_unlock(&dev->mutex);
+       up(&dev->sem);
 
        return ret;
 }
@@ -828,41 +763,37 @@ static int inotify_release(struct inode *ignored, struct file *file)
         * Destroy all of the watches on this device.  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 dev->mutex.  The following works.
+        * hold inode->inotify_sem before dev->sem.  The following works.
         */
        while (1) {
                struct inotify_watch *watch;
                struct list_head *watches;
                struct inode *inode;
 
-               mutex_lock(&dev->mutex);
+               down(&dev->sem);
                watches = &dev->watches;
                if (list_empty(watches)) {
-                       mutex_unlock(&dev->mutex);
+                       up(&dev->sem);
                        break;
                }
                watch = list_entry(watches->next, struct inotify_watch, d_list);
                get_inotify_watch(watch);
-               mutex_unlock(&dev->mutex);
+               up(&dev->sem);
 
                inode = watch->inode;
-               mutex_lock(&inode->inotify_mutex);
-               mutex_lock(&dev->mutex);
-
-               /* make sure we didn't race with another list removal */
-               if (likely(idr_find(&dev->idr, watch->wd)))
-                       remove_watch_no_event(watch, dev);
-
-               mutex_unlock(&dev->mutex);
-               mutex_unlock(&inode->inotify_mutex);
+               down(&inode->inotify_sem);
+               down(&dev->sem);
+               remove_watch_no_event(watch, dev);
+               up(&dev->sem);
+               up(&inode->inotify_sem);
                put_inotify_watch(watch);
        }
 
        /* destroy all of the events on this device */
-       mutex_lock(&dev->mutex);
+       down(&dev->sem);
        while (!list_empty(&dev->events))
                inotify_dev_event_dequeue(dev);
-       mutex_unlock(&dev->mutex);
+       up(&dev->sem);
 
        /* free this device: the put matching the get in inotify_init() */
        put_inotify_dev(dev);
@@ -880,25 +811,26 @@ static int inotify_ignore(struct inotify_device *dev, s32 wd)
        struct inotify_watch *watch;
        struct inode *inode;
 
-       mutex_lock(&dev->mutex);
+       down(&dev->sem);
        watch = idr_find(&dev->idr, wd);
        if (unlikely(!watch)) {
-               mutex_unlock(&dev->mutex);
+               up(&dev->sem);
                return -EINVAL;
        }
        get_inotify_watch(watch);
        inode = watch->inode;
-       mutex_unlock(&dev->mutex);
+       up(&dev->sem);
 
-       mutex_lock(&inode->inotify_mutex);
-       mutex_lock(&dev->mutex);
+       down(&inode->inotify_sem);
+       down(&dev->sem);
 
        /* make sure that we did not race */
-       if (likely(idr_find(&dev->idr, wd) == watch))
+       watch = idr_find(&dev->idr, wd);
+       if (likely(watch))
                remove_watch(watch, dev);
 
-       mutex_unlock(&dev->mutex);
-       mutex_unlock(&inode->inotify_mutex);
+       up(&dev->sem);
+       up(&inode->inotify_sem);
        put_inotify_watch(watch);
 
        return 0;
@@ -923,7 +855,7 @@ static long inotify_ioctl(struct file *file, unsigned int cmd,
        return ret;
 }
 
-static const struct file_operations inotify_fops = {
+static struct file_operations inotify_fops = {
        .poll           = inotify_poll,
        .read           = inotify_read,
        .release        = inotify_release,
@@ -973,7 +905,7 @@ asmlinkage long sys_inotify_init(void)
        INIT_LIST_HEAD(&dev->events);
        INIT_LIST_HEAD(&dev->watches);
        init_waitqueue_head(&dev->wq);
-       mutex_init(&dev->mutex);
+       sema_init(&dev->sem, 1);
        dev->event_count = 0;
        dev->queue_size = 0;
        dev->max_events = inotify_max_queued_events;
@@ -1028,8 +960,8 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
        inode = nd.dentry->d_inode;
        dev = filp->private_data;
 
-       mutex_lock(&inode->inotify_mutex);
-       mutex_lock(&dev->mutex);
+       down(&inode->inotify_sem);
+       down(&dev->sem);
 
        if (mask & IN_MASK_ADD)
                mask_add = 1;
@@ -1061,16 +993,13 @@ asmlinkage long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
                goto out;
        }
 
-       if (!inotify_inode_watched(inode))
-               set_dentry_child_flags(inode, 1);
-
        /* Add the watch to the device's and the inode's list */
        list_add(&watch->d_list, &dev->watches);
        list_add(&watch->i_list, &inode->inotify_watches);
        ret = watch->wd;
 out:
-       mutex_unlock(&dev->mutex);
-       mutex_unlock(&inode->inotify_mutex);
+       up(&dev->sem);
+       up(&inode->inotify_sem);
        path_release(&nd);
 fput_and_out:
        fput_light(filp, fput_needed);
@@ -1136,6 +1065,7 @@ static int __init inotify_setup(void)
        inotify_max_user_watches = 8192;
 
        atomic_set(&inotify_cookie, 0);
+       atomic_set(&inotify_watches, 0);
 
        watch_cachep = kmem_cache_create("inotify_watch_cache",
                                         sizeof(struct inotify_watch),