git: 7b02c1e8c6a1 - main - iscsi: Fetch limits based on a socket rather than assuming global limits.

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Mon, 18 Apr 2022 19:53:45 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=7b02c1e8c6a17d11b69988f3e1b449b783785415

commit 7b02c1e8c6a17d11b69988f3e1b449b783785415
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2022-04-18 19:48:42 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2022-04-18 19:53:28 +0000

    iscsi: Fetch limits based on a socket rather than assuming global limits.
    
    cxgbei needs the ability to return different limits based on the
    connection (e.g. if the connection is over a T5 adapter or a T6
    adapter as well as factoring in the MTU).
    
    This change plumbs through the changes in the ioctls without changing
    any of the backends.  The limits callback passed to icl_register now
    accepts a second socket argument which holds the integer file
    descriptor.  To support ABI compatiblity for old binaries, the
    callback should return "global" values if the socket fd is zero.
    
    The CTL_ISCSI_LIMITS argument used with CTL_ISCSI by ctld(8) now
    accepts the socket fd in a field that was previously part of a
    reserved spare field.  Old binaries zero this request which results in
    passing a socket fd of 0 to the limits callback.
    
    The ISCSIDREQUEST ioctl no longer returns limits.  Instead, iscsid(8)
    invokes a new ISCSIDLIMITS ioctl after establishing the connection via
    connect(2).  For ABI compat, if the old ISCSIDREQUEST is invoked, the
    global limits are still fetched (with a socket fd of 0) and returned.
    
    Reviewed by:    mav
    Sponsored by:   Chelsio Communications
    Differential Revision:  https://reviews.freebsd.org/D34928
---
 sys/cam/ctl/ctl_frontend_iscsi.c  |   2 +-
 sys/cam/ctl/ctl_ioctl.h           |   5 +-
 sys/dev/cxgbe/cxgbei/icl_cxgbei.c |   2 +-
 sys/dev/iscsi/icl.c               |  10 ++--
 sys/dev/iscsi/icl.h               |   4 +-
 sys/dev/iscsi/icl_soft.c          |   2 +-
 sys/dev/iscsi/iscsi.c             | 110 +++++++++++++++++++++++++++++++-------
 sys/dev/iscsi/iscsi_ioctl.h       |   8 ++-
 sys/dev/iser/icl_iser.c           |   2 +-
 usr.sbin/ctld/ctld.h              |   2 +-
 usr.sbin/ctld/kernel.c            |   3 +-
 usr.sbin/ctld/login.c             |   1 +
 usr.sbin/iscsid/iscsid.c          |  87 ++++++++++++++++++------------
 13 files changed, 170 insertions(+), 68 deletions(-)

diff --git a/sys/cam/ctl/ctl_frontend_iscsi.c b/sys/cam/ctl/ctl_frontend_iscsi.c
index cccb3fd90317..b78c6a7d8441 100644
--- a/sys/cam/ctl/ctl_frontend_iscsi.c
+++ b/sys/cam/ctl/ctl_frontend_iscsi.c
@@ -1896,7 +1896,7 @@ cfiscsi_ioctl_limits(struct ctl_iscsi *ci)
 
 	cilp = (struct ctl_iscsi_limits_params *)&(ci->data);
 
