u64 handles[];
 };
 
+/* Flags for the umem flags field. */
+#define XDP_UMEM_USES_NEED_WAKEUP (1 << 0)
+
 struct xdp_umem {
        struct xsk_queue *fq;
        struct xsk_queue *cq;
        struct work_struct work;
        struct page **pgs;
        u32 npgs;
+       u16 queue_id;
+       u8 need_wakeup;
+       u8 flags;
        int id;
        struct net_device *dev;
        struct xdp_umem_fq_reuse *fq_reuse;
-       u16 queue_id;
        bool zc;
        spinlock_t xsk_list_lock;
        struct list_head xsk_list;
                                          struct xdp_umem_fq_reuse *newq);
 void xsk_reuseq_free(struct xdp_umem_fq_reuse *rq);
 struct xdp_umem *xdp_get_umem_from_qid(struct net_device *dev, u16 queue_id);
+void xsk_set_rx_need_wakeup(struct xdp_umem *umem);
+void xsk_set_tx_need_wakeup(struct xdp_umem *umem);
+void xsk_clear_rx_need_wakeup(struct xdp_umem *umem);
+void xsk_clear_tx_need_wakeup(struct xdp_umem *umem);
+bool xsk_umem_uses_need_wakeup(struct xdp_umem *umem);
 
 static inline char *xdp_umem_get_data(struct xdp_umem *umem, u64 addr)
 {
 {
 }
 
+static inline void xsk_set_rx_need_wakeup(struct xdp_umem *umem)
+{
+}
+
+static inline void xsk_set_tx_need_wakeup(struct xdp_umem *umem)
+{
+}
+
+static inline void xsk_clear_rx_need_wakeup(struct xdp_umem *umem)
+{
+}
+
+static inline void xsk_clear_tx_need_wakeup(struct xdp_umem *umem)
+{
+}
+
+static inline bool xsk_umem_uses_need_wakeup(struct xdp_umem *umem)
+{
+       return false;
+}
+
 #endif /* CONFIG_XDP_SOCKETS */
 
 #endif /* _LINUX_XDP_SOCK_H */
 
 }
 EXPORT_SYMBOL(xsk_umem_discard_addr);
 
+void xsk_set_rx_need_wakeup(struct xdp_umem *umem)
+{
+       if (umem->need_wakeup & XDP_WAKEUP_RX)
+               return;
+
+       umem->fq->ring->flags |= XDP_RING_NEED_WAKEUP;
+       umem->need_wakeup |= XDP_WAKEUP_RX;
+}
+EXPORT_SYMBOL(xsk_set_rx_need_wakeup);
+
+void xsk_set_tx_need_wakeup(struct xdp_umem *umem)
+{
+       struct xdp_sock *xs;
+
+       if (umem->need_wakeup & XDP_WAKEUP_TX)
+               return;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(xs, &umem->xsk_list, list) {
+               xs->tx->ring->flags |= XDP_RING_NEED_WAKEUP;
+       }
+       rcu_read_unlock();
+
+       umem->need_wakeup |= XDP_WAKEUP_TX;
+}
+EXPORT_SYMBOL(xsk_set_tx_need_wakeup);
+
+void xsk_clear_rx_need_wakeup(struct xdp_umem *umem)
+{
+       if (!(umem->need_wakeup & XDP_WAKEUP_RX))
+               return;
+
+       umem->fq->ring->flags &= ~XDP_RING_NEED_WAKEUP;
+       umem->need_wakeup &= ~XDP_WAKEUP_RX;
+}
+EXPORT_SYMBOL(xsk_clear_rx_need_wakeup);
+
+void xsk_clear_tx_need_wakeup(struct xdp_umem *umem)
+{
+       struct xdp_sock *xs;
+
+       if (!(umem->need_wakeup & XDP_WAKEUP_TX))
+               return;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(xs, &umem->xsk_list, list) {
+               xs->tx->ring->flags &= ~XDP_RING_NEED_WAKEUP;
+       }
+       rcu_read_unlock();
+
+       umem->need_wakeup &= ~XDP_WAKEUP_TX;
+}
+EXPORT_SYMBOL(xsk_clear_tx_need_wakeup);
+
+bool xsk_umem_uses_need_wakeup(struct xdp_umem *umem)
+{
+       return umem->flags & XDP_UMEM_USES_NEED_WAKEUP;
+}
+EXPORT_SYMBOL(xsk_umem_uses_need_wakeup);
+
 static int __xsk_rcv(struct xdp_sock *xs, struct xdp_buff *xdp, u32 len)
 {
        void *to_buf, *from_buf;
        unsigned int mask = datagram_poll(file, sock, wait);
        struct sock *sk = sock->sk;
        struct xdp_sock *xs = xdp_sk(sk);
+       struct net_device *dev = xs->dev;
+       struct xdp_umem *umem = xs->umem;
+
+       if (umem->need_wakeup)
+               dev->netdev_ops->ndo_xsk_wakeup(dev, xs->queue_id,
+                                               umem->need_wakeup);
 
        if (xs->rx && !xskq_empty_desc(xs->rx))
                mask |= POLLIN | POLLRDNORM;
                return -EINVAL;
 
        flags = sxdp->sxdp_flags;
-       if (flags & ~(XDP_SHARED_UMEM | XDP_COPY | XDP_ZEROCOPY))
+       if (flags & ~(XDP_SHARED_UMEM | XDP_COPY | XDP_ZEROCOPY |
+                     XDP_USE_NEED_WAKEUP))
                return -EINVAL;
 
        rtnl_lock();
                struct xdp_sock *umem_xs;
                struct socket *sock;
 
-               if ((flags & XDP_COPY) || (flags & XDP_ZEROCOPY)) {
+               if ((flags & XDP_COPY) || (flags & XDP_ZEROCOPY) ||
+                   (flags & XDP_USE_NEED_WAKEUP)) {
                        /* Cannot specify flags for shared sockets. */
                        err = -EINVAL;
                        goto out_unlock;
                }
                q = (optname == XDP_TX_RING) ? &xs->tx : &xs->rx;
                err = xsk_init_queue(entries, q, false);
+               if (!err && optname == XDP_TX_RING)
+                       /* Tx needs to be explicitly woken up the first time */
+                       xs->tx->ring->flags |= XDP_RING_NEED_WAKEUP;
                mutex_unlock(&xs->mutex);
                return err;
        }
        return -ENOPROTOOPT;
 }
 
