]> err.no Git - linux-2.6/blobdiff - fs/sysfs/bin.c
Merge branch 'next' of master.kernel.org:/pub/scm/linux/kernel/git/jwboyer/powerpc-4xx
[linux-2.6] / fs / sysfs / bin.c
index 5dc47fe5de5e67ebc89624c0c52a9c37521bae83..006fc64227ddb16b051b85b0a959cfee7bd652a8 100644 (file)
@@ -1,9 +1,15 @@
 /*
- * bin.c - binary file operations for sysfs.
+ * fs/sysfs/bin.c - sysfs binary file implementation
  *
  * Copyright (c) 2003 Patrick Mochel
  * Copyright (c) 2003 Matthew Wilcox
  * Copyright (c) 2004 Silicon Graphics, Inc.
+ * Copyright (c) 2007 SUSE Linux Products GmbH
+ * Copyright (c) 2007 Tejun Heo <teheo@suse.de>
+ *
+ * This file is released under the GPLv2.
+ *
+ * Please see Documentation/filesystems/sysfs.txt for more information.
  */
 
 #undef DEBUG
 #include <linux/kobject.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/mutex.h>
 
 #include <asm/uaccess.h>
-#include <asm/semaphore.h>
 
 #include "sysfs.h"
 
 struct bin_buffer {
        struct mutex    mutex;
        void            *buffer;
+       int             mmapped;
 };
 
 static int
 fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count)
 {
        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 bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
+       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
+       int rc;
+
+       /* need attr_sd for attr, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
+
+       rc = -EIO;
+       if (attr->read)
+               rc = attr->read(kobj, attr, buffer, off, count);
 
-       if (!attr->read)
-               return -EIO;
+       sysfs_put_active_two(attr_sd);
 
-       return attr->read(kobj, buffer, off, count);
+       return rc;
 }
 
 static ssize_t
@@ -78,13 +93,21 @@ static int
 flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count)
 {
        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 bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
+       struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;
+       int rc;
+
+       /* need attr_sd for attr, its parent for kobj */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
+
+       rc = -EIO;
+       if (attr->write)
+               rc = attr->write(kobj, attr, buffer, offset, count);
 
-       if (!attr->write)
-               return -EIO;
+       sysfs_put_active_two(attr_sd);
 
-       return attr->write(kobj, buffer, offset, count);
+       return rc;
 }
 
 static ssize_t write(struct file *file, const char __user *userbuf,
@@ -123,15 +146,25 @@ static int mmap(struct file *file, struct vm_area_struct *vma)
 {
        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 bin_attribute *attr = attr_sd->s_bin_attr.bin_attr;
+       struct kobject *kobj = attr_sd->s_parent->s_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;
@@ -139,59 +172,50 @@ static int mmap(struct file *file, struct vm_area_struct *vma)
 
 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_attribute *attr = attr_sd->s_bin_attr.bin_attr;
        struct bin_buffer *bb = NULL;
-       int error = -EINVAL;
-
-       if (!kobj || !attr)
-               goto Done;
+       int error;
 
-       /* Grab the module reference for this attribute if we have one */
-       error = -ENODEV;
-       if (!try_module_get(attr->attr.owner)) 
-               goto Done;
+       /* binary file operations requires both @sd and its parent */
+       if (!sysfs_get_active_two(attr_sd))
+               return -ENODEV;
 
        error = -EACCES;
        if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap))
-               goto Error;
+               goto err_out;
        if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap))
-               goto Error;
+               goto err_out;
 
        error = -ENOMEM;
        bb = kzalloc(sizeof(*bb), GFP_KERNEL);
        if (!bb)
-               goto Error;
+               goto err_out;
 
        bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
        if (!bb->buffer)
-               goto Error;
+               goto err_out;
 
        mutex_init(&bb->mutex);
        file->private_data = bb;
 
-       error = 0;
-       goto Done;
+       /* open succeeded, put active references */
+       sysfs_put_active_two(attr_sd);
+       return 0;
 
- Error:
+ err_out:
+       sysfs_put_active_two(attr_sd);
        kfree(bb);
-       module_put(attr->attr.owner);
- Done:
-       if (error)
-               kobject_put(kobj);
        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);
-       module_put(attr->attr.owner);
+       if (bb->mmapped)
+               sysfs_put_active_two(attr_sd);
        kfree(bb->buffer);
        kfree(bb);
        return 0;
@@ -214,9 +238,9 @@ const struct file_operations bin_fops = {
 
 int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 {
-       BUG_ON(!kobj || !kobj->dentry || !attr);
+       BUG_ON(!kobj || !kobj->sd || !attr);
 
-       return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
+       return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR);
 }
 
 
@@ -228,12 +252,7 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 
 void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr)
 {
-       if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) {
-               printk(KERN_ERR "%s: "
-                       "bad dentry or inode or no such file: \"%s\"\n",
-                       __FUNCTION__, attr->attr.name);
-               dump_stack();
-       }
+       sysfs_hash_and_remove(kobj->sd, attr->attr.name);
 }
 
 EXPORT_SYMBOL_GPL(sysfs_create_bin_file);