diff options
Diffstat (limited to 'fs/sysfs')
-rw-r--r-- | fs/sysfs/bin.c | 64 |
1 files changed, 49 insertions, 15 deletions
diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 67a0d5030c96..5dc47fe5de5e 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c | |||
@@ -20,6 +20,11 @@ | |||
20 | 20 | ||
21 | #include "sysfs.h" | 21 | #include "sysfs.h" |
22 | 22 | ||
23 | struct bin_buffer { | ||
24 | struct mutex mutex; | ||
25 | void *buffer; | ||
26 | }; | ||
27 | |||
23 | static int | 28 | static int |
24 | fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) | 29 | fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) |
25 | { | 30 | { |
@@ -36,7 +41,7 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) | |||
36 | static ssize_t | 41 | static ssize_t |
37 | read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) | 42 | read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) |
38 | { | 43 | { |
39 | char *buffer = file->private_data; | 44 | struct bin_buffer *bb = file->private_data; |
40 | struct dentry *dentry = file->f_path.dentry; | 45 | struct dentry *dentry = file->f_path.dentry; |
41 | int size = dentry->d_inode->i_size; | 46 | int size = dentry->d_inode->i_size; |
42 | loff_t offs = *off; | 47 | loff_t offs = *off; |
@@ -49,17 +54,23 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) | |||
49 | count = size - offs; | 54 | count = size - offs; |
50 | } | 55 | } |
51 | 56 | ||
52 | count = fill_read(dentry, buffer, offs, count); | 57 | mutex_lock(&bb->mutex); |
58 | |||
59 | count = fill_read(dentry, bb->buffer, offs, count); | ||
53 | if (count < 0) | 60 | if (count < 0) |
54 | return count; | 61 | goto out_unlock; |
55 | 62 | ||
56 | if (copy_to_user(userbuf, buffer, count)) | 63 | if (copy_to_user(userbuf, bb->buffer, count)) { |
57 | return -EFAULT; | 64 | count = -EFAULT; |
65 | goto out_unlock; | ||
66 | } | ||
58 | 67 | ||
59 | pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); | 68 | pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); |
60 | 69 | ||
61 | *off = offs + count; | 70 | *off = offs + count; |
62 | 71 | ||
72 | out_unlock: | ||
73 | mutex_unlock(&bb->mutex); | ||
63 | return count; | 74 | return count; |
64 | } | 75 | } |
65 | 76 | ||
@@ -79,7 +90,7 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) | |||
79 | static ssize_t write(struct file *file, const char __user *userbuf, | 90 | static ssize_t write(struct file *file, const char __user *userbuf, |
80 | size_t bytes, loff_t *off) | 91 | size_t bytes, loff_t *off) |
81 | { | 92 | { |
82 | char *buffer = file->private_data; | 93 | struct bin_buffer *bb = file->private_data; |
83 | struct dentry *dentry = file->f_path.dentry; | 94 | struct dentry *dentry = file->f_path.dentry; |
84 | int size = dentry->d_inode->i_size; | 95 | int size = dentry->d_inode->i_size; |
85 | loff_t offs = *off; | 96 | loff_t offs = *off; |
@@ -92,25 +103,38 @@ static ssize_t write(struct file *file, const char __user *userbuf, | |||
92 | count = size - offs; | 103 | count = size - offs; |
93 | } | 104 | } |
94 | 105 | ||
95 | if (copy_from_user(buffer, userbuf, count)) | 106 | mutex_lock(&bb->mutex); |
96 | return -EFAULT; | 107 | |
108 | if (copy_from_user(bb->buffer, userbuf, count)) { | ||
109 | count = -EFAULT; | ||
110 | goto out_unlock; | ||
111 | } | ||
97 | 112 | ||
98 | count = flush_write(dentry, buffer, offs, count); | 113 | count = flush_write(dentry, bb->buffer, offs, count); |
99 | if (count > 0) | 114 | if (count > 0) |
100 | *off = offs + count; | 115 | *off = offs + count; |
116 | |||
117 | out_unlock: | ||
118 | mutex_unlock(&bb->mutex); | ||
101 | return count; | 119 | return count; |
102 | } | 120 | } |
103 | 121 | ||
104 | static int mmap(struct file *file, struct vm_area_struct *vma) | 122 | static int mmap(struct file *file, struct vm_area_struct *vma) |
105 | { | 123 | { |
124 | struct bin_buffer *bb = file->private_data; | ||
106 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 125 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
107 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 126 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
108 | struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); | 127 | struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); |
128 | int rc; | ||
109 | 129 | ||
110 | if (!attr->mmap) | 130 | if (!attr->mmap) |
111 | return -EINVAL; | 131 | return -EINVAL; |
112 | 132 | ||
113 | return attr->mmap(kobj, attr, vma); | 133 | mutex_lock(&bb->mutex); |
134 | rc = attr->mmap(kobj, attr, vma); | ||
135 | mutex_unlock(&bb->mutex); | ||
136 | |||
137 | return rc; | ||
114 | } | 138 | } |
115 | 139 | ||
116 | static int open(struct inode * inode, struct file * file) | 140 | static int open(struct inode * inode, struct file * file) |
@@ -118,6 +142,7 @@ static int open(struct inode * inode, struct file * file) | |||
118 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); | 142 | struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); |
119 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 143 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
120 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 144 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
145 | struct bin_buffer *bb = NULL; | ||
121 | int error = -EINVAL; | 146 | int error = -EINVAL; |
122 | 147 | ||
123 | if (!kobj || !attr) | 148 | if (!kobj || !attr) |
@@ -135,14 +160,22 @@ static int open(struct inode * inode, struct file * file) | |||
135 | goto Error; | 160 | goto Error; |
136 | 161 | ||
137 | error = -ENOMEM; | 162 | error = -ENOMEM; |
138 | file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); | 163 | bb = kzalloc(sizeof(*bb), GFP_KERNEL); |
139 | if (!file->private_data) | 164 | if (!bb) |
140 | goto Error; | 165 | goto Error; |
141 | 166 | ||
167 | bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); | ||
168 | if (!bb->buffer) | ||
169 | goto Error; | ||
170 | |||
171 | mutex_init(&bb->mutex); | ||
172 | file->private_data = bb; | ||
173 | |||
142 | error = 0; | 174 | error = 0; |
143 | goto Done; | 175 | goto Done; |
144 | 176 | ||
145 | Error: | 177 | Error: |
178 | kfree(bb); | ||
146 | module_put(attr->attr.owner); | 179 | module_put(attr->attr.owner); |
147 | Done: | 180 | Done: |
148 | if (error) | 181 | if (error) |
@@ -155,11 +188,12 @@ static int release(struct inode * inode, struct file * file) | |||
155 | struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); | 188 | struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); |
156 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; | 189 | struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; |
157 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; | 190 | struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; |
158 | u8 * buffer = file->private_data; | 191 | struct bin_buffer *bb = file->private_data; |
159 | 192 | ||
160 | kobject_put(kobj); | 193 | kobject_put(kobj); |
161 | module_put(attr->attr.owner); | 194 | module_put(attr->attr.owner); |
162 | kfree(buffer); | 195 | kfree(bb->buffer); |
196 | kfree(bb); | ||
163 | return 0; | 197 | return 0; |
164 | } | 198 | } |
165 | 199 | ||