Skip to content

Commit 048832a

Browse files
Taotao Chenbrauner
authored andcommitted
drm/i915: Refactor shmem_pwrite() to use kiocb and write_iter
Refactors shmem_pwrite() to replace the ->write_begin/end logic with a write_iter-based implementation using kiocb and iov_iter. While kernel_write() was considered, it caused about 50% performance regression. vfs_write() is not exported for kernel use. Therefore, file->f_op->write_iter() is called directly with a synchronously initialized kiocb to preserve performance and remove write_begin usage. Performance results use gem_pwrite on Intel CPU i7-10700 (average of 10 runs): - ./gem_pwrite --run-subtest bench -s 16384 Before: 0.205s, After: 0.214s - ./gem_pwrite --run-subtest bench -s 524288 Before: 6.1021s, After: 4.8047s Part of a series refactoring address_space_operations write_begin and write_end callbacks to use struct kiocb for passing write context and flags. Signed-off-by: Taotao Chen <chentaotao@didiglobal.com> Link: https://lore.kernel.org/20250716093559.217344-3-chentaotao@didiglobal.com Signed-off-by: Christian Brauner <brauner@kernel.org>
1 parent e7b840f commit 048832a

1 file changed

Lines changed: 22 additions & 60 deletions

File tree

drivers/gpu/drm/i915/gem/i915_gem_shmem.c

Lines changed: 22 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <linux/pagevec.h>
77
#include <linux/shmem_fs.h>
88
#include <linux/swap.h>
9+
#include <linux/uio.h>
910

1011
#include <drm/drm_cache.h>
1112

@@ -400,12 +401,12 @@ static int
400401
shmem_pwrite(struct drm_i915_gem_object *obj,
401402
const struct drm_i915_gem_pwrite *arg)
402403
{
403-
struct address_space *mapping = obj->base.filp->f_mapping;
404-
const struct address_space_operations *aops = mapping->a_ops;
405404
char __user *user_data = u64_to_user_ptr(arg->data_ptr);
406-
u64 remain;
407-
loff_t pos;
408-
unsigned int pg;
405+
struct file *file = obj->base.filp;
406+
struct kiocb kiocb;
407+
struct iov_iter iter;
408+
ssize_t written;
409+
u64 size = arg->size;
409410

410411
/* Caller already validated user args */
411412
GEM_BUG_ON(!access_ok(user_data, arg->size));
@@ -428,63 +429,24 @@ shmem_pwrite(struct drm_i915_gem_object *obj,
428429
if (obj->mm.madv != I915_MADV_WILLNEED)
429430
return -EFAULT;
430431

431-
/*
432-
* Before the pages are instantiated the object is treated as being
433-
* in the CPU domain. The pages will be clflushed as required before
434-
* use, and we can freely write into the pages directly. If userspace
435-
* races pwrite with any other operation; corruption will ensue -
436-
* that is userspace's prerogative!
437-
*/
432+
if (size > MAX_RW_COUNT)
433+
return -EFBIG;
438434

439-
remain = arg->size;
440-
pos = arg->offset;
441-
pg = offset_in_page(pos);
435+
if (!file->f_op->write_iter)
436+
return -EINVAL;
442437

443-
do {
444-
unsigned int len, unwritten;
445-
struct folio *folio;
446-
void *data, *vaddr;
447-
int err;
448-
char __maybe_unused c;
449-
450-
len = PAGE_SIZE - pg;
451-
if (len > remain)
452-
len = remain;
453-
454-
/* Prefault the user page to reduce potential recursion */
455-
err = __get_user(c, user_data);
456-
if (err)
457-
return err;
458-
459-
err = __get_user(c, user_data + len - 1);
460-
if (err)
461-
return err;
462-
463-
err = aops->write_begin(obj->base.filp, mapping, pos, len,
464-
&folio, &data);
465-
if (err < 0)
466-
return err;
467-
468-
vaddr = kmap_local_folio(folio, offset_in_folio(folio, pos));
469-
pagefault_disable();
470-
unwritten = __copy_from_user_inatomic(vaddr, user_data, len);
471-
pagefault_enable();
472-
kunmap_local(vaddr);
473-
474-
err = aops->write_end(obj->base.filp, mapping, pos, len,
475-
len - unwritten, folio, data);
476-
if (err < 0)
477-
return err;
478-
479-
/* We don't handle -EFAULT, leave it to the caller to check */
480-
if (unwritten)
481-
return -ENODEV;
482-
483-
remain -= len;
484-
user_data += len;
485-
pos += len;
486-
pg = 0;
487-
} while (remain);
438+
init_sync_kiocb(&kiocb, file);
439+
kiocb.ki_pos = arg->offset;
440+
iov_iter_ubuf(&iter, ITER_SOURCE, (void __user *)user_data, size);
441+
442+
written = file->f_op->write_iter(&kiocb, &iter);
443+
BUG_ON(written == -EIOCBQUEUED);
444+
445+
if (written != size)
446+
return -EIO;
447+
448+
if (written < 0)
449+
return written;
488450

489451
return 0;
490452
}

0 commit comments

Comments
 (0)