+static void xsk_enter_rxtx_offsets(struct xdp_ring_offset_v1 *ring)
+{
+       ring->producer = offsetof(struct xdp_rxtx_ring, ptrs.producer);
+       ring->consumer = offsetof(struct xdp_rxtx_ring, ptrs.consumer);
+       ring->desc = offsetof(struct xdp_rxtx_ring, desc);
+}
+
+static void xsk_enter_umem_offsets(struct xdp_ring_offset_v1 *ring)
+{
+       ring->producer = offsetof(struct xdp_umem_ring, ptrs.producer);
+       ring->consumer = offsetof(struct xdp_umem_ring, ptrs.consumer);
+       ring->desc = offsetof(struct xdp_umem_ring, desc);
+}
+
 static int xsk_getsockopt(struct socket *sock, int level, int optname,
                          char __user *optval, int __user *optlen)
 {
        case XDP_MMAP_OFFSETS:
        {
                struct xdp_mmap_offsets off;
+               struct xdp_mmap_offsets_v1 off_v1;
+               bool flags_supported = true;
+               void *to_copy;
 
-               if (len < sizeof(off))
+               if (len < sizeof(off_v1))
                        return -EINVAL;
+               else if (len < sizeof(off))
+                       flags_supported = false;
+
+               if (flags_supported) {
+                       /* xdp_ring_offset is identical to xdp_ring_offset_v1
+                        * except for the flags field added to the end.
+                        */
+                       xsk_enter_rxtx_offsets((struct xdp_ring_offset_v1 *)
+                                              &off.rx);
+                       xsk_enter_rxtx_offsets((struct xdp_ring_offset_v1 *)
+                                              &off.tx);
+                       xsk_enter_umem_offsets((struct xdp_ring_offset_v1 *)
+                                              &off.fr);
+                       xsk_enter_umem_offsets((struct xdp_ring_offset_v1 *)
+                                              &off.cr);
+                       off.rx.flags = offsetof(struct xdp_rxtx_ring,
+                                               ptrs.flags);
+                       off.tx.flags = offsetof(struct xdp_rxtx_ring,
+                                               ptrs.flags);
+                       off.fr.flags = offsetof(struct xdp_umem_ring,
+                                               ptrs.flags);
+                       off.cr.flags = offsetof(struct xdp_umem_ring,
+                                               ptrs.flags);
+
+                       len = sizeof(off);
+                       to_copy = &off;
+               } else {
+                       xsk_enter_rxtx_offsets(&off_v1.rx);
+                       xsk_enter_rxtx_offsets(&off_v1.tx);
+                       xsk_enter_umem_offsets(&off_v1.fr);
+                       xsk_enter_umem_offsets(&off_v1.cr);
+
+                       len = sizeof(off_v1);
+                       to_copy = &off_v1;
+               }
 
-               off.rx.producer = offsetof(struct xdp_rxtx_ring, ptrs.producer);
-               off.rx.consumer = offsetof(struct xdp_rxtx_ring, ptrs.consumer);
-               off.rx.desc     = offsetof(struct xdp_rxtx_ring, desc);
-               off.tx.producer = offsetof(struct xdp_rxtx_ring, ptrs.producer);
-               off.tx.consumer = offsetof(struct xdp_rxtx_ring, ptrs.consumer);
-               off.tx.desc     = offsetof(struct xdp_rxtx_ring, desc);
-
-               off.fr.producer = offsetof(struct xdp_umem_ring, ptrs.producer);
-               off.fr.consumer = offsetof(struct xdp_umem_ring, ptrs.consumer);
-               off.fr.desc     = offsetof(struct xdp_umem_ring, desc);
-               off.cr.producer = offsetof(struct xdp_umem_ring, ptrs.producer);
-               off.cr.consumer = offsetof(struct xdp_umem_ring, ptrs.consumer);
-               off.cr.desc     = offsetof(struct xdp_umem_ring, desc);
-
-               len = sizeof(off);
-               if (copy_to_user(optval, &off, len))
+               if (copy_to_user(optval, to_copy, len))
                        return -EFAULT;
                if (put_user(len, optlen))
                        return -EFAULT;