Index: sys/cam/ctl/ctl_frontend_iscsi.h =================================================================== --- sys/cam/ctl/ctl_frontend_iscsi.h +++ sys/cam/ctl/ctl_frontend_iscsi.h @@ -84,9 +84,10 @@ struct cv cs_maintenance_cv; bool cs_terminating; bool cs_tasks_aborted; - size_t cs_max_data_segment_length; - size_t cs_max_burst_length; - size_t cs_first_burst_length; + int cs_max_recv_data_segment_length; + int cs_max_send_data_segment_length; + int cs_max_burst_length; + int cs_first_burst_length; bool cs_immediate_data; char cs_initiator_name[CTL_ISCSI_NAME_LEN]; char cs_initiator_addr[CTL_ISCSI_ADDR_LEN]; 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,9 +1653,10 @@ "%u" "%s" "%s" - "%zd" - "%zd" - "%zd" + "%d" + "%d" + "%d" + "%d" "%d" "%d" "%s" @@ -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,13 @@ return; } + cilp->max_recv_data_segment_length = + idl.idl_max_recv_data_segment_length; + cilp->max_send_data_segment_length = + idl.idl_max_send_data_segment_length; + cilp->max_burst_length = idl.idl_max_burst_length; + cilp->first_burst_length = idl.idl_first_burst_length; + ci->status = CTL_ISCSI_OK; } @@ -2466,12 +2476,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 %d", + 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 +2539,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). */ @@ -630,17 +630,17 @@ ctl_iscsi_digest data_digest; uint32_t cmdsn; uint32_t statsn; - uint32_t max_recv_data_segment_length; - uint32_t max_burst_length; - uint32_t first_burst_length; + int max_recv_data_segment_length; + int max_burst_length; + int first_burst_length; uint32_t immediate_data; 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->idl_max_recv_data_segment_length = CXGBEI_MAX_DSL; + idl->idl_max_send_data_segment_length = CXGBEI_MAX_DSL; + idl->idl_max_burst_length = 2 * 1024 * 1024; + idl->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,20 @@ void *ic_prv0; }; +struct icl_drv_limits { + int idl_max_recv_data_segment_length; + int idl_max_send_data_segment_length; + int idl_max_burst_length; + int idl_first_burst_length; + int spare[4]; +}; + 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->idl_max_recv_data_segment_length, 512, 16777215) || + OUT_OF_RANGE(idl->idl_max_send_data_segment_length, 512, 16777215) || + OUT_OF_RANGE(idl->idl_max_burst_length, 512, 16777215) || + OUT_OF_RANGE(idl->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->idl_first_burst_length > 0 && + idl->idl_max_burst_length > 0 && + idl->idl_first_burst_length > idl->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->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 @@ -62,12 +62,13 @@ int is_header_digest; int is_data_digest; int is_initial_r2t; - size_t is_max_burst_length; - size_t is_first_burst_length; + int is_max_burst_length; + int is_first_burst_length; uint8_t is_isid[6]; uint16_t is_tsih; bool is_immediate_data; - size_t is_max_data_segment_length; + int is_max_recv_data_segment_length; + int 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,14 @@ 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_recv_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; sx_sunlock(&sc->sc_lock); return (0); @@ -1417,12 +1425,10 @@ 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); + 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 +1640,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 +1939,15 @@ 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); @@ -2259,12 +2268,13 @@ len = csio->dxfer_len; //ISCSI_SESSION_DEBUG(is, "adding %zd of immediate data", len); if (len > is->is_first_burst_length) { - ISCSI_SESSION_DEBUG(is, "len %zd -> %zd", len, is->is_first_burst_length); + ISCSI_SESSION_DEBUG(is, "len %zd -> %d", 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 -> %d", 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,13 @@ uint32_t idh_statsn; int idh_header_digest; int idh_data_digest; - size_t idh_max_data_segment_length; - size_t idh_max_burst_length; - size_t idh_first_burst_length; + size_t spare[3]; 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_max_burst_length; + int idh_first_burst_length; }; 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->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 @@ -514,70 +514,74 @@ * Display-only modifier as this information * is also present within the 'session' container */ - xo_emit("{L:/%-18s}{V:sessionId/%u}\n", + xo_emit("{L:/%-25s}{V:sessionId/%u}\n", "Session ID:", state->iss_id); xo_open_container("initiator"); - xo_emit("{L:/%-18s}{V:name/%s}\n", + xo_emit("{L:/%-25s}{V:name/%s}\n", "Initiator name:", conf->isc_initiator); - xo_emit("{L:/%-18s}{V:portal/%s}\n", + xo_emit("{L:/%-25s}{V:portal/%s}\n", "Initiator portal:", conf->isc_initiator_addr); - xo_emit("{L:/%-18s}{V:alias/%s}\n", + xo_emit("{L:/%-25s}{V:alias/%s}\n", "Initiator alias:", conf->isc_initiator_alias); xo_close_container("initiator"); xo_open_container("target"); - xo_emit("{L:/%-18s}{V:name/%s}\n", + xo_emit("{L:/%-25s}{V:name/%s}\n", "Target name:", conf->isc_target); - xo_emit("{L:/%-18s}{V:portal/%s}\n", + xo_emit("{L:/%-25s}{V:portal/%s}\n", "Target portal:", conf->isc_target_addr); - xo_emit("{L:/%-18s}{V:alias/%s}\n", + xo_emit("{L:/%-25s}{V:alias/%s}\n", "Target alias:", state->iss_target_alias); xo_close_container("target"); xo_open_container("auth"); - xo_emit("{L:/%-18s}{V:user/%s}\n", + xo_emit("{L:/%-25s}{V:user/%s}\n", "User:", conf->isc_user); - xo_emit("{L:/%-18s}{V:secret/%s}\n", + xo_emit("{L:/%-25s}{V:secret/%s}\n", "Secret:", conf->isc_secret); - xo_emit("{L:/%-18s}{V:mutualUser/%s}\n", + xo_emit("{L:/%-25s}{V:mutualUser/%s}\n", "Mutual user:", conf->isc_mutual_user); - xo_emit("{L:/%-18s}{V:mutualSecret/%s}\n", + xo_emit("{L:/%-25s}{V:mutualSecret/%s}\n", "Mutual secret:", conf->isc_mutual_secret); xo_close_container("auth"); - xo_emit("{L:/%-18s}{V:type/%s}\n", + xo_emit("{L:/%-25s}{V:type/%s}\n", "Session type:", conf->isc_discovery ? "Discovery" : "Normal"); - xo_emit("{L:/%-18s}{V:enable/%s}\n", + xo_emit("{L:/%-25s}{V:enable/%s}\n", "Enable:", conf->isc_enable ? "Yes" : "No"); - xo_emit("{L:/%-18s}{V:state/%s}\n", + xo_emit("{L:/%-25s}{V:state/%s}\n", "Session state:", state->iss_connected ? "Connected" : "Disconnected"); - xo_emit("{L:/%-18s}{V:failureReason/%s}\n", + xo_emit("{L:/%-25s}{V:failureReason/%s}\n", "Failure reason:", state->iss_reason); - xo_emit("{L:/%-18s}{V:headerDigest/%s}\n", + xo_emit("{L:/%-25s}{V:headerDigest/%s}\n", "Header digest:", state->iss_header_digest == ISCSI_DIGEST_CRC32C ? "CRC32C" : "None"); - xo_emit("{L:/%-18s}{V:dataDigest/%s}\n", + xo_emit("{L:/%-25s}{V:dataDigest/%s}\n", "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:maxBurstLen/%d}\n", + xo_emit("{L:/%-25s}{V:recvDataSegmentLen/%d}\n", + "MaxRecvDataSegmentLength:", + state->iss_max_recv_data_segment_length); + xo_emit("{L:/%-25s}{V:sendDataSegmentLen/%d}\n", + "MaxSendDataSegmentLength:", + state->iss_max_send_data_segment_length); + xo_emit("{L:/%-25s}{V:maxBurstLen/%d}\n", "MaxBurstLen:", state->iss_max_burst_length); - xo_emit("{L:/%-18s}{V:firstBurstLen/%d}\n", + xo_emit("{L:/%-25s}{V:firstBurstLen/%d}\n", "FirstBurstLen:", state->iss_first_burst_length); - xo_emit("{L:/%-18s}{V:immediateData/%s}\n", + xo_emit("{L:/%-25s}{V:immediateData/%s}\n", "ImmediateData:", state->iss_immediate_data ? "Yes" : "No"); - xo_emit("{L:/%-18s}{V:iSER/%s}\n", + xo_emit("{L:/%-25s}{V:iSER/%s}\n", "iSER (RDMA):", conf->isc_iser ? "Yes" : "No"); - xo_emit("{L:/%-18s}{V:offloadDriver/%s}\n", + xo_emit("{L:/%-25s}{V:offloadDriver/%s}\n", "Offload driver:", state->iss_offload); - xo_emit("{L:/%-18s}", + xo_emit("{L:/%-25s}", "Device nodes:"); print_periphs(state->iss_id); xo_emit("\n\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; @@ -3030,20 +3034,21 @@ if (verbose != 0) { STAILQ_FOREACH(conn, &islist.conn_list, links) { - printf("Session ID: %d\n", conn->connection_id); - printf("Initiator name: %s\n", conn->initiator); - printf("Initiator portal: %s\n", conn->initiator_addr); - printf("Initiator alias: %s\n", conn->initiator_alias); - printf("Target name: %s\n", conn->target); - 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("MaxBurstLen: %s\n", conn->max_burst_length); - printf("FirstBurstLen: %s\n", conn->first_burst_length); - printf("ImmediateData: %s\n", conn->immediate_data ? "Yes" : "No"); - printf("iSER (RDMA): %s\n", conn->iser ? "Yes" : "No"); - printf("Offload driver: %s\n", conn->offload); + printf("%-25s %d\n", "Session ID:", conn->connection_id); + printf("%-25s %s\n", "Initiator name:", conn->initiator); + printf("%-25s %s\n", "Initiator portal:", conn->initiator_addr); + printf("%-25s %s\n", "Initiator alias:", conn->initiator_alias); + printf("%-25s %s\n", "Target name:", conn->target); + printf("%-25s %s\n", "Target alias:", conn->target_alias); + printf("%-25s %s\n", "Header digest:", conn->header_digest); + printf("%-25s %s\n", "Data digest:", conn->data_digest); + printf("%-25s %s\n", "MaxRecvDataSegmentLength:", conn->max_recv_data_segment_length); + printf("%-25s %s\n", "MaxSendDataSegmentLength:", conn->max_send_data_segment_length); + printf("%-25s %s\n", "MaxBurstLen:", conn->max_burst_length); + printf("%-25s %s\n", "FirstBurstLen:", conn->first_burst_length); + printf("%-25s %s\n", "ImmediateData:", conn->immediate_data ? "Yes" : "No"); + printf("%-25s %s\n", "iSER (RDMA):", conn->iser ? "Yes" : "No"); + printf("%-25s %s\n", "Offload driver:", conn->offload); printf("\n"); } } else { Index: usr.sbin/ctld/ctld.h =================================================================== --- usr.sbin/ctld/ctld.h +++ usr.sbin/ctld/ctld.h @@ -242,10 +242,10 @@ 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_burst_length; - size_t conn_first_burst_length; + int conn_max_recv_data_segment_length; + int conn_max_send_data_segment_length; + int conn_max_burst_length; + int conn_first_burst_length; int conn_immediate_data; int conn_header_digest; int conn_data_digest; @@ -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); + int *max_recv_data_segment_length, + int *max_send_data_segment_length, + int *max_burst_length, + int *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, int *max_recv_dsl, int *max_send_dsl, + int *max_burst_length, int *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=%d, max_send_dsl=%d, " + "MaxBurstLength=%d, FirstBurstLength=%d", + 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=%d, max_send_dsl=%d, " + "MaxBurstLength=%d, FirstBurstLength=%d", + *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,32 @@ 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 ((int)tmp > conn->conn_max_send_data_segment_length) { + log_debugx("capping max_send_data_segment_length " + "from %zd to %d", tmp, + conn->conn_max_send_data_segment_length); + tmp = conn->conn_max_send_data_segment_length; } - conn->conn_max_data_segment_length = tmp; - keys_add_int(response_keys, name, conn->conn_data_segment_limit); + conn->conn_max_send_data_segment_length = tmp; + 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) { + if ((int)tmp > conn->conn_max_burst_length) { log_debugx("capping MaxBurstLength from %zd to %d", - tmp, MAX_BURST_LENGTH); - tmp = MAX_BURST_LENGTH; + tmp, conn->conn_max_burst_length); + tmp = conn->conn_max_burst_length; } conn->conn_max_burst_length = tmp; keys_add_int(response_keys, name, tmp); @@ -576,10 +585,10 @@ login_send_error(request, 0x02, 0x00); log_errx(1, "received invalid FirstBurstLength"); } - if (tmp > FIRST_BURST_LENGTH) { + if ((int)tmp > conn->conn_first_burst_length) { log_debugx("capping FirstBurstLength from %zd to %d", - tmp, FIRST_BURST_LENGTH); - tmp = FIRST_BURST_LENGTH; + 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 +690,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 +764,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 ((int)len > conn->conn_max_recv_data_segment_length) { log_errx(1, "protocol error: received PDU " "with DataSegmentLength exceeding %d", - MAX_DATA_SEGMENT_LENGTH); + 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,9 +61,10 @@ int conn_data_digest; bool conn_initial_r2t; bool conn_immediate_data; - size_t conn_max_data_segment_length; - size_t conn_max_burst_length; - size_t conn_first_burst_length; + int conn_max_recv_data_segment_length; + int conn_max_send_data_segment_length; + int conn_max_burst_length; + int 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; + "from %d to %d", tmp, + 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; + "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 { 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=%d, max_send_dsl=%d, " + "MaxBurstLength=%d, FirstBurstLength=%d", + 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");