Commit cc6b32ee authored by Frank Mayhar's avatar Frank Mayhar Committed by Martin K. Petersen

scsi: iscsi: Add support for asynchronous iSCSI session destruction

iSCSI session destruction can be arbitrarily slow, since it might require
network operations and serialization inside the SCSI layer.  This patch
adds a new user event to trigger the destruction work asynchronously,
releasing the rx_queue_mutex as soon as the operation is queued and before
it is performed.  This change allows other operations to run in other
sessions in the meantime, removing one of the major iSCSI bottlenecks for
us.

To prevent the session from being used after the destruction request, we
remove it immediately from the sesslist. This simplifies the locking
required during the asynchronous removal.

Link: https://lore.kernel.org/r/20200227195945.761719-1-krisman@collabora.comCo-developed-by: default avatarGabriel Krisman Bertazi <krisman@collabora.com>
Co-developed-by: default avatarKhazhismel Kumykov <khazhy@google.com>
Reviewed-by: default avatarLee Duncan <lduncan@suse.com>
Signed-off-by: default avatarGabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: default avatarFrank Mayhar <fmayhar@google.com>
Signed-off-by: default avatarKhazhismel Kumykov <khazhy@google.com>
Signed-off-by: default avatarMartin K. Petersen <martin.petersen@oracle.com>
parent bef18d30
......@@ -95,6 +95,8 @@ static DECLARE_WORK(stop_conn_work, stop_conn_work_fn);
static atomic_t iscsi_session_nr; /* sysfs session id for next new session */
static struct workqueue_struct *iscsi_eh_timer_workq;
static struct workqueue_struct *iscsi_destroy_workq;
static DEFINE_IDA(iscsi_sess_ida);
/*
* list of registered transports and lock that must
......@@ -1615,6 +1617,7 @@ static struct sock *nls;
static DEFINE_MUTEX(rx_queue_mutex);
static LIST_HEAD(sesslist);
static LIST_HEAD(sessdestroylist);
static DEFINE_SPINLOCK(sesslock);
static LIST_HEAD(connlist);
static LIST_HEAD(connlist_err);
......@@ -2035,6 +2038,14 @@ static void __iscsi_unbind_session(struct work_struct *work)
ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n");
}
static void __iscsi_destroy_session(struct work_struct *work)
{
struct iscsi_cls_session *session =
container_of(work, struct iscsi_cls_session, destroy_work);
session->transport->destroy_session(session);
}
struct iscsi_cls_session *
iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
int dd_size)
......@@ -2057,6 +2068,7 @@ iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport,
INIT_WORK(&session->block_work, __iscsi_block_session);
INIT_WORK(&session->unbind_work, __iscsi_unbind_session);
INIT_WORK(&session->scan_work, iscsi_scan_session);
INIT_WORK(&session->destroy_work, __iscsi_destroy_session);
spin_lock_init(&session->lock);
/* this is released in the dev's release function */
......@@ -3631,6 +3643,23 @@ iscsi_if_recv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, uint32_t *group)
else
transport->destroy_session(session);
break;
case ISCSI_UEVENT_DESTROY_SESSION_ASYNC:
session = iscsi_session_lookup(ev->u.d_session.sid);
if (!session)
err = -EINVAL;
else if (iscsi_session_has_conns(ev->u.d_session.sid))
err = -EBUSY;
else {
unsigned long flags;
/* Prevent this session from being found again */
spin_lock_irqsave(&sesslock, flags);
list_move(&session->sess_list, &sessdestroylist);
spin_unlock_irqrestore(&sesslock, flags);
queue_work(iscsi_destroy_workq, &session->destroy_work);
}
break;
case ISCSI_UEVENT_UNBIND_SESSION:
session = iscsi_session_lookup(ev->u.d_session.sid);
if (session)
......@@ -4676,8 +4705,16 @@ static __init int iscsi_transport_init(void)
goto release_nls;
}
iscsi_destroy_workq = create_singlethread_workqueue("iscsi_destroy");
if (!iscsi_destroy_workq) {
err = -ENOMEM;
goto destroy_wq;
}
return 0;
destroy_wq:
destroy_workqueue(iscsi_eh_timer_workq);
release_nls:
netlink_kernel_release(nls);
unregister_flashnode_bus:
......@@ -4699,6 +4736,7 @@ static __init int iscsi_transport_init(void)
static void __exit iscsi_transport_exit(void)
{
destroy_workqueue(iscsi_destroy_workq);
destroy_workqueue(iscsi_eh_timer_workq);
netlink_kernel_release(nls);
bus_unregister(&iscsi_flashnode_bus);
......
......@@ -60,6 +60,7 @@ enum iscsi_uevent_e {
ISCSI_UEVENT_LOGOUT_FLASHNODE_SID = UEVENT_BASE + 30,
ISCSI_UEVENT_SET_CHAP = UEVENT_BASE + 31,
ISCSI_UEVENT_GET_HOST_STATS = UEVENT_BASE + 32,
ISCSI_UEVENT_DESTROY_SESSION_ASYNC = UEVENT_BASE + 33,
/* up events */
ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
......
......@@ -226,6 +226,7 @@ struct iscsi_cls_session {
struct work_struct unblock_work;
struct work_struct scan_work;
struct work_struct unbind_work;
struct work_struct destroy_work;
/* recovery fields */
int recovery_tmo;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment