aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--fs/kernfs/file.c30
-rw-r--r--fs/sysfs/file.c32
2 files changed, 49 insertions, 13 deletions
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c
index 70186e2e692a..697390ea47b8 100644
--- a/fs/kernfs/file.c
+++ b/fs/kernfs/file.c
@@ -189,13 +189,16 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
189 const struct kernfs_ops *ops; 189 const struct kernfs_ops *ops;
190 char *buf; 190 char *buf;
191 191
192 buf = kmalloc(len, GFP_KERNEL); 192 buf = of->prealloc_buf;
193 if (!buf)
194 buf = kmalloc(len, GFP_KERNEL);
193 if (!buf) 195 if (!buf)
194 return -ENOMEM; 196 return -ENOMEM;
195 197
196 /* 198 /*
197 * @of->mutex nests outside active ref and is primarily to ensure that 199 * @of->mutex nests outside active ref and is used both to ensure that
198 * the ops aren't called concurrently for the same open file. 200 * the ops aren't called concurrently for the same open file, and
201 * to provide exclusive access to ->prealloc_buf (when that exists).
199 */ 202 */
200 mutex_lock(&of->mutex); 203 mutex_lock(&of->mutex);
201 if (!kernfs_get_active(of->kn)) { 204 if (!kernfs_get_active(of->kn)) {
@@ -210,21 +213,22 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of,
210 else 213 else
211 len = -EINVAL; 214 len = -EINVAL;
212 215
213 kernfs_put_active(of->kn);
214 mutex_unlock(&of->mutex);
215
216 if (len < 0) 216 if (len < 0)
217 goto out_free; 217 goto out_unlock;
218 218
219 if (copy_to_user(user_buf, buf, len)) { 219 if (copy_to_user(user_buf, buf, len)) {
220 len = -EFAULT; 220 len = -EFAULT;
221 goto out_free; 221 goto out_unlock;
222 } 222 }
223 223
224 *ppos += len; 224 *ppos += len;
225 225
226 out_unlock:
227 kernfs_put_active(of->kn);
228 mutex_unlock(&of->mutex);
226 out_free: 229 out_free:
227 kfree(buf); 230 if (buf != of->prealloc_buf)
231 kfree(buf);
228 return len; 232 return len;
229} 233}
230 234
@@ -690,6 +694,14 @@ static int kernfs_fop_open(struct inode *inode, struct file *file)
690 */ 694 */
691 of->atomic_write_len = ops->atomic_write_len; 695 of->atomic_write_len = ops->atomic_write_len;
692 696
697 error = -EINVAL;
698 /*
699 * ->seq_show is incompatible with ->prealloc,
700 * as seq_read does its own allocation.
701 * ->read must be used instead.
702 */
703 if (ops->prealloc && ops->seq_show)
704 goto err_free;
693 if (ops->prealloc) { 705 if (ops->prealloc) {
694 int len = of->atomic_write_len ?: PAGE_SIZE; 706 int len = of->atomic_write_len ?: PAGE_SIZE;
695 of->prealloc_buf = kmalloc(len + 1, GFP_KERNEL); 707 of->prealloc_buf = kmalloc(len + 1, GFP_KERNEL);
diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c
index 4ad3721a991c..dfe928a9540f 100644
--- a/fs/sysfs/file.c
+++ b/fs/sysfs/file.c
@@ -102,6 +102,22 @@ static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf,
102 return battr->read(of->file, kobj, battr, buf, pos, count); 102 return battr->read(of->file, kobj, battr, buf, pos, count);
103} 103}
104 104
105/* kernfs read callback for regular sysfs files with pre-alloc */
106static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
107 size_t count, loff_t pos)
108{
109 const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
110 struct kobject *kobj = of->kn->parent->priv;
111
112 /*
113 * If buf != of->prealloc_buf, we don't know how
114 * large it is, so cannot safely pass it to ->show
115 */
116 if (pos || WARN_ON_ONCE(buf != of->prealloc_buf))
117 return 0;
118 return ops->show(kobj, of->kn->priv, buf);
119}
120
105/* kernfs write callback for regular sysfs files */ 121/* kernfs write callback for regular sysfs files */
106static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf, 122static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
107 size_t count, loff_t pos) 123 size_t count, loff_t pos)
@@ -184,13 +200,18 @@ static const struct kernfs_ops sysfs_file_kfops_rw = {
184 .write = sysfs_kf_write, 200 .write = sysfs_kf_write,
185}; 201};
186 202
203static const struct kernfs_ops sysfs_prealloc_kfops_ro = {
204 .read = sysfs_kf_read,
205 .prealloc = true,
206};
207
187static const struct kernfs_ops sysfs_prealloc_kfops_wo = { 208static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
188 .write = sysfs_kf_write, 209 .write = sysfs_kf_write,
189 .prealloc = true, 210 .prealloc = true,
190}; 211};
191 212
192static const struct kernfs_ops sysfs_prealloc_kfops_rw = { 213static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
193 .seq_show = sysfs_kf_seq_show, 214 .read = sysfs_kf_read,
194 .write = sysfs_kf_write, 215 .write = sysfs_kf_write,
195 .prealloc = true, 216 .prealloc = true,
196}; 217};
@@ -238,9 +259,12 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
238 ops = &sysfs_prealloc_kfops_rw; 259 ops = &sysfs_prealloc_kfops_rw;
239 else 260 else
240 ops = &sysfs_file_kfops_rw; 261 ops = &sysfs_file_kfops_rw;
241 } else if (sysfs_ops->show) 262 } else if (sysfs_ops->show) {
242 ops = &sysfs_file_kfops_ro; 263 if (mode & SYSFS_PREALLOC)
243 else if (sysfs_ops->store) { 264 ops = &sysfs_prealloc_kfops_ro;
265 else
266 ops = &sysfs_file_kfops_ro;
267 } else if (sysfs_ops->store) {
244 if (mode & SYSFS_PREALLOC) 268 if (mode & SYSFS_PREALLOC)
245 ops = &sysfs_prealloc_kfops_wo; 269 ops = &sysfs_prealloc_kfops_wo;
246 else 270 else