mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-17 18:51:27 +00:00
Merged revisions 3786 via svnmerge from
https://vlnb@scst.svn.sourceforge.net/svnroot/scst/trunk ........ r3786 | vlnb | 2011-07-28 18:27:22 -0400 (Thu, 28 Jul 2011) | 3 lines Fix possible deadlock in the sysfs thread processing ........ git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/2.1.0.x@3788 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -412,64 +412,6 @@ void scst_sysfs_work_put(struct scst_sysfs_work_item *work)
|
||||
}
|
||||
EXPORT_SYMBOL(scst_sysfs_work_put);
|
||||
|
||||
/**
|
||||
* scst_sysfs_queue_wait_work() - waits for the work to complete
|
||||
*
|
||||
* Returns status of the completed work or -EAGAIN if the work not
|
||||
* completed before timeout. In the latter case a user should poll
|
||||
* last_sysfs_mgmt_res until it returns the result of the processing.
|
||||
*/
|
||||
int scst_sysfs_queue_wait_work(struct scst_sysfs_work_item *work)
|
||||
{
|
||||
int res = 0, rc;
|
||||
unsigned long timeout = 15*HZ;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
spin_lock(&sysfs_work_lock);
|
||||
|
||||
TRACE_DBG("Adding sysfs work %p to the list", work);
|
||||
list_add_tail(&work->sysfs_work_list_entry, &sysfs_work_list);
|
||||
|
||||
active_sysfs_works++;
|
||||
|
||||
kref_get(&work->sysfs_work_kref);
|
||||
|
||||
spin_unlock(&sysfs_work_lock);
|
||||
|
||||
wake_up(&sysfs_work_waitQ);
|
||||
|
||||
while (1) {
|
||||
rc = wait_for_completion_interruptible_timeout(
|
||||
&work->sysfs_work_done, timeout);
|
||||
if (rc == 0) {
|
||||
if (!mutex_is_locked(&scst_mutex)) {
|
||||
TRACE_DBG("scst_mutex not locked, continue "
|
||||
"waiting (work %p)", work);
|
||||
timeout = 5*HZ;
|
||||
continue;
|
||||
}
|
||||
TRACE_MGMT_DBG("Time out waiting for work %p",
|
||||
work);
|
||||
res = -EAGAIN;
|
||||
goto out_put;
|
||||
} else if (rc < 0) {
|
||||
res = rc;
|
||||
goto out_put;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
res = work->work_res;
|
||||
|
||||
out_put:
|
||||
kref_put(&work->sysfs_work_kref, scst_sysfs_work_release);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(scst_sysfs_queue_wait_work);
|
||||
|
||||
/* Called under sysfs_work_lock and drops/reacquire it inside */
|
||||
static void scst_process_sysfs_works(void)
|
||||
__releases(&sysfs_work_lock)
|
||||
@@ -514,9 +456,12 @@ static inline int test_sysfs_work_list(void)
|
||||
|
||||
static int sysfs_work_thread_fn(void *arg)
|
||||
{
|
||||
bool one_time_only = (bool)arg;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
PRINT_INFO("User interface thread started, PID %d", current->pid);
|
||||
if (!one_time_only)
|
||||
PRINT_INFO("User interface thread started, PID %d", current->pid);
|
||||
|
||||
current->flags |= PF_NOFREEZE;
|
||||
|
||||
@@ -527,6 +472,9 @@ static int sysfs_work_thread_fn(void *arg)
|
||||
wait_queue_t wait;
|
||||
init_waitqueue_entry(&wait, current);
|
||||
|
||||
if (one_time_only && !test_sysfs_work_list())
|
||||
break;
|
||||
|
||||
if (!test_sysfs_work_list()) {
|
||||
add_wait_queue_exclusive(&sysfs_work_waitQ, &wait);
|
||||
for (;;) {
|
||||
@@ -545,18 +493,101 @@ static int sysfs_work_thread_fn(void *arg)
|
||||
}
|
||||
spin_unlock(&sysfs_work_lock);
|
||||
|
||||
/*
|
||||
* If kthread_should_stop() is true, we are guaranteed to be
|
||||
* on the module unload, so both lists must be empty.
|
||||
*/
|
||||
sBUG_ON(!list_empty(&sysfs_work_list));
|
||||
if (!one_time_only) {
|
||||
/*
|
||||
* If kthread_should_stop() is true, we are guaranteed to be
|
||||
* on the module unload, so both lists must be empty.
|
||||
*/
|
||||
sBUG_ON(!list_empty(&sysfs_work_list));
|
||||
|
||||
PRINT_INFO("User interface thread PID %d finished", current->pid);
|
||||
PRINT_INFO("User interface thread PID %d finished", current->pid);
|
||||
}
|
||||
|
||||
TRACE_EXIT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* scst_sysfs_queue_wait_work() - waits for the work to complete
|
||||
*
|
||||
* Returns status of the completed work or -EAGAIN if the work not
|
||||
* completed before timeout. In the latter case a user should poll
|
||||
* last_sysfs_mgmt_res until it returns the result of the processing.
|
||||
*/
|
||||
int scst_sysfs_queue_wait_work(struct scst_sysfs_work_item *work)
|
||||
{
|
||||
int res = 0, rc;
|
||||
unsigned long timeout = 15*HZ;
|
||||
struct task_struct *t;
|
||||
static atomic_t uid_thread_name = ATOMIC_INIT(0);
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
spin_lock(&sysfs_work_lock);
|
||||
|
||||
TRACE_DBG("Adding sysfs work %p to the list", work);
|
||||
list_add_tail(&work->sysfs_work_list_entry, &sysfs_work_list);
|
||||
|
||||
active_sysfs_works++;
|
||||
|
||||
kref_get(&work->sysfs_work_kref);
|
||||
|
||||
spin_unlock(&sysfs_work_lock);
|
||||
|
||||
wake_up(&sysfs_work_waitQ);
|
||||
|
||||
/*
|
||||
* We can have a dead lock possibility like: the sysfs thread is waiting
|
||||
* for the last put during some object unregistration and at the same
|
||||
* time another queued work is having reference on that object taken and
|
||||
* waiting for attention from the sysfs thread. Generally, all sysfs
|
||||
* function calling kobject_get() and then queuing sysfs thread job. For
|
||||
* instance. This is especially dangerous in read only cases, like
|
||||
* vdev_sysfs_filename_show().
|
||||
*
|
||||
* So, to eliminate that deadlock we will create an extra sysfs thread
|
||||
* for each queued sysfs work. This thread will quit as soon as it will
|
||||
* see that there is not more queued works to process.
|
||||
*/
|
||||
|
||||
t = kthread_run(sysfs_work_thread_fn, (void *)true, "scst_uid%d",
|
||||
atomic_inc_return(&uid_thread_name));
|
||||
if (IS_ERR(t))
|
||||
PRINT_ERROR("kthread_run() for user interface thread %d "
|
||||
"failed: %d", atomic_read(&uid_thread_name),
|
||||
(int)PTR_ERR(t));
|
||||
|
||||
while (1) {
|
||||
rc = wait_for_completion_interruptible_timeout(
|
||||
&work->sysfs_work_done, timeout);
|
||||
if (rc == 0) {
|
||||
if (!mutex_is_locked(&scst_mutex)) {
|
||||
TRACE_DBG("scst_mutex not locked, continue "
|
||||
"waiting (work %p)", work);
|
||||
timeout = 5*HZ;
|
||||
continue;
|
||||
}
|
||||
TRACE_MGMT_DBG("Time out waiting for work %p",
|
||||
work);
|
||||
res = -EAGAIN;
|
||||
goto out_put;
|
||||
} else if (rc < 0) {
|
||||
res = rc;
|
||||
goto out_put;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
res = work->work_res;
|
||||
|
||||
out_put:
|
||||
kref_put(&work->sysfs_work_kref, scst_sysfs_work_release);
|
||||
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
}
|
||||
EXPORT_SYMBOL(scst_sysfs_queue_wait_work);
|
||||
|
||||
/* No locks */
|
||||
static int scst_check_grab_tgtt_ptr(struct scst_tgt_template *tgtt)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user