diff mbox series

[ovs-dev,v12,1/3] netdev: Add optional qfill output parameter to rxq_recv()

Message ID 1524159646-7094-2-git-send-email-jan.scheurich@ericsson.com
State Accepted
Delegated to: Ian Stokes
Headers show
Series dpif-netdev: Detailed PMD performance metrics and supervision | expand

Commit Message

Jan Scheurich April 19, 2018, 5:40 p.m. UTC
If the caller provides a non-NULL qfill pointer and the netdev
implemementation supports reading the rx queue fill level, the rxq_recv()
function returns the remaining number of packets in the rx queue after
reception of the packet burst to the caller. If the implementation does
not support this, it returns -ENOTSUP instead. Reading the remaining queue
fill level should not substantilly slow down the recv() operation.

A first implementation is provided for ethernet and vhostuser DPDK ports
in netdev-dpdk.c.

This output parameter will be used in the upcoming commit for PMD
performance metrics to supervise the rx queue fill level for DPDK
vhostuser ports.

Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
Acked-by: Billy O'Mahony <billy.o.mahony@intel.com>
---
 lib/dpif-netdev.c     |  2 +-
 lib/netdev-bsd.c      |  8 +++++++-
 lib/netdev-dpdk.c     | 41 ++++++++++++++++++++++++++++++++++++-----
 lib/netdev-dummy.c    |  8 +++++++-
 lib/netdev-linux.c    |  7 ++++++-
 lib/netdev-provider.h |  8 +++++++-
 lib/netdev.c          |  5 +++--
 lib/netdev.h          |  3 ++-
 8 files changed, 69 insertions(+), 13 deletions(-)

Comments

Billy O'Mahony April 20, 2018, 10:27 a.m. UTC | #1
Acked-by: Billy O'Mahony <billy.o.mahony@intel.com>

> -----Original Message-----
> From: Jan Scheurich [mailto:jan.scheurich@ericsson.com]
> Sent: Thursday, April 19, 2018 6:41 PM
> To: dev@openvswitch.org
> Cc: ktraynor@redhat.com; Stokes, Ian <ian.stokes@intel.com>;
> i.maximets@samsung.com; O Mahony, Billy <billy.o.mahony@intel.com>; Jan
> Scheurich <jan.scheurich@ericsson.com>
> Subject: [PATCH v12 1/3] netdev: Add optional qfill output parameter to
> rxq_recv()
> 
> If the caller provides a non-NULL qfill pointer and the netdev implemementation
> supports reading the rx queue fill level, the rxq_recv() function returns the
> remaining number of packets in the rx queue after reception of the packet burst
> to the caller. If the implementation does not support this, it returns -ENOTSUP
> instead. Reading the remaining queue fill level should not substantilly slow down
> the recv() operation.
> 
> A first implementation is provided for ethernet and vhostuser DPDK ports in
> netdev-dpdk.c.
> 
> This output parameter will be used in the upcoming commit for PMD
> performance metrics to supervise the rx queue fill level for DPDK vhostuser
> ports.
> 
> Signed-off-by: Jan Scheurich <jan.scheurich@ericsson.com>
> Acked-by: Billy O'Mahony <billy.o.mahony@intel.com>
> ---
>  lib/dpif-netdev.c     |  2 +-
>  lib/netdev-bsd.c      |  8 +++++++-
>  lib/netdev-dpdk.c     | 41 ++++++++++++++++++++++++++++++++++++-----
>  lib/netdev-dummy.c    |  8 +++++++-
>  lib/netdev-linux.c    |  7 ++++++-
>  lib/netdev-provider.h |  8 +++++++-
>  lib/netdev.c          |  5 +++--
>  lib/netdev.h          |  3 ++-
>  8 files changed, 69 insertions(+), 13 deletions(-)
> 
> diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c index be31fd0..7ce3943 100644
> --- a/lib/dpif-netdev.c
> +++ b/lib/dpif-netdev.c
> @@ -3277,7 +3277,7 @@ dp_netdev_process_rxq_port(struct
> dp_netdev_pmd_thread *pmd,
>      pmd->ctx.last_rxq = rxq;
>      dp_packet_batch_init(&batch);
> 
> -    error = netdev_rxq_recv(rxq->rx, &batch);
> +    error = netdev_rxq_recv(rxq->rx, &batch, NULL);
>      if (!error) {
>          /* At least one packet received. */
>          *recirc_depth_get() = 0;
> diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c index 05974c1..b70f327 100644
> --- a/lib/netdev-bsd.c
> +++ b/lib/netdev-bsd.c
> @@ -618,7 +618,8 @@ netdev_rxq_bsd_recv_tap(struct netdev_rxq_bsd *rxq,
> struct dp_packet *buffer)  }
> 
>  static int
> -netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
> +netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch,
> +                    int *qfill)
>  {
>      struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
>      struct netdev *netdev = rxq->up.netdev; @@ -643,6 +644,11 @@
> netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
>          batch->packets[0] = packet;
>          batch->count = 1;
>      }
> +
> +    if (qfill) {
> +        *qfill = -ENOTSUP;
> +    }
> +
>      return retval;
>  }
> 
> diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c index ee39cbe..a4fc382
> 100644
> --- a/lib/netdev-dpdk.c
> +++ b/lib/netdev-dpdk.c
> @@ -1812,13 +1812,13 @@ netdev_dpdk_vhost_update_rx_counters(struct
> netdev_stats *stats,
>   */
>  static int
>  netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
> -                           struct dp_packet_batch *batch)
> +                           struct dp_packet_batch *batch, int *qfill)
>  {
>      struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev);
>      struct ingress_policer *policer = netdev_dpdk_get_ingress_policer(dev);
>      uint16_t nb_rx = 0;
>      uint16_t dropped = 0;
> -    int qid = rxq->queue_id;
> +    int qid = rxq->queue_id * VIRTIO_QNUM + VIRTIO_TXQ;
>      int vid = netdev_dpdk_get_vid(dev);
> 
>      if (OVS_UNLIKELY(vid < 0 || !dev->vhost_reconfigured @@ -1826,14
> +1826,23 @@ netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
>          return EAGAIN;
>      }
> 
> -    nb_rx = rte_vhost_dequeue_burst(vid, qid * VIRTIO_QNUM + VIRTIO_TXQ,
> -                                    dev->mp,
> +    nb_rx = rte_vhost_dequeue_burst(vid, qid, dev->mp,
>                                      (struct rte_mbuf **) batch->packets,
>                                      NETDEV_MAX_BURST);
>      if (!nb_rx) {
>          return EAGAIN;
>      }
> 
> +    if (qfill) {
> +        if (nb_rx == NETDEV_MAX_BURST) {
> +            /* The DPDK API returns a uint32_t which often has invalid bits in
> +             * the upper 16-bits. Need to restrict the value to uint16_t. */
> +            *qfill = rte_vhost_rx_queue_count(vid, qid) & UINT16_MAX;
> +        } else {
> +            *qfill = 0;
> +        }
> +    }
> +
>      if (policer) {
>          dropped = nb_rx;
>          nb_rx = ingress_policer_run(policer, @@ -1854,7 +1863,8 @@
> netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,  }
> 
>  static int
> -netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch *batch)
> +netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch *batch,
> +                     int *qfill)
>  {
>      struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq);
>      struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev); @@ -1891,6
> +1901,14 @@ netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct
> dp_packet_batch *batch)
>      batch->count = nb_rx;
>      dp_packet_batch_init_packet_fields(batch);
> 
> +    if (qfill) {
> +        if (nb_rx == NETDEV_MAX_BURST) {
> +            *qfill = rte_eth_rx_queue_count(rx->port_id, rxq->queue_id);
> +        } else {
> +            *qfill = 0;
> +        }
> +    }
> +
>      return 0;
>  }
> 
> @@ -3172,6 +3190,19 @@ vring_state_changed(int vid, uint16_t queue_id, int
> enable)
>      return 0;
>  }
> 
> +/*
> + * Retrieve the DPDK virtio device ID (vid) associated with a vhostuser
> + * or vhostuserclient netdev.
> + *
> + * Returns a value greater or equal to zero for a valid vid or '-1' if
> + * there is no valid vid associated. A vid of '-1' must not be used in
> + * rte_vhost_ APi calls.
> + *
> + * Once obtained and validated, a vid can be used by a PMD for multiple
> + * subsequent rte_vhost API calls until the PMD quiesces. A PMD should
> + * not fetch the vid again for each of a series of API calls.
> + */
> +
>  int
>  netdev_dpdk_get_vid(const struct netdev_dpdk *dev)  { diff --git a/lib/netdev-
> dummy.c b/lib/netdev-dummy.c index 8af9e1a..13bc580 100644
> --- a/lib/netdev-dummy.c
> +++ b/lib/netdev-dummy.c
> @@ -992,7 +992,8 @@ netdev_dummy_rxq_dealloc(struct netdev_rxq *rxq_)  }
> 
>  static int
> -netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch
> *batch)
> +netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch
> *batch,
> +                      int *qfill)
>  {
>      struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_);
>      struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev); @@ -
> 1037,6 +1038,11 @@ netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct
> dp_packet_batch *batch)
> 
>      batch->packets[0] = packet;
>      batch->count = 1;
> +
> +    if (qfill) {
> +        *qfill = -ENOTSUP;
> +    }
> +
>      return 0;
>  }
> 
> diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c index afa4de0..d19bd86
> 100644
> --- a/lib/netdev-linux.c
> +++ b/lib/netdev-linux.c
> @@ -1196,7 +1196,8 @@ netdev_linux_rxq_recv_tap(int fd, struct dp_packet
> *buffer)  }
> 
>  static int
> -netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch
> *batch)
> +netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch
> *batch,
> +                      int *qfill)
>  {
>      struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
>      struct netdev *netdev = rx->up.netdev; @@ -1225,6 +1226,10 @@
> netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
>          dp_packet_batch_init_packet(batch, buffer);
>      }
> 
> +    if (qfill) {
> +        *qfill = -ENOTSUP;
> +    }
> +
>      return retval;
>  }
> 
> diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h index
> 25bd671..6e8ae4f 100644
> --- a/lib/netdev-provider.h
> +++ b/lib/netdev-provider.h
> @@ -789,9 +789,15 @@ struct netdev_class {
>       * Implementations should allocate buffers with DP_NETDEV_HEADROOM
> bytes of
>       * headroom.
>       *
> +     * If the caller provides a non-NULL qfill pointer, the implementation
> +     * should return the number (zero or more) of remaining packets in the
> +     * queue after the reception the current batch, if it supports that,
> +     * or -ENOTSUP otherwise.
> +     *
>       * Returns EAGAIN immediately if no packet is ready to be received or
>       * another positive errno value if an error was encountered. */
> -    int (*rxq_recv)(struct netdev_rxq *rx, struct dp_packet_batch *batch);
> +    int (*rxq_recv)(struct netdev_rxq *rx, struct dp_packet_batch *batch,
> +                    int *qfill);
> 
>      /* Registers with the poll loop to wake up from the next call to
>       * poll_block() when a packet is ready to be received with diff --git
> a/lib/netdev.c b/lib/netdev.c index 8f09004..a1ac1b5 100644
> --- a/lib/netdev.c
> +++ b/lib/netdev.c
> @@ -696,11 +696,12 @@ netdev_rxq_close(struct netdev_rxq *rx)
>   * Returns EAGAIN immediately if no packet is ready to be received or another
>   * positive errno value if an error was encountered. */  int -
> netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *batch)
> +netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *batch,
> +                int *qfill)
>  {
>      int retval;
> 
> -    retval = rx->netdev->netdev_class->rxq_recv(rx, batch);
> +    retval = rx->netdev->netdev_class->rxq_recv(rx, batch, qfill);
>      if (!retval) {
>          COVERAGE_INC(netdev_received);
>      } else {
> diff --git a/lib/netdev.h b/lib/netdev.h index 441e53d..bd6f45a 100644
> --- a/lib/netdev.h
> +++ b/lib/netdev.h
> @@ -175,7 +175,8 @@ void netdev_rxq_close(struct netdev_rxq *);  const char
> *netdev_rxq_get_name(const struct netdev_rxq *);  int
> netdev_rxq_get_queue_id(const struct netdev_rxq *);
> 
> -int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *);
> +int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *,
> +                    int *qfill);
>  void netdev_rxq_wait(struct netdev_rxq *);  int netdev_rxq_drain(struct
> netdev_rxq *);
> 
> --
> 1.9.1
diff mbox series

