Author: trasz
Date: Mon Apr 16 17:24:33 2018
New Revision: 332622
URL: https://svnweb.freebsd.org/changeset/base/332622

Log:
  MFC r331013:
  
  Fix iSCSI target crash on session reinstation.
  
  The crash scenario goes like this: there's a thread waiting on "reinstate";
  because it doesn't update the timeout counter it gets terminated by the
  callout; at this point the maintenance thread starts the termination routine.
  The first thread finishes waiting, proceeds to icl_conn_handoff(), and drops
  the refcount, which allows the maintenance thread to free its resources.  At
  this point another thread receives a PDU.  Boom.
  
  PR:           222898, 219866
  Sponsored by: playkey.net

Modified:
  stable/11/sys/cam/ctl/ctl_frontend_iscsi.c
  stable/11/sys/cam/ctl/ctl_frontend_iscsi.h
Directory Properties:
  stable/11/   (props changed)

Modified: stable/11/sys/cam/ctl/ctl_frontend_iscsi.c
==============================================================================
--- stable/11/sys/cam/ctl/ctl_frontend_iscsi.c  Mon Apr 16 17:22:51 2018        
(r332621)
+++ stable/11/sys/cam/ctl/ctl_frontend_iscsi.c  Mon Apr 16 17:24:33 2018        
(r332622)
@@ -1162,11 +1162,11 @@ cfiscsi_maintenance_thread(void *arg)
 
        for (;;) {
                CFISCSI_SESSION_LOCK(cs);
-               if (cs->cs_terminating == false)
+               if (cs->cs_terminating == false || cs->cs_handoff_in_progress)
                        cv_wait(&cs->cs_maintenance_cv, &cs->cs_lock);
                CFISCSI_SESSION_UNLOCK(cs);
 
-               if (cs->cs_terminating) {
+               if (cs->cs_terminating && cs->cs_handoff_in_progress == false) {
 
                        /*
                         * We used to wait up to 30 seconds to deliver queued
@@ -1194,8 +1194,6 @@ static void
 cfiscsi_session_terminate(struct cfiscsi_session *cs)
 {
 
-       if (cs->cs_terminating)
-               return;
        cs->cs_terminating = true;
        cv_signal(&cs->cs_maintenance_cv);
 #ifdef ICL_KERNEL_PROXY
@@ -1266,6 +1264,13 @@ cfiscsi_session_new(struct cfiscsi_softc *softc, const
        cv_init(&cs->cs_login_cv, "cfiscsi_login");
 #endif
 
+       /*
+        * The purpose of this is to avoid racing with session shutdown.
+        * Otherwise we could have the maintenance thread call icl_conn_close()
+        * before we call icl_conn_handoff().
+        */
+       cs->cs_handoff_in_progress = true;
+
        cs->cs_conn = icl_new_conn(offload, false, "cfiscsi", &cs->cs_lock);
        if (cs->cs_conn == NULL) {
                free(cs, M_CFISCSI);
@@ -1376,8 +1381,18 @@ cfiscsi_accept(struct socket *so, struct sockaddr *sa,
        icl_conn_handoff_sock(cs->cs_conn, so);
        cs->cs_initiator_sa = sa;
        cs->cs_portal_id = portal_id;
+       cs->cs_handoff_in_progress = false;
        cs->cs_waiting_for_ctld = true;
        cv_signal(&cfiscsi_softc.accept_cv);
+
+       CFISCSI_SESSION_LOCK(cs);
+       /*
+        * Wake up the maintenance thread if we got scheduled for termination
+        * somewhere between cfiscsi_session_new() and icl_conn_handoff_sock().
+        */
+       if (cs->cs_terminating)
+               cfiscsi_session_terminate(cs);
+       CFISCSI_SESSION_UNLOCK(cs);
 }
 #endif
 
@@ -1556,6 +1571,7 @@ cfiscsi_ioctl_handoff(struct ctl_iscsi *ci)
        mtx_lock(&softc->lock);
        if (ct->ct_online == 0) {
                mtx_unlock(&softc->lock);
+               cs->cs_handoff_in_progress = false;
                cfiscsi_session_terminate(cs);
                cfiscsi_target_release(ct);
                ci->status = CTL_ISCSI_ERROR;
@@ -1566,7 +1582,6 @@ cfiscsi_ioctl_handoff(struct ctl_iscsi *ci)
        cs->cs_target = ct;
        mtx_unlock(&softc->lock);
 
-       refcount_acquire(&cs->cs_outstanding_ctl_pdus);
 restart:
        if (!cs->cs_terminating) {
                mtx_lock(&softc->lock);
@@ -1603,8 +1618,8 @@ restart:
 #endif
                error = icl_conn_handoff(cs->cs_conn, cihp->socket);
                if (error != 0) {
+                       cs->cs_handoff_in_progress = false;
                        cfiscsi_session_terminate(cs);
-                       refcount_release(&cs->cs_outstanding_ctl_pdus);
                        ci->status = CTL_ISCSI_ERROR;
                        snprintf(ci->error_str, sizeof(ci->error_str),
                            "%s: icl_conn_handoff failed with error %d",
@@ -1629,7 +1644,16 @@ restart:
        }
 #endif
 
-       refcount_release(&cs->cs_outstanding_ctl_pdus);
+       CFISCSI_SESSION_LOCK(cs);
+       cs->cs_handoff_in_progress = false;
+
+       /*
+        * Wake up the maintenance thread if we got scheduled for termination.
+        */
+       if (cs->cs_terminating)
+               cfiscsi_session_terminate(cs);
+       CFISCSI_SESSION_UNLOCK(cs);
+
        ci->status = CTL_ISCSI_OK;
 }
 

Modified: stable/11/sys/cam/ctl/ctl_frontend_iscsi.h
==============================================================================
--- stable/11/sys/cam/ctl/ctl_frontend_iscsi.h  Mon Apr 16 17:22:51 2018        
(r332621)
+++ stable/11/sys/cam/ctl/ctl_frontend_iscsi.h  Mon Apr 16 17:24:33 2018        
(r332622)
@@ -83,6 +83,7 @@ struct cfiscsi_session {
        int                             cs_timeout;
        struct cv                       cs_maintenance_cv;
        bool                            cs_terminating;
+       bool                            cs_handoff_in_progress;
        bool                            cs_tasks_aborted;
        size_t                          cs_max_data_segment_length;
        size_t                          cs_max_burst_length;
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to