On Sat, Jun 18, 2016 at 3:36 PM, William Tu <u9012...@gmail.com> wrote: > The patch adds a new action to support packet truncation. The new action > is formatted as 'output(port=n,max_len=m)', as output to port n, with > packet size being MIN(original_size, m). > > One use case is to enable port mirroring to send smaller packets to the > destination port so that only useful packet information is mirrored/copied, > saving some performance overhead of copying entire packet payload. Example > use case is below as well as shown in the testcases: > > - Output to port 1 with max_len 100 bytes. > - The output packet size on port 1 will be MIN(original_packet_size, 100). > # ovs-ofctl add-flow br0 'actions=output(port=1,max_len=100)' > > - The scope of max_len is limited to output action itself. The following > packet size of output:1 and output:2 will be intact. > # ovs-ofctl add-flow br0 \ > 'actions=output(port=1,max_len=100),output:1,output:2' > - The Datapath actions shows: > # Datapath actions: trunc(100),1,1,2 > > Signed-off-by: William Tu <u9012...@gmail.com> > --- > include/openvswitch/ofp-actions.h | 10 +++ > lib/dp-packet.c | 2 + > lib/dp-packet.h | 67 +++++++++++++++ > lib/dpif-netdev.c | 32 +++++++- > lib/dpif.c | 24 +++++- > lib/dpif.h | 1 + > lib/netdev-bsd.c | 3 + > lib/netdev-dpdk.c | 6 ++ > lib/netdev-dummy.c | 5 ++ > lib/netdev-linux.c | 3 + > lib/netdev.c | 6 +- > lib/odp-execute.c | 12 +++ > lib/odp-util.c | 23 ++++++ > lib/ofp-actions.c | 106 ++++++++++++++++++++++++ > ofproto/ofproto-dpif-sflow.c | 1 + > ofproto/ofproto-dpif-xlate.c | 57 +++++++++++++ > ofproto/ofproto-dpif.c | 58 +++++++++++++ > ofproto/ofproto-dpif.h | 3 + > tests/odp.at | 1 + > tests/ofp-actions.at | 3 + > tests/ofproto-dpif.at | 124 ++++++++++++++++++++++++++++ > tests/ovs-ofctl.at | 8 ++ > tests/system-traffic.at | 168 > ++++++++++++++++++++++++++++++++++++++ > 23 files changed, 719 insertions(+), 4 deletions(-) > > diff --git a/include/openvswitch/ofp-actions.h > b/include/openvswitch/ofp-actions.h > index 91c7ee5..0b8ccbb 100644 > --- a/include/openvswitch/ofp-actions.h > +++ b/include/openvswitch/ofp-actions.h > @@ -108,6 +108,7 @@ > OFPACT(UNROLL_XLATE, ofpact_unroll_xlate, ofpact, "unroll_xlate") \ > OFPACT(CT, ofpact_conntrack, ofpact, "ct") \ > OFPACT(NAT, ofpact_nat, ofpact, "nat") \ > + OFPACT(OUTPUT_TRUNC, ofpact_output_trunc,ofpact, "output_trunc") \ > \ > /* Debugging actions. \ > * \ > @@ -290,6 +291,15 @@ struct ofpact_output_reg { > struct mf_subfield src; > }; > > +/* OFPACT_OUTPUT_TRUNC. > + * > + * Used for NXAST_OUTPUT_TRUNC. */ > +struct ofpact_output_trunc { > + struct ofpact ofpact; > + ofp_port_t port; /* Output port. */ > + uint32_t max_len; /* Max send len. */ > +}; > + > /* Bundle slave choice algorithm to apply. > * > * In the descriptions below, 'slaves' is the list of possible slaves in the > diff --git a/lib/dp-packet.c b/lib/dp-packet.c > index 0c85d50..8e7defc 100644 > --- a/lib/dp-packet.c > +++ b/lib/dp-packet.c > @@ -30,6 +30,7 @@ dp_packet_init__(struct dp_packet *b, size_t allocated, > enum dp_packet_source so > dp_packet_reset_offsets(b); > pkt_metadata_init(&b->md, 0); > dp_packet_rss_invalidate(b); > + dp_packet_reset_cutlen(b); > } > > static void > @@ -168,6 +169,7 @@ dp_packet_clone_with_headroom(const struct dp_packet > *buffer, size_t headroom) > new_buffer->l3_ofs = buffer->l3_ofs; > new_buffer->l4_ofs = buffer->l4_ofs; > new_buffer->md = buffer->md; > + new_buffer->cutlen = buffer->cutlen; > #ifdef DPDK_NETDEV > new_buffer->mbuf.ol_flags = buffer->mbuf.ol_flags; > #else > diff --git a/lib/dp-packet.h b/lib/dp-packet.h > index 118c84d..a2af649 100644 > --- a/lib/dp-packet.h > +++ b/lib/dp-packet.h > @@ -60,6 +60,7 @@ struct dp_packet { > * or UINT16_MAX. */ > uint16_t l4_ofs; /* Transport-level header offset, > or UINT16_MAX. */ > + uint32_t cutlen; /* length in bytes to cut from the end. */ > union { > struct pkt_metadata md; > uint64_t data[DP_PACKET_CONTEXT_SIZE / 8]; > @@ -494,6 +495,34 @@ dp_packet_set_allocated(struct dp_packet *b, uint16_t s) > } > #endif > > +static inline void > +dp_packet_reset_cutlen(struct dp_packet *b) > +{ > + b->cutlen = 0; > +} > + > +static inline uint32_t > +dp_packet_set_cutlen(struct dp_packet *b, uint32_t max_len) > +{ > + if (max_len < ETH_HEADER_LEN) { > + max_len = ETH_HEADER_LEN; > + } > + > + if (max_len >= dp_packet_size(b)) { > + b->cutlen = 0; > + } else { > + b->cutlen = dp_packet_size(b) - max_len; > + } > + return b->cutlen; > +} > + > +static inline uint32_t > +dp_packet_get_cutlen(struct dp_packet *b) > +{ > + /* Always in valid range if user uses dp_packet_set_cutlen. */ > + return b->cutlen; > +} > + > static inline void * > dp_packet_data(const struct dp_packet *b) > { > @@ -567,12 +596,14 @@ enum { NETDEV_MAX_BURST = 32 }; /* Maximum number > packets in a batch. */ > > struct dp_packet_batch { > int count; > + bool trunc; /* true if the batch needs truncate. */ > struct dp_packet *packets[NETDEV_MAX_BURST]; > }; > > static inline void dp_packet_batch_init(struct dp_packet_batch *b) > { > b->count = 0; > + b->trunc = false; > } > > static inline void > @@ -585,12 +616,14 @@ dp_packet_batch_clone(struct dp_packet_batch *dst, > dst->packets[i] = dp_packet_clone(src->packets[i]); > } > dst->count = src->count; > + dst->trunc = src->trunc; > } > > static inline void > packet_batch_init_packet(struct dp_packet_batch *b, struct dp_packet *p) > { > b->count = 1; > + b->trunc = false; > b->packets[0] = p; > } > > @@ -606,6 +639,40 @@ dp_packet_delete_batch(struct dp_packet_batch *batch, > bool may_steal) > } > } > > +static inline void > +dp_packet_batch_cutlen(struct dp_packet_batch *pktb) This function name is not descriptive enough, can indicate that this is actually truncating all packets from the batch.
> +{ > + int i; > + > + if (!pktb->trunc) > + return; > + > + for (i = 0; i < pktb->count; i++) { > + uint32_t cutlen = dp_packet_get_cutlen(pktb->packets[i]); > + > + if (cutlen > 0) { > + dp_packet_set_size(pktb->packets[i], > + dp_packet_size(pktb->packets[i]) - cutlen); > + dp_packet_reset_cutlen(pktb->packets[i]); > + } > + } > + pktb->trunc = false; > +} > + > +static inline void > +dp_packet_batch_reset_cutlen(struct dp_packet_batch *pktb) > +{ > + int i; > + > + if (!pktb->trunc) > + return; > + > + pktb->trunc = false; > + for (i = 0; i < pktb->count; i++) { > + dp_packet_reset_cutlen(pktb->packets[i]); > + } > +} > + > #ifdef __cplusplus > } > #endif > diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c > index 8d39d9e..90e1f31 100644 > --- a/lib/dpif-netdev.c > +++ b/lib/dpif-netdev.c > @@ -4057,6 +4057,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch > *packets_, > case OVS_ACTION_ATTR_TUNNEL_PUSH: > if (*depth < MAX_RECIRC_DEPTH) { > struct dp_packet_batch tnl_pkt; > + struct dp_packet_batch *orig_packets_ = packets_; > int err; > > if (!may_steal) { > @@ -4064,6 +4065,9 @@ dp_execute_cb(void *aux_, struct dp_packet_batch > *packets_, > packets_ = &tnl_pkt; > } > > + dp_packet_batch_cutlen(packets_); > + dp_packet_batch_reset_cutlen(orig_packets_); > + > err = push_tnl_action(pmd, a, packets_); > if (!err) { > (*depth)++; > @@ -4076,6 +4080,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch > *packets_, > > case OVS_ACTION_ATTR_TUNNEL_POP: > if (*depth < MAX_RECIRC_DEPTH) { > + struct dp_packet_batch *orig_packets_ = packets_; > odp_port_t portno = u32_to_odp(nl_attr_get_u32(a)); > > p = pmd_tx_port_cache_lookup(pmd, portno); > @@ -4084,10 +4089,13 @@ dp_execute_cb(void *aux_, struct dp_packet_batch > *packets_, > int i; > > if (!may_steal) { > - dp_packet_batch_clone(&tnl_pkt, packets_); > - packets_ = &tnl_pkt; > + dp_packet_batch_clone(&tnl_pkt, packets_); > + packets_ = &tnl_pkt; > } > > + dp_packet_batch_cutlen(packets_); > + dp_packet_batch_reset_cutlen(orig_packets_); > + is there any need to call dp_packet_batch_reset_cutlen() function after dp_packet_batch_cutlen()? > netdev_pop_header(p->netdev, packets_); > if (!packets_->count) { > return; > @@ -4107,22 +4115,41 @@ dp_execute_cb(void *aux_, struct dp_packet_batch > *packets_, > > case OVS_ACTION_ATTR_USERSPACE: > if (!fat_rwlock_tryrdlock(&dp->upcall_rwlock)) { > + struct dp_packet_batch *orig_packets_ = packets_; > struct dp_packet **packets = packets_->packets; > const struct nlattr *userdata; > + struct dp_packet_batch usr_pkt; > struct ofpbuf actions; > struct flow flow; > ovs_u128 ufid; > + bool clone = false; > int i; > > userdata = nl_attr_find_nested(a, OVS_USERSPACE_ATTR_USERDATA); > ofpbuf_init(&actions, 0); > > + if (packets_->trunc) { > + if (!may_steal) { > + dp_packet_batch_clone(&usr_pkt, packets_); > + packets_ = &usr_pkt; > + packets = packets_->packets; > + clone = true; > + } > + dp_packet_batch_cutlen(packets_); > + dp_packet_batch_reset_cutlen(orig_packets_); > + } > + > for (i = 0; i < packets_->count; i++) { > flow_extract(packets[i], &flow); > dpif_flow_hash(dp->dpif, &flow, sizeof flow, &ufid); > dp_execute_userspace_action(pmd, packets[i], may_steal, > &flow, > &ufid, &actions, userdata); > } > + > + if (clone) { > + dp_packet_delete_batch(packets_, true); > + } > + > ofpbuf_uninit(&actions); > fat_rwlock_unlock(&dp->upcall_rwlock); > > @@ -4170,6 +4197,7 @@ dp_execute_cb(void *aux_, struct dp_packet_batch > *packets_, > case OVS_ACTION_ATTR_SAMPLE: > case OVS_ACTION_ATTR_HASH: > case OVS_ACTION_ATTR_UNSPEC: > + case OVS_ACTION_ATTR_TRUNC: > case __OVS_ACTION_ATTR_MAX: > OVS_NOT_REACHED(); > } > diff --git a/lib/dpif.c b/lib/dpif.c > index c4f24c7..b9cbadf 100644 > --- a/lib/dpif.c > +++ b/lib/dpif.c > @@ -1092,6 +1092,7 @@ dpif_execute_helper_cb(void *aux_, struct > dp_packet_batch *packets_, > struct dpif_execute_helper_aux *aux = aux_; > int type = nl_attr_type(action); > struct dp_packet *packet = packets_->packets[0]; > + struct dp_packet *trunc_packet = NULL, *orig_packet; > > ovs_assert(packets_->count == 1); > > @@ -1106,7 +1107,8 @@ dpif_execute_helper_cb(void *aux_, struct > dp_packet_batch *packets_, > struct ofpbuf execute_actions; > uint64_t stub[256 / 8]; > struct pkt_metadata *md = &packet->md; > - bool dst_set; > + bool dst_set, clone = false; > + uint32_t cutlen = dp_packet_get_cutlen(packet); > > dst_set = flow_tnl_dst_is_set(&md->tunnel); > if (dst_set) { > @@ -1124,6 +1126,21 @@ dpif_execute_helper_cb(void *aux_, struct > dp_packet_batch *packets_, > execute.actions_len = NLA_ALIGN(action->nla_len); > } > > + orig_packet = packet; > + > + if (cutlen > 0 && > + type != OVS_ACTION_ATTR_CT && > + type != OVS_ACTION_ATTR_RECIRC) { Checking for allowed action is easier to read than not allowed. > + if (!may_steal) { > + trunc_packet = dp_packet_clone(packet); > + packet = trunc_packet; > + clone = true; > + } > + > + dp_packet_set_size(packet, dp_packet_size(packet) - cutlen); > + dp_packet_reset_cutlen(orig_packet); > + } > + > execute.packet = packet; > execute.flow = aux->flow; > execute.needs_help = false; > @@ -1135,6 +1152,10 @@ dpif_execute_helper_cb(void *aux_, struct > dp_packet_batch *packets_, > if (dst_set) { > ofpbuf_uninit(&execute_actions); > } > + > + if (clone) { > + dp_packet_delete(trunc_packet); > + } > break; > } > > @@ -1146,6 +1167,7 @@ dpif_execute_helper_cb(void *aux_, struct > dp_packet_batch *packets_, > case OVS_ACTION_ATTR_SET: > case OVS_ACTION_ATTR_SET_MASKED: > case OVS_ACTION_ATTR_SAMPLE: > + case OVS_ACTION_ATTR_TRUNC: > case OVS_ACTION_ATTR_UNSPEC: > case __OVS_ACTION_ATTR_MAX: > OVS_NOT_REACHED(); > diff --git a/lib/dpif.h b/lib/dpif.h > index 6788301..981868c 100644 > --- a/lib/dpif.h > +++ b/lib/dpif.h > @@ -784,6 +784,7 @@ struct dpif_upcall { > size_t key_len; /* Length of 'key' in bytes. */ > ovs_u128 ufid; /* Unique flow identifier for 'key'. */ > struct nlattr *mru; /* Maximum receive unit. */ > + struct nlattr *cutlen; /* Number of bytes shrink from the end. */ > > /* DPIF_UC_ACTION only. */ > struct nlattr *userdata; /* Argument to OVS_ACTION_ATTR_USERSPACE. */ > diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c > index 43fa982..2e92d97 100644 > --- a/lib/netdev-bsd.c > +++ b/lib/netdev-bsd.c > @@ -699,6 +699,9 @@ netdev_bsd_send(struct netdev *netdev_, int qid > OVS_UNUSED, > const void *data = dp_packet_data(pkts[i]); > size_t size = dp_packet_size(pkts[i]); > > + /* Truncate the packet if it is configured. */ > + size -= dp_packet_get_cutlen(pkts[i]); > + > while (!error) { > ssize_t retval; > if (dev->tap_fd >= 0) { > diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c > index fc0c8d3..8474b17 100644 > --- a/lib/netdev-dpdk.c > +++ b/lib/netdev-dpdk.c > @@ -1585,6 +1585,12 @@ netdev_dpdk_send__(struct netdev_dpdk *dev, int qid, > > for (i = 0; i < cnt; i++) { > int size = dp_packet_size(pkts[i]); > + uint32_t cutlen = dp_packet_get_cutlen(pkts[i]); > + > + if (cutlen > 0) { > + size -= cutlen; > + dp_packet_set_size(pkts[i], size); > + } No need to check if cutlen is positive value, subtracting zero does not matter anyways. > > if (OVS_UNLIKELY(size > dev->max_packet_len)) { > if (next_tx_idx != i) { > diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c > index aa244b6..cd90c33 100644 > --- a/lib/netdev-dummy.c > +++ b/lib/netdev-dummy.c > @@ -1043,6 +1043,11 @@ netdev_dummy_send(struct netdev *netdev, int qid > OVS_UNUSED, > for (i = 0; i < cnt; i++) { > const void *buffer = dp_packet_data(pkts[i]); > size_t size = dp_packet_size(pkts[i]); > + uint32_t cutlen = dp_packet_get_cutlen(pkts[i]); > + > + if (cutlen > 0) { > + size -= cutlen; > + } > > if (size < ETH_HEADER_LEN) { > error = EMSGSIZE; > diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c > index 82813ba..d6e5a54 100644 > --- a/lib/netdev-linux.c > +++ b/lib/netdev-linux.c > @@ -1170,6 +1170,9 @@ netdev_linux_send(struct netdev *netdev_, int qid > OVS_UNUSED, > size_t size = dp_packet_size(pkts[i]); > ssize_t retval; > > + /* Truncate the packet if it is configured. */ > + size -= dp_packet_get_cutlen(pkts[i]); > + > if (!is_tap_netdev(netdev_)) { > /* Use our AF_PACKET socket to send to this device. */ > struct sockaddr_ll sll; > diff --git a/lib/netdev.c b/lib/netdev.c > index 4be806d..444998d 100644 > --- a/lib/netdev.c > +++ b/lib/netdev.c > @@ -681,7 +681,7 @@ netdev_set_tx_multiq(struct netdev *netdev, unsigned int > n_txq) > return error; > } > > -/* Sends 'buffers' on 'netdev'. Returns 0 if successful (for every packet), > +/* Sends 'batch' on 'netdev'. Returns 0 if successful (for every packet), > * otherwise a positive errno value. Returns EAGAIN without blocking if > * at least one the packets cannot be queued immediately. Returns EMSGSIZE > * if a partial packet was transmitted or if a packet is too big or too small > @@ -717,6 +717,10 @@ (struct netdev *netdev, int qid, struct dp_packet_batch > *batch, > if (!error) { > COVERAGE_INC(netdev_sent); > } > + > + if (!may_steal) { > + dp_packet_batch_reset_cutlen(batch); > + } This could be moved to if block above. it is not required if there is any error. > return error; > } > > diff --git a/lib/odp-execute.c b/lib/odp-execute.c > index 4239624..5a43904 100644 > --- a/lib/odp-execute.c > +++ b/lib/odp-execute.c > @@ -503,6 +503,7 @@ requires_datapath_assistance(const struct nlattr *a) > case OVS_ACTION_ATTR_HASH: > case OVS_ACTION_ATTR_PUSH_MPLS: > case OVS_ACTION_ATTR_POP_MPLS: > + case OVS_ACTION_ATTR_TRUNC: > return false; > > case OVS_ACTION_ATTR_UNSPEC: > @@ -625,6 +626,17 @@ odp_execute_actions(void *dp, struct dp_packet_batch > *batch, bool steal, > } > break; > > + case OVS_ACTION_ATTR_TRUNC: { > + const struct ovs_action_trunc *trunc = > + nl_attr_get_unspec(a, sizeof *trunc); > + > + batch->trunc = true; > + for (i = 0; i < cnt; i++) { > + dp_packet_set_cutlen(packets[i], trunc->max_len); > + } > + break; > + } > + > case OVS_ACTION_ATTR_OUTPUT: > case OVS_ACTION_ATTR_TUNNEL_PUSH: > case OVS_ACTION_ATTR_TUNNEL_POP: > diff --git a/lib/odp-util.c b/lib/odp-util.c > index 48c05f5..d7b6a2d 100644 > --- a/lib/odp-util.c > +++ b/lib/odp-util.c > @@ -107,6 +107,7 @@ odp_action_len(uint16_t type) > > switch ((enum ovs_action_attr) type) { > case OVS_ACTION_ATTR_OUTPUT: return sizeof(uint32_t); > + case OVS_ACTION_ATTR_TRUNC: return sizeof(struct ovs_action_trunc); > case OVS_ACTION_ATTR_TUNNEL_PUSH: return ATTR_LEN_VARIABLE; > case OVS_ACTION_ATTR_TUNNEL_POP: return sizeof(uint32_t); > case OVS_ACTION_ATTR_USERSPACE: return ATTR_LEN_VARIABLE; > @@ -777,6 +778,14 @@ format_odp_action(struct ds *ds, const struct nlattr *a) > case OVS_ACTION_ATTR_OUTPUT: > ds_put_format(ds, "%"PRIu32, nl_attr_get_u32(a)); > break; > + case OVS_ACTION_ATTR_TRUNC: { > + const struct ovs_action_trunc *trunc = > + nl_attr_get_unspec(a, sizeof *trunc); > + > + ds_put_format(ds, "trunc(%"PRIu32")", trunc->max_len); > + break; > + } > + break; > case OVS_ACTION_ATTR_TUNNEL_POP: > ds_put_format(ds, "tnl_pop(%"PRIu32")", nl_attr_get_u32(a)); > break; > @@ -1528,6 +1537,20 @@ parse_odp_action(const char *s, const struct simap > *port_names, > } > } > > + { > + uint32_t max_len; > + int n; > + > + if (ovs_scan(s, "trunc(%"SCNi32")%n", &max_len, &n)) { > + struct ovs_action_trunc *trunc; > + > + trunc = nl_msg_put_unspec_uninit(actions, > + OVS_ACTION_ATTR_TRUNC, sizeof *trunc); > + trunc->max_len = max_len; > + return n; > + } > + } > + > if (port_names) { > int len = strcspn(s, delimiters); > struct simap_node *node; > diff --git a/lib/ofp-actions.c b/lib/ofp-actions.c > index ea55896..997cc15 100644 > --- a/lib/ofp-actions.c > +++ b/lib/ofp-actions.c > @@ -301,6 +301,9 @@ enum ofp_raw_action_type { > /* NX1.0+(36): struct nx_action_nat, ... */ > NXAST_RAW_NAT, > > + /* NX1.0+(39): struct nx_action_output_trunc. */ > + NXAST_RAW_OUTPUT_TRUNC, > + > /* ## ------------------ ## */ > /* ## Debugging actions. ## */ > /* ## ------------------ ## */ > @@ -381,6 +384,7 @@ ofpact_next_flattened(const struct ofpact *ofpact) > case OFPACT_CONTROLLER: > case OFPACT_ENQUEUE: > case OFPACT_OUTPUT_REG: > + case OFPACT_OUTPUT_TRUNC: > case OFPACT_BUNDLE: > case OFPACT_SET_FIELD: > case OFPACT_SET_VLAN_VID: > @@ -538,6 +542,40 @@ encode_OUTPUT(const struct ofpact_output *output, > } > > static char * OVS_WARN_UNUSED_RESULT > +parse_truncate_subfield(struct ofpact_output_trunc *output_trunc, > + const char *arg_) > +{ > + char *key, *value; > + char *arg = CONST_CAST(char *, arg_); > + > + while (ofputil_parse_key_value(&arg, &key, &value)) { > + if (!strcmp(key, "port")) { > + if (!ofputil_port_from_string(value, &output_trunc->port)) { > + return xasprintf("output to unknown truncate port: %s", > + value); > + } > + if (ofp_to_u16(output_trunc->port) > ofp_to_u16(OFPP_MAX)) { > + if (output_trunc->port != OFPP_LOCAL && > + output_trunc->port != OFPP_IN_PORT) > + return xasprintf("output to unsupported truncate port: %s", > + value); > + } > + } else if (!strcmp(key, "max_len")) { > + char *err; > + > + err = str_to_u32(value, &output_trunc->max_len); > + if (err) { > + return err; > + } > + } else { > + return xasprintf("invalid key '%s' in output_trunc argument", > + key); > + } > + } > + return NULL; > +} > + > +static char * OVS_WARN_UNUSED_RESULT > parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts, > enum ofputil_protocol *usable_protocols OVS_UNUSED) > { > @@ -547,6 +585,11 @@ parse_OUTPUT(const char *arg, struct ofpbuf *ofpacts, > output_reg = ofpact_put_OUTPUT_REG(ofpacts); > output_reg->max_len = UINT16_MAX; > return mf_parse_subfield(&output_reg->src, arg); > + } else if (strstr(arg, "port") && strstr(arg, "max_len")) { > + struct ofpact_output_trunc *output_trunc; > + > + output_trunc = ofpact_put_OUTPUT_TRUNC(ofpacts); > + return parse_truncate_subfield(output_trunc, arg); > } else { > struct ofpact_output *output; > > @@ -5603,6 +5646,61 @@ parse_NAT(char *arg, struct ofpbuf *ofpacts, > return NULL; > } > > +/* Truncate output action. */ > +struct nx_action_output_trunc { > + ovs_be16 type; /* OFPAT_VENDOR. */ > + ovs_be16 len; /* At least 16. */ > + ovs_be32 vendor; /* NX_VENDOR_ID. */ > + ovs_be16 subtype; /* NXAST_OUTPUT_TRUNC. */ > + ovs_be16 port; /* Output port */ > + ovs_be32 max_len; /* Truncate packet to size bytes */ > +}; > +OFP_ASSERT(sizeof(struct nx_action_output_trunc) == 16); > + > +static enum ofperr > +decode_NXAST_RAW_OUTPUT_TRUNC(const struct nx_action_output_trunc *natrc, > + enum ofp_version ofp_version OVS_UNUSED, > + struct ofpbuf *out) > +{ > + struct ofpact_output_trunc *output_trunc; > + > + output_trunc = ofpact_put_OUTPUT_TRUNC(out); > + output_trunc->max_len = ntohl(natrc->max_len); > + output_trunc->port = u16_to_ofp(ntohs(natrc->port)); > + > + if (output_trunc->max_len < ETH_HEADER_LEN) { > + return OFPERR_OFPBAC_BAD_ARGUMENT; > + } > + return 0; > +} > + > +static void > +encode_OUTPUT_TRUNC(const struct ofpact_output_trunc *output_trunc, > + enum ofp_version ofp_version OVS_UNUSED, > + struct ofpbuf *out) > +{ > + struct nx_action_output_trunc *natrc = put_NXAST_OUTPUT_TRUNC(out); > + > + natrc->max_len = htonl(output_trunc->max_len); > + natrc->port = htons(ofp_to_u16(output_trunc->port)); > +} > + > +static char * OVS_WARN_UNUSED_RESULT > +parse_OUTPUT_TRUNC(const char *arg, struct ofpbuf *ofpacts OVS_UNUSED, > + enum ofputil_protocol *usable_protocols OVS_UNUSED) > +{ > + /* Disable output_trunc parsing. Expose as output(port=N,max_len=M) and > + * reuse parse_OUTPUT to parse output_trunc action. */ > + return xasprintf("unknown action %s", arg); > +} > + > +static void > +format_OUTPUT_TRUNC(const struct ofpact_output_trunc *a, struct ds *s) > +{ > + ds_put_format(s, "%soutput%s(port=%"PRIu16",max_len=%"PRIu32")", > + colors.special, colors.end, a->port, a->max_len); > +} > + > > /* Meter instruction. */ > > @@ -5997,6 +6095,7 @@ ofpact_is_set_or_move_action(const struct ofpact *a) > case OFPACT_NOTE: > case OFPACT_OUTPUT: > case OFPACT_OUTPUT_REG: > + case OFPACT_OUTPUT_TRUNC: > case OFPACT_POP_MPLS: > case OFPACT_POP_QUEUE: > case OFPACT_PUSH_MPLS: > @@ -6025,6 +6124,7 @@ ofpact_is_allowed_in_actions_set(const struct ofpact *a) > case OFPACT_DEC_TTL: > case OFPACT_GROUP: > case OFPACT_OUTPUT: > + case OFPACT_OUTPUT_TRUNC: > case OFPACT_POP_MPLS: > case OFPACT_PUSH_MPLS: > case OFPACT_PUSH_VLAN: > @@ -6249,6 +6349,7 @@ ovs_instruction_type_from_ofpact_type(enum ofpact_type > type) > case OFPACT_CONTROLLER: > case OFPACT_ENQUEUE: > case OFPACT_OUTPUT_REG: > + case OFPACT_OUTPUT_TRUNC: > case OFPACT_BUNDLE: > case OFPACT_SET_VLAN_VID: > case OFPACT_SET_VLAN_PCP: > @@ -6677,6 +6778,10 @@ ofpact_check__(enum ofputil_protocol > *usable_protocols, struct ofpact *a, > case OFPACT_OUTPUT_REG: > return mf_check_src(&ofpact_get_OUTPUT_REG(a)->src, flow); > > + case OFPACT_OUTPUT_TRUNC: > + return ofpact_check_output_port(ofpact_get_OUTPUT_TRUNC(a)->port, > + max_ports); > + > case OFPACT_BUNDLE: > return bundle_check(ofpact_get_BUNDLE(a), max_ports, flow); > > @@ -7354,6 +7459,7 @@ ofpact_outputs_to_port(const struct ofpact *ofpact, > ofp_port_t port) > return port == OFPP_CONTROLLER; > > case OFPACT_OUTPUT_REG: > + case OFPACT_OUTPUT_TRUNC: > case OFPACT_BUNDLE: > case OFPACT_SET_VLAN_VID: > case OFPACT_SET_VLAN_PCP: > diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c > index fbc82b7..5d26b7c 100644 > --- a/ofproto/ofproto-dpif-sflow.c > +++ b/ofproto/ofproto-dpif-sflow.c > @@ -1140,6 +1140,7 @@ dpif_sflow_read_actions(const struct flow *flow, > } > break; > > + case OVS_ACTION_ATTR_TRUNC: > case OVS_ACTION_ATTR_USERSPACE: > case OVS_ACTION_ATTR_RECIRC: > case OVS_ACTION_ATTR_HASH: > diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c > index 6013831..078c286 100644 > --- a/ofproto/ofproto-dpif-xlate.c > +++ b/ofproto/ofproto-dpif-xlate.c > @@ -3995,6 +3995,56 @@ xlate_output_reg_action(struct xlate_ctx *ctx, > } > > static void > +xlate_output_trunc_action(struct xlate_ctx *ctx, > + ofp_port_t port, uint32_t max_len) > +{ > + bool support_trunc = ctx->xbridge->support.trunc; > + struct ovs_action_trunc *trunc; > + char name[OFP_MAX_PORT_NAME_LEN]; > + > + switch (port) { > + case OFPP_TABLE: > + case OFPP_NORMAL: > + case OFPP_FLOOD: > + case OFPP_ALL: > + case OFPP_CONTROLLER: > + case OFPP_NONE: > + ofputil_port_to_string(port, name, sizeof name); > + xlate_report(ctx, "output_trunc does not support port: %s", name); > + break; > + case OFPP_LOCAL: > + case OFPP_IN_PORT: > + default: > + if (port != ctx->xin->flow.in_port.ofp_port) { > + const struct xport *xport = get_ofp_port(ctx->xbridge, port); > + > + if (xport == NULL || xport->odp_port == ODPP_NONE) { > + /* Since truncate happens at its following output action, if > + * the output port is a patch port, the behavior is somehow > + * unpredicable. For simpilicity, disallow this case. */ > + ofputil_port_to_string(port, name, sizeof name); > + XLATE_REPORT_ERROR(ctx, "bridge %s: " > + "output_trunc does not support port: %s", > + ctx->xbridge->name, name); > + break; > + } > + > + trunc = nl_msg_put_unspec_uninit(ctx->odp_actions, > + OVS_ACTION_ATTR_TRUNC, > + sizeof *trunc); > + trunc->max_len = max_len; > + xlate_output_action(ctx, port, max_len, false); > + if (!support_trunc) { > + ctx->xout->slow |= SLOW_ACTION; > + } > + } else { > + xlate_report(ctx, "skipping output to input port"); > + } > + break; > + } > +} > + > +static void > xlate_enqueue_action(struct xlate_ctx *ctx, > const struct ofpact_enqueue *enqueue) > { > @@ -4333,6 +4383,7 @@ freeze_unroll_actions(const struct ofpact *a, const > struct ofpact *end, > for (; a < end; a = ofpact_next(a)) { > switch (a->type) { > case OFPACT_OUTPUT_REG: > + case OFPACT_OUTPUT_TRUNC: > case OFPACT_GROUP: > case OFPACT_OUTPUT: > case OFPACT_CONTROLLER: > @@ -4584,6 +4635,7 @@ recirc_for_mpls(const struct ofpact *a, struct > xlate_ctx *ctx) > > /* Output actions do not require recirculation. */ > case OFPACT_OUTPUT: > + case OFPACT_OUTPUT_TRUNC: > case OFPACT_ENQUEUE: > case OFPACT_OUTPUT_REG: > /* Set actions that don't touch L3+ fields do not require recirculation. > */ > @@ -4933,6 +4985,11 @@ do_xlate_actions(const struct ofpact *ofpacts, size_t > ofpacts_len, > xlate_output_reg_action(ctx, ofpact_get_OUTPUT_REG(a)); > break; > > + case OFPACT_OUTPUT_TRUNC: > + xlate_output_trunc_action(ctx, ofpact_get_OUTPUT_TRUNC(a)->port, > + ofpact_get_OUTPUT_TRUNC(a)->max_len); > + break; > + > case OFPACT_LEARN: > xlate_learn_action(ctx, ofpact_get_LEARN(a)); > break; > diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c > index 8a3c525..8c19f91 100644 > --- a/ofproto/ofproto-dpif.c > +++ b/ofproto/ofproto-dpif.c > @@ -1212,6 +1212,63 @@ check_masked_set_action(struct dpif_backer *backer) > return !error; > } > > +/* Tests whether 'backer''s datapath supports truncation of a packet in > + * OVS_ACTION_ATTR_TRUNC. We need to disable some features on older > + * datapaths that don't support this feature. */ > +static bool > +check_trunc_action(struct dpif_backer *backer) > +{ > + struct eth_header *eth; > + struct ofpbuf actions; > + struct dpif_execute execute; > + struct dp_packet packet; > + struct ovs_action_trunc *trunc; > + struct flow flow; > + int error; > + > + /* Compose an action with output(port:1, > + * max_len:OVS_ACTION_OUTPUT_MIN + 1). > + * This translates to one truncate action and one output action. */ > + ofpbuf_init(&actions, 64); > + trunc = nl_msg_put_unspec_uninit(&actions, > + OVS_ACTION_ATTR_TRUNC, sizeof *trunc); > + > + trunc->max_len = ETH_HEADER_LEN + 1; > + nl_msg_put_odp_port(&actions, OVS_ACTION_ATTR_OUTPUT, u32_to_odp(1)); > + > + /* Compose a dummy Ethernet packet. */ > + dp_packet_init(&packet, ETH_HEADER_LEN); > + eth = dp_packet_put_zeros(&packet, ETH_HEADER_LEN); > + eth->eth_type = htons(0x1234); > + > + flow_extract(&packet, &flow); > + > + /* Execute the actions. On older datapaths this fails with EINVAL, on > + * newer datapaths it succeeds. */ > + execute.actions = actions.data; > + execute.actions_len = actions.size; > + execute.packet = &packet; > + execute.flow = &flow; > + execute.needs_help = false; > + execute.probe = true; > + execute.mtu = 0; > + > + error = dpif_execute(backer->dpif, &execute); > + > + dp_packet_uninit(&packet); > + ofpbuf_uninit(&actions); > + > + if (error) { > + VLOG_INFO("%s: Datapath does not support truncate action", > + dpif_name(backer->dpif)); > + } else { > + VLOG_INFO("%s: Datapath supports truncate action", > + dpif_name(backer->dpif)); > + } > + > + return !error; > +} > + > #define CHECK_FEATURE__(NAME, SUPPORT, FIELD, VALUE) \ > static bool \ > check_##NAME(struct dpif_backer *backer) \ > @@ -1263,6 +1320,7 @@ check_support(struct dpif_backer *backer) > backer->support.odp.recirc = check_recirc(backer); > backer->support.odp.max_mpls_depth = check_max_mpls_depth(backer); > backer->support.masked_set_action = check_masked_set_action(backer); > + backer->support.trunc = check_trunc_action(backer); > backer->support.ufid = check_ufid(backer); > backer->support.tnl_push_pop = dpif_supports_tnl_push_pop(backer->dpif); > > diff --git a/ofproto/ofproto-dpif.h b/ofproto/ofproto-dpif.h > index 9e03b01..4034475 100644 > --- a/ofproto/ofproto-dpif.h > +++ b/ofproto/ofproto-dpif.h > @@ -90,6 +90,9 @@ struct dpif_backer_support { > /* True if the datapath supports OVS_FLOW_ATTR_UFID. */ > bool ufid; > > + /* True if the datapath supports OVS_ACTION_ATTR_TRUNC action. */ > + bool trunc; > + > /* Each member represents support for related OVS_KEY_ATTR_* fields. */ > struct odp_support odp; > }; > diff --git a/tests/odp.at b/tests/odp.at > index 7b94c92..e630855 100644 > --- a/tests/odp.at > +++ b/tests/odp.at ... > AT_SETUP([conntrack - controller]) > CHECK_CONNTRACK() > OVS_TRAFFIC_VSWITCHD_START() > -- I really like number of tests you have added. Can you also add test to validate SLOW_ACTION case of truncate? You can add ovs-appctl command to disable datapath truncate support to simulate the case. _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev