diff --git a/sys/cam/ctl/ctl_frontend_iscsi.c b/sys/cam/ctl/ctl_frontend_iscsi.c index 817f9ffbdb96..4b8b4923d304 100644 --- a/sys/cam/ctl/ctl_frontend_iscsi.c +++ b/sys/cam/ctl/ctl_frontend_iscsi.c @@ -1222,7 +1222,7 @@ cfiscsi_session_unregister_initiator(struct cfiscsi_session *cs) } static struct cfiscsi_session * -cfiscsi_session_new(struct cfiscsi_softc *softc) +cfiscsi_session_new(struct cfiscsi_softc *softc, const char *offload) { struct cfiscsi_session *cs; int error; @@ -1242,7 +1242,11 @@ cfiscsi_session_new(struct cfiscsi_softc *softc) cv_init(&cs->cs_login_cv, "cfiscsi_login"); #endif - cs->cs_conn = icl_new_conn(NULL, "cfiscsi", &cs->cs_lock); + cs->cs_conn = icl_new_conn(offload, "cfiscsi", &cs->cs_lock); + if (cs->cs_conn == NULL) { + free(cs, M_CFISCSI); + return (NULL); + } cs->cs_conn->ic_receive = cfiscsi_receive_callback; cs->cs_conn->ic_error = cfiscsi_error_callback; cs->cs_conn->ic_prv0 = cs; @@ -1325,7 +1329,7 @@ cfiscsi_accept(struct socket *so, struct sockaddr *sa, int portal_id) { struct cfiscsi_session *cs; - cs = cfiscsi_session_new(&cfiscsi_softc); + cs = cfiscsi_session_new(&cfiscsi_softc, NULL); if (cs == NULL) { CFISCSI_WARN("failed to create session"); return; @@ -1469,7 +1473,7 @@ cfiscsi_ioctl_handoff(struct ctl_iscsi *ci) mtx_unlock(&cfiscsi_softc.lock); } else { #endif - cs = cfiscsi_session_new(softc); + cs = cfiscsi_session_new(softc, cihp->offload); if (cs == NULL) { ci->status = CTL_ISCSI_ERROR; snprintf(ci->error_str, sizeof(ci->error_str), @@ -1620,6 +1624,7 @@ cfiscsi_ioctl_list(struct ctl_iscsi *ci) "%zd" "%d" "%d" + "%s" "\n", cs->cs_id, cs->cs_initiator_name, cs->cs_initiator_addr, cs->cs_initiator_alias, @@ -1629,7 +1634,8 @@ cfiscsi_ioctl_list(struct ctl_iscsi *ci) cs->cs_conn->ic_data_crc32c ? "CRC32C" : "None", cs->cs_max_data_segment_length, cs->cs_immediate_data, - cs->cs_conn->ic_iser); + cs->cs_conn->ic_iser, + cs->cs_conn->ic_offload); if (error != 0) break; } @@ -1749,6 +1755,26 @@ cfiscsi_ioctl_logout(struct ctl_iscsi *ci) ci->status = CTL_ISCSI_OK; } +static void +cfiscsi_ioctl_limits(struct ctl_iscsi *ci) +{ + struct ctl_iscsi_limits_params *cilp; + int error; + + cilp = (struct ctl_iscsi_limits_params *)&(ci->data); + + error = icl_limits(cilp->offload, &cilp->data_segment_limit); + if (error != 0) { + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "%s: icl_limits failed with error %d", + __func__, error); + return; + } + + ci->status = CTL_ISCSI_OK; +} + #ifdef ICL_KERNEL_PROXY static void cfiscsi_ioctl_listen(struct ctl_iscsi *ci) @@ -2176,6 +2202,9 @@ cfiscsi_ioctl(struct cdev *dev, case CTL_ISCSI_LOGOUT: cfiscsi_ioctl_logout(ci); break; + case CTL_ISCSI_LIMITS: + cfiscsi_ioctl_limits(ci); + break; #ifdef ICL_KERNEL_PROXY case CTL_ISCSI_LISTEN: cfiscsi_ioctl_listen(ci); diff --git a/sys/cam/ctl/ctl_ioctl.h b/sys/cam/ctl/ctl_ioctl.h index 532953fb7fab..c7a3c2938400 100644 --- a/sys/cam/ctl/ctl_ioctl.h +++ b/sys/cam/ctl/ctl_ioctl.h @@ -657,6 +657,7 @@ typedef enum { CTL_ISCSI_LIST, CTL_ISCSI_LOGOUT, CTL_ISCSI_TERMINATE, + CTL_ISCSI_LIMITS, #if defined(ICL_KERNEL_PROXY) || 1 /* * We actually need those in all cases, but leave the ICL_KERNEL_PROXY, @@ -677,6 +678,7 @@ typedef enum { #define CTL_ISCSI_NAME_LEN 224 /* 223 bytes, by RFC 3720, + '\0' */ #define CTL_ISCSI_ADDR_LEN 47 /* INET6_ADDRSTRLEN + '\0' */ #define CTL_ISCSI_ALIAS_LEN 128 /* Arbitrary. */ +#define CTL_ISCSI_OFFLOAD_LEN 8 /* Arbitrary. */ struct ctl_iscsi_handoff_params { char initiator_name[CTL_ISCSI_NAME_LEN]; @@ -698,11 +700,12 @@ struct ctl_iscsi_handoff_params { uint32_t max_burst_length; uint32_t first_burst_length; uint32_t immediate_data; + char offload[CTL_ISCSI_OFFLOAD_LEN]; #ifdef ICL_KERNEL_PROXY int connection_id; - int spare[3]; + int spare[1]; #else - int spare[4]; + int spare[2]; #endif }; @@ -733,6 +736,14 @@ struct ctl_iscsi_terminate_params { int spare[4]; }; +struct ctl_iscsi_limits_params { + char offload[CTL_ISCSI_OFFLOAD_LEN]; + /* passed to kernel */ + size_t data_segment_limit; + /* passed to userland */ + int spare[4]; +}; + #ifdef ICL_KERNEL_PROXY struct ctl_iscsi_listen_params { int iser; @@ -780,6 +791,7 @@ union ctl_iscsi_data { struct ctl_iscsi_list_params list; struct ctl_iscsi_logout_params logout; struct ctl_iscsi_terminate_params terminate; + struct ctl_iscsi_limits_params limits; #ifdef ICL_KERNEL_PROXY struct ctl_iscsi_listen_params listen; struct ctl_iscsi_accept_params accept; diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c index 03c751b21f31..aefba042aa54 100644 --- a/usr.sbin/ctladm/ctladm.c +++ b/usr.sbin/ctladm/ctladm.c @@ -3439,6 +3439,7 @@ struct cctl_islist_conn { char *header_digest; char *data_digest; char *max_data_segment_length;; + char *offload;; int immediate_data; int iser; STAILQ_ENTRY(cctl_islist_conn) links; @@ -3552,6 +3553,9 @@ cctl_islist_end_element(void *user_data, const char *name) } else if (strcmp(name, "max_data_segment_length") == 0) { cur_conn->max_data_segment_length = str; str = NULL; + } else if (strcmp(name, "offload") == 0) { + cur_conn->offload = str; + str = NULL; } else if (strcmp(name, "immediate_data") == 0) { cur_conn->immediate_data = atoi(str); } else if (strcmp(name, "iser") == 0) { @@ -3672,6 +3676,7 @@ retry: printf("DataSegmentLen: %s\n", conn->max_data_segment_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("\n"); } } else { diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5 index fa053b0f20cf..404699829920 100644 --- a/usr.sbin/ctld/ctl.conf.5 +++ b/usr.sbin/ctld/ctl.conf.5 @@ -310,6 +310,8 @@ This clause is mutually exclusive with .Sy auth-group ; one cannot use both in a single target. +.It Ic offload Ar driver +Define iSCSI hardware offload driver to use for this target. .It Ic portal-group Ar name Op Ar agname Assign a previously defined portal group to the target. The default portal group is diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c index 513e073630b1..dd864b95d286 100644 --- a/usr.sbin/ctld/ctld.c +++ b/usr.sbin/ctld/ctld.c @@ -1272,6 +1272,22 @@ target_set_redirection(struct target *target, const char *addr) return (0); } +int +target_set_offload(struct target *target, const char *offload) +{ + + if (target->t_offload != NULL) { + log_warnx("cannot set offload to \"%s\" for " + "target \"%s\"; already defined", + offload, target->t_name); + return (1); + } + + target->t_offload = checked_strdup(offload); + + return (0); +} + struct lun * lun_new(struct conf *conf, const char *name) { @@ -1514,6 +1530,8 @@ conf_print(struct conf *conf) fprintf(stderr, "target %s {\n", targ->t_name); if (targ->t_alias != NULL) fprintf(stderr, "\t alias %s\n", targ->t_alias); + if (targ->t_offload != NULL) + fprintf(stderr, "\t offload %s\n", targ->t_offload); fprintf(stderr, "}\n"); } } diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h index e213e0bcbb82..395b0144ae55 100644 --- a/usr.sbin/ctld/ctld.h +++ b/usr.sbin/ctld/ctld.h @@ -169,6 +169,7 @@ struct target { TAILQ_HEAD(, port) t_ports; char *t_name; char *t_alias; + char *t_offload; char *t_redirection; }; @@ -223,6 +224,7 @@ struct connection { 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; int conn_immediate_data; @@ -344,6 +346,8 @@ struct target *target_find(struct conf *conf, const char *name); int target_set_redirection(struct target *target, const char *addr); +int target_set_offload(struct target *target, + const char *offload); struct lun *lun_new(struct conf *conf, const char *name); void lun_delete(struct lun *lun); @@ -370,6 +374,8 @@ int kernel_lun_add(struct lun *lun); int kernel_lun_resize(struct lun *lun); 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 kernel_port_add(struct port *port); int kernel_port_update(struct port *port); int kernel_port_remove(struct port *port); diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c index f36bdcb77f73..47dc56aa3be3 100644 --- a/usr.sbin/ctld/kernel.c +++ b/usr.sbin/ctld/kernel.c @@ -799,6 +799,10 @@ kernel_handoff(struct connection *conn) sizeof(req.data.handoff.initiator_isid)); strlcpy(req.data.handoff.target_name, conn->conn_target->t_name, sizeof(req.data.handoff.target_name)); + if (conn->conn_target->t_offload != NULL) { + strlcpy(req.data.handoff.offload, + conn->conn_target->t_offload, sizeof(req.data.handoff.offload)); + } #ifdef ICL_KERNEL_PROXY if (proxy_mode) req.data.handoff.connection_id = conn->conn_socket; @@ -831,6 +835,39 @@ kernel_handoff(struct connection *conn) } } +void +kernel_limits(const char *offload, size_t *max_data_segment_length) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_LIMITS; + if (offload != NULL) { + strlcpy(req.data.limits.offload, offload, + sizeof(req.data.limits.offload)); + } + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + } + + if (req.status != CTL_ISCSI_OK) { + log_errx(1, "error returned from CTL iSCSI limits request: " + "%s; dropping connection", req.error_str); + } + + *max_data_segment_length = req.data.limits.data_segment_limit; + if (offload != NULL) { + log_debugx("MaxRecvDataSegment kernel limit for offload " + "\"%s\" is %zd", offload, *max_data_segment_length); + } else { + log_debugx("MaxRecvDataSegment kernel limit is %zd", + *max_data_segment_length); + } +} + int kernel_port_add(struct port *port) { diff --git a/usr.sbin/ctld/login.c b/usr.sbin/ctld/login.c index 865a19a73ab0..11d97cfae793 100644 --- a/usr.sbin/ctld/login.c +++ b/usr.sbin/ctld/login.c @@ -453,7 +453,8 @@ static void login_negotiate_key(struct pdu *request, const char *name, const char *value, bool skipped_security, struct keys *response_keys) { - int which, tmp; + int which; + size_t tmp; struct connection *conn; conn = request->pdu_connection; @@ -552,13 +553,13 @@ login_negotiate_key(struct pdu *request, const char *name, log_errx(1, "received invalid " "MaxRecvDataSegmentLength"); } - if (tmp > MAX_DATA_SEGMENT_LENGTH) { + if (tmp > conn->conn_data_segment_limit) { log_debugx("capping MaxRecvDataSegmentLength " - "from %d to %d", tmp, MAX_DATA_SEGMENT_LENGTH); - tmp = MAX_DATA_SEGMENT_LENGTH; + "from %zd to %zd", tmp, conn->conn_data_segment_limit); + tmp = conn->conn_data_segment_limit; } conn->conn_max_data_segment_length = tmp; - keys_add_int(response_keys, name, MAX_DATA_SEGMENT_LENGTH); + keys_add_int(response_keys, name, tmp); } else if (strcmp(name, "MaxBurstLength") == 0) { tmp = strtoul(value, NULL, 10); if (tmp <= 0) { @@ -566,7 +567,7 @@ login_negotiate_key(struct pdu *request, const char *name, log_errx(1, "received invalid MaxBurstLength"); } if (tmp > MAX_BURST_LENGTH) { - log_debugx("capping MaxBurstLength from %d to %d", + log_debugx("capping MaxBurstLength from %zd to %d", tmp, MAX_BURST_LENGTH); tmp = MAX_BURST_LENGTH; } @@ -579,10 +580,10 @@ login_negotiate_key(struct pdu *request, const char *name, log_errx(1, "received invalid " "FirstBurstLength"); } - if (tmp > MAX_DATA_SEGMENT_LENGTH) { - log_debugx("capping FirstBurstLength from %d to %d", - tmp, MAX_DATA_SEGMENT_LENGTH); - tmp = MAX_DATA_SEGMENT_LENGTH; + if (tmp > conn->conn_data_segment_limit) { + log_debugx("capping FirstBurstLength from %zd to %zd", + tmp, conn->conn_data_segment_limit); + tmp = conn->conn_data_segment_limit; } /* * We don't pass the value to the kernel; it only enforces @@ -680,6 +681,18 @@ login_negotiate(struct connection *conn, struct pdu *request) int i; bool redirected, skipped_security; + 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. + */ + assert(conn->conn_target != NULL); + kernel_limits(conn->conn_target->t_offload, + &conn->conn_data_segment_limit); + } else { + conn->conn_data_segment_limit = MAX_DATA_SEGMENT_LENGTH; + } + if (request == NULL) { log_debugx("beginning operational parameter negotiation; " "waiting for Login PDU"); diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y index d0b2c514ca0b..5eaffe4e324a 100644 --- a/usr.sbin/ctld/parse.y +++ b/usr.sbin/ctld/parse.y @@ -60,7 +60,7 @@ extern void yyrestart(FILE *); %token ALIAS AUTH_GROUP AUTH_TYPE BACKEND BLOCKSIZE CHAP CHAP_MUTUAL %token CLOSING_BRACKET DEBUG DEVICE_ID DISCOVERY_AUTH_GROUP DISCOVERY_FILTER %token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT -%token LISTEN LISTEN_ISER LUN MAXPROC OPENING_BRACKET OPTION +%token LISTEN LISTEN_ISER LUN MAXPROC OFFLOAD OPENING_BRACKET OPTION %token PATH PIDFILE PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR %token TARGET TIMEOUT @@ -463,6 +463,8 @@ target_entry: | target_initiator_portal | + target_offload + | target_portal_group | target_redirect @@ -652,6 +654,17 @@ target_initiator_portal: INITIATOR_PORTAL STR } ; +target_offload: OFFLOAD STR + { + int error; + + error = target_set_offload(target, $2); + free($2); + if (error != 0) + return (1); + } + ; + target_portal_group: PORTAL_GROUP STR STR { struct portal_group *tpg; diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l index d4bf823b8b08..fd274983f447 100644 --- a/usr.sbin/ctld/token.l +++ b/usr.sbin/ctld/token.l @@ -65,6 +65,7 @@ listen { return LISTEN; } listen-iser { return LISTEN_ISER; } lun { return LUN; } maxproc { return MAXPROC; } +offload { return OFFLOAD; } option { return OPTION; } path { return PATH; } pidfile { return PIDFILE; }