aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-10-01 17:42:01 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-05 20:21:03 -0400
commit8ef445f0807457dd7d158e43d9e8f9568c47910d (patch)
treeb3c270a7125a20112a365cf5124fd1a23d91916b
parentbcafe4eea3e58a60e9c2c63781700a9ab1d70f93 (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.c114
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 */
173static int fill_write_buffer(struct sysfs_open_file *of, 173static 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 */
204static 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 */
238static ssize_t sysfs_write_file(struct file *file, const char __user *buf, 216static 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); 239out_free:
240 kfree(buf);
251 return len; 241 return len;
252} 242}
253 243