Author: trasz
Date: Fri Feb  6 21:03:25 2015
New Revision: 278331
URL: https://svnweb.freebsd.org/changeset/base/278331

Log:
  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

Modified:
  head/sys/cam/ctl/ctl_frontend_iscsi.c
  head/sys/cam/ctl/ctl_ioctl.h
  head/usr.sbin/ctladm/ctladm.c
  head/usr.sbin/ctld/ctl.conf.5
  head/usr.sbin/ctld/ctld.c
  head/usr.sbin/ctld/ctld.h
  head/usr.sbin/ctld/kernel.c
  head/usr.sbin/ctld/login.c
  head/usr.sbin/ctld/parse.y
  head/usr.sbin/ctld/token.l

Modified: head/sys/cam/ctl/ctl_frontend_iscsi.c
==============================================================================
--- head/sys/cam/ctl/ctl_frontend_iscsi.c       Fri Feb  6 20:52:01 2015        
(r278330)
+++ head/sys/cam/ctl/ctl_frontend_iscsi.c       Fri Feb  6 21:03:25 2015        
(r278331)
@@ -1222,7 +1222,7 @@ cfiscsi_session_unregister_initiator(str
 }
 
 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
        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
 {
        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 *
                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)
                    "<max_data_segment_length>%zd</max_data_segment_length>"
                    "<immediate_data>%d</immediate_data>"
                    "<iser>%d</iser>"
+                   "<offload>%s</offload>"
                    "</connection>\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 *c
        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);

Modified: head/sys/cam/ctl/ctl_ioctl.h
==============================================================================
--- head/sys/cam/ctl/ctl_ioctl.h        Fri Feb  6 20:52:01 2015        
(r278330)
+++ head/sys/cam/ctl/ctl_ioctl.h        Fri Feb  6 21:03:25 2015        
(r278331)
@@ -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;

Modified: head/usr.sbin/ctladm/ctladm.c
==============================================================================
--- head/usr.sbin/ctladm/ctladm.c       Fri Feb  6 20:52:01 2015        
(r278330)
+++ head/usr.sbin/ctladm/ctladm.c       Fri Feb  6 21:03:25 2015        
(r278331)
@@ -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,
        } 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 {

Modified: head/usr.sbin/ctld/ctl.conf.5
==============================================================================
--- head/usr.sbin/ctld/ctl.conf.5       Fri Feb  6 20:52:01 2015        
(r278330)
+++ head/usr.sbin/ctld/ctl.conf.5       Fri Feb  6 21:03:25 2015        
(r278331)
@@ -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

Modified: head/usr.sbin/ctld/ctld.c
==============================================================================
--- head/usr.sbin/ctld/ctld.c   Fri Feb  6 20:52:01 2015        (r278330)
+++ head/usr.sbin/ctld/ctld.c   Fri Feb  6 21:03:25 2015        (r278331)
@@ -1272,6 +1272,22 @@ target_set_redirection(struct target *ta
        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");
        }
 }

Modified: head/usr.sbin/ctld/ctld.h
==============================================================================
--- head/usr.sbin/ctld/ctld.h   Fri Feb  6 20:52:01 2015        (r278330)
+++ head/usr.sbin/ctld/ctld.h   Fri Feb  6 21:03:25 2015        (r278331)
@@ -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 
                            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);

Modified: head/usr.sbin/ctld/kernel.c
==============================================================================
--- head/usr.sbin/ctld/kernel.c Fri Feb  6 20:52:01 2015        (r278330)
+++ head/usr.sbin/ctld/kernel.c Fri Feb  6 21:03:25 2015        (r278331)
@@ -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)
 {

Modified: head/usr.sbin/ctld/login.c
==============================================================================
--- head/usr.sbin/ctld/login.c  Fri Feb  6 20:52:01 2015        (r278330)
+++ head/usr.sbin/ctld/login.c  Fri Feb  6 21:03:25 2015        (r278331)
@@ -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,
                        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,
                        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,
                        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,
        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");

Modified: head/usr.sbin/ctld/parse.y
==============================================================================
--- head/usr.sbin/ctld/parse.y  Fri Feb  6 20:52:01 2015        (r278330)
+++ head/usr.sbin/ctld/parse.y  Fri Feb  6 21:03:25 2015        (r278331)
@@ -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_PORTA
        }
        ;
 
+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;

Modified: head/usr.sbin/ctld/token.l
==============================================================================
--- head/usr.sbin/ctld/token.l  Fri Feb  6 20:52:01 2015        (r278330)
+++ head/usr.sbin/ctld/token.l  Fri Feb  6 21:03:25 2015        (r278331)
@@ -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; }
_______________________________________________
svn-src-head@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to