Close iSCSI sessions on shutdown

Ensure that all iSCSI sessions are correctly terminated during shutdown.

* Enhances the changes done by r286226 (D3052).
* Add shutdown post sync event to run after filesystem shutdown
  (SHUTDOWN_PRI_FIRST) but before CAM shutdown (SHUTDOWN_PRI_DEFAULT).
* Changes iscsi_maintenance_thread to processes terminate in preference to
  reconnect.

Reviewed by:	trasz
MFC after:	2 weeks
Sponsored by:	Multiplay
Differential Revision:	https://reviews.freebsd.org/D4429
This commit is contained in:
Steven Hartland 2016-01-11 10:24:30 +00:00
parent b53f4a640f
commit 481b36c66a
2 changed files with 73 additions and 29 deletions

View file

@ -98,6 +98,9 @@ SYSCTL_INT(_kern_iscsi, OID_AUTO, maxtags, CTLFLAG_RWTUN, &maxtags,
static int fail_on_disconnection = 0;
SYSCTL_INT(_kern_iscsi, OID_AUTO, fail_on_disconnection, CTLFLAG_RWTUN,
&fail_on_disconnection, 0, "Destroy CAM SIM on connection failure");
static int fail_on_shutdown = 1;
SYSCTL_INT(_kern_iscsi, OID_AUTO, fail_on_shutdown, CTLFLAG_RWTUN,
&fail_on_shutdown, 0, "Fail disconnected sessions on shutdown");
static MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI initiator");
static uma_zone_t iscsi_outstanding_zone;
@ -417,8 +420,6 @@ iscsi_maintenance_thread_terminate(struct iscsi_session *is)
sc = is->is_softc;
sx_xlock(&sc->sc_lock);
TAILQ_REMOVE(&sc->sc_sessions, is, is_next);
sx_xunlock(&sc->sc_lock);
icl_conn_close(is->is_conn);
callout_drain(&is->is_callout);
@ -450,6 +451,9 @@ iscsi_maintenance_thread_terminate(struct iscsi_session *is)
#ifdef ICL_KERNEL_PROXY
cv_destroy(&is->is_login_cv);
#endif
TAILQ_REMOVE(&sc->sc_sessions, is, is_next);
sx_xunlock(&sc->sc_lock);
ISCSI_SESSION_DEBUG(is, "terminated");
free(is, M_ISCSI);
@ -473,12 +477,7 @@ iscsi_maintenance_thread(void *arg)
STAILQ_EMPTY(&is->is_postponed))
cv_wait(&is->is_maintenance_cv, &is->is_lock);
if (is->is_reconnecting) {
ISCSI_SESSION_UNLOCK(is);
iscsi_maintenance_thread_reconnect(is);
continue;
}
/* Terminate supersedes reconnect. */
if (is->is_terminating) {
ISCSI_SESSION_UNLOCK(is);
iscsi_maintenance_thread_terminate(is);
@ -486,6 +485,12 @@ iscsi_maintenance_thread(void *arg)
return;
}
if (is->is_reconnecting) {
ISCSI_SESSION_UNLOCK(is);
iscsi_maintenance_thread_reconnect(is);
continue;
}
iscsi_session_send_postponed(is);
ISCSI_SESSION_UNLOCK(is);
}
@ -605,6 +610,11 @@ iscsi_callout(void *context)
return;
out:
if (is->is_terminating) {
ISCSI_SESSION_UNLOCK(is);
return;
}
ISCSI_SESSION_UNLOCK(is);
if (reconnect_needed)
@ -2326,30 +2336,62 @@ iscsi_poll(struct cam_sim *sim)
}
static void
iscsi_shutdown(struct iscsi_softc *sc)
iscsi_terminate_sessions(struct iscsi_softc *sc)
{
struct iscsi_session *is;
/*
* Trying to reconnect during system shutdown would lead to hang.
*/
fail_on_disconnection = 1;
sx_slock(&sc->sc_lock);
TAILQ_FOREACH(is, &sc->sc_sessions, is_next)
iscsi_session_terminate(is);
while(!TAILQ_EMPTY(&sc->sc_sessions)) {
ISCSI_DEBUG("waiting for sessions to terminate");
cv_wait(&sc->sc_cv, &sc->sc_lock);
}
ISCSI_DEBUG("all sessions terminated");
sx_sunlock(&sc->sc_lock);
}
static void
iscsi_shutdown_pre(struct iscsi_softc *sc)
{
struct iscsi_session *is;
if (!fail_on_shutdown)
return;
/*
* If we have any sessions waiting for reconnection, request
* maintenance thread to fail them immediately instead of waiting
* for reconnect timeout.
*
* This prevents LUNs with mounted filesystems that are supported
* by disconnected iSCSI sessions from hanging, however it will
* fail all queued BIOs.
*/
ISCSI_DEBUG("forcing failing all disconnected sessions due to shutdown");
fail_on_disconnection = 1;
sx_slock(&sc->sc_lock);
TAILQ_FOREACH(is, &sc->sc_sessions, is_next) {
ISCSI_SESSION_LOCK(is);
if (is->is_waiting_for_iscsid)
if (!is->is_connected) {
ISCSI_SESSION_DEBUG(is, "force failing disconnected session early");
iscsi_session_reconnect(is);
}
ISCSI_SESSION_UNLOCK(is);
}
sx_sunlock(&sc->sc_lock);
}
static void
iscsi_shutdown_post(struct iscsi_softc *sc)
{
ISCSI_DEBUG("removing all sessions due to shutdown");
iscsi_terminate_sessions(sc);
}
static int
iscsi_load(void)
{
@ -2372,8 +2414,16 @@ iscsi_load(void)
}
sc->sc_cdev->si_drv1 = sc;
sc->sc_shutdown_eh = EVENTHANDLER_REGISTER(shutdown_pre_sync,
iscsi_shutdown, sc, SHUTDOWN_PRI_DEFAULT-1);
sc->sc_shutdown_pre_eh = EVENTHANDLER_REGISTER(shutdown_pre_sync,
iscsi_shutdown_pre, sc, SHUTDOWN_PRI_FIRST);
/*
* shutdown_post_sync needs to run after filesystem shutdown and before
* CAM shutdown - otherwise when rebooting with an iSCSI session that is
* disconnected but has outstanding requests, dashutdown() will hang on
* cam_periph_runccb().
*/
sc->sc_shutdown_post_eh = EVENTHANDLER_REGISTER(shutdown_post_sync,
iscsi_shutdown_post, sc, SHUTDOWN_PRI_DEFAULT - 1);
return (0);
}
@ -2381,7 +2431,6 @@ iscsi_load(void)
static int
iscsi_unload(void)
{
struct iscsi_session *is, *tmp;
if (sc->sc_cdev != NULL) {
ISCSI_DEBUG("removing device node");
@ -2389,18 +2438,12 @@ iscsi_unload(void)
ISCSI_DEBUG("device node removed");
}
if (sc->sc_shutdown_eh != NULL)
EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->sc_shutdown_eh);
if (sc->sc_shutdown_pre_eh != NULL)
EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->sc_shutdown_pre_eh);
if (sc->sc_shutdown_post_eh != NULL)
EVENTHANDLER_DEREGISTER(shutdown_post_sync, sc->sc_shutdown_post_eh);
sx_slock(&sc->sc_lock);
TAILQ_FOREACH_SAFE(is, &sc->sc_sessions, is_next, tmp)
iscsi_session_terminate(is);
while(!TAILQ_EMPTY(&sc->sc_sessions)) {
ISCSI_DEBUG("waiting for sessions to terminate");
cv_wait(&sc->sc_cv, &sc->sc_lock);
}
ISCSI_DEBUG("all sessions terminated");
sx_sunlock(&sc->sc_lock);
iscsi_terminate_sessions(sc);
uma_zdestroy(iscsi_outstanding_zone);
sx_destroy(&sc->sc_lock);

View file

@ -131,7 +131,8 @@ struct iscsi_softc {
TAILQ_HEAD(, iscsi_session) sc_sessions;
struct cv sc_cv;
unsigned int sc_last_session_id;
eventhandler_tag sc_shutdown_eh;
eventhandler_tag sc_shutdown_pre_eh;
eventhandler_tag sc_shutdown_post_eh;
};
#endif /* !ISCSI_H */