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; }