Index: share/man/man4/iscsi.4 =================================================================== --- share/man/man4/iscsi.4 +++ share/man/man4/iscsi.4 @@ -74,6 +74,7 @@ forcibly restarted. Set to 0 to disable sending NOP-Out PDUs. Defaults to 5. +This can be set in increments of 0.1. .It Va kern.iscsi.iscsid_timeout The number of seconds to wait for .Xr iscsid 8 @@ -81,13 +82,13 @@ After that time .Nm will abort and retry. -Defaults to 60. +Defaults to 60 and accepts increments of 0.1. .It Va kern.iscsi.login_timeout The number of seconds to wait for a login attempt to succeed. After that time .Nm will abort and retry. -Defaults to 60. +Defaults to 60 and accepts increments of 0.1. .It Va kern.iscsi.maxtags The maximum number of outstanding IO requests. Defaults to 255. Index: sys/dev/iscsi/iscsi.h =================================================================== --- sys/dev/iscsi/iscsi.h +++ sys/dev/iscsi/iscsi.h @@ -75,6 +75,8 @@ struct callout is_callout; unsigned int is_timeout; + int is_ping_timeout; + int is_login_timeout; /* * XXX: This could be rewritten using a single variable, Index: sys/dev/iscsi/iscsi.c =================================================================== --- sys/dev/iscsi/iscsi.c +++ sys/dev/iscsi/iscsi.c @@ -42,9 +42,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -80,21 +82,26 @@ * Think about how to do this properly. */ static struct iscsi_softc *sc; +static int +sysctl_handle_deciint(SYSCTL_HANDLER_ARGS); SYSCTL_NODE(_kern, OID_AUTO, iscsi, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "iSCSI initiator"); static int debug = 1; SYSCTL_INT(_kern_iscsi, OID_AUTO, debug, CTLFLAG_RWTUN, &debug, 0, "Enable debug messages"); -static int ping_timeout = 5; -SYSCTL_INT(_kern_iscsi, OID_AUTO, ping_timeout, CTLFLAG_RWTUN, &ping_timeout, - 0, "Timeout for ping (NOP-Out) requests, in seconds"); -static int iscsid_timeout = 60; -SYSCTL_INT(_kern_iscsi, OID_AUTO, iscsid_timeout, CTLFLAG_RWTUN, &iscsid_timeout, - 0, "Time to wait for iscsid(8) to handle reconnection, in seconds"); -static int login_timeout = 60; -SYSCTL_INT(_kern_iscsi, OID_AUTO, login_timeout, CTLFLAG_RWTUN, &login_timeout, - 0, "Time to wait for iscsid(8) to finish Login Phase, in seconds"); +static int ping_timeout = 50; +SYSCTL_PROC(_kern_iscsi, OID_AUTO, ping_timeout, + CTLFLAG_RWTUN | CTLTYPE_STRING, &ping_timeout, 0, sysctl_handle_deciint, + "A", "Timeout for ping (NOP-Out) requests, in seconds"); +static int iscsid_timeout = 600; +SYSCTL_PROC(_kern_iscsi, OID_AUTO, iscsid_timeout, + CTLFLAG_RWTUN | CTLTYPE_STRING, &iscsid_timeout, 0, sysctl_handle_deciint, + "A", "Time to wait for iscsid(8) to handle reconnection, in seconds"); +static int login_timeout = 600; +SYSCTL_PROC(_kern_iscsi, OID_AUTO, login_timeout, + CTLFLAG_RWTUN | CTLTYPE_STRING, &login_timeout, 0, sysctl_handle_deciint, + "A", "Time to wait for iscsid(8) to finish Login Phase, in seconds"); static int maxtags = 255; SYSCTL_INT(_kern_iscsi, OID_AUTO, maxtags, CTLFLAG_RWTUN, &maxtags, 0, "Max number of IO requests queued"); @@ -179,6 +186,95 @@ static void iscsi_outstanding_remove(struct iscsi_session *is, struct iscsi_outstanding *io); + + +static int +sysctl_handle_deciint(SYSCTL_HANDLER_ARGS) +{ + int error; + char oldstr[10], newstr[10]; + char *s, c; + int newval = 0; + bool havesign = false; + bool havenumber = false; + bool havedot = false; + bool havedecimal = false; + bool isnegative = false; + + if ((*(int *)arg1 % 10) == 0) + snprintf(oldstr, sizeof(oldstr), "%d", *(int *)arg1/10); + else + snprintf(oldstr, sizeof(oldstr), "%d.%d", *(int *)arg1/10, *(int *)arg1%10); + + strncpy(newstr, oldstr, sizeof(newstr)); + + error = sysctl_handle_string(oidp, newstr, sizeof(newstr), req); + + s = &newstr[0]; + while ((c=*s++)) { + if (c == ' ' || c == '\t') { + if (!havenumber) { + continue; + } else { + break; + } + } + if (c == '+') { + if (!havesign) { + havesign = true; + } else { + error = EINVAL; + break; + } + } + if (c == '-') { + if (!havesign) { + havesign = true; + isnegative = true; + } else { + error = EINVAL; + break; + } + } + if (c == '.') { + if (!havedot) { + havedot = true; + } else { + error = EINVAL; + break; + } + } + if (c >= '0' && c <= '9') { + havenumber = true; + if (havedot && havedecimal) { + if (c >= '5') /* Round up */ + newval += 1; + break; + } + if (havedot && !havedecimal) { + havedecimal = true; + } + newval = newval * 10 + (c - '0'); + } + } + if (!havenumber) { + error = EINVAL; + } else { + if (isnegative) { + newval = -newval; + } + if (!havedot) { + newval *= 10; + } + *(int *)arg1 = newval; + } + + if (newval < 0) + error = EINVAL; + + return(error); +} + static bool iscsi_pdu_prepare(struct icl_pdu *request) { @@ -380,6 +476,22 @@ static void iscsi_maintenance_thread_reconnect(struct iscsi_session *is) { + /* + * As we will be reconnecting shortly, + * discard outstanding data immediately on + * close(), also notify peer via RST if + * any packets come in. + */ + struct sockopt sopt; + struct linger sl; + sopt.sopt_dir = SOPT_SET; + sopt.sopt_level = SOL_SOCKET; + sopt.sopt_name = SO_LINGER; + sopt.sopt_val = &sl; + sopt.sopt_valsize = sizeof(sl); + sl.l_onoff = 1; /* non-zero value enables linger option in kernel */ + sl.l_linger = 0; /* timeout interval in seconds */ + sosetopt(is->is_conn->ic_socket, &sopt); icl_conn_close(is->is_conn); @@ -546,6 +658,8 @@ struct iscsi_bhs_nop_out *bhsno; struct iscsi_session *is; bool reconnect_needed = false; + bool fast_timer = false; + char tmpstr[8]; is = context; @@ -555,33 +669,43 @@ return; } - callout_schedule(&is->is_callout, 1 * hz); + fast_timer = ((is->is_ping_timeout <= 20) || + (is->is_login_timeout <= 20)); + callout_schedule(&is->is_callout, (fast_timer ? hz / 10 : 1 * hz)); if (is->is_conf.isc_enable == 0) goto out; - is->is_timeout++; + is->is_timeout += (fast_timer ? 1 : 10); if (is->is_waiting_for_iscsid) { if (iscsid_timeout > 0 && is->is_timeout > iscsid_timeout) { + tmpstr[0] = '\0'; + if (fast_timer) + snprintf(tmpstr, sizeof(tmpstr), ".%d", + is->is_timeout % 10); ISCSI_SESSION_WARN(is, "timed out waiting for iscsid(8) " - "for %d seconds; reconnecting", - is->is_timeout); + "for %d%s seconds; reconnecting", is->is_timeout/10, + tmpstr); reconnect_needed = true; } goto out; } if (is->is_login_phase) { - if (login_timeout > 0 && is->is_timeout > login_timeout) { - ISCSI_SESSION_WARN(is, "login timed out after %d seconds; " - "reconnecting", is->is_timeout); + if (is->is_login_timeout > 0 && is->is_timeout > is->is_login_timeout) { + tmpstr[0] = '\0'; + if (fast_timer) + snprintf(tmpstr, sizeof(tmpstr), ".%d", + is->is_timeout % 10); + ISCSI_SESSION_WARN(is, "login timed out after %d%s seconds; " + "reconnecting", is->is_timeout/10, tmpstr); reconnect_needed = true; } goto out; } - if (ping_timeout <= 0) { + if (is->is_ping_timeout <= 0) { /* * Pings are disabled. Don't send NOP-Out in this case. * Reset the timeout, to avoid triggering reconnection, @@ -591,9 +715,13 @@ goto out; } - if (is->is_timeout >= ping_timeout) { - ISCSI_SESSION_WARN(is, "no ping reply (NOP-In) after %d seconds; " - "reconnecting", ping_timeout); + if (is->is_timeout >= is->is_ping_timeout) { + tmpstr[0] = '\0'; + if (fast_timer) + snprintf(tmpstr, sizeof(tmpstr), ".%d", + ping_timeout % 10); + ISCSI_SESSION_WARN(is, "no ping reply (NOP-In) after %d%s seconds; " + "reconnecting", ping_timeout/10, tmpstr); reconnect_needed = true; goto out; } @@ -1482,6 +1610,12 @@ is->is_waiting_for_iscsid = false; is->is_login_phase = false; is->is_timeout = 0; + is->is_ping_timeout = handoff->idh_ping_timeout; + if (is->is_ping_timeout < 0) + is->is_ping_timeout = ping_timeout; + is->is_login_timeout = handoff->idh_login_timeout; + if (is->is_login_timeout < 0) + is->is_login_timeout = login_timeout; is->is_connected = true; is->is_reason[0] = '\0'; @@ -1810,6 +1944,7 @@ struct iscsi_session *is; const struct iscsi_session *is2; int error; + bool fast_timer; iscsi_sanitize_session_conf(&isa->isa_conf); if (iscsi_valid_session_conf(&isa->isa_conf) == false) @@ -1886,8 +2021,16 @@ sx_xunlock(&sc->sc_lock); return (error); } + is->is_ping_timeout = is->is_conf.isc_ping_timeout; + if (is->is_ping_timeout < 0) + is->is_ping_timeout = ping_timeout; + is->is_login_timeout = is->is_conf.isc_login_timeout; + if (is->is_login_timeout < 0) + is->is_login_timeout = login_timeout; - callout_reset(&is->is_callout, 1 * hz, iscsi_callout, is); + fast_timer = ((is->is_ping_timeout <= 20) || + (is->is_ping_timeout <= 20)); + callout_reset(&is->is_callout, (fast_timer ? hz / 10 : 1 * hz), iscsi_callout, is); TAILQ_INSERT_TAIL(&sc->sc_sessions, is, is_next); ISCSI_SESSION_LOCK(is); Index: sys/dev/iscsi/iscsi_ioctl.h =================================================================== --- sys/dev/iscsi/iscsi_ioctl.h +++ sys/dev/iscsi/iscsi_ioctl.h @@ -71,7 +71,8 @@ int isc_enable; int isc_dscp; int isc_pcp; - int isc_spare[2]; + int isc_ping_timeout; + int isc_login_timeout; }; /* @@ -138,6 +139,8 @@ int idh_max_send_data_segment_length; int idh_max_burst_length; int idh_first_burst_length; + int idh_ping_timeout; + int idh_login_timeout; }; struct iscsi_daemon_fail { Index: usr.bin/iscsictl/iscsi.conf.5 =================================================================== --- usr.bin/iscsictl/iscsi.conf.5 +++ usr.bin/iscsictl/iscsi.conf.5 @@ -162,6 +162,24 @@ to .Qq Ar 7 . When omitted, the default for the outgoing interface is used. +.It Cm PingTimeout +Specify the time in seconds to wait between pings (SCSI NOP), and +for a ping response before declaring the session as dead and +attempting a re-establishment. +If this entry is not present in the conf file, a default value of +5 seconds is taken by the driver. +The PingTimeout can be set to any positive value starting with +.Qq Ar 0.1 . +.It Cm LoginTimeout +Specify the time in seconds to wait for a login PDU to be sent or +received after trying to establish a new session. +When no login PDU is received within this time, the login on a +particular connection fails and a new reconnection attempt is made. +If this entry is not present in the conf file, a default value of +.Qq Ar 60 +secs is used. +The LoginTimeout can be set to any positive value starting with +.Qq Ar 0.1 . .El .Sh FILES .Bl -tag -width indent Index: usr.bin/iscsictl/iscsictl.h =================================================================== --- usr.bin/iscsictl/iscsictl.h +++ usr.bin/iscsictl/iscsictl.h @@ -79,6 +79,8 @@ int t_protocol; int t_dscp; int t_pcp; + int t_pingtimeout; + int t_logintimeout; char *t_offload; char *t_user; char *t_secret; Index: usr.bin/iscsictl/iscsictl.c =================================================================== --- usr.bin/iscsictl/iscsictl.c +++ usr.bin/iscsictl/iscsictl.c @@ -88,6 +88,8 @@ targ->t_conf = conf; targ->t_dscp = -1; targ->t_pcp = -1; + targ->t_pingtimeout = -1; + targ->t_logintimeout = -1; TAILQ_INSERT_TAIL(&conf->conf_targets, targ, t_next); return (targ); @@ -361,6 +363,8 @@ conf->isc_data_digest = ISCSI_DIGEST_NONE; conf->isc_dscp = targ->t_dscp; conf->isc_pcp = targ->t_pcp; + conf->isc_ping_timeout = targ->t_pingtimeout; + conf->isc_login_timeout = targ->t_logintimeout; } static int @@ -544,6 +548,16 @@ if (conf->isc_pcp != -1) xo_emit("{L:/%-26s}{V:pcp/0x%02x}\n", "Target PCP:", conf->isc_pcp); + if (conf->isc_ping_timeout != -1) + xo_emit("{L:/%-26s}{V:ping timeout/%d.%d}\n", + "Target Ping Timeout:", + conf->isc_ping_timeout/10, + conf->isc_ping_timeout%10); + if (conf->isc_login_timeout != -1) + xo_emit("{L:/%-26s}{V:login timeout/%d.%d}\n", + "Target Login Timeout:", + conf->isc_login_timeout/10, + conf->isc_login_timeout%10); xo_close_container("target"); xo_open_container("auth"); Index: usr.bin/iscsictl/parse.y =================================================================== --- usr.bin/iscsictl/parse.y +++ usr.bin/iscsictl/parse.y @@ -61,16 +61,18 @@ %token AUTH_METHOD ENABLE HEADER_DIGEST DATA_DIGEST TARGET_NAME TARGET_ADDRESS %token INITIATOR_NAME INITIATOR_ADDRESS INITIATOR_ALIAS USER SECRET %token MUTUAL_USER MUTUAL_SECRET SEMICOLON SESSION_TYPE PROTOCOL OFFLOAD -%token IGNORED EQUALS OPENING_BRACKET CLOSING_BRACKET DSCP +%token IGNORED EQUALS OPENING_BRACKET CLOSING_BRACKET DSCP PINGTIMEOUT LOGINTIMEOUT %token AF11 AF12 AF13 AF21 AF22 AF23 AF31 AF32 AF33 AF41 AF42 AF43 %token BE EF CS0 CS1 CS2 CS3 CS4 CS5 CS6 CS7 %union { char *str; + double floating; } %token STR +%token FLOAT %% @@ -133,6 +135,10 @@ dscp | pcp + | + ping_timeout + | + login_timeout ; target_name: TARGET_NAME EQUALS STR @@ -367,6 +373,58 @@ } ; +ping_timeout: PINGTIMEOUT EQUALS STR + { + uint64_t tmp; + + if (target->t_pingtimeout != -1) + xo_errx(1, "duplicated ping timeout at line %d", lineno); + + if (expand_number($3, &tmp) != 0) { + yyerror("invalid numeric value"); + free($3); + return(1); + } + target->t_pingtimeout = tmp * 10; + } + | PINGTIMEOUT EQUALS FLOAT + { + float tmp; + + if (target->t_pingtimeout != -1) + xo_errx(1, "duplicated ping timeout at line %d", lineno); + + tmp = $3; + target->t_pingtimeout = (int)(tmp * 10 + 0.5f); + } + ; + +login_timeout: LOGINTIMEOUT EQUALS STR + { + uint64_t tmp; + + if (target->t_logintimeout != -1) + xo_errx(1, "duplicated login timeout at line %d", lineno); + + if (expand_number($3, &tmp) != 0) { + yyerror("invalid numeric value"); + free($3); + return(1); + } + target->t_logintimeout = tmp * 10; + } + | LOGINTIMEOUT EQUALS FLOAT + { + float tmp; + + if (target->t_logintimeout != -1) + xo_errx(1, "duplicated login timeout at line %d", lineno); + + tmp = $3; + target->t_logintimeout = (int)(tmp * 10 + 0.5f); + } + ; + %% void Index: usr.bin/iscsictl/token.l =================================================================== --- usr.bin/iscsictl/token.l +++ usr.bin/iscsictl/token.l @@ -69,6 +69,8 @@ port { return IGNORED; } dscp { return DSCP; } pcp { return PCP; } +PingTimeout { return PINGTIMEOUT; } +LoginTimeout { return LOGINTIMEOUT; } MaxConnections { return IGNORED; } TargetAlias { return IGNORED; } TargetPortalGroupTag { return IGNORED; } @@ -111,6 +113,7 @@ cs7 { return CS7; } \"[^"]+\" { yylval.str = strndup(yytext + 1, strlen(yytext) - 2); return STR; } +[0-9]*\.[0-9]+ { yylval.floating = atof(yytext); return FLOAT; } [a-zA-Z0-9\.\-_/\:\[\]]+ { yylval.str = strdup(yytext); return STR; } \{ { return OPENING_BRACKET; } \} { return CLOSING_BRACKET; } Index: usr.sbin/iscsid/iscsid.h =================================================================== --- usr.sbin/iscsid/iscsid.h +++ usr.sbin/iscsid/iscsid.h @@ -65,6 +65,8 @@ int conn_max_send_data_segment_length; int conn_max_burst_length; int conn_first_burst_length; + int conn_ping_timeout; + int conn_login_timeout; struct chap *conn_mutual_chap; }; Index: usr.sbin/iscsid/iscsid.c =================================================================== --- usr.sbin/iscsid/iscsid.c +++ usr.sbin/iscsid/iscsid.c @@ -38,9 +38,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -325,6 +327,34 @@ from_addr); } } + { + /* + * Reduce TCP SYN_SENT timeout while + * no connectivity exists, to allow + * rapid reuse of the available slots. + */ + int keepinit = 0; + if (conn->conn_conf.isc_login_timeout > 0) { + keepinit = (conn->conn_conf.isc_login_timeout + 9) / 10; + log_debugx("session specific login_timeout at %d sec", + keepinit); + } + if (conn->conn_conf.isc_login_timeout == -1) { + char value[8]; + size_t size = sizeof(value); + sysctlbyname("kern.iscsi.login_timeout", &value, &size, + NULL, 0); + keepinit = strtol(value, NULL, 10); + log_debugx("global login_timeout at %d sec", keepinit); + } + if (keepinit > 0) { + if (setsockopt(conn->conn_socket, + IPPROTO_TCP, TCP_KEEPINIT, + &keepinit, sizeof(keepinit)) == -1) + log_warnx("setsockopt(TCP_KEEPINIT) " + "failed for %s", to_addr); + } + } if (from_ai != NULL) { error = bind(conn->conn_socket, from_ai->ai_addr, from_ai->ai_addrlen); @@ -373,6 +403,8 @@ conn->conn_max_send_data_segment_length; idh.idh_max_burst_length = conn->conn_max_burst_length; idh.idh_first_burst_length = conn->conn_first_burst_length; + idh.idh_ping_timeout = conn->conn_conf.isc_ping_timeout; + idh.idh_login_timeout = conn->conn_conf.isc_login_timeout; error = ioctl(conn->conn_iscsi_fd, ISCSIDHANDOFF, &idh); if (error != 0)