summaryrefslogtreecommitdiff
path: root/linux/drivers/media/video/pvrusb2
diff options
context:
space:
mode:
authorMike Isely <isely@pobox.com>2008-04-09 00:14:11 -0500
committerMike Isely <isely@pobox.com>2008-04-09 00:14:11 -0500
commitc53d26a5aee5ca936d008f46e576789e6d1be17c (patch)
tree99ae42d3a958041da64caa577a84790536034107 /linux/drivers/media/video/pvrusb2
parentcc47ad63d90e229ca6e286443e8b3b8d4a330f14 (diff)
downloadmediapointer-dvb-s2-c53d26a5aee5ca936d008f46e576789e6d1be17c.tar.gz
mediapointer-dvb-s2-c53d26a5aee5ca936d008f46e576789e6d1be17c.tar.bz2
pvrusb2: Fix hang on module removal
From: Mike Isely <isely@pobox.com> The pvrusb2 driver was getting had by this scenario: 1. Task A calls kthread_stop() for task B. 2. Before exiting, then Task B calls kthread_stop() for task C. The problem is, kthread_stop() wants to allocate an internal resource to itself (i.e. acquire a lock), which won't be released until kthread_stop() returns. But kthread_stop() won't return until task B is dead. But task B won't die until it finishes its call to kthread_stop() for task C, and that will block waiting on the resource already allocated inside task A. Deadlock. With the pvrusb2 driver, task A is the caller to pvr_exit(), task B is the control thread run inside of pvrusb2-context.c, and task C is any worker thread run inside of pvrusb2-hdw.c. This problem got introduced by the previous threading setup change, which was itself an attempt to fix a module tear-down race (which it actually did fix). The lesson here is that a task being waited on as part of a kthread_stop() simply cannot be allow to also issue a kthread_stop() - or we make sure not to issue the enclosing kthread_stop() until we know that the inner kthread_stop() has completed first. The solution for the pvrusb2 driver is some hackish code which changes the main control thread tear down into a two step process. This then makes it possible to delay issuing the kthread_stop() on the control thread until after we know that everything has been torn down first. (And yes, we really need that kthread_stop() because it's the only way to safely guarantee that a module-referencing kernel thread has safely returned back out of the module before we finally remove the module.) Signed-off-by: Mike Isely <isely@pobox.com>
Diffstat (limited to 'linux/drivers/media/video/pvrusb2')
-rw-r--r--linux/drivers/media/video/pvrusb2/pvrusb2-context.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c
index 9e5862e7c..ecbb23f3d 100644
--- a/linux/drivers/media/video/pvrusb2/pvrusb2-context.c
+++ b/linux/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -37,6 +37,9 @@ static struct pvr2_context *pvr2_context_notify_first;
static struct pvr2_context *pvr2_context_notify_last;
static DEFINE_MUTEX(pvr2_context_mutex);
static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
+static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
+static int pvr2_context_cleanup_flag;
+static int pvr2_context_cleaned_flag;
static struct task_struct *pvr2_context_thread_ptr;
@@ -155,7 +158,7 @@ static void pvr2_context_check(struct pvr2_context *mp)
static int pvr2_context_shutok(void)
{
- return kthread_should_stop() && (pvr2_context_exist_first == NULL);
+ return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
}
@@ -176,6 +179,15 @@ static int pvr2_context_thread_func(void *foo)
pvr2_context_shutok()));
} while (!pvr2_context_shutok());
+ pvr2_context_cleaned_flag = !0;
+ wake_up(&pvr2_context_cleanup_data);
+
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
+
+ wait_event_interruptible(
+ pvr2_context_sync_data,
+ kthread_should_stop());
+
pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
return 0;
@@ -193,6 +205,11 @@ int pvr2_context_global_init(void)
void pvr2_context_global_done(void)
{
+ pvr2_context_cleanup_flag = !0;
+ wake_up(&pvr2_context_sync_data);
+ wait_event_interruptible(
+ pvr2_context_cleanup_data,
+ pvr2_context_cleaned_flag);
kthread_stop(pvr2_context_thread_ptr);
}