Patch

diff --git a/lib/dpif-netdev.c b/lib/dpif-netdev.c
index be31fd0..7ce3943 100644
--- a/lib/dpif-netdev.c
+++ b/lib/dpif-netdev.c
@@ -3277,7 +3277,7 @@  dp_netdev_process_rxq_port(struct dp_netdev_pmd_thread *pmd,
     pmd->ctx.last_rxq = rxq;
     dp_packet_batch_init(&batch);
 
-    error = netdev_rxq_recv(rxq->rx, &batch);
+    error = netdev_rxq_recv(rxq->rx, &batch, NULL);
     if (!error) {
         /* At least one packet received. */
         *recirc_depth_get() = 0;
diff --git a/lib/netdev-bsd.c b/lib/netdev-bsd.c
index 05974c1..b70f327 100644
--- a/lib/netdev-bsd.c
+++ b/lib/netdev-bsd.c
@@ -618,7 +618,8 @@  netdev_rxq_bsd_recv_tap(struct netdev_rxq_bsd *rxq, struct dp_packet *buffer)
 }
 
 static int
-netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
+netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch,
+                    int *qfill)
 {
     struct netdev_rxq_bsd *rxq = netdev_rxq_bsd_cast(rxq_);
     struct netdev *netdev = rxq->up.netdev;
@@ -643,6 +644,11 @@  netdev_bsd_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
         batch->packets[0] = packet;
         batch->count = 1;
     }
+
+    if (qfill) {
+        *qfill = -ENOTSUP;
+    }
+
     return retval;
 }
 
diff --git a/lib/netdev-dpdk.c b/lib/netdev-dpdk.c
index ee39cbe..a4fc382 100644
--- a/lib/netdev-dpdk.c
+++ b/lib/netdev-dpdk.c
@@ -1812,13 +1812,13 @@  netdev_dpdk_vhost_update_rx_counters(struct netdev_stats *stats,
  */
 static int
 netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
-                           struct dp_packet_batch *batch)
+                           struct dp_packet_batch *batch, int *qfill)
 {
     struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev);
     struct ingress_policer *policer = netdev_dpdk_get_ingress_policer(dev);
     uint16_t nb_rx = 0;
     uint16_t dropped = 0;
-    int qid = rxq->queue_id;
+    int qid = rxq->queue_id * VIRTIO_QNUM + VIRTIO_TXQ;
     int vid = netdev_dpdk_get_vid(dev);
 
     if (OVS_UNLIKELY(vid < 0 || !dev->vhost_reconfigured
@@ -1826,14 +1826,23 @@  netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
         return EAGAIN;
     }
 
-    nb_rx = rte_vhost_dequeue_burst(vid, qid * VIRTIO_QNUM + VIRTIO_TXQ,
-                                    dev->mp,
+    nb_rx = rte_vhost_dequeue_burst(vid, qid, dev->mp,
                                     (struct rte_mbuf **) batch->packets,
                                     NETDEV_MAX_BURST);
     if (!nb_rx) {
         return EAGAIN;
     }
 
+    if (qfill) {
+        if (nb_rx == NETDEV_MAX_BURST) {
+            /* The DPDK API returns a uint32_t which often has invalid bits in
+             * the upper 16-bits. Need to restrict the value to uint16_t. */
+            *qfill = rte_vhost_rx_queue_count(vid, qid) & UINT16_MAX;
+        } else {
+            *qfill = 0;
+        }
+    }
+
     if (policer) {
         dropped = nb_rx;
         nb_rx = ingress_policer_run(policer,
@@ -1854,7 +1863,8 @@  netdev_dpdk_vhost_rxq_recv(struct netdev_rxq *rxq,
 }
 
 static int
-netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch *batch)
+netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch *batch,
+                     int *qfill)
 {
     struct netdev_rxq_dpdk *rx = netdev_rxq_dpdk_cast(rxq);
     struct netdev_dpdk *dev = netdev_dpdk_cast(rxq->netdev);
@@ -1891,6 +1901,14 @@  netdev_dpdk_rxq_recv(struct netdev_rxq *rxq, struct dp_packet_batch *batch)
     batch->count = nb_rx;
     dp_packet_batch_init_packet_fields(batch);
 
+    if (qfill) {
+        if (nb_rx == NETDEV_MAX_BURST) {
+            *qfill = rte_eth_rx_queue_count(rx->port_id, rxq->queue_id);
+        } else {
+            *qfill = 0;
+        }
+    }
+
     return 0;
 }
 
@@ -3172,6 +3190,19 @@  vring_state_changed(int vid, uint16_t queue_id, int enable)
     return 0;
 }
 
+/*
+ * Retrieve the DPDK virtio device ID (vid) associated with a vhostuser
+ * or vhostuserclient netdev.
+ *
+ * Returns a value greater or equal to zero for a valid vid or '-1' if
+ * there is no valid vid associated. A vid of '-1' must not be used in
+ * rte_vhost_ APi calls.
+ *
+ * Once obtained and validated, a vid can be used by a PMD for multiple
+ * subsequent rte_vhost API calls until the PMD quiesces. A PMD should
+ * not fetch the vid again for each of a series of API calls.
+ */
+
 int
 netdev_dpdk_get_vid(const struct netdev_dpdk *dev)
 {
diff --git a/lib/netdev-dummy.c b/lib/netdev-dummy.c
index 8af9e1a..13bc580 100644
--- a/lib/netdev-dummy.c
+++ b/lib/netdev-dummy.c
@@ -992,7 +992,8 @@  netdev_dummy_rxq_dealloc(struct netdev_rxq *rxq_)
 }
 
 static int
-netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
+netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch,
+                      int *qfill)
 {
     struct netdev_rxq_dummy *rx = netdev_rxq_dummy_cast(rxq_);
     struct netdev_dummy *netdev = netdev_dummy_cast(rx->up.netdev);
@@ -1037,6 +1038,11 @@  netdev_dummy_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
 
     batch->packets[0] = packet;
     batch->count = 1;
+
+    if (qfill) {
+        *qfill = -ENOTSUP;
+    }
+
     return 0;
 }
 
diff --git a/lib/netdev-linux.c b/lib/netdev-linux.c
index afa4de0..d19bd86 100644
--- a/lib/netdev-linux.c
+++ b/lib/netdev-linux.c
@@ -1196,7 +1196,8 @@  netdev_linux_rxq_recv_tap(int fd, struct dp_packet *buffer)
 }
 
 static int
-netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
+netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch,
+                      int *qfill)
 {
     struct netdev_rxq_linux *rx = netdev_rxq_linux_cast(rxq_);
     struct netdev *netdev = rx->up.netdev;
@@ -1225,6 +1226,10 @@  netdev_linux_rxq_recv(struct netdev_rxq *rxq_, struct dp_packet_batch *batch)
         dp_packet_batch_init_packet(batch, buffer);
     }
 
+    if (qfill) {
+        *qfill = -ENOTSUP;
+    }
+
     return retval;
 }
 
diff --git a/lib/netdev-provider.h b/lib/netdev-provider.h
index 25bd671..6e8ae4f 100644
--- a/lib/netdev-provider.h
+++ b/lib/netdev-provider.h
@@ -789,9 +789,15 @@  struct netdev_class {
      * Implementations should allocate buffers with DP_NETDEV_HEADROOM bytes of
      * headroom.
      *
+     * If the caller provides a non-NULL qfill pointer, the implementation
+     * should return the number (zero or more) of remaining packets in the
+     * queue after the reception the current batch, if it supports that,
+     * or -ENOTSUP otherwise.
+     *
      * Returns EAGAIN immediately if no packet is ready to be received or
      * another positive errno value if an error was encountered. */
-    int (*rxq_recv)(struct netdev_rxq *rx, struct dp_packet_batch *batch);
+    int (*rxq_recv)(struct netdev_rxq *rx, struct dp_packet_batch *batch,
+                    int *qfill);
 
     /* Registers with the poll loop to wake up from the next call to
      * poll_block() when a packet is ready to be received with
diff --git a/lib/netdev.c b/lib/netdev.c
index 8f09004..a1ac1b5 100644
--- a/lib/netdev.c
+++ b/lib/netdev.c
@@ -696,11 +696,12 @@  netdev_rxq_close(struct netdev_rxq *rx)
  * Returns EAGAIN immediately if no packet is ready to be received or another
  * positive errno value if an error was encountered. */
 int
-netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *batch)
+netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *batch,
+                int *qfill)
 {
     int retval;
 
-    retval = rx->netdev->netdev_class->rxq_recv(rx, batch);
+    retval = rx->netdev->netdev_class->rxq_recv(rx, batch, qfill);
     if (!retval) {
         COVERAGE_INC(netdev_received);
     } else {
diff --git a/lib/netdev.h b/lib/netdev.h
index 441e53d..bd6f45a 100644
--- a/lib/netdev.h
+++ b/lib/netdev.h
@@ -175,7 +175,8 @@  void netdev_rxq_close(struct netdev_rxq *);
 const char *netdev_rxq_get_name(const struct netdev_rxq *);
 int netdev_rxq_get_queue_id(const struct netdev_rxq *);
 
-int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *);
+int netdev_rxq_recv(struct netdev_rxq *rx, struct dp_packet_batch *,
+                    int *qfill);
 void netdev_rxq_wait(struct netdev_rxq *);
 int netdev_rxq_drain(struct netdev_rxq *);