On 29.08.2019 12:21, Yanqin Wei (Arm Technology China) wrote:
> Hi Ben,
> Thanks for the feedback. It is indeed related with userspace datapath.
>
> Hi Ilya,
> Could you take a look at this patch when you have time?>
> I knew first-pass and recirculating traffic share the same packet handling.
> It makes code common and maintainable.
> But if we can introduce some data-oriented and well-defined flag to bypass
> some time-consuming handling, it can improve performance a lot.
Hi. I had a quick look at the patch.
Few thoughts:
* 'md' is actually always valid inside 'miniflow_extract', so the
variable 'md_valid' should be renamed to not be misleading.
Maybe something like 'md_is_full'? I'm not sure about the name.
* How much is the performance benefit of the compiler code stripping?
I mean, what is the difference between direct call
miniflow_extract__(packet, dst, md_is_valid);
where 'md_is_valid == false' and the call to
miniflow_extract_firstpass(packet, dst);
?
Asking because this complicates dfc_processing() function.
I'm, actually have a patch locally to combine rss_hash
calculation functions to reduce code duplication, so I'm trying
to figure out what are the possibilities here.
Best regards, Ilya Maximets.
>
> Best Regards,
> Wei Yanqin
>
> -----Original Message-----
> From: Ben Pfaff <[email protected]>
> Sent: Thursday, August 29, 2019 6:11 AM
> To: Yanqin Wei (Arm Technology China) <[email protected]>; Ilya Maximets
> <[email protected]>
> Cc: [email protected]; nd <[email protected]>; Gavin Hu (Arm Technology China)
> <[email protected]>
> Subject: Re: [ovs-dev] [PATCH v2] flow: miniflow_extract metadata branchless
> optimization
>
> This fails to apply to current master.
>
> Whereas most of the time I'd be comfortable with reviewing 'flow'
> patches myself, this one is closed related to the dpif-netdev code and I'd
> prefer to have someone who understands that code well, as well as the
> tradeoffs between performance and maintainability, review it. Ilya (added to
> the To line) is a good choice.
>
> On Thu, Aug 22, 2019 at 06:09:16PM +0800, Yanqin Wei wrote:
>> "miniflow_extract" is a branch heavy implementation for packet header
>> and metadata parsing. There is a lot of meta data handling for all traffic.
>> But this should not be applicable for packets from interface.
>> This patch adds a layer of inline encapsulation to miniflow_extract
>> and introduces constant "md_valid" input parameter as a branch condition.
>> The new branch will be removed by the compiler at compile time. Two
>> instances of miniflow_extract with different branches will be generated.
>>
>> This patch is tested on an arm64 platform. It improves more than 3.5%
>> performance in P2P forwarding cases.
>>
>> Reviewed-by: Gavin Hu <[email protected]>
>> Signed-off-by: Yanqin Wei <[email protected]>
>> ---
>> lib/dpif-netdev.c | 13 +++---
>> lib/flow.c | 116
>> ++++++++++++++++++++++++++++++++----------------------
>> lib/flow.h | 2 +
>> 3 files changed, 79 insertions(+), 52 deletions(-)
>>
>> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index
>> d0a1c58..6686b14 100644
>> --- a/lib/dpif-netdev.c
>> +++ b/lib/dpif-netdev.c
>> @@ -6508,12 +6508,15 @@ dfc_processing(struct dp_netdev_pmd_thread *pmd,
>> }
>> }
>>
>> - miniflow_extract(packet, &key->mf);
>> + if (!md_is_valid) {
>> + miniflow_extract_firstpass(packet, &key->mf);
>> + key->hash =
>> + dpif_netdev_packet_get_rss_hash_orig_pkt(packet, &key->mf);
>> + } else {
>> + miniflow_extract(packet, &key->mf);
>> + key->hash = dpif_netdev_packet_get_rss_hash(packet, &key->mf);
>> + }
>> key->len = 0; /* Not computed yet. */
>> - key->hash =
>> - (md_is_valid == false)
>> - ? dpif_netdev_packet_get_rss_hash_orig_pkt(packet, &key->mf)
>> - : dpif_netdev_packet_get_rss_hash(packet, &key->mf);
>>
>> /* If EMC is disabled skip emc_lookup */
>> flow = (cur_min != 0) ? emc_lookup(&cache->emc_cache, key) :
>> NULL; diff --git a/lib/flow.c b/lib/flow.c index e54fd2e..e5b554b
>> 100644
>> --- a/lib/flow.c
>> +++ b/lib/flow.c
>> @@ -707,7 +707,8 @@ ipv6_sanity_check(const struct
>> ovs_16aligned_ip6_hdr *nh, size_t size) }
>>
>> /* Initializes 'dst' from 'packet' and 'md', taking the packet type
>> into
>> - * account. 'dst' must have enough space for FLOW_U64S * 8 bytes.
>> + * account. 'dst' must have enough space for FLOW_U64S * 8 bytes.
>> + Metadata
>> + * initialization should be bypassed if "md_valid" is false.
>> *
>> * Initializes the layer offsets as follows:
>> *
>> @@ -732,8 +733,9 @@ ipv6_sanity_check(const struct ovs_16aligned_ip6_hdr
>> *nh, size_t size)
>> * present and the packet has at least the content used for the fields
>> * of interest for the flow, otherwise UINT16_MAX.
>> */
>> -void
>> -miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
>> +static inline ALWAYS_INLINE void
>> +miniflow_extract__(struct dp_packet *packet, struct miniflow *dst,
>> + const bool md_valid)
>> {
>> /* Add code to this function (or its callees) to extract new fields. */
>> BUILD_ASSERT_DECL(FLOW_WC_SEQ == 41); @@ -752,54 +754,60 @@
>> miniflow_extract(struct dp_packet *packet, struct miniflow *dst)
>> ovs_be16 ct_tp_src = 0, ct_tp_dst = 0;
>>
>> /* Metadata. */
>> - if (flow_tnl_dst_is_set(&md->tunnel)) {
>> - miniflow_push_words(mf, tunnel, &md->tunnel,
>> - offsetof(struct flow_tnl, metadata) /
>> - sizeof(uint64_t));
>> -
>> - if (!(md->tunnel.flags & FLOW_TNL_F_UDPIF)) {
>> - if (md->tunnel.metadata.present.map) {
>> - miniflow_push_words(mf, tunnel.metadata,
>> &md->tunnel.metadata,
>> - sizeof md->tunnel.metadata /
>> - sizeof(uint64_t));
>> - }
>> - } else {
>> - if (md->tunnel.metadata.present.len) {
>> - miniflow_push_words(mf, tunnel.metadata.present,
>> - &md->tunnel.metadata.present, 1);
>> - miniflow_push_words(mf, tunnel.metadata.opts.gnv,
>> - md->tunnel.metadata.opts.gnv,
>> -
>> DIV_ROUND_UP(md->tunnel.metadata.present.len,
>> - sizeof(uint64_t)));
>> + if (md_valid) {
>> + if (flow_tnl_dst_is_set(&md->tunnel)) {
>> + miniflow_push_words(mf, tunnel, &md->tunnel,
>> + offsetof(struct flow_tnl, metadata) /
>> + sizeof(uint64_t));
>> +
>> + if (!(md->tunnel.flags & FLOW_TNL_F_UDPIF)) {
>> + if (md->tunnel.metadata.present.map) {
>> + miniflow_push_words(mf, tunnel.metadata,
>> + &md->tunnel.metadata,
>> + sizeof md->tunnel.metadata /
>> + sizeof(uint64_t));
>> + }
>> + } else {
>> + if (md->tunnel.metadata.present.len) {
>> + miniflow_push_words(mf, tunnel.metadata.present,
>> + &md->tunnel.metadata.present, 1);
>> + miniflow_push_words(mf, tunnel.metadata.opts.gnv,
>> + md->tunnel.metadata.opts.gnv,
>> + DIV_ROUND_UP(
>> +
>> md->tunnel.metadata.present.len,
>> + sizeof(uint64_t)));
>> + }
>> }
>> }
>> - }
>> - if (md->skb_priority || md->pkt_mark) {
>> - miniflow_push_uint32(mf, skb_priority, md->skb_priority);
>> - miniflow_push_uint32(mf, pkt_mark, md->pkt_mark);
>> - }
>> - miniflow_push_uint32(mf, dp_hash, md->dp_hash);
>> - miniflow_push_uint32(mf, in_port, odp_to_u32(md->in_port.odp_port));
>> - if (md->ct_state) {
>> - miniflow_push_uint32(mf, recirc_id, md->recirc_id);
>> - miniflow_push_uint8(mf, ct_state, md->ct_state);
>> - ct_nw_proto_p = miniflow_pointer(mf, ct_nw_proto);
>> - miniflow_push_uint8(mf, ct_nw_proto, 0);
>> - miniflow_push_uint16(mf, ct_zone, md->ct_zone);
>> - } else if (md->recirc_id) {
>> - miniflow_push_uint32(mf, recirc_id, md->recirc_id);
>> - miniflow_pad_to_64(mf, recirc_id);
>> - }
>> -
>> - if (md->ct_state) {
>> - miniflow_push_uint32(mf, ct_mark, md->ct_mark);
>> - miniflow_push_be32(mf, packet_type, packet_type);
>> -
>> - if (!ovs_u128_is_zero(md->ct_label)) {
>> - miniflow_push_words(mf, ct_label, &md->ct_label,
>> - sizeof md->ct_label / sizeof(uint64_t));
>> + if (md->skb_priority || md->pkt_mark) {
>> + miniflow_push_uint32(mf, skb_priority, md->skb_priority);
>> + miniflow_push_uint32(mf, pkt_mark, md->pkt_mark);
>> + }
>> + miniflow_push_uint32(mf, dp_hash, md->dp_hash);
>> + miniflow_push_uint32(mf, in_port, odp_to_u32(md->in_port.odp_port));
>> + if (md->ct_state) {
>> + miniflow_push_uint32(mf, recirc_id, md->recirc_id);
>> + miniflow_push_uint8(mf, ct_state, md->ct_state);
>> + ct_nw_proto_p = miniflow_pointer(mf, ct_nw_proto);
>> + miniflow_push_uint8(mf, ct_nw_proto, 0);
>> + miniflow_push_uint16(mf, ct_zone, md->ct_zone);
>> + } else if (md->recirc_id) {
>> + miniflow_push_uint32(mf, recirc_id, md->recirc_id);
>> + miniflow_pad_to_64(mf, recirc_id);
>> + }
>> +
>> + if (md->ct_state) {
>> + miniflow_push_uint32(mf, ct_mark, md->ct_mark);
>> + miniflow_push_be32(mf, packet_type, packet_type);
>> +
>> + if (!ovs_u128_is_zero(md->ct_label)) {
>> + miniflow_push_words(mf, ct_label, &md->ct_label,
>> + sizeof md->ct_label / sizeof(uint64_t));
>> + }
>> }
>> } else {
>> + miniflow_push_uint32(mf, dp_hash, md->dp_hash);
>> + miniflow_push_uint32(mf, in_port,
>> + odp_to_u32(md->in_port.odp_port));
>> miniflow_pad_from_64(mf, packet_type);
>> miniflow_push_be32(mf, packet_type, packet_type);
>> }
>> @@ -865,6 +873,7 @@ miniflow_extract(struct dp_packet *packet, struct
>> miniflow *dst)
>>
>> /* Push both source and destination address at once. */
>> miniflow_push_words(mf, nw_src, &nh->ip_src, 1);
>> +
>> if (ct_nw_proto_p && !md->ct_orig_tuple_ipv6) {
>> *ct_nw_proto_p = md->ct_orig_tuple.ipv4.ipv4_proto;
>> if (*ct_nw_proto_p) {
>> @@ -900,6 +909,7 @@ miniflow_extract(struct dp_packet *packet, struct
>> miniflow *dst)
>> sizeof nh->ip6_src / 8);
>> miniflow_push_words(mf, ipv6_dst, &nh->ip6_dst,
>> sizeof nh->ip6_dst / 8);
>> +
>> if (ct_nw_proto_p && md->ct_orig_tuple_ipv6) {
>> *ct_nw_proto_p = md->ct_orig_tuple.ipv6.ipv6_proto;
>> if (*ct_nw_proto_p) {
>> @@ -1076,6 +1086,18 @@ miniflow_extract(struct dp_packet *packet, struct
>> miniflow *dst)
>> dst->map = mf.map;
>> }
>>
>> +void
>> +miniflow_extract(struct dp_packet *packet, struct miniflow *dst) {
>> + miniflow_extract__(packet, dst, true); }
>> +
>> +void
>> +miniflow_extract_firstpass(struct dp_packet *packet, struct miniflow
>> +*dst) {
>> + miniflow_extract__(packet, dst, false); }
>> +
>> ovs_be16
>> parse_dl_type(const struct eth_header *data_, size_t size) { diff
>> --git a/lib/flow.h b/lib/flow.h index 7298c71..2d38574 100644
>> --- a/lib/flow.h
>> +++ b/lib/flow.h
>> @@ -541,6 +541,8 @@ struct pkt_metadata;
>> * 'dst->map' is ignored on input and set on output to indicate which fields
>> * were extracted. */
>> void miniflow_extract(struct dp_packet *packet, struct miniflow
>> *dst);
>> +void miniflow_extract_firstpass(struct dp_packet *packet,
>> + struct miniflow *dst);
>> void miniflow_map_init(struct miniflow *, const struct flow *); void
>> flow_wc_map(const struct flow *, struct flowmap *); size_t
>> miniflow_alloc(struct miniflow *dsts[], size_t n,
>> --
>> 2.7.4
>>
>> _______________________________________________
>> dev mailing list
>> [email protected]
>> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev