| From 542bb9788a1f485eb1a2229178f665d8ea166156 Mon Sep 17 00:00:00 2001 |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| Date: Sun, 3 Jun 2018 16:40:56 +0200 |
| Subject: udl-kms: handle allocation failure |
| |
| From: Mikulas Patocka <mpatocka@redhat.com> |
| |
| commit 542bb9788a1f485eb1a2229178f665d8ea166156 upstream. |
| |
| Allocations larger than PAGE_ALLOC_COSTLY_ORDER are unreliable and they |
| may fail anytime. This patch fixes the udl kms driver so that when a large |
| alloactions fails, it tries to do multiple smaller allocations. |
| |
| Signed-off-by: Mikulas Patocka <mpatocka@redhat.com> |
| Cc: stable@vger.kernel.org |
| Signed-off-by: Dave Airlie <airlied@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/gpu/drm/udl/udl_main.c | 28 ++++++++++++++++++---------- |
| 1 file changed, 18 insertions(+), 10 deletions(-) |
| |
| --- a/drivers/gpu/drm/udl/udl_main.c |
| +++ b/drivers/gpu/drm/udl/udl_main.c |
| @@ -199,17 +199,22 @@ static void udl_free_urb_list(struct drm |
| static int udl_alloc_urb_list(struct drm_device *dev, int count, size_t size) |
| { |
| struct udl_device *udl = dev->dev_private; |
| - int i = 0; |
| struct urb *urb; |
| struct urb_node *unode; |
| char *buf; |
| + size_t wanted_size = count * size; |
| |
| spin_lock_init(&udl->urbs.lock); |
| |
| +retry: |
| udl->urbs.size = size; |
| INIT_LIST_HEAD(&udl->urbs.list); |
| |
| - while (i < count) { |
| + sema_init(&udl->urbs.limit_sem, 0); |
| + udl->urbs.count = 0; |
| + udl->urbs.available = 0; |
| + |
| + while (udl->urbs.count * size < wanted_size) { |
| unode = kzalloc(sizeof(struct urb_node), GFP_KERNEL); |
| if (!unode) |
| break; |
| @@ -225,11 +230,16 @@ static int udl_alloc_urb_list(struct drm |
| } |
| unode->urb = urb; |
| |
| - buf = usb_alloc_coherent(udl->udev, MAX_TRANSFER, GFP_KERNEL, |
| + buf = usb_alloc_coherent(udl->udev, size, GFP_KERNEL, |
| &urb->transfer_dma); |
| if (!buf) { |
| kfree(unode); |
| usb_free_urb(urb); |
| + if (size > PAGE_SIZE) { |
| + size /= 2; |
| + udl_free_urb_list(dev); |
| + goto retry; |
| + } |
| break; |
| } |
| |
| @@ -240,16 +250,14 @@ static int udl_alloc_urb_list(struct drm |
| |
| list_add_tail(&unode->entry, &udl->urbs.list); |
| |
| - i++; |
| + up(&udl->urbs.limit_sem); |
| + udl->urbs.count++; |
| + udl->urbs.available++; |
| } |
| |
| - sema_init(&udl->urbs.limit_sem, i); |
| - udl->urbs.count = i; |
| - udl->urbs.available = i; |
| - |
| - DRM_DEBUG("allocated %d %d byte urbs\n", i, (int) size); |
| + DRM_DEBUG("allocated %d %d byte urbs\n", udl->urbs.count, (int) size); |
| |
| - return i; |
| + return udl->urbs.count; |
| } |
| |
| struct urb *udl_get_urb(struct drm_device *dev) |