[ovs-dev,v9,09/14] dp-packet: Add support for data "linearization".

Message ID 1535127870-91663-10-git-send-email-tiago.lam@intel.com
State Superseded
Delegated to: Ian Stokes
Headers show
Series
  • Support multi-segment mbufs
Related show

Commit Message

Lam, Tiago Aug. 24, 2018, 4:24 p.m.
Previous commits have added support to the dp_packet API to handle
multi-segmented packets, where data is not stored contiguously in
memory. However, in some cases, it is inevitable and data must be
provided contiguously. Examples of such cases are when performing csums
over the entire packet data, or when write()'ing to a file descriptor
(for a tap interface, for example). For such cases, the dp_packet API
has been extended to provide a way to transform a multi-segmented
DPBUF_DPDK packet into a DPBUF_MALLOC system packet (at the expense of a
copy of memory). If the packet's data is already stored in memory
contigously then there's no need to convert the packet.

Additionally, the main use cases that were assuming that a dp_packet's
data is always held contiguously in memory were changed to make use of
the new "linear functions" in the dp_packet API when there's a need to
traverse the entire's packet data. Per the example above, when the
packet's data needs to be write() to the tap's file descriptor, or when
the conntrack module needs to verify a packet's checksum, the data is
now linearized.

Signed-off-by: Tiago Lam <tiago.lam@intel.com>
---
 lib/bfd.c                     |  3 +-
 lib/conntrack.c               | 17 +++++----
 lib/dp-packet.c               | 18 +++++++++
 lib/dp-packet.h               | 89 +++++++++++++++++++++++++++++++++++++++----
 lib/dpif-netlink.c            |  2 +-
 lib/dpif.c                    |  2 +-
 lib/netdev-bsd.c              |  2 +-
 lib/netdev-dummy.c            |  5 ++-
 lib/netdev-linux.c            |  5 ++-
 lib/netdev-native-tnl.c       | 10 ++++-
 lib/odp-execute.c             |  2 +-
 lib/ofp-print.c               |  2 +-
 lib/ovs-lldp.c                |  3 +-
 lib/packets.c                 |  3 +-
 ofproto/ofproto-dpif-sflow.c  |  2 +-
 ofproto/ofproto-dpif-upcall.c |  2 +-
 ofproto/ofproto-dpif-xlate.c  | 12 ++++--
 17 files changed, 145 insertions(+), 34 deletions(-)

Patch

diff --git a/lib/bfd.c b/lib/bfd.c
index 5308262..d50d2da 100644
--- a/lib/bfd.c
+++ b/lib/bfd.c
@@ -722,7 +722,8 @@  bfd_process_packet(struct bfd *bfd, const struct flow *flow,
     if (!msg) {
         VLOG_INFO_RL(&rl, "%s: Received too-short BFD control message (only "
                      "%"PRIdPTR" bytes long, at least %d required).",
-                     bfd->name, (uint8_t *) dp_packet_tail(p) - l7,
+                     bfd->name, dp_packet_size(p) -
+                     (l7 - (uint8_t *) dp_packet_data(p)),
                      BFD_PACKET_LEN);
         goto out;
     }
diff --git a/lib/conntrack.c b/lib/conntrack.c
index 974f985..15d1ed2 100644
--- a/lib/conntrack.c
+++ b/lib/conntrack.c
@@ -636,6 +636,8 @@  reverse_pat_packet(struct dp_packet *pkt, const struct conn *conn)
 static void
 reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
 {
+    void *l3 = dp_packet_linear_ofs(pkt, pkt->l3_ofs);
+    void *l4 = dp_packet_linear_ofs(pkt, pkt->l4_ofs);
     char *tail = dp_packet_tail(pkt);
     char pad = dp_packet_l2_pad_size(pkt);
     struct conn_key inner_key;
@@ -644,8 +646,8 @@  reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
     uint16_t orig_l4_ofs = pkt->l4_ofs;
 
     if (conn->key.dl_type == htons(ETH_TYPE_IP)) {
-        struct ip_header *nh = dp_packet_l3(pkt);
-        struct icmp_header *icmp = dp_packet_l4(pkt);
+        struct ip_header *nh = l3;
+        struct icmp_header *icmp = l4;
         struct ip_header *inner_l3 = (struct ip_header *) (icmp + 1);
         extract_l3_ipv4(&inner_key, inner_l3, tail - ((char *)inner_l3) - pad,
                         &inner_l4, false);
@@ -664,8 +666,8 @@  reverse_nat_packet(struct dp_packet *pkt, const struct conn *conn)
         icmp->icmp_csum = 0;
         icmp->icmp_csum = csum(icmp, tail - (char *) icmp - pad);
     } else {
-        struct ovs_16aligned_ip6_hdr *nh6 = dp_packet_l3(pkt);
-        struct icmp6_error_header *icmp6 = dp_packet_l4(pkt);
+        struct ovs_16aligned_ip6_hdr *nh6 = l3;
+        struct icmp6_error_header *icmp6 = l4;
         struct ovs_16aligned_ip6_hdr *inner_l3_6 =
             (struct ovs_16aligned_ip6_hdr *) (icmp6 + 1);
         extract_l3_ipv6(&inner_key, inner_l3_6,
@@ -1320,6 +1322,7 @@  conntrack_execute(struct conntrack *ct, struct dp_packet_batch *pkt_batch,
             write_ct_md(packet, zone, NULL, NULL, NULL);
             continue;
         }
+
         process_one(ct, packet, &ctx, zone, force, commit, now, setmark,
                     setlabel, nat_action_info, tp_src, tp_dst, helper);
     }
@@ -1902,8 +1905,8 @@  conn_key_extract(struct conntrack *ct, struct dp_packet *pkt, ovs_be16 dl_type,
                  struct conn_lookup_ctx *ctx, uint16_t zone)
 {
     const struct eth_header *l2 = dp_packet_eth(pkt);
-    const struct ip_header *l3 = dp_packet_l3(pkt);
-    const char *l4 = dp_packet_l4(pkt);
+    const struct ip_header *l3 = dp_packet_linear_ofs(pkt, pkt->l3_ofs);
+    const char *l4 = dp_packet_linear_ofs(pkt, pkt->l4_ofs);
 
     memset(ctx, 0, sizeof *ctx);
 
@@ -3167,7 +3170,7 @@  handle_ftp_ctl(struct conntrack *ct, const struct conn_lookup_ctx *ctx,
                const struct conn *conn_for_expectation,
                long long now, enum ftp_ctl_pkt ftp_ctl, bool nat)
 {
-    struct ip_header *l3_hdr = dp_packet_l3(pkt);
+    struct ip_header *l3_hdr = dp_packet_linear_ofs(pkt, pkt->l3_ofs);
     ovs_be32 v4_addr_rep = 0;
     struct ct_addr v6_addr_rep;
     size_t addr_offset_from_ftp_data_start;
diff --git a/lib/dp-packet.c b/lib/dp-packet.c
index 806640b..b8f5242 100644
--- a/lib/dp-packet.c
+++ b/lib/dp-packet.c
@@ -118,6 +118,9 @@  void
 dp_packet_init_dpdk(struct dp_packet *b)
 {
     b->source = DPBUF_DPDK;
+#ifdef DPDK_NETDEV
+    b->mstate = NULL;
+#endif
 }
 
 /* Initializes 'b' as an empty dp_packet with an initial capacity of 'size'
@@ -135,6 +138,21 @@  dp_packet_uninit(struct dp_packet *b)
     if (b) {
         if (b->source == DPBUF_MALLOC) {
             free(dp_packet_base(b));
+
+#ifdef DPDK_NETDEV
+            /* Packet has been "linearized" */
+            if (b->mstate) {
+                b->source = DPBUF_DPDK;
+                b->mbuf.buf_addr = b->mstate->addr;
+                b->mbuf.buf_len = b->mstate->len;
+                b->mbuf.data_off = b->mstate->off;
+
+                free(b->mstate);
+                b->mstate = NULL;
+
+                free_dpdk_buf((struct dp_packet *) b);
+            }
+#endif
         } else if (b->source == DPBUF_DPDK) {
 #ifdef DPDK_NETDEV
             /* If this dp_packet was allocated by DPDK it must have been
diff --git a/lib/dp-packet.h b/lib/dp-packet.h
index 022e420..7f7b5f5 100644
--- a/lib/dp-packet.h
+++ b/lib/dp-packet.h
@@ -46,6 +46,16 @@  enum OVS_PACKED_ENUM dp_packet_source {
 
 #define DP_PACKET_CONTEXT_SIZE 64
 
+#ifdef DPDK_NETDEV
+/* Struct to save data for when a DPBUF_DPDK packet is converted to
+ * DPBUF_MALLOC. */
+struct mbuf_state {
+    void *addr;
+    uint16_t len;
+    uint16_t off;
+};
+#endif
+
 /* Buffer for holding packet data.  A dp_packet is automatically reallocated
  * as necessary if it grows too large for the available memory.
  * By default the packet type is set to Ethernet (PT_ETH).
@@ -53,6 +63,7 @@  enum OVS_PACKED_ENUM dp_packet_source {
 struct dp_packet {
 #ifdef DPDK_NETDEV
     struct rte_mbuf mbuf;       /* DPDK mbuf */
+    struct mbuf_state *mstate;  /* Used when packet has been "linearized" */
 #else
     void *base_;                /* First byte of allocated space. */
     uint16_t allocated_;        /* Number of bytes allocated. */
@@ -86,6 +97,7 @@  struct dp_packet {
 #endif
 
 static inline void *dp_packet_data(const struct dp_packet *);
+static inline void *dp_packet_linear_data(const struct dp_packet *b);
 static inline void dp_packet_set_data(struct dp_packet *, void *);
 static inline void *dp_packet_base(const struct dp_packet *);
 static inline void dp_packet_set_base(struct dp_packet *, void *);
@@ -139,6 +151,8 @@  static inline void dp_packet_delete(struct dp_packet *);
 
 static inline void *dp_packet_at(const struct dp_packet *, size_t offset,
                                  size_t size);
+static inline void *dp_packet_linear_ofs(const struct dp_packet *b,
+                                         uint16_t ofs);
 static inline void *dp_packet_at_assert(const struct dp_packet *,
                                         size_t offset, size_t size);
 #ifdef DPDK_NETDEV
@@ -181,15 +195,11 @@  static inline void
 dp_packet_delete(struct dp_packet *b)
 {
     if (b) {
-        if (b->source == DPBUF_DPDK) {
-            /* If this dp_packet was allocated by DPDK it must have been
-             * created as a dp_packet */
-            free_dpdk_buf((struct dp_packet*) b);
-            return;
-        }
-
         dp_packet_uninit(b);
-        free(b);
+
+        if (b->source != DPBUF_DPDK) {
+            free(b);
+        }
     }
 }
 
@@ -747,6 +757,68 @@  dp_packet_data(const struct dp_packet *b)
            ? (char *) dp_packet_base(b) + __packet_data(b) : NULL;
 }
 
+/* Linearizes the data held by 'b', if and only if its content is
+ * non-contiguous, and returns a pointer to the byte 'ofs' within linearized
+ * 'b', if 'ofs' has been set (!= UINT16_MAX).  Otherwise, returns a null
+ * pointer. */
+static inline void *
+dp_packet_linear_ofs(const struct dp_packet *b, uint16_t ofs)
+{
+    /* "Linearize" the data in the packet, iff needed */
+    dp_packet_linear_data(b);
+
+    return ofs != UINT16_MAX
+           ? (char *) dp_packet_data(b) + ofs
+           : NULL;
+}
+
+/* Copies the content of the DPDK packet 'b', if and only if its content is
+ * distributed amongst multiple segments, into system's memory, so that data is
+ * stored linearly. A pointer to the newly allocated (copied) data is returned.
+ *
+ * This is an expensive operation which should only be performed as a last
+ * resort, when multi-segments are under use but data must be accessed
+ * linearly. Otherwise dp_packet_data() should be used instead. */
+static inline void *
+dp_packet_linear_data(const struct dp_packet *b)
+{
+    if (b->source == DPBUF_DPDK) {
+#ifdef DPDK_NETDEV
+        if (!rte_pktmbuf_is_contiguous(&b->mbuf)) {
+            struct rte_mbuf *mbuf = CONST_CAST(struct rte_mbuf *, &b->mbuf);
+            struct dp_packet *pkt = CONST_CAST(struct dp_packet *, b);
+            uint32_t pkt_len = dp_packet_size(pkt);
+            struct mbuf_state *mstate = NULL;
+            void *dst = xmalloc(pkt_len);
+
+            /* Copy packet's data to system's memory */
+            if (!rte_pktmbuf_read(mbuf, 0, pkt_len, dst)) {
+                return NULL;
+            }
+
+            /* Free all mbufs except for the first */
+            dp_packet_clear(pkt);
+
+            /* Save mbuf's buf_addr to restore later */
+            mstate = xmalloc(sizeof(*mstate));
+            mstate->addr = pkt->mbuf.buf_addr;
+            mstate->len = pkt->mbuf.buf_len;
+            mstate->off = pkt->mbuf.data_off;
+            pkt->mstate = mstate;
+
+            /* Tranform DPBUF_DPDK packet into a DPBUF_MALLOC packet */
+            pkt->source = DPBUF_MALLOC;
+            pkt->mbuf.buf_addr = dst;
+            pkt->mbuf.buf_len = pkt_len;
+            pkt->mbuf.data_off = 0;
+            dp_packet_set_size(pkt, pkt_len);
+        }
+#endif
+    }
+
+    return dp_packet_data(b);
+}
+
 static inline void
 dp_packet_set_data(struct dp_packet *b, void *data)
 {
@@ -825,6 +897,7 @@  dp_packet_mbuf_init(struct dp_packet *p OVS_UNUSED)
     mbuf->ol_flags = mbuf->tx_offload = mbuf->packet_type = 0;
     mbuf->nb_segs = 1;
     mbuf->next = NULL;
+    p->mstate = NULL;
 #endif
 }
 
diff --git a/lib/dpif-netlink.c b/lib/dpif-netlink.c
index e6d5a6e..14e6b3e 100644
--- a/lib/dpif-netlink.c
+++ b/lib/dpif-netlink.c
@@ -1875,7 +1875,7 @@  dpif_netlink_encode_execute(int dp_ifindex, const struct dpif_execute *d_exec,
     k_exec->dp_ifindex = dp_ifindex;
 
     nl_msg_put_unspec(buf, OVS_PACKET_ATTR_PACKET,
-                      dp_packet_data(d_exec->packet),
+                      dp_packet_linear_data(d_exec->packet),
                       dp_packet_size(d_exec->packet));
 
     key_ofs = nl_msg_start_nested(buf, OVS_PACKET_ATTR_KEY);
diff --git a/lib/dpif.c b/lib/dpif.c
index d799f97..9cd0d07 100644
--- a/lib/dpif.c
+++ b/lib/dpif.c
@@ -1830,7 +1830,7 @@  log_execute_message(const struct dpif *dpif,
         uint64_t stub[1024 / 8];
         struct ofpbuf md = OFPBUF_STUB_INITIALIZER(stub);
 
-        packet = ofp_packet_to_string(dp_packet_data(execute->packet),
+        packet = ofp_packet_to_string(dp_packet_linear_data(execute->packet),
                                       dp_packet_size(execute->packet),
                                       execute->packet->packet_type);
         odp_key_from_dp_packet(&md, execute->packet);
diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
index a153aa2..71dc87f 100644
--- a/lib/netdev-bsd.c
+++ b/lib/netdev-bsd.c
@@ -701,7 +701,7 @@  netdev_bsd_send(struct netdev *netdev_, int qid OVS_UNUSED,
     }
 
     for (i = 0; i < batch->count; i++) {
-        const void *data = dp_packet_data(batch->packets[i]);
+        const void *data = dp_packet_data_linear(batch->packets[i]);
         size_t size = dp_packet_size(batch->packets[i]);
 
         while (!error) {
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index d498467..eef169c 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -233,7 +233,8 @@  dummy_packet_stream_run(struct netdev_dummy *dev, struct dummy_packet_stream *s)
 
         ASSIGN_CONTAINER(txbuf_node, ovs_list_front(&s->txq), list_node);
         txbuf = txbuf_node->pkt;
-        retval = stream_send(s->stream, dp_packet_data(txbuf), dp_packet_size(txbuf));
+        retval = stream_send(s->stream, dp_packet_linear_data(txbuf),
+                             dp_packet_size(txbuf));
 
         if (retval > 0) {
             dp_packet_pull(txbuf, retval);
@@ -1088,7 +1089,7 @@  netdev_dummy_send(struct netdev *netdev, int qid OVS_UNUSED,
 
     struct dp_packet *packet;
     DP_PACKET_BATCH_FOR_EACH(i, packet, batch) {
-        const void *buffer = dp_packet_data(packet);
+        const void *buffer = dp_packet_linear_data(packet);
         size_t size = dp_packet_size(packet);
 
         if (batch->packets[i]->packet_type != htonl(PT_ETH)) {
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index e16ea58..7581467 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -1380,7 +1380,7 @@  netdev_linux_sock_batch_send(int sock, int ifindex,
 
     struct dp_packet *packet;
     DP_PACKET_BATCH_FOR_EACH (i, packet, batch) {
-        iov[i].iov_base = dp_packet_data(packet);
+        iov[i].iov_base = dp_packet_linear_data(packet);
         iov[i].iov_len = dp_packet_size(packet);
         mmsg[i].msg_hdr = (struct msghdr) { .msg_name = &sll,
                                             .msg_namelen = sizeof sll,
@@ -1434,7 +1434,8 @@  netdev_linux_tap_batch_send(struct netdev *netdev_,
         int error;
 
         do {
-            retval = write(netdev->tap_fd, dp_packet_data(packet), size);
+            retval = write(netdev->tap_fd, dp_packet_linear_data(packet),
+                           size);
             error = retval < 0 ? errno : 0;
         } while (error == EINTR);
 
diff --git a/lib/netdev-native-tnl.c b/lib/netdev-native-tnl.c
index 56baaa2..0b6be43 100644
--- a/lib/netdev-native-tnl.c
+++ b/lib/netdev-native-tnl.c
@@ -87,6 +87,7 @@  netdev_tnl_ip_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
         ovs_be32 ip_src, ip_dst;
 
         if (OVS_UNLIKELY(!dp_packet_ip_checksum_valid(packet))) {
+            ip = dp_packet_linear_ofs(packet, packet->l3_ofs);
             if (csum(ip, IP_IHL(ip->ip_ihl_ver) * 4)) {
                 VLOG_WARN_RL(&err_rl, "ip packet has invalid checksum");
                 return NULL;
@@ -196,6 +197,8 @@  udp_extract_tnl_md(struct dp_packet *packet, struct flow_tnl *tnl,
                 csum = packet_csum_pseudoheader(dp_packet_l3(packet));
             }
 
+            udp = dp_packet_linear_ofs(packet, packet->l4_ofs);
+
             csum = csum_continue(csum, udp, dp_packet_size(packet) -
                                  ((const unsigned char *)udp -
                                   (const unsigned char *)dp_packet_eth(packet)
@@ -236,6 +239,8 @@  netdev_tnl_push_udp_header(const struct netdev *netdev OVS_UNUSED,
             csum = packet_csum_pseudoheader(netdev_tnl_ip_hdr(dp_packet_data(packet)));
         }
 
+        udp = dp_packet_linear_ofs(packet, packet->l4_ofs);
+
         csum = csum_continue(csum, udp, ip_tot_size);
         udp->udp_csum = csum_finish(csum);
 
@@ -373,6 +378,8 @@  parse_gre_header(struct dp_packet *packet,
     if (greh->flags & htons(GRE_CSUM)) {
         ovs_be16 pkt_csum;
 
+        greh = dp_packet_linear_ofs(packet, packet->l4_ofs);
+
         pkt_csum = csum(greh, dp_packet_size(packet) -
                               ((const unsigned char *)greh -
                                (const unsigned char *)dp_packet_eth(packet)));
@@ -448,7 +455,8 @@  netdev_gre_push_header(const struct netdev *netdev,
     greh = netdev_tnl_push_ip_header(packet, data->header, data->header_len, &ip_tot_size);
 
     if (greh->flags & htons(GRE_CSUM)) {
-        ovs_be16 *csum_opt = (ovs_be16 *) (greh + 1);
+        greh = dp_packet_linear_ofs(packet, packet->l4_ofs);
+        ovs_be16 *csum_opt = (ovs_be16 *) greh;
         *csum_opt = csum(greh, ip_tot_size);
     }
 
diff --git a/lib/odp-execute.c b/lib/odp-execute.c
index 5831d1f..e4d2604 100644
--- a/lib/odp-execute.c
+++ b/lib/odp-execute.c
@@ -231,7 +231,7 @@  static void
 odp_set_nd(struct dp_packet *packet, const struct ovs_key_nd *key,
            const struct ovs_key_nd *mask)
 {
-    const struct ovs_nd_msg *ns = dp_packet_l4(packet);
+    const struct ovs_nd_msg *ns = dp_packet_linear_ofs(packet, packet->l4_ofs);
     const struct ovs_nd_lla_opt *lla_opt = dp_packet_get_nd_payload(packet);
 
     if (OVS_LIKELY(ns && lla_opt)) {
diff --git a/lib/ofp-print.c b/lib/ofp-print.c
index cf93d2e..459e59f 100644
--- a/lib/ofp-print.c
+++ b/lib/ofp-print.c
@@ -111,7 +111,7 @@  ofp_packet_to_string(const void *data, size_t len, ovs_be32 packet_type)
 char *
 ofp_dp_packet_to_string(const struct dp_packet *packet)
 {
-    return ofp_packet_to_string(dp_packet_data(packet),
+    return ofp_packet_to_string(dp_packet_linear_data(packet),
                                 dp_packet_size(packet),
                                 packet->packet_type);
 }
diff --git a/lib/ovs-lldp.c b/lib/ovs-lldp.c
index 05c1dd4..21605c6 100644
--- a/lib/ovs-lldp.c
+++ b/lib/ovs-lldp.c
@@ -668,7 +668,8 @@  lldp_process_packet(struct lldp *lldp, const struct dp_packet *p)
 {
     if (lldp) {
         lldpd_recv(lldp->lldpd, lldpd_first_hardware(lldp->lldpd),
-                   (char *) dp_packet_data(p), dp_packet_size(p));
+                   (char *) dp_packet_linear_data(p),
+                   dp_packet_size(p));
     }
 }
 
diff --git a/lib/packets.c b/lib/packets.c
index 38bfb60..2cb8da4 100644
--- a/lib/packets.c
+++ b/lib/packets.c
@@ -1180,7 +1180,8 @@  packet_set_ipv6(struct dp_packet *packet, const struct in6_addr *src,
                 const struct in6_addr *dst, uint8_t key_tc, ovs_be32 key_fl,
                 uint8_t key_hl)
 {
-    struct ovs_16aligned_ip6_hdr *nh = dp_packet_l3(packet);
+    struct ovs_16aligned_ip6_hdr *nh = dp_packet_linear_ofs(packet,
+                                                            packet->l3_ofs);
     uint8_t proto = 0;
     bool rh_present;
 
diff --git a/ofproto/ofproto-dpif-sflow.c b/ofproto/ofproto-dpif-sflow.c
index d17d7a8..3167ee4 100644
--- a/ofproto/ofproto-dpif-sflow.c
+++ b/ofproto/ofproto-dpif-sflow.c
@@ -1319,7 +1319,7 @@  dpif_sflow_received(struct dpif_sflow *ds, const struct dp_packet *packet,
     header->stripped = 4;
     header->header_length = MIN(dp_packet_size(packet),
                                 sampler->sFlowFsMaximumHeaderSize);
-    header->header_bytes = dp_packet_data(packet);
+    header->header_bytes = dp_packet_linear_data(packet);
 
     /* Add extended switch element. */
     memset(&switchElem, 0, sizeof(switchElem));
diff --git a/ofproto/ofproto-dpif-upcall.c b/ofproto/ofproto-dpif-upcall.c
index 6222207..ec55e69 100644
--- a/ofproto/ofproto-dpif-upcall.c
+++ b/ofproto/ofproto-dpif-upcall.c
@@ -1455,7 +1455,7 @@  process_upcall(struct udpif *udpif, struct upcall *upcall,
                 .pin = {
                     .up = {
                         .base = {
-                            .packet = xmemdup(dp_packet_data(packet),
+                            .packet = xmemdup(dp_packet_linear_data(packet),
                                               dp_packet_size(packet)),
                             .packet_len = dp_packet_size(packet),
                             .reason = cookie->controller.reason,
diff --git a/ofproto/ofproto-dpif-xlate.c b/ofproto/ofproto-dpif-xlate.c
index e26f6c8..a9d2547 100644
--- a/ofproto/ofproto-dpif-xlate.c
+++ b/ofproto/ofproto-dpif-xlate.c
@@ -1735,7 +1735,8 @@  stp_process_packet(const struct xport *xport, const struct dp_packet *packet)
     }
 
     if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
-        stp_received_bpdu(sp, dp_packet_data(&payload), dp_packet_size(&payload));
+        stp_received_bpdu(sp, dp_packet_linear_data(&payload),
+                          dp_packet_size(&payload));
     }
 }
 
@@ -1786,7 +1787,8 @@  rstp_process_packet(const struct xport *xport, const struct dp_packet *packet)
     }
 
     if (dp_packet_try_pull(&payload, ETH_HEADER_LEN + LLC_HEADER_LEN)) {
-        rstp_port_received_bpdu(xport->rstp_port, dp_packet_data(&payload),
+        rstp_port_received_bpdu(xport->rstp_port,
+                                dp_packet_linear_data(&payload),
                                 dp_packet_size(&payload));
     }
 }
@@ -2559,7 +2561,8 @@  update_mcast_snooping_table4__(const struct xlate_ctx *ctx,
     size_t offset;
     ovs_be32 ip4 = flow->igmp_group_ip4;
 
-    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
+    offset = (char *) dp_packet_linear_ofs(packet, packet->l4_ofs) -
+        (char *) dp_packet_linear_data(packet);
     igmp = dp_packet_at(packet, offset, IGMP_HEADER_LEN);
     if (!igmp || csum(igmp, dp_packet_l4_size(packet)) != 0) {
         xlate_report_debug(ctx, OFT_DETAIL,
@@ -2618,7 +2621,8 @@  update_mcast_snooping_table6__(const struct xlate_ctx *ctx,
     int count;
     size_t offset;
 
-    offset = (char *) dp_packet_l4(packet) - (char *) dp_packet_data(packet);
+    offset = (char *) dp_packet_linear_ofs(packet, packet->l4_ofs) -
+        (char *) dp_packet_linear_data(packet);
     mld = dp_packet_at(packet, offset, MLD_HEADER_LEN);
 
     if (!mld ||