From 7f16c1b4a23fb83fb801cba105ce39e29de0251d Mon Sep 17 00:00:00 2001 From: Vladislav Bolkhovitin Date: Tue, 28 Aug 2012 01:38:11 +0000 Subject: [PATCH] scst: Fix race triggered by target unregistration Without this patch it is possible that some target's sysfs attributes can be called after release() callback returned, when, for instance, tgt_priv is already set to NULL. git-svn-id: http://svn.code.sf.net/p/scst/svn/trunk@4495 d57e44dd-8a1f-0410-8b47-8ef2f437770f --- scst/src/scst_main.c | 17 +++++++++++++---- scst/src/scst_priv.h | 1 + scst/src/scst_sysfs.c | 19 ++++++++++++++----- 3 files changed, 28 insertions(+), 9 deletions(-) diff --git a/scst/src/scst_main.c b/scst/src/scst_main.c index 503171e7e..1c40ffac3 100644 --- a/scst/src/scst_main.c +++ b/scst/src/scst_main.c @@ -618,6 +618,17 @@ void scst_unregister_target(struct scst_tgt *tgt) TRACE_ENTRY(); + /* + * Remove the sysfs attributes of a target before invoking + * tgt->tgtt->release(tgt) such that the "enabled" attribute can't be + * accessed during or after the tgt->tgtt->release(tgt) call. + */ +#ifdef CONFIG_SCST_PROC + scst_cleanup_proc_target_entries(tgt); +#else + scst_tgt_sysfs_del(tgt); +#endif + TRACE_DBG("%s", "Calling target driver's release()"); tgt->tgtt->release(tgt); TRACE_DBG("%s", "Target driver's release() returned"); @@ -653,9 +664,7 @@ again: scst_tg_tgt_remove_by_tgt(tgt); -#ifdef CONFIG_SCST_PROC - scst_cleanup_proc_target_entries(tgt); -#else +#ifndef CONFIG_SCST_PROC scst_del_free_acg(tgt->default_acg); list_for_each_entry_safe(acg, acg_tmp, &tgt->tgt_acg_list, @@ -668,7 +677,7 @@ again: scst_resume_activity(); #ifndef CONFIG_SCST_PROC - scst_tgt_sysfs_del(tgt); + scst_tgt_sysfs_put(tgt); #endif PRINT_INFO("Target %s for template %s unregistered successfully", diff --git a/scst/src/scst_priv.h b/scst/src/scst_priv.h index e60d5ba50..9da084aee 100644 --- a/scst/src/scst_priv.h +++ b/scst/src/scst_priv.h @@ -415,6 +415,7 @@ void scst_tg_tgt_remove_by_tgt(struct scst_tgt *tgt); #ifndef CONFIG_SCST_PROC int scst_dg_sysfs_add(struct kobject *parent, struct scst_dev_group *dg); void scst_dg_sysfs_del(struct scst_dev_group *dg); +void scst_tgt_sysfs_put(struct scst_tgt *tgt); int scst_dg_dev_sysfs_add(struct scst_dev_group *dg, struct scst_dg_dev *dgdev); void scst_dg_dev_sysfs_del(struct scst_dev_group *dg, struct scst_dg_dev *dgdev); diff --git a/scst/src/scst_sysfs.c b/scst/src/scst_sysfs.c index 809f0c227..3dcba8bff 100644 --- a/scst/src/scst_sysfs.c +++ b/scst/src/scst_sysfs.c @@ -2399,13 +2399,8 @@ out_err: */ void scst_tgt_sysfs_del(struct scst_tgt *tgt) { - int rc; - DECLARE_COMPLETION_ONSTACK(c); - TRACE_ENTRY(); - tgt->tgt_kobj_release_cmpl = &c; - kobject_del(tgt->tgt_sess_kobj); kobject_del(tgt->tgt_luns_kobj); kobject_del(tgt->tgt_ini_grp_kobj); @@ -2414,6 +2409,20 @@ void scst_tgt_sysfs_del(struct scst_tgt *tgt) kobject_put(tgt->tgt_sess_kobj); kobject_put(tgt->tgt_luns_kobj); kobject_put(tgt->tgt_ini_grp_kobj); + + TRACE_EXIT(); + return; +} + +void scst_tgt_sysfs_put(struct scst_tgt *tgt) +{ + int rc; + DECLARE_COMPLETION_ONSTACK(c); + + TRACE_ENTRY(); + + tgt->tgt_kobj_release_cmpl = &c; + kobject_put(&tgt->tgt_kobj); rc = wait_for_completion_timeout(tgt->tgt_kobj_release_cmpl, HZ);