Index: sys/cam/ctl/ctl_frontend_iscsi.h =================================================================== --- sys/cam/ctl/ctl_frontend_iscsi.h +++ sys/cam/ctl/ctl_frontend_iscsi.h @@ -47,6 +47,8 @@ int ct_online; int ct_target_id; struct ctl_port ct_port; + int ct_nb_conn; + int ct_max_conn; }; struct cfiscsi_data_wait { Index: sys/cam/ctl/ctl_frontend_iscsi.c =================================================================== --- sys/cam/ctl/ctl_frontend_iscsi.c +++ sys/cam/ctl/ctl_frontend_iscsi.c @@ -98,6 +98,9 @@ static int maxtags = 256; SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, maxtags, CTLFLAG_RWTUN, &maxtags, 0, "Max number of requests queued by initiator"); +static int maxconn = 512; +SYSCTL_INT(_kern_cam_ctl_iscsi, OID_AUTO, maxconn, CTLFLAG_RWTUN, + &maxconn, 0, "Default max number of connections to a target"); #define CFISCSI_DEBUG(X, ...) \ do { \ @@ -1309,8 +1312,10 @@ ("destroying session with non-empty queue")); cfiscsi_session_unregister_initiator(cs); - if (cs->cs_target != NULL) + if (cs->cs_target != NULL) { + cs->cs_target->ct_nb_conn--; cfiscsi_target_release(cs->cs_target); + } icl_conn_close(cs->cs_conn); icl_conn_free(cs->cs_conn); @@ -1565,6 +1570,7 @@ return; } cs->cs_target = ct; + ct->ct_nb_conn++; mtx_unlock(&softc->lock); refcount_acquire(&cs->cs_outstanding_ctl_pdus); @@ -1816,6 +1822,32 @@ ci->status = CTL_ISCSI_OK; } +static void +cfiscsi_ioctl_maxconn(struct ctl_iscsi *ci) +{ + struct cfiscsi_softc *softc; + struct ctl_iscsi_maxconn_params *cihp; + struct cfiscsi_target *ct; + + cihp = (struct ctl_iscsi_maxconn_params *)&(ci->data); + softc = &cfiscsi_softc; + + ct = cfiscsi_target_find(softc, cihp->target_name, + cihp->portal_group_tag); + + if (ct == NULL) { + ci->status = CTL_ISCSI_ERROR; + snprintf(ci->error_str, sizeof(ci->error_str), + "%s: target not found", __func__); + return; + } + + cihp->max_conn = ct->ct_max_conn; + cihp->nb_conn = ct->ct_nb_conn; + + ci->status = CTL_ISCSI_OK; +} + static void cfiscsi_ioctl_limits(struct ctl_iscsi *ci) { @@ -2074,6 +2106,7 @@ struct scsi_vpd_id_descriptor *desc; ctl_options_t opts; int retval, len, idlen; + uint64_t max_conn; uint16_t tag; ctl_init_opts(&opts, req->num_args, req->kern_args); @@ -2105,6 +2138,10 @@ ctl_free_opts(&opts); return; } + retval = ctl_get_opt_number(&opts, "cfiscsi_max_conn", &max_conn); + if (!retval) { + ct->ct_max_conn = max_conn; + } port = &ct->ct_port; // WAT if (ct->ct_state == CFISCSI_TARGET_STATE_DYING) @@ -2266,6 +2303,9 @@ case CTL_ISCSI_LIMITS: cfiscsi_ioctl_limits(ci); break; + case CTL_ISCSI_MAXCONN: + cfiscsi_ioctl_maxconn(ci); + break; #ifdef ICL_KERNEL_PROXY case CTL_ISCSI_LISTEN: cfiscsi_ioctl_listen(ci); @@ -2373,6 +2413,9 @@ return (ct); } + newct->ct_nb_conn = 0; + newct->ct_max_conn = maxconn; + strlcpy(newct->ct_name, name, sizeof(newct->ct_name)); if (alias != NULL) strlcpy(newct->ct_alias, alias, sizeof(newct->ct_alias)); Index: sys/cam/ctl/ctl_ioctl.h =================================================================== --- sys/cam/ctl/ctl_ioctl.h +++ sys/cam/ctl/ctl_ioctl.h @@ -671,6 +671,7 @@ CTL_ISCSI_SEND, CTL_ISCSI_RECEIVE, #endif + CTL_ISCSI_MAXCONN, } ctl_iscsi_type; typedef enum { @@ -747,6 +748,14 @@ int spare[4]; }; +struct ctl_iscsi_maxconn_params { + char target_name[CTL_ISCSI_NAME_LEN]; + int portal_group_tag; /* passed to kernel */ + int nb_conn; /* filled in kernel */ + int max_conn; /* filled in kernel */ + int spare[4]; +}; + #ifdef ICL_KERNEL_PROXY struct ctl_iscsi_listen_params { int iser; @@ -795,6 +804,7 @@ struct ctl_iscsi_logout_params logout; struct ctl_iscsi_terminate_params terminate; struct ctl_iscsi_limits_params limits; + struct ctl_iscsi_maxconn_params maxconn; #ifdef ICL_KERNEL_PROXY struct ctl_iscsi_listen_params listen; struct ctl_iscsi_accept_params accept; Index: usr.sbin/ctld/ctld.h =================================================================== --- usr.sbin/ctld/ctld.h +++ usr.sbin/ctld/ctld.h @@ -187,6 +187,7 @@ char *t_name; char *t_alias; char *t_redirection; + char *t_max_conn; }; struct isns { @@ -403,6 +404,7 @@ int kernel_lun_modify(struct lun *lun); int kernel_lun_remove(struct lun *lun); void kernel_handoff(struct connection *conn); +int kernel_maxconn(struct connection *conn); void kernel_limits(const char *offload, size_t *max_data_segment_length); int kernel_port_add(struct port *port); Index: usr.sbin/ctld/kernel.c =================================================================== --- usr.sbin/ctld/kernel.c +++ usr.sbin/ctld/kernel.c @@ -922,6 +922,33 @@ } } +int +kernel_maxconn(struct connection *conn) +{ + struct ctl_iscsi req; + + bzero(&req, sizeof(req)); + + req.type = CTL_ISCSI_MAXCONN; + strlcpy(req.data.maxconn.target_name, + conn->conn_target->t_name, sizeof(req.data.maxconn.target_name)); + req.data.maxconn.portal_group_tag = + conn->conn_portal->p_portal_group->pg_tag; + + if (ioctl(ctl_fd, CTL_ISCSI, &req) == -1) { + log_err(1, "error issuing CTL_ISCSI ioctl; " + "dropping connection"); + return (1); + } + + if (req.status != CTL_ISCSI_OK) { + log_errx(1, "error returned from CTL iSCSI maxconn request: " + "%s; dropping connection", req.error_str); + return (1); + } + return !(req.data.maxconn.nb_conn < req.data.maxconn.max_conn); +} + void kernel_limits(const char *offload, size_t *max_data_segment_length) { @@ -972,7 +999,7 @@ bzero(&req, sizeof(req)); strlcpy(req.driver, "iscsi", sizeof(req.driver)); req.reqtype = CTL_REQ_CREATE; - req.num_args = 5; + req.num_args = 6; TAILQ_FOREACH(o, &pg->pg_options, o_next) req.num_args++; req.args = malloc(req.num_args * sizeof(*req.args)); @@ -989,6 +1016,8 @@ str_arg(&req.args[n++], "cfiscsi_portal_group_tag", tagstr); if (targ->t_alias) str_arg(&req.args[n++], "cfiscsi_target_alias", targ->t_alias); + if (targ->t_max_conn) + str_arg(&req.args[n++], "cfiscsi_max_conn", targ->t_max_conn); str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name); TAILQ_FOREACH(o, &pg->pg_options, o_next) str_arg(&req.args[n++], o->o_name, o->o_value); Index: usr.sbin/ctld/login.c =================================================================== --- usr.sbin/ctld/login.c +++ usr.sbin/ctld/login.c @@ -687,6 +687,11 @@ assert(conn->conn_target != NULL); kernel_limits(conn->conn_portal->p_portal_group->pg_offload, &conn->conn_data_segment_limit); + if (kernel_maxconn(conn) == true) { + login_send_error(request, 0x02, 0x00); + log_errx(1, "Max number of connections to target reached"); + } + } else { conn->conn_data_segment_limit = MAX_DATA_SEGMENT_LENGTH; } Index: usr.sbin/ctld/parse.y =================================================================== --- usr.sbin/ctld/parse.y +++ usr.sbin/ctld/parse.y @@ -60,7 +60,7 @@ %token CLOSING_BRACKET CTL_LUN DEBUG DEVICE_ID DEVICE_TYPE %token DISCOVERY_AUTH_GROUP DISCOVERY_FILTER FOREIGN %token INITIATOR_NAME INITIATOR_PORTAL ISNS_SERVER ISNS_PERIOD ISNS_TIMEOUT -%token LISTEN LISTEN_ISER LUN MAXPROC OFFLOAD OPENING_BRACKET OPTION +%token LISTEN LISTEN_ISER LUN MAXCONN MAXPROC OFFLOAD OPENING_BRACKET OPTION %token PATH PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR %token TAG TARGET TIMEOUT @@ -524,6 +524,8 @@ target_lun | target_lun_ref + | + target_max_conn ; target_alias: ALIAS STR @@ -803,6 +805,17 @@ } ; +target_max_conn: MAXCONN STR + { + if (target->t_max_conn != NULL) { + log_warnx("max_conn for target \"%s\" " + "specified more than once", target->t_name); + return (1); + } + target->t_max_conn = $2; + } + ; + target_lun: LUN lun_number OPENING_BRACKET lun_entries CLOSING_BRACKET { Index: usr.sbin/ctld/token.l =================================================================== --- usr.sbin/ctld/token.l +++ usr.sbin/ctld/token.l @@ -67,6 +67,7 @@ listen-iser { return LISTEN_ISER; } lun { return LUN; } maxproc { return MAXPROC; } +max_conn { return MAXCONN; } offload { return OFFLOAD; } option { return OPTION; } path { return PATH; } Index: usr.sbin/ctld/uclparse.c =================================================================== --- usr.sbin/ctld/uclparse.c +++ usr.sbin/ctld/uclparse.c @@ -775,6 +775,16 @@ } } + if (!strcmp(key, "max_conn")) { + if (obj->type != UCL_STRING) { + log_warnx("\"max_conn\" property of target " + "\"%s\" is not a string", target->t_name); + return (1); + } + + target->t_max_conn = strdup(ucl_object_tostring(obj)); + } + if (!strcmp(key, "redirect")) { if (obj->type != UCL_STRING) { log_warnx("\"redirect\" property of target "