diff options
author | Tejun Heo <tj@kernel.org> | 2013-10-01 17:42:01 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-10-05 20:21:03 -0400 |
commit | 8ef445f0807457dd7d158e43d9e8f9568c47910d (patch) | |
tree | b3c270a7125a20112a365cf5124fd1a23d91916b | |
parent | bcafe4eea3e58a60e9c2c63781700a9ab1d70f93 (diff) |
sysfs: use transient write buffer
There isn't much to be gained by keeping around kernel buffer while a
file is open especially as the read path planned to be converted to
use seq_file and won't use the buffer. This patch makes
sysfs_write_file() use per-write transient buffer instead of
sysfs_open_file->page.
This simplifies the write path, enables removing sysfs_open_file->page
once read path is updated and will help merging bin file write path
which already requires the use of a transient buffer due to a locking
order issue.
As the function comments of flush_write_buffer() and
sysfs_write_buffer() are being updated anyway, reformat them so that
they're more conventional.
v2: Use min_t() instead of min() in sysfs_write_file() to avoid build
warning on arm. Reported by build test robot.
Signed-off-by: Tejun Heo <tj@kernel.org>
Cc: kbuild test robot <fengguang.wu@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | fs/sysfs/file.c | 114 |
1 files changed, 52 insertions, 62 deletions
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index af6e9092a679..53cc096e6a1b 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -162,92 +162,82 @@ out: | |||
162 | } | 162 | } |
163 | 163 | ||
164 | /** | 164 | /** |
165 | * fill_write_buffer - copy buffer from userspace. | 165 | * flush_write_buffer - push buffer to kobject |
166 | * @of: open file struct. | 166 | * @of: open file |
167 | * @buf: data from user. | 167 | * @buf: data buffer for file |
168 | * @count: number of bytes in @userbuf. | 168 | * @count: number of bytes |
169 | * | 169 | * |
170 | * Allocate @of->page if it hasn't been already, then copy the | 170 | * Get the correct pointers for the kobject and the attribute we're dealing |
171 | * user-supplied buffer into it. | 171 | * with, then call the store() method for it with @buf. |
172 | */ | 172 | */ |
173 | static int fill_write_buffer(struct sysfs_open_file *of, | 173 | static int flush_write_buffer(struct sysfs_open_file *of, char *buf, |
174 | const char __user *buf, size_t count) | 174 | size_t count) |
175 | { | ||
176 | int error; | ||
177 | |||
178 | if (!of->page) | ||
179 | of->page = (char *)get_zeroed_page(GFP_KERNEL); | ||
180 | if (!of->page) | ||
181 | return -ENOMEM; | ||
182 | |||
183 | if (count >= PAGE_SIZE) | ||
184 | count = PAGE_SIZE - 1; | ||
185 | error = copy_from_user(of->page, buf, count); | ||
186 | |||
187 | /* | ||
188 | * If buf is assumed to contain a string, terminate it by \0, so | ||
189 | * e.g. sscanf() can scan the string easily. | ||
190 | */ | ||
191 | of->page[count] = 0; | ||
192 | return error ? -EFAULT : count; | ||
193 | } | ||
194 | |||
195 | /** | ||
196 | * flush_write_buffer - push buffer to kobject. | ||
197 | * @of: open file | ||
198 | * @count: number of bytes | ||
199 | * | ||
200 | * Get the correct pointers for the kobject and the attribute we're | ||
201 | * dealing with, then call the store() method for the attribute, | ||
202 | * passing the buffer that we acquired in fill_write_buffer(). | ||
203 | */ | ||
204 | static int flush_write_buffer(struct sysfs_open_file *of, size_t count) | ||
205 | { | 175 | { |
206 | struct kobject *kobj = of->sd->s_parent->s_dir.kobj; | 176 | struct kobject *kobj = of->sd->s_parent->s_dir.kobj; |
207 | const struct sysfs_ops *ops; | 177 | const struct sysfs_ops *ops; |
208 | int rc; | 178 | int rc = 0; |
209 | 179 | ||
210 | /* need @of->sd for attr and ops, its parent for kobj */ | 180 | /* |
211 | if (!sysfs_get_active(of->sd)) | 181 | * Need @of->sd for attr and ops, its parent for kobj. @of->mutex |
182 | * nests outside active ref and is just to ensure that the ops | ||
183 | * aren't called concurrently for the same open file. | ||
184 | */ | ||
185 | mutex_lock(&of->mutex); | ||
186 | if (!sysfs_get_active(of->sd)) { | ||
187 | mutex_unlock(&of->mutex); | ||
212 | return -ENODEV; | 188 | return -ENODEV; |
189 | } | ||
213 | 190 | ||
214 | ops = sysfs_file_ops(of->sd); | 191 | ops = sysfs_file_ops(of->sd); |
215 | rc = ops->store(kobj, of->sd->s_attr.attr, of->page, count); | 192 | rc = ops->store(kobj, of->sd->s_attr.attr, buf, count); |
216 | 193 | ||
217 | sysfs_put_active(of->sd); | 194 | sysfs_put_active(of->sd); |
195 | mutex_unlock(&of->mutex); | ||
218 | 196 | ||
219 | return rc; | 197 | return rc; |
220 | } | 198 | } |
221 | 199 | ||
222 | /** | 200 | /** |
223 | * sysfs_write_file - write an attribute. | 201 | * sysfs_write_file - write an attribute |
224 | * @file: file pointer | 202 | * @file: file pointer |
225 | * @buf: data to write | 203 | * @user_buf: data to write |
226 | * @count: number of bytes | 204 | * @count: number of bytes |
227 | * @ppos: starting offset | 205 | * @ppos: starting offset |
228 | * | 206 | * |
229 | * Similar to sysfs_read_file(), though working in the opposite direction. | 207 | * Copy data in from userland and pass it to the matching |
230 | * We allocate and fill the data from the user in fill_write_buffer(), | 208 | * sysfs_ops->store() by invoking flush_write_buffer(). |
231 | * then push it to the kobject in flush_write_buffer(). | 209 | * |
232 | * There is no easy way for us to know if userspace is only doing a partial | 210 | * There is no easy way for us to know if userspace is only doing a partial |
233 | * write, so we don't support them. We expect the entire buffer to come | 211 | * write, so we don't support them. We expect the entire buffer to come on |
234 | * on the first write. | 212 | * the first write. Hint: if you're writing a value, first read the file, |
235 | * Hint: if you're writing a value, first read the file, modify only the | 213 | * modify only the the value you're changing, then write entire buffer |
236 | * the value you're changing, then write entire buffer back. | 214 | * back. |
237 | */ | 215 | */ |
238 | static ssize_t sysfs_write_file(struct file *file, const char __user *buf, | 216 | static ssize_t sysfs_write_file(struct file *file, const char __user *user_buf, |
239 | size_t count, loff_t *ppos) | 217 | size_t count, loff_t *ppos) |
240 | { | 218 | { |
241 | struct sysfs_open_file *of = file->private_data; | 219 | struct sysfs_open_file *of = file->private_data; |
242 | ssize_t len; | 220 | ssize_t len = min_t(size_t, count, PAGE_SIZE - 1); |
221 | char *buf; | ||
243 | 222 | ||
244 | mutex_lock(&of->mutex); | 223 | if (!len) |
245 | len = fill_write_buffer(of, buf, count); | 224 | return 0; |
246 | if (len > 0) | 225 | |
247 | len = flush_write_buffer(of, len); | 226 | buf = kmalloc(len + 1, GFP_KERNEL); |
227 | if (!buf) | ||
228 | return -ENOMEM; | ||
229 | |||
230 | if (copy_from_user(buf, user_buf, len)) { | ||
231 | len = -EFAULT; | ||
232 | goto out_free; | ||
233 | } | ||
234 | buf[len] = '\0'; /* guarantee string termination */ | ||
235 | |||
236 | len = flush_write_buffer(of, buf, len); | ||
248 | if (len > 0) | 237 | if (len > 0) |
249 | *ppos += len; | 238 | *ppos += len; |
250 | mutex_unlock(&of->mutex); | 239 | out_free: |
240 | kfree(buf); | ||
251 | return len; | 241 | return len; |
252 | } | 242 | } |
253 | 243 | ||