diff mbox series

[ovs-dev,v8] odp-execute: Add ISA implementation of set_masked IPv6 action

Message ID 20221208160123.1923483-1-emma.finn@intel.com
State Accepted
Headers show
Series [ovs-dev,v8] odp-execute: Add ISA implementation of set_masked IPv6 action | expand

Checks

Context Check Description
ovsrobot/apply-robot success apply and check: success
ovsrobot/github-robot-_Build_and_Test success github build: passed
ovsrobot/intel-ovs-compilation fail test: fail

Commit Message

Finn, Emma Dec. 8, 2022, 4:01 p.m. UTC
This commit adds support for the AVX512 implementation of the
ipv6_set_addrs action as well as an AVX512 implementation of
updating the L4 checksums.

Here are some relative performance numbers for this patch:
+-----------------------------+----------------+
| Actions                     | AVX with patch |
+-----------------------------+----------------+
| ipv6_src                    | 1.14x          |
+-----------------------------+----------------+
| ipv6_src + ipv6_dst         | 1.40x          |
+-----------------------------+----------------+
| ipv6_label                  | 1.14x          |
+-----------------------------+----------------+
| mod_ipv6 4 x field          | 1.43x          |
+-----------------------------+----------------+

Signed-off-by: Emma Finn <emma.finn@intel.com>

---
v8:
  - Added check for L4 header length.
v7:
  - Added clearing of connection tracking fields.
v6:
  - Added check for ipv6 extension headers.
v5:
  - Fixed load for ip6 src and dst mask for checksum check.
v4:
  - Reworked and moved check for checksum outside loop.
  - Code cleanup based on review from Eelco.
v3:
  - Added a runtime check for AVX512 vbmi.
v2:
  - Added check for availbility of s6_addr32 field of struct in6_addr.
  - Fixed network headers for freebsd builds.
---
---
 lib/odp-execute-avx512.c  | 222 ++++++++++++++++++++++++++++++++++++++
 lib/odp-execute-private.c |  14 +++
 lib/odp-execute-private.h |   1 +
 lib/packets.c             |   2 +-
 lib/packets.h             |   2 +
 5 files changed, 240 insertions(+), 1 deletion(-)

Comments

Eelco Chaudron Dec. 14, 2022, 11:17 a.m. UTC | #1
On 8 Dec 2022, at 17:01, Emma Finn wrote:

> This commit adds support for the AVX512 implementation of the
> ipv6_set_addrs action as well as an AVX512 implementation of
> updating the L4 checksums.
>
> Here are some relative performance numbers for this patch:
> +-----------------------------+----------------+
> | Actions                     | AVX with patch |
> +-----------------------------+----------------+
> | ipv6_src                    | 1.14x          |
> +-----------------------------+----------------+
> | ipv6_src + ipv6_dst         | 1.40x          |
> +-----------------------------+----------------+
> | ipv6_label                  | 1.14x          |
> +-----------------------------+----------------+
> | mod_ipv6 4 x field          | 1.43x          |
> +-----------------------------+----------------+
>
> Signed-off-by: Emma Finn <emma.finn@intel.com>
>
> ---
> v8:
>   - Added check for L4 header length.
> v7:
>   - Added clearing of connection tracking fields.
> v6:
>   - Added check for ipv6 extension headers.
> v5:
>   - Fixed load for ip6 src and dst mask for checksum check.
> v4:
>   - Reworked and moved check for checksum outside loop.
>   - Code cleanup based on review from Eelco.
> v3:
>   - Added a runtime check for AVX512 vbmi.
> v2:
>   - Added check for availbility of s6_addr32 field of struct in6_addr.
>   - Fixed network headers for freebsd builds.
> ---

Thanks for following this through! The changes look good to me, and found an AVX machine to run some tests :)

