diff options
-rw-r--r-- | fs/kernfs/file.c | 45 | ||||
-rw-r--r-- | fs/sysfs/file.c | 31 | ||||
-rw-r--r-- | include/linux/kernfs.h | 8 | ||||
-rw-r--r-- | include/linux/sysfs.h | 9 |
4 files changed, 71 insertions, 22 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 4429d6d9217f..70186e2e692a 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c | |||
@@ -106,7 +106,7 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) | |||
106 | const struct kernfs_ops *ops; | 106 | const struct kernfs_ops *ops; |
107 | 107 | ||
108 | /* | 108 | /* |
109 | * @of->mutex nests outside active ref and is just to ensure that | 109 | * @of->mutex nests outside active ref and is primarily to ensure that |
110 | * the ops aren't called concurrently for the same open file. | 110 | * the ops aren't called concurrently for the same open file. |
111 | */ | 111 | */ |
112 | mutex_lock(&of->mutex); | 112 | mutex_lock(&of->mutex); |
@@ -194,7 +194,7 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of, | |||
194 | return -ENOMEM; | 194 | return -ENOMEM; |
195 | 195 | ||
196 | /* | 196 | /* |
197 | * @of->mutex nests outside active ref and is just to ensure that | 197 | * @of->mutex nests outside active ref and is primarily to ensure that |
198 | * the ops aren't called concurrently for the same open file. | 198 | * the ops aren't called concurrently for the same open file. |
199 | */ | 199 | */ |
200 | mutex_lock(&of->mutex); | 200 | mutex_lock(&of->mutex); |
@@ -278,19 +278,16 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, | |||
278 | len = min_t(size_t, count, PAGE_SIZE); | 278 | len = min_t(size_t, count, PAGE_SIZE); |
279 | } | 279 | } |
280 | 280 | ||
281 | buf = kmalloc(len + 1, GFP_KERNEL); | 281 | buf = of->prealloc_buf; |
282 | if (!buf) | ||
283 | buf = kmalloc(len + 1, GFP_KERNEL); | ||
282 | if (!buf) | 284 | if (!buf) |
283 | return -ENOMEM; | 285 | return -ENOMEM; |
284 | 286 | ||
285 | if (copy_from_user(buf, user_buf, len)) { | ||
286 | len = -EFAULT; | ||
287 | goto out_free; | ||
288 | } | ||
289 | buf[len] = '\0'; /* guarantee string termination */ | ||
290 | |||
291 | /* | 287 | /* |
292 | * @of->mutex nests outside active ref and is just to ensure that | 288 | * @of->mutex nests outside active ref and is used both to ensure that |
293 | * the ops aren't called concurrently for the same open file. | 289 | * the ops aren't called concurrently for the same open file, and |
290 | * to provide exclusive access to ->prealloc_buf (when that exists). | ||
294 | */ | 291 | */ |
295 | mutex_lock(&of->mutex); | 292 | mutex_lock(&of->mutex); |
296 | if (!kernfs_get_active(of->kn)) { | 293 | if (!kernfs_get_active(of->kn)) { |
@@ -299,19 +296,27 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, | |||
299 | goto out_free; | 296 | goto out_free; |
300 | } | 297 | } |
301 | 298 | ||
299 | if (copy_from_user(buf, user_buf, len)) { | ||
300 | len = -EFAULT; | ||
301 | goto out_unlock; | ||
302 | } | ||
303 | buf[len] = '\0'; /* guarantee string termination */ | ||
304 | |||
302 | ops = kernfs_ops(of->kn); | 305 | ops = kernfs_ops(of->kn); |
303 | if (ops->write) | 306 | if (ops->write) |
304 | len = ops->write(of, buf, len, *ppos); | 307 | len = ops->write(of, buf, len, *ppos); |
305 | else | 308 | else |
306 | len = -EINVAL; | 309 | len = -EINVAL; |
307 | 310 | ||
308 | kernfs_put_active(of->kn); | ||
309 | mutex_unlock(&of->mutex); | ||
310 | |||
311 | if (len > 0) | 311 | if (len > 0) |
312 | *ppos += len; | 312 | *ppos += len; |
313 | |||
314 | out_unlock: | ||
315 | kernfs_put_active(of->kn); | ||
316 | mutex_unlock(&of->mutex); | ||
313 | out_free: | 317 | out_free: |
314 | kfree(buf); | 318 | if (buf != of->prealloc_buf) |
319 | kfree(buf); | ||
315 | return len; | 320 | return len; |
316 | } | 321 | } |
317 | 322 | ||
@@ -685,6 +690,14 @@ static int kernfs_fop_open(struct inode *inode, struct file *file) | |||
685 | */ | 690 | */ |
686 | of->atomic_write_len = ops->atomic_write_len; | 691 | of->atomic_write_len = ops->atomic_write_len; |
687 | 692 | ||
693 | if (ops->prealloc) { | ||
694 | int len = of->atomic_write_len ?: PAGE_SIZE; | ||
695 | of->prealloc_buf = kmalloc(len + 1, GFP_KERNEL); | ||
696 | error = -ENOMEM; | ||
697 | if (!of->prealloc_buf) | ||
698 | goto err_free; | ||
699 | } | ||
700 | |||
688 | /* | 701 | /* |
689 | * Always instantiate seq_file even if read access doesn't use | 702 | * Always instantiate seq_file even if read access doesn't use |
690 | * seq_file or is not requested. This unifies private data access | 703 | * seq_file or is not requested. This unifies private data access |
@@ -715,6 +728,7 @@ static int kernfs_fop_open(struct inode *inode, struct file *file) | |||
715 | err_close: | 728 | err_close: |
716 | seq_release(inode, file); | 729 | seq_release(inode, file); |
717 | err_free: | 730 | err_free: |
731 | kfree(of->prealloc_buf); | ||
718 | kfree(of); | 732 | kfree(of); |
719 | err_out: | 733 | err_out: |
720 | kernfs_put_active(kn); | 734 | kernfs_put_active(kn); |
@@ -728,6 +742,7 @@ static int kernfs_fop_release(struct inode *inode, struct file *filp) | |||
728 | 742 | ||
729 | kernfs_put_open_node(kn, of); | 743 | kernfs_put_open_node(kn, of); |
730 | seq_release(inode, filp); | 744 | seq_release(inode, filp); |
745 | kfree(of->prealloc_buf); | ||
731 | kfree(of); | 746 | kfree(of); |
732 | 747 | ||
733 | return 0; | 748 | return 0; |
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 589abee16a39..4ad3721a991c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c | |||
@@ -184,6 +184,17 @@ static const struct kernfs_ops sysfs_file_kfops_rw = { | |||
184 | .write = sysfs_kf_write, | 184 | .write = sysfs_kf_write, |
185 | }; | 185 | }; |
186 | 186 | ||
187 | static const struct kernfs_ops sysfs_prealloc_kfops_wo = { | ||
188 | .write = sysfs_kf_write, | ||
189 | .prealloc = true, | ||
190 | }; | ||
191 | |||
192 | static const struct kernfs_ops sysfs_prealloc_kfops_rw = { | ||
193 | .seq_show = sysfs_kf_seq_show, | ||
194 | .write = sysfs_kf_write, | ||
195 | .prealloc = true, | ||
196 | }; | ||
197 | |||
187 | static const struct kernfs_ops sysfs_bin_kfops_ro = { | 198 | static const struct kernfs_ops sysfs_bin_kfops_ro = { |
188 | .read = sysfs_kf_bin_read, | 199 | .read = sysfs_kf_bin_read, |
189 | }; | 200 | }; |
@@ -222,13 +233,19 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent, | |||
222 | kobject_name(kobj))) | 233 | kobject_name(kobj))) |
223 | return -EINVAL; | 234 | return -EINVAL; |
224 | 235 | ||
225 | if (sysfs_ops->show && sysfs_ops->store) | 236 | if (sysfs_ops->show && sysfs_ops->store) { |
226 | ops = &sysfs_file_kfops_rw; | 237 | if (mode & SYSFS_PREALLOC) |
227 | else if (sysfs_ops->show) | 238 | ops = &sysfs_prealloc_kfops_rw; |
239 | else | ||
240 | ops = &sysfs_file_kfops_rw; | ||
241 | } else if (sysfs_ops->show) | ||
228 | ops = &sysfs_file_kfops_ro; | 242 | ops = &sysfs_file_kfops_ro; |
229 | else if (sysfs_ops->store) | 243 | else if (sysfs_ops->store) { |
230 | ops = &sysfs_file_kfops_wo; | 244 | if (mode & SYSFS_PREALLOC) |
231 | else | 245 | ops = &sysfs_prealloc_kfops_wo; |
246 | else | ||
247 | ops = &sysfs_file_kfops_wo; | ||
248 | } else | ||
232 | ops = &sysfs_file_kfops_empty; | 249 | ops = &sysfs_file_kfops_empty; |
233 | 250 | ||
234 | size = PAGE_SIZE; | 251 | size = PAGE_SIZE; |
@@ -253,7 +270,7 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent, | |||
253 | if (!attr->ignore_lockdep) | 270 | if (!attr->ignore_lockdep) |
254 | key = attr->key ?: (struct lock_class_key *)&attr->skey; | 271 | key = attr->key ?: (struct lock_class_key *)&attr->skey; |
255 | #endif | 272 | #endif |
256 | kn = __kernfs_create_file(parent, attr->name, mode, size, ops, | 273 | kn = __kernfs_create_file(parent, attr->name, mode & 0777, size, ops, |
257 | (void *)attr, ns, true, key); | 274 | (void *)attr, ns, true, key); |
258 | if (IS_ERR(kn)) { | 275 | if (IS_ERR(kn)) { |
259 | if (PTR_ERR(kn) == -EEXIST) | 276 | if (PTR_ERR(kn) == -EEXIST) |
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 30faf797c2c3..d4e01b358341 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h | |||
@@ -179,6 +179,7 @@ struct kernfs_open_file { | |||
179 | struct mutex mutex; | 179 | struct mutex mutex; |
180 | int event; | 180 | int event; |
181 | struct list_head list; | 181 | struct list_head list; |
182 | char *prealloc_buf; | ||
182 | 183 | ||
183 | size_t atomic_write_len; | 184 | size_t atomic_write_len; |
184 | bool mmapped; | 185 | bool mmapped; |
@@ -214,6 +215,13 @@ struct kernfs_ops { | |||
214 | * larger ones are rejected with -E2BIG. | 215 | * larger ones are rejected with -E2BIG. |
215 | */ | 216 | */ |
216 | size_t atomic_write_len; | 217 | size_t atomic_write_len; |
218 | /* | ||
219 | * "prealloc" causes a buffer to be allocated at open for | ||
220 | * all read/write requests. As ->seq_show uses seq_read() | ||
221 | * which does its own allocation, it is incompatible with | ||
222 | * ->prealloc. Provide ->read and ->write with ->prealloc. | ||
223 | */ | ||
224 | bool prealloc; | ||
217 | ssize_t (*write)(struct kernfs_open_file *of, char *buf, size_t bytes, | 225 | ssize_t (*write)(struct kernfs_open_file *of, char *buf, size_t bytes, |
218 | loff_t off); | 226 | loff_t off); |
219 | 227 | ||
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index f97d0dbb59fa..ddad16148bd6 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h | |||
@@ -70,6 +70,8 @@ struct attribute_group { | |||
70 | * for examples.. | 70 | * for examples.. |
71 | */ | 71 | */ |
72 | 72 | ||
73 | #define SYSFS_PREALLOC 010000 | ||
74 | |||
73 | #define __ATTR(_name, _mode, _show, _store) { \ | 75 | #define __ATTR(_name, _mode, _show, _store) { \ |
74 | .attr = {.name = __stringify(_name), \ | 76 | .attr = {.name = __stringify(_name), \ |
75 | .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ | 77 | .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \ |
@@ -77,6 +79,13 @@ struct attribute_group { | |||
77 | .store = _store, \ | 79 | .store = _store, \ |
78 | } | 80 | } |
79 | 81 | ||
82 | #define __ATTR_PREALLOC(_name, _mode, _show, _store) { \ | ||
83 | .attr = {.name = __stringify(_name), \ | ||
84 | .mode = SYSFS_PREALLOC | VERIFY_OCTAL_PERMISSIONS(_mode) },\ | ||
85 | .show = _show, \ | ||
86 | .store = _store, \ | ||
87 | } | ||
88 | |||
80 | #define __ATTR_RO(_name) { \ | 89 | #define __ATTR_RO(_name) { \ |
81 | .attr = { .name = __stringify(_name), .mode = S_IRUGO }, \ | 90 | .attr = { .name = __stringify(_name), .mode = S_IRUGO }, \ |
82 | .show = _name##_show, \ | 91 | .show = _name##_show, \ |