[ovs-dev] [PATCH v3 3/3] dpif-netdev: batch packet processing
Daniele Di Proietto
ddiproietto at vmware.com
Sat Jun 7 00:13:15 UTC 2014
This change in dpif-netdev allows faster packet processing for devices which
implement batching (netdev-dpdk currently).
Signed-off-by: Daniele Di Proietto <ddiproietto at vmware.com>
---
lib/dpif-netdev.c | 350 +++++++++++++++++++++++++++++++------------
lib/dpif.c | 12 +-
lib/odp-execute.c | 83 +++++++---
lib/odp-execute.h | 6 +-
lib/ofpbuf.h | 4 +-
ofproto/ofproto-dpif-xlate.c | 2 +-
6 files changed, 332 insertions(+), 125 deletions(-)
diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index 70daaf2..c780fb6 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -331,19 +331,19 @@ static void dp_netdev_destroy_all_queues(struct dp_netdev *dp)
OVS_REQ_WRLOCK(dp->queue_rwlock);
static int dpif_netdev_open(const struct dpif_class *, const char *name,
bool create, struct dpif **);
-static int dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *,
- int queue_no, int type,
+static int dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf **,
+ int cnt, int queue_no, int type,
const struct miniflow *,
const struct nlattr *userdata);
static void dp_netdev_execute_actions(struct dp_netdev *dp,
const struct miniflow *,
- struct dpif_packet *, bool may_steal,
- struct pkt_metadata *,
+ struct dpif_packet **, int c,
+ bool may_steal, struct pkt_metadata *,
const struct nlattr *actions,
size_t actions_len);
static void dp_netdev_port_input(struct dp_netdev *dp,
- struct dpif_packet *packet,
- struct pkt_metadata *);
+ struct dpif_packet **packets, int cnt,
+ odp_port_t port_no);
static void dp_netdev_set_pmd_threads(struct dp_netdev *, int n);
@@ -1533,11 +1533,11 @@ dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute)
/* Extract flow key. */
miniflow_initialize(&key.flow, key.buf);
- miniflow_extract(execute->packet, md, &key.flow);
+ miniflow_extract(execute->packet, &execute->md, &key.flow);
packet = dpif_packet_clone_from_ofpbuf(execute->packet);
- dp_netdev_execute_actions(dp, &key.flow, packet, true, md,
+ dp_netdev_execute_actions(dp, &key.flow, &packet, 1, true, md,
execute->actions, execute->actions_len);
return 0;
@@ -1751,17 +1751,12 @@ dp_netdev_process_rxq_port(struct dp_netdev *dp,
struct dp_netdev_port *port,
struct netdev_rxq *rxq)
{
- struct dpif_packet *packet[NETDEV_MAX_RX_BATCH];
- int error, c;
+ struct dpif_packet *packets[NETDEV_MAX_RX_BATCH];
+ int error, cnt;
- error = netdev_rxq_recv(rxq, packet, &c);
+ error = netdev_rxq_recv(rxq, packets, &cnt);
if (!error) {
- struct pkt_metadata md = PKT_METADATA_INITIALIZER(port->port_no);
- int i;
-
- for (i = 0; i < c; i++) {
- dp_netdev_port_input(dp, packet[i], &md);
- }
+ dp_netdev_port_input(dp, packets, cnt, port->port_no);
} else if (error != EAGAIN && error != EOPNOTSUPP) {
static struct vlog_rate_limit rl
= VLOG_RATE_LIMIT_INIT(1, 5);
@@ -1958,10 +1953,9 @@ dp_netdev_flow_stats_new_cb(void)
static void
dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow,
- const struct ofpbuf *packet,
- const struct miniflow *key)
+ int cnt, int size,
+ uint16_t tcp_flags)
{
- uint16_t tcp_flags = miniflow_get_tcp_flags(key);
long long int now = time_msec();
struct dp_netdev_flow_stats *bucket;
@@ -1970,8 +1964,8 @@ dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow,
ovs_mutex_lock(&bucket->mutex);
bucket->used = MAX(now, bucket->used);
- bucket->packet_count++;
- bucket->byte_count += ofpbuf_size(packet);
+ bucket->packet_count += cnt;
+ bucket->byte_count += size;
bucket->tcp_flags |= tcp_flags;
ovs_mutex_unlock(&bucket->mutex);
}
@@ -1985,73 +1979,160 @@ dp_netdev_stats_new_cb(void)
}
static void
-dp_netdev_count_packet(struct dp_netdev *dp, enum dp_stat_type type)
+dp_netdev_count_packet(struct dp_netdev *dp, enum dp_stat_type type, int cnt)
{
struct dp_netdev_stats *bucket;
bucket = ovsthread_stats_bucket_get(&dp->stats, dp_netdev_stats_new_cb);
ovs_mutex_lock(&bucket->mutex);
- bucket->n[type]++;
+ bucket->n[type] += cnt;
ovs_mutex_unlock(&bucket->mutex);
}
+struct batch_pkt_execute {
+ unsigned int packet_count;
+ unsigned int byte_count;
+ uint16_t tcp_flags;
+
+ struct dp_netdev_flow *flow;
+ const struct miniflow *mf;
+
+ struct dpif_packet *packets[NETDEV_MAX_RX_BATCH];
+ struct pkt_metadata md;
+};
+
+static inline void
+packet_batch_update(struct batch_pkt_execute *batch, struct dpif_packet *packet,
+ const struct miniflow *mf)
+{
+ batch->tcp_flags |= miniflow_get_tcp_flags(mf);
+ batch->packets[batch->packet_count++] = packet;
+ batch->byte_count += dpif_packet_size(packet);
+}
+
+static inline void
+packet_batch_init(struct batch_pkt_execute *batch, struct dp_netdev_flow *flow,
+ struct dpif_packet *packet, struct pkt_metadata *md,
+ const struct miniflow *mf)
+{
+ batch->flow = flow;
+ batch->md = *md;
+ batch->mf = mf;
+ batch->packets[0] = packet;
+
+ batch->packet_count = 0;
+ batch->byte_count = 0;
+ batch->tcp_flags = 0;
+
+ packet_batch_update(batch, packet, mf);
+}
+
+static inline void
+packet_batch_execute(struct batch_pkt_execute *batch, struct dp_netdev *dp)
+{
+ struct dp_netdev_actions *actions;
+ struct dp_netdev_flow *flow = batch->flow;
+
+ dp_netdev_flow_used(batch->flow, batch->packet_count, batch->byte_count,
+ batch->tcp_flags);
+
+ actions = dp_netdev_flow_get_actions(flow);
+
+ dp_netdev_execute_actions(dp, batch->mf, batch->packets,
+ batch->packet_count, true, &batch->md,
+ actions->actions, actions->size);
+
+ dp_netdev_count_packet(dp, DP_STAT_HIT, batch->packet_count);
+}
+
static void
-dp_netdev_input(struct dp_netdev *dp, struct dpif_packet *packet,
+dp_netdev_input(struct dp_netdev *dp, struct dpif_packet **packets, int cnt,
struct pkt_metadata *md)
{
- struct dp_netdev_flow *netdev_flow;
- struct ofpbuf * ofp = dpif_packet_to_ofpbuf(packet);
- struct {
+ struct batch_pkt_execute batch;
+
+ struct miniflowkey {
struct miniflow flow;
uint32_t buf[FLOW_U32S];
- } key;
+ } keys[2];
- if (dpif_packet_size(packet) < ETH_HEADER_LEN) {
- dpif_packet_delete(packet);
- return;
+ /* To handle a batch of packets we need to store two miniflow 'keys':
+ * - one to extract the miniflow for the new packet,
+ * - the other to remember the miniflow for the current batch
+ * (it may be needed for executing certain actions)
+ *
+ * To avoid a copy we use the 'mfk' pointer, which points to the memory
+ * that should be used to extract the miniflow for the new packet.
+ * Every time a new batch is initialized, the mfk pointer changes. */
+
+ struct miniflowkey *mfk = &keys[0];
+
+ int i;
+
+ batch.flow = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(keys); i++) {
+ miniflow_initialize(&keys[i].flow, keys[i].buf);
}
- miniflow_initialize(&key.flow, key.buf);
- miniflow_extract(ofp, md, &key.flow);
- netdev_flow = dp_netdev_lookup_flow(dp, &key.flow);
- if (netdev_flow) {
- struct dp_netdev_actions *actions;
+ for (i = 0; i < cnt; i++) {
+ struct dp_netdev_flow *netdev_flow;
+ struct ofpbuf *ofp = dpif_packet_to_ofpbuf(packets[i]);
+
+ if (dpif_packet_size(packets[i]) < ETH_HEADER_LEN) {
+ dpif_packet_delete(packets[i]);
+ continue;
+ }
+
+ miniflow_extract(ofp, md, &mfk->flow);
+
+ netdev_flow = dp_netdev_lookup_flow(dp, &mfk->flow);
- dp_netdev_flow_used(netdev_flow, ofp, &key.flow);
+ if (netdev_flow) {
+ if (!batch.flow) {
+ packet_batch_init(&batch, netdev_flow, packets[i], md,
+ &mfk->flow);
+ mfk = (batch.mf == &keys[0].flow) ? &keys[1] : &keys[0];
+ } else if (batch.flow == netdev_flow) {
+ packet_batch_update(&batch, packets[i], &mfk->flow);
+ } else {
+ packet_batch_execute(&batch, dp);
+ packet_batch_init(&batch, netdev_flow, packets[i], md,
+ &mfk->flow);
+ mfk = (batch.mf == &keys[0].flow) ? &keys[1] : &keys[0];
+ }
+ } else if (dp->handler_queues) {
+ dp_netdev_count_packet(dp, DP_STAT_MISS, 1);
+ dp_netdev_output_userspace(dp, &ofp, 1,
+ miniflow_hash_5tuple(&mfk->flow, 0)
+ % dp->n_handlers,
+ DPIF_UC_MISS, &mfk->flow, NULL);
+ }
+ }
- actions = dp_netdev_flow_get_actions(netdev_flow);
- dp_netdev_execute_actions(dp, &key.flow, packet, true, md,
- actions->actions, actions->size);
- dp_netdev_count_packet(dp, DP_STAT_HIT);
- } else if (dp->handler_queues) {
- dp_netdev_count_packet(dp, DP_STAT_MISS);
- dp_netdev_output_userspace(dp, ofp, miniflow_hash_5tuple(&key.flow, 0)
- % dp->n_handlers,
- DPIF_UC_MISS, &key.flow, NULL);
+ if (batch.flow) {
+ packet_batch_execute(&batch, dp);
}
}
static void
-dp_netdev_port_input(struct dp_netdev *dp, struct dpif_packet *packet,
- struct pkt_metadata *md)
+dp_netdev_port_input(struct dp_netdev *dp, struct dpif_packet **packets,
+ int cnt, odp_port_t port_no)
{
uint32_t *recirc_depth = recirc_depth_get();
+ struct pkt_metadata md = PKT_METADATA_INITIALIZER(port_no);
*recirc_depth = 0;
- dp_netdev_input(dp, packet, md);
+ dp_netdev_input(dp, packets, cnt, &md);
}
static int
-dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
- int queue_no, int type, const struct miniflow *key,
- const struct nlattr *userdata)
+dp_netdev_queue_userspace_packet(struct dp_netdev_queue *q,
+ struct ofpbuf *packet, int type,
+ const struct miniflow *key,
+ const struct nlattr *userdata)
+OVS_REQUIRES(q->mutex)
{
- struct dp_netdev_queue *q;
- int error;
-
- fat_rwlock_rdlock(&dp->queue_rwlock);
- q = &dp->handler_queues[queue_no];
- ovs_mutex_lock(&q->mutex);
if (q->head - q->tail < MAX_QUEUE_LEN) {
struct dp_netdev_upcall *u = &q->upcalls[q->head++ & QUEUE_MASK];
struct dpif_upcall *upcall = &u->upcall;
@@ -2077,18 +2158,42 @@ dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf *packet,
/* Put userdata. */
if (userdata) {
upcall->userdata = ofpbuf_put(buf, userdata,
- NLA_ALIGN(userdata->nla_len));
+ NLA_ALIGN(userdata->nla_len));
}
upcall->packet = *packet;
seq_change(q->seq);
- error = 0;
+ return 0;
} else {
- dp_netdev_count_packet(dp, DP_STAT_LOST);
ofpbuf_delete(packet);
- error = ENOBUFS;
+ return ENOBUFS;
+ }
+
+}
+
+static int
+dp_netdev_output_userspace(struct dp_netdev *dp, struct ofpbuf **packets,
+ int cnt, int queue_no, int type,
+ const struct miniflow *key,
+ const struct nlattr *userdata)
+{
+ struct dp_netdev_queue *q;
+ int error;
+ int i;
+
+ fat_rwlock_rdlock(&dp->queue_rwlock);
+ q = &dp->handler_queues[queue_no];
+ ovs_mutex_lock(&q->mutex);
+ for (i = 0; i < cnt; i++) {
+ struct ofpbuf *packet = packets[i];
+
+ error = dp_netdev_queue_userspace_packet(q, packet, type, key,
+ userdata);
+ if (error == ENOBUFS) {
+ dp_netdev_count_packet(dp, DP_STAT_LOST, 1);
+ }
}
ovs_mutex_unlock(&q->mutex);
fat_rwlock_unlock(&dp->queue_rwlock);
@@ -2102,7 +2207,7 @@ struct dp_netdev_execute_aux {
};
static void
-dp_execute_cb(void *aux_, struct dpif_packet *packet,
+dp_execute_cb(void *aux_, struct dpif_packet **packets, int cnt,
struct pkt_metadata *md,
const struct nlattr *a, bool may_steal)
OVS_NO_THREAD_SAFETY_ANALYSIS
@@ -2111,31 +2216,54 @@ dp_execute_cb(void *aux_, struct dpif_packet *packet,
int type = nl_attr_type(a);
struct dp_netdev_port *p;
uint32_t *depth = recirc_depth_get();
+ int i;
switch ((enum ovs_action_attr)type) {
case OVS_ACTION_ATTR_OUTPUT:
p = dp_netdev_lookup_port(aux->dp, u32_to_odp(nl_attr_get_u32(a)));
if (p) {
- struct ofpbuf * ofp = dpif_packet_to_ofpbuf(packet);
+ /* This must be true to safely convert from dpif_packet to
+ * ofpbuf */
+ BUILD_ASSERT(offsetof (struct dpif_packet, packet) == 0);
- netdev_send(p->netdev, &ofp, 1, may_steal);
+ netdev_send(p->netdev, (struct ofpbuf **)packets, cnt, may_steal);
}
break;
case OVS_ACTION_ATTR_USERSPACE: {
- struct ofpbuf *userspace_packet;
const struct nlattr *userdata;
userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA);
- userspace_packet = may_steal
- ? dpif_packet_to_ofpbuf(packet)
- : ofpbuf_clone(dpif_packet_to_ofpbuf(packet));
-
- dp_netdev_output_userspace(aux->dp, userspace_packet,
- miniflow_hash_5tuple(aux->key, 0)
- % aux->dp->n_handlers,
- DPIF_UC_ACTION, aux->key,
- userdata);
+
+ for (i = 0; i < cnt; i++) {
+ struct ofpbuf *packet, *userspace_packet;
+ struct miniflowkey {
+ struct miniflow flow;
+ uint32_t buf[FLOW_U32S];
+ } key;
+
+ const struct miniflow *mfk = aux->key;
+
+ packet = dpif_packet_to_ofpbuf(packets[i]);
+
+ if (i != 0) {
+ /* The miniflow that we are passed is the miniflow of the first
+ * packet. If this is not the first packet reextract the
+ * miniflow */
+
+ miniflow_initialize(&key.flow, key.buf);
+ miniflow_extract(packet, md, &key.flow);
+ mfk = &key.flow;
+ }
+
+ userspace_packet = may_steal ? packet : ofpbuf_clone(packet);
+
+ dp_netdev_output_userspace(aux->dp, &userspace_packet, 1,
+ miniflow_hash_5tuple(mfk, 0)
+ % aux->dp->n_handlers,
+ DPIF_UC_ACTION, mfk,
+ userdata);
+ }
break;
}
@@ -2144,33 +2272,67 @@ dp_execute_cb(void *aux_, struct dpif_packet *packet,
uint32_t hash;
hash_act = nl_attr_get(a);
- if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
- /* Hash need not be symmetric, nor does it need to include
- * L2 fields. */
- hash = miniflow_hash_5tuple(aux->key, hash_act->hash_basis);
+
+ for (i = 0; i < cnt; i++) {
+ struct miniflowkey {
+ struct miniflow flow;
+ uint32_t buf[FLOW_U32S];
+ } key;
+
+ const struct miniflow *mfk = aux->key;
+
+ if (i != 0) {
+ /* The miniflow that we are passed is the miniflow of the first
+ * packet. If this is not the first packet reextract the
+ * miniflow */
+
+ /* TODO: this is slow. Use RSS hash in the future */
+ miniflow_initialize(&key.flow, key.buf);
+ miniflow_extract(dpif_packet_to_ofpbuf(packets[i]),
+ md, &key.flow);
+ mfk = &key.flow;
+ }
+
+ if (hash_act->hash_alg == OVS_HASH_ALG_L4) {
+ /* Hash need not be symmetric, nor does it need to include
+ * L2 fields. */
+ hash = miniflow_hash_5tuple(mfk, hash_act->hash_basis);
+ } else {
+ VLOG_WARN("Unknown hash algorithm specified"
+ "for the hash action.");
+ hash = 2;
+ }
+
if (!hash) {
hash = 1; /* 0 is not valid */
}
- } else {
- VLOG_WARN("Unknown hash algorithm specified for the hash action.");
- hash = 2;
+ if (i == 0) {
+ md->dp_hash = hash;
+ }
+ packets[i]->dp_hash = hash;
}
-
- md->dp_hash = hash;
break;
}
case OVS_ACTION_ATTR_RECIRC:
if (*depth < MAX_RECIRC_DEPTH) {
- struct pkt_metadata recirc_md = *md;
- struct dpif_packet *recirc_packet;
-
- recirc_packet = may_steal ? packet : dpif_packet_clone(packet);
- recirc_md.recirc_id = nl_attr_get_u32(a);
(*depth)++;
- dp_netdev_input(aux->dp, recirc_packet, &recirc_md);
+ for (i = 0; i < cnt; i++) {
+ struct dpif_packet *recirc_pkt;
+ struct pkt_metadata recirc_md = *md;
+
+ recirc_pkt = (may_steal) ? packets[i]
+ : dpif_packet_clone(packets[i]);
+
+ recirc_md.recirc_id = nl_attr_get_u32(a);
+
+ /* Hash is private to each packet */
+ recirc_md.dp_hash = packets[i]->dp_hash;
+
+ dp_netdev_input(aux->dp, &recirc_pkt, 1, &recirc_md);
+ }
(*depth)--;
break;
@@ -2193,14 +2355,14 @@ dp_execute_cb(void *aux_, struct dpif_packet *packet,
static void
dp_netdev_execute_actions(struct dp_netdev *dp, const struct miniflow *key,
- struct dpif_packet *packet, bool may_steal,
+ struct dpif_packet **packets, int cnt, bool may_steal,
struct pkt_metadata *md,
const struct nlattr *actions, size_t actions_len)
{
struct dp_netdev_execute_aux aux = {dp, key};
- odp_execute_actions(&aux, packet, may_steal, md,
- actions, actions_len, dp_execute_cb);
+ odp_execute_actions(&aux, packets, cnt, may_steal, md, actions, actions_len,
+ dp_execute_cb);
}
const struct dpif_class dpif_netdev_class = {
diff --git a/lib/dpif.c b/lib/dpif.c
index 0c1bcc9..8866b9c 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1058,13 +1058,16 @@ struct dpif_execute_helper_aux {
/* This is called for actions that need the context of the datapath to be
* meaningful. */
static void
-dpif_execute_helper_cb(void *aux_, struct dpif_packet *packet,
+dpif_execute_helper_cb(void *aux_, struct dpif_packet **packets, int cnt,
struct pkt_metadata *md,
const struct nlattr *action, bool may_steal OVS_UNUSED)
{
struct dpif_execute_helper_aux *aux = aux_;
struct dpif_execute execute;
int type = nl_attr_type(action);
+ struct ofpbuf * packet = dpif_packet_to_ofpbuf(packets[0]);
+
+ ovs_assert(cnt == 1);
switch ((enum ovs_action_attr)type) {
case OVS_ACTION_ATTR_OUTPUT:
@@ -1072,7 +1075,7 @@ dpif_execute_helper_cb(void *aux_, struct dpif_packet *packet,
case OVS_ACTION_ATTR_RECIRC:
execute.actions = action;
execute.actions_len = NLA_ALIGN(action->nla_len);
- execute.packet = dpif_packet_to_ofpbuf(packet);
+ execute.packet = packet;
execute.md = *md;
execute.needs_help = false;
aux->error = aux->dpif->dpif_class->execute(aux->dpif, &execute);
@@ -1106,8 +1109,9 @@ dpif_execute_with_help(struct dpif *dpif, struct dpif_execute *execute)
packet = dpif_packet_clone_from_ofpbuf(execute->packet);
- odp_execute_actions(&aux, packet, true, &execute->md, execute->actions,
- execute->actions_len, dpif_execute_helper_cb);
+ odp_execute_actions(&aux, &packet, 1, true, &execute->md,
+ execute->actions, execute->actions_len,
+ dpif_execute_helper_cb);
return aux.error;
}
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index ceb4208..e4c32e9 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -135,7 +135,7 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a,
break;
case OVS_KEY_ATTR_DP_HASH:
- md->dp_hash = nl_attr_get_u32(a);
+ packet->dp_hash = md->dp_hash = nl_attr_get_u32(a);
break;
case OVS_KEY_ATTR_RECIRC_ID:
@@ -158,8 +158,8 @@ odp_execute_set_action(struct dpif_packet *packet, const struct nlattr *a,
}
static void
-odp_execute_actions__(void *dp, struct dpif_packet *packet, bool steal,
- struct pkt_metadata *,
+odp_execute_actions__(void *dp, struct dpif_packet **packets, int cnt,
+ bool steal, struct pkt_metadata *,
const struct nlattr *actions, size_t actions_len,
odp_execute_cb dp_execute_action, bool more_actions);
@@ -193,20 +193,22 @@ odp_execute_sample(void *dp, struct dpif_packet *packet, bool steal,
}
}
- odp_execute_actions__(dp, packet, steal, md, nl_attr_get(subactions),
+ odp_execute_actions__(dp, &packet, 1, steal, md, nl_attr_get(subactions),
nl_attr_get_size(subactions), dp_execute_action,
more_actions);
}
static void
-odp_execute_actions__(void *dp, struct dpif_packet *packet, bool steal,
- struct pkt_metadata *md,
+odp_execute_actions__(void *dp, struct dpif_packet **packets, int cnt,
+ bool steal, struct pkt_metadata *md,
const struct nlattr *actions, size_t actions_len,
odp_execute_cb dp_execute_action, bool more_actions)
{
const struct nlattr *a;
unsigned int left;
+ int i;
+
NL_ATTR_FOR_EACH_UNSAFE (a, left, actions, actions_len) {
int type = nl_attr_type(a);
@@ -221,7 +223,7 @@ odp_execute_actions__(void *dp, struct dpif_packet *packet, bool steal,
bool may_steal = steal && (!more_actions
&& left <= NLA_ALIGN(a->nla_len)
&& type != OVS_ACTION_ATTR_RECIRC);
- dp_execute_action(dp, packet, md, a, may_steal);
+ dp_execute_action(dp, packets, cnt, md, a, may_steal);
}
break;
@@ -236,9 +238,20 @@ odp_execute_actions__(void *dp, struct dpif_packet *packet, bool steal,
struct flow flow;
uint32_t hash;
- flow_extract(dpif_packet_to_ofpbuf(packet), md, &flow);
- hash = flow_hash_5tuple(&flow, hash_act->hash_basis);
- md->dp_hash = hash ? hash : 1;
+ for (i = 0; i < cnt; i++) {
+ struct ofpbuf *ofp = dpif_packet_to_ofpbuf(packets[i]);
+
+ flow_extract(ofp, md, &flow);
+ hash = flow_hash_5tuple(&flow, hash_act->hash_basis);
+
+ /* The hash of the first packet is in shared metadata */
+ if (i == 0) {
+ md->dp_hash = hash ? hash : 1;
+ }
+
+ /* We also store the hash value with each packet */
+ packets[i]->dp_hash = hash ? hash : 1;
+ }
} else {
/* Assert on unknown hash algorithm. */
OVS_NOT_REACHED();
@@ -248,33 +261,55 @@ odp_execute_actions__(void *dp, struct dpif_packet *packet, bool steal,
case OVS_ACTION_ATTR_PUSH_VLAN: {
const struct ovs_action_push_vlan *vlan = nl_attr_get(a);
- eth_push_vlan(dpif_packet_to_ofpbuf(packet),
- htons(ETH_TYPE_VLAN), vlan->vlan_tci);
+
+ for (i = 0; i < cnt; i++) {
+ struct ofpbuf *ofp = dpif_packet_to_ofpbuf(packets[i]);
+
+ eth_push_vlan(ofp, htons(ETH_TYPE_VLAN), vlan->vlan_tci);
+ }
break;
}
case OVS_ACTION_ATTR_POP_VLAN:
- eth_pop_vlan(dpif_packet_to_ofpbuf(packet));
+ for (i = 0; i < cnt; i++) {
+ struct ofpbuf *ofp = dpif_packet_to_ofpbuf(packets[i]);
+ eth_pop_vlan(ofp);
+ }
break;
case OVS_ACTION_ATTR_PUSH_MPLS: {
const struct ovs_action_push_mpls *mpls = nl_attr_get(a);
- push_mpls(dpif_packet_to_ofpbuf(packet),
- mpls->mpls_ethertype, mpls->mpls_lse);
+
+ for (i = 0; i < cnt; i++) {
+ struct ofpbuf *ofp = dpif_packet_to_ofpbuf(packets[i]);
+
+ push_mpls(ofp, mpls->mpls_ethertype, mpls->mpls_lse);
+ }
break;
}
case OVS_ACTION_ATTR_POP_MPLS:
- pop_mpls(dpif_packet_to_ofpbuf(packet), nl_attr_get_be16(a));
+ for (i = 0; i < cnt; i++) {
+ struct ofpbuf *ofp = dpif_packet_to_ofpbuf(packets[i]);
+
+ pop_mpls(ofp, nl_attr_get_be16(a));
+ }
break;
case OVS_ACTION_ATTR_SET:
- odp_execute_set_action(packet, nl_attr_get(a), md);
+ for (i = 0; i < cnt; i++) {
+ odp_execute_set_action(packets[i], nl_attr_get(a),
+ md);
+ }
break;
case OVS_ACTION_ATTR_SAMPLE:
- odp_execute_sample(dp, packet, steal, md, a, dp_execute_action,
- more_actions || left > NLA_ALIGN(a->nla_len));
+ for (i = 0; i < cnt; i++) {
+ odp_execute_sample(dp, packets[i], steal, md, a,
+ dp_execute_action,
+ more_actions ||
+ left > NLA_ALIGN(a->nla_len));
+ }
break;
case OVS_ACTION_ATTR_UNSPEC:
@@ -285,16 +320,20 @@ odp_execute_actions__(void *dp, struct dpif_packet *packet, bool steal,
}
void
-odp_execute_actions(void *dp, struct dpif_packet *packet, bool steal,
+odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt, bool steal,
struct pkt_metadata *md,
const struct nlattr *actions, size_t actions_len,
odp_execute_cb dp_execute_action)
{
- odp_execute_actions__(dp, packet, steal, md, actions, actions_len,
+ odp_execute_actions__(dp, packets, cnt, steal, md, actions, actions_len,
dp_execute_action, false);
if (!actions_len && steal) {
/* Drop action. */
- dpif_packet_delete(packet);
+ int i;
+
+ for (i = 0; i < cnt; i++) {
+ dpif_packet_delete(packets[i]);
+ }
}
}
diff --git a/lib/odp-execute.h b/lib/odp-execute.h
index 1f836d5..23dc219 100644
--- a/lib/odp-execute.h
+++ b/lib/odp-execute.h
@@ -27,7 +27,7 @@ struct nlattr;
struct dpif_packet;
struct pkt_metadata;
-typedef void (*odp_execute_cb)(void *dp, struct dpif_packet *packet,
+typedef void (*odp_execute_cb)(void *dp, struct dpif_packet **packets, int cnt,
struct pkt_metadata *,
const struct nlattr *action, bool may_steal);
@@ -35,8 +35,8 @@ typedef void (*odp_execute_cb)(void *dp, struct dpif_packet *packet,
* to 'dp_execute_action', if non-NULL. Currently this is called only for
* actions OVS_ACTION_ATTR_OUTPUT and OVS_ACTION_ATTR_USERSPACE so
* 'dp_execute_action' needs to handle only these. */
-void odp_execute_actions(void *dp, struct dpif_packet *packet, bool steal,
- struct pkt_metadata *,
+void odp_execute_actions(void *dp, struct dpif_packet **packets, int cnt,
+ bool steal, struct pkt_metadata *,
const struct nlattr *actions, size_t actions_len,
odp_execute_cb dp_execute_action);
#endif
diff --git a/lib/ofpbuf.h b/lib/ofpbuf.h
index 8d2a87c..e77e07e 100644
--- a/lib/ofpbuf.h
+++ b/lib/ofpbuf.h
@@ -421,10 +421,11 @@ static inline void ofpbuf_set_size(struct ofpbuf *b, uint32_t v)
}
#endif
-/* A packet received from a netdev and passed to a dpif. */
+/* A packet received from a netdev and passed to a dpif. */
struct dpif_packet {
struct ofpbuf packet; /* Packet data. */
+ uint32_t dp_hash; /* Packet hash. */
};
static inline struct dpif_packet *
@@ -487,6 +488,7 @@ dpif_packet_clone(struct dpif_packet * p)
struct dpif_packet *newp;
newp = dpif_packet_clone_from_ofpbuf(dpif_packet_to_ofpbuf(p));
+ newp->dp_hash = p->dp_hash;
return newp;
}
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index 202084c..c7c1fad 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -2657,7 +2657,7 @@ execute_controller_action(struct xlate_ctx *ctx, int len,
&ctx->xout->odp_actions,
&ctx->xout->wc);
- odp_execute_actions(NULL, packet, false, &md,
+ odp_execute_actions(NULL, &packet, 1, false, &md,
ofpbuf_data(&ctx->xout->odp_actions),
ofpbuf_size(&ctx->xout->odp_actions), NULL);
--
2.0.0.rc2
More information about the dev
mailing list