Index: sys/cam/ctl/ctl_frontend_iscsi.h =================================================================== --- sys/cam/ctl/ctl_frontend_iscsi.h +++ sys/cam/ctl/ctl_frontend_iscsi.h @@ -84,7 +84,8 @@ struct cv cs_maintenance_cv; bool cs_terminating; bool cs_tasks_aborted; - size_t cs_max_data_segment_length; + size_t cs_max_recv_data_segment_length; + size_t cs_max_send_data_segment_length; size_t cs_max_burst_length; size_t cs_first_burst_length; bool cs_immediate_data; Index: sys/cam/ctl/ctl_frontend_iscsi.c =================================================================== --- sys/cam/ctl/ctl_frontend_iscsi.c +++ sys/cam/ctl/ctl_frontend_iscsi.c @@ -1512,7 +1512,8 @@ */ cs->cs_cmdsn = cihp->cmdsn; cs->cs_statsn = cihp->statsn; - cs->cs_max_data_segment_length = cihp->max_recv_data_segment_length; + cs->cs_max_recv_data_segment_length = cihp->max_recv_data_segment_length; + cs->cs_max_send_data_segment_length = cihp->max_send_data_segment_length; cs->cs_max_burst_length = cihp->max_burst_length; cs->cs_first_burst_length = cihp->first_burst_length; cs->cs_immediate_data = !!cihp->immediate_data; @@ -1652,7 +1653,8 @@ "%u" "%s" "%s" - "%zd" + "%zd" + "%zd" "%zd" "%zd" "%d" @@ -1665,7 +1667,8 @@ cs->cs_target->ct_tag, cs->cs_conn->ic_header_crc32c ? "CRC32C" : "None", cs->cs_conn->ic_data_crc32c ? "CRC32C" : "None", - cs->cs_max_data_segment_length, + cs->cs_max_recv_data_segment_length, + cs->cs_max_send_data_segment_length, cs->cs_max_burst_length, cs->cs_first_burst_length, cs->cs_immediate_data, @@ -1794,12 +1797,12 @@ cfiscsi_ioctl_limits(struct ctl_iscsi *ci) { struct ctl_iscsi_limits_params *cilp; + struct icl_drv_limits idl; int error; cilp = (struct ctl_iscsi_limits_params *)&(ci->data); - error = icl_limits(cilp->offload, false, - &cilp->data_segment_limit); + error = icl_limits(cilp->offload, false, &idl); if (error != 0) { ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), @@ -1808,6 +1811,11 @@ return; } + cilp->max_recv_data_segment_length = idl.max_recv_data_segment_length; + cilp->max_send_data_segment_length = idl.max_send_data_segment_length; + cilp->max_burst_length = idl.max_burst_length; + cilp->first_burst_length = idl.first_burst_length; + ci->status = CTL_ISCSI_OK; } @@ -2466,12 +2474,12 @@ /* * Truncate to maximum data segment length. */ - KASSERT(response->ip_data_len < cs->cs_max_data_segment_length, - ("ip_data_len %zd >= max_data_segment_length %zd", - response->ip_data_len, cs->cs_max_data_segment_length)); + KASSERT(response->ip_data_len < cs->cs_max_send_data_segment_length, + ("ip_data_len %zd >= max_send_data_segment_length %zd", + response->ip_data_len, cs->cs_max_send_data_segment_length)); if (response->ip_data_len + len > - cs->cs_max_data_segment_length) { - len = cs->cs_max_data_segment_length - + cs->cs_max_send_data_segment_length) { + len = cs->cs_max_send_data_segment_length - response->ip_data_len; KASSERT(len <= sg_len, ("len %zd > sg_len %zd", len, sg_len)); @@ -2529,7 +2537,7 @@ i++; } - if (response->ip_data_len == cs->cs_max_data_segment_length) { + if (response->ip_data_len == cs->cs_max_send_data_segment_length) { /* * Can't stuff more data into the current PDU; * queue it. Note that's not enough to check Index: sys/cam/ctl/ctl_ioctl.h =================================================================== --- sys/cam/ctl/ctl_ioctl.h +++ sys/cam/ctl/ctl_ioctl.h @@ -622,7 +622,7 @@ char target_name[CTL_ISCSI_NAME_LEN]; int socket; int portal_group_tag; - + /* * Connection parameters negotiated by ctld(8). */ @@ -637,10 +637,10 @@ char offload[CTL_ISCSI_OFFLOAD_LEN]; #ifdef ICL_KERNEL_PROXY int connection_id; - int spare[1]; #else - int spare[2]; + int spare; #endif + int max_send_data_segment_length; }; struct ctl_iscsi_list_params { @@ -671,11 +671,15 @@ }; struct ctl_iscsi_limits_params { + /* passed to kernel */ char offload[CTL_ISCSI_OFFLOAD_LEN]; - /* passed to kernel */ - size_t data_segment_limit; - /* passed to userland */ - int spare[4]; + + /* passed to userland */ + size_t spare; + int max_recv_data_segment_length; + int max_send_data_segment_length; + int max_burst_length; + int first_burst_length; }; #ifdef ICL_KERNEL_PROXY Index: sys/dev/cxgbe/cxgbei/icl_cxgbei.c =================================================================== --- sys/dev/cxgbe/cxgbei/icl_cxgbei.c +++ sys/dev/cxgbe/cxgbei/icl_cxgbei.c @@ -832,10 +832,13 @@ } static int -icl_cxgbei_limits(size_t *limitp) +icl_cxgbei_limits(struct icl_drv_limits *idl) { - *limitp = CXGBEI_MAX_DSL; + idl->max_recv_data_segment_length = CXGBEI_MAX_DSL; + idl->max_send_data_segment_length = CXGBEI_MAX_DSL; + idl->max_burst_length = 2 * 1024 * 1024; + idl->first_burst_length = CXGBEI_MAX_DSL; return (0); } Index: sys/dev/iscsi/icl.h =================================================================== --- sys/dev/iscsi/icl.h +++ sys/dev/iscsi/icl.h @@ -126,12 +126,19 @@ void *ic_prv0; }; +struct icl_drv_limits { + int max_recv_data_segment_length; + int max_send_data_segment_length; + int max_burst_length; + int first_burst_length; +}; + 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, size_t *limitp); - +int icl_limits(const char *offload, bool iser, + struct icl_drv_limits *idl); int icl_register(const char *offload, bool iser, int priority, - int (*limits)(size_t *), + int (*limits)(struct icl_drv_limits *), struct icl_conn *(*new_conn)(const char *, struct mtx *)); int icl_unregister(const char *offload, bool rdma); Index: sys/dev/iscsi/icl.c =================================================================== --- sys/dev/iscsi/icl.c +++ sys/dev/iscsi/icl.c @@ -59,7 +59,7 @@ char *im_name; bool im_iser; int im_priority; - int (*im_limits)(size_t *limitp); + int (*im_limits)(struct icl_drv_limits *idl); struct icl_conn *(*im_new_conn)(const char *name, struct mtx *lock); }; @@ -182,11 +182,12 @@ } int -icl_limits(const char *offload, bool iser, size_t *limitp) +icl_limits(const char *offload, bool iser, struct icl_drv_limits *idl) { struct icl_module *im; int error; + bzero(idl, sizeof(*idl)); sx_slock(&sc->sc_lock); im = icl_find(offload, iser, false); if (im == NULL) { @@ -194,14 +195,42 @@ return (ENXIO); } - error = im->im_limits(limitp); + error = im->im_limits(idl); sx_sunlock(&sc->sc_lock); + /* + * Validate the limits provided by the driver against values allowed by + * the iSCSI RFC. 0 means iscsid/ctld should pick a reasonable value. + * + * Note that max_send_dsl is an internal implementation detail and not + * part of the RFC. + */ +#define OUT_OF_RANGE(x, lo, hi) ((x) != 0 && ((x) < (lo) || (x) > (hi))) + if (error == 0 && + (OUT_OF_RANGE(idl->max_recv_data_segment_length, 512, 16777215) || + OUT_OF_RANGE(idl->max_send_data_segment_length, 512, 16777215) || + OUT_OF_RANGE(idl->max_burst_length, 512, 16777215) || + OUT_OF_RANGE(idl->first_burst_length, 512, 16777215))) { + error = EINVAL; + } +#undef OUT_OF_RANGE + + /* + * If both first_burst and max_burst are provided then first_burst must + * not exceed max_burst. + */ + if (error == 0 && idl->first_burst_length > 0 && + idl->max_burst_length > 0 && + idl->first_burst_length > idl->max_burst_length) { + error = EINVAL; + } + return (error); } int -icl_register(const char *offload, bool iser, int priority, int (*limits)(size_t *), +icl_register(const char *offload, bool iser, int priority, + int (*limits)(struct icl_drv_limits *), struct icl_conn *(*new_conn)(const char *, struct mtx *)) { struct icl_module *im; Index: sys/dev/iscsi/icl_soft.c =================================================================== --- sys/dev/iscsi/icl_soft.c +++ sys/dev/iscsi/icl_soft.c @@ -1474,10 +1474,10 @@ } static int -icl_soft_limits(size_t *limitp) +icl_soft_limits(struct icl_drv_limits *idl) { - *limitp = 128 * 1024; + idl->max_recv_data_segment_length = 128 * 1024; return (0); } Index: sys/dev/iscsi/iscsi.h =================================================================== --- sys/dev/iscsi/iscsi.h +++ sys/dev/iscsi/iscsi.h @@ -67,7 +67,8 @@ uint8_t is_isid[6]; uint16_t is_tsih; bool is_immediate_data; - size_t is_max_data_segment_length; + size_t is_max_recv_data_segment_length; + size_t is_max_send_data_segment_length; char is_target_alias[ISCSI_ALIAS_LEN]; TAILQ_HEAD(, iscsi_outstanding) is_outstanding; Index: sys/dev/iscsi/iscsi.c =================================================================== --- sys/dev/iscsi/iscsi.c +++ sys/dev/iscsi/iscsi.c @@ -1204,8 +1204,8 @@ for (;;) { len = total_len; - if (len > is->is_max_data_segment_length) - len = is->is_max_data_segment_length; + if (len > is->is_max_send_data_segment_length) + len = is->is_max_send_data_segment_length; if (off + len > csio->dxfer_len) { ISCSI_SESSION_WARN(is, "target requested invalid " @@ -1313,6 +1313,7 @@ struct iscsi_daemon_request *request) { struct iscsi_session *is; + struct icl_drv_limits idl; int error; sx_slock(&sc->sc_lock); @@ -1352,10 +1353,9 @@ request->idr_tsih = 0; /* New or reinstated session. */ memcpy(&request->idr_conf, &is->is_conf, sizeof(request->idr_conf)); - + error = icl_limits(is->is_conf.isc_offload, - is->is_conf.isc_iser, - &request->idr_limits.isl_max_data_segment_length); + 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, @@ -1363,6 +1363,13 @@ sx_sunlock(&sc->sc_lock); return (error); } + request->idr_limits.isl_max_recv_data_segment_length = + idl.max_recv_data_segment_length; + request->idr_limits.isl_max_send_data_segment_length = + idl.max_recv_data_segment_length; + request->idr_limits.isl_max_burst_length = idl.max_burst_length; + request->idr_limits.isl_first_burst_length = + idl.first_burst_length; sx_sunlock(&sc->sc_lock); return (0); @@ -1417,12 +1424,12 @@ is->is_initial_r2t = handoff->idh_initial_r2t; is->is_immediate_data = handoff->idh_immediate_data; - /* - * Cap MaxRecvDataSegmentLength obtained from the target to the maximum - * size supported by our ICL module. - */ - is->is_max_data_segment_length = min(ic->ic_max_data_segment_length, - handoff->idh_max_data_segment_length); + /* XXXNP: what is icl_max_data_segment_length for anyway? */ + MPASS(is->is_max_recv_data_segment_length <= ic->ic_max_data_segment_length); + is->is_max_recv_data_segment_length = + handoff->idh_max_recv_data_segment_length; + is->is_max_send_data_segment_length = + handoff->idh_max_send_data_segment_length; is->is_max_burst_length = handoff->idh_max_burst_length; is->is_first_burst_length = handoff->idh_first_burst_length; @@ -1634,7 +1641,7 @@ return (EIO); datalen = ids->ids_data_segment_len; - if (datalen > ISCSI_MAX_DATA_SEGMENT_LENGTH) + if (datalen > is->is_max_send_data_segment_length) return (EINVAL); if (datalen > 0) { data = malloc(datalen, M_ISCSI, M_WAITOK); @@ -1933,12 +1940,13 @@ else iss.iss_data_digest = ISCSI_DIGEST_NONE; - iss.iss_max_data_segment_length = is->is_max_data_segment_length; + iss.iss_max_send_data_segment_length = is->is_max_send_data_segment_length; + iss.iss_max_recv_data_segment_length = is->is_max_recv_data_segment_length; iss.iss_max_burst_length = is->is_max_burst_length; iss.iss_first_burst_length = is->is_first_burst_length; iss.iss_immediate_data = is->is_immediate_data; iss.iss_connected = is->is_connected; - + error = copyout(&iss, isl->isl_pstates + i, sizeof(iss)); if (error != 0) { sx_sunlock(&sc->sc_lock); @@ -2262,9 +2270,9 @@ ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_first_burst_length); len = is->is_first_burst_length; } - if (len > is->is_max_data_segment_length) { - ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_max_data_segment_length); - len = is->is_max_data_segment_length; + if (len > is->is_max_send_data_segment_length) { + ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_max_send_data_segment_length); + len = is->is_max_send_data_segment_length; } error = icl_pdu_append_data(request, csio->data_ptr, len, M_NOWAIT); Index: sys/dev/iscsi/iscsi_ioctl.h =================================================================== --- sys/dev/iscsi/iscsi_ioctl.h +++ sys/dev/iscsi/iscsi_ioctl.h @@ -76,8 +76,12 @@ * iscsid(8) must obey those when negotiating operational parameters. */ struct iscsi_session_limits { - size_t isl_max_data_segment_length; - int isl_spare[8]; + size_t isl_spare0; + int isl_max_recv_data_segment_length; + int isl_max_send_data_segment_length; + int isl_max_burst_length; + int isl_first_burst_length; + int isl_spare[4]; }; /* @@ -89,14 +93,15 @@ char iss_target_alias[ISCSI_ALIAS_LEN]; int iss_header_digest; int iss_data_digest; - int iss_max_data_segment_length; + int iss_max_recv_data_segment_length; int iss_max_burst_length; int iss_first_burst_length; int iss_immediate_data; int iss_connected; char iss_reason[ISCSI_REASON_LEN]; char iss_offload[ISCSI_OFFLOAD_LEN]; - int iss_spare[4]; + int iss_max_send_data_segment_length; + int iss_spare[3]; }; /* @@ -122,12 +127,14 @@ uint32_t idh_statsn; int idh_header_digest; int idh_data_digest; - size_t idh_max_data_segment_length; + size_t spare; size_t idh_max_burst_length; size_t idh_first_burst_length; int idh_immediate_data; int idh_initial_r2t; - int idh_spare[4]; + int idh_max_recv_data_segment_length; + int idh_max_send_data_segment_length; + int idh_spare2[2]; }; struct iscsi_daemon_fail { Index: sys/dev/iser/icl_iser.c =================================================================== --- sys/dev/iser/icl_iser.c +++ sys/dev/iser/icl_iser.c @@ -483,9 +483,9 @@ } static int -iser_limits(size_t *limitp) +iser_limits(struct icl_drv_limits *idl) { - *limitp = 128 * 1024; + idl->max_recv_data_segment_length = 128 * 1024; return (0); } Index: usr.bin/iscsictl/iscsictl.c =================================================================== --- usr.bin/iscsictl/iscsictl.c +++ usr.bin/iscsictl/iscsictl.c @@ -565,8 +565,10 @@ "Data digest:", state->iss_data_digest == ISCSI_DIGEST_CRC32C ? "CRC32C" : "None"); - xo_emit("{L:/%-18s}{V:dataSegmentLen/%d}\n", - "DataSegmentLen:", state->iss_max_data_segment_length); + xo_emit("{L:/%-18s}{V:recvDataSegmentLen/%d}\n", + "MaxRecvDSL:", state->iss_max_recv_data_segment_length); + xo_emit("{L:/%-18s}{V:sendDataSegmentLen/%d}\n", + "MaxSendDSL:", state->iss_max_send_data_segment_length); xo_emit("{L:/%-18s}{V:maxBurstLen/%d}\n", "MaxBurstLen:", state->iss_max_burst_length); xo_emit("{L:/%-18s}{V:firstBurstLen/%d}\n", Index: usr.sbin/ctladm/ctladm.c =================================================================== --- usr.sbin/ctladm/ctladm.c +++ usr.sbin/ctladm/ctladm.c @@ -2794,7 +2794,8 @@ char *target_alias; char *header_digest; char *data_digest; - char *max_data_segment_length; + char *max_recv_data_segment_length; + char *max_send_data_segment_length; char *max_burst_length; char *first_burst_length; char *offload; @@ -2908,8 +2909,11 @@ } else if (strcmp(name, "data_digest") == 0) { cur_conn->data_digest = str; str = NULL; - } else if (strcmp(name, "max_data_segment_length") == 0) { - cur_conn->max_data_segment_length = str; + } else if (strcmp(name, "max_recv_data_segment_length") == 0) { + cur_conn->max_recv_data_segment_length = str; + str = NULL; + } else if (strcmp(name, "max_send_data_segment_length") == 0) { + cur_conn->max_send_data_segment_length = str; str = NULL; } else if (strcmp(name, "max_burst_length") == 0) { cur_conn->max_burst_length = str; @@ -3038,7 +3042,8 @@ printf("Target alias: %s\n", conn->target_alias); printf("Header digest: %s\n", conn->header_digest); printf("Data digest: %s\n", conn->data_digest); - printf("DataSegmentLen: %s\n", conn->max_data_segment_length); + printf("MaxRecvDSL: %s\n", conn->max_recv_data_segment_length); + printf("MaxSendDSL: %s\n", conn->max_send_data_segment_length); printf("MaxBurstLen: %s\n", conn->max_burst_length); printf("FirstBurstLen: %s\n", conn->first_burst_length); printf("ImmediateData: %s\n", conn->immediate_data ? "Yes" : "No"); Index: usr.sbin/ctld/ctld.h =================================================================== --- usr.sbin/ctld/ctld.h +++ usr.sbin/ctld/ctld.h @@ -242,8 +242,8 @@ struct sockaddr_storage conn_initiator_sa; uint32_t conn_cmdsn; uint32_t conn_statsn; - size_t conn_data_segment_limit; - size_t conn_max_data_segment_length; + size_t conn_max_recv_data_segment_length; + size_t conn_max_send_data_segment_length; size_t conn_max_burst_length; size_t conn_first_burst_length; int conn_immediate_data; @@ -404,7 +404,10 @@ int kernel_lun_remove(struct lun *lun); void kernel_handoff(struct connection *conn); void kernel_limits(const char *offload, - size_t *max_data_segment_length); + size_t *max_recv_data_segment_length, + size_t *max_send_data_segment_length, + size_t *max_burst_length, + size_t *first_burst_length); int kernel_port_add(struct port *port); int kernel_port_update(struct port *port, struct port *old); int kernel_port_remove(struct port *port); Index: usr.sbin/ctld/ctld.c =================================================================== --- usr.sbin/ctld/ctld.c +++ usr.sbin/ctld/ctld.c @@ -1578,8 +1578,9 @@ /* * Default values, from RFC 3720, section 12. */ - conn->conn_max_data_segment_length = 8192; + conn->conn_max_recv_data_segment_length = 8192; conn->conn_max_burst_length = 262144; + conn->conn_first_burst_length = 65536; conn->conn_immediate_data = true; return (conn); Index: usr.sbin/ctld/kernel.c =================================================================== --- usr.sbin/ctld/kernel.c +++ usr.sbin/ctld/kernel.c @@ -898,7 +898,9 @@ req.data.handoff.cmdsn = conn->conn_cmdsn; req.data.handoff.statsn = conn->conn_statsn; req.data.handoff.max_recv_data_segment_length = - conn->conn_max_data_segment_length; + conn->conn_max_recv_data_segment_length; + req.data.handoff.max_send_data_segment_length = + conn->conn_max_send_data_segment_length; req.data.handoff.max_burst_length = conn->conn_max_burst_length; req.data.handoff.first_burst_length = conn->conn_first_burst_length; req.data.handoff.immediate_data = conn->conn_immediate_data; @@ -915,16 +917,18 @@ } void -kernel_limits(const char *offload, size_t *max_data_segment_length) +kernel_limits(const char *offload, size_t *max_recv_dsl, size_t *max_send_dsl, + size_t *max_burst_length, size_t *first_burst_length) { struct ctl_iscsi req; + struct ctl_iscsi_limits_params *cilp; bzero(&req, sizeof(req)); req.type = CTL_ISCSI_LIMITS; + cilp = (struct ctl_iscsi_limits_params *)&(req.data.limits); if (offload != NULL) { - strlcpy(req.data.limits.offload, offload, - sizeof(req.data.limits.offload)); + strlcpy(cilp->offload, offload, sizeof(cilp->offload)); } if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { @@ -937,13 +941,31 @@ "%s; dropping connection", req.error_str); } - *max_data_segment_length = req.data.limits.data_segment_limit; + if (cilp->max_recv_data_segment_length != 0) { + *max_recv_dsl = cilp->max_recv_data_segment_length; + *max_send_dsl = cilp->max_recv_data_segment_length; + } + if (cilp->max_send_data_segment_length != 0) + *max_send_dsl = cilp->max_send_data_segment_length; + if (cilp->max_burst_length != 0) + *max_burst_length = cilp->max_burst_length; + if (cilp->first_burst_length != 0) + *first_burst_length = cilp->first_burst_length; + if (*max_burst_length < *first_burst_length) + *first_burst_length = *max_burst_length; + if (offload != NULL) { - log_debugx("MaxRecvDataSegment kernel limit for offload " - "\"%s\" is %zd", offload, *max_data_segment_length); + log_debugx("Kernel limits for offload \"%s\" are " + "MaxRecvDataSegment=%zd, max_send_dsl=%zd, " + "MaxBurstLength=%zd, FirstBurstLength=%zd", + offload, *max_recv_dsl, *max_send_dsl, *max_burst_length, + *first_burst_length); } else { - log_debugx("MaxRecvDataSegment kernel limit is %zd", - *max_data_segment_length); + log_debugx("Kernel limits are " + "MaxRecvDataSegment=%zd, max_send_dsl=%zd, " + "MaxBurstLength=%zd, FirstBurstLength=%zd", + *max_recv_dsl, *max_send_dsl, *max_burst_length, + *first_burst_length); } } @@ -1217,18 +1239,21 @@ void kernel_receive(struct pdu *pdu) { + struct connection *conn; struct ctl_iscsi req; - pdu->pdu_data = malloc(MAX_DATA_SEGMENT_LENGTH); + conn = pdu->pdu_connection; + pdu->pdu_data = malloc(conn->conn_max_recv_data_segment_length); if (pdu->pdu_data == NULL) log_err(1, "malloc"); bzero(&req, sizeof(req)); req.type = CTL_ISCSI_RECEIVE; - req.data.receive.connection_id = pdu->pdu_connection->conn_socket; + req.data.receive.connection_id = conn->conn_socket; req.data.receive.bhs = pdu->pdu_bhs; - req.data.receive.data_segment_len = MAX_DATA_SEGMENT_LENGTH; + req.data.receive.data_segment_len = + conn->conn_max_recv_data_segment_length; req.data.receive.data_segment = pdu->pdu_data; if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { Index: usr.sbin/ctld/login.c =================================================================== --- usr.sbin/ctld/login.c +++ usr.sbin/ctld/login.c @@ -550,23 +550,31 @@ log_errx(1, "received invalid " "MaxRecvDataSegmentLength"); } - if (tmp > conn->conn_data_segment_limit) { - log_debugx("capping MaxRecvDataSegmentLength " - "from %zd to %zd", tmp, conn->conn_data_segment_limit); - tmp = conn->conn_data_segment_limit; + + /* + * MaxRecvDataSegmentLength is a direction-specific parameter. + * We'll limit our _send_ to what the initiator can handle but + * our MaxRecvDataSegmentLength is not influenced by the + * initiator in any way. + */ + if (tmp > conn->conn_max_send_data_segment_length) { + log_debugx("capping max_send_data_segment_length " + "from %zd to %zd", tmp, + conn->conn_max_send_data_segment_length); + conn->conn_max_send_data_segment_length = tmp; } - conn->conn_max_data_segment_length = tmp; - keys_add_int(response_keys, name, conn->conn_data_segment_limit); + keys_add_int(response_keys, name, + conn->conn_max_recv_data_segment_length); } else if (strcmp(name, "MaxBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) { login_send_error(request, 0x02, 0x00); log_errx(1, "received invalid MaxBurstLength"); } - if (tmp > MAX_BURST_LENGTH) { - log_debugx("capping MaxBurstLength from %zd to %d", - tmp, MAX_BURST_LENGTH); - tmp = MAX_BURST_LENGTH; + if (tmp > conn->conn_max_burst_length) { + log_debugx("capping MaxBurstLength from %zd to %zd", + tmp, conn->conn_max_burst_length); + tmp = conn->conn_max_burst_length; } conn->conn_max_burst_length = tmp; keys_add(response_keys, name, value); @@ -576,10 +584,10 @@ login_send_error(request, 0x02, 0x00); log_errx(1, "received invalid FirstBurstLength"); } - if (tmp > FIRST_BURST_LENGTH) { - log_debugx("capping FirstBurstLength from %zd to %d", - tmp, FIRST_BURST_LENGTH); - tmp = FIRST_BURST_LENGTH; + if (tmp > conn->conn_first_burst_length) { + log_debugx("capping FirstBurstLength from %zd to %zd", + tmp, conn->conn_first_burst_length); + tmp = conn->conn_first_burst_length; } conn->conn_first_burst_length = tmp; keys_add_int(response_keys, name, tmp); @@ -681,14 +689,30 @@ if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL) { /* - * Query the kernel for MaxDataSegmentLength it can handle. - * In case of offload, it depends on hardware capabilities. + * Query the kernel for various size limits. In case of + * offload, it depends on hardware capabilities. */ assert(conn->conn_target != NULL); kernel_limits(conn->conn_portal->p_portal_group->pg_offload, - &conn->conn_data_segment_limit); + &conn->conn_max_recv_data_segment_length, + &conn->conn_max_send_data_segment_length, + &conn->conn_max_burst_length, + &conn->conn_first_burst_length); + + /* We expect legal, usable values at this point. */ + assert(conn->conn_max_recv_data_segment_length >= 512); + assert(conn->conn_max_recv_data_segment_length < (1 << 24)); + assert(conn->conn_max_burst_length >= 512); + assert(conn->conn_max_burst_length < (1 << 24)); + assert(conn->conn_first_burst_length >= 512); + assert(conn->conn_first_burst_length < (1 << 24)); + assert(conn->conn_first_burst_length <= + conn->conn_max_burst_length); } else { - conn->conn_data_segment_limit = MAX_DATA_SEGMENT_LENGTH; + conn->conn_max_recv_data_segment_length = + MAX_DATA_SEGMENT_LENGTH; + conn->conn_max_send_data_segment_length = + MAX_DATA_SEGMENT_LENGTH; } if (request == NULL) { @@ -739,6 +763,18 @@ response_keys); } + /* + * We'd started with usable values at our end. But a bad initiator + * could have presented a large FirstBurstLength and then a smaller + * MaxBurstLength (in that order) and because we process the key/value + * pairs in the order they are in the request we might have ended up + * with iLLegal values here. + */ + if (conn->conn_session_type == CONN_SESSION_TYPE_NORMAL && + conn->conn_first_burst_length > conn->conn_max_burst_length) { + log_errx(1, "initiator sent FirstBurstLength > MaxBurstLength"); + } + log_debugx("operational parameter negotiation done; " "transitioning to Full Feature Phase"); Index: usr.sbin/ctld/pdu.c =================================================================== --- usr.sbin/ctld/pdu.c +++ usr.sbin/ctld/pdu.c @@ -117,7 +117,7 @@ log_errx(1, "protocol error: non-empty AHS"); len = pdu_data_segment_length(pdu); - assert(len <= MAX_DATA_SEGMENT_LENGTH); + assert(len <= pdu->pdu_connection->conn_max_recv_data_segment_length); pdu->pdu_data_len = len; } @@ -164,6 +164,7 @@ void pdu_receive(struct pdu *pdu) { + struct connection *conn; size_t len, padding; char dummy[4]; @@ -173,9 +174,10 @@ #endif assert(proxy_mode == false); + conn = pdu->pdu_connection; - pdu_read(pdu->pdu_connection->conn_socket, - (char *)pdu->pdu_bhs, sizeof(*pdu->pdu_bhs)); + pdu_read(conn->conn_socket, (char *)pdu->pdu_bhs, + sizeof(*pdu->pdu_bhs)); len = pdu_ahs_length(pdu); if (len > 0) @@ -183,10 +185,10 @@ len = pdu_data_segment_length(pdu); if (len > 0) { - if (len > MAX_DATA_SEGMENT_LENGTH) { + if (len > conn->conn_max_recv_data_segment_length) { log_errx(1, "protocol error: received PDU " - "with DataSegmentLength exceeding %d", - MAX_DATA_SEGMENT_LENGTH); + "with DataSegmentLength exceeding %zd", + conn->conn_max_recv_data_segment_length); } pdu->pdu_data_len = len; @@ -194,14 +196,13 @@ if (pdu->pdu_data == NULL) log_err(1, "malloc"); - pdu_read(pdu->pdu_connection->conn_socket, - (char *)pdu->pdu_data, pdu->pdu_data_len); + pdu_read(conn->conn_socket, (char *)pdu->pdu_data, + pdu->pdu_data_len); padding = pdu_padding(pdu); if (padding != 0) { assert(padding < sizeof(dummy)); - pdu_read(pdu->pdu_connection->conn_socket, - (char *)dummy, padding); + pdu_read(conn->conn_socket, (char *)dummy, padding); } } } Index: usr.sbin/iscsid/iscsid.h =================================================================== --- usr.sbin/iscsid/iscsid.h +++ usr.sbin/iscsid/iscsid.h @@ -61,7 +61,8 @@ int conn_data_digest; bool conn_initial_r2t; bool conn_immediate_data; - size_t conn_max_data_segment_length; + size_t conn_max_recv_data_segment_length; + size_t conn_max_send_data_segment_length; size_t conn_max_burst_length; size_t conn_first_burst_length; struct chap *conn_mutual_chap; Index: usr.sbin/iscsid/iscsid.c =================================================================== --- usr.sbin/iscsid/iscsid.c +++ usr.sbin/iscsid/iscsid.c @@ -153,6 +153,7 @@ connection_new(int iscsi_fd, const struct iscsi_daemon_request *request) { struct connection *conn; + struct iscsi_session_limits *isl; struct addrinfo *from_ai, *to_ai; const char *from_addr, *to_addr; #ifdef ICL_KERNEL_PROXY @@ -171,16 +172,49 @@ conn->conn_data_digest = CONN_DIGEST_NONE; conn->conn_initial_r2t = true; conn->conn_immediate_data = true; - conn->conn_max_data_segment_length = 8192; - conn->conn_max_burst_length = 262144; - conn->conn_first_burst_length = 65536; + conn->conn_max_burst_length = MAX_BURST_LENGTH; + conn->conn_first_burst_length = FIRST_BURST_LENGTH; conn->conn_iscsi_fd = iscsi_fd; conn->conn_session_id = request->idr_session_id; memcpy(&conn->conn_conf, &request->idr_conf, sizeof(conn->conn_conf)); memcpy(&conn->conn_isid, &request->idr_isid, sizeof(conn->conn_isid)); conn->conn_tsih = request->idr_tsih; - memcpy(&conn->conn_limits, &request->idr_limits, sizeof(conn->conn_limits)); + + /* + * 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) { + conn->conn_max_recv_data_segment_length = 8192; + conn->conn_max_send_data_segment_length = 8192; + isl->isl_max_recv_data_segment_length = 8192; + } else { + conn->conn_max_recv_data_segment_length = + isl->isl_max_recv_data_segment_length; + conn->conn_max_send_data_segment_length = + isl->isl_max_recv_data_segment_length; + } + if (isl->isl_max_send_data_segment_length == 0) { + isl->isl_max_send_data_segment_length = + isl->isl_max_recv_data_segment_length; + } else { + conn->conn_max_send_data_segment_length = + isl->isl_max_send_data_segment_length; + } + if (isl->isl_max_burst_length == 0) + isl->isl_max_burst_length = conn->conn_max_burst_length; + if (isl->isl_first_burst_length == 0) { + if (isl->isl_max_burst_length < (int)conn->conn_first_burst_length) + isl->isl_first_burst_length = isl->isl_max_burst_length; + else + isl->isl_first_burst_length = conn->conn_first_burst_length; + } from_addr = conn->conn_conf.isc_initiator_addr; to_addr = conn->conn_conf.isc_target_addr; @@ -277,7 +311,10 @@ idh.idh_data_digest = conn->conn_data_digest; idh.idh_initial_r2t = conn->conn_initial_r2t; idh.idh_immediate_data = conn->conn_immediate_data; - idh.idh_max_data_segment_length = conn->conn_max_data_segment_length; + idh.idh_max_recv_data_segment_length = + conn->conn_max_recv_data_segment_length; + idh.idh_max_send_data_segment_length = + 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; Index: usr.sbin/iscsid/login.c =================================================================== --- usr.sbin/iscsid/login.c +++ usr.sbin/iscsid/login.c @@ -330,8 +330,10 @@ login_negotiate_key(struct connection *conn, const char *name, const char *value) { + struct iscsi_session_limits *isl; int which, tmp; + isl = &conn->conn_limits; if (strcmp(name, "TargetAlias") == 0) { strlcpy(conn->conn_target_alias, value, sizeof(conn->conn_target_alias)); @@ -388,30 +390,31 @@ if (tmp <= 0) log_errx(1, "received invalid " "MaxRecvDataSegmentLength"); - if (tmp > ISCSI_MAX_DATA_SEGMENT_LENGTH) { - log_debugx("capping MaxRecvDataSegmentLength " - "from %d to %d", tmp, ISCSI_MAX_DATA_SEGMENT_LENGTH); - tmp = ISCSI_MAX_DATA_SEGMENT_LENGTH; + if (tmp > isl->isl_max_send_data_segment_length) { + log_debugx("capping max_send_data_segment_length " + "from %d to %d", tmp, + isl->isl_max_send_data_segment_length); + tmp = isl->isl_max_send_data_segment_length; } - conn->conn_max_data_segment_length = tmp; + conn->conn_max_send_data_segment_length = tmp; } else if (strcmp(name, "MaxBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) log_errx(1, "received invalid MaxBurstLength"); - if (tmp > MAX_BURST_LENGTH) { + if (tmp > isl->isl_max_burst_length) { log_debugx("capping MaxBurstLength " - "from %d to %d", tmp, MAX_BURST_LENGTH); - tmp = MAX_BURST_LENGTH; + "from %d to %d", tmp, isl->isl_max_burst_length); + tmp = isl->isl_max_burst_length; } conn->conn_max_burst_length = tmp; } else if (strcmp(name, "FirstBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) log_errx(1, "received invalid FirstBurstLength"); - if (tmp > FIRST_BURST_LENGTH) { + if (tmp > isl->isl_first_burst_length) { log_debugx("capping FirstBurstLength " - "from %d to %d", tmp, FIRST_BURST_LENGTH); - tmp = FIRST_BURST_LENGTH; + "from %d to %d", tmp, isl->isl_first_burst_length); + tmp = isl->isl_first_burst_length; } conn->conn_first_burst_length = tmp; } else if (strcmp(name, "DefaultTime2Wait") == 0) { @@ -440,13 +443,13 @@ if (tmp <= 0) log_errx(1, "received invalid " "InitiatorRecvDataSegmentLength"); - if ((size_t)tmp > conn->conn_limits.isl_max_data_segment_length) { + if ((int)tmp > isl->isl_max_recv_data_segment_length) { log_debugx("capping InitiatorRecvDataSegmentLength " "from %d to %zd", tmp, - conn->conn_limits.isl_max_data_segment_length); - tmp = conn->conn_limits.isl_max_data_segment_length; + isl->isl_max_recv_data_segment_length); + tmp = isl->isl_max_recv_data_segment_length; } - conn->conn_max_data_segment_length = tmp; + conn->conn_max_recv_data_segment_length = tmp; } else if (strcmp(name, "TargetPortalGroupTag") == 0) { /* Ignore */ } else if (strcmp(name, "TargetRecvDataSegmentLength") == 0) { @@ -455,13 +458,13 @@ log_errx(1, "received invalid TargetRecvDataSegmentLength"); } - if ((size_t)tmp > conn->conn_limits.isl_max_data_segment_length) { + if (tmp > isl->isl_max_send_data_segment_length) { log_debugx("capping TargetRecvDataSegmentLength " "from %d to %zd", tmp, - conn->conn_limits.isl_max_data_segment_length); - tmp = conn->conn_limits.isl_max_data_segment_length; + isl->isl_max_send_data_segment_length); + tmp = isl->isl_max_send_data_segment_length; } - conn->conn_max_data_segment_length = tmp; + conn->conn_max_send_data_segment_length = tmp; } else { log_debugx("unknown key \"%s\"; ignoring", name); } @@ -474,14 +477,19 @@ struct keys *request_keys, *response_keys; struct iscsi_bhs_login_response *bhslr; int i, nrequests = 0; + struct iscsi_session_limits *isl; log_debugx("beginning operational parameter negotiation"); request = login_new_request(conn, BHSLR_STAGE_OPERATIONAL_NEGOTIATION); request_keys = keys_new(); - log_debugx("offload \"%s\" limits MaxRecvDataSegmentLength to %zd", - conn->conn_conf.isc_offload, - conn->conn_limits.isl_max_data_segment_length); + isl = &conn->conn_limits; + log_debugx("Limits for offload \"%s\" are " + "MaxRecvDataSegment=%zd, max_send_dsl=%zd, " + "MaxBurstLength=%zd, FirstBurstLength=%zd", + conn->conn_conf.isc_offload, isl->isl_max_recv_data_segment_length, + isl->isl_max_send_data_segment_length, isl->isl_max_burst_length, + isl->isl_first_burst_length); /* * The following keys are irrelevant for discovery sessions. @@ -497,25 +505,27 @@ keys_add(request_keys, "DataDigest", "None"); keys_add(request_keys, "ImmediateData", "Yes"); - keys_add_int(request_keys, "MaxBurstLength", MAX_BURST_LENGTH); - keys_add_int(request_keys, "FirstBurstLength", FIRST_BURST_LENGTH); + keys_add_int(request_keys, "MaxBurstLength", + isl->isl_max_burst_length); + keys_add_int(request_keys, "FirstBurstLength", + isl->isl_first_burst_length); keys_add(request_keys, "InitialR2T", "Yes"); keys_add(request_keys, "MaxOutstandingR2T", "1"); if (conn->conn_conf.isc_iser == 1) { keys_add_int(request_keys, "InitiatorRecvDataSegmentLength", - conn->conn_limits.isl_max_data_segment_length); + isl->isl_max_recv_data_segment_length); keys_add_int(request_keys, "TargetRecvDataSegmentLength", - conn->conn_limits.isl_max_data_segment_length); + isl->isl_max_send_data_segment_length); keys_add(request_keys, "RDMAExtensions", "Yes"); } else { keys_add_int(request_keys, "MaxRecvDataSegmentLength", - conn->conn_limits.isl_max_data_segment_length); + isl->isl_max_recv_data_segment_length); } } else { keys_add(request_keys, "HeaderDigest", "None"); keys_add(request_keys, "DataDigest", "None"); keys_add_int(request_keys, "MaxRecvDataSegmentLength", - conn->conn_limits.isl_max_data_segment_length); + isl->isl_max_recv_data_segment_length); } keys_add(request_keys, "DefaultTime2Wait", "0");