If we write to a file that implements ->write_iter there is no need
to change the address limit if we send a kvec down.  Implement that
case, and prefer it over using plain ->write with a changed address
limit if available.

Signed-off-by: Christoph Hellwig <h...@lst.de>
---
 fs/read_write.c | 34 ++++++++++++++++++++++------------
 1 file changed, 22 insertions(+), 12 deletions(-)

diff --git a/fs/read_write.c b/fs/read_write.c
index 3bcb084f160de..8cfca5f8fc3ce 100644
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -489,10 +489,9 @@ static ssize_t new_sync_write(struct file *filp, const 
char __user *buf, size_t
 }
 
 /* caller is responsible for file_start_write/file_end_write */
-ssize_t __kernel_write(struct file *file, const void *buf, size_t count, 
loff_t *pos)
+ssize_t __kernel_write(struct file *file, const void *buf, size_t count,
+               loff_t *pos)
 {
-       mm_segment_t old_fs;
-       const char __user *p;
        ssize_t ret;
 
        if (!(file->f_mode & FMODE_WRITE))
@@ -500,18 +499,29 @@ ssize_t __kernel_write(struct file *file, const void 
*buf, size_t count, loff_t
        if (!(file->f_mode & FMODE_CAN_WRITE))
                return -EINVAL;
 
-       old_fs = get_fs();
-       set_fs(KERNEL_DS);
-       p = (__force const char __user *)buf;
        if (count > MAX_RW_COUNT)
                count =  MAX_RW_COUNT;
-       if (file->f_op->write)
-               ret = file->f_op->write(file, p, count, pos);
-       else if (file->f_op->write_iter)
-               ret = new_sync_write(file, p, count, pos);
-       else
+       if (file->f_op->write_iter) {
+               struct kvec iov = { .iov_base = (void *)buf, .iov_len = count };
+               struct kiocb kiocb;
+               struct iov_iter iter;
+
+               init_sync_kiocb(&kiocb, file);
+               kiocb.ki_pos = *pos;
+               iov_iter_kvec(&iter, WRITE, &iov, 1, count);
+               ret = file->f_op->write_iter(&kiocb, &iter);
+               if (ret > 0)
+                       *pos = kiocb.ki_pos;
+       } else if (file->f_op->write) {
+               mm_segment_t old_fs = get_fs();
+
+               set_fs(KERNEL_DS);
+               ret = file->f_op->write(file, (__force const char __user *)buf,
+                               count, pos);
+               set_fs(old_fs);
+       } else {
                ret = -EINVAL;
-       set_fs(old_fs);
+       }
        if (ret > 0) {
                fsnotify_modify(file);
                add_wchar(current, ret);
-- 
2.26.2

Reply via email to