summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
authormav <mav@FreeBSD.org>2015-02-20 17:09:49 +0000
committermav <mav@FreeBSD.org>2015-02-20 17:09:49 +0000
commite153ed8e1a0b3f8b99080751661f20e98ae34ec8 (patch)
tree1bcbb40474ed9f4d90600664b6b78ee19c302f2c /usr.sbin
parent441b67d0c3c5982947eab6b086c58d8d5a6399d7 (diff)
downloadFreeBSD-src-e153ed8e1a0b3f8b99080751661f20e98ae34ec8.zip
FreeBSD-src-e153ed8e1a0b3f8b99080751661f20e98ae34ec8.tar.gz
MFC r278354: 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. Relnotes: yes Sponsored by: iXsystems, Inc.
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/ctld/ctl.conf.57
-rw-r--r--usr.sbin/ctld/ctld.c101
-rw-r--r--usr.sbin/ctld/ctld.h22
-rw-r--r--usr.sbin/ctld/kernel.c158
-rw-r--r--usr.sbin/ctld/parse.y40
-rw-r--r--usr.sbin/ctld/token.l1
6 files changed, 257 insertions, 72 deletions
diff --git a/usr.sbin/ctld/ctl.conf.5 b/usr.sbin/ctld/ctl.conf.5
index 6201788..b4f59cd 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
@@ -319,6 +320,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 513e073..eb34ccf 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;
@@ -1157,6 +1212,31 @@ 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;
+ 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)
{
struct port *port;
@@ -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);
@@ -1761,6 +1843,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",
@@ -1885,8 +1968,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);
}
@@ -2398,7 +2483,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) {
@@ -2433,7 +2518,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 e213e0b..b98d56a 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;
@@ -186,6 +197,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;
@@ -278,7 +290,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);
@@ -331,8 +343,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 245134c..a94fa2b 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 @@ retry_port:
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) {
@@ -843,39 +860,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;
@@ -934,40 +954,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);
- }
-
- if (req.status == CTL_LUN_ERROR) {
- log_warnx("error returned from port removal request: %s",
- req.error_str);
- return (1);
+ log_warn("CTL_DISABLE_PORT ioctl failed");
+ return (-1);
}
- if (req.status != CTL_LUN_OK) {
- log_warnx("unknown port removal request status %d",
- req.status);
- 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");
}
-
return (0);
}
diff --git a/usr.sbin/ctld/parse.y b/usr.sbin/ctld/parse.y
index d0b2c51..af481fe 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 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
@@ -465,6 +465,8 @@ target_entry:
|
target_portal_group
|
+ target_port
+ |
target_redirect
|
target_lun
@@ -708,6 +710,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;
@@ -937,16 +969,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 d4bf823..ca4ae2a 100644
--- a/usr.sbin/ctld/token.l
+++ b/usr.sbin/ctld/token.l
@@ -71,6 +71,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; }
OpenPOWER on IntegriCloud