diff options
author | Noralf Trønnes <noralf@tronnes.org> | 2021-07-01 20:07:48 +0300 |
---|---|---|
committer | Noralf Trønnes <noralf@tronnes.org> | 2021-07-08 15:32:32 +0300 |
commit | 2eecd93b743b5611cd3654698794b4d0cefdc9ee (patch) | |
tree | 0ccbdfa9163126586581fbc44be72e9f39ab35b4 /drivers/gpu/drm/gud/gud_pipe.c | |
parent | f8ac863b6a93863334cefb94285daaa6617381b5 (diff) | |
download | linux-2eecd93b743b5611cd3654698794b4d0cefdc9ee.tar.xz |
drm/gud: Use scatter-gather USB bulk transfer
There'a limit to how big a kmalloc buffer can be, and as memory gets
fragmented it becomes more difficult to get big buffers. The downside of
smaller buffers is that the driver has to split the transfer up which
hampers performance. Compression might also take a hit because of the
splitting.
Solve this by allocating the transfer buffer using vmalloc and create a
SG table to be passed on to the USB subsystem. vmalloc_32() is used to
avoid DMA bounce buffers on USB controllers that can only access 32-bit
addresses.
This also solves the problem that split transfers can give host side
tearing since flushing is decoupled from rendering.
usb_sg_wait() doesn't have timeout handling builtin, so it is wrapped in
a timer like 4 out of 6 users in the kernel have done.
v2:
- Use DIV_ROUND_UP (Linus)
- Add timeout note to the commit log (Linus)
- Expand note about upper buffer limit (Linus)
- Change var name s/timer/ctx/ in gud_usb_bulk_timeout()
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Noralf Trønnes <noralf@tronnes.org>
Link: https://patchwork.freedesktop.org/patch/msgid/20210701170748.58009-2-noralf@tronnes.org
Diffstat (limited to 'drivers/gpu/drm/gud/gud_pipe.c')
-rw-r--r-- | drivers/gpu/drm/gud/gud_pipe.c | 47 |
1 files changed, 41 insertions, 6 deletions
diff --git a/drivers/gpu/drm/gud/gud_pipe.c b/drivers/gpu/drm/gud/gud_pipe.c index 2f83ab6b8e61..e0fb6cc969a3 100644 --- a/drivers/gpu/drm/gud/gud_pipe.c +++ b/drivers/gpu/drm/gud/gud_pipe.c @@ -220,13 +220,51 @@ vunmap: return ret; } +struct gud_usb_bulk_context { + struct timer_list timer; + struct usb_sg_request sgr; +}; + +static void gud_usb_bulk_timeout(struct timer_list *t) +{ + struct gud_usb_bulk_context *ctx = from_timer(ctx, t, timer); + + usb_sg_cancel(&ctx->sgr); +} + +static int gud_usb_bulk(struct gud_device *gdrm, size_t len) +{ + struct gud_usb_bulk_context ctx; + int ret; + + ret = usb_sg_init(&ctx.sgr, gud_to_usb_device(gdrm), gdrm->bulk_pipe, 0, + gdrm->bulk_sgt.sgl, gdrm->bulk_sgt.nents, len, GFP_KERNEL); + if (ret) + return ret; + + timer_setup_on_stack(&ctx.timer, gud_usb_bulk_timeout, 0); + mod_timer(&ctx.timer, jiffies + msecs_to_jiffies(3000)); + + usb_sg_wait(&ctx.sgr); + + if (!del_timer_sync(&ctx.timer)) + ret = -ETIMEDOUT; + else if (ctx.sgr.status < 0) + ret = ctx.sgr.status; + else if (ctx.sgr.bytes != len) + ret = -EIO; + + destroy_timer_on_stack(&ctx.timer); + + return ret; +} + static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb, const struct drm_format_info *format, struct drm_rect *rect) { - struct usb_device *usb = gud_to_usb_device(gdrm); struct gud_set_buffer_req req; - int ret, actual_length; size_t len, trlen; + int ret; drm_dbg(&gdrm->drm, "Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect)); @@ -255,10 +293,7 @@ static int gud_flush_rect(struct gud_device *gdrm, struct drm_framebuffer *fb, return ret; } - ret = usb_bulk_msg(usb, gdrm->bulk_pipe, gdrm->bulk_buf, trlen, - &actual_length, msecs_to_jiffies(3000)); - if (!ret && trlen != actual_length) - ret = -EIO; + ret = gud_usb_bulk(gdrm, trlen); if (ret) gdrm->stats_num_errors++; |