• Jesper Dangaard Brouer's avatar
    virtio_net: Add XDP frame size in two code paths · 9ce6146e
    Jesper Dangaard Brouer authored
    The virtio_net driver is running inside the guest-OS. There are two
    XDP receive code-paths in virtio_net, namely receive_small() and
    receive_mergeable(). The receive_big() function does not support XDP.
    
    In receive_small() the frame size is available in buflen. The buffer
    backing these frames are allocated in add_recvbuf_small() with same
    size, except for the headroom, but tailroom have reserved room for
    skb_shared_info. The headroom is encoded in ctx pointer as a value.
    
    In receive_mergeable() the frame size is more dynamic. There are two
    basic cases: (1) buffer size is based on a exponentially weighted
    moving average (see DECLARE_EWMA) of packet length. Or (2) in case
    virtnet_get_headroom() have any headroom then buffer size is
    PAGE_SIZE. The ctx pointer is this time used for encoding two values;
    the buffer len "truesize" and headroom. In case (1) if the rx buffer
    size is underestimated, the packet will have been split over more
    buffers (num_buf info in virtio_net_hdr_mrg_rxbuf placed in top of
    buffer area). If that happens the XDP path does a xdp_linearize_page
    operation.
    
    V3: Adjust frame_sz in receive_mergeable() case, spotted by Jason Wang.
    
    The code is really hard to follow, so some hints to reviewers.
    The receive_mergeable() case gets frames that were allocated in
    add_recvbuf_mergeable() which uses headroom=virtnet_get_headroom(),
    and 'buf' ptr is advanced this headroom.  The headroom can only
    be 0 or VIRTIO_XDP_HEADROOM, as virtnet_get_headroom is really
    simple:
    
      static unsigned int virtnet_get_headroom(struct virtnet_info *vi)
      {
    	return vi->xdp_queue_pairs ? VIRTIO_XDP_HEADROOM : 0;
      }
    
    As frame_sz is an offset size from xdp.data_hard_start, reviewers
    should notice how this is calculated in receive_mergeable():
    
      int offset = buf - page_address(page);
      [...]
      data = page_address(xdp_page) + offset;
      xdp.data_hard_start = data - VIRTIO_XDP_HEADROOM + vi->hdr_len;
    
    The calculated offset will always be VIRTIO_XDP_HEADROOM when
    reaching this code.  Thus, xdp.data_hard_start will be page-start
    address plus vi->hdr_len.  Given this xdp.frame_sz need to be
    reduced with vi->hdr_len size.
    
    IMHO a followup patch should cleanup this code to make it easier
    to maintain and understand, but it is outside the scope of this
    patchset.
    Signed-off-by: default avatarJesper Dangaard Brouer <brouer@redhat.com>
    Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
    Acked-by: default avatarMichael S. Tsirkin <mst@redhat.com>
    Acked-by: default avatarJason Wang <jasowang@redhat.com>
    Link: https://lore.kernel.org/bpf/158945344436.97035.9445115070189151680.stgit@firesoul
    9ce6146e
virtio_net.c 84.1 KB