Merge tag 'mips_5.3' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
[linux-2.6-block.git] / net / xdp / xsk.c
index f53a6ef7c155de864f46361fe01c909ba793eb1e..d4d6f10aa93646d280c23eb6f28b89582cbb922f 100644 (file)
@@ -37,6 +37,12 @@ bool xsk_is_setup_for_bpf_map(struct xdp_sock *xs)
                READ_ONCE(xs->umem->fq);
 }
 
+bool xsk_umem_has_addrs(struct xdp_umem *umem, u32 cnt)
+{
+       return xskq_has_addrs(umem->fq, cnt);
+}
+EXPORT_SYMBOL(xsk_umem_has_addrs);
+
 u64 *xsk_umem_peek_addr(struct xdp_umem *umem, u64 *addr)
 {
        return xskq_peek_addr(umem->fq, addr);
@@ -123,13 +129,17 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
        u64 addr;
        int err;
 
-       if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index)
-               return -EINVAL;
+       spin_lock_bh(&xs->rx_lock);
+
+       if (xs->dev != xdp->rxq->dev || xs->queue_id != xdp->rxq->queue_index) {
+               err = -EINVAL;
+               goto out_unlock;
+       }
 
        if (!xskq_peek_addr(xs->umem->fq, &addr) ||
            len > xs->umem->chunk_size_nohr - XDP_PACKET_HEADROOM) {
-               xs->rx_dropped++;
-               return -ENOSPC;
+               err = -ENOSPC;
+               goto out_drop;
        }
 
        addr += xs->umem->headroom;
@@ -138,13 +148,21 @@ int xsk_generic_rcv(struct xdp_sock *xs, struct xdp_buff *xdp)
        memcpy(buffer, xdp->data_meta, len + metalen);
        addr += metalen;
        err = xskq_produce_batch_desc(xs->rx, addr, len);
-       if (!err) {
-               xskq_discard_addr(xs->umem->fq);
-               xsk_flush(xs);
-               return 0;
-       }
+       if (err)
+               goto out_drop;
+
+       xskq_discard_addr(xs->umem->fq);
+       xskq_produce_flush_desc(xs->rx);
+
+       spin_unlock_bh(&xs->rx_lock);
 
+       xs->sk.sk_data_ready(&xs->sk);
+       return 0;
+
+out_drop:
        xs->rx_dropped++;
+out_unlock:
+       spin_unlock_bh(&xs->rx_lock);
        return err;
 }
 
@@ -166,22 +184,18 @@ void xsk_umem_consume_tx_done(struct xdp_umem *umem)
 }
 EXPORT_SYMBOL(xsk_umem_consume_tx_done);
 
-bool xsk_umem_consume_tx(struct xdp_umem *umem, dma_addr_t *dma, u32 *len)
+bool xsk_umem_consume_tx(struct xdp_umem *umem, struct xdp_desc *desc)
 {
-       struct xdp_desc desc;
        struct xdp_sock *xs;
 
        rcu_read_lock();
        list_for_each_entry_rcu(xs, &umem->xsk_list, list) {
-               if (!xskq_peek_desc(xs->tx, &desc))
+               if (!xskq_peek_desc(xs->tx, desc))
                        continue;
 
-               if (xskq_produce_addr_lazy(umem->cq, desc.addr))
+               if (xskq_produce_addr_lazy(umem->cq, desc->addr))
                        goto out;
 
-               *dma = xdp_umem_get_dma(umem, desc.addr);
-               *len = desc.len;
-
                xskq_discard_desc(xs->tx);
                rcu_read_unlock();
                return true;
@@ -662,6 +676,26 @@ static int xsk_getsockopt(struct socket *sock, int level, int optname,
 
                return 0;
        }
+       case XDP_OPTIONS:
+       {
+               struct xdp_options opts = {};
+
+               if (len < sizeof(opts))
+                       return -EINVAL;
+
+               mutex_lock(&xs->mutex);
+               if (xs->zc)
+                       opts.flags |= XDP_OPTIONS_ZEROCOPY;
+               mutex_unlock(&xs->mutex);
+
+               len = sizeof(opts);
+               if (copy_to_user(optval, &opts, len))
+                       return -EFAULT;
+               if (put_user(len, optlen))
+                       return -EFAULT;
+
+               return 0;
+       }
        default:
                break;
        }
@@ -819,6 +853,7 @@ static int xsk_create(struct net *net, struct socket *sock, int protocol,
        xs = xdp_sk(sk);
        xs->state = XSK_READY;
        mutex_init(&xs->mutex);
+       spin_lock_init(&xs->rx_lock);
        spin_lock_init(&xs->tx_completion_lock);
 
        mutex_lock(&net->xdp.lock);