summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authortrasz <trasz@FreeBSD.org>2015-02-06 21:03:25 +0000
committertrasz <trasz@FreeBSD.org>2015-02-06 21:03:25 +0000
commitba70dc0f4deacb9392d178bd7b48c20556486a5c (patch)
tree87c429dd203f72c20ec2b1a4649579aab5189db6 /usr.sbin
parent83fb4b4e9bdce5fa574089d1a3272a152b4a4a18 (diff)
downloadFreeBSD-src-ba70dc0f4deacb9392d178bd7b48c20556486a5c.zip
FreeBSD-src-ba70dc0f4deacb9392d178bd7b48c20556486a5c.tar.gz
Make it possible to set (via ctl.conf(5)) and query (via ctladm islist -v)
target iSCSI offload. Add mechanism to query maximum receive data segment size supported by chosen hardware offload module, and use it in ctld(8) to determine the value to advertise to the other side. MFC after: 1 month Sponsored by: The FreeBSD Foundation
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/ctladm/ctladm.c5
-rw-r--r--usr.sbin/ctld/ctl.conf.52
-rw-r--r--usr.sbin/ctld/ctld.c18
-rw-r--r--usr.sbin/ctld/ctld.h6
-rw-r--r--usr.sbin/ctld/kernel.c37
-rw-r--r--usr.sbin/ctld/login.c33
-rw-r--r--usr.sbin/ctld/parse.y15
-rw-r--r--usr.sbin/ctld/token.l1
8 files changed, 106 insertions, 11 deletions
diff --git a/usr.sbin/ctladm/ctladm.c b/usr.sbin/ctladm/ctladm.c
index 03c751b..aefba04 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 fa053b0..4046998 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 513e073..dd864b9 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 e213e0b..395b014 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 f36bdcb..47dc56a 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 865a19a..11d97cf 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 d0b2c51..5eaffe4 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 d4bf823..fd27498 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; }
OpenPOWER on IntegriCloud