Couple of questions. First, this patch adds a bit of code which extends beyond 79 characters.
dp_netdev_input() does this rather interesting stuff with two keys that mfk switches between. This is definitely not obvious when first looking at it. Minimally it needs a comment explaining why. That said I don't totally understand it, why can't we ruse the same key after each batch is executed? If we can't, why is using only two keys sufficient? This seems a bit odd. Actually on that note, do we need miniflowkey at all? Could it just be embedded at the end of batch_pkt_execute? After this patch, both the hash and recirc and set actions have the wrong behavior. I'd rather they abort() the switch than silently misbehave. Looking at this now, I think this is a rather deep issue that we need to think through. Let's discuss it tomorrow. Ethan On Tue, Jun 3, 2014 at 5:10 PM, Daniele Di Proietto <ddiproie...@vmware.com> wrote: > This change in dpif-netdev allows faster packet processing for devices which > implement batching (netdev-dpdk currently). > > Signed-off-by: Daniele Di Proietto <ddiproie...@vmware.com> > --- > lib/dpif-netdev.c | 240 > ++++++++++++++++++++++++++++++------------- > lib/dpif.c | 7 +- > lib/odp-execute.c | 49 ++++++--- > lib/odp-execute.h | 4 +- > ofproto/ofproto-dpif-xlate.c | 2 +- > 5 files changed, 211 insertions(+), 91 deletions(-) > > diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c > index e0e5ad8..fba1d65 100644 > --- a/lib/dpif-netdev.c > +++ b/lib/dpif-netdev.c > @@ -331,18 +331,18 @@ 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 ofpbuf *, bool may_steal, > + struct ofpbuf **, int cnt, 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 ofpbuf *packet, > - struct pkt_metadata *); > +static void dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf > **packets, > + int cnt, odp_port_t port_no); > > static void dp_netdev_set_pmd_threads(struct dp_netdev *, int n); > > @@ -1518,7 +1518,6 @@ static int > dpif_netdev_execute(struct dpif *dpif, struct dpif_execute *execute) > { > struct dp_netdev *dp = get_dp_netdev(dpif); > - struct pkt_metadata *md = &execute->md; > struct { > struct miniflow flow; > uint32_t buf[FLOW_U32S]; > @@ -1531,9 +1530,9 @@ 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); > > - dp_netdev_execute_actions(dp, &key.flow, execute->packet, false, md, > + dp_netdev_execute_actions(dp, &key.flow, &execute->packet, 1, false, > &execute->md, > execute->actions, execute->actions_len); > > return 0; > @@ -1747,17 +1746,12 @@ dp_netdev_process_rxq_port(struct dp_netdev *dp, > struct dp_netdev_port *port, > struct netdev_rxq *rxq) > { > - struct ofpbuf *packet[NETDEV_MAX_RX_BATCH]; > - int error, c; > + struct ofpbuf *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); > @@ -1954,7 +1948,7 @@ dp_netdev_flow_stats_new_cb(void) > > static void > dp_netdev_flow_used(struct dp_netdev_flow *netdev_flow, > - const struct ofpbuf *packet, > + int cnt, int size, > const struct miniflow *key) > { > uint16_t tcp_flags = miniflow_get_tcp_flags(key); > @@ -1966,8 +1960,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); > } > @@ -1981,74 +1975,143 @@ 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; > + > + struct dp_netdev_flow *flow; > + const struct miniflow *mf; > + > + struct ofpbuf *packets[NETDEV_MAX_RX_BATCH]; > + struct pkt_metadata md; > +}; > + > +static inline void > +packet_batch_init(struct batch_pkt_execute *batch, struct dp_netdev_flow > *flow, > + struct ofpbuf *packet, > + struct pkt_metadata *md, const struct miniflow *mf) > +{ > + batch->flow = flow; > + batch->mf = mf; > + batch->md = *md; > + batch->packets[0] = packet; > + > + /* no need to update time and tcp_flags, its same in given input batch. > */ > + batch->packet_count = 1; > + batch->byte_count = ofpbuf_size(packet); > +} > + > +static inline void > +packet_batch_update(struct batch_pkt_execute *batch, struct ofpbuf *packet) > +{ > + batch->packets[batch->packet_count++] = packet; > + batch->byte_count += ofpbuf_size(packet); > +} > + > +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->mf); > + > + actions = ovsrcu_get(struct dp_netdev_actions *, &flow->actions); > + > + 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 ofpbuf *packet, > +dp_netdev_input(struct dp_netdev *dp, struct ofpbuf **packets, int cnt, > struct pkt_metadata *md) > { > - struct dp_netdev_flow *netdev_flow; > - struct { > + struct batch_pkt_execute batch; > + > + struct miniflowkey{ > struct miniflow flow; > uint32_t buf[FLOW_U32S]; > - } key; > + } keys[2]; > > - if (ofpbuf_size(packet) < ETH_HEADER_LEN) { > - ofpbuf_delete(packet); > - return; > + struct miniflowkey *mfk = &keys[0]; > + > + int i; > + > + batch.flow = NULL; > + batch.mf = 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(packet, 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; > + > + if (ofpbuf_size(packets[i]) < ETH_HEADER_LEN) { > + ofpbuf_delete(packets[i]); > + continue; > + } > + > + miniflow_extract(packets[i], md, &mfk->flow); > + > + netdev_flow = dp_netdev_lookup_flow(dp, &mfk->flow); > > - dp_netdev_flow_used(netdev_flow, packet, &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]); > + } 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, &packets[i], 1, > + miniflow_hash_5tuple(&mfk->flow, 0) > + % dp->n_handlers, > + DPIF_UC_MISS, &mfk->flow, NULL); > + ofpbuf_delete(packets[i]); > + } > + } > > - 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, packet, > - miniflow_hash_5tuple(&key.flow, 0) > - % dp->n_handlers, > - DPIF_UC_MISS, &key.flow, NULL); > - ofpbuf_delete(packet); > + if (batch.flow) { > + packet_batch_execute(&batch, dp); > } > } > > static void > -dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf *packet, > - struct pkt_metadata *md) > +dp_netdev_port_input(struct dp_netdev *dp, struct ofpbuf **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) > { > - 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; > @@ -2075,19 +2138,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)); > } > > ofpbuf_set_data(&upcall->packet, > - ofpbuf_put(buf, ofpbuf_data(packet), > ofpbuf_size(packet))); > + ofpbuf_put(buf, ofpbuf_data(packet), > + ofpbuf_size(packet))); > ofpbuf_set_size(&upcall->packet, ofpbuf_size(packet)); > > seq_change(q->seq); > > - error = 0; > + return 0; > } else { > - dp_netdev_count_packet(dp, DP_STAT_LOST); > - 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); > @@ -2101,7 +2187,7 @@ struct dp_netdev_execute_aux { > }; > > static void > -dp_execute_cb(void *aux_, struct ofpbuf *packet, > +dp_execute_cb(void *aux_, struct ofpbuf **packets, int cnt, > struct pkt_metadata *md, > const struct nlattr *a, bool may_steal) > OVS_NO_THREAD_SAFETY_ANALYSIS > @@ -2110,12 +2196,13 @@ dp_execute_cb(void *aux_, struct ofpbuf *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) { > - netdev_send(p->netdev, &packet, 1, may_steal); > + netdev_send(p->netdev, packets, cnt, may_steal); > } > break; > > @@ -2124,14 +2211,16 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet, > > userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA); > > - dp_netdev_output_userspace(aux->dp, packet, > + dp_netdev_output_userspace(aux->dp, packets, cnt, > miniflow_hash_5tuple(aux->key, 0) > % aux->dp->n_handlers, > DPIF_UC_ACTION, aux->key, > userdata); > > if (may_steal) { > - ofpbuf_delete(packet); > + for (i = 0; i < cnt; i++) { > + ofpbuf_delete(packets[i]); > + } > } > break; > } > @@ -2144,6 +2233,7 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet, > if (hash_act->hash_alg == OVS_HASH_ALG_L4) { > /* Hash need not be symmetric, nor does it need to include > * L2 fields. */ > + /* TODO: hash the miniflow, but a batch could have different > miniflows */ > hash = miniflow_hash_5tuple(aux->key, hash_act->hash_basis); > if (!hash) { > hash = 1; /* 0 is not valid */ > @@ -2155,19 +2245,25 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet, > } > > md->dp_hash = hash; > + > break; > } > > case OVS_ACTION_ATTR_RECIRC: > if (*depth < MAX_RECIRC_DEPTH) { > + struct ofpbuf *recirc_packets[cnt]; > struct pkt_metadata recirc_md = *md; > - struct ofpbuf *recirc_packet; > > - recirc_packet = may_steal ? packet : ofpbuf_clone(packet); > + if (!may_steal) { > + for (i = 0; i < cnt; i++) > + recirc_packets[i] = ofpbuf_clone(packets[i]); > + } > + > recirc_md.recirc_id = nl_attr_get_u32(a); > > (*depth)++; > - dp_netdev_input(aux->dp, recirc_packet, &recirc_md); > + dp_netdev_input(aux->dp, may_steal ? packets : recirc_packets, > + cnt, &recirc_md); > (*depth)--; > > break; > @@ -2190,13 +2286,13 @@ dp_execute_cb(void *aux_, struct ofpbuf *packet, > > static void > dp_netdev_execute_actions(struct dp_netdev *dp, const struct miniflow *key, > - struct ofpbuf *packet, bool may_steal, > + struct ofpbuf **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, > + odp_execute_actions(&aux, packets, cnt, may_steal, md, > actions, actions_len, dp_execute_cb); > } > > diff --git a/lib/dpif.c b/lib/dpif.c > index 84dba28..852fcd6 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 ofpbuf *packet, > +dpif_execute_helper_cb(void *aux_, struct ofpbuf **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 = packets[0]; > + > + ovs_assert(cnt == 1); > > switch ((enum ovs_action_attr)type) { > case OVS_ACTION_ATTR_OUTPUT: > @@ -1103,7 +1106,7 @@ dpif_execute_with_help(struct dpif *dpif, struct > dpif_execute *execute) > > COVERAGE_INC(dpif_execute_with_help); > > - odp_execute_actions(&aux, execute->packet, false, &execute->md, > + odp_execute_actions(&aux, &execute->packet, 1, false, &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 12ad679..db67150 100644 > --- a/lib/odp-execute.c > +++ b/lib/odp-execute.c > @@ -151,7 +151,7 @@ odp_execute_set_action(struct ofpbuf *packet, const > struct nlattr *a, > } > > static void > -odp_execute_actions__(void *dp, struct ofpbuf *packet, bool steal, > +odp_execute_actions__(void *dp, struct ofpbuf **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); > @@ -186,13 +186,13 @@ odp_execute_sample(void *dp, struct ofpbuf *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 ofpbuf *packet, bool steal, > +odp_execute_actions__(void *dp, struct ofpbuf **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) > @@ -200,6 +200,8 @@ odp_execute_actions__(void *dp, struct ofpbuf *packet, > bool steal, > 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); > > @@ -215,37 +217,52 @@ odp_execute_actions__(void *dp, struct ofpbuf *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; > > case OVS_ACTION_ATTR_PUSH_VLAN: { > const struct ovs_action_push_vlan *vlan = nl_attr_get(a); > - eth_push_vlan(packet, htons(ETH_TYPE_VLAN), vlan->vlan_tci); > + > + for (i = 0; i < cnt; i++) { > + eth_push_vlan(packets[i], htons(ETH_TYPE_VLAN), > vlan->vlan_tci); > + } > break; > } > > case OVS_ACTION_ATTR_POP_VLAN: > - eth_pop_vlan(packet); > + for (i = 0; i < cnt; i++) { > + eth_pop_vlan(packets[i]); > + } > break; > > case OVS_ACTION_ATTR_PUSH_MPLS: { > const struct ovs_action_push_mpls *mpls = nl_attr_get(a); > - push_mpls(packet, mpls->mpls_ethertype, mpls->mpls_lse); > + for (i = 0; i < cnt; i++) { > + push_mpls(packets[i], mpls->mpls_ethertype, mpls->mpls_lse); > + } > break; > } > > case OVS_ACTION_ATTR_POP_MPLS: > - pop_mpls(packet, nl_attr_get_be16(a)); > + for (i = 0; i < cnt; i++) { > + pop_mpls(packets[i], 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++) { > + /* TODO: if setting metadata could do it only once */ > + 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: > @@ -256,16 +273,20 @@ odp_execute_actions__(void *dp, struct ofpbuf *packet, > bool steal, > } > > void > -odp_execute_actions(void *dp, struct ofpbuf *packet, bool steal, > +odp_execute_actions(void *dp, struct ofpbuf **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. */ > - ofpbuf_delete(packet); > + int i = 0; > + > + for (i = 0; i < cnt; i++) { > + ofpbuf_delete(packets[i]); > + } > } > } > diff --git a/lib/odp-execute.h b/lib/odp-execute.h > index 91f0c51..006deda 100644 > --- a/lib/odp-execute.h > +++ b/lib/odp-execute.h > @@ -27,7 +27,7 @@ struct nlattr; > struct ofpbuf; > struct pkt_metadata; > > -typedef void (*odp_execute_cb)(void *dp, struct ofpbuf *packet, > +typedef void (*odp_execute_cb)(void *dp, struct ofpbuf **packets, int cnt, > struct pkt_metadata *, > const struct nlattr *action, bool may_steal); > > @@ -35,7 +35,7 @@ typedef void (*odp_execute_cb)(void *dp, struct ofpbuf > *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 ofpbuf *packet, bool steal, > +void odp_execute_actions(void *dp, struct ofpbuf **packets, int cnt, bool > steal, > struct pkt_metadata *, > const struct nlattr *actions, size_t actions_len, > odp_execute_cb dp_execute_action); > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index 068d54f..875c9e8 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -2655,7 +2655,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 > > _______________________________________________ > dev mailing list > dev@openvswitch.org > http://openvswitch.org/mailman/listinfo/dev _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev