aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/bcm27xx/patches-5.10/950-0714-drm-gud-Use-scatter-gather-USB-bulk-transfer.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/bcm27xx/patches-5.10/950-0714-drm-gud-Use-scatter-gather-USB-bulk-transfer.patch')
-rw-r--r--target/linux/bcm27xx/patches-5.10/950-0714-drm-gud-Use-scatter-gather-USB-bulk-transfer.patch204
1 files changed, 204 insertions, 0 deletions
diff --git a/target/linux/bcm27xx/patches-5.10/950-0714-drm-gud-Use-scatter-gather-USB-bulk-transfer.patch b/target/linux/bcm27xx/patches-5.10/950-0714-drm-gud-Use-scatter-gather-USB-bulk-transfer.patch
new file mode 100644
index 0000000000..4a7771fdb3
--- /dev/null
+++ b/target/linux/bcm27xx/patches-5.10/950-0714-drm-gud-Use-scatter-gather-USB-bulk-transfer.patch
@@ -0,0 +1,204 @@
+From 1cff637824f8e5626a754cbbb97bc8f3730a88f0 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Noralf=20Tr=C3=B8nnes?= <noralf@tronnes.org>
+Date: Thu, 1 Jul 2021 19:07:48 +0200
+Subject: [PATCH] drm/gud: Use scatter-gather USB bulk transfer
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+[ drm-misc commit 2eecd93b743b5611cd3654698794b4d0cefdc9ee ]
+
+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
+---
+ drivers/gpu/drm/gud/gud_drv.c | 50 +++++++++++++++++++++---------
+ drivers/gpu/drm/gud/gud_internal.h | 2 ++
+ drivers/gpu/drm/gud/gud_pipe.c | 47 ++++++++++++++++++++++++----
+ 3 files changed, 78 insertions(+), 21 deletions(-)
+
+--- a/drivers/gpu/drm/gud/gud_drv.c
++++ b/drivers/gpu/drm/gud/gud_drv.c
+@@ -407,13 +407,40 @@ static struct drm_driver gud_drm_driver
+ .minor = 0,
+ };
+
++static int gud_alloc_bulk_buffer(struct gud_device *gdrm)
++{
++ unsigned int i, num_pages;
++ struct page **pages;
++ void *ptr;
++ int ret;
++
++ gdrm->bulk_buf = vmalloc_32(gdrm->bulk_len);
++ if (!gdrm->bulk_buf)
++ return -ENOMEM;
++
++ num_pages = DIV_ROUND_UP(gdrm->bulk_len, PAGE_SIZE);
++ pages = kmalloc_array(num_pages, sizeof(struct page *), GFP_KERNEL);
++ if (!pages)
++ return -ENOMEM;
++
++ for (i = 0, ptr = gdrm->bulk_buf; i < num_pages; i++, ptr += PAGE_SIZE)
++ pages[i] = vmalloc_to_page(ptr);
++
++ ret = sg_alloc_table_from_pages(&gdrm->bulk_sgt, pages, num_pages,
++ 0, gdrm->bulk_len, GFP_KERNEL);
++ kfree(pages);
++
++ return ret;
++}
++
+ static void gud_free_buffers_and_mutex(void *data)
+ {
+ struct gud_device *gdrm = data;
+
+ vfree(gdrm->compress_buf);
+ gdrm->compress_buf = NULL;
+- kfree(gdrm->bulk_buf);
++ sg_free_table(&gdrm->bulk_sgt);
++ vfree(gdrm->bulk_buf);
+ gdrm->bulk_buf = NULL;
+ mutex_destroy(&gdrm->ctrl_lock);
+ }
+@@ -550,24 +577,17 @@ static int gud_probe(struct usb_interfac
+
+ if (desc.max_buffer_size)
+ max_buffer_size = le32_to_cpu(desc.max_buffer_size);
+-retry:
+- /*
+- * Use plain kmalloc here since devm_kmalloc() places struct devres at the beginning
+- * of the buffer it allocates. This wastes a lot of memory when allocating big buffers.
+- * Asking for 2M would actually allocate 4M. This would also prevent getting the biggest
+- * possible buffer potentially leading to split transfers.
+- */
+- gdrm->bulk_buf = kmalloc(max_buffer_size, GFP_KERNEL | __GFP_NOWARN);
+- if (!gdrm->bulk_buf) {
+- max_buffer_size = roundup_pow_of_two(max_buffer_size) / 2;
+- if (max_buffer_size < SZ_512K)
+- return -ENOMEM;
+- goto retry;
+- }
++ /* Prevent a misbehaving device from allocating loads of RAM. 4096x4096@XRGB8888 = 64 MB */
++ if (max_buffer_size > SZ_64M)
++ max_buffer_size = SZ_64M;
+
+ gdrm->bulk_pipe = usb_sndbulkpipe(interface_to_usbdev(intf), usb_endpoint_num(bulk_out));
+ gdrm->bulk_len = max_buffer_size;
+
++ ret = gud_alloc_bulk_buffer(gdrm);
++ if (ret)
++ return ret;
++
+ if (gdrm->compression & GUD_COMPRESSION_LZ4) {
+ gdrm->lz4_comp_mem = devm_kmalloc(dev, LZ4_MEM_COMPRESS, GFP_KERNEL);
+ if (!gdrm->lz4_comp_mem)
+--- a/drivers/gpu/drm/gud/gud_internal.h
++++ b/drivers/gpu/drm/gud/gud_internal.h
+@@ -5,6 +5,7 @@
+
+ #include <linux/list.h>
+ #include <linux/mutex.h>
++#include <linux/scatterlist.h>
+ #include <linux/usb.h>
+ #include <linux/workqueue.h>
+ #include <uapi/drm/drm_fourcc.h>
+@@ -26,6 +27,7 @@ struct gud_device {
+ unsigned int bulk_pipe;
+ void *bulk_buf;
+ size_t bulk_len;
++ struct sg_table bulk_sgt;
+
+ u8 compression;
+ void *lz4_comp_mem;
+--- a/drivers/gpu/drm/gud/gud_pipe.c
++++ b/drivers/gpu/drm/gud/gud_pipe.c
+@@ -219,13 +219,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));
+
+@@ -254,10 +292,7 @@ static int gud_flush_rect(struct gud_dev
+ 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++;
+