summaryrefslogtreecommitdiff
path: root/fs/sysfs
diff options
context:
space:
mode:
authorNeilBrown <neilb@suse.de>2014-10-14 16:57:26 +1100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-11-07 10:54:38 -0800
commit4ef67a8c95f32ed0c8c6ed5fe01d1dd16358350e (patch)
tree82e66c1da3bbe63345edb27d7e64e3245df729ee /fs/sysfs
parent2b75869bba676c248d8d25ae6d2bd9221dfffdb6 (diff)
downloadlwn-4ef67a8c95f32ed0c8c6ed5fe01d1dd16358350e.tar.gz
lwn-4ef67a8c95f32ed0c8c6ed5fe01d1dd16358350e.zip
sysfs/kernfs: make read requests on pre-alloc files use the buffer.
To match the previous patch which used the pre-alloc buffer for writes, this patch causes reads to use the same buffer. This is not strictly necessary as the current seq_read() will allocate on first read, so user-space can trigger the required pre-alloc. But consistency is valuable. The read function is somewhat simpler than seq_read() and, for example, does not support reading from an offset into the file: reads must be at the start of the file. As seq_read() does not use the prealloc buffer, ->seq_show is incompatible with ->prealloc and caused an EINVAL return from open(). sysfs code which calls into kernfs always chooses the correct function. As the buffer is shared with writes and other reads, the mutex is extended to cover the copy_to_user. Signed-off-by: NeilBrown <neilb@suse.de> Reviewed-by: Tejun Heo <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'fs/sysfs')
-rw-r--r--fs/sysfs/file.c32
1 files changed, 28 insertions, 4 deletions
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,
return battr->read(of->file, kobj, battr, buf, pos, count);
}
+/* kernfs read callback for regular sysfs files with pre-alloc */
+static ssize_t sysfs_kf_read(struct kernfs_open_file *of, char *buf,
+ size_t count, loff_t pos)
+{
+ const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
+ struct kobject *kobj = of->kn->parent->priv;
+
+ /*
+ * If buf != of->prealloc_buf, we don't know how
+ * large it is, so cannot safely pass it to ->show
+ */
+ if (pos || WARN_ON_ONCE(buf != of->prealloc_buf))
+ return 0;
+ return ops->show(kobj, of->kn->priv, buf);
+}
+
/* kernfs write callback for regular sysfs files */
static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf,
size_t count, loff_t pos)
@@ -184,13 +200,18 @@ static const struct kernfs_ops sysfs_file_kfops_rw = {
.write = sysfs_kf_write,
};
+static const struct kernfs_ops sysfs_prealloc_kfops_ro = {
+ .read = sysfs_kf_read,
+ .prealloc = true,
+};
+
static const struct kernfs_ops sysfs_prealloc_kfops_wo = {
.write = sysfs_kf_write,
.prealloc = true,
};
static const struct kernfs_ops sysfs_prealloc_kfops_rw = {
- .seq_show = sysfs_kf_seq_show,
+ .read = sysfs_kf_read,
.write = sysfs_kf_write,
.prealloc = true,
};
@@ -238,9 +259,12 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent,
ops = &sysfs_prealloc_kfops_rw;
else
ops = &sysfs_file_kfops_rw;
- } else if (sysfs_ops->show)
- ops = &sysfs_file_kfops_ro;
- else if (sysfs_ops->store) {
+ } else if (sysfs_ops->show) {
+ if (mode & SYSFS_PREALLOC)
+ ops = &sysfs_prealloc_kfops_ro;
+ else
+ ops = &sysfs_file_kfops_ro;
+ } else if (sysfs_ops->store) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_wo;
else