Skip to content

Commit a3be19b

Browse files
wenchao-haomartinkpetersen
authored andcommitted
scsi: iscsi: Fix multiple iSCSI session unbind events sent to userspace
It was observed that the kernel would potentially send ISCSI_KEVENT_UNBIND_SESSION multiple times. Introduce 'target_state' in iscsi_cls_session() to make sure session will send only one unbind session event. This introduces a regression wrt. the issue fixed in commit 13e60d3 ("scsi: iscsi: Report unbind session event when the target has been removed"). If iscsid dies for any reason after sending an unbind session to kernel, once iscsid is restarted, the kernel's ISCSI_KEVENT_UNBIND_SESSION event is lost and userspace is then unable to logout. However, the session is actually in invalid state (its target_id is INVALID) so iscsid should not sync this session during restart. Consequently we need to check the session's target state during iscsid restart. If session is in unbound state, do not sync this session and perform session teardown. This is OK because once a session is unbound, we can not recover it any more (mainly because its target id is INVALID). Signed-off-by: Wenchao Hao <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Mike Christie <[email protected]> Reviewed-by: Wu Bo <[email protected]> Signed-off-by: Martin K. Petersen <[email protected]>
1 parent 68ad831 commit a3be19b

File tree

2 files changed

+54
-5
lines changed

2 files changed

+54
-5
lines changed

drivers/scsi/scsi_transport_iscsi.c

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,6 +1676,13 @@ static const char *iscsi_session_state_name(int state)
16761676
return name;
16771677
}
16781678

1679+
static char *iscsi_session_target_state_name[] = {
1680+
[ISCSI_SESSION_TARGET_UNBOUND] = "UNBOUND",
1681+
[ISCSI_SESSION_TARGET_ALLOCATED] = "ALLOCATED",
1682+
[ISCSI_SESSION_TARGET_SCANNED] = "SCANNED",
1683+
[ISCSI_SESSION_TARGET_UNBINDING] = "UNBINDING",
1684+
};
1685+
16791686
int iscsi_session_chkready(struct iscsi_cls_session *session)
16801687
{
16811688
int err;
@@ -1785,9 +1792,13 @@ static int iscsi_user_scan_session(struct device *dev, void *data)
17851792
if ((scan_data->channel == SCAN_WILD_CARD ||
17861793
scan_data->channel == 0) &&
17871794
(scan_data->id == SCAN_WILD_CARD ||
1788-
scan_data->id == id))
1795+
scan_data->id == id)) {
17891796
scsi_scan_target(&session->dev, 0, id,
17901797
scan_data->lun, scan_data->rescan);
1798+
spin_lock_irqsave(&session->lock, flags);
1799+
session->target_state = ISCSI_SESSION_TARGET_SCANNED;
1800+
spin_unlock_irqrestore(&session->lock, flags);
1801+
}
17911802
}
17921803

17931804
user_scan_exit:
@@ -1960,31 +1971,41 @@ static void __iscsi_unbind_session(struct work_struct *work)
19601971
struct iscsi_cls_host *ihost = shost->shost_data;
19611972
unsigned long flags;
19621973
unsigned int target_id;
1974+
bool remove_target = true;
19631975

19641976
ISCSI_DBG_TRANS_SESSION(session, "Unbinding session\n");
19651977

19661978
/* Prevent new scans and make sure scanning is not in progress */
19671979
mutex_lock(&ihost->mutex);
19681980
spin_lock_irqsave(&session->lock, flags);
1969-
if (session->target_id == ISCSI_MAX_TARGET) {
1981+
if (session->target_state == ISCSI_SESSION_TARGET_ALLOCATED) {
1982+
remove_target = false;
1983+
} else if (session->target_state != ISCSI_SESSION_TARGET_SCANNED) {
19701984
spin_unlock_irqrestore(&session->lock, flags);
19711985
mutex_unlock(&ihost->mutex);
1972-
goto unbind_session_exit;
1986+
ISCSI_DBG_TRANS_SESSION(session,
1987+
"Skipping target unbinding: Session is unbound/unbinding.\n");
1988+
return;
19731989
}
19741990

1991+
session->target_state = ISCSI_SESSION_TARGET_UNBINDING;
19751992
target_id = session->target_id;
19761993
session->target_id = ISCSI_MAX_TARGET;
19771994
spin_unlock_irqrestore(&session->lock, flags);
19781995
mutex_unlock(&ihost->mutex);
19791996

1980-
scsi_remove_target(&session->dev);
1997+
if (remove_target)
1998+
scsi_remove_target(&session->dev);
19811999

19822000
if (session->ida_used)
19832001
ida_free(&iscsi_sess_ida, target_id);
19842002

1985-
unbind_session_exit:
19862003
iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION);
19872004
ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
2005+
2006+
spin_lock_irqsave(&session->lock, flags);
2007+
session->target_state = ISCSI_SESSION_TARGET_UNBOUND;
2008+
spin_unlock_irqrestore(&session->lock, flags);
19882009
}
19892010

