[PATCH] time: x86_64: split x86_64/kernel/time.c up
[powerpc.git] / fs / sysfs / file.c
index 146f1de..c0e1176 100644 (file)
@@ -7,6 +7,7 @@
 #include <linux/kobject.h>
 #include <linux/namei.h>
 #include <linux/poll.h>
+#include <linux/list.h>
 #include <asm/uaccess.h>
 #include <asm/semaphore.h>
 
@@ -50,17 +51,29 @@ static struct sysfs_ops subsys_sysfs_ops = {
        .store  = subsys_attr_store,
 };
 
+/**
+ *     add_to_collection - add buffer to a collection
+ *     @buffer:        buffer to be added
+ *     @node           inode of set to add to
+ */
 
-struct sysfs_buffer {
-       size_t                  count;
-       loff_t                  pos;
-       char                    * page;
-       struct sysfs_ops        * ops;
-       struct semaphore        sem;
-       int                     needs_read_fill;
-       int                     event;
-};
+static inline void
+add_to_collection(struct sysfs_buffer *buffer, struct inode *node)
+{
+       struct sysfs_buffer_collection *set = node->i_private;
 
+       mutex_lock(&node->i_mutex);
+       list_add(&buffer->associates, &set->associates);
+       mutex_unlock(&node->i_mutex);
+}
+
+static inline void
+remove_from_collection(struct sysfs_buffer *buffer, struct inode *node)
+{
+       mutex_lock(&node->i_mutex);
+       list_del(&buffer->associates);
+       mutex_unlock(&node->i_mutex);
+}
 
 /**
  *     fill_read_buffer - allocate and fill buffer from object.
@@ -70,7 +83,8 @@ struct sysfs_buffer {
  *     Allocate @buffer->page, if it hasn't been already, then call the
  *     kobject's show() method to fill the buffer with this attribute's 
  *     data. 
- *     This is called only once, on the file's first read. 
+ *     This is called only once, on the file's first read unless an error
+ *     is returned.
  */
 static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer)
 {
@@ -88,12 +102,13 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer
 
        buffer->event = atomic_read(&sd->s_event);
        count = ops->show(kobj,attr,buffer->page);
-       buffer->needs_read_fill = 0;
        BUG_ON(count > (ssize_t)PAGE_SIZE);
-       if (count >= 0)
+       if (count >= 0) {
+               buffer->needs_read_fill = 0;
                buffer->count = count;
-       else
+       } else {
                ret = count;
+       }
        return ret;
 }
 
@@ -153,8 +168,12 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos)
        ssize_t retval = 0;
 
        down(&buffer->sem);
+       if (buffer->orphaned) {
+               retval = -ENODEV;
+               goto out;
+       }
        if (buffer->needs_read_fill) {
-               if ((retval = fill_read_buffer(file->f_dentry,buffer)))
+               if ((retval = fill_read_buffer(file->f_path.dentry,buffer)))
                        goto out;
        }
        pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n",
@@ -165,7 +184,6 @@ out:
        return retval;
 }
 
-
 /**
  *     fill_write_buffer - copy buffer from userspace.
  *     @buffer:        data buffer for file.
@@ -190,6 +208,9 @@ fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t
                count = PAGE_SIZE - 1;
        error = copy_from_user(buffer->page,buf,count);
        buffer->needs_read_fill = 1;
+       /* if buf is assumed to contain a string, terminate it by \0,
+          so e.g. sscanf() can scan the string easily */
+       buffer->page[count] = 0;
        return error ? -EFAULT : count;
 }
 
@@ -240,19 +261,25 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t
        ssize_t len;
 
        down(&buffer->sem);
+       if (buffer->orphaned) {
+               len = -ENODEV;
+               goto out;
+       }
        len = fill_write_buffer(buffer, buf, count);
        if (len > 0)
-               len = flush_write_buffer(file->f_dentry, buffer, len);
+               len = flush_write_buffer(file->f_path.dentry, buffer, len);
        if (len > 0)
                *ppos += len;
+out:
        up(&buffer->sem);
        return len;
 }
 
-static int check_perm(struct inode * inode, struct file * file)
+static int sysfs_open_file(struct inode *inode, struct file *file)
 {
-       struct kobject *kobj = sysfs_get_kobject(file->f_dentry->d_parent);
-       struct attribute * attr = to_attr(file->f_dentry);
+       struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent);
+       struct attribute * attr = to_attr(file->f_path.dentry);
+       struct sysfs_buffer_collection *set;
        struct sysfs_buffer * buffer;
        struct sysfs_ops * ops = NULL;
        int error = 0;
@@ -282,6 +309,18 @@ static int check_perm(struct inode * inode, struct file * file)
        if (!ops)
                goto Eaccess;
 
+       /* 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 {
+                       INIT_LIST_HEAD(&set->associates);
+               }
+       }
+       mutex_unlock(&inode->i_mutex);
+
        /* File needs write support.
         * The inode's perms must say it's ok, 
         * and we must have a store method.
@@ -307,9 +346,11 @@ static int check_perm(struct inode * inode, struct file * file)
         */
        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;
@@ -322,25 +363,21 @@ static int check_perm(struct inode * inode, struct file * file)
        error = -EACCES;
        module_put(attr->owner);
  Done:
-       if (error && kobj)
+       if (error)
                kobject_put(kobj);
        return error;
 }
 
-static int sysfs_open_file(struct inode * inode, struct file * filp)
-{
-       return check_perm(inode,filp);
-}
-
 static int sysfs_release(struct inode * inode, struct file * filp)
 {
-       struct kobject * kobj = to_kobj(filp->f_dentry->d_parent);
-       struct attribute * attr = to_attr(filp->f_dentry);
+       struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent);
+       struct attribute * attr = to_attr(filp->f_path.dentry);
        struct module * owner = attr->owner;
        struct sysfs_buffer * buffer = filp->private_data;
 
-       if (kobj) 
-               kobject_put(kobj);
+       if (buffer)
+               remove_from_collection(buffer, inode);
+       kobject_put(kobj);
        /* After this point, attr should not be accessed. */
        module_put(owner);
 
@@ -369,8 +406,8 @@ static int sysfs_release(struct inode * inode, struct file * filp)
 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_dentry->d_parent);
-       struct sysfs_dirent * sd = filp->f_dentry->d_fsdata;
+       struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent);
+       struct sysfs_dirent * sd = filp->f_path.dentry->d_fsdata;
        int res = 0;
 
        poll_wait(filp, &kobj->poll, wait);
@@ -483,17 +520,12 @@ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr)
                    (victim->d_parent->d_inode == dir->d_inode)) {
                        victim->d_inode->i_mtime = CURRENT_TIME;
                        fsnotify_modify(victim);
-
-                       /**
-                        * Drop reference from initial sysfs_get_dentry().
-                        */
-                       dput(victim);
                        res = 0;
                } else
                        d_drop(victim);
                
                /**
-                * Drop the reference acquired from sysfs_get_dentry() above.
+                * Drop the reference acquired from lookup_one_len() above.
                 */
                dput(victim);
        }
@@ -550,7 +582,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file);
 
 void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr)
 {
-       sysfs_hash_and_remove(kobj->dentry,attr->name);
+       sysfs_hash_and_remove(kobj->dentry, attr->name);
 }