-	error = icl_limits(cilp->offload, false, &idl);
+	error = icl_limits(cilp->offload, false, cilp->socket, &idl);
 	if (error != 0) {
 		ci->status = CTL_ISCSI_ERROR;
 		snprintf(ci->error_str, sizeof(ci->error_str),
diff --git a/sys/cam/ctl/ctl_ioctl.h b/sys/cam/ctl/ctl_ioctl.h
index 49c48afbd766..5cb7f29d82a7 100644
--- a/sys/cam/ctl/ctl_ioctl.h
+++ b/sys/cam/ctl/ctl_ioctl.h
@@ -669,9 +669,12 @@ struct ctl_iscsi_terminate_params {
 struct ctl_iscsi_limits_params {
 	/* passed to kernel */
 	char			offload[CTL_ISCSI_OFFLOAD_LEN];
+	int			socket;
 
 	/* passed to userland */
-	size_t			spare;
+#ifdef __LP64__
+	int			spare;
+#endif
 	int			max_recv_data_segment_length;
 	int			max_send_data_segment_length;
 	int			max_burst_length;
diff --git a/sys/dev/cxgbe/cxgbei/icl_cxgbei.c b/sys/dev/cxgbe/cxgbei/icl_cxgbei.c
index a3dbf4cb88a2..f0189372bd03 100644
--- a/sys/dev/cxgbe/cxgbei/icl_cxgbei.c
+++ b/sys/dev/cxgbe/cxgbei/icl_cxgbei.c
@@ -1773,7 +1773,7 @@ cxgbei_limits(struct adapter *sc, void *arg)
 }
 
 static int
-icl_cxgbei_limits(struct icl_drv_limits *idl)
+icl_cxgbei_limits(struct icl_drv_limits *idl, int socket)
 {
 
 	/* Maximum allowed by the RFC.	cxgbei_limits will clip them. */
diff --git a/sys/dev/iscsi/icl.c b/sys/dev/iscsi/icl.c
index 1e1f1bef91bb..1a86474a5033 100644
--- a/sys/dev/iscsi/icl.c
+++ b/sys/dev/iscsi/icl.c
@@ -60,7 +60,8 @@ struct icl_module {
 	char				*im_name;
 	bool				im_iser;
 	int				im_priority;
-	int				(*im_limits)(struct icl_drv_limits *idl);
+	int				(*im_limits)(struct icl_drv_limits *idl,
+					    int socket);
 	struct icl_conn			*(*im_new_conn)(const char *name,
 					    struct mtx *lock);
 };
@@ -184,7 +185,8 @@ icl_new_conn(const char *offload, bool iser, const char *name, struct mtx *lock)
 }
 
 int
-icl_limits(const char *offload, bool iser, struct icl_drv_limits *idl)
+icl_limits(const char *offload, bool iser, int socket,
+    struct icl_drv_limits *idl)
 {
 	struct icl_module *im;
 	int error;
@@ -197,7 +199,7 @@ icl_limits(const char *offload, bool iser, struct icl_drv_limits *idl)
 		return (ENXIO);
 	}
 
-	error = im->im_limits(idl);
+	error = im->im_limits(idl, socket);
 	sx_sunlock(&sc->sc_lock);
 
 	/*
@@ -232,7 +234,7 @@ icl_limits(const char *offload, bool iser, struct icl_drv_limits *idl)
 
 int
 icl_register(const char *offload, bool iser, int priority,
-    int (*limits)(struct icl_drv_limits *),
+    int (*limits)(struct icl_drv_limits *, int),
     struct icl_conn *(*new_conn)(const char *, struct mtx *))
 {
 	struct icl_module *im;
diff --git a/sys/dev/iscsi/icl.h b/sys/dev/iscsi/icl.h
index edd43a45ba2e..59b1e2aacc96 100644
--- a/sys/dev/iscsi/icl.h
+++ b/sys/dev/iscsi/icl.h
@@ -137,10 +137,10 @@ typedef void (*icl_pdu_cb)(struct icl_pdu *, int error);
 
 struct icl_conn	*icl_new_conn(const char *offload, bool iser, const char *name,
 		    struct mtx *lock);
-int		icl_limits(const char *offload, bool iser,
+int		icl_limits(const char *offload, bool iser, int socket,
 		    struct icl_drv_limits *idl);
 int		icl_register(const char *offload, bool iser, int priority,
-		    int (*limits)(struct icl_drv_limits *),
+		    int (*limits)(struct icl_drv_limits *, int),
 		    struct icl_conn *(*new_conn)(const char *, struct mtx *));
 int		icl_unregister(const char *offload, bool rdma);
 
diff --git a/sys/dev/iscsi/icl_soft.c b/sys/dev/iscsi/icl_soft.c
index c8adad7e9d03..669cb9618d3a 100644
--- a/sys/dev/iscsi/icl_soft.c
+++ b/sys/dev/iscsi/icl_soft.c
@@ -1652,7 +1652,7 @@ icl_soft_conn_transfer_done(struct icl_conn *ic, void *prv)
 }
 
 static int
-icl_soft_limits(struct icl_drv_limits *idl)
+icl_soft_limits(struct icl_drv_limits *idl, int socket)
 {
 
 	idl->idl_max_recv_data_segment_length = max_data_segment_length;
diff --git a/sys/dev/iscsi/iscsi.c b/sys/dev/iscsi/iscsi.c
index 9535aa9b9e2a..f7a585c02545 100644
--- a/sys/dev/iscsi/iscsi.c
+++ b/sys/dev/iscsi/iscsi.c
@@ -78,6 +78,20 @@ __FBSDID("$FreeBSD$");
 FEATURE(iscsi_kernel_proxy, "iSCSI initiator built with ICL_KERNEL_PROXY");
 #endif
 
+#ifdef COMPAT_FREEBSD13
+struct iscsi_daemon_request13 {
+	unsigned int			idr_session_id;
+	struct iscsi_session_conf	idr_conf;
+	uint8_t				idr_isid[6];
+	uint16_t			idr_tsih;
+	uint16_t			idr_spare_cid;
+	struct iscsi_session_limits	idr_limits;
+	int				idr_spare[4];
+};
+
+#define	ISCSIDWAIT13	_IOR('I', 0x01, struct iscsi_daemon_request13)
+#endif
+
 /*
  * XXX: This is global so the iscsi_unload() can access it.
  * 	Think about how to do this properly.
@@ -1423,10 +1437,9 @@ iscsi_pdu_handle_reject(struct icl_pdu *response)
 
 static int
 iscsi_ioctl_daemon_wait(struct iscsi_softc *sc,
-    struct iscsi_daemon_request *request)
+    struct iscsi_daemon_request *request, bool freebsd13)
 {
 	struct iscsi_session *is;
-	struct icl_drv_limits idl;
 	int error;
 
 	sx_slock(&sc->sc_lock);
@@ -1472,29 +1485,78 @@ iscsi_ioctl_daemon_wait(struct iscsi_softc *sc,
 		memcpy(&request->idr_conf, &is->is_conf,
 		    sizeof(request->idr_conf));
 
-		error = icl_limits(is->is_conf.isc_offload,
-		    is->is_conf.isc_iser, &idl);
-		if (error != 0) {
-			ISCSI_SESSION_WARN(is, "icl_limits for offload \"%s\" "
-			    "failed with error %d", is->is_conf.isc_offload,
-			    error);
-			sx_sunlock(&sc->sc_lock);
-			return (error);
-		}
-		request->idr_limits.isl_max_recv_data_segment_length =
-		    idl.idl_max_recv_data_segment_length;
-		request->idr_limits.isl_max_send_data_segment_length =
-		    idl.idl_max_send_data_segment_length;
-		request->idr_limits.isl_max_burst_length =
-		    idl.idl_max_burst_length;
-		request->idr_limits.isl_first_burst_length =
-		    idl.idl_first_burst_length;
+#ifdef COMPAT_FREEBSD13
+		if (freebsd13) {
+			struct icl_drv_limits idl;
+			struct iscsi_daemon_request13 *request13;
 
+			error = icl_limits(is->is_conf.isc_offload,
+			    is->is_conf.isc_iser, 0, &idl);
+			if (error != 0) {
+				ISCSI_SESSION_WARN(is, "icl_limits for "
+				    "offload \"%s\" failed with error %d",
+				    is->is_conf.isc_offload, error);
+				sx_sunlock(&sc->sc_lock);
+				return (error);
+			}
+			request13 = (struct iscsi_daemon_request13 *)request;
+			request13->idr_limits.isl_max_recv_data_segment_length =
+			    idl.idl_max_recv_data_segment_length;
+			request13->idr_limits.isl_max_send_data_segment_length =
+			    idl.idl_max_send_data_segment_length;
+			request13->idr_limits.isl_max_burst_length =
+			    idl.idl_max_burst_length;
+			request13->idr_limits.isl_first_burst_length =
+			    idl.idl_first_burst_length;
+		}
+#endif
 		sx_sunlock(&sc->sc_lock);
 		return (0);
 	}
 }
 
+static int
+iscsi_ioctl_daemon_limits(struct iscsi_softc *sc,
+    struct iscsi_daemon_limits *limits)
+{
+	struct icl_drv_limits idl;
+	struct iscsi_session *is;
+	int error;
+
+	sx_slock(&sc->sc_lock);
+
+	/*
+	 * Find the session to fetch limits for.
+	 */
+	TAILQ_FOREACH(is, &sc->sc_sessions, is_next) {
+		if (is->is_id == limits->idl_session_id)
+			break;
+	}
+	if (is == NULL) {
+		sx_sunlock(&sc->sc_lock);
+		return (ESRCH);
+	}
+
+	error = icl_limits(is->is_conf.isc_offload, is->is_conf.isc_iser,
+	    limits->idl_socket, &idl);
+	sx_sunlock(&sc->sc_lock);
+	if (error != 0) {
+		ISCSI_SESSION_WARN(is, "icl_limits for offload \"%s\" "
+		    "failed with error %d", is->is_conf.isc_offload, error);
+		return (error);
+	}
+	limits->idl_limits.isl_max_recv_data_segment_length =
+	    idl.idl_max_recv_data_segment_length;
+	limits->idl_limits.isl_max_send_data_segment_length =
+	    idl.idl_max_send_data_segment_length;
+	limits->idl_limits.isl_max_burst_length =
+	    idl.idl_max_burst_length;
+	limits->idl_limits.isl_first_burst_length =
+	    idl.idl_first_burst_length;
+	
+	return (0);
+}
+
 static int
 iscsi_ioctl_daemon_handoff(struct iscsi_softc *sc,
     struct iscsi_daemon_handoff *handoff)
@@ -2179,7 +2241,15 @@ iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode,
 	switch (cmd) {
 	case ISCSIDWAIT:
 		return (iscsi_ioctl_daemon_wait(sc,
-		    (struct iscsi_daemon_request *)arg));
+		    (struct iscsi_daemon_request *)arg, false));
+#ifdef COMPAT_FREEBSD13
+	case ISCSIDWAIT13:
+		return (iscsi_ioctl_daemon_wait(sc,
+		    (struct iscsi_daemon_request *)arg, true));
+#endif
+	case ISCSIDLIMITS:
+		return (iscsi_ioctl_daemon_limits(sc,
+		    (struct iscsi_daemon_limits *)arg));
 	case ISCSIDHANDOFF:
 		return (iscsi_ioctl_daemon_handoff(sc,
 		    (struct iscsi_daemon_handoff *)arg));
diff --git a/sys/dev/iscsi/iscsi_ioctl.h b/sys/dev/iscsi/iscsi_ioctl.h
index c1de089c9d3f..caf403b55970 100644
--- a/sys/dev/iscsi/iscsi_ioctl.h
+++ b/sys/dev/iscsi/iscsi_ioctl.h
@@ -117,10 +117,15 @@ struct iscsi_daemon_request {
 	uint8_t				idr_isid[6];
 	uint16_t			idr_tsih;
 	uint16_t			idr_spare_cid;
-	struct iscsi_session_limits	idr_limits;
 	int				idr_spare[4];
 };
 
+struct iscsi_daemon_limits {
+	unsigned int			idl_session_id;
+	int				idl_socket;
+	struct iscsi_session_limits	idl_limits;
+};
+
 struct iscsi_daemon_handoff {
 	unsigned int			idh_session_id;
 	int				idh_socket;
@@ -150,6 +155,7 @@ struct iscsi_daemon_fail {
 #define	ISCSIDWAIT	_IOR('I', 0x01, struct iscsi_daemon_request)
 #define	ISCSIDHANDOFF	_IOW('I', 0x02, struct iscsi_daemon_handoff)
 #define	ISCSIDFAIL	_IOW('I', 0x03, struct iscsi_daemon_fail)
+#define	ISCSIDLIMITS	_IOWR('I', 0x07, struct iscsi_daemon_limits)
 
 #ifdef ICL_KERNEL_PROXY
 
diff --git a/sys/dev/iser/icl_iser.c b/sys/dev/iser/icl_iser.c
index 140b5622385d..d43c1dc3b885 100644
--- a/sys/dev/iser/icl_iser.c
+++ b/sys/dev/iser/icl_iser.c
@@ -511,7 +511,7 @@ iser_conn_task_done(struct icl_conn *ic, void *prv)
 }
 
 static int
-iser_limits(struct icl_drv_limits *idl)
+iser_limits(struct icl_drv_limits *idl, int socket)
 {
 
 	idl->idl_max_recv_data_segment_length = 128 * 1024;
diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h
index 293f5378592f..de2f480af30b 100644
--- a/usr.sbin/ctld/ctld.h
+++ b/usr.sbin/ctld/ctld.h
@@ -356,7 +356,7 @@ int			kernel_lun_add(struct lun *lun);
 int			kernel_lun_modify(struct lun *lun);
 int			kernel_lun_remove(struct lun *lun);
 void			kernel_handoff(struct ctld_connection *conn);
-void			kernel_limits(const char *offload,
+void			kernel_limits(const char *offload, int s,
 			    int *max_recv_data_segment_length,
 			    int *max_send_data_segment_length,
 			    int *max_burst_length,
diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c
index dbbd7c35f2d9..7e5d2a386878 100644
--- a/usr.sbin/ctld/kernel.c
+++ b/usr.sbin/ctld/kernel.c
@@ -954,7 +954,7 @@ kernel_handoff(struct ctld_connection *conn)
 }
 
 void
-kernel_limits(const char *offload, int *max_recv_dsl, int *max_send_dsl,
+kernel_limits(const char *offload, int s, int *max_recv_dsl, int *max_send_dsl,
     int *max_burst_length, int *first_burst_length)
 {
 	struct ctl_iscsi req;
@@ -967,6 +967,7 @@ kernel_limits(const char *offload, int *max_recv_dsl, int *max_send_dsl,
 	if (offload != NULL) {
 		strlcpy(cilp->offload, offload, sizeof(cilp->offload));
 	}
+	cilp->socket = s;
 
 	if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) {
 		log_err(1, "error issuing CTL_ISCSI ioctl; "
diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c
index 19fab3bc494c..c52733f7894a 100644
--- a/usr.sbin/ctld/login.c
+++ b/usr.sbin/ctld/login.c
@@ -698,6 +698,7 @@ login_negotiate(struct ctld_connection *conn, struct pdu *request)
 		conn->conn_max_burst_limit = (1 << 24) - 1;
 		conn->conn_first_burst_limit = (1 << 24) - 1;
 		kernel_limits(conn->conn_portal->p_portal_group->pg_offload,
+		    conn->conn.conn_socket,
 		    &conn->conn_max_recv_data_segment_limit,
 		    &conn->conn_max_send_data_segment_limit,
 		    &conn->conn_max_burst_limit,
diff --git a/usr.sbin/iscsid/iscsid.c b/usr.sbin/iscsid/iscsid.c
index 5555ffccbf7c..c81692948c1a 100644
--- a/usr.sbin/iscsid/iscsid.c
+++ b/usr.sbin/iscsid/iscsid.c
@@ -225,7 +225,6 @@ static struct iscsid_connection *
 connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
 {
 	struct iscsid_connection *conn;
-	struct iscsi_session_limits *isl;
 	struct addrinfo *from_ai, *to_ai;
 	const char *from_addr, *to_addr;
 #ifdef ICL_KERNEL_PROXY
@@ -249,38 +248,6 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
 	    sizeof(conn->conn.conn_isid));
 	conn->conn.conn_tsih = request->idr_tsih;
 
-	/*
-	 * Read the driver limits and provide reasonable defaults for the ones
-	 * the driver doesn't care about.  If a max_snd_dsl is not explicitly
-	 * provided by the driver then we'll make sure both conn->max_snd_dsl
-	 * and isl->max_snd_dsl are set to the rcv_dsl.  This preserves historic
-	 * behavior.
-	 */
-	isl = &conn->conn_limits;
-	memcpy(isl, &request->idr_limits, sizeof(*isl));
-	if (isl->isl_max_recv_data_segment_length == 0)
-		isl->isl_max_recv_data_segment_length = (1 << 24) - 1;
-	if (isl->isl_max_send_data_segment_length == 0)
-		isl->isl_max_send_data_segment_length =
-		    isl->isl_max_recv_data_segment_length;
-	if (isl->isl_max_burst_length == 0)
-		isl->isl_max_burst_length = (1 << 24) - 1;
-	if (isl->isl_first_burst_length == 0)
-		isl->isl_first_burst_length = (1 << 24) - 1;
-	if (isl->isl_first_burst_length > isl->isl_max_burst_length)
-		isl->isl_first_burst_length = isl->isl_max_burst_length;
-
-	/*
-	 * Limit default send length in case it won't be negotiated.
-	 * We can't do it for other limits, since they may affect both
-	 * sender and receiver operation, and we must obey defaults.
-	 */
-	if (conn->conn.conn_max_send_data_segment_length >
-	    isl->isl_max_send_data_segment_length) {
-		conn->conn.conn_max_send_data_segment_length =
-		    isl->isl_max_send_data_segment_length;
-	}
-
 	from_addr = conn->conn_conf.isc_initiator_addr;
 	to_addr = conn->conn_conf.isc_target_addr;
 
@@ -436,6 +403,56 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request)
 	return (conn);
 }
 
+static void
+limits(struct iscsid_connection *conn)
+{
+	struct iscsi_daemon_limits idl;
+	struct iscsi_session_limits *isl;
+	int error;
+
+	log_debugx("fetching limits from the kernel");
+
+	memset(&idl, 0, sizeof(idl));
+	idl.idl_session_id = conn->conn_session_id;
+	idl.idl_socket = conn->conn.conn_socket;
+
+	error = ioctl(conn->conn_iscsi_fd, ISCSIDLIMITS, &idl);
+	if (error != 0)
+		log_err(1, "ISCSIDLIMITS");
+	
+	/*
+	 * Read the driver limits and provide reasonable defaults for the ones
+	 * the driver doesn't care about.  If a max_snd_dsl is not explicitly
+	 * provided by the driver then we'll make sure both conn->max_snd_dsl
+	 * and isl->max_snd_dsl are set to the rcv_dsl.  This preserves historic
+	 * behavior.
+	 */
+	isl = &conn->conn_limits;
+	memcpy(isl, &idl.idl_limits, sizeof(*isl));
+	if (isl->isl_max_recv_data_segment_length == 0)
+		isl->isl_max_recv_data_segment_length = (1 << 24) - 1;
+	if (isl->isl_max_send_data_segment_length == 0)
+		isl->isl_max_send_data_segment_length =
+		    isl->isl_max_recv_data_segment_length;
+	if (isl->isl_max_burst_length == 0)
+		isl->isl_max_burst_length = (1 << 24) - 1;
+	if (isl->isl_first_burst_length == 0)
+		isl->isl_first_burst_length = (1 << 24) - 1;
+	if (isl->isl_first_burst_length > isl->isl_max_burst_length)
+		isl->isl_first_burst_length = isl->isl_max_burst_length;
+
+	/*
+	 * Limit default send length in case it won't be negotiated.
+	 * We can't do it for other limits, since they may affect both
+	 * sender and receiver operation, and we must obey defaults.
+	 */
+	if (conn->conn.conn_max_send_data_segment_length >
+	    isl->isl_max_send_data_segment_length) {
+		conn->conn.conn_max_send_data_segment_length =
+		    isl->isl_max_send_data_segment_length;
+	}
+}
+
 static void
 handoff(struct iscsid_connection *conn)
 {
@@ -502,6 +519,7 @@ capsicate(struct iscsid_connection *conn)
 		ISCSIDSEND,
 		ISCSIDRECEIVE,
 #endif
+		ISCSIDLIMITS,
 		ISCSIDHANDOFF,
 		ISCSIDFAIL,
 		ISCSISADD,
@@ -626,8 +644,9 @@ handle_request(int iscsi_fd, const struct iscsi_daemon_request *request, int tim
 	}
 
 	conn = connection_new(iscsi_fd, request);
-	set_timeout(timeout);
 	capsicate(conn);
+	limits(conn);
+	set_timeout(timeout);
 	login(conn);
 	if (conn->conn_conf.isc_discovery != 0)
 		discovery(conn);