struct bin_buffer {
        struct mutex    mutex;
        void            *buffer;
+       int             mmapped;
 };
 
 static int
 {
        struct sysfs_dirent *attr_sd = dentry->d_fsdata;
        struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
-       struct kobject * kobj = to_kobj(dentry->d_parent);
+       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
+       int rc;
+
+       /* need attr_sd for attr, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
 
-       if (!attr->read)
-               return -EIO;
+       rc = -EIO;
+       if (attr->read)
+               rc = attr->read(kobj, buffer, off, count);
 
-       return attr->read(kobj, buffer, off, count);
+       sysfs_put_active_two(attr_sd);
+
+       return rc;
 }
 
 static ssize_t
 {
        struct sysfs_dirent *attr_sd = dentry->d_fsdata;
        struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
-       struct kobject *kobj = to_kobj(dentry->d_parent);
+       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
+       int rc;
+
+       /* need attr_sd for attr, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
 
-       if (!attr->write)
-               return -EIO;
+       rc = -EIO;
+       if (attr->write)
+               rc = attr->write(kobj, buffer, offset, count);
 
-       return attr->write(kobj, buffer, offset, count);
+       sysfs_put_active_two(attr_sd);
+
+       return rc;
 }
 
 static ssize_t write(struct file *file, const char __user *userbuf,
        struct bin_buffer *bb = file->private_data;
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
        struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
-       struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent);
+       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
        int rc;
 
-       if (!attr->mmap)
-               return -EINVAL;
-
        mutex_lock(&bb->mutex);
-       rc = attr->mmap(kobj, attr, vma);
+
+       /* need attr_sd for attr, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
+
+       rc = -EINVAL;
+       if (attr->mmap)
+               rc = attr->mmap(kobj, attr, vma);
+
+       if (rc == 0 && !bb->mmapped)
+               bb->mmapped = 1;
+       else
+               sysfs_put_active_two(attr_sd);
+
        mutex_unlock(&bb->mutex);
 
        return rc;
 
 static int open(struct inode * inode, struct file * file)
 {
-       struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
        struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
        struct bin_buffer *bb = NULL;
-       int error = -EINVAL;
+       int error;
 
-       if (!kobj || !attr)
-               goto Done;
+       /* need attr_sd for attr */
+       if (!sysfs_get_active(attr_sd))
+               return -ENODEV;
 
-       /* Grab the module reference for this attribute if we have one */
+       /* Grab the module reference for this attribute */
        error = -ENODEV;
-       if (!try_module_get(attr->attr.owner)) 
-               goto Done;
+       if (!try_module_get(attr->attr.owner))
+               goto err_sput;
 
        error = -EACCES;
        if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
-               goto Error;
+               goto err_mput;
        if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
-               goto Error;
+               goto err_mput;
 
        error = -ENOMEM;
        bb = kzalloc(sizeof(*bb), GFP_KERNEL);
        if (!bb)
-               goto Error;
+               goto err_mput;
 
        bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!bb->buffer)
-               goto Error;
+               goto err_mput;
 
        mutex_init(&bb->mutex);
        file->private_data = bb;
 
-       error = 0;
-       goto Done;
+       /* open succeeded, put active reference and pin attr_sd */
+       sysfs_put_active(attr_sd);
+       sysfs_get(attr_sd);
+       return 0;
 
- Error:
-       kfree(bb);
+ err_mput:
        module_put(attr->attr.owner);
- Done:
-       if (error)
-               kobject_put(kobj);
+ err_sput:
+       sysfs_put_active(attr_sd);
+       kfree(bb);
        return error;
 }
 
 static int release(struct inode * inode, struct file * file)
 {
-       struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent);
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
        struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr;
        struct bin_buffer *bb = file->private_data;
 
-       kobject_put(kobj);
+       if (bb->mmapped)
+               sysfs_put_active_two(attr_sd);
+       sysfs_put(attr_sd);
        module_put(attr->attr.owner);
        kfree(bb->buffer);
        kfree(bb);
 
  repeat:
        parent_sd = sd->s_parent;
 
+       /* If @sd is being released after deletion, s_active is write
+        * locked.  If @sd is cursor for directory walk or being
+        * released prematurely, s_active has no reader or writer.
+        *
+        * sysfs_deactivate() lies to lockdep that s_active is
+        * unlocked immediately.  Lie one more time to cover the
+        * previous lie.
+        */
+       if (!down_write_trylock(&sd->s_active))
+               rwsem_acquire(&sd->s_active.dep_map,
+                             SYSFS_S_ACTIVE_DEACTIVATE, 0, _RET_IP_);
+       up_write(&sd->s_active);
+
        if (sd->s_type & SYSFS_KOBJ_LINK)
                sysfs_put(sd->s_elem.symlink.target_sd);
        if (sd->s_type & SYSFS_COPY_NAME)
 
        atomic_set(&sd->s_count, 1);
        atomic_set(&sd->s_event, 1);
+       init_rwsem(&sd->s_active);
        INIT_LIST_HEAD(&sd->s_children);
        INIT_LIST_HEAD(&sd->s_sibling);
 
        d_delete(d);
        sd = d->d_fsdata;
        list_del_init(&sd->s_sibling);
-       sysfs_put(sd);
        if (d->d_inode)
                simple_rmdir(parent->d_inode,d);
 
 
        mutex_unlock(&parent->d_inode->i_mutex);
        dput(parent);
+
+       sysfs_deactivate(sd);
+       sysfs_put(sd);
 }
 
 void sysfs_remove_subdir(struct dentry * d)
 
 static void __sysfs_remove_dir(struct dentry *dentry)
 {
+       LIST_HEAD(removed);
        struct sysfs_dirent * parent_sd;
        struct sysfs_dirent * sd, * tmp;
 
        list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) {
                if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED))
                        continue;
-               list_del_init(&sd->s_sibling);
+               list_move(&sd->s_sibling, &removed);
                sysfs_drop_dentry(sd, dentry);
-               sysfs_put(sd);
        }
        mutex_unlock(&dentry->d_inode->i_mutex);
 
+       list_for_each_entry_safe(sd, tmp, &removed, s_sibling) {
+               list_del_init(&sd->s_sibling);
+               sysfs_deactivate(sd);
+               sysfs_put(sd);
+       }
+
        remove_dir(dentry);
        /**
         * Drop reference from dget() on entrance.
 
  */
 static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
 {
-       struct sysfs_dirent * sd = dentry->d_fsdata;
-       struct kobject * kobj = to_kobj(dentry->d_parent);
+       struct sysfs_dirent *attr_sd = dentry->d_fsdata;
+       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
        struct sysfs_ops * ops = buffer->ops;
        int ret = 0;
        ssize_t count;
        if (!buffer->page)
                return -ENOMEM;
 
-       buffer->event = atomic_read(&sd->s_event);
-       count = ops->show(kobj, sd->s_elem.attr.attr, buffer->page);
+       /* need attr_sd for attr and ops, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
+
+       buffer->event = atomic_read(&attr_sd->s_event);
+       count = ops->show(kobj, attr_sd->s_elem.attr.attr, buffer->page);
+
+       sysfs_put_active_two(attr_sd);
+
        BUG_ON(count > (ssize_t)PAGE_SIZE);
        if (count >= 0) {
                buffer->needs_read_fill = 0;
  *     passing the buffer that we acquired in fill_write_buffer().
  */
 
-static int 
+static int
 flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count)
 {
        struct sysfs_dirent *attr_sd = dentry->d_fsdata;
-       struct kobject * kobj = to_kobj(dentry->d_parent);
+       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
        struct sysfs_ops * ops = buffer->ops;
+       int rc;
+
+       /* need attr_sd for attr and ops, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
+
+       rc = ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count);
+
+       sysfs_put_active_two(attr_sd);
 
-       return ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count);
+       return rc;
 }
 
 
 
 static int sysfs_open_file(struct inode *inode, struct file *file)
 {
-       struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
        struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;
        struct attribute *attr = attr_sd->s_elem.attr.attr;
+       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
        struct sysfs_buffer_collection *set;
        struct sysfs_buffer * buffer;
        struct sysfs_ops * ops = NULL;
-       int error = 0;
+       int error;
 
-       if (!kobj || !attr)
-               goto Einval;
+       /* need attr_sd for attr and ops, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
 
-       /* Grab the module reference for this attribute if we have one */
-       if (!try_module_get(attr->owner)) {
-               error = -ENODEV;
-               goto Done;
-       }
+       /* Grab the module reference for this attribute */
+       error = -ENODEV;
+       if (!try_module_get(attr->owner))
+               goto err_sput;
 
        /* if the kobject has no ktype, then we assume that it is a subsystem
         * itself, and use ops for it.
        /* No sysfs operations, either from having no subsystem,
         * or the subsystem have no operations.
         */
+       error = -EACCES;
        if (!ops)
-               goto Eaccess;
+               goto err_mput;
 
        /* make sure we have a collection to add our buffers to */
        mutex_lock(&inode->i_mutex);
        if (!(set = inode->i_private)) {
-               if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) {
-                       error = -ENOMEM;
-                       goto Done;
-               } else {
+               error = -ENOMEM;
+               if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL)))
+                       goto err_mput;
+               else
                        INIT_LIST_HEAD(&set->associates);
-               }
        }
        mutex_unlock(&inode->i_mutex);
 
+       error = -EACCES;
+
        /* File needs write support.
         * The inode's perms must say it's ok, 
         * and we must have a store method.
         */
        if (file->f_mode & FMODE_WRITE) {
-
                if (!(inode->i_mode & S_IWUGO) || !ops->store)
-                       goto Eaccess;
-
+                       goto err_mput;
        }
 
        /* File needs read support.
         */
        if (file->f_mode & FMODE_READ) {
                if (!(inode->i_mode & S_IRUGO) || !ops->show)
-                       goto Eaccess;
+                       goto err_mput;
        }
 
        /* No error? Great, allocate a buffer for the file, and store it
         * it in file->private_data for easy access.
         */
+       error = -ENOMEM;
        buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);
-       if (buffer) {
-               INIT_LIST_HEAD(&buffer->associates);
-               init_MUTEX(&buffer->sem);
-               buffer->needs_read_fill = 1;
-               buffer->ops = ops;
-               add_to_collection(buffer, inode);
-               file->private_data = buffer;
-       } else
-               error = -ENOMEM;
-       goto Done;
+       if (!buffer)
+               goto err_mput;
 
- Einval:
-       error = -EINVAL;
-       goto Done;
- Eaccess:
-       error = -EACCES;
+       INIT_LIST_HEAD(&buffer->associates);
+       init_MUTEX(&buffer->sem);
+       buffer->needs_read_fill = 1;
+       buffer->ops = ops;
+       add_to_collection(buffer, inode);
+       file->private_data = buffer;
+
+       /* open succeeded, put active references and pin attr_sd */
+       sysfs_put_active_two(attr_sd);
+       sysfs_get(attr_sd);
+       return 0;
+
+ err_mput:
        module_put(attr->owner);
- Done:
-       if (error)
-               kobject_put(kobj);
+ err_sput:
+       sysfs_put_active_two(attr_sd);
        return error;
 }
 
 static int sysfs_release(struct inode * inode, struct file * filp)
 {
-       struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent);
        struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata;
        struct attribute *attr = attr_sd->s_elem.attr.attr;
        struct sysfs_buffer * buffer = filp->private_data;
 
        if (buffer)
                remove_from_collection(buffer, inode);
-       kobject_put(kobj);
+       sysfs_put(attr_sd);
        /* After this point, attr should not be accessed. */
        module_put(attr->owner);
 
 static unsigned int sysfs_poll(struct file *filp, poll_table *wait)
 {
        struct sysfs_buffer * buffer = filp->private_data;
-       struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent);
-       struct sysfs_dirent * sd = filp->f_path.dentry->d_fsdata;
-       int res = 0;
+       struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata;
+       struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj;
+
+       /* need parent for the kobj, grab both */
+       if (!sysfs_get_active_two(attr_sd))
+               goto trigger;
 
        poll_wait(filp, &kobj->poll, wait);
 
-       if (buffer->event != atomic_read(&sd->s_event)) {
-               res = POLLERR|POLLPRI;
-               buffer->needs_read_fill = 1;
-       }
+       sysfs_put_active_two(attr_sd);
 
-       return res;
+       if (buffer->event != atomic_read(&attr_sd->s_event))
+               goto trigger;
+
+       return 0;
+
+ trigger:
+       buffer->needs_read_fill = 1;
+       return POLLERR|POLLPRI;
 }
 
 
 
                if (!strcmp(sd->s_name, name)) {
                        list_del_init(&sd->s_sibling);
                        sysfs_drop_dentry(sd, dir);
-                       sysfs_put(sd);
                        found = 1;
                        break;
                }
        }
        mutex_unlock(&dir->d_inode->i_mutex);
 
-       return found ? 0 : -ENOENT;
+       if (!found)
+               return -ENOENT;
+
+       sysfs_deactivate(sd);
+       sysfs_put(sd);
+       return 0;
 }
 
        struct bin_attribute    * bin_attr;
 };
 
+/*
+ * As long as s_count reference is held, the sysfs_dirent itself is
+ * accessible.  Dereferencing s_elem or any other outer entity
+ * requires s_active reference.
+ */
 struct sysfs_dirent {
        atomic_t                s_count;
+       struct rw_semaphore     s_active;
        struct sysfs_dirent     * s_parent;
        struct list_head        s_sibling;
        struct list_head        s_children;
        atomic_t                s_event;
 };
 
+/*
+ * A sysfs file which deletes another file when written to need to
+ * write lock the s_active of the victim while its s_active is read
+ * locked for the write operation.  Tell lockdep that this is okay.
+ */
+enum sysfs_s_active_class
+{
+       SYSFS_S_ACTIVE_NORMAL,          /* file r/w access, etc - default */
+       SYSFS_S_ACTIVE_DEACTIVATE,      /* file deactivation */
+};
+
 extern struct vfsmount * sysfs_mount;
 extern struct kmem_cache *sysfs_dir_cachep;
 
        struct list_head        associates;
 };
 
-static inline struct kobject * to_kobj(struct dentry * dentry)
+static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd)
 {
-       struct sysfs_dirent * sd = dentry->d_fsdata;
-       return sd->s_elem.dir.kobj;
+       if (sd) {
+               WARN_ON(!atomic_read(&sd->s_count));
+               atomic_inc(&sd->s_count);
+       }
+       return sd;
 }
 
-static inline struct kobject *sysfs_get_kobject(struct dentry *dentry)
+static inline void sysfs_put(struct sysfs_dirent * sd)
 {
-       struct kobject * kobj = NULL;
-
-       spin_lock(&dcache_lock);
-       if (!d_unhashed(dentry)) {
-               struct sysfs_dirent * sd = dentry->d_fsdata;
-
-               if (sd->s_type & SYSFS_KOBJ_LINK)
-                       sd = sd->s_elem.symlink.target_sd;
+       if (sd && atomic_dec_and_test(&sd->s_count))
+               release_sysfs_dirent(sd);
+}
 
-               kobj = kobject_get(sd->s_elem.dir.kobj);
+/**
+ *     sysfs_get_active - get an active reference to sysfs_dirent
+ *     @sd: sysfs_dirent to get an active reference to
+ *
+ *     Get an active reference of @sd.  This function is noop if @sd
+ *     is NULL.
+ *
+ *     RETURNS:
+ *     Pointer to @sd on success, NULL on failure.
+ */
+static inline struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd)
+{
+       if (sd) {
+               if (unlikely(!down_read_trylock(&sd->s_active)))
+                       sd = NULL;
        }
-       spin_unlock(&dcache_lock);
+       return sd;
+}
 
-       return kobj;
+/**
+ *     sysfs_put_active - put an active reference to sysfs_dirent
+ *     @sd: sysfs_dirent to put an active reference to
+ *
+ *     Put an active reference to @sd.  This function is noop if @sd
+ *     is NULL.
+ */
+static inline void sysfs_put_active(struct sysfs_dirent *sd)
+{
+       if (sd)
+               up_read(&sd->s_active);
 }
 
-static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd)
+/**
+ *     sysfs_get_active_two - get active references to sysfs_dirent and parent
+ *     @sd: sysfs_dirent of interest
+ *
+ *     Get active reference to @sd and its parent.  Parent's active
+ *     reference is grabbed first.  This function is noop if @sd is
+ *     NULL.
+ *
+ *     RETURNS:
+ *     Pointer to @sd on success, NULL on failure.
+ */
+static inline struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd)
 {
        if (sd) {
-               WARN_ON(!atomic_read(&sd->s_count));
-               atomic_inc(&sd->s_count);
+               if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent)))
+                       return NULL;
+               if (unlikely(!sysfs_get_active(sd))) {
+                       sysfs_put_active(sd->s_parent);
+                       return NULL;
+               }
        }
        return sd;
 }
 
-static inline void sysfs_put(struct sysfs_dirent * sd)
+/**
+ *     sysfs_put_active_two - put active references to sysfs_dirent and parent
+ *     @sd: sysfs_dirent of interest
+ *
+ *     Put active references to @sd and its parent.  This function is
+ *     noop if @sd is NULL.
+ */
+static inline void sysfs_put_active_two(struct sysfs_dirent *sd)
 {
-       if (sd && atomic_dec_and_test(&sd->s_count))
-               release_sysfs_dirent(sd);
+       if (sd) {
+               sysfs_put_active(sd);
+               sysfs_put_active(sd->s_parent);
+       }
+}
+
+/**
+ *     sysfs_deactivate - deactivate sysfs_dirent
+ *     @sd: sysfs_dirent to deactivate
+ *
+ *     Deny new active references and drain existing ones.  s_active
+ *     will be unlocked when the sysfs_dirent is released.
+ */
+static inline void sysfs_deactivate(struct sysfs_dirent *sd)
+{
+       down_write_nested(&sd->s_active, SYSFS_S_ACTIVE_DEACTIVATE);
+
+       /* s_active will be unlocked by the thread doing the final put
+        * on @sd.  Lie to lockdep.
+        */
+       rwsem_release(&sd->s_active.dep_map, 1, _RET_IP_);
 }
 
 static inline int sysfs_is_shadowed_inode(struct inode *inode)