19902011
static void __iscsi_destroy_session(struct work_struct *work)
@@ -2061,6 +2082,9 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id)
20612082
session->ida_used = true;
20622083
} else
20632084
session->target_id = target_id;
2085+
spin_lock_irqsave(&session->lock, flags);
2086+
session->target_state = ISCSI_SESSION_TARGET_ALLOCATED;
2087+
spin_unlock_irqrestore(&session->lock, flags);
20642088

20652089
dev_set_name(&session->dev, "session%u", session->sid);
20662090
err = device_add(&session->dev);
@@ -4368,6 +4392,19 @@ iscsi_session_attr(def_taskmgmt_tmo, ISCSI_PARAM_DEF_TASKMGMT_TMO, 0);
43684392
iscsi_session_attr(discovery_parent_idx, ISCSI_PARAM_DISCOVERY_PARENT_IDX, 0);
43694393
iscsi_session_attr(discovery_parent_type, ISCSI_PARAM_DISCOVERY_PARENT_TYPE, 0);
43704394

4395+
static ssize_t
4396+
show_priv_session_target_state(struct device *dev, struct device_attribute *attr,
4397+
char *buf)
4398+
{
4399+
struct iscsi_cls_session *session = iscsi_dev_to_session(dev->parent);
4400+
4401+
return sysfs_emit(buf, "%s\n",
4402+
iscsi_session_target_state_name[session->target_state]);
4403+
}
4404+
4405+
static ISCSI_CLASS_ATTR(priv_sess, target_state, S_IRUGO,
4406+
show_priv_session_target_state, NULL);
4407+
43714408
static ssize_t
43724409
show_priv_session_state(struct device *dev, struct device_attribute *attr,
43734410
char *buf)
@@ -4470,6 +4507,7 @@ static struct attribute *iscsi_session_attrs[] = {
44704507
&dev_attr_sess_boot_target.attr,
44714508
&dev_attr_priv_sess_recovery_tmo.attr,
44724509
&dev_attr_priv_sess_state.attr,
4510+
&dev_attr_priv_sess_target_state.attr,
44734511
&dev_attr_priv_sess_creator.attr,
44744512
&dev_attr_sess_chap_out_idx.attr,
44754513
&dev_attr_sess_chap_in_idx.attr,
@@ -4583,6 +4621,8 @@ static umode_t iscsi_session_attr_is_visible(struct kobject *kobj,
45834621
return S_IRUGO | S_IWUSR;
45844622
else if (attr == &dev_attr_priv_sess_state.attr)
45854623
return S_IRUGO;
4624+
else if (attr == &dev_attr_priv_sess_target_state.attr)
4625+
return S_IRUGO;
45864626
else if (attr == &dev_attr_priv_sess_creator.attr)
45874627
return S_IRUGO;
45884628
else if (attr == &dev_attr_priv_sess_target_id.attr)

include/scsi/scsi_transport_iscsi.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,14 @@ enum {
236236
ISCSI_SESSION_FREE,
237237
};
238238

239+
enum {
240+
ISCSI_SESSION_TARGET_UNBOUND,
241+
ISCSI_SESSION_TARGET_ALLOCATED,
242+
ISCSI_SESSION_TARGET_SCANNED,
243+
ISCSI_SESSION_TARGET_UNBINDING,
244+
ISCSI_SESSION_TARGET_MAX,
245+
};
246+
239247
#define ISCSI_MAX_TARGET -1
240248

241249
struct iscsi_cls_session {
@@ -264,6 +272,7 @@ struct iscsi_cls_session {
264272
*/
265273
pid_t creator;
266274
int state;
275+
int target_state; /* session target bind state */
267276
int sid; /* session id */
268277
void *dd_data; /* LLD private data */
269278
struct device dev; /* sysfs transport/container device */

0 commit comments

Comments
 (0)