mirror of
https://github.com/SCST-project/scst.git
synced 2026-05-18 19:21:26 +00:00
scst: Fix race between closing a session and adding a LUN
Fixes this call stack (kernel 3.2.15): WARNING: at lib/kref.c:34 kref_get+0x2d/0x30() Hardware name: X8ST3 Modules linked in: scst_vdisk ib_srpt scst Pid: 2159, comm: scst_uid Tainted: G W 3.2.15-07+ #2 Call Trace: [<ffffffff81059cba>] warn_slowpath_common+0x7a/0xb0 [<ffffffff81059d05>] warn_slowpath_null+0x15/0x20 [<ffffffff812a5d7d>] kref_get+0x2d/0x30 [<ffffffff812a46ba>] kobject_get+0x1a/0x30 [<ffffffff812a471b>] kobject_add_internal+0x4b/0x240 [<ffffffff812a4bf3>] kobject_init_and_add+0x63/0x90 [<ffffffffa001082b>] ? scst_tgt_dev_setup_threads+0xab/0x310 [scst] [<ffffffffa001a722>] scst_tgt_dev_sysfs_create+0x42/0x80 [scst] [<ffffffffa0010e8c>] scst_alloc_add_tgt_dev+0x31c/0x4b0 [scst] [<ffffffffa001362e>] scst_acg_add_lun+0xfe/0x230 [scst] [<ffffffff812b4511>] ? _kstrtoull+0x31/0x90 [<ffffffffa001777c>] scst_luns_mgmt_store_work_fn+0x29c/0x6b0 [scst] [<ffffffffa0019634>] sysfs_work_thread_fn+0xd4/0x2e0 [scst] [<ffffffff8107a020>] ? wake_up_bit+0x40/0x40 [<ffffffffa0019560>] ? scst_tgt_cpu_mask_show+0x20/0x20 [scst] [<ffffffff81079ab6>] kthread+0x96/0xa0 [<ffffffff8163d7b4>] kernel_thread_helper+0x4/0x10 [<ffffffff81079a20>] ? kthread_worker_fn+0x190/0x190 [<ffffffff8163d7b0>] ? gs_change+0x13/0x13 Reported-by: Sebastian Riemer <sebastian.riemer@profitbricks.com> (Merge r4452:4456 and r4464 from trunk). git-svn-id: http://svn.code.sf.net/p/scst/svn/branches/2.2.x@4506 d57e44dd-8a1f-0410-8b47-8ef2f437770f
This commit is contained in:
@@ -2362,8 +2362,9 @@ typedef struct scsi_qla_host {
|
||||
uint16_t atio_q_length;
|
||||
|
||||
/*
|
||||
* Protected by tgt_mutex AND hardware_lock for writing and
|
||||
* tgt_mutex OR hardware_lock for reading.
|
||||
* Processing Q2T tgt reference. NULL on not enabled targets. Protected
|
||||
* by tgt_mutex AND hardware_lock for writing and tgt_mutex OR
|
||||
* hardware_lock for reading.
|
||||
*/
|
||||
struct q2t_tgt *tgt;
|
||||
|
||||
@@ -2632,7 +2633,10 @@ typedef struct scsi_qla_host {
|
||||
|
||||
struct mutex tgt_host_action_mutex;
|
||||
|
||||
/* Protected by tgt_host_action_mutex */
|
||||
/*
|
||||
* Main Q2T tgt reference, which always points to the target, if the
|
||||
* target mode addon loaded. Protected by tgt_host_action_mutex.
|
||||
*/
|
||||
struct q2t_tgt *q2t_tgt;
|
||||
|
||||
struct list_head ha_list_entry;
|
||||
|
||||
@@ -1445,6 +1445,12 @@ struct scst_tgt {
|
||||
/* List of remote sessions per target, protected by scst_mutex */
|
||||
struct list_head sess_list;
|
||||
|
||||
/*
|
||||
* List of remote sessions registered in sysfs per target, protected
|
||||
* by scst_mutex.
|
||||
*/
|
||||
struct list_head sysfs_sess_list;
|
||||
|
||||
/* List entry of targets per template (tgts_list) */
|
||||
struct list_head tgt_list_entry;
|
||||
|
||||
@@ -1609,9 +1615,15 @@ struct scst_session {
|
||||
/* Name of attached initiator */
|
||||
const char *initiator_name;
|
||||
|
||||
/* Unique session name: initiator name + optional _%d. */
|
||||
const char *sess_name;
|
||||
|
||||
/* List entry of sessions per target */
|
||||
struct list_head sess_list_entry;
|
||||
|
||||
/* Per target list entry for sessions registered in sysfs. */
|
||||
struct list_head sysfs_sess_list_entry;
|
||||
|
||||
/* List entry for the list that keeps session, waiting for the init */
|
||||
struct list_head sess_init_list_entry;
|
||||
|
||||
|
||||
@@ -2555,6 +2555,7 @@ int scst_alloc_tgt(struct scst_tgt_template *tgtt, struct scst_tgt **tgt)
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&t->sess_list);
|
||||
INIT_LIST_HEAD(&t->sysfs_sess_list);
|
||||
init_waitqueue_head(&t->unreg_waitQ);
|
||||
t->tgtt = tgtt;
|
||||
t->sg_tablesize = tgtt->sg_tablesize;
|
||||
@@ -4013,6 +4014,12 @@ void scst_free_session(struct scst_session *sess)
|
||||
|
||||
scst_sess_free_tgt_devs(sess);
|
||||
|
||||
TRACE_DBG("Removing sess %p from the list", sess);
|
||||
list_del(&sess->sess_list_entry);
|
||||
|
||||
TRACE_DBG("Removing session %p from acg %s", sess, sess->acg->acg_name);
|
||||
list_del(&sess->acg_sess_list_entry);
|
||||
|
||||
mutex_unlock(&scst_mutex);
|
||||
|
||||
#ifndef CONFIG_SCST_PROC
|
||||
@@ -4026,15 +4033,7 @@ void scst_free_session(struct scst_session *sess)
|
||||
|
||||
mutex_lock(&scst_mutex);
|
||||
|
||||
/*
|
||||
* The lists delete must be after sysfs del. Otherwise it would break
|
||||
* logic in scst_sess_sysfs_create() to avoid duplicate sysfs names.
|
||||
*/
|
||||
|
||||
TRACE_DBG("Removing sess %p from the list", sess);
|
||||
list_del(&sess->sess_list_entry);
|
||||
TRACE_DBG("Removing session %p from acg %s", sess, sess->acg->acg_name);
|
||||
list_del(&sess->acg_sess_list_entry);
|
||||
list_del(&sess->sysfs_sess_list_entry);
|
||||
|
||||
/* Called under lock to protect from too early tgt release */
|
||||
wake_up_all(&sess->tgt->unreg_waitQ);
|
||||
@@ -4047,6 +4046,8 @@ void scst_free_session(struct scst_session *sess)
|
||||
|
||||
kfree(sess->transport_id);
|
||||
kfree(sess->initiator_name);
|
||||
if (sess->sess_name != sess->initiator_name)
|
||||
kfree(sess->sess_name);
|
||||
|
||||
kmem_cache_free(scst_sess_cachep, sess);
|
||||
|
||||
|
||||
@@ -3645,48 +3645,18 @@ int scst_recreate_sess_luns_link(struct scst_session *sess)
|
||||
int scst_sess_sysfs_create(struct scst_session *sess)
|
||||
{
|
||||
int res = 0;
|
||||
struct scst_session *s;
|
||||
char *name = (char *)sess->initiator_name;
|
||||
int len = strlen(name) + 1, n = 1;
|
||||
const char *name;
|
||||
|
||||
TRACE_ENTRY();
|
||||
|
||||
restart:
|
||||
list_for_each_entry(s, &sess->tgt->sess_list, sess_list_entry) {
|
||||
if (!s->sess_kobj_ready)
|
||||
continue;
|
||||
|
||||
if (strcmp(name, kobject_name(&s->sess_kobj)) == 0) {
|
||||
if (s == sess)
|
||||
continue;
|
||||
|
||||
TRACE_DBG("Duplicated session from the same initiator "
|
||||
"%s found", name);
|
||||
|
||||
if (name == sess->initiator_name) {
|
||||
len = strlen(sess->initiator_name);
|
||||
len += 20;
|
||||
name = kmalloc(len, GFP_KERNEL);
|
||||
if (name == NULL) {
|
||||
PRINT_ERROR("Unable to allocate a "
|
||||
"replacement name (size %d)",
|
||||
len);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(name, len, "%s_%d", sess->initiator_name, n);
|
||||
n++;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
|
||||
name = sess->sess_name;
|
||||
TRACE_DBG("Adding session %s to sysfs", name);
|
||||
|
||||
res = kobject_init_and_add(&sess->sess_kobj, &scst_session_ktype,
|
||||
sess->tgt->tgt_sess_kobj, name);
|
||||
if (res != 0) {
|
||||
PRINT_ERROR("Can't add session %s to sysfs", name);
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
sess->sess_kobj_ready = 1;
|
||||
@@ -3706,10 +3676,7 @@ restart:
|
||||
goto out_del;
|
||||
}
|
||||
|
||||
out_free:
|
||||
if (name != sess->initiator_name)
|
||||
kfree(name);
|
||||
|
||||
out:
|
||||
TRACE_EXIT_RES(res);
|
||||
return res;
|
||||
|
||||
@@ -3717,7 +3684,7 @@ out_del:
|
||||
kobject_del(&sess->sess_kobj);
|
||||
kobject_put(&sess->sess_kobj);
|
||||
sess->sess_kobj_ready = 0;
|
||||
goto out_free;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -6276,6 +6276,44 @@ bool scst_initiator_has_luns(struct scst_tgt *tgt, const char *initiator_name)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scst_initiator_has_luns);
|
||||
|
||||
/* Supposed to be called under scst_mutex */
|
||||
static char *scst_get_unique_sess_name(struct list_head *sysfs_sess_list,
|
||||
const char *initiator_name)
|
||||
{
|
||||
char *name = (char *)initiator_name;
|
||||
struct scst_session *s;
|
||||
int len = 0, n = 1;
|
||||
|
||||
BUG_ON(!initiator_name);
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 32)
|
||||
lockdep_assert_held(&scst_mutex);
|
||||
#endif
|
||||
|
||||
restart:
|
||||
list_for_each_entry(s, sysfs_sess_list, sysfs_sess_list_entry) {
|
||||
BUG_ON(!s->sess_name);
|
||||
if (strcmp(name, s->sess_name) == 0) {
|
||||
TRACE_DBG("Duplicated session from the same initiator "
|
||||
"%s found", name);
|
||||
|
||||
if (name == initiator_name) {
|
||||
len = strlen(initiator_name) + 20;
|
||||
name = kmalloc(len, GFP_KERNEL);
|
||||
if (name == NULL) {
|
||||
PRINT_ERROR("Unable to allocate a "
|
||||
"replacement name (size %d)",
|
||||
len);
|
||||
}
|
||||
}
|
||||
|
||||
snprintf(name, len, "%s_%d", initiator_name, n);
|
||||
n++;
|
||||
goto restart;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
static int scst_init_session(struct scst_session *sess)
|
||||
{
|
||||
int res = 0;
|
||||
@@ -6298,6 +6336,8 @@ static int scst_init_session(struct scst_session *sess)
|
||||
TRACE_DBG("Adding sess %p to tgt->sess_list", sess);
|
||||
list_add_tail(&sess->sess_list_entry, &sess->tgt->sess_list);
|
||||
|
||||
INIT_LIST_HEAD(&sess->sysfs_sess_list_entry);
|
||||
|
||||
if (sess->tgt->tgtt->get_initiator_port_transport_id != NULL) {
|
||||
res = sess->tgt->tgtt->get_initiator_port_transport_id(
|
||||
sess->tgt, sess, &sess->transport_id);
|
||||
@@ -6312,10 +6352,19 @@ static int scst_init_session(struct scst_session *sess)
|
||||
sess->transport_id), sess->tgt->rel_tgt_id);
|
||||
}
|
||||
|
||||
res = -ENOMEM;
|
||||
sess->sess_name = scst_get_unique_sess_name(&sess->tgt->sysfs_sess_list,
|
||||
sess->initiator_name);
|
||||
if (!sess->sess_name)
|
||||
goto failed;
|
||||
|
||||
res = scst_sess_sysfs_create(sess);
|
||||
if (res != 0)
|
||||
goto failed;
|
||||
|
||||
list_add_tail(&sess->sysfs_sess_list_entry,
|
||||
&sess->tgt->sysfs_sess_list);
|
||||
|
||||
/*
|
||||
* scst_sess_alloc_tgt_devs() must be called after session added in the
|
||||
* sess_list to not race with scst_check_reassign_sess()!
|
||||
|
||||
Reference in New Issue
Block a user