};
struct upcall {
- struct ofproto_dpif *ofproto;
+ struct ofproto_dpif *ofproto; /* Parent ofproto. */
- struct flow flow;
- const struct nlattr *key;
- size_t key_len;
- enum dpif_upcall_type upcall_type;
- struct dpif_flow_stats stats;
- odp_port_t odp_in_port;
+ /* The flow and packet are only required to be constant when using
+ * dpif-netdev. If a modification is absolutely necessary, a const cast
+ * may be used with other datapaths. */
+ const struct flow *flow; /* Parsed representation of the packet. */
+ const struct ofpbuf *packet; /* Packet associated with this upcall. */
+ ofp_port_t in_port; /* OpenFlow in port, or OFPP_NONE. */
- uint64_t slow_path_buf[128 / 8];
- struct odputil_keybuf mask_buf;
+ enum dpif_upcall_type type; /* Datapath type of the upcall. */
+ const struct nlattr *userdata; /* Userdata for DPIF_UC_ACTION Upcalls. */
+
+ bool xout_initialized; /* True if 'xout' must be uninitialized. */
+ struct xlate_out xout; /* Result of xlate_actions(). */
+ struct ofpbuf put_actions; /* Actions 'put' in the fastapath. */
+
+ struct dpif_ipfix *ipfix; /* IPFIX reference or NULL. */
+ struct dpif_sflow *sflow; /* SFlow reference or NULL. */
+ struct netflow *netflow; /* Netlow reference or NULL. */
- struct xlate_out xout;
+ bool vsp_adjusted; /* 'packet' and 'flow' were adjusted for
+ VLAN splinters if true. */
- /* Raw upcall plus data for keeping track of the memory backing it. */
- struct dpif_upcall dpif_upcall; /* As returned by dpif_recv() */
- struct ofpbuf upcall_buf; /* Owns some data in 'dpif_upcall'. */
- uint64_t upcall_stub[512 / 8]; /* Buffer to reduce need for malloc(). */
+ /* Not used by the upcall callback interface. */
+ const struct nlattr *key; /* Datapath flow key. */
+ size_t key_len; /* Datapath flow key length. */
};
/* 'udpif_key's are responsible for tracking the little bit of state udpif
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
static struct list all_udpifs = LIST_INITIALIZER(&all_udpifs);
-static size_t read_upcalls(struct handler *,
- struct upcall upcalls[UPCALL_MAX_BATCH]);
-static void free_upcall(struct upcall *);
-static int convert_upcall(struct udpif *, struct upcall *);
+static size_t recv_upcalls(struct handler *);
+static int process_upcall(struct udpif *, struct upcall *,
+ struct ofpbuf *odp_actions);
static void handle_upcalls(struct udpif *, struct upcall *, size_t n_upcalls);
static void udpif_stop_threads(struct udpif *);
static void udpif_start_threads(struct udpif *, size_t n_handlers,
size_t key_len, long long int used,
struct udpif_key **result);
static void ukey_delete(struct revalidator *, struct udpif_key *);
+static enum upcall_type classify_upcall(enum dpif_upcall_type type,
+ const struct nlattr *userdata);
+
+static void exec_upcalls(struct dpif *, struct dpif_upcall *, struct ofpbuf *,
+ int cnt);
+
+static int upcall_receive(struct upcall *, const struct dpif_backer *,
+ const struct ofpbuf *packet, enum dpif_upcall_type,
+ const struct nlattr *userdata, const struct flow *);
+static void upcall_uninit(struct upcall *);
static atomic_bool enable_megaflows = ATOMIC_VAR_INIT(true);
struct udpif *udpif = handler->udpif;
while (!latch_is_set(&handler->udpif->exit_latch)) {
- struct upcall upcalls[UPCALL_MAX_BATCH];
- size_t n_upcalls, i;
-
- n_upcalls = read_upcalls(handler, upcalls);
- if (!n_upcalls) {
+ if (!recv_upcalls(handler)) {
dpif_recv_wait(udpif->dpif, handler->handler_id);
latch_wait(&udpif->exit_latch);
poll_block();
- } else {
- handle_upcalls(handler->udpif, upcalls, n_upcalls);
-
- for (i = 0; i < n_upcalls; i++) {
- free_upcall(&upcalls[i]);
- }
}
coverage_clear();
}
return NULL;
}
+static size_t
+recv_upcalls(struct handler *handler)
+{
+ struct udpif *udpif = handler->udpif;
+ uint64_t recv_stubs[UPCALL_MAX_BATCH][512 / 8];
+ struct ofpbuf recv_bufs[UPCALL_MAX_BATCH];
+ struct upcall upcalls[UPCALL_MAX_BATCH];
+ size_t n_upcalls, i;
+
+ n_upcalls = 0;
+ while (n_upcalls < UPCALL_MAX_BATCH) {
+ struct ofpbuf *recv_buf = &recv_bufs[n_upcalls];
+ struct upcall *upcall = &upcalls[n_upcalls];
+ struct dpif_upcall dupcall;
+ struct pkt_metadata md;
+ struct flow flow;
+ int error;
+
+ ofpbuf_use_stub(&recv_buf[n_upcalls], recv_stubs[n_upcalls],
+ sizeof recv_stubs[n_upcalls]);
+ if (dpif_recv(udpif->dpif, handler->handler_id, &dupcall, recv_buf)) {
+ ofpbuf_uninit(recv_buf);
+ break;
+ }
+
+ if (odp_flow_key_to_flow(dupcall.key, dupcall.key_len, &flow)
+ == ODP_FIT_ERROR) {
+ goto free_dupcall;
+ }
+
+ error = upcall_receive(upcall, udpif->backer, &dupcall.packet,
+ dupcall.type, dupcall.userdata, &flow);
+ if (error) {
+ if (error == ENODEV) {
+ /* Received packet on datapath port for which we couldn't
+ * associate an ofproto. This can happen if a port is removed
+ * while traffic is being received. Print a rate-limited
+ * message in case it happens frequently. */
+ dpif_flow_put(udpif->dpif, DPIF_FP_CREATE, dupcall.key,
+ dupcall.key_len, NULL, 0, NULL, 0, NULL);
+ VLOG_INFO_RL(&rl, "received packet on unassociated datapath "
+ "port %"PRIu32, flow.in_port.odp_port);
+ }
+ goto free_dupcall;
+ }
+
+ upcall->key = dupcall.key;
+ upcall->key_len = dupcall.key_len;
+
+ if (vsp_adjust_flow(upcall->ofproto, &flow, &dupcall.packet)) {
+ upcall->vsp_adjusted = true;
+ }
+
+ md = pkt_metadata_from_flow(&flow);
+ flow_extract(&dupcall.packet, &md, &flow);
+
+ error = process_upcall(udpif, upcall, NULL);
+ if (error) {
+ goto cleanup;
+ }
+
+ n_upcalls++;
+ continue;
+
+cleanup:
+ upcall_uninit(upcall);
+free_dupcall:
+ ofpbuf_uninit(&dupcall.packet);
+ ofpbuf_uninit(recv_buf);
+ }
+
+ if (n_upcalls) {
+ handle_upcalls(handler->udpif, upcalls, n_upcalls);
+ for (i = 0; i < n_upcalls; i++) {
+ ofpbuf_uninit(CONST_CAST(struct ofpbuf *, upcalls[i].packet));
+ ofpbuf_uninit(&recv_bufs[i]);
+ upcall_uninit(&upcalls[i]);
+ }
+ }
+
+ return n_upcalls;
+}
+
static void *
udpif_revalidator(void *arg)
{
}
\f
static enum upcall_type
-classify_upcall(const struct upcall *upcall)
+classify_upcall(enum dpif_upcall_type type, const struct nlattr *userdata)
{
- const struct dpif_upcall *dpif_upcall = &upcall->dpif_upcall;
union user_action_cookie cookie;
size_t userdata_len;
/* First look at the upcall type. */
- switch (dpif_upcall->type) {
+ switch (type) {
case DPIF_UC_ACTION:
break;
case DPIF_N_UC_TYPES:
default:
- VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32,
- dpif_upcall->type);
+ VLOG_WARN_RL(&rl, "upcall has unexpected type %"PRIu32, type);
return BAD_UPCALL;
}
/* "action" upcalls need a closer look. */
- if (!dpif_upcall->userdata) {
+ if (!userdata) {
VLOG_WARN_RL(&rl, "action upcall missing cookie");
return BAD_UPCALL;
}
- userdata_len = nl_attr_get_size(dpif_upcall->userdata);
+ userdata_len = nl_attr_get_size(userdata);
if (userdata_len < sizeof cookie.type
|| userdata_len > sizeof cookie) {
VLOG_WARN_RL(&rl, "action upcall cookie has unexpected size %"PRIuSIZE,
return BAD_UPCALL;
}
memset(&cookie, 0, sizeof cookie);
- memcpy(&cookie, nl_attr_get(dpif_upcall->userdata), userdata_len);
+ memcpy(&cookie, nl_attr_get(userdata), userdata_len);
if (userdata_len == MAX(8, sizeof cookie.sflow)
&& cookie.type == USER_ACTION_COOKIE_SFLOW) {
return SFLOW_UPCALL;
* initialized with at least 128 bytes of space. */
static void
compose_slow_path(struct udpif *udpif, struct xlate_out *xout,
- struct flow *flow, odp_port_t odp_in_port,
+ const struct flow *flow, odp_port_t odp_in_port,
struct ofpbuf *buf)
{
union user_action_cookie cookie;
odp_put_userspace_action(pid, &cookie, sizeof cookie.slow_path, buf);
}
+static int
+upcall_receive(struct upcall *upcall, const struct dpif_backer *backer,
+ const struct ofpbuf *packet, enum dpif_upcall_type type,
+ const struct nlattr *userdata, const struct flow *flow)
+{
+ int error;
+
+ error = xlate_receive(backer, flow, &upcall->ofproto, &upcall->ipfix,
+ &upcall->sflow, &upcall->netflow,
+ &upcall->in_port);
+ if (error) {
+ return error;
+ }
+
+ upcall->flow = flow;
+ upcall->packet = packet;
+ upcall->type = type;
+ upcall->userdata = userdata;
+ ofpbuf_init(&upcall->put_actions, 0);
+
+ upcall->xout_initialized = false;
+ upcall->vsp_adjusted = false;
+
+ upcall->key = NULL;
+ upcall->key_len = 0;
+
+ return 0;
+}
+
static void
-upcall_init(struct upcall *upcall, struct flow *flow, struct ofpbuf *packet,
- struct ofproto_dpif *ofproto, struct dpif_upcall *dupcall,
- odp_port_t odp_in_port)
+upcall_xlate(struct udpif *udpif, struct upcall *upcall,
+ struct ofpbuf *odp_actions)
{
- struct pkt_metadata md = pkt_metadata_from_flow(flow);
+ struct dpif_flow_stats stats;
struct xlate_in xin;
- flow_extract(packet, &md, &upcall->flow);
+ stats.n_packets = 1;
+ stats.n_bytes = ofpbuf_size(upcall->packet);
+ stats.used = time_msec();
+ stats.tcp_flags = ntohs(upcall->flow->tcp_flags);
- upcall->ofproto = ofproto;
- upcall->key = dupcall->key;
- upcall->key_len = dupcall->key_len;
- upcall->upcall_type = dupcall->type;
- upcall->stats.n_packets = 1;
- upcall->stats.n_bytes = ofpbuf_size(packet);
- upcall->stats.used = time_msec();
- upcall->stats.tcp_flags = ntohs(upcall->flow.tcp_flags);
- upcall->odp_in_port = odp_in_port;
+ xlate_in_init(&xin, upcall->ofproto, upcall->flow, upcall->in_port, NULL,
+ stats.tcp_flags, upcall->packet);
+ xin.odp_actions = odp_actions;
- xlate_in_init(&xin, upcall->ofproto, &upcall->flow, NULL,
- upcall->stats.tcp_flags, packet);
-
- if (upcall->upcall_type == DPIF_UC_MISS) {
- xin.resubmit_stats = &upcall->stats;
+ if (upcall->type == DPIF_UC_MISS) {
+ xin.resubmit_stats = &stats;
} else {
/* For non-miss upcalls, there's a flow in the datapath which this
* packet was accounted to. Presumably the revalidators will deal
}
xlate_actions(&xin, &upcall->xout);
+ upcall->xout_initialized = true;
+
+ /* Special case for fail-open mode.
+ *
+ * If we are in fail-open mode, but we are connected to a controller too,
+ * then we should send the packet up to the controller in the hope that it
+ * will try to set up a flow and thereby allow us to exit fail-open.
+ *
+ * See the top-level comment in fail-open.c for more information.
+ *
+ * Copy packets before they are modified by execution. */
+ if (upcall->xout.fail_open) {
+ const struct ofpbuf *packet = upcall->packet;
+ struct ofproto_packet_in *pin;
+
+ pin = xmalloc(sizeof *pin);
+ pin->up.packet = xmemdup(ofpbuf_data(packet), ofpbuf_size(packet));
+ pin->up.packet_len = ofpbuf_size(packet);
+ pin->up.reason = OFPR_NO_MATCH;
+ pin->up.table_id = 0;
+ pin->up.cookie = OVS_BE64_MAX;
+ flow_get_metadata(upcall->flow, &pin->up.fmd);
+ pin->send_len = 0; /* Not used for flow table misses. */
+ pin->miss_type = OFPROTO_PACKET_IN_NO_MISS;
+ ofproto_dpif_send_packet_in(upcall->ofproto, pin);
+ }
+
+ if (!upcall->xout.slow) {
+ ofpbuf_use_const(&upcall->put_actions,
+ ofpbuf_data(upcall->xout.odp_actions),
+ ofpbuf_size(upcall->xout.odp_actions));
+ } else {
+ ofpbuf_init(&upcall->put_actions, 0);
+ compose_slow_path(udpif, &upcall->xout, upcall->flow,
+ upcall->flow->in_port.odp_port,
+ &upcall->put_actions);
+ }
}
static void
-free_upcall(struct upcall *upcall)
+upcall_uninit(struct upcall *upcall)
{
- xlate_out_uninit(&upcall->xout);
- ofpbuf_uninit(&upcall->dpif_upcall.packet);
- ofpbuf_uninit(&upcall->upcall_buf);
+ if (upcall) {
+ if (upcall->xout_initialized) {
+ xlate_out_uninit(&upcall->xout);
+ }
+ ofpbuf_uninit(&upcall->put_actions);
+ dpif_ipfix_unref(upcall->ipfix);
+ dpif_sflow_unref(upcall->sflow);
+ netflow_unref(upcall->netflow);
+ }
}
static struct udpif *
return NULL;
}
-void
+static void
exec_upcalls(struct dpif *dpif, struct dpif_upcall *dupcalls,
- struct ofpbuf *bufs, int cnt)
+ struct ofpbuf *bufs OVS_UNUSED, int cnt)
{
struct upcall upcalls[UPCALL_MAX_BATCH];
struct udpif *udpif;
for (j = i; j < MIN(i + UPCALL_MAX_BATCH, cnt); j++) {
struct upcall *upcall = &upcalls[n_upcalls];
struct dpif_upcall *dupcall = &dupcalls[j];
- struct ofpbuf *buf = &bufs[j];
-
- upcall->dpif_upcall = *dupcall;
- upcall->upcall_buf = *buf;
+ struct pkt_metadata md;
+ struct flow flow;
+ int error;
dpif_print_packet(dpif, dupcall);
- if (!convert_upcall(udpif, upcall)) {
- n_upcalls += 1;
+
+ if (odp_flow_key_to_flow(dupcall->key, dupcall->key_len, &flow)
+ == ODP_FIT_ERROR) {
+ continue;
}
- }
- if (n_upcalls) {
- handle_upcalls(udpif, upcalls, n_upcalls);
- for (j = 0; j < n_upcalls; j++) {
- free_upcall(&upcalls[j]);
+ error = upcall_receive(upcall, udpif->backer, &dupcall->packet,
+ dupcall->type, dupcall->userdata, &flow);
+ if (error) {
+ goto cleanup;
}
- }
- }
-}
-/* Reads and classifies upcalls. Returns the number of upcalls successfully
- * read. */
-static size_t
-read_upcalls(struct handler *handler,
- struct upcall upcalls[UPCALL_MAX_BATCH])
-{
- struct udpif *udpif = handler->udpif;
- size_t i;
- size_t n_upcalls = 0;
+ upcall->key = dupcall->key;
+ upcall->key_len = dupcall->key_len;
- /* Try reading UPCALL_MAX_BATCH upcalls from dpif. */
- for (i = 0; i < UPCALL_MAX_BATCH; i++) {
- struct upcall *upcall = &upcalls[n_upcalls];
- int error;
+ md = pkt_metadata_from_flow(&flow);
+ flow_extract(&dupcall->packet, &md, &flow);
- ofpbuf_use_stub(&upcall->upcall_buf, upcall->upcall_stub,
- sizeof upcall->upcall_stub);
- error = dpif_recv(udpif->dpif, handler->handler_id,
- &upcall->dpif_upcall, &upcall->upcall_buf);
- if (error) {
- ofpbuf_uninit(&upcall->upcall_buf);
- break;
+ error = process_upcall(udpif, upcall, NULL);
+ if (error) {
+ goto cleanup;
+ }
+
+ n_upcalls++;
+ continue;
+
+cleanup:
+ upcall_uninit(upcall);
}
- if (!convert_upcall(udpif, upcall)) {
- n_upcalls += 1;
+ if (n_upcalls) {
+ handle_upcalls(udpif, upcalls, n_upcalls);
+ for (j = 0; j < n_upcalls; j++) {
+ upcall_uninit(&upcalls[j]);
+ }
}
}
- return n_upcalls;
}
static int
-convert_upcall(struct udpif *udpif, struct upcall *upcall)
+process_upcall(struct udpif *udpif, struct upcall *upcall,
+ struct ofpbuf *odp_actions)
{
- struct dpif_upcall *dupcall = &upcall->dpif_upcall;
- struct ofpbuf *packet = &dupcall->packet;
- struct ofproto_dpif *ofproto;
- struct dpif_sflow *sflow;
- struct dpif_ipfix *ipfix;
- struct flow flow;
- enum upcall_type type;
- odp_port_t odp_in_port;
- int error;
-
- error = xlate_receive(udpif->backer, packet, dupcall->key,
- dupcall->key_len, &flow,
- &ofproto, &ipfix, &sflow, NULL, &odp_in_port);
-
- if (error) {
- if (error == ENODEV) {
- /* Received packet on datapath port for which we couldn't
- * associate an ofproto. This can happen if a port is removed
- * while traffic is being received. Print a rate-limited
- * message in case it happens frequently. Install a drop flow
- * so that future packets of the flow are inexpensively dropped
- * in the kernel. */
- VLOG_INFO_RL(&rl, "received packet on unassociated datapath "
- "port %"PRIu32, odp_in_port);
- dpif_flow_put(udpif->dpif, DPIF_FP_CREATE,
- dupcall->key, dupcall->key_len, NULL, 0, NULL, 0,
- NULL);
- }
- goto destroy_upcall;
- }
+ const struct nlattr *userdata = upcall->userdata;
+ const struct ofpbuf *packet = upcall->packet;
+ const struct flow *flow = upcall->flow;
- type = classify_upcall(upcall);
- if (type == MISS_UPCALL) {
- upcall_init(upcall, &flow, packet, ofproto, dupcall, odp_in_port);
- return error;
- }
+ switch (classify_upcall(upcall->type, userdata)) {
+ case MISS_UPCALL:
+ upcall_xlate(udpif, upcall, odp_actions);
+ return 0;
- switch (type) {
case SFLOW_UPCALL:
- if (sflow) {
+ if (upcall->sflow) {
union user_action_cookie cookie;
memset(&cookie, 0, sizeof cookie);
- memcpy(&cookie, nl_attr_get(dupcall->userdata),
- sizeof cookie.sflow);
- dpif_sflow_received(sflow, packet, &flow, odp_in_port,
- &cookie);
+ memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.sflow);
+ dpif_sflow_received(upcall->sflow, packet, flow,
+ flow->in_port.odp_port, &cookie);
}
break;
+
case IPFIX_UPCALL:
- if (ipfix) {
- dpif_ipfix_bridge_sample(ipfix, packet, &flow);
+ if (upcall->ipfix) {
+ dpif_ipfix_bridge_sample(upcall->ipfix, packet, flow);
}
break;
+
case FLOW_SAMPLE_UPCALL:
- if (ipfix) {
+ if (upcall->ipfix) {
union user_action_cookie cookie;
memset(&cookie, 0, sizeof cookie);
- memcpy(&cookie, nl_attr_get(dupcall->userdata),
- sizeof cookie.flow_sample);
+ memcpy(&cookie, nl_attr_get(userdata), sizeof cookie.flow_sample);
/* The flow reflects exactly the contents of the packet.
* Sample the packet using it. */
- dpif_ipfix_flow_sample(ipfix, packet, &flow,
+ dpif_ipfix_flow_sample(upcall->ipfix, packet, flow,
cookie.flow_sample.collector_set_id,
cookie.flow_sample.probability,
cookie.flow_sample.obs_domain_id,
cookie.flow_sample.obs_point_id);
}
break;
+
case BAD_UPCALL:
break;
- case MISS_UPCALL:
- OVS_NOT_REACHED();
}
- dpif_ipfix_unref(ipfix);
- dpif_sflow_unref(sflow);
- error = EAGAIN;
-
-destroy_upcall:
- ofpbuf_uninit(&upcall->dpif_upcall.packet);
- ofpbuf_uninit(&upcall->upcall_buf);
- return error;
+ return EAGAIN;
}
static void
handle_upcalls(struct udpif *udpif, struct upcall *upcalls,
size_t n_upcalls)
{
+ struct odputil_keybuf mask_bufs[UPCALL_MAX_BATCH];
struct dpif_op *opsp[UPCALL_MAX_BATCH * 2];
struct dpif_op ops[UPCALL_MAX_BATCH * 2];
- size_t n_ops, i;
unsigned int flow_limit;
- bool fail_open, may_put;
+ size_t n_ops, i;
+ bool may_put;
atomic_read(&udpif->flow_limit, &flow_limit);
may_put = udpif_get_n_flows(udpif) < flow_limit;
*
* The loop fills 'ops' with an array of operations to execute in the
* datapath. */
- fail_open = false;
n_ops = 0;
for (i = 0; i < n_upcalls; i++) {
struct upcall *upcall = &upcalls[i];
- struct ofpbuf *packet = &upcall->dpif_upcall.packet;
+ const struct ofpbuf *packet = upcall->packet;
struct dpif_op *op;
- fail_open = fail_open || upcall->xout.fail_open;
-
- if (upcall->flow.in_port.ofp_port
- != vsp_realdev_to_vlandev(upcall->ofproto,
- upcall->flow.in_port.ofp_port,
- upcall->flow.vlan_tci)) {
- /* This packet was received on a VLAN splinter port. We
- * added a VLAN to the packet to make the packet resemble
- * the flow, but the actions were composed assuming that
- * the packet contained no VLAN. So, we must remove the
- * VLAN header from the packet before trying to execute the
- * actions. */
- if (ofpbuf_size(&upcall->xout.odp_actions)) {
- eth_pop_vlan(packet);
+ if (upcall->vsp_adjusted) {
+ /* This packet was received on a VLAN splinter port. We added a
+ * VLAN to the packet to make the packet resemble the flow, but the
+ * actions were composed assuming that the packet contained no
+ * VLAN. So, we must remove the VLAN header from the packet before
+ * trying to execute the actions. */
+ if (ofpbuf_size(upcall->xout.odp_actions)) {
+ eth_pop_vlan(CONST_CAST(struct ofpbuf *, upcall->packet));
}
/* Remove the flow vlan tags inserted by vlan splinter logic
* to ensure megaflow masks generated match the data path flow. */
- upcall->flow.vlan_tci = 0;
+ CONST_CAST(struct flow *, upcall->flow)->vlan_tci = 0;
}
/* Do not install a flow into the datapath if:
*
* - We received this packet via some flow installed in the kernel
* already. */
- if (may_put
- && upcall->dpif_upcall.type == DPIF_UC_MISS) {
+ if (may_put && upcall->type == DPIF_UC_MISS) {
struct ofpbuf mask;
bool megaflow;
atomic_read(&enable_megaflows, &megaflow);
- ofpbuf_use_stack(&mask, &upcall->mask_buf, sizeof upcall->mask_buf);
+ ofpbuf_use_stack(&mask, &mask_bufs[i], sizeof mask_bufs[i]);
if (megaflow) {
size_t max_mpls;
bool recirc;
recirc = ofproto_dpif_get_enable_recirc(upcall->ofproto);
max_mpls = ofproto_dpif_get_max_mpls_depth(upcall->ofproto);
odp_flow_key_from_mask(&mask, &upcall->xout.wc.masks,
- &upcall->flow, UINT32_MAX, max_mpls,
+ upcall->flow, UINT32_MAX, max_mpls,
recirc);
}
op->u.flow_put.mask = ofpbuf_data(&mask);
op->u.flow_put.mask_len = ofpbuf_size(&mask);
op->u.flow_put.stats = NULL;
-
- if (!upcall->xout.slow) {
- op->u.flow_put.actions = ofpbuf_data(&upcall->xout.odp_actions);
- op->u.flow_put.actions_len = ofpbuf_size(&upcall->xout.odp_actions);
- } else {
- struct ofpbuf buf;
-
- ofpbuf_use_stack(&buf, upcall->slow_path_buf,
- sizeof upcall->slow_path_buf);
- compose_slow_path(udpif, &upcall->xout, &upcall->flow,
- upcall->odp_in_port, &buf);
- op->u.flow_put.actions = ofpbuf_data(&buf);
- op->u.flow_put.actions_len = ofpbuf_size(&buf);
- }
+ op->u.flow_put.actions = ofpbuf_data(&upcall->put_actions);
+ op->u.flow_put.actions_len = ofpbuf_size(&upcall->put_actions);
}
- if (ofpbuf_size(&upcall->xout.odp_actions)) {
-
+ if (ofpbuf_size(upcall->xout.odp_actions)) {
op = &ops[n_ops++];
op->type = DPIF_OP_EXECUTE;
- op->u.execute.packet = packet;
+ op->u.execute.packet = CONST_CAST(struct ofpbuf *, packet);
odp_key_to_pkt_metadata(upcall->key, upcall->key_len,
&op->u.execute.md);
- op->u.execute.actions = ofpbuf_data(&upcall->xout.odp_actions);
- op->u.execute.actions_len = ofpbuf_size(&upcall->xout.odp_actions);
+ op->u.execute.actions = ofpbuf_data(upcall->xout.odp_actions);
+ op->u.execute.actions_len = ofpbuf_size(upcall->xout.odp_actions);
op->u.execute.needs_help = (upcall->xout.slow & SLOW_ACTION) != 0;
}
}
- /* Special case for fail-open mode.
- *
- * If we are in fail-open mode, but we are connected to a controller too,
- * then we should send the packet up to the controller in the hope that it
- * will try to set up a flow and thereby allow us to exit fail-open.
- *
- * See the top-level comment in fail-open.c for more information.
- *
- * Copy packets before they are modified by execution. */
- if (fail_open) {
- for (i = 0; i < n_upcalls; i++) {
- struct upcall *upcall = &upcalls[i];
- struct ofpbuf *packet = &upcall->dpif_upcall.packet;
- struct ofproto_packet_in *pin;
-
- pin = xmalloc(sizeof *pin);
- pin->up.packet = xmemdup(ofpbuf_data(packet), ofpbuf_size(packet));
- pin->up.packet_len = ofpbuf_size(packet);
- pin->up.reason = OFPR_NO_MATCH;
- pin->up.table_id = 0;
- pin->up.cookie = OVS_BE64_MAX;
- flow_get_metadata(&upcall->flow, &pin->up.fmd);
- pin->send_len = 0; /* Not used for flow table misses. */
- pin->miss_type = OFPROTO_PACKET_IN_NO_MISS;
- ofproto_dpif_send_packet_in(upcall->ofproto, pin);
- }
- }
-
/* Execute batch. */
for (i = 0; i < n_ops; i++) {
opsp[i] = &ops[i];
struct ofpbuf xout_actions;
struct flow flow, dp_mask;
uint32_t *dp32, *xout32;
- odp_port_t odp_in_port;
+ ofp_port_t ofp_in_port;
struct xlate_in xin;
long long int last_used;
int error;
goto exit;
}
- error = xlate_receive(udpif->backer, NULL, ukey->key, ukey->key_len, &flow,
- &ofproto, NULL, NULL, &netflow, &odp_in_port);
+ if (odp_flow_key_to_flow(ukey->key, ukey->key_len, &flow)
+ == ODP_FIT_ERROR) {
+ goto exit;
+ }
+
+ error = xlate_receive(udpif->backer, &flow, &ofproto, NULL, NULL, &netflow,
+ &ofp_in_port);
if (error) {
goto exit;
}
ukey->xcache = xlate_cache_new();
}
- xlate_in_init(&xin, ofproto, &flow, NULL, push.tcp_flags, NULL);
+ xlate_in_init(&xin, ofproto, &flow, ofp_in_port, NULL, push.tcp_flags,
+ NULL);
xin.resubmit_stats = push.n_packets ? &push : NULL;
xin.xcache = ukey->xcache;
xin.may_learn = may_learn;
}
if (!xout.slow) {
- ofpbuf_use_const(&xout_actions, ofpbuf_data(&xout.odp_actions),
- ofpbuf_size(&xout.odp_actions));
+ ofpbuf_use_const(&xout_actions, ofpbuf_data(xout.odp_actions),
+ ofpbuf_size(xout.odp_actions));
} else {
ofpbuf_use_stack(&xout_actions, slow_path_buf, sizeof slow_path_buf);
- compose_slow_path(udpif, &xout, &flow, odp_in_port, &xout_actions);
+ compose_slow_path(udpif, &xout, &flow, flow.in_port.odp_port,
+ &xout_actions);
}
if (f->actions_len != ofpbuf_size(&xout_actions)
if (push->n_packets || netflow_exists()) {
struct ofproto_dpif *ofproto;
struct netflow *netflow;
+ ofp_port_t ofp_in_port;
struct flow flow;
bool may_learn;
int error;
}
ovs_mutex_unlock(&op->ukey->mutex);
- error = xlate_receive(udpif->backer, NULL, op->op.u.flow_del.key,
- op->op.u.flow_del.key_len, &flow, &ofproto,
- NULL, NULL, &netflow, NULL);
+ if (odp_flow_key_to_flow(op->op.u.flow_del.key,
+ op->op.u.flow_del.key_len, &flow)
+ == ODP_FIT_ERROR) {
+ continue;
+ }
+
+ error = xlate_receive(udpif->backer, &flow, &ofproto,
+ NULL, NULL, &netflow, &ofp_in_port);
if (!error) {
struct xlate_in xin;
- xlate_in_init(&xin, ofproto, &flow, NULL, push->tcp_flags,
- NULL);
+ xlate_in_init(&xin, ofproto, &flow, ofp_in_port, NULL,
+ push->tcp_flags, NULL);
xin.resubmit_stats = push->n_packets ? push : NULL;
xin.may_learn = may_learn;
xin.skip_wildcards = true;
xlate_xport_remove(new_xcfg, xport);
}
-/* Given a datpath, packet, and flow metadata ('backer', 'packet', and 'key'
- * respectively), populates 'flow' with the result of odp_flow_key_to_flow().
- * Optionally populates 'ofproto' with the ofproto_dpif, 'odp_in_port' with
- * the datapath in_port, that 'packet' ingressed, and 'ipfix', 'sflow', and
- * 'netflow' with the appropriate handles for those protocols if they're
- * enabled. Caller is responsible for unrefing them.
+/* Given a datapath and flow metadata ('backer', and 'flow' respectively),
+ * Optionally populates 'ofproto' with the ofproto_dpif, 'ofp_in_port' with the
+ * openflow in_port, and 'ipfix', 'sflow', and 'netflow' with the appropriate
+ * handles for those protocols if they're enabled. Caller is responsible for
+ * unrefing them.
*
* If 'ofproto' is nonnull, requires 'flow''s in_port to exist. Otherwise sets
* 'flow''s in_port to OFPP_NONE.
*
- * This function does post-processing on data returned from
- * odp_flow_key_to_flow() to help make VLAN splinters transparent to the rest
- * of the upcall processing logic. In particular, if the extracted in_port is
- * a VLAN splinter port, it replaces flow->in_port by the "real" port, sets
- * flow->vlan_tci correctly for the VLAN of the VLAN splinter port, and pushes
- * a VLAN header onto 'packet' (if it is nonnull).
- *
* Similarly, this function also includes some logic to help with tunnels. It
* may modify 'flow' as necessary to make the tunneling implementation
* transparent to the upcall processing logic.
* Returns 0 if successful, ENODEV if the parsed flow has no associated ofport,
* or some other positive errno if there are other problems. */
int
-xlate_receive(const struct dpif_backer *backer, struct ofpbuf *packet,
- const struct nlattr *key, size_t key_len, struct flow *flow,
+xlate_receive(const struct dpif_backer *backer, const struct flow *flow,
struct ofproto_dpif **ofproto, struct dpif_ipfix **ipfix,
struct dpif_sflow **sflow, struct netflow **netflow,
- odp_port_t *odp_in_port)
+ ofp_port_t *ofp_in_port)
{
struct xlate_cfg *xcfg = ovsrcu_get(struct xlate_cfg *, &xcfgp);
- int error = ENODEV;
const struct xport *xport;
- if (odp_flow_key_to_flow(key, key_len, flow) == ODP_FIT_ERROR) {
- error = EINVAL;
- return error;
- }
-
- if (odp_in_port) {
- *odp_in_port = flow->in_port.odp_port;
- }
-
xport = xport_lookup(xcfg, tnl_port_should_receive(flow)
? tnl_port_receive(flow)
: odp_port_to_ofport(backer, flow->in_port.odp_port));
- flow->in_port.ofp_port = xport ? xport->ofp_port : OFPP_NONE;
- if (!xport) {
- return error;
+ if (ofp_in_port) {
+ *ofp_in_port = xport ? xport->ofp_port : OFPP_NONE;
}
- if (vsp_adjust_flow(xport->xbridge->ofproto, flow)) {
- if (packet) {
- /* Make the packet resemble the flow, so that it gets sent to
- * an OpenFlow controller properly, so that it looks correct
- * for sFlow, and so that flow_extract() will get the correct
- * vlan_tci if it is called on 'packet'. */
- eth_push_vlan(packet, htons(ETH_TYPE_VLAN), flow->vlan_tci);
- }
+ if (!xport) {
+ return ENODEV;
}
- error = 0;
if (ofproto) {
*ofproto = xport->xbridge->ofproto;
if (netflow) {
*netflow = netflow_ref(xport->xbridge->netflow);
}
- return error;
+ return 0;
}
static struct xbridge *
"%s, which is reserved exclusively for mirroring",
ctx->xbridge->name, in_xbundle->name);
}
- ofpbuf_clear(&ctx->xout->odp_actions);
+ ofpbuf_clear(ctx->xout->odp_actions);
return;
}
add_sflow_action(struct xlate_ctx *ctx)
{
ctx->user_cookie_offset = compose_sflow_action(ctx->xbridge,
- &ctx->xout->odp_actions,
+ ctx->xout->odp_actions,
&ctx->xin->flow, ODPP_NONE);
ctx->sflow_odp_port = 0;
ctx->sflow_n_outputs = 0;
static void
add_ipfix_action(struct xlate_ctx *ctx)
{
- compose_ipfix_action(ctx->xbridge, &ctx->xout->odp_actions,
+ compose_ipfix_action(ctx->xbridge, ctx->xout->odp_actions,
&ctx->xin->flow);
}
return;
}
- cookie = ofpbuf_at(&ctx->xout->odp_actions, ctx->user_cookie_offset,
+ cookie = ofpbuf_at(ctx->xout->odp_actions, ctx->user_cookie_offset,
sizeof cookie->sflow);
ovs_assert(cookie->type == USER_ACTION_COOKIE_SFLOW);
/* Forwarding is disabled by STP. Let OFPP_NORMAL and the
* learning action look at the packet, then drop it. */
struct flow old_base_flow = ctx->base_flow;
- size_t old_size = ofpbuf_size(&ctx->xout->odp_actions);
+ size_t old_size = ofpbuf_size(ctx->xout->odp_actions);
mirror_mask_t old_mirrors = ctx->xout->mirrors;
xlate_table_action(ctx, flow->in_port.ofp_port, 0, true, true);
ctx->xout->mirrors = old_mirrors;
ctx->base_flow = old_base_flow;
- ofpbuf_set_size(&ctx->xout->odp_actions, old_size);
+ ofpbuf_set_size(ctx->xout->odp_actions, old_size);
}
}
}
out_port = odp_port;
commit_odp_tunnel_action(flow, &ctx->base_flow,
- &ctx->xout->odp_actions);
+ ctx->xout->odp_actions);
flow->tunnel = flow_tnl; /* Restore tunnel metadata */
} else {
odp_port = xport->odp_port;
if (out_port != ODPP_NONE) {
ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
- &ctx->xout->odp_actions,
+ ctx->xout->odp_actions,
&ctx->xout->wc);
if (ctx->use_recirc) {
struct xlate_recirc *xr = &ctx->recirc;
/* Hash action. */
- act_hash = nl_msg_put_unspec_uninit(&ctx->xout->odp_actions,
+ act_hash = nl_msg_put_unspec_uninit(ctx->xout->odp_actions,
OVS_ACTION_ATTR_HASH,
sizeof *act_hash);
act_hash->hash_alg = xr->hash_alg;
act_hash->hash_basis = xr->hash_basis;
/* Recirc action. */
- nl_msg_put_u32(&ctx->xout->odp_actions, OVS_ACTION_ATTR_RECIRC,
+ nl_msg_put_u32(ctx->xout->odp_actions, OVS_ACTION_ATTR_RECIRC,
xr->recirc_id);
} else {
- nl_msg_put_odp_port(&ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
+ nl_msg_put_odp_port(ctx->xout->odp_actions, OVS_ACTION_ATTR_OUTPUT,
out_port);
}
MAX_RESUBMIT_RECURSION);
} else if (ctx->resubmits >= MAX_RESUBMITS + MAX_INTERNAL_RESUBMITS) {
VLOG_ERR_RL(&rl, "over %d resubmit actions", MAX_RESUBMITS);
- } else if (ofpbuf_size(&ctx->xout->odp_actions) > UINT16_MAX) {
+ } else if (ofpbuf_size(ctx->xout->odp_actions) > UINT16_MAX) {
VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of actions");
} else if (ofpbuf_size(&ctx->stack) >= 65536) {
VLOG_ERR_RL(&rl, "resubmits yielded over 64 kB of stack");
packet = dpif_packet_clone_from_ofpbuf(ctx->xin->packet);
ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
- &ctx->xout->odp_actions,
+ ctx->xout->odp_actions,
&ctx->xout->wc);
odp_execute_actions(NULL, &packet, 1, false, &md,
- ofpbuf_data(&ctx->xout->odp_actions),
- ofpbuf_size(&ctx->xout->odp_actions), NULL);
+ ofpbuf_data(ctx->xout->odp_actions),
+ ofpbuf_size(ctx->xout->odp_actions), NULL);
pin = xmalloc(sizeof *pin);
pin->up.packet_len = ofpbuf_size(&packet->ofpbuf);
}
ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
- &ctx->xout->odp_actions,
+ ctx->xout->odp_actions,
&ctx->xout->wc);
- nl_msg_put_u32(&ctx->xout->odp_actions, OVS_ACTION_ATTR_RECIRC, id);
+ nl_msg_put_u32(ctx->xout->odp_actions, OVS_ACTION_ATTR_RECIRC, id);
}
static void
n = flow_count_mpls_labels(flow, wc);
if (!n) {
ctx->xout->slow |= commit_odp_actions(flow, &ctx->base_flow,
- &ctx->xout->odp_actions,
+ ctx->xout->odp_actions,
&ctx->xout->wc);
} else if (n >= FLOW_MAX_MPLS_LABELS) {
if (ctx->xin->packet != NULL) {
ctx->xbridge->name, FLOW_MAX_MPLS_LABELS);
}
ctx->exit = true;
- ofpbuf_clear(&ctx->xout->odp_actions);
+ ofpbuf_clear(ctx->xout->odp_actions);
}
}
}
ctx->xout->slow |= commit_odp_actions(&ctx->xin->flow, &ctx->base_flow,
- &ctx->xout->odp_actions,
+ ctx->xout->odp_actions,
&ctx->xout->wc);
compose_flow_sample_cookie(os->probability, os->collector_set_id,
os->obs_domain_id, os->obs_point_id, &cookie);
- compose_sample_action(ctx->xbridge, &ctx->xout->odp_actions, &ctx->xin->flow,
+ compose_sample_action(ctx->xbridge, ctx->xout->odp_actions, &ctx->xin->flow,
probability, &cookie, sizeof cookie.flow_sample);
}
void
xlate_in_init(struct xlate_in *xin, struct ofproto_dpif *ofproto,
- const struct flow *flow, struct rule_dpif *rule,
- uint16_t tcp_flags, const struct ofpbuf *packet)
+ const struct flow *flow, ofp_port_t in_port,
+ struct rule_dpif *rule, uint16_t tcp_flags,
+ const struct ofpbuf *packet)
{
xin->ofproto = ofproto;
xin->flow = *flow;
+ xin->flow.in_port.ofp_port = in_port;
xin->packet = packet;
xin->may_learn = packet != NULL;
xin->rule = rule;
xin->report_hook = NULL;
xin->resubmit_stats = NULL;
xin->skip_wildcards = false;
+ xin->odp_actions = NULL;
}
void
xlate_out_uninit(struct xlate_out *xout)
{
- if (xout) {
- ofpbuf_uninit(&xout->odp_actions);
+ if (xout && xout->odp_actions == &xout->odp_actions_buf) {
+ ofpbuf_uninit(xout->odp_actions);
}
}
dst->nf_output_iface = src->nf_output_iface;
dst->mirrors = src->mirrors;
- ofpbuf_use_stub(&dst->odp_actions, dst->odp_actions_stub,
+ dst->odp_actions = &dst->odp_actions_buf;
+ ofpbuf_use_stub(dst->odp_actions, dst->odp_actions_stub,
sizeof dst->odp_actions_stub);
- ofpbuf_put(&dst->odp_actions, ofpbuf_data(&src->odp_actions),
- ofpbuf_size(&src->odp_actions));
+ ofpbuf_put(dst->odp_actions, ofpbuf_data(src->odp_actions),
+ ofpbuf_size(src->odp_actions));
}
\f
static struct skb_priority_to_dscp *
const struct nlattr *a;
unsigned int left;
- NL_ATTR_FOR_EACH_UNSAFE (a, left, ofpbuf_data(&ctx->xout->odp_actions),
- ofpbuf_size(&ctx->xout->odp_actions)) {
+ NL_ATTR_FOR_EACH_UNSAFE (a, left, ofpbuf_data(ctx->xout->odp_actions),
+ ofpbuf_size(ctx->xout->odp_actions)) {
if (nl_attr_type(a) == OVS_ACTION_ATTR_OUTPUT
&& nl_attr_get_odp_port(a) == local_odp_port) {
return true;
ctx.xout->has_fin_timeout = false;
ctx.xout->nf_output_iface = NF_OUT_DROP;
ctx.xout->mirrors = 0;
- ofpbuf_use_stub(&ctx.xout->odp_actions, ctx.xout->odp_actions_stub,
- sizeof ctx.xout->odp_actions_stub);
- ofpbuf_reserve(&ctx.xout->odp_actions, NL_A_U32_SIZE);
+
+ xout->odp_actions = xin->odp_actions;
+ if (!xout->odp_actions) {
+ xout->odp_actions = &xout->odp_actions_buf;
+ ofpbuf_use_stub(xout->odp_actions, xout->odp_actions_stub,
+ sizeof xout->odp_actions_stub);
+ }
+ ofpbuf_reserve(xout->odp_actions, NL_A_U32_SIZE);
ctx.xbridge = xbridge_lookup(xcfg, xin->ofproto);
if (!ctx.xbridge) {
add_sflow_action(&ctx);
add_ipfix_action(&ctx);
- sample_actions_len = ofpbuf_size(&ctx.xout->odp_actions);
+ sample_actions_len = ofpbuf_size(ctx.xout->odp_actions);
if (tnl_may_send && (!in_port || may_receive(in_port, &ctx))) {
do_xlate_actions(ofpacts, ofpacts_len, &ctx);
/* We've let OFPP_NORMAL and the learning action look at the
* packet, so drop it now if forwarding is disabled. */
if (in_port && !xport_stp_forward_state(in_port)) {
- ofpbuf_set_size(&ctx.xout->odp_actions, sample_actions_len);
+ ofpbuf_set_size(ctx.xout->odp_actions, sample_actions_len);
}
}
}
}
- if (nl_attr_oversized(ofpbuf_size(&ctx.xout->odp_actions))) {
+ if (nl_attr_oversized(ofpbuf_size(ctx.xout->odp_actions))) {
/* These datapath actions are too big for a Netlink attribute, so we
* can't hand them to the kernel directly. dpif_execute() can execute
* them one by one with help, so just mark the result as SLOW_ACTION to