Acked-by: Eelco Chaudron <echaudro@redhat.com>
Phelan, Michael Dec. 15, 2022, 2:09 p.m. UTC | #2
> -----Original Message-----
> From: dev <ovs-dev-bounces@openvswitch.org> On Behalf Of Eelco Chaudron
> Sent: Wednesday 14 December 2022 11:17
> To: Finn, Emma <emma.finn@intel.com>
> Cc: dev@openvswitch.org; i.maximets@ovn.org
> Subject: Re: [ovs-dev] [v8] odp-execute: Add ISA implementation of set_masked
> IPv6 action
> 
> 
> 
> On 8 Dec 2022, at 17:01, Emma Finn wrote:
> 
> > This commit adds support for the AVX512 implementation of the
> > ipv6_set_addrs action as well as an AVX512 implementation of updating
> > the L4 checksums.
> >
> > Here are some relative performance numbers for this patch:
> > +-----------------------------+----------------+
> > | Actions                     | AVX with patch |
> > +-----------------------------+----------------+
> > | ipv6_src                    | 1.14x          |
> > +-----------------------------+----------------+
> > | ipv6_src + ipv6_dst         | 1.40x          |
> > +-----------------------------+----------------+
> > | ipv6_label                  | 1.14x          |
> > +-----------------------------+----------------+
> > | mod_ipv6 4 x field          | 1.43x          |
> > +-----------------------------+----------------+
> >
> > Signed-off-by: Emma Finn <emma.finn@intel.com>
> >
> > ---
> > v8:
> >   - Added check for L4 header length.
> > v7:
> >   - Added clearing of connection tracking fields.
> > v6:
> >   - Added check for ipv6 extension headers.
> > v5:
> >   - Fixed load for ip6 src and dst mask for checksum check.
> > v4:
> >   - Reworked and moved check for checksum outside loop.
> >   - Code cleanup based on review from Eelco.
> > v3:
> >   - Added a runtime check for AVX512 vbmi.
> > v2:
> >   - Added check for availbility of s6_addr32 field of struct in6_addr.
> >   - Fixed network headers for freebsd builds.
> > ---
> 
> Thanks for following this through! The changes look good to me, and found an
> AVX machine to run some tests :)
> 
> Acked-by: Eelco Chaudron <echaudro@redhat.com>
Hi Emma,
Saw some failing tests on the Intel CI, this was related to an issue on our side where the NICs were unbound from DPDK resulting in failures on some of the OVS DPDK unit tests testing physical ports.

I reran the failed tests and they were all fine so the failures weren't patch related.

Thanks,
Michael.
> 
> _______________________________________________
> dev mailing list
> dev@openvswitch.org
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
Stokes, Ian Dec. 21, 2022, 2:51 p.m. UTC | #3
> > -----Original Message-----
> > From: dev <ovs-dev-bounces@openvswitch.org> On Behalf Of Eelco Chaudron
> > Sent: Wednesday 14 December 2022 11:17
> > To: Finn, Emma <emma.finn@intel.com>
> > Cc: dev@openvswitch.org; i.maximets@ovn.org
> > Subject: Re: [ovs-dev] [v8] odp-execute: Add ISA implementation of
> set_masked
> > IPv6 action
> >
> >
> >
> > On 8 Dec 2022, at 17:01, Emma Finn wrote:
> >
> > > This commit adds support for the AVX512 implementation of the
> > > ipv6_set_addrs action as well as an AVX512 implementation of updating
> > > the L4 checksums.
> > >
> > > Here are some relative performance numbers for this patch:
> > > +-----------------------------+----------------+
> > > | Actions                     | AVX with patch |
> > > +-----------------------------+----------------+
> > > | ipv6_src                    | 1.14x          |
> > > +-----------------------------+----------------+
> > > | ipv6_src + ipv6_dst         | 1.40x          |
> > > +-----------------------------+----------------+
> > > | ipv6_label                  | 1.14x          |
> > > +-----------------------------+----------------+
> > > | mod_ipv6 4 x field          | 1.43x          |
> > > +-----------------------------+----------------+
> > >
> > > Signed-off-by: Emma Finn <emma.finn@intel.com>
> > >
> > > ---
> > > v8:
> > >   - Added check for L4 header length.
> > > v7:
> > >   - Added clearing of connection tracking fields.
> > > v6:
> > >   - Added check for ipv6 extension headers.
> > > v5:
> > >   - Fixed load for ip6 src and dst mask for checksum check.
> > > v4:
> > >   - Reworked and moved check for checksum outside loop.
> > >   - Code cleanup based on review from Eelco.
> > > v3:
> > >   - Added a runtime check for AVX512 vbmi.
> > > v2:
> > >   - Added check for availbility of s6_addr32 field of struct in6_addr.
> > >   - Fixed network headers for freebsd builds.
> > > ---
> >
> > Thanks for following this through! The changes look good to me, and found an
> > AVX machine to run some tests :)
> >
> > Acked-by: Eelco Chaudron <echaudro@redhat.com>
> Hi Emma,
> Saw some failing tests on the Intel CI, this was related to an issue on our side
> where the NICs were unbound from DPDK resulting in failures on some of the
> OVS DPDK unit tests testing physical ports.
> 
> I reran the failed tests and they were all fine so the failures weren't patch
> related.
> 
> Thanks,
> Michael.

Thanks Eelco for all the reviewing/testing and Michael for checking the CI issue, given that this is now acked and validated I've pushed to master.

Thanks
Ian
diff mbox series

Patch

diff --git a/lib/odp-execute-avx512.c b/lib/odp-execute-avx512.c
index 66b3998da..ac5c1c63d 100644
--- a/lib/odp-execute-avx512.c
+++ b/lib/odp-execute-avx512.c
@@ -20,6 +20,9 @@ 
 
 #include <config.h>
 #include <errno.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
 
 #include "csum.h"
 #include "dp-packet.h"
@@ -28,6 +31,7 @@ 
 #include "odp-execute-private.h"
 #include "odp-netlink.h"
 #include "openvswitch/vlog.h"
+#include "packets.h"
 
 VLOG_DEFINE_THIS_MODULE(odp_execute_avx512);
 
@@ -75,6 +79,26 @@  BUILD_ASSERT_DECL(offsetof(struct ovs_key_ipv4, ipv4_tos) +
                   MEMBER_SIZEOF(struct ovs_key_ipv4, ipv4_tos) ==
                   offsetof(struct ovs_key_ipv4, ipv4_ttl));
 
+BUILD_ASSERT_DECL(offsetof(struct ovs_key_ipv6, ipv6_src) +
+                  MEMBER_SIZEOF(struct ovs_key_ipv6, ipv6_src) ==
+                  offsetof(struct ovs_key_ipv6, ipv6_dst));
+
+BUILD_ASSERT_DECL(offsetof(struct ovs_key_ipv6, ipv6_dst) +
+                  MEMBER_SIZEOF(struct ovs_key_ipv6, ipv6_dst) ==
+                  offsetof(struct ovs_key_ipv6, ipv6_label));
+
+BUILD_ASSERT_DECL(offsetof(struct ovs_key_ipv6, ipv6_label) +
+                  MEMBER_SIZEOF(struct ovs_key_ipv6, ipv6_label) ==
+                  offsetof(struct ovs_key_ipv6, ipv6_proto));
+
+BUILD_ASSERT_DECL(offsetof(struct ovs_key_ipv6, ipv6_proto) +
+                  MEMBER_SIZEOF(struct ovs_key_ipv6, ipv6_proto) ==
+                  offsetof(struct ovs_key_ipv6, ipv6_tclass));
+
+BUILD_ASSERT_DECL(offsetof(struct ovs_key_ipv6, ipv6_tclass) +
+                  MEMBER_SIZEOF(struct ovs_key_ipv6, ipv6_tclass) ==
+                  offsetof(struct ovs_key_ipv6, ipv6_hlimit));
+
 /* Array of callback functions, one for each masked operation. */
 odp_execute_action_cb impl_set_masked_funcs[__OVS_KEY_ATTR_MAX];
 
@@ -485,6 +509,198 @@  action_avx512_ipv4_set_addrs(struct dp_packet_batch *batch,
     }
 }
 
+#if HAVE_AVX512VBMI
+static inline uint16_t ALWAYS_INLINE
+__attribute__((__target__("avx512vbmi")))
+avx512_ipv6_sum_header(__m512i ip6_header)
+{
+    __m256i v_zeros = _mm256_setzero_si256();
+    __m512i v_shuf_src_dst = _mm512_setr_epi64(0x01, 0x02, 0x03, 0x04,
+                                               0xFF, 0xFF, 0xFF, 0xFF);
+
+    /* Shuffle ip6 src and dst to beginning of register. */
+    __m512i v_ip6_hdr_shuf = _mm512_permutexvar_epi64(v_shuf_src_dst,
+                                                      ip6_header);
+
+    /* Extract ip6 src and dst into smaller 256-bit wide register. */
+    __m256i v_ip6_src_dst = _mm512_extracti64x4_epi64(v_ip6_hdr_shuf, 0);
+
+    /* These two shuffle masks, v_swap16a and v_swap16b, are to shuffle the
+     * src and dst fields and add padding after each 16-bit value for the
+     * following carry over addition. */
+    __m256i v_swap16a = _mm256_setr_epi16(0x0100, 0xFFFF, 0x0302, 0xFFFF,
+                                          0x0504, 0xFFFF, 0x0706, 0xFFFF,
+                                          0x0100, 0xFFFF, 0x0302, 0xFFFF,
+                                          0x0504, 0xFFFF, 0x0706, 0xFFFF);
+    __m256i v_swap16b = _mm256_setr_epi16(0x0908, 0xFFFF, 0x0B0A, 0xFFFF,
+                                          0x0D0C, 0xFFFF, 0x0F0E, 0xFFFF,
+                                          0x0908, 0xFFFF, 0x0B0A, 0xFFFF,
+                                          0x0D0C, 0xFFFF, 0x0F0E, 0xFFFF);
+    __m256i v_shuf_old1 = _mm256_shuffle_epi8(v_ip6_src_dst, v_swap16a);
+    __m256i v_shuf_old2 = _mm256_shuffle_epi8(v_ip6_src_dst, v_swap16b);
+
+    /* Add each part of the old and new headers together. */
+    __m256i v_delta = _mm256_add_epi32(v_shuf_old1, v_shuf_old2);
+
+    /* Perform horizontal add to go from 8x32-bits to 2x32-bits. */
+    v_delta = _mm256_hadd_epi32(v_delta, v_zeros);
+    v_delta = _mm256_hadd_epi32(v_delta, v_zeros);
+
+    /* Shuffle 32-bit value from 3rd lane into first lane for final
+     * horizontal add. */
+    __m256i v_swap32a = _mm256_setr_epi32(0x0, 0x4, 0xF, 0xF,
+                                          0xF, 0xF, 0xF, 0xF);
+
+    v_delta = _mm256_permutexvar_epi32(v_swap32a, v_delta);
+    v_delta = _mm256_hadd_epi32(v_delta, v_zeros);
+    v_delta = _mm256_hadd_epi16(v_delta, v_zeros);
+
+    /* Extract delta value. */
+    return _mm256_extract_epi16(v_delta, 0);
+}
+
+static inline uint16_t ALWAYS_INLINE
+__attribute__((__target__("avx512vbmi")))
+avx512_ipv6_addr_csum_delta(__m512i old_header, __m512i new_header)
+{
+    uint16_t old_delta = avx512_ipv6_sum_header(old_header);
+    uint16_t new_delta = avx512_ipv6_sum_header(new_header);
+    uint32_t csum_delta = ((uint16_t) ~old_delta) + new_delta;
+
+    return ~csum_finish(csum_delta);
+}
+
+/* This function performs the same operation on each packet in the batch as
+ * the scalar odp_set_ipv6() function. */
+static void
+__attribute__((__target__("avx512vbmi")))
+action_avx512_set_ipv6(struct dp_packet_batch *batch, const struct nlattr *a)
+{
+    const struct ovs_key_ipv6 *key, *mask;
+    struct dp_packet *packet;
+
+    a = nl_attr_get(a);
+    key = nl_attr_get(a);
+    mask = odp_get_key_mask(a, struct ovs_key_ipv6);
+
+    /* Read the content of the key and mask in the respective registers. We
+     * only load the size of the actual structure, which is only 40 bytes. */
+    __m512i v_key = _mm512_maskz_loadu_epi64(0x1F, (void *) key);
+    __m512i v_mask = _mm512_maskz_loadu_epi64(0x1F, (void *) mask);
+
+    /* This shuffle mask v_shuffle, is to shuffle key and mask to match the
+     * ip6_hdr structure layout. */
+    static const uint8_t ip_shuffle_mask[64] = {
+        0x20, 0x21, 0x22, 0x23, 0xFF, 0xFF, 0x24, 0x26,
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
+        0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+        0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0XFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+        0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0XFF, 0xFF
+    };
+
+    __m512i v_shuffle = _mm512_loadu_si512((void *) ip_shuffle_mask);
+
+    /* This shuffle is required for key and mask to match the layout of the
+     * ip6_hdr struct. */
+    __m512i v_key_shuf = _mm512_permutexvar_epi8(v_shuffle, v_key);
+    __m512i v_mask_shuf = _mm512_permutexvar_epi8(v_shuffle, v_mask);
+
+    /* Set the v_zero register to all zero's. */
+    const __m128i v_zeros = _mm_setzero_si128();
+
+    /* Set the v_all_ones register to all one's. */
+    const __m128i v_all_ones = _mm_cmpeq_epi16(v_zeros, v_zeros);
+
+    /* Load ip6 src and dst masks respectively into 128-bit wide registers. */
+    __m128i v_src = _mm_loadu_si128((void *) &mask->ipv6_src);
+    __m128i v_dst = _mm_loadu_si128((void *) &mask->ipv6_dst);
+
+    /* Perform a bitwise OR between src and dst registers. */
+    __m128i v_or = _mm_or_si128(v_src, v_dst);
+
+    /* Will return true if any bit has been set in v_or, else it will return
+     * false. */
+    bool do_checksum = !_mm_test_all_zeros(v_or, v_all_ones);
+
+    DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
+        struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet);
+
+        /* Load the 40 bytes of the IPv6 header. */
+        __m512i v_packet = _mm512_maskz_loadu_epi64(0x1F, (void *) nh);
+
+        /* AND the v_pkt_mask to the packet data (v_packet). */
+        __m512i v_pkt_masked = _mm512_andnot_si512(v_mask_shuf, v_packet);
+
+        /* OR the new addresses (v_key_shuf) with the masked packet addresses
+         * (v_pkt_masked). */
+        __m512i v_new_hdr = _mm512_or_si512(v_key_shuf, v_pkt_masked);
+
+        /* If ip6_src or ip6_dst has been modified, L4 checksum needs to be
+         * updated. */
+        uint8_t proto = 0;
+        bool rh_present;
+        bool do_csum = do_checksum;
+
+        rh_present = packet_rh_present(packet, &proto, &do_csum);
+
+        if (do_csum) {
+            size_t l4_size = dp_packet_l4_size(packet);
+            __m512i v_new_hdr_for_cksum = v_new_hdr;
+            uint16_t delta_checksum;
+
+            /* In case of routing header being present, checksum should not be
+             * updated for the destination address. */
+            if (rh_present) {
+                v_new_hdr_for_cksum = _mm512_mask_blend_epi64(0x18, v_new_hdr,
+                                                              v_packet);
+            }
+
+            delta_checksum = avx512_ipv6_addr_csum_delta(v_packet,
+                                                         v_new_hdr_for_cksum);
+
+            if (proto == IPPROTO_UDP && l4_size >= UDP_HEADER_LEN) {
+                struct udp_header *uh = dp_packet_l4(packet);
+
+                if (uh->udp_csum) {
+                    uint16_t old_udp_checksum = ~uh->udp_csum;
+                    uint32_t udp_checksum = old_udp_checksum + delta_checksum;
+
+                    udp_checksum = csum_finish(udp_checksum);
+
+                    if (!udp_checksum) {
+                        udp_checksum = htons(0xffff);
+                    }
+
+                    uh->udp_csum = udp_checksum;
+                }
+            } else if (proto == IPPROTO_TCP && l4_size >= TCP_HEADER_LEN) {
+                struct tcp_header *th = dp_packet_l4(packet);
+                uint16_t old_tcp_checksum = ~th->tcp_csum;
+                uint32_t tcp_checksum = old_tcp_checksum + delta_checksum;
+
+                tcp_checksum = csum_finish(tcp_checksum);
+                th->tcp_csum = tcp_checksum;
+            } else if (proto == IPPROTO_ICMPV6 &&
+                       l4_size >= sizeof(struct icmp6_header)) {
+                struct icmp6_header *icmp = dp_packet_l4(packet);
+                uint16_t old_icmp6_checksum = ~icmp->icmp6_cksum;
+                uint32_t icmp6_checksum = old_icmp6_checksum + delta_checksum;
+
+                icmp6_checksum = csum_finish(icmp6_checksum);
+                icmp->icmp6_cksum = icmp6_checksum;
+            }
+
+            pkt_metadata_init_conn(&packet->md);
+        }
+        /* Write back the modified IPv6 addresses. */
+        _mm512_mask_storeu_epi64((void *) nh, 0x1F, v_new_hdr);
+    }
+}
+#endif /* HAVE_AVX512VBMI */
+
 static void
 action_avx512_set_masked(struct dp_packet_batch *batch, const struct nlattr *a)
 {
@@ -516,6 +732,12 @@  action_avx512_init(struct odp_execute_action_impl *self OVS_UNUSED)
     impl_set_masked_funcs[OVS_KEY_ATTR_ETHERNET] = action_avx512_eth_set_addrs;
     impl_set_masked_funcs[OVS_KEY_ATTR_IPV4] = action_avx512_ipv4_set_addrs;
 
+#if HAVE_AVX512VBMI
+    if (action_avx512vbmi_isa_probe()) {
+        impl_set_masked_funcs[OVS_KEY_ATTR_IPV6] = action_avx512_set_ipv6;
+    }
+#endif
+
     return 0;
 }
 
diff --git a/lib/odp-execute-private.c b/lib/odp-execute-private.c
index 57be5cfe7..8b7a6b4ab 100644
--- a/lib/odp-execute-private.c
+++ b/lib/odp-execute-private.c
@@ -60,6 +60,20 @@  action_avx512_isa_probe(void)
 
 #endif
 
+#if ACTION_IMPL_AVX512_CHECK && HAVE_AVX512VBMI
+bool
+action_avx512vbmi_isa_probe(void)
+{
+    return cpu_has_isa(OVS_CPU_ISA_X86_AVX512VBMI);
+}
+#else
+bool
+action_avx512vbmi_isa_probe(void)
+{
+    return false;
+}
+#endif
+
 static struct odp_execute_action_impl action_impls[] = {
     [ACTION_IMPL_AUTOVALIDATOR] = {
         .available = false,
diff --git a/lib/odp-execute-private.h b/lib/odp-execute-private.h
index 940180c99..643f41c2a 100644
--- a/lib/odp-execute-private.h
+++ b/lib/odp-execute-private.h
@@ -78,6 +78,7 @@  BUILD_ASSERT_DECL(ACTION_IMPL_AUTOVALIDATOR == 1);
 #define ACTION_IMPL_BEGIN (ACTION_IMPL_AUTOVALIDATOR + 1)
 
 bool action_avx512_isa_probe(void);
+bool action_avx512vbmi_isa_probe(void);
 
 /* Odp execute init handles setting up the state of the actions functions at
  * initialization time. It cannot return errors, as it must always succeed in
diff --git a/lib/packets.c b/lib/packets.c
index 1dcd4a6fc..06f516cb1 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1152,7 +1152,7 @@  packet_set_ipv4_addr(struct dp_packet *packet,
  * segements_left > 0.
  *
  * This function assumes that L3 and L4 offsets are set in the packet. */
-static bool
+bool
 packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr, bool *first_frag)
 {
     const struct ovs_16aligned_ip6_hdr *nh;
diff --git a/lib/packets.h b/lib/packets.h
index 5bdf6e4bb..8626aac8d 100644
--- a/lib/packets.h
+++ b/lib/packets.h
@@ -1642,6 +1642,8 @@  void packet_put_ra_prefix_opt(struct dp_packet *,
                               ovs_be32 preferred_lifetime,
                               const ovs_be128 router_prefix);
 uint32_t packet_csum_pseudoheader(const struct ip_header *);
+bool packet_rh_present(struct dp_packet *packet, uint8_t *nexthdr,
+                       bool *first_frag);
 void IP_ECN_set_ce(struct dp_packet *pkt, bool is_ipv6);
 
 #define DNS_HEADER_LEN 12