From 057abcb00413010898f3046f7704444b8f537bab Mon Sep 17 00:00:00 2001 From: Alexander Motin Date: Sat, 7 Feb 2015 13:19:04 +0000 Subject: [PATCH] Teach ctld(8) to control non-iSCSI CTL ports. This change introduces new target option "port", that assigns current target to specified CTL port. On config application ctld(8) will apply LUN mapping according to target configuration to specified port and bring the port up. On shutdown cltd(8) will remove the mapping and put the port down. This change allows to configure both iSCSI and FibreChannel targets in the same configuration file in alike way. Kernel side support was added earlier at r278037. MFC after: 2 weeks Relnotes: yes Sponsored by: iXsystems, Inc. --- usr.sbin/ctld/ctl.conf.5 | 7 +- usr.sbin/ctld/ctld.c | 101 +++++++++++++++++++++++-- usr.sbin/ctld/ctld.h | 22 +++++- usr.sbin/ctld/kernel.c | 158 ++++++++++++++++++++++++--------------- usr.sbin/ctld/parse.y | 40 +++++++++- usr.sbin/ctld/token.l | 1 + 6 files changed, 257 insertions(+), 72 deletions(-) diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5 index 404699829920..c74e2919b9e1 100644 --- a/usr.sbin/ctld/ctl.conf.5 +++ b/usr.sbin/ctld/ctl.conf.5 @@ -27,7 +27,7 @@ .\" .\" $FreeBSD$ .\" -.Dd February 6, 2015 +.Dd February 7, 2015 .Dt CTL.CONF 5 .Os .Sh NAME @@ -67,6 +67,7 @@ file is: .No target Ar name { .Dl auth-group Ar name .Dl portal-group Ar name Op Ar agname +.Dl port Ar name .Dl lun Ar number Ar name .Dl lun Ar number No { .Dl path Ar path @@ -321,6 +322,10 @@ on TCP port 3260 on all configured IPv4 and IPv6 addresses. Optional second argument specifies auth group name for connections to this specific portal group. If second argument is not specified, target auth group is used. +.It Ic port Ar name +Assign specified CTL port (such as "isp0") to the target. +On startup ctld configures LUN mapping and enables all assigned ports. +Each port can be assigned to only one target. .It Ic redirect Aq Ar address IPv4 or IPv6 address to redirect initiators to. When configured, all initiators attempting to connect to this target diff --git a/usr.sbin/ctld/ctld.c b/usr.sbin/ctld/ctld.c index dd864b95d286..0f62ba3f104e 100644 --- a/usr.sbin/ctld/ctld.c +++ b/usr.sbin/ctld/ctld.c @@ -93,6 +93,7 @@ conf_new(void) TAILQ_INIT(&conf->conf_auth_groups); TAILQ_INIT(&conf->conf_ports); TAILQ_INIT(&conf->conf_portal_groups); + TAILQ_INIT(&conf->conf_pports); TAILQ_INIT(&conf->conf_isns); conf->conf_isns_period = 900; @@ -111,6 +112,7 @@ conf_delete(struct conf *conf) struct target *targ, *tmp; struct auth_group *ag, *cagtmp; struct portal_group *pg, *cpgtmp; + struct pport *pp, *pptmp; struct isns *is, *istmp; assert(conf->conf_pidfh == NULL); @@ -123,6 +125,8 @@ conf_delete(struct conf *conf) auth_group_delete(ag); TAILQ_FOREACH_SAFE(pg, &conf->conf_portal_groups, pg_next, cpgtmp) portal_group_delete(pg); + TAILQ_FOREACH_SAFE(pp, &conf->conf_pports, pp_next, pptmp) + pport_delete(pp); TAILQ_FOREACH_SAFE(is, &conf->conf_isns, i_next, istmp) isns_delete(is); assert(TAILQ_EMPTY(&conf->conf_ports)); @@ -1133,21 +1137,72 @@ valid_iscsi_name(const char *name) return (true); } +struct pport * +pport_new(struct conf *conf, const char *name, uint32_t ctl_port) +{ + struct pport *pp; + + pp = calloc(1, sizeof(*pp)); + if (pp == NULL) + log_err(1, "calloc"); + pp->pp_conf = conf; + pp->pp_name = checked_strdup(name); + pp->pp_ctl_port = ctl_port; + TAILQ_INIT(&pp->pp_ports); + TAILQ_INSERT_TAIL(&conf->conf_pports, pp, pp_next); + return (pp); +} + +struct pport * +pport_find(const struct conf *conf, const char *name) +{ + struct pport *pp; + + TAILQ_FOREACH(pp, &conf->conf_pports, pp_next) { + if (strcasecmp(pp->pp_name, name) == 0) + return (pp); + } + return (NULL); +} + +struct pport * +pport_copy(struct pport *pp, struct conf *conf) +{ + struct pport *ppnew; + + ppnew = pport_new(conf, pp->pp_name, pp->pp_ctl_port); + return (ppnew); +} + +void +pport_delete(struct pport *pp) +{ + struct port *port, *tport; + + TAILQ_FOREACH_SAFE(port, &pp->pp_ports, p_ts, tport) + port_delete(port); + TAILQ_REMOVE(&pp->pp_conf->conf_pports, pp, pp_next); + free(pp->pp_name); + free(pp); +} + struct port * port_new(struct conf *conf, struct target *target, struct portal_group *pg) { struct port *port; + char *name; + asprintf(&name, "%s-%s", pg->pg_name, target->t_name); + if (port_find(conf, name) != NULL) { + log_warnx("duplicate port \"%s\"", name); + free(name); + return (NULL); + } port = calloc(1, sizeof(*port)); if (port == NULL) log_err(1, "calloc"); - asprintf(&port->p_name, "%s-%s", pg->pg_name, target->t_name); - if (port_find(conf, port->p_name) != NULL) { - log_warnx("duplicate port \"%s\"", port->p_name); - free(port); - return (NULL); - } port->p_conf = conf; + port->p_name = name; TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); port->p_target = target; @@ -1156,6 +1211,31 @@ port_new(struct conf *conf, struct target *target, struct portal_group *pg) return (port); } +struct port * +port_new_pp(struct conf *conf, struct target *target, struct pport *pp) +{ + struct port *port; + char *name; + + asprintf(&name, "%s-%s", pp->pp_name, target->t_name); + if (port_find(conf, name) != NULL) { + log_warnx("duplicate port \"%s\"", name); + free(name); + return (NULL); + } + port = calloc(1, sizeof(*port)); + if (port == NULL) + log_err(1, "calloc"); + port->p_conf = conf; + port->p_name = name; + TAILQ_INSERT_TAIL(&conf->conf_ports, port, p_next); + TAILQ_INSERT_TAIL(&target->t_ports, port, p_ts); + port->p_target = target; + TAILQ_INSERT_TAIL(&pp->pp_ports, port, p_pps); + port->p_pport = pp; + return (port); +} + struct port * port_find(const struct conf *conf, const char *name) { @@ -1188,6 +1268,8 @@ port_delete(struct port *port) if (port->p_portal_group) TAILQ_REMOVE(&port->p_portal_group->pg_ports, port, p_pgs); + if (port->p_pport) + TAILQ_REMOVE(&port->p_pport->pp_ports, port, p_pps); if (port->p_target) TAILQ_REMOVE(&port->p_target->t_ports, port, p_ts); TAILQ_REMOVE(&port->p_conf->conf_ports, port, p_next); @@ -1779,6 +1861,7 @@ conf_apply(struct conf *oldconf, struct conf *newconf) newport = port_find(newconf, oldport->p_name); if (newport != NULL) continue; + log_debugx("removing port \"%s\"", oldport->p_name); error = kernel_port_remove(oldport); if (error != 0) { log_warnx("failed to remove port %s", @@ -1903,8 +1986,10 @@ conf_apply(struct conf *oldconf, struct conf *newconf) oldport = port_find(oldconf, newport->p_name); if (oldport == NULL) { + log_debugx("adding port \"%s\"", newport->p_name); error = kernel_port_add(newport); } else { + log_debugx("updating port \"%s\"", newport->p_name); newport->p_ctl_port = oldport->p_ctl_port; error = kernel_port_update(newport); } @@ -2416,7 +2501,7 @@ main(int argc, char **argv) kernel_init(); oldconf = conf_new_from_kernel(); - newconf = conf_new_from_file(config_path); + newconf = conf_new_from_file(config_path, oldconf); if (newconf == NULL) log_errx(1, "configuration error; exiting"); if (debug > 0) { @@ -2451,7 +2536,7 @@ main(int argc, char **argv) if (sighup_received) { sighup_received = false; log_debugx("received SIGHUP, reloading configuration"); - tmpconf = conf_new_from_file(config_path); + tmpconf = conf_new_from_file(config_path, newconf); if (tmpconf == NULL) { log_warnx("configuration error, " "continuing with old configuration"); diff --git a/usr.sbin/ctld/ctld.h b/usr.sbin/ctld/ctld.h index 395b0144ae55..c07c0346a946 100644 --- a/usr.sbin/ctld/ctld.h +++ b/usr.sbin/ctld/ctld.h @@ -125,14 +125,25 @@ struct portal_group { uint16_t pg_tag; }; +struct pport { + TAILQ_ENTRY(pport) pp_next; + TAILQ_HEAD(, port) pp_ports; + struct conf *pp_conf; + char *pp_name; + + uint32_t pp_ctl_port; +}; + struct port { TAILQ_ENTRY(port) p_next; TAILQ_ENTRY(port) p_pgs; + TAILQ_ENTRY(port) p_pps; TAILQ_ENTRY(port) p_ts; struct conf *p_conf; char *p_name; struct auth_group *p_auth_group; struct portal_group *p_portal_group; + struct pport *p_pport; struct target *p_target; uint32_t p_ctl_port; @@ -187,6 +198,7 @@ struct conf { TAILQ_HEAD(, auth_group) conf_auth_groups; TAILQ_HEAD(, port) conf_ports; TAILQ_HEAD(, portal_group) conf_portal_groups; + TAILQ_HEAD(, pport) conf_pports; TAILQ_HEAD(, isns) conf_isns; int conf_isns_period; int conf_isns_timeout; @@ -280,7 +292,7 @@ char *rchap_get_response(struct rchap *rchap); void rchap_delete(struct rchap *rchap); struct conf *conf_new(void); -struct conf *conf_new_from_file(const char *path); +struct conf *conf_new_from_file(const char *path, struct conf *old); struct conf *conf_new_from_kernel(void); void conf_delete(struct conf *conf); int conf_verify(struct conf *conf); @@ -333,8 +345,16 @@ void isns_register(struct isns *isns, struct isns *oldisns); void isns_check(struct isns *isns); void isns_deregister(struct isns *isns); +struct pport *pport_new(struct conf *conf, const char *name, + uint32_t ctl_port); +struct pport *pport_find(const struct conf *conf, const char *name); +struct pport *pport_copy(struct pport *pport, struct conf *conf); +void pport_delete(struct pport *pport); + struct port *port_new(struct conf *conf, struct target *target, struct portal_group *pg); +struct port *port_new_pp(struct conf *conf, struct target *target, + struct pport *pp); struct port *port_find(const struct conf *conf, const char *name); struct port *port_find_in_pg(const struct portal_group *pg, const char *target); diff --git a/usr.sbin/ctld/kernel.c b/usr.sbin/ctld/kernel.c index 47dc56aa3be3..dc3c02af16fd 100644 --- a/usr.sbin/ctld/kernel.c +++ b/usr.sbin/ctld/kernel.c @@ -121,6 +121,7 @@ struct cctl_lun { struct cctl_port { uint32_t port_id; + char *port_name; int cfiscsi_state; char *cfiscsi_target; uint16_t cfiscsi_portal_group_tag; @@ -330,7 +331,10 @@ cctl_end_pelement(void *user_data, const char *name) devlist->cur_sb[devlist->level] = NULL; devlist->level--; - if (strcmp(name, "cfiscsi_target") == 0) { + if (strcmp(name, "port_name") == 0) { + cur_port->port_name = str; + str = NULL; + } else if (strcmp(name, "cfiscsi_target") == 0) { cur_port->cfiscsi_target = str; str = NULL; } else if (strcmp(name, "cfiscsi_state") == 0) { @@ -378,6 +382,7 @@ conf_new_from_kernel(void) struct conf *conf = NULL; struct target *targ; struct portal_group *pg; + struct pport *pp; struct port *cp; struct lun *cl; struct lun_option *lo; @@ -498,8 +503,20 @@ conf_new_from_kernel(void) STAILQ_FOREACH(port, &devlist.port_list, links) { if (port->cfiscsi_target == NULL) { - log_debugx("CTL port %ju wasn't managed by ctld; " - "ignoring", (uintmax_t)port->port_id); + log_debugx("CTL port %u \"%s\" wasn't managed by ctld; ", + port->port_id, port->port_name); + pp = pport_find(conf, port->port_name); + if (pp == NULL) { +#if 0 + log_debugx("found new kernel port %u \"%s\"", + port->port_id, port->port_name); +#endif + pp = pport_new(conf, port->port_name, port->port_id); + if (pp == NULL) { + log_warnx("pport_new failed"); + continue; + } + } continue; } if (port->cfiscsi_state != 1) { @@ -880,39 +897,42 @@ kernel_port_add(struct port *port) int error, i, n; /* Create iSCSI port. */ - bzero(&req, sizeof(req)); - strlcpy(req.driver, "iscsi", sizeof(req.driver)); - req.reqtype = CTL_REQ_CREATE; - req.args = malloc(req.num_args * sizeof(*req.args)); - n = 0; - req.args[n].namelen = sizeof("port_id"); - req.args[n].name = __DECONST(char *, "port_id"); - req.args[n].vallen = sizeof(port->p_ctl_port); - req.args[n].value = &port->p_ctl_port; - req.args[n++].flags = CTL_BEARG_WR; - str_arg(&req.args[n++], "cfiscsi_target", targ->t_name); - snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag); - 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); - str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name); - req.num_args = n; - error = ioctl(ctl_fd, CTL_PORT_REQ, &req); - free(req.args); - if (error != 0) { - log_warn("error issuing CTL_PORT_REQ ioctl"); - return (1); - } - if (req.status == CTL_LUN_ERROR) { - log_warnx("error returned from port creation request: %s", - req.error_str); - return (1); - } - if (req.status != CTL_LUN_OK) { - log_warnx("unknown port creation request status %d", - req.status); - return (1); - } + if (port->p_portal_group) { + bzero(&req, sizeof(req)); + strlcpy(req.driver, "iscsi", sizeof(req.driver)); + req.reqtype = CTL_REQ_CREATE; + req.args = malloc(req.num_args * sizeof(*req.args)); + n = 0; + req.args[n].namelen = sizeof("port_id"); + req.args[n].name = __DECONST(char *, "port_id"); + req.args[n].vallen = sizeof(port->p_ctl_port); + req.args[n].value = &port->p_ctl_port; + req.args[n++].flags = CTL_BEARG_WR; + str_arg(&req.args[n++], "cfiscsi_target", targ->t_name); + snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag); + 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); + str_arg(&req.args[n++], "ctld_portal_group_name", pg->pg_name); + req.num_args = n; + error = ioctl(ctl_fd, CTL_PORT_REQ, &req); + free(req.args); + if (error != 0) { + log_warn("error issuing CTL_PORT_REQ ioctl"); + return (1); + } + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from port creation request: %s", + req.error_str); + return (1); + } + if (req.status != CTL_LUN_OK) { + log_warnx("unknown port creation request status %d", + req.status); + return (1); + } + } else if (port->p_pport) + port->p_ctl_port = port->p_pport->pp_ctl_port; /* Explicitly enable mapping to block any access except allowed. */ lm.port = port->p_ctl_port; @@ -971,40 +991,58 @@ kernel_port_update(struct port *port) int kernel_port_remove(struct port *port) { + struct ctl_port_entry entry; + struct ctl_lun_map lm; struct ctl_req req; char tagstr[16]; struct target *targ = port->p_target; struct portal_group *pg = port->p_portal_group; int error; - bzero(&req, sizeof(req)); - strlcpy(req.driver, "iscsi", sizeof(req.driver)); - req.reqtype = CTL_REQ_REMOVE; - req.num_args = 2; - req.args = malloc(req.num_args * sizeof(*req.args)); - str_arg(&req.args[0], "cfiscsi_target", targ->t_name); - snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag); - str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr); - - error = ioctl(ctl_fd, CTL_PORT_REQ, &req); - free(req.args); + /* Disable port */ + bzero(&entry, sizeof(entry)); + entry.targ_port = port->p_ctl_port; + error = ioctl(ctl_fd, CTL_DISABLE_PORT, &entry); if (error != 0) { - log_warn("error issuing CTL_PORT_REQ ioctl"); - return (1); + log_warn("CTL_DISABLE_PORT ioctl failed"); + return (-1); } - if (req.status == CTL_LUN_ERROR) { - log_warnx("error returned from port removal request: %s", - req.error_str); - return (1); + /* Remove iSCSI port. */ + if (port->p_portal_group) { + bzero(&req, sizeof(req)); + strlcpy(req.driver, "iscsi", sizeof(req.driver)); + req.reqtype = CTL_REQ_REMOVE; + req.num_args = 2; + req.args = malloc(req.num_args * sizeof(*req.args)); + str_arg(&req.args[0], "cfiscsi_target", targ->t_name); + snprintf(tagstr, sizeof(tagstr), "%d", pg->pg_tag); + str_arg(&req.args[1], "cfiscsi_portal_group_tag", tagstr); + error = ioctl(ctl_fd, CTL_PORT_REQ, &req); + free(req.args); + if (error != 0) { + log_warn("error issuing CTL_PORT_REQ ioctl"); + return (1); + } + if (req.status == CTL_LUN_ERROR) { + log_warnx("error returned from port removal request: %s", + req.error_str); + return (1); + } + if (req.status != CTL_LUN_OK) { + log_warnx("unknown port removal request status %d", + req.status); + return (1); + } + } else { + /* Disable LUN mapping. */ + lm.port = port->p_ctl_port; + lm.plun = UINT32_MAX; + lm.lun = UINT32_MAX; + error = ioctl(ctl_fd, CTL_LUN_MAP, &lm); + if (error != 0) + log_warn("CTL_LUN_MAP ioctl failed"); } - - if (req.status != CTL_LUN_OK) { - log_warnx("unknown port removal request status %d", - req.status); - return (1); - } - return (0); } diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y index 5eaffe4e324a..a7807eff1434 100644 --- a/usr.sbin/ctld/parse.y +++ b/usr.sbin/ctld/parse.y @@ -61,7 +61,7 @@ extern void yyrestart(FILE *); %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 OFFLOAD OPENING_BRACKET OPTION -%token PATH PIDFILE PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR +%token PATH PIDFILE PORT PORTAL_GROUP REDIRECT SEMICOLON SERIAL SIZE STR %token TARGET TIMEOUT %union @@ -467,6 +467,8 @@ target_entry: | target_portal_group | + target_port + | target_redirect | target_lun @@ -721,6 +723,36 @@ target_portal_group: PORTAL_GROUP STR STR } ; +target_port: PORT STR + { + struct pport *pp; + struct port *tp; + + pp = pport_find(conf, $2); + if (pp == NULL) { + log_warnx("unknown port \"%s\" for target \"%s\"", + $2, target->t_name); + free($2); + return (1); + } + if (!TAILQ_EMPTY(&pp->pp_ports)) { + log_warnx("can't link port \"%s\" to target \"%s\", " + "port already linked to some target", + $2, target->t_name); + free($2); + return (1); + } + tp = port_new_pp(conf, target, pp); + if (tp == NULL) { + log_warnx("can't link port \"%s\" to target \"%s\"", + $2, target->t_name); + free($2); + return (1); + } + free($2); + } + ; + target_redirect: REDIRECT STR { int error; @@ -950,16 +982,20 @@ check_perms(const char *path) } struct conf * -conf_new_from_file(const char *path) +conf_new_from_file(const char *path, struct conf *oldconf) { struct auth_group *ag; struct portal_group *pg; + struct pport *pp; int error; log_debugx("obtaining configuration from %s", path); conf = conf_new(); + TAILQ_FOREACH(pp, &oldconf->conf_pports, pp_next) + pport_copy(pp, conf); + ag = auth_group_new(conf, "default"); assert(ag != NULL); diff --git a/usr.sbin/ctld/token.l b/usr.sbin/ctld/token.l index fd274983f447..f0cb597bcd07 100644 --- a/usr.sbin/ctld/token.l +++ b/usr.sbin/ctld/token.l @@ -72,6 +72,7 @@ pidfile { return PIDFILE; } isns-server { return ISNS_SERVER; } isns-period { return ISNS_PERIOD; } isns-timeout { return ISNS_TIMEOUT; } +port { return PORT; } portal-group { return PORTAL_GROUP; } redirect { return REDIRECT; } serial { return SERIAL; }