scst_local, RHEL 5: Avoid that AEN handling triggers a deadlock during device removal (merge r4613 from trunk)

git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/2.2.x@4708 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
Bart Van Assche
2013-01-06 14:17:56 +00:00
parent 4137032bf4
commit 0dabf18259

View File

@@ -102,6 +102,8 @@ module_param_named(add_default_tgt, scst_local_add_default_tgt, bool, S_IRUGO);
MODULE_PARM_DESC(add_default_tgt, "add (default) or not on start default "
"target scst_local_tgt with default session scst_local_host");
static struct workqueue_struct *aen_workqueue;
struct scst_aen_work_item {
struct list_head work_list_entry;
struct scst_aen *aen;
@@ -1239,12 +1241,17 @@ static int scst_local_report_aen(struct scst_aen *aen)
goto out;
}
list_add_tail(&work_item->work_list_entry, &sess->aen_work_list);
list_add_tail(&work_item->work_list_entry,
&sess->aen_work_list);
work_item->aen = aen;
spin_unlock(&sess->aen_lock);
schedule_work(&sess->aen_work);
/*
* We might queue the same item over and over, but that is OK
* It will be ignored by queue_work if it is already queued.
*/
queue_work(aen_workqueue, &sess->aen_work);
break;
default:
@@ -1588,21 +1595,35 @@ static void scst_local_release_adapter(struct device *dev)
if (sess == NULL)
goto out;
spin_lock(&sess->aen_lock);
WARN_ON_ONCE(!sess->unregistering);
scst_process_aens(sess, true);
spin_unlock(&sess->aen_lock);
/*
* At this point the SCSI device is almost gone because the SCSI
* Mid Layer calls us when the device is being unregistered. However,
* SCST might have queued some AENs to us that have not yet been
* processed when unregister_device started working.
*
* To prevent a race between us and AEN handling we must flush the
* workqueue before we clean up the AEN list (calling scst_process_aens
* with cleanup_only set to true) and then unregister the session.
*
* For kernels after 2.6.22 it is sufficient to cancel any outstanding
* work.
*/
#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 22)
/*
* cancel_work_sync() was introduced in 2.6.22. We can only wait until
* all scheduled work is done.
*/
flush_scheduled_work();
flush_workqueue(aen_workqueue);
#else
cancel_work_sync(&sess->aen_work);
#endif
spin_lock(&sess->aen_lock);
WARN_ON_ONCE(!sess->unregistering);
scst_process_aens(sess, true);
spin_unlock(&sess->aen_lock);
scst_unregister_session(sess->scst_sess, false, scst_local_free_sess);
out:
@@ -1883,6 +1904,16 @@ static int __init scst_local_init(void)
goto driver_unreg;
}
/*
* We don't expect much work on this queue, so only create a
* single thread workqueue rather than one on each core.
*/
aen_workqueue = create_singlethread_workqueue("scstlclaen");
if (!aen_workqueue) {
PRINT_ERROR("%s", "Unable to create scst_local workqueue");
goto tgt_templ_unreg;
}
#ifndef CONFIG_SCST_PROC
/*
* If we are using sysfs, then don't add a default target unless
@@ -1896,7 +1927,7 @@ static int __init scst_local_init(void)
ret = scst_local_add_target("scst_local_tgt", &tgt);
if (ret != 0)
goto tgt_templ_unreg;
goto workqueue_unreg;
ret = scst_local_add_adapter(tgt, "scst_local_host");
if (ret != 0)
@@ -1909,6 +1940,9 @@ out:
tgt_unreg:
scst_local_remove_target(tgt);
workqueue_unreg:
destroy_workqueue(aen_workqueue);
tgt_templ_unreg:
scst_unregister_target_template(&scst_local_targ_tmpl);
@@ -1947,6 +1981,8 @@ static void __exit scst_local_exit(void)
}
mutex_unlock(&scst_local_mutex);
destroy_workqueue(aen_workqueue);
driver_unregister(&scst_local_driver);
bus_unregister(&scst_local_lld_bus);
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 29))