Skip to content

Commit 9d0aa60

Browse files
Mikulas Patockabzolnier
authored andcommitted
udlfb: fix semaphore value leak
I observed that the performance of the udl fb driver degrades over time. On a freshly booted machine, it takes 6 seconds to do "ls -la /usr/bin"; after some time of use, the same operation takes 14 seconds. The reason is that the value of "limit_sem" decays over time. The udl driver uses a semaphore "limit_set" to specify how many free urbs are there on dlfb->urbs.list. If the count is zero, the "down" operation will sleep until some urbs are added to the freelist. In order to avoid some hypothetical deadlock, the driver will not call "up" immediately, but it will offload it to a workqueue. The problem is that if we call "schedule_delayed_work" on the same work item multiple times, the work item may only be executed once. This is happening: * some urb completes * dlfb_urb_completion adds it to the free list * dlfb_urb_completion calls schedule_delayed_work to schedule the function dlfb_release_urb_work to increase the semaphore count * as the urb is on the free list, some other task grabs it and submits it * the submitted urb completes, dlfb_urb_completion is called again * dlfb_urb_completion calls schedule_delayed_work, but the work is already scheduled, so it does nothing * finally, dlfb_release_urb_work is called, it increases the semaphore count by 1, although it should increase it by 2 So, the semaphore count is decreasing over time, and this causes gradual performance degradation. Note that in the current kernel, the "up" function may be called from interrupt and it may race with the "down" function called by another thread, so we don't have to offload the call of "up" to a workqueue at all. This patch removes the workqueue code. The patch also changes "down_interruptible" to "down" in dlfb_free_urb_list, so that we will clean up the driver properly even if a signal arrives. With this patch, the performance of udlfb no longer degrades. Signed-off-by: Mikulas Patocka <[email protected]> Cc: [email protected] [b.zolnierkie: fix immediatelly -> immediately typo] Signed-off-by: Bartlomiej Zolnierkiewicz <[email protected]>
1 parent 8c5b044 commit 9d0aa60

File tree

2 files changed

+2
-26
lines changed

2 files changed

+2
-26
lines changed

drivers/video/fbdev/udlfb.c

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -922,14 +922,6 @@ static void dlfb_free(struct kref *kref)
922922
kfree(dlfb);
923923
}
924924

925-
static void dlfb_release_urb_work(struct work_struct *work)
926-
{
927-
struct urb_node *unode = container_of(work, struct urb_node,
928-
release_urb_work.work);
929-
930-
up(&unode->dlfb->urbs.limit_sem);
931-
}
932-
933925
static void dlfb_free_framebuffer(struct dlfb_data *dlfb)
934926
{
935927
struct fb_info *info = dlfb->info;
@@ -1789,14 +1781,7 @@ static void dlfb_urb_completion(struct urb *urb)
17891781
dlfb->urbs.available++;
17901782
spin_unlock_irqrestore(&dlfb->urbs.lock, flags);
17911783

1792-
/*
1793-
* When using fb_defio, we deadlock if up() is called
1794-
* while another is waiting. So queue to another process.
1795-
*/
1796-
if (fb_defio)
1797-
schedule_delayed_work(&unode->release_urb_work, 0);
1798-
else
1799-
up(&dlfb->urbs.limit_sem);
1784+
up(&dlfb->urbs.limit_sem);
18001785
}
18011786

18021787
static void dlfb_free_urb_list(struct dlfb_data *dlfb)
@@ -1805,16 +1790,11 @@ static void dlfb_free_urb_list(struct dlfb_data *dlfb)
18051790
struct list_head *node;
18061791
struct urb_node *unode;
18071792
struct urb *urb;
1808-
int ret;
18091793
unsigned long flags;
18101794

18111795
/* keep waiting and freeing, until we've got 'em all */
18121796
while (count--) {
1813-
1814-
/* Getting interrupted means a leak, but ok at disconnect */
1815-
ret = down_interruptible(&dlfb->urbs.limit_sem);
1816-
if (ret)
1817-
break;
1797+
down(&dlfb->urbs.limit_sem);
18181798

18191799
spin_lock_irqsave(&dlfb->urbs.lock, flags);
18201800

@@ -1854,9 +1834,6 @@ static int dlfb_alloc_urb_list(struct dlfb_data *dlfb, int count, size_t size)
18541834
break;
18551835
unode->dlfb = dlfb;
18561836

1857-
INIT_DELAYED_WORK(&unode->release_urb_work,
1858-
dlfb_release_urb_work);
1859-
18601837
urb = usb_alloc_urb(0, GFP_KERNEL);
18611838
if (!urb) {
18621839
kfree(unode);

include/video/udlfb.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ struct dloarea {
2020
struct urb_node {
2121
struct list_head entry;
2222
struct dlfb_data *dlfb;
23-
struct delayed_work release_urb_work;
2423
struct urb *urb;
2524
};
2625

0 commit comments

Comments
 (0)