mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-19 11:41:26 +00:00
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:
@@ -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))
|
||||
|
||||
Reference in New Issue
Block a user