[PATCH 039/437] libfs: switch to read iter and add copy helpers

Jens Axboe
2024-04-11T15:32:37Z

fs/debugfs/file.c | 44 +++++++++++++++++++++
fs/libfs.c | 87 +++++++++++++++++++++++++++++++++++++++++
include/linux/debugfs.h | 25 +++++++++++-
include/linux/fs.h | 7 +++-
4 files changed, 159 insertions(+), 4 deletions(-)

diff --git a/fs/debugfs/file.c b/fs/debugfs/file.c
index c6ff9084bc0f..31749d8c35f8 100644
--- a/fs/debugfs/file.c
+++ b/fs/debugfs/file.c
@@ -468,6 +468,20 @@ ssize_t debugfs_attr_read(struct file *file, char __user *buf,
}
EXPORT_SYMBOL_GPL(debugfs_attr_read);

+ssize_t debugfs_attr_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct dentry *dentry = F_DENTRY(iocb->ki_filp);
+ ssize_t ret;
+
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+ ret = simple_attr_read_iter(iocb, to);
+ debugfs_file_put(dentry);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(debugfs_attr_read_iter);
+
static ssize_t debugfs_attr_write_xsigned(struct file *file, const char __user *buf,
size_t len, loff_t *ppos, bool is_signed)
{
@@ -499,6 +513,36 @@ ssize_t debugfs_attr_write_signed(struct file *file, const char __user *buf,
}
EXPORT_SYMBOL_GPL(debugfs_attr_write_signed);

+static ssize_t debugfs_attr_write_iter_xsigned(struct kiocb *iocb,
+ struct iov_iter *from,
+ bool is_signed)
+{
+ struct dentry *dentry = F_DENTRY(iocb->ki_filp);
+ ssize_t ret;
+
+ ret = debugfs_file_get(dentry);
+ if (unlikely(ret))
+ return ret;
+ if (is_signed)
+ ret = simple_attr_write_iter_signed(iocb, from);
+ else
+ ret = simple_attr_write_iter(iocb, from);
+ debugfs_file_put(dentry);
+ return ret;
+}
+
+ssize_t debugfs_attr_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ return debugfs_attr_write_iter_xsigned(iocb, from, false);
+}
+EXPORT_SYMBOL_GPL(debugfs_attr_write_iter);
+
+ssize_t debugfs_attr_write_iter_signed(struct kiocb *iocb, struct iov_iter *from)
+{
+ return debugfs_attr_write_iter_xsigned(iocb, from, true);
+}
+EXPORT_SYMBOL_GPL(debugfs_attr_write_iter_signed);
+
static struct dentry *debugfs_create_mode_unsafe(const char *name, umode_t mode,
struct dentry *parent, void *value,
const struct file_operations *fops,
diff --git a/fs/libfs.c b/fs/libfs.c
index 074cfacc377f..90f79113e48b 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1365,6 +1365,93 @@ ssize_t simple_attr_read(struct file *file, char __user *buf,
}
EXPORT_SYMBOL_GPL(simple_attr_read);

+/* read from the buffer that is filled with the get function */
+ssize_t simple_attr_read_iter(struct kiocb *iocb, struct iov_iter *to)
+{
+ struct simple_attr *attr;
+ size_t size;
+ ssize_t ret;
+
+ attr = iocb->ki_filp->private_data;
+
+ if (!attr->get)
+ return -EACCES;
+
+ ret = mutex_lock_interruptible(&attr->mutex);
+ if (ret)
+ return ret;
+
+ if (iocb->ki_pos && attr->get_buf[0]) {
+ /* continued read */
+ size = strlen(attr->get_buf);
+ } else {
+ /* first read */
+ u64 val;
+ ret = attr->get(attr->data, &val);
+ if (ret)
+ goto out;
+
+ size = scnprintf(attr->get_buf, sizeof(attr->get_buf),
+ attr->fmt, (unsigned long long)val);
+ }
+
+ ret = simple_copy_to_iter(attr->get_buf, &iocb->ki_pos, size, to);
+out:
+ mutex_unlock(&attr->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(simple_attr_read_iter);
+
+static ssize_t __simple_write_iter(struct kiocb *iocb, struct iov_iter *from,
+ bool is_signed)
+{
+ struct simple_attr *attr;
+ unsigned long long val;
+ size_t len = iov_iter_count(from);
+ size_t size;
+ ssize_t ret;
+
+ attr = iocb->ki_filp->private_data;
+ if (!attr->set)
+ return -EACCES;
+
+ ret = mutex_lock_interruptible(&attr->mutex);
+ if (ret)
+ return ret;
+
+ ret = -EFAULT;
+ size = min(sizeof(attr->set_buf) - 1, len);
+ if (!copy_from_iter(attr->set_buf, size, from))
+ goto out;
+
+ attr->set_buf[size] = '\0';
+ if (is_signed)
+ ret = kstrtoll(attr->set_buf, 0, &val);
+ else
+ ret = kstrtoull(attr->set_buf, 0, &val);
+ if (ret)
+ goto out;
+ ret = attr->set(attr->data, val);
+ if (ret == 0)
+ ret = len; /* on success, claim we got the whole input */
+out:
+ mutex_unlock(&attr->mutex);
+ return ret;
+}
+
+ssize_t simple_attr_write_iter(struct kiocb *iocb, struct iov_iter *from)
+{
+ return __simple_write_iter(iocb, from, false);
+}
+EXPORT_SYMBOL_GPL(simple_attr_write_iter);
+
+ssize_t simple_attr_write_iter_signed(struct kiocb *iocb, struct iov_iter *from)
+
+{
+ return __simple_write_iter(iocb, from, true);
+}
+EXPORT_SYMBOL_GPL(simple_attr_write_iter_signed);
+
/* interpret the buffer as a number to call the set function with */
static ssize_t simple_attr_write_xsigned(struct file *file, const char __user *buf,
size_t len, loff_t *ppos, bool is_signed)
diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h
index ad417d079ec9..c6d189059d3d 100644
--- a/include/linux/debugfs.h
+++ b/include/linux/debugfs.h
@@ -55,8 +55,8 @@ static const struct file_operations __fops = { \
.owner = THIS_MODULE, \
.open = __fops ## _open, \
.release = simple_attr_release, \
- .read = debugfs_attr_read, \
- .write = (__is_signed) ? debugfs_attr_write_signed : debugfs_attr_write, \
+ .read_iter = debugfs_attr_read_iter, \
+ .write_iter= (__is_signed) ? debugfs_attr_write_iter_signed : debugfs_attr_write_iter, \
.llseek = no_llseek, \
}

@@ -106,10 +106,13 @@ void debugfs_file_put(struct dentry *dentry);

ssize_t debugfs_attr_read(struct file *file, char __user *buf,
size_t len, loff_t *ppos);
+ssize_t debugfs_attr_read_iter(struct kiocb *iocb, struct iov_iter *to);
ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos);
+ssize_t debugfs_attr_write_iter(struct kiocb *iocb, struct iov_iter *from);
ssize_t debugfs_attr_write_signed(struct file *file, const char __user *buf,
size_t len, loff_t *ppos);
+ssize_t debugfs_attr_write_iter_signed(struct kiocb *iocb, struct iov_iter *from);

struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
struct dentry *new_dir, const char *new_name);
@@ -271,6 +274,12 @@ static inline ssize_t debugfs_attr_read(struct file *file, char __user *buf,
return -ENODEV;
}

+static inline ssize_t debugfs_attr_read_iter(struct kiocb *iocb,
+ struct iov_iter *to)
+{
+ return -ENODEV;
+}
+
static inline ssize_t debugfs_attr_write(struct file *file,
const char __user *buf,
size_t len, loff_t *ppos)
@@ -278,6 +287,12 @@ static inline ssize_t debugfs_attr_write(struct file *file,
return -ENODEV;
}

+static inline ssize_t debugfs_attr_write_iter(struct kiocb *iocb,
+ struct iov_iter *from)
+{
+ return -ENODEV;
+}
+
static inline ssize_t debugfs_attr_write_signed(struct file *file,
const char __user *buf,
size_t len, loff_t *ppos)
@@ -285,6 +300,12 @@ static inline ssize_t debugfs_attr_write_signed(struct file *file,
return -ENODEV;
}

+static inline ssize_t debugfs_attr_write_iter_signed(struct kiocb *iocb,
+ struct iov_iter *from)
+{
+ return -ENODEV;
+}
+
static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
struct dentry *new_dir, char *new_name)
{
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 5806cea7a3ac..d15a79020efa 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3528,8 +3528,8 @@ static const struct file_operations __fops = { \
.owner = THIS_MODULE, \
.open = __fops ## _open, \
.release = simple_attr_release, \
- .read = simple_attr_read, \
- .write = (__is_signed) ? simple_attr_write_signed : simple_attr_write, \
+ .read_iter = simple_attr_read_iter, \
+ .write_iter = (__is_signed) ? simple_attr_write_iter_signed : simple_attr_write_iter, \
.llseek = generic_file_llseek, \
}

@@ -3555,6 +3555,9 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf,
size_t len, loff_t *ppos);
ssize_t simple_attr_write_signed(struct file *file, const char __user *buf,
size_t len, loff_t *ppos);
+ssize_t simple_attr_read_iter(struct kiocb *iocb, struct iov_iter *to);
+ssize_t simple_attr_write_iter(struct kiocb *iocb, struct iov_iter *from);
+ssize_t simple_attr_write_iter_signed(struct kiocb *iocb, struct iov_iter *from);

struct ctl_table;
int __init list_bdev_fs_names(char *buf, size_t size);
--
2.43.0