Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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.com
Co-developed-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Co-developed-by: Khazhismel Kumykov <khazhy@google.com>
Reviewed-by: Lee Duncan <lduncan@suse.com>
Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com>
Signed-off-by: Frank Mayhar <fmayhar@google.com>
Signed-off-by: Khazhismel Kumykov <khazhy@google.com>
Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>

authored by

Frank Mayhar and committed by
Martin K. Petersen
cc6b32ee bef18d30

+40
+38
drivers/scsi/scsi_transport_iscsi.c
··· 95 95 static atomic_t iscsi_session_nr; /* sysfs session id for next new session */ 96 96 static struct workqueue_struct *iscsi_eh_timer_workq; 97 97 98 + static struct workqueue_struct *iscsi_destroy_workq; 99 + 98 100 static DEFINE_IDA(iscsi_sess_ida); 99 101 /* 100 102 * list of registered transports and lock that must ··· 1617 1615 static DEFINE_MUTEX(rx_queue_mutex); 1618 1616 1619 1617 static LIST_HEAD(sesslist); 1618 + static LIST_HEAD(sessdestroylist); 1620 1619 static DEFINE_SPINLOCK(sesslock); 1621 1620 static LIST_HEAD(connlist); 1622 1621 static LIST_HEAD(connlist_err); ··· 2038 2035 ISCSI_DBG_TRANS_SESSION(session, "Completed target removal\n"); 2039 2036 } 2040 2037 2038 + static void __iscsi_destroy_session(struct work_struct *work) 2039 + { 2040 + struct iscsi_cls_session *session = 2041 + container_of(work, struct iscsi_cls_session, destroy_work); 2042 + 2043 + session->transport->destroy_session(session); 2044 + } 2045 + 2041 2046 struct iscsi_cls_session * 2042 2047 iscsi_alloc_session(struct Scsi_Host *shost, struct iscsi_transport *transport, 2043 2048 int dd_size) ··· 2068 2057 INIT_WORK(&session->block_work, __iscsi_block_session); 2069 2058 INIT_WORK(&session->unbind_work, __iscsi_unbind_session); 2070 2059 INIT_WORK(&session->scan_work, iscsi_scan_session); 2060 + INIT_WORK(&session->destroy_work, __iscsi_destroy_session); 2071 2061 spin_lock_init(&session->lock); 2072 2062 2073 2063 /* this is released in the dev's release function */ ··· 3643 3631 else 3644 3632 transport->destroy_session(session); 3645 3633 break; 3634 + case ISCSI_UEVENT_DESTROY_SESSION_ASYNC: 3635 + session = iscsi_session_lookup(ev->u.d_session.sid); 3636 + if (!session) 3637 + err = -EINVAL; 3638 + else if (iscsi_session_has_conns(ev->u.d_session.sid)) 3639 + err = -EBUSY; 3640 + else { 3641 + unsigned long flags; 3642 + 3643 + /* Prevent this session from being found again */ 3644 + spin_lock_irqsave(&sesslock, flags); 3645 + list_move(&session->sess_list, &sessdestroylist); 3646 + spin_unlock_irqrestore(&sesslock, flags); 3647 + 3648 + queue_work(iscsi_destroy_workq, &session->destroy_work); 3649 + } 3650 + break; 3646 3651 case ISCSI_UEVENT_UNBIND_SESSION: 3647 3652 session = iscsi_session_lookup(ev->u.d_session.sid); 3648 3653 if (session) ··· 4705 4676 goto release_nls; 4706 4677 } 4707 4678 4679 + iscsi_destroy_workq = create_singlethread_workqueue("iscsi_destroy"); 4680 + if (!iscsi_destroy_workq) { 4681 + err = -ENOMEM; 4682 + goto destroy_wq; 4683 + } 4684 + 4708 4685 return 0; 4709 4686 4687 + destroy_wq: 4688 + destroy_workqueue(iscsi_eh_timer_workq); 4710 4689 release_nls: 4711 4690 netlink_kernel_release(nls); 4712 4691 unregister_flashnode_bus: ··· 4736 4699 4737 4700 static void __exit iscsi_transport_exit(void) 4738 4701 { 4702 + destroy_workqueue(iscsi_destroy_workq); 4739 4703 destroy_workqueue(iscsi_eh_timer_workq); 4740 4704 netlink_kernel_release(nls); 4741 4705 bus_unregister(&iscsi_flashnode_bus);
+1
include/scsi/iscsi_if.h
··· 60 60 ISCSI_UEVENT_LOGOUT_FLASHNODE_SID = UEVENT_BASE + 30, 61 61 ISCSI_UEVENT_SET_CHAP = UEVENT_BASE + 31, 62 62 ISCSI_UEVENT_GET_HOST_STATS = UEVENT_BASE + 32, 63 + ISCSI_UEVENT_DESTROY_SESSION_ASYNC = UEVENT_BASE + 33, 63 64 64 65 /* up events */ 65 66 ISCSI_KEVENT_RECV_PDU = KEVENT_BASE + 1,
+1
include/scsi/scsi_transport_iscsi.h
··· 226 226 struct work_struct unblock_work; 227 227 struct work_struct scan_work; 228 228 struct work_struct unbind_work; 229 + struct work_struct destroy_work; 229 230 230 231 /* recovery fields */ 231 232 int recovery_tmo;