#include "vlog.h"
#define THIS_MODULE VLM_dpif
+/* A datapath interface. */
+struct dpif {
+ char *name;
+ unsigned int minor;
+ int fd;
+};
+
/* Rate limit for individual messages going to or from the datapath, output at
* DBG level. This is very high because, if these are enabled, it is because
* we really need to see them. */
static int get_minor_from_name(const char *name, unsigned int *minor);
static int name_to_minor(const char *name, unsigned int *minor);
static int lookup_minor(const char *name, unsigned int *minor);
-static int open_by_minor(unsigned int minor, struct dpif *);
+static int open_by_minor(unsigned int minor, struct dpif **dpifp);
static int make_openvswitch_device(unsigned int minor, char **fnp);
static void check_rw_odp_flow(struct odp_flow *);
int
-dpif_open(const char *name, struct dpif *dpif)
+dpif_open(const char *name, struct dpif **dpifp)
{
+ struct dpif *dpif;
+ unsigned int minor;
int listen_mask;
int error;
- dpif->fd = -1;
+ *dpifp = NULL;
- error = name_to_minor(name, &dpif->minor);
+ error = name_to_minor(name, &minor);
if (error) {
return error;
}
- error = open_by_minor(dpif->minor, dpif);
+ error = open_by_minor(minor, &dpif);
if (error) {
return error;
}
if (ioctl(dpif->fd, ODP_GET_LISTEN_MASK, &listen_mask)) {
error = errno;
if (error != ENODEV) {
- VLOG_WARN("dp%u: probe returned unexpected error: %s",
- dpif->minor, strerror(error));
+ VLOG_WARN("%s: probe returned unexpected error: %s",
+ dpif_name(dpif), strerror(error));
}
dpif_close(dpif);
return error;
}
+ *dpifp = dpif;
return 0;
}
dpif_close(struct dpif *dpif)
{
if (dpif) {
+ free(dpif->name);
close(dpif->fd);
- dpif->fd = -1;
+ free(dpif);
}
}
int error = ioctl(dpif->fd, cmd, arg) ? errno : 0;
if (cmd_name) {
if (error) {
- VLOG_WARN_RL(&error_rl, "dp%u: ioctl(%s) failed (%s)",
- dpif->minor, cmd_name, strerror(error));
+ VLOG_WARN_RL(&error_rl, "%s: ioctl(%s) failed (%s)",
+ dpif_name(dpif), cmd_name, strerror(error));
} else {
- VLOG_DBG_RL(&dpmsg_rl, "dp%u: ioctl(%s): success",
- dpif->minor, cmd_name);
+ VLOG_DBG_RL(&dpmsg_rl, "%s: ioctl(%s): success",
+ dpif_name(dpif), cmd_name);
}
}
return error;
}
int
-dpif_create(const char *name, struct dpif *dpif)
+dpif_create(const char *name, struct dpif **dpifp)
{
unsigned int minor;
int error;
+ *dpifp = NULL;
if (!get_minor_from_name(name, &minor)) {
/* Minor was specified in 'name', go ahead and create it. */
- error = open_by_minor(minor, dpif);
+ struct dpif *dpif;
+
+ error = open_by_minor(minor, &dpif);
if (error) {
return error;
}
- if (!strncmp(name, "nl:", 3)) {
- char devname[128];
- sprintf(devname, "of%u", minor);
- error = ioctl(dpif->fd, ODP_DP_CREATE, devname) < 0 ? errno : 0;
+ error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
+ if (!error) {
+ *dpifp = dpif;
} else {
- error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
- }
- if (error) {
dpif_close(dpif);
}
return error;
} else {
for (minor = 0; minor < ODP_MAX; minor++) {
- error = open_by_minor(minor, dpif);
+ struct dpif *dpif;
+
+ error = open_by_minor(minor, &dpif);
if (error) {
return error;
}
error = ioctl(dpif->fd, ODP_DP_CREATE, name) < 0 ? errno : 0;
if (!error) {
+ *dpifp = dpif;
return 0;
}
dpif_close(dpif);
}
}
-int
-dpif_get_name(struct dpif *dpif, char *name, size_t name_size)
+const char *
+dpif_name(const struct dpif *dpif)
{
- struct odp_port port;
- int error;
-
- assert(name_size > 0);
- *name = '\0';
-
- error = dpif_port_query_by_number(dpif, ODPP_LOCAL, &port);
- if (!error) {
- ovs_strlcpy(name, port.devname, name_size);
- }
- return error;
+ return dpif->name;
}
int
}
int
-dpif_get_listen_mask(const struct dpif *dpif, int *listen_mask)
-{
- int error = do_ioctl(dpif, ODP_GET_LISTEN_MASK, "ODP_GET_LISTEN_MASK",
- listen_mask);
- if (error) {
- *listen_mask = 0;
- }
- return error;
-}
-
-int
-dpif_set_listen_mask(struct dpif *dpif, int listen_mask)
-{
- return do_ioctl(dpif, ODP_SET_LISTEN_MASK, "ODP_SET_LISTEN_MASK",
- &listen_mask);
-}
-
-int
-dpif_purge(struct dpif *dpif)
+dpif_recv_purge(struct dpif *dpif)
{
struct odp_stats stats;
unsigned int i;
}
int
-dpif_port_add(struct dpif *dpif, const char *devname, uint16_t port_no,
- uint16_t flags)
+dpif_port_add(struct dpif *dpif, const char *devname, uint16_t flags,
+ uint16_t *port_nop)
{
struct odp_port port;
+ uint16_t port_no;
+ int error;
COVERAGE_INC(dpif_port_add);
+
memset(&port, 0, sizeof port);
strncpy(port.devname, devname, sizeof port.devname);
- port.port = port_no;
port.flags = flags;
- if (!ioctl(dpif->fd, ODP_PORT_ADD, &port)) {
- VLOG_DBG_RL(&dpmsg_rl, "dp%u: added %s as port %"PRIu16,
- dpif->minor, devname, port_no);
- return 0;
+
+ error = do_ioctl(dpif, ODP_PORT_ADD, NULL, &port);
+ if (!error) {
+ port_no = port.port;
+ VLOG_DBG_RL(&dpmsg_rl, "%s: added %s as port %"PRIu16,
+ dpif_name(dpif), devname, port_no);
} else {
- VLOG_WARN_RL(&error_rl, "dp%u: failed to add %s as port "
- "%"PRIu16": %s", dpif->minor, devname, port_no,
- strerror(errno));
- return errno;
+ port_no = UINT16_MAX;
+ VLOG_WARN_RL(&error_rl, "%s: failed to add %s as port: %s",
+ dpif_name(dpif), devname, strerror(errno));
}
+ if (port_nop) {
+ *port_nop = port_no;
+ }
+ return error;
}
int
memset(port, 0, sizeof *port);
port->port = port_no;
if (!ioctl(dpif->fd, ODP_PORT_QUERY, port)) {
- VLOG_DBG_RL(&dpmsg_rl, "dp%u: port %"PRIu16" is device %s",
- dpif->minor, port_no, port->devname);
+ VLOG_DBG_RL(&dpmsg_rl, "%s: port %"PRIu16" is device %s",
+ dpif_name(dpif), port_no, port->devname);
return 0;
} else {
- VLOG_WARN_RL(&error_rl, "dp%u: failed to query port %"PRIu16": %s",
- dpif->minor, port_no, strerror(errno));
+ VLOG_WARN_RL(&error_rl, "%s: failed to query port %"PRIu16": %s",
+ dpif_name(dpif), port_no, strerror(errno));
return errno;
}
}
memset(port, 0, sizeof *port);
strncpy(port->devname, devname, sizeof port->devname);
if (!ioctl(dpif->fd, ODP_PORT_QUERY, port)) {
- VLOG_DBG_RL(&dpmsg_rl, "dp%u: device %s is on port %"PRIu16,
- dpif->minor, devname, port->port);
+ VLOG_DBG_RL(&dpmsg_rl, "%s: device %s is on port %"PRIu16,
+ dpif_name(dpif), devname, port->port);
return 0;
} else {
- VLOG_WARN_RL(&error_rl, "dp%u: failed to query port %s: %s",
- dpif->minor, devname, strerror(errno));
+ /* Log level is DBG here because all the current callers are interested
+ * in whether 'dpif' actually has a port 'devname', so that it's not an
+ * issue worth logging if it doesn't. */
+ VLOG_DBG_RL(&error_rl, "%s: failed to query port %s: %s",
+ dpif_name(dpif), devname, strerror(errno));
return errno;
}
}
+int
+dpif_port_get_name(struct dpif *dpif, uint16_t port_no,
+ char *name, size_t name_size)
+{
+ struct odp_port port;
+ int error;
+
+ assert(name_size > 0);
+
+ error = dpif_port_query_by_number(dpif, port_no, &port);
+ if (!error) {
+ ovs_strlcpy(name, port.devname, name_size);
+ } else {
+ *name = '\0';
+ }
+ return error;
+}
+
int
dpif_port_list(const struct dpif *dpif,
- struct odp_port **ports, size_t *n_ports)
+ struct odp_port **portsp, size_t *n_portsp)
{
- struct odp_portvec pv;
- struct odp_stats stats;
+ struct odp_port *ports;
+ size_t n_ports;
int error;
- do {
+ for (;;) {
+ struct odp_stats stats;
+ struct odp_portvec pv;
+
error = dpif_get_dp_stats(dpif, &stats);
if (error) {
- goto error;
+ goto exit;
}
- *ports = xcalloc(1, stats.n_ports * sizeof **ports);
- pv.ports = *ports;
+ ports = xcalloc(stats.n_ports, sizeof *ports);
+ pv.ports = ports;
pv.n_ports = stats.n_ports;
error = do_ioctl(dpif, ODP_PORT_LIST, "ODP_PORT_LIST", &pv);
if (error) {
- free(*ports);
- goto error;
+ /* Hard error. */
+ free(ports);
+ goto exit;
+ } else if (pv.n_ports <= stats.n_ports) {
+ /* Success. */
+ error = 0;
+ n_ports = pv.n_ports;
+ goto exit;
+ } else {
+ /* Soft error: port count increased behind our back. Try again. */
+ free(ports);
}
- } while (pv.n_ports != stats.n_ports);
- *n_ports = pv.n_ports;
- return 0;
+ }
-error:
- *ports = NULL;
- *n_ports = 0;
+exit:
+ if (error) {
+ *portsp = NULL;
+ *n_portsp = 0;
+ } else {
+ *portsp = ports;
+ *n_portsp = n_ports;
+ }
return error;
}
return do_ioctl(dpif, ODP_PORT_GROUP_SET, "ODP_PORT_GROUP_SET", &pg);
}
-/* Careful: '*n_out' can be greater than 'n_ports' on return, if 'n_ports' is
- * less than the number of ports in 'group'. */
int
dpif_port_group_get(const struct dpif *dpif, uint16_t group,
- uint16_t ports[], size_t n_ports, size_t *n_out)
+ uint16_t **ports, size_t *n_ports)
{
- struct odp_port_group pg;
int error;
- assert(n_ports <= UINT16_MAX);
- pg.group = group;
- pg.ports = ports;
- pg.n_ports = n_ports;
- error = do_ioctl(dpif, ODP_PORT_GROUP_GET, "ODP_PORT_GROUP_GET", &pg);
- *n_out = error ? 0 : pg.n_ports;
+ *ports = NULL;
+ *n_ports = 0;
+ for (;;) {
+ struct odp_port_group pg;
+ pg.group = group;
+ pg.ports = *ports;
+ pg.n_ports = *n_ports;
+
+ error = do_ioctl(dpif, ODP_PORT_GROUP_GET, "ODP_PORT_GROUP_GET", &pg);
+ if (error) {
+ /* Hard error. */
+ free(*ports);
+ *ports = NULL;
+ *n_ports = 0;
+ break;
+ } else if (pg.n_ports <= *n_ports) {
+ /* Success. */
+ *n_ports = pg.n_ports;
+ break;
+ } else {
+ /* Soft error: there were more ports than we expected in the
+ * group. Try again. */
+ free(*ports);
+ *ports = xcalloc(pg.n_ports, sizeof **ports);
+ *n_ports = pg.n_ports;
+ }
+ }
return error;
}
const union odp_action *actions, size_t n_actions)
{
struct ds ds = DS_EMPTY_INITIALIZER;
- ds_put_format(&ds, "dp%u: ", dpif->minor);
+ ds_put_format(&ds, "%s: ", dpif_name(dpif));
if (error) {
ds_put_cstr(&ds, "failed to ");
}
int
dpif_flow_get(const struct dpif *dpif, struct odp_flow *flow)
{
- COVERAGE_INC(dpif_flow_query);
+ struct odp_flowvec fv;
+ int error;
+
+ COVERAGE_INC(dpif_flow_get);
+
check_rw_odp_flow(flow);
- memset(&flow->stats, 0, sizeof flow->stats);
- return do_flow_ioctl(dpif, ODP_FLOW_GET, flow, "get flow", true);
+ fv.flows = flow;
+ fv.n_flows = 1;
+ error = do_ioctl(dpif, ODP_FLOW_GET, "ODP_FLOW_GET", &fv);
+ if (!error) {
+ error = flow->stats.error;
+ if (error) {
+ VLOG_WARN_RL(&error_rl, "%s: ioctl(ODP_FLOW_GET) failed (%s)",
+ dpif_name(dpif), strerror(error));
+ }
+ }
+ return error;
}
int
for (i = 0; i < n; i++) {
check_rw_odp_flow(&flows[i]);
}
- return do_ioctl(dpif, ODP_FLOW_GET_MULTIPLE, "ODP_FLOW_GET_MULTIPLE",
+ return do_ioctl(dpif, ODP_FLOW_GET, "ODP_FLOW_GET",
&fv);
}
error = do_ioctl(dpif, ODP_FLOW_LIST, NULL, &fv);
if (error) {
*n_out = 0;
- VLOG_WARN_RL(&error_rl, "dp%u: flow list failed (%s)",
- dpif->minor, strerror(error));
+ VLOG_WARN_RL(&error_rl, "%s: flow list failed (%s)",
+ dpif_name(dpif), strerror(error));
} else {
COVERAGE_ADD(dpif_flow_query_list_n, fv.n_flows);
*n_out = fv.n_flows;
- VLOG_DBG_RL(&dpmsg_rl, "dp%u: listed %zu flows", dpif->minor, *n_out);
+ VLOG_DBG_RL(&dpmsg_rl, "%s: listed %zu flows",
+ dpif_name(dpif), *n_out);
}
return error;
}
}
if (stats.n_flows != n_flows) {
- VLOG_WARN_RL(&error_rl, "dp%u: datapath stats reported %"PRIu32" "
+ VLOG_WARN_RL(&error_rl, "%s: datapath stats reported %"PRIu32" "
"flows but flow listing reported %zu",
- dpif->minor, stats.n_flows, n_flows);
+ dpif_name(dpif), stats.n_flows, n_flows);
}
*flowsp = flows;
*np = n_flows;
if (!(error ? VLOG_DROP_WARN(&error_rl) : VLOG_DROP_DBG(&dpmsg_rl))) {
struct ds ds = DS_EMPTY_INITIALIZER;
char *packet = ofp_packet_to_string(buf->data, buf->size, buf->size);
- ds_put_format(&ds, "dp%u: execute ", dpif->minor);
+ ds_put_format(&ds, "%s: execute ", dpif_name(dpif));
format_odp_actions(&ds, actions, n_actions);
if (error) {
ds_put_format(&ds, " failed (%s)", strerror(error));
return error;
}
+int
+dpif_recv_get_mask(const struct dpif *dpif, int *listen_mask)
+{
+ int error = do_ioctl(dpif, ODP_GET_LISTEN_MASK, "ODP_GET_LISTEN_MASK",
+ listen_mask);
+ if (error) {
+ *listen_mask = 0;
+ }
+ return error;
+}
+
+int
+dpif_recv_set_mask(struct dpif *dpif, int listen_mask)
+{
+ return do_ioctl(dpif, ODP_SET_LISTEN_MASK, "ODP_SET_LISTEN_MASK",
+ &listen_mask);
+}
+
int
dpif_recv(struct dpif *dpif, struct ofpbuf **bufp)
{
if (retval < 0) {
error = errno;
if (error != EAGAIN) {
- VLOG_WARN_RL(&error_rl, "dp%u: read failed: %s",
- dpif->minor, strerror(error));
+ VLOG_WARN_RL(&error_rl, "%s: read failed: %s",
+ dpif_name(dpif), strerror(error));
}
} else if (retval >= sizeof(struct odp_msg)) {
struct odp_msg *msg = buf->data;
void *payload = msg + 1;
size_t length = buf->size - sizeof *msg;
char *s = ofp_packet_to_string(payload, length, length);
- VLOG_DBG_RL(&dpmsg_rl, "dp%u: received %s message of length "
- "%zu on port %"PRIu16": %s", dpif->minor,
+ VLOG_DBG_RL(&dpmsg_rl, "%s: received %s message of length "
+ "%zu on port %"PRIu16": %s", dpif_name(dpif),
(msg->type == _ODPL_MISS_NR ? "miss"
: msg->type == _ODPL_ACTION_NR ? "action"
: "<unknown>"),
COVERAGE_INC(dpif_recv);
return 0;
} else {
- VLOG_WARN_RL(&error_rl, "dp%u: discarding message truncated "
+ VLOG_WARN_RL(&error_rl, "%s: discarding message truncated "
"from %zu bytes to %d",
- dpif->minor, msg->length, retval);
+ dpif_name(dpif), msg->length, retval);
error = ERANGE;
}
} else if (!retval) {
- VLOG_WARN_RL(&error_rl, "dp%u: unexpected end of file", dpif->minor);
+ VLOG_WARN_RL(&error_rl, "%s: unexpected end of file", dpif_name(dpif));
error = EPROTO;
} else {
VLOG_WARN_RL(&error_rl,
- "dp%u: discarding too-short message (%d bytes)",
- dpif->minor, retval);
+ "%s: discarding too-short message (%d bytes)",
+ dpif_name(dpif), retval);
error = ERANGE;
}
{
poll_fd_wait(dpif->fd, POLLIN);
}
+
+void
+dpif_get_netflow_ids(const struct dpif *dpif,
+ uint8_t *engine_type, uint8_t *engine_id)
+{
+ *engine_type = *engine_id = dpif->minor;
+}
\f
struct dpifmon {
- struct dpif dpif;
+ struct dpif *dpif;
struct nl_sock *sock;
int local_ifindex;
};
if (error) {
goto error;
}
- error = dpif_get_name(&mon->dpif, local_name, sizeof local_name);
+ error = dpif_port_get_name(mon->dpif, ODPP_LOCAL,
+ local_name, sizeof local_name);
if (error) {
goto error_close_dpif;
}
return 0;
error_close_dpif:
- dpif_close(&mon->dpif);
+ dpif_close(mon->dpif);
error:
free(mon);
*monp = NULL;
dpifmon_destroy(struct dpifmon *mon)
{
if (mon) {
- dpif_close(&mon->dpif);
+ dpif_close(mon->dpif);
nl_sock_destroy(mon->sock);
}
}
uint32_t master_ifindex = nl_attr_get_u32(attrs[IFLA_MASTER]);
for_us = master_ifindex == mon->local_ifindex;
} else {
- /* It's for us if that device is one of our ports. This is
- * open-coded instead of using dpif_port_query_by_name() to
- * avoid logging a warning on failure. */
+ /* It's for us if that device is one of our ports. */
struct odp_port port;
- memset(&port, 0, sizeof port);
- strncpy(port.devname, devname, sizeof port.devname);
- for_us = !ioctl(mon->dpif.fd, ODP_PORT_QUERY, &port);
+ for_us = !dpif_port_query_by_name(mon->dpif, devname, &port);
}
if (!for_us) {
if (!strncmp(name, "dp", 2) && isdigit(name[2])) {
*minor = atoi(name + 2);
return 0;
- } else if (!strncmp(name, "nl:", 3) && isdigit(name[3])) {
- /* This is for compatibility only and will be dropped. */
- *minor = atoi(name + 3);
- return 0;
} else {
return EINVAL;
}
}
static int
-open_by_minor(unsigned int minor, struct dpif *dpif)
+open_by_minor(unsigned int minor, struct dpif **dpifp)
{
+ struct dpif *dpif;
int error;
char *fn;
int fd;
- dpif->minor = -1;
- dpif->fd = -1;
+ *dpifp = NULL;
error = make_openvswitch_device(minor, &fn);
if (error) {
return error;
free(fn);
return error;
}
-
free(fn);
+
+ dpif = xmalloc(sizeof *dpif);
+ dpif->name = xasprintf("dp%u", dpif->minor);
dpif->minor = minor;
dpif->fd = fd;
+ *dpifp = dpif;
return 0;
}
\f