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:
Bart Van Assche
2012-09-07 10:03:02 +00:00
parent 23390aca64
commit a67be01951
5 changed files with 83 additions and 50 deletions

View File

@@ -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;

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;
}
/*

View File

@@ -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()!