|
@@ -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.
|
|
@@ -153,6 +166,10 @@ 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_path.dentry,buffer)))
|
|
|
goto out;
|
|
@@ -165,7 +182,6 @@ out:
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
-
|
|
|
/**
|
|
|
* fill_write_buffer - copy buffer from userspace.
|
|
|
* @buffer: data buffer for file.
|
|
@@ -243,19 +259,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_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_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;
|
|
@@ -285,6 +307,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.
|
|
@@ -310,9 +344,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;
|
|
@@ -330,11 +366,6 @@ static int check_perm(struct inode * inode, struct file * file)
|
|
|
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_path.dentry->d_parent);
|
|
@@ -342,6 +373,8 @@ static int sysfs_release(struct inode * inode, struct file * filp)
|
|
|
struct module * owner = attr->owner;
|
|
|
struct sysfs_buffer * buffer = filp->private_data;
|
|
|
|
|
|
+ if (buffer)
|
|
|
+ remove_from_collection(buffer, inode);
|
|
|
if (kobj)
|
|
|
kobject_put(kobj);
|
|
|
/* After this point, attr should not be accessed. */
|
|
@@ -548,7 +581,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);
|
|
|
}
|
|
|
|
|
|
|