162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* A network driver using virtio. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright 2007 Rusty Russell <rusty@rustcorp.com.au> IBM Corporation 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci//#define DEBUG 762306a36Sopenharmony_ci#include <linux/netdevice.h> 862306a36Sopenharmony_ci#include <linux/etherdevice.h> 962306a36Sopenharmony_ci#include <linux/ethtool.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/virtio.h> 1262306a36Sopenharmony_ci#include <linux/virtio_net.h> 1362306a36Sopenharmony_ci#include <linux/bpf.h> 1462306a36Sopenharmony_ci#include <linux/bpf_trace.h> 1562306a36Sopenharmony_ci#include <linux/scatterlist.h> 1662306a36Sopenharmony_ci#include <linux/if_vlan.h> 1762306a36Sopenharmony_ci#include <linux/slab.h> 1862306a36Sopenharmony_ci#include <linux/cpu.h> 1962306a36Sopenharmony_ci#include <linux/average.h> 2062306a36Sopenharmony_ci#include <linux/filter.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <net/route.h> 2362306a36Sopenharmony_ci#include <net/xdp.h> 2462306a36Sopenharmony_ci#include <net/net_failover.h> 2562306a36Sopenharmony_ci#include <net/netdev_rx_queue.h> 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic int napi_weight = NAPI_POLL_WEIGHT; 2862306a36Sopenharmony_cimodule_param(napi_weight, int, 0444); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_cistatic bool csum = true, gso = true, napi_tx = true; 3162306a36Sopenharmony_cimodule_param(csum, bool, 0444); 3262306a36Sopenharmony_cimodule_param(gso, bool, 0444); 3362306a36Sopenharmony_cimodule_param(napi_tx, bool, 0644); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci/* FIXME: MTU in config. */ 3662306a36Sopenharmony_ci#define GOOD_PACKET_LEN (ETH_HLEN + VLAN_HLEN + ETH_DATA_LEN) 3762306a36Sopenharmony_ci#define GOOD_COPY_LEN 128 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define VIRTNET_RX_PAD (NET_IP_ALIGN + NET_SKB_PAD) 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* Amount of XDP headroom to prepend to packets for use by xdp_adjust_head */ 4262306a36Sopenharmony_ci#define VIRTIO_XDP_HEADROOM 256 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci/* Separating two types of XDP xmit */ 4562306a36Sopenharmony_ci#define VIRTIO_XDP_TX BIT(0) 4662306a36Sopenharmony_ci#define VIRTIO_XDP_REDIR BIT(1) 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci#define VIRTIO_XDP_FLAG BIT(0) 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* RX packet size EWMA. The average packet size is used to determine the packet 5162306a36Sopenharmony_ci * buffer size when refilling RX rings. As the entire RX ring may be refilled 5262306a36Sopenharmony_ci * at once, the weight is chosen so that the EWMA will be insensitive to short- 5362306a36Sopenharmony_ci * term, transient changes in packet size. 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ciDECLARE_EWMA(pkt_len, 0, 64) 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci#define VIRTNET_DRIVER_VERSION "1.0.0" 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic const unsigned long guest_offloads[] = { 6062306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_TSO4, 6162306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_TSO6, 6262306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_ECN, 6362306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_UFO, 6462306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_CSUM, 6562306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_USO4, 6662306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_USO6, 6762306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_HDRLEN 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define GUEST_OFFLOAD_GRO_HW_MASK ((1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ 7162306a36Sopenharmony_ci (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ 7262306a36Sopenharmony_ci (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ 7362306a36Sopenharmony_ci (1ULL << VIRTIO_NET_F_GUEST_UFO) | \ 7462306a36Sopenharmony_ci (1ULL << VIRTIO_NET_F_GUEST_USO4) | \ 7562306a36Sopenharmony_ci (1ULL << VIRTIO_NET_F_GUEST_USO6)) 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistruct virtnet_stat_desc { 7862306a36Sopenharmony_ci char desc[ETH_GSTRING_LEN]; 7962306a36Sopenharmony_ci size_t offset; 8062306a36Sopenharmony_ci}; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistruct virtnet_sq_stats { 8362306a36Sopenharmony_ci struct u64_stats_sync syncp; 8462306a36Sopenharmony_ci u64_stats_t packets; 8562306a36Sopenharmony_ci u64_stats_t bytes; 8662306a36Sopenharmony_ci u64_stats_t xdp_tx; 8762306a36Sopenharmony_ci u64_stats_t xdp_tx_drops; 8862306a36Sopenharmony_ci u64_stats_t kicks; 8962306a36Sopenharmony_ci u64_stats_t tx_timeouts; 9062306a36Sopenharmony_ci}; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistruct virtnet_rq_stats { 9362306a36Sopenharmony_ci struct u64_stats_sync syncp; 9462306a36Sopenharmony_ci u64_stats_t packets; 9562306a36Sopenharmony_ci u64_stats_t bytes; 9662306a36Sopenharmony_ci u64_stats_t drops; 9762306a36Sopenharmony_ci u64_stats_t xdp_packets; 9862306a36Sopenharmony_ci u64_stats_t xdp_tx; 9962306a36Sopenharmony_ci u64_stats_t xdp_redirects; 10062306a36Sopenharmony_ci u64_stats_t xdp_drops; 10162306a36Sopenharmony_ci u64_stats_t kicks; 10262306a36Sopenharmony_ci}; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define VIRTNET_SQ_STAT(m) offsetof(struct virtnet_sq_stats, m) 10562306a36Sopenharmony_ci#define VIRTNET_RQ_STAT(m) offsetof(struct virtnet_rq_stats, m) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic const struct virtnet_stat_desc virtnet_sq_stats_desc[] = { 10862306a36Sopenharmony_ci { "packets", VIRTNET_SQ_STAT(packets) }, 10962306a36Sopenharmony_ci { "bytes", VIRTNET_SQ_STAT(bytes) }, 11062306a36Sopenharmony_ci { "xdp_tx", VIRTNET_SQ_STAT(xdp_tx) }, 11162306a36Sopenharmony_ci { "xdp_tx_drops", VIRTNET_SQ_STAT(xdp_tx_drops) }, 11262306a36Sopenharmony_ci { "kicks", VIRTNET_SQ_STAT(kicks) }, 11362306a36Sopenharmony_ci { "tx_timeouts", VIRTNET_SQ_STAT(tx_timeouts) }, 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic const struct virtnet_stat_desc virtnet_rq_stats_desc[] = { 11762306a36Sopenharmony_ci { "packets", VIRTNET_RQ_STAT(packets) }, 11862306a36Sopenharmony_ci { "bytes", VIRTNET_RQ_STAT(bytes) }, 11962306a36Sopenharmony_ci { "drops", VIRTNET_RQ_STAT(drops) }, 12062306a36Sopenharmony_ci { "xdp_packets", VIRTNET_RQ_STAT(xdp_packets) }, 12162306a36Sopenharmony_ci { "xdp_tx", VIRTNET_RQ_STAT(xdp_tx) }, 12262306a36Sopenharmony_ci { "xdp_redirects", VIRTNET_RQ_STAT(xdp_redirects) }, 12362306a36Sopenharmony_ci { "xdp_drops", VIRTNET_RQ_STAT(xdp_drops) }, 12462306a36Sopenharmony_ci { "kicks", VIRTNET_RQ_STAT(kicks) }, 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci#define VIRTNET_SQ_STATS_LEN ARRAY_SIZE(virtnet_sq_stats_desc) 12862306a36Sopenharmony_ci#define VIRTNET_RQ_STATS_LEN ARRAY_SIZE(virtnet_rq_stats_desc) 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_cistruct virtnet_interrupt_coalesce { 13162306a36Sopenharmony_ci u32 max_packets; 13262306a36Sopenharmony_ci u32 max_usecs; 13362306a36Sopenharmony_ci}; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci/* The dma information of pages allocated at a time. */ 13662306a36Sopenharmony_cistruct virtnet_rq_dma { 13762306a36Sopenharmony_ci dma_addr_t addr; 13862306a36Sopenharmony_ci u32 ref; 13962306a36Sopenharmony_ci u16 len; 14062306a36Sopenharmony_ci u16 need_sync; 14162306a36Sopenharmony_ci}; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Internal representation of a send virtqueue */ 14462306a36Sopenharmony_cistruct send_queue { 14562306a36Sopenharmony_ci /* Virtqueue associated with this send _queue */ 14662306a36Sopenharmony_ci struct virtqueue *vq; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci /* TX: fragments + linear part + virtio header */ 14962306a36Sopenharmony_ci struct scatterlist sg[MAX_SKB_FRAGS + 2]; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Name of the send queue: output.$index */ 15262306a36Sopenharmony_ci char name[16]; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci struct virtnet_sq_stats stats; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci struct virtnet_interrupt_coalesce intr_coal; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci struct napi_struct napi; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* Record whether sq is in reset state. */ 16162306a36Sopenharmony_ci bool reset; 16262306a36Sopenharmony_ci}; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci/* Internal representation of a receive virtqueue */ 16562306a36Sopenharmony_cistruct receive_queue { 16662306a36Sopenharmony_ci /* Virtqueue associated with this receive_queue */ 16762306a36Sopenharmony_ci struct virtqueue *vq; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci struct napi_struct napi; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci struct bpf_prog __rcu *xdp_prog; 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci struct virtnet_rq_stats stats; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci struct virtnet_interrupt_coalesce intr_coal; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* Chain pages by the private ptr. */ 17862306a36Sopenharmony_ci struct page *pages; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* Average packet length for mergeable receive buffers. */ 18162306a36Sopenharmony_ci struct ewma_pkt_len mrg_avg_pkt_len; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Page frag for packet buffer allocation. */ 18462306a36Sopenharmony_ci struct page_frag alloc_frag; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci /* RX: fragments + linear part + virtio header */ 18762306a36Sopenharmony_ci struct scatterlist sg[MAX_SKB_FRAGS + 2]; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Min single buffer size for mergeable buffers case. */ 19062306a36Sopenharmony_ci unsigned int min_buf_len; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci /* Name of this receive queue: input.$index */ 19362306a36Sopenharmony_ci char name[16]; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci struct xdp_rxq_info xdp_rxq; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci /* Record the last dma info to free after new pages is allocated. */ 19862306a36Sopenharmony_ci struct virtnet_rq_dma *last_dma; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Do dma by self */ 20162306a36Sopenharmony_ci bool do_dma; 20262306a36Sopenharmony_ci}; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci/* This structure can contain rss message with maximum settings for indirection table and keysize 20562306a36Sopenharmony_ci * Note, that default structure that describes RSS configuration virtio_net_rss_config 20662306a36Sopenharmony_ci * contains same info but can't handle table values. 20762306a36Sopenharmony_ci * In any case, structure would be passed to virtio hw through sg_buf split by parts 20862306a36Sopenharmony_ci * because table sizes may be differ according to the device configuration. 20962306a36Sopenharmony_ci */ 21062306a36Sopenharmony_ci#define VIRTIO_NET_RSS_MAX_KEY_SIZE 40 21162306a36Sopenharmony_ci#define VIRTIO_NET_RSS_MAX_TABLE_LEN 128 21262306a36Sopenharmony_cistruct virtio_net_ctrl_rss { 21362306a36Sopenharmony_ci u32 hash_types; 21462306a36Sopenharmony_ci u16 indirection_table_mask; 21562306a36Sopenharmony_ci u16 unclassified_queue; 21662306a36Sopenharmony_ci u16 indirection_table[VIRTIO_NET_RSS_MAX_TABLE_LEN]; 21762306a36Sopenharmony_ci u16 max_tx_vq; 21862306a36Sopenharmony_ci u8 hash_key_length; 21962306a36Sopenharmony_ci u8 key[VIRTIO_NET_RSS_MAX_KEY_SIZE]; 22062306a36Sopenharmony_ci}; 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci/* Control VQ buffers: protected by the rtnl lock */ 22362306a36Sopenharmony_cistruct control_buf { 22462306a36Sopenharmony_ci struct virtio_net_ctrl_hdr hdr; 22562306a36Sopenharmony_ci virtio_net_ctrl_ack status; 22662306a36Sopenharmony_ci struct virtio_net_ctrl_mq mq; 22762306a36Sopenharmony_ci u8 promisc; 22862306a36Sopenharmony_ci u8 allmulti; 22962306a36Sopenharmony_ci __virtio16 vid; 23062306a36Sopenharmony_ci __virtio64 offloads; 23162306a36Sopenharmony_ci struct virtio_net_ctrl_rss rss; 23262306a36Sopenharmony_ci struct virtio_net_ctrl_coal_tx coal_tx; 23362306a36Sopenharmony_ci struct virtio_net_ctrl_coal_rx coal_rx; 23462306a36Sopenharmony_ci struct virtio_net_ctrl_coal_vq coal_vq; 23562306a36Sopenharmony_ci}; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_cistruct virtnet_info { 23862306a36Sopenharmony_ci struct virtio_device *vdev; 23962306a36Sopenharmony_ci struct virtqueue *cvq; 24062306a36Sopenharmony_ci struct net_device *dev; 24162306a36Sopenharmony_ci struct send_queue *sq; 24262306a36Sopenharmony_ci struct receive_queue *rq; 24362306a36Sopenharmony_ci unsigned int status; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Max # of queue pairs supported by the device */ 24662306a36Sopenharmony_ci u16 max_queue_pairs; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci /* # of queue pairs currently used by the driver */ 24962306a36Sopenharmony_ci u16 curr_queue_pairs; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* # of XDP queue pairs currently used by the driver */ 25262306a36Sopenharmony_ci u16 xdp_queue_pairs; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_ci /* xdp_queue_pairs may be 0, when xdp is already loaded. So add this. */ 25562306a36Sopenharmony_ci bool xdp_enabled; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* I like... big packets and I cannot lie! */ 25862306a36Sopenharmony_ci bool big_packets; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* number of sg entries allocated for big packets */ 26162306a36Sopenharmony_ci unsigned int big_packets_num_skbfrags; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Host will merge rx buffers for big packets (shake it! shake it!) */ 26462306a36Sopenharmony_ci bool mergeable_rx_bufs; 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci /* Host supports rss and/or hash report */ 26762306a36Sopenharmony_ci bool has_rss; 26862306a36Sopenharmony_ci bool has_rss_hash_report; 26962306a36Sopenharmony_ci u8 rss_key_size; 27062306a36Sopenharmony_ci u16 rss_indir_table_size; 27162306a36Sopenharmony_ci u32 rss_hash_types_supported; 27262306a36Sopenharmony_ci u32 rss_hash_types_saved; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* Has control virtqueue */ 27562306a36Sopenharmony_ci bool has_cvq; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Host can handle any s/g split between our header and packet data */ 27862306a36Sopenharmony_ci bool any_header_sg; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci /* Packet virtio header size */ 28162306a36Sopenharmony_ci u8 hdr_len; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci /* Work struct for delayed refilling if we run low on memory. */ 28462306a36Sopenharmony_ci struct delayed_work refill; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Is delayed refill enabled? */ 28762306a36Sopenharmony_ci bool refill_enabled; 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci /* The lock to synchronize the access to refill_enabled */ 29062306a36Sopenharmony_ci spinlock_t refill_lock; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci /* Work struct for config space updates */ 29362306a36Sopenharmony_ci struct work_struct config_work; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Does the affinity hint is set for virtqueues? */ 29662306a36Sopenharmony_ci bool affinity_hint_set; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* CPU hotplug instances for online & dead */ 29962306a36Sopenharmony_ci struct hlist_node node; 30062306a36Sopenharmony_ci struct hlist_node node_dead; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci struct control_buf *ctrl; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci /* Ethtool settings */ 30562306a36Sopenharmony_ci u8 duplex; 30662306a36Sopenharmony_ci u32 speed; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Interrupt coalescing settings */ 30962306a36Sopenharmony_ci struct virtnet_interrupt_coalesce intr_coal_tx; 31062306a36Sopenharmony_ci struct virtnet_interrupt_coalesce intr_coal_rx; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci unsigned long guest_offloads; 31362306a36Sopenharmony_ci unsigned long guest_offloads_capable; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci /* failover when STANDBY feature enabled */ 31662306a36Sopenharmony_ci struct failover *failover; 31762306a36Sopenharmony_ci}; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_cistruct padded_vnet_hdr { 32062306a36Sopenharmony_ci struct virtio_net_hdr_v1_hash hdr; 32162306a36Sopenharmony_ci /* 32262306a36Sopenharmony_ci * hdr is in a separate sg buffer, and data sg buffer shares same page 32362306a36Sopenharmony_ci * with this header sg. This padding makes next sg 16 byte aligned 32462306a36Sopenharmony_ci * after the header. 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci char padding[12]; 32762306a36Sopenharmony_ci}; 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistruct virtio_net_common_hdr { 33062306a36Sopenharmony_ci union { 33162306a36Sopenharmony_ci struct virtio_net_hdr hdr; 33262306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf mrg_hdr; 33362306a36Sopenharmony_ci struct virtio_net_hdr_v1_hash hash_v1_hdr; 33462306a36Sopenharmony_ci }; 33562306a36Sopenharmony_ci}; 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_cistatic void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf); 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_cistatic bool is_xdp_frame(void *ptr) 34062306a36Sopenharmony_ci{ 34162306a36Sopenharmony_ci return (unsigned long)ptr & VIRTIO_XDP_FLAG; 34262306a36Sopenharmony_ci} 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_cistatic void *xdp_to_ptr(struct xdp_frame *ptr) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci return (void *)((unsigned long)ptr | VIRTIO_XDP_FLAG); 34762306a36Sopenharmony_ci} 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_cistatic struct xdp_frame *ptr_to_xdp(void *ptr) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci return (struct xdp_frame *)((unsigned long)ptr & ~VIRTIO_XDP_FLAG); 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci/* Converting between virtqueue no. and kernel tx/rx queue no. 35562306a36Sopenharmony_ci * 0:rx0 1:tx0 2:rx1 3:tx1 ... 2N:rxN 2N+1:txN 2N+2:cvq 35662306a36Sopenharmony_ci */ 35762306a36Sopenharmony_cistatic int vq2txq(struct virtqueue *vq) 35862306a36Sopenharmony_ci{ 35962306a36Sopenharmony_ci return (vq->index - 1) / 2; 36062306a36Sopenharmony_ci} 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_cistatic int txq2vq(int txq) 36362306a36Sopenharmony_ci{ 36462306a36Sopenharmony_ci return txq * 2 + 1; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic int vq2rxq(struct virtqueue *vq) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci return vq->index / 2; 37062306a36Sopenharmony_ci} 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_cistatic int rxq2vq(int rxq) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci return rxq * 2; 37562306a36Sopenharmony_ci} 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_cistatic inline struct virtio_net_common_hdr * 37862306a36Sopenharmony_ciskb_vnet_common_hdr(struct sk_buff *skb) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci return (struct virtio_net_common_hdr *)skb->cb; 38162306a36Sopenharmony_ci} 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci/* 38462306a36Sopenharmony_ci * private is used to chain pages for big packets, put the whole 38562306a36Sopenharmony_ci * most recent used list in the beginning for reuse 38662306a36Sopenharmony_ci */ 38762306a36Sopenharmony_cistatic void give_pages(struct receive_queue *rq, struct page *page) 38862306a36Sopenharmony_ci{ 38962306a36Sopenharmony_ci struct page *end; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci /* Find end of list, sew whole thing into vi->rq.pages. */ 39262306a36Sopenharmony_ci for (end = page; end->private; end = (struct page *)end->private); 39362306a36Sopenharmony_ci end->private = (unsigned long)rq->pages; 39462306a36Sopenharmony_ci rq->pages = page; 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_cistatic struct page *get_a_page(struct receive_queue *rq, gfp_t gfp_mask) 39862306a36Sopenharmony_ci{ 39962306a36Sopenharmony_ci struct page *p = rq->pages; 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci if (p) { 40262306a36Sopenharmony_ci rq->pages = (struct page *)p->private; 40362306a36Sopenharmony_ci /* clear private here, it is used to chain pages */ 40462306a36Sopenharmony_ci p->private = 0; 40562306a36Sopenharmony_ci } else 40662306a36Sopenharmony_ci p = alloc_page(gfp_mask); 40762306a36Sopenharmony_ci return p; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic void virtnet_rq_free_buf(struct virtnet_info *vi, 41162306a36Sopenharmony_ci struct receive_queue *rq, void *buf) 41262306a36Sopenharmony_ci{ 41362306a36Sopenharmony_ci if (vi->mergeable_rx_bufs) 41462306a36Sopenharmony_ci put_page(virt_to_head_page(buf)); 41562306a36Sopenharmony_ci else if (vi->big_packets) 41662306a36Sopenharmony_ci give_pages(rq, buf); 41762306a36Sopenharmony_ci else 41862306a36Sopenharmony_ci put_page(virt_to_head_page(buf)); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic void enable_delayed_refill(struct virtnet_info *vi) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci spin_lock_bh(&vi->refill_lock); 42462306a36Sopenharmony_ci vi->refill_enabled = true; 42562306a36Sopenharmony_ci spin_unlock_bh(&vi->refill_lock); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void disable_delayed_refill(struct virtnet_info *vi) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci spin_lock_bh(&vi->refill_lock); 43162306a36Sopenharmony_ci vi->refill_enabled = false; 43262306a36Sopenharmony_ci spin_unlock_bh(&vi->refill_lock); 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void virtqueue_napi_schedule(struct napi_struct *napi, 43662306a36Sopenharmony_ci struct virtqueue *vq) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci if (napi_schedule_prep(napi)) { 43962306a36Sopenharmony_ci virtqueue_disable_cb(vq); 44062306a36Sopenharmony_ci __napi_schedule(napi); 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic void virtqueue_napi_complete(struct napi_struct *napi, 44562306a36Sopenharmony_ci struct virtqueue *vq, int processed) 44662306a36Sopenharmony_ci{ 44762306a36Sopenharmony_ci int opaque; 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci opaque = virtqueue_enable_cb_prepare(vq); 45062306a36Sopenharmony_ci if (napi_complete_done(napi, processed)) { 45162306a36Sopenharmony_ci if (unlikely(virtqueue_poll(vq, opaque))) 45262306a36Sopenharmony_ci virtqueue_napi_schedule(napi, vq); 45362306a36Sopenharmony_ci } else { 45462306a36Sopenharmony_ci virtqueue_disable_cb(vq); 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_cistatic void skb_xmit_done(struct virtqueue *vq) 45962306a36Sopenharmony_ci{ 46062306a36Sopenharmony_ci struct virtnet_info *vi = vq->vdev->priv; 46162306a36Sopenharmony_ci struct napi_struct *napi = &vi->sq[vq2txq(vq)].napi; 46262306a36Sopenharmony_ci 46362306a36Sopenharmony_ci /* Suppress further interrupts. */ 46462306a36Sopenharmony_ci virtqueue_disable_cb(vq); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci if (napi->weight) 46762306a36Sopenharmony_ci virtqueue_napi_schedule(napi, vq); 46862306a36Sopenharmony_ci else 46962306a36Sopenharmony_ci /* We were probably waiting for more output buffers. */ 47062306a36Sopenharmony_ci netif_wake_subqueue(vi->dev, vq2txq(vq)); 47162306a36Sopenharmony_ci} 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci#define MRG_CTX_HEADER_SHIFT 22 47462306a36Sopenharmony_cistatic void *mergeable_len_to_ctx(unsigned int truesize, 47562306a36Sopenharmony_ci unsigned int headroom) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci return (void *)(unsigned long)((headroom << MRG_CTX_HEADER_SHIFT) | truesize); 47862306a36Sopenharmony_ci} 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_cistatic unsigned int mergeable_ctx_to_headroom(void *mrg_ctx) 48162306a36Sopenharmony_ci{ 48262306a36Sopenharmony_ci return (unsigned long)mrg_ctx >> MRG_CTX_HEADER_SHIFT; 48362306a36Sopenharmony_ci} 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_cistatic unsigned int mergeable_ctx_to_truesize(void *mrg_ctx) 48662306a36Sopenharmony_ci{ 48762306a36Sopenharmony_ci return (unsigned long)mrg_ctx & ((1 << MRG_CTX_HEADER_SHIFT) - 1); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_cistatic struct sk_buff *virtnet_build_skb(void *buf, unsigned int buflen, 49162306a36Sopenharmony_ci unsigned int headroom, 49262306a36Sopenharmony_ci unsigned int len) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct sk_buff *skb; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci skb = build_skb(buf, buflen); 49762306a36Sopenharmony_ci if (unlikely(!skb)) 49862306a36Sopenharmony_ci return NULL; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci skb_reserve(skb, headroom); 50162306a36Sopenharmony_ci skb_put(skb, len); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return skb; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci/* Called from bottom half context */ 50762306a36Sopenharmony_cistatic struct sk_buff *page_to_skb(struct virtnet_info *vi, 50862306a36Sopenharmony_ci struct receive_queue *rq, 50962306a36Sopenharmony_ci struct page *page, unsigned int offset, 51062306a36Sopenharmony_ci unsigned int len, unsigned int truesize, 51162306a36Sopenharmony_ci unsigned int headroom) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct sk_buff *skb; 51462306a36Sopenharmony_ci struct virtio_net_common_hdr *hdr; 51562306a36Sopenharmony_ci unsigned int copy, hdr_len, hdr_padded_len; 51662306a36Sopenharmony_ci struct page *page_to_free = NULL; 51762306a36Sopenharmony_ci int tailroom, shinfo_size; 51862306a36Sopenharmony_ci char *p, *hdr_p, *buf; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci p = page_address(page) + offset; 52162306a36Sopenharmony_ci hdr_p = p; 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci hdr_len = vi->hdr_len; 52462306a36Sopenharmony_ci if (vi->mergeable_rx_bufs) 52562306a36Sopenharmony_ci hdr_padded_len = hdr_len; 52662306a36Sopenharmony_ci else 52762306a36Sopenharmony_ci hdr_padded_len = sizeof(struct padded_vnet_hdr); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci buf = p - headroom; 53062306a36Sopenharmony_ci len -= hdr_len; 53162306a36Sopenharmony_ci offset += hdr_padded_len; 53262306a36Sopenharmony_ci p += hdr_padded_len; 53362306a36Sopenharmony_ci tailroom = truesize - headroom - hdr_padded_len - len; 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci shinfo_size = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci /* copy small packet so we can reuse these pages */ 53862306a36Sopenharmony_ci if (!NET_IP_ALIGN && len > GOOD_COPY_LEN && tailroom >= shinfo_size) { 53962306a36Sopenharmony_ci skb = virtnet_build_skb(buf, truesize, p - buf, len); 54062306a36Sopenharmony_ci if (unlikely(!skb)) 54162306a36Sopenharmony_ci return NULL; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci page = (struct page *)page->private; 54462306a36Sopenharmony_ci if (page) 54562306a36Sopenharmony_ci give_pages(rq, page); 54662306a36Sopenharmony_ci goto ok; 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* copy small packet so we can reuse these pages for small data */ 55062306a36Sopenharmony_ci skb = napi_alloc_skb(&rq->napi, GOOD_COPY_LEN); 55162306a36Sopenharmony_ci if (unlikely(!skb)) 55262306a36Sopenharmony_ci return NULL; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci /* Copy all frame if it fits skb->head, otherwise 55562306a36Sopenharmony_ci * we let virtio_net_hdr_to_skb() and GRO pull headers as needed. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_ci if (len <= skb_tailroom(skb)) 55862306a36Sopenharmony_ci copy = len; 55962306a36Sopenharmony_ci else 56062306a36Sopenharmony_ci copy = ETH_HLEN; 56162306a36Sopenharmony_ci skb_put_data(skb, p, copy); 56262306a36Sopenharmony_ci 56362306a36Sopenharmony_ci len -= copy; 56462306a36Sopenharmony_ci offset += copy; 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci if (vi->mergeable_rx_bufs) { 56762306a36Sopenharmony_ci if (len) 56862306a36Sopenharmony_ci skb_add_rx_frag(skb, 0, page, offset, len, truesize); 56962306a36Sopenharmony_ci else 57062306a36Sopenharmony_ci page_to_free = page; 57162306a36Sopenharmony_ci goto ok; 57262306a36Sopenharmony_ci } 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* 57562306a36Sopenharmony_ci * Verify that we can indeed put this data into a skb. 57662306a36Sopenharmony_ci * This is here to handle cases when the device erroneously 57762306a36Sopenharmony_ci * tries to receive more than is possible. This is usually 57862306a36Sopenharmony_ci * the case of a broken device. 57962306a36Sopenharmony_ci */ 58062306a36Sopenharmony_ci if (unlikely(len > MAX_SKB_FRAGS * PAGE_SIZE)) { 58162306a36Sopenharmony_ci net_dbg_ratelimited("%s: too much data\n", skb->dev->name); 58262306a36Sopenharmony_ci dev_kfree_skb(skb); 58362306a36Sopenharmony_ci return NULL; 58462306a36Sopenharmony_ci } 58562306a36Sopenharmony_ci BUG_ON(offset >= PAGE_SIZE); 58662306a36Sopenharmony_ci while (len) { 58762306a36Sopenharmony_ci unsigned int frag_size = min((unsigned)PAGE_SIZE - offset, len); 58862306a36Sopenharmony_ci skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags, page, offset, 58962306a36Sopenharmony_ci frag_size, truesize); 59062306a36Sopenharmony_ci len -= frag_size; 59162306a36Sopenharmony_ci page = (struct page *)page->private; 59262306a36Sopenharmony_ci offset = 0; 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci if (page) 59662306a36Sopenharmony_ci give_pages(rq, page); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ciok: 59962306a36Sopenharmony_ci hdr = skb_vnet_common_hdr(skb); 60062306a36Sopenharmony_ci memcpy(hdr, hdr_p, hdr_len); 60162306a36Sopenharmony_ci if (page_to_free) 60262306a36Sopenharmony_ci put_page(page_to_free); 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_ci return skb; 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic void virtnet_rq_unmap(struct receive_queue *rq, void *buf, u32 len) 60862306a36Sopenharmony_ci{ 60962306a36Sopenharmony_ci struct page *page = virt_to_head_page(buf); 61062306a36Sopenharmony_ci struct virtnet_rq_dma *dma; 61162306a36Sopenharmony_ci void *head; 61262306a36Sopenharmony_ci int offset; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci head = page_address(page); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci dma = head; 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci --dma->ref; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci if (dma->need_sync && len) { 62162306a36Sopenharmony_ci offset = buf - (head + sizeof(*dma)); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci virtqueue_dma_sync_single_range_for_cpu(rq->vq, dma->addr, 62462306a36Sopenharmony_ci offset, len, 62562306a36Sopenharmony_ci DMA_FROM_DEVICE); 62662306a36Sopenharmony_ci } 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (dma->ref) 62962306a36Sopenharmony_ci return; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci virtqueue_dma_unmap_single_attrs(rq->vq, dma->addr, dma->len, 63262306a36Sopenharmony_ci DMA_FROM_DEVICE, DMA_ATTR_SKIP_CPU_SYNC); 63362306a36Sopenharmony_ci put_page(page); 63462306a36Sopenharmony_ci} 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_cistatic void *virtnet_rq_get_buf(struct receive_queue *rq, u32 *len, void **ctx) 63762306a36Sopenharmony_ci{ 63862306a36Sopenharmony_ci void *buf; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci buf = virtqueue_get_buf_ctx(rq->vq, len, ctx); 64162306a36Sopenharmony_ci if (buf && rq->do_dma) 64262306a36Sopenharmony_ci virtnet_rq_unmap(rq, buf, *len); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci return buf; 64562306a36Sopenharmony_ci} 64662306a36Sopenharmony_ci 64762306a36Sopenharmony_cistatic void virtnet_rq_init_one_sg(struct receive_queue *rq, void *buf, u32 len) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci struct virtnet_rq_dma *dma; 65062306a36Sopenharmony_ci dma_addr_t addr; 65162306a36Sopenharmony_ci u32 offset; 65262306a36Sopenharmony_ci void *head; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci if (!rq->do_dma) { 65562306a36Sopenharmony_ci sg_init_one(rq->sg, buf, len); 65662306a36Sopenharmony_ci return; 65762306a36Sopenharmony_ci } 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci head = page_address(rq->alloc_frag.page); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci offset = buf - head; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci dma = head; 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci addr = dma->addr - sizeof(*dma) + offset; 66662306a36Sopenharmony_ci 66762306a36Sopenharmony_ci sg_init_table(rq->sg, 1); 66862306a36Sopenharmony_ci rq->sg[0].dma_address = addr; 66962306a36Sopenharmony_ci rq->sg[0].length = len; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void *virtnet_rq_alloc(struct receive_queue *rq, u32 size, gfp_t gfp) 67362306a36Sopenharmony_ci{ 67462306a36Sopenharmony_ci struct page_frag *alloc_frag = &rq->alloc_frag; 67562306a36Sopenharmony_ci struct virtnet_rq_dma *dma; 67662306a36Sopenharmony_ci void *buf, *head; 67762306a36Sopenharmony_ci dma_addr_t addr; 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci if (unlikely(!skb_page_frag_refill(size, alloc_frag, gfp))) 68062306a36Sopenharmony_ci return NULL; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci head = page_address(alloc_frag->page); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (rq->do_dma) { 68562306a36Sopenharmony_ci dma = head; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* new pages */ 68862306a36Sopenharmony_ci if (!alloc_frag->offset) { 68962306a36Sopenharmony_ci if (rq->last_dma) { 69062306a36Sopenharmony_ci /* Now, the new page is allocated, the last dma 69162306a36Sopenharmony_ci * will not be used. So the dma can be unmapped 69262306a36Sopenharmony_ci * if the ref is 0. 69362306a36Sopenharmony_ci */ 69462306a36Sopenharmony_ci virtnet_rq_unmap(rq, rq->last_dma, 0); 69562306a36Sopenharmony_ci rq->last_dma = NULL; 69662306a36Sopenharmony_ci } 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci dma->len = alloc_frag->size - sizeof(*dma); 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci addr = virtqueue_dma_map_single_attrs(rq->vq, dma + 1, 70162306a36Sopenharmony_ci dma->len, DMA_FROM_DEVICE, 0); 70262306a36Sopenharmony_ci if (virtqueue_dma_mapping_error(rq->vq, addr)) 70362306a36Sopenharmony_ci return NULL; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci dma->addr = addr; 70662306a36Sopenharmony_ci dma->need_sync = virtqueue_dma_need_sync(rq->vq, addr); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Add a reference to dma to prevent the entire dma from 70962306a36Sopenharmony_ci * being released during error handling. This reference 71062306a36Sopenharmony_ci * will be freed after the pages are no longer used. 71162306a36Sopenharmony_ci */ 71262306a36Sopenharmony_ci get_page(alloc_frag->page); 71362306a36Sopenharmony_ci dma->ref = 1; 71462306a36Sopenharmony_ci alloc_frag->offset = sizeof(*dma); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci rq->last_dma = dma; 71762306a36Sopenharmony_ci } 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci ++dma->ref; 72062306a36Sopenharmony_ci } 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci buf = head + alloc_frag->offset; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci get_page(alloc_frag->page); 72562306a36Sopenharmony_ci alloc_frag->offset += size; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci return buf; 72862306a36Sopenharmony_ci} 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_cistatic void virtnet_rq_set_premapped(struct virtnet_info *vi) 73162306a36Sopenharmony_ci{ 73262306a36Sopenharmony_ci int i; 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci /* disable for big mode */ 73562306a36Sopenharmony_ci if (!vi->mergeable_rx_bufs && vi->big_packets) 73662306a36Sopenharmony_ci return; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 73962306a36Sopenharmony_ci if (virtqueue_set_dma_premapped(vi->rq[i].vq)) 74062306a36Sopenharmony_ci continue; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci vi->rq[i].do_dma = true; 74362306a36Sopenharmony_ci } 74462306a36Sopenharmony_ci} 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_cistatic void virtnet_rq_unmap_free_buf(struct virtqueue *vq, void *buf) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct virtnet_info *vi = vq->vdev->priv; 74962306a36Sopenharmony_ci struct receive_queue *rq; 75062306a36Sopenharmony_ci int i = vq2rxq(vq); 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci rq = &vi->rq[i]; 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_ci if (rq->do_dma) 75562306a36Sopenharmony_ci virtnet_rq_unmap(rq, buf, 0); 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci virtnet_rq_free_buf(vi, rq, buf); 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_cistatic void free_old_xmit_skbs(struct send_queue *sq, bool in_napi) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci unsigned int len; 76362306a36Sopenharmony_ci unsigned int packets = 0; 76462306a36Sopenharmony_ci unsigned int bytes = 0; 76562306a36Sopenharmony_ci void *ptr; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci while ((ptr = virtqueue_get_buf(sq->vq, &len)) != NULL) { 76862306a36Sopenharmony_ci if (likely(!is_xdp_frame(ptr))) { 76962306a36Sopenharmony_ci struct sk_buff *skb = ptr; 77062306a36Sopenharmony_ci 77162306a36Sopenharmony_ci pr_debug("Sent skb %p\n", skb); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci bytes += skb->len; 77462306a36Sopenharmony_ci napi_consume_skb(skb, in_napi); 77562306a36Sopenharmony_ci } else { 77662306a36Sopenharmony_ci struct xdp_frame *frame = ptr_to_xdp(ptr); 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_ci bytes += xdp_get_frame_len(frame); 77962306a36Sopenharmony_ci xdp_return_frame(frame); 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci packets++; 78262306a36Sopenharmony_ci } 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci /* Avoid overhead when no packets have been processed 78562306a36Sopenharmony_ci * happens when called speculatively from start_xmit. 78662306a36Sopenharmony_ci */ 78762306a36Sopenharmony_ci if (!packets) 78862306a36Sopenharmony_ci return; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci u64_stats_update_begin(&sq->stats.syncp); 79162306a36Sopenharmony_ci u64_stats_add(&sq->stats.bytes, bytes); 79262306a36Sopenharmony_ci u64_stats_add(&sq->stats.packets, packets); 79362306a36Sopenharmony_ci u64_stats_update_end(&sq->stats.syncp); 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_cistatic bool is_xdp_raw_buffer_queue(struct virtnet_info *vi, int q) 79762306a36Sopenharmony_ci{ 79862306a36Sopenharmony_ci if (q < (vi->curr_queue_pairs - vi->xdp_queue_pairs)) 79962306a36Sopenharmony_ci return false; 80062306a36Sopenharmony_ci else if (q < vi->curr_queue_pairs) 80162306a36Sopenharmony_ci return true; 80262306a36Sopenharmony_ci else 80362306a36Sopenharmony_ci return false; 80462306a36Sopenharmony_ci} 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cistatic void check_sq_full_and_disable(struct virtnet_info *vi, 80762306a36Sopenharmony_ci struct net_device *dev, 80862306a36Sopenharmony_ci struct send_queue *sq) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci bool use_napi = sq->napi.weight; 81162306a36Sopenharmony_ci int qnum; 81262306a36Sopenharmony_ci 81362306a36Sopenharmony_ci qnum = sq - vi->sq; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci /* If running out of space, stop queue to avoid getting packets that we 81662306a36Sopenharmony_ci * are then unable to transmit. 81762306a36Sopenharmony_ci * An alternative would be to force queuing layer to requeue the skb by 81862306a36Sopenharmony_ci * returning NETDEV_TX_BUSY. However, NETDEV_TX_BUSY should not be 81962306a36Sopenharmony_ci * returned in a normal path of operation: it means that driver is not 82062306a36Sopenharmony_ci * maintaining the TX queue stop/start state properly, and causes 82162306a36Sopenharmony_ci * the stack to do a non-trivial amount of useless work. 82262306a36Sopenharmony_ci * Since most packets only take 1 or 2 ring slots, stopping the queue 82362306a36Sopenharmony_ci * early means 16 slots are typically wasted. 82462306a36Sopenharmony_ci */ 82562306a36Sopenharmony_ci if (sq->vq->num_free < 2+MAX_SKB_FRAGS) { 82662306a36Sopenharmony_ci netif_stop_subqueue(dev, qnum); 82762306a36Sopenharmony_ci if (use_napi) { 82862306a36Sopenharmony_ci if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) 82962306a36Sopenharmony_ci virtqueue_napi_schedule(&sq->napi, sq->vq); 83062306a36Sopenharmony_ci } else if (unlikely(!virtqueue_enable_cb_delayed(sq->vq))) { 83162306a36Sopenharmony_ci /* More just got used, free them then recheck. */ 83262306a36Sopenharmony_ci free_old_xmit_skbs(sq, false); 83362306a36Sopenharmony_ci if (sq->vq->num_free >= 2+MAX_SKB_FRAGS) { 83462306a36Sopenharmony_ci netif_start_subqueue(dev, qnum); 83562306a36Sopenharmony_ci virtqueue_disable_cb(sq->vq); 83662306a36Sopenharmony_ci } 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci} 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_cistatic int __virtnet_xdp_xmit_one(struct virtnet_info *vi, 84262306a36Sopenharmony_ci struct send_queue *sq, 84362306a36Sopenharmony_ci struct xdp_frame *xdpf) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf *hdr; 84662306a36Sopenharmony_ci struct skb_shared_info *shinfo; 84762306a36Sopenharmony_ci u8 nr_frags = 0; 84862306a36Sopenharmony_ci int err, i; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci if (unlikely(xdpf->headroom < vi->hdr_len)) 85162306a36Sopenharmony_ci return -EOVERFLOW; 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci if (unlikely(xdp_frame_has_frags(xdpf))) { 85462306a36Sopenharmony_ci shinfo = xdp_get_shared_info_from_frame(xdpf); 85562306a36Sopenharmony_ci nr_frags = shinfo->nr_frags; 85662306a36Sopenharmony_ci } 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci /* In wrapping function virtnet_xdp_xmit(), we need to free 85962306a36Sopenharmony_ci * up the pending old buffers, where we need to calculate the 86062306a36Sopenharmony_ci * position of skb_shared_info in xdp_get_frame_len() and 86162306a36Sopenharmony_ci * xdp_return_frame(), which will involve to xdpf->data and 86262306a36Sopenharmony_ci * xdpf->headroom. Therefore, we need to update the value of 86362306a36Sopenharmony_ci * headroom synchronously here. 86462306a36Sopenharmony_ci */ 86562306a36Sopenharmony_ci xdpf->headroom -= vi->hdr_len; 86662306a36Sopenharmony_ci xdpf->data -= vi->hdr_len; 86762306a36Sopenharmony_ci /* Zero header and leave csum up to XDP layers */ 86862306a36Sopenharmony_ci hdr = xdpf->data; 86962306a36Sopenharmony_ci memset(hdr, 0, vi->hdr_len); 87062306a36Sopenharmony_ci xdpf->len += vi->hdr_len; 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci sg_init_table(sq->sg, nr_frags + 1); 87362306a36Sopenharmony_ci sg_set_buf(sq->sg, xdpf->data, xdpf->len); 87462306a36Sopenharmony_ci for (i = 0; i < nr_frags; i++) { 87562306a36Sopenharmony_ci skb_frag_t *frag = &shinfo->frags[i]; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci sg_set_page(&sq->sg[i + 1], skb_frag_page(frag), 87862306a36Sopenharmony_ci skb_frag_size(frag), skb_frag_off(frag)); 87962306a36Sopenharmony_ci } 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci err = virtqueue_add_outbuf(sq->vq, sq->sg, nr_frags + 1, 88262306a36Sopenharmony_ci xdp_to_ptr(xdpf), GFP_ATOMIC); 88362306a36Sopenharmony_ci if (unlikely(err)) 88462306a36Sopenharmony_ci return -ENOSPC; /* Caller handle free/refcnt */ 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci return 0; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_ci/* when vi->curr_queue_pairs > nr_cpu_ids, the txq/sq is only used for xdp tx on 89062306a36Sopenharmony_ci * the current cpu, so it does not need to be locked. 89162306a36Sopenharmony_ci * 89262306a36Sopenharmony_ci * Here we use marco instead of inline functions because we have to deal with 89362306a36Sopenharmony_ci * three issues at the same time: 1. the choice of sq. 2. judge and execute the 89462306a36Sopenharmony_ci * lock/unlock of txq 3. make sparse happy. It is difficult for two inline 89562306a36Sopenharmony_ci * functions to perfectly solve these three problems at the same time. 89662306a36Sopenharmony_ci */ 89762306a36Sopenharmony_ci#define virtnet_xdp_get_sq(vi) ({ \ 89862306a36Sopenharmony_ci int cpu = smp_processor_id(); \ 89962306a36Sopenharmony_ci struct netdev_queue *txq; \ 90062306a36Sopenharmony_ci typeof(vi) v = (vi); \ 90162306a36Sopenharmony_ci unsigned int qp; \ 90262306a36Sopenharmony_ci \ 90362306a36Sopenharmony_ci if (v->curr_queue_pairs > nr_cpu_ids) { \ 90462306a36Sopenharmony_ci qp = v->curr_queue_pairs - v->xdp_queue_pairs; \ 90562306a36Sopenharmony_ci qp += cpu; \ 90662306a36Sopenharmony_ci txq = netdev_get_tx_queue(v->dev, qp); \ 90762306a36Sopenharmony_ci __netif_tx_acquire(txq); \ 90862306a36Sopenharmony_ci } else { \ 90962306a36Sopenharmony_ci qp = cpu % v->curr_queue_pairs; \ 91062306a36Sopenharmony_ci txq = netdev_get_tx_queue(v->dev, qp); \ 91162306a36Sopenharmony_ci __netif_tx_lock(txq, cpu); \ 91262306a36Sopenharmony_ci } \ 91362306a36Sopenharmony_ci v->sq + qp; \ 91462306a36Sopenharmony_ci}) 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci#define virtnet_xdp_put_sq(vi, q) { \ 91762306a36Sopenharmony_ci struct netdev_queue *txq; \ 91862306a36Sopenharmony_ci typeof(vi) v = (vi); \ 91962306a36Sopenharmony_ci \ 92062306a36Sopenharmony_ci txq = netdev_get_tx_queue(v->dev, (q) - v->sq); \ 92162306a36Sopenharmony_ci if (v->curr_queue_pairs > nr_cpu_ids) \ 92262306a36Sopenharmony_ci __netif_tx_release(txq); \ 92362306a36Sopenharmony_ci else \ 92462306a36Sopenharmony_ci __netif_tx_unlock(txq); \ 92562306a36Sopenharmony_ci} 92662306a36Sopenharmony_ci 92762306a36Sopenharmony_cistatic int virtnet_xdp_xmit(struct net_device *dev, 92862306a36Sopenharmony_ci int n, struct xdp_frame **frames, u32 flags) 92962306a36Sopenharmony_ci{ 93062306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 93162306a36Sopenharmony_ci struct receive_queue *rq = vi->rq; 93262306a36Sopenharmony_ci struct bpf_prog *xdp_prog; 93362306a36Sopenharmony_ci struct send_queue *sq; 93462306a36Sopenharmony_ci unsigned int len; 93562306a36Sopenharmony_ci int packets = 0; 93662306a36Sopenharmony_ci int bytes = 0; 93762306a36Sopenharmony_ci int nxmit = 0; 93862306a36Sopenharmony_ci int kicks = 0; 93962306a36Sopenharmony_ci void *ptr; 94062306a36Sopenharmony_ci int ret; 94162306a36Sopenharmony_ci int i; 94262306a36Sopenharmony_ci 94362306a36Sopenharmony_ci /* Only allow ndo_xdp_xmit if XDP is loaded on dev, as this 94462306a36Sopenharmony_ci * indicate XDP resources have been successfully allocated. 94562306a36Sopenharmony_ci */ 94662306a36Sopenharmony_ci xdp_prog = rcu_access_pointer(rq->xdp_prog); 94762306a36Sopenharmony_ci if (!xdp_prog) 94862306a36Sopenharmony_ci return -ENXIO; 94962306a36Sopenharmony_ci 95062306a36Sopenharmony_ci sq = virtnet_xdp_get_sq(vi); 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci if (unlikely(flags & ~XDP_XMIT_FLAGS_MASK)) { 95362306a36Sopenharmony_ci ret = -EINVAL; 95462306a36Sopenharmony_ci goto out; 95562306a36Sopenharmony_ci } 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci /* Free up any pending old buffers before queueing new ones. */ 95862306a36Sopenharmony_ci while ((ptr = virtqueue_get_buf(sq->vq, &len)) != NULL) { 95962306a36Sopenharmony_ci if (likely(is_xdp_frame(ptr))) { 96062306a36Sopenharmony_ci struct xdp_frame *frame = ptr_to_xdp(ptr); 96162306a36Sopenharmony_ci 96262306a36Sopenharmony_ci bytes += xdp_get_frame_len(frame); 96362306a36Sopenharmony_ci xdp_return_frame(frame); 96462306a36Sopenharmony_ci } else { 96562306a36Sopenharmony_ci struct sk_buff *skb = ptr; 96662306a36Sopenharmony_ci 96762306a36Sopenharmony_ci bytes += skb->len; 96862306a36Sopenharmony_ci napi_consume_skb(skb, false); 96962306a36Sopenharmony_ci } 97062306a36Sopenharmony_ci packets++; 97162306a36Sopenharmony_ci } 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci for (i = 0; i < n; i++) { 97462306a36Sopenharmony_ci struct xdp_frame *xdpf = frames[i]; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci if (__virtnet_xdp_xmit_one(vi, sq, xdpf)) 97762306a36Sopenharmony_ci break; 97862306a36Sopenharmony_ci nxmit++; 97962306a36Sopenharmony_ci } 98062306a36Sopenharmony_ci ret = nxmit; 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (!is_xdp_raw_buffer_queue(vi, sq - vi->sq)) 98362306a36Sopenharmony_ci check_sq_full_and_disable(vi, dev, sq); 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci if (flags & XDP_XMIT_FLUSH) { 98662306a36Sopenharmony_ci if (virtqueue_kick_prepare(sq->vq) && virtqueue_notify(sq->vq)) 98762306a36Sopenharmony_ci kicks = 1; 98862306a36Sopenharmony_ci } 98962306a36Sopenharmony_ciout: 99062306a36Sopenharmony_ci u64_stats_update_begin(&sq->stats.syncp); 99162306a36Sopenharmony_ci u64_stats_add(&sq->stats.bytes, bytes); 99262306a36Sopenharmony_ci u64_stats_add(&sq->stats.packets, packets); 99362306a36Sopenharmony_ci u64_stats_add(&sq->stats.xdp_tx, n); 99462306a36Sopenharmony_ci u64_stats_add(&sq->stats.xdp_tx_drops, n - nxmit); 99562306a36Sopenharmony_ci u64_stats_add(&sq->stats.kicks, kicks); 99662306a36Sopenharmony_ci u64_stats_update_end(&sq->stats.syncp); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci virtnet_xdp_put_sq(vi, sq); 99962306a36Sopenharmony_ci return ret; 100062306a36Sopenharmony_ci} 100162306a36Sopenharmony_ci 100262306a36Sopenharmony_cistatic void put_xdp_frags(struct xdp_buff *xdp) 100362306a36Sopenharmony_ci{ 100462306a36Sopenharmony_ci struct skb_shared_info *shinfo; 100562306a36Sopenharmony_ci struct page *xdp_page; 100662306a36Sopenharmony_ci int i; 100762306a36Sopenharmony_ci 100862306a36Sopenharmony_ci if (xdp_buff_has_frags(xdp)) { 100962306a36Sopenharmony_ci shinfo = xdp_get_shared_info_from_buff(xdp); 101062306a36Sopenharmony_ci for (i = 0; i < shinfo->nr_frags; i++) { 101162306a36Sopenharmony_ci xdp_page = skb_frag_page(&shinfo->frags[i]); 101262306a36Sopenharmony_ci put_page(xdp_page); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci } 101562306a36Sopenharmony_ci} 101662306a36Sopenharmony_ci 101762306a36Sopenharmony_cistatic int virtnet_xdp_handler(struct bpf_prog *xdp_prog, struct xdp_buff *xdp, 101862306a36Sopenharmony_ci struct net_device *dev, 101962306a36Sopenharmony_ci unsigned int *xdp_xmit, 102062306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 102162306a36Sopenharmony_ci{ 102262306a36Sopenharmony_ci struct xdp_frame *xdpf; 102362306a36Sopenharmony_ci int err; 102462306a36Sopenharmony_ci u32 act; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci act = bpf_prog_run_xdp(xdp_prog, xdp); 102762306a36Sopenharmony_ci u64_stats_inc(&stats->xdp_packets); 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci switch (act) { 103062306a36Sopenharmony_ci case XDP_PASS: 103162306a36Sopenharmony_ci return act; 103262306a36Sopenharmony_ci 103362306a36Sopenharmony_ci case XDP_TX: 103462306a36Sopenharmony_ci u64_stats_inc(&stats->xdp_tx); 103562306a36Sopenharmony_ci xdpf = xdp_convert_buff_to_frame(xdp); 103662306a36Sopenharmony_ci if (unlikely(!xdpf)) { 103762306a36Sopenharmony_ci netdev_dbg(dev, "convert buff to frame failed for xdp\n"); 103862306a36Sopenharmony_ci return XDP_DROP; 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci err = virtnet_xdp_xmit(dev, 1, &xdpf, 0); 104262306a36Sopenharmony_ci if (unlikely(!err)) { 104362306a36Sopenharmony_ci xdp_return_frame_rx_napi(xdpf); 104462306a36Sopenharmony_ci } else if (unlikely(err < 0)) { 104562306a36Sopenharmony_ci trace_xdp_exception(dev, xdp_prog, act); 104662306a36Sopenharmony_ci return XDP_DROP; 104762306a36Sopenharmony_ci } 104862306a36Sopenharmony_ci *xdp_xmit |= VIRTIO_XDP_TX; 104962306a36Sopenharmony_ci return act; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci case XDP_REDIRECT: 105262306a36Sopenharmony_ci u64_stats_inc(&stats->xdp_redirects); 105362306a36Sopenharmony_ci err = xdp_do_redirect(dev, xdp, xdp_prog); 105462306a36Sopenharmony_ci if (err) 105562306a36Sopenharmony_ci return XDP_DROP; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci *xdp_xmit |= VIRTIO_XDP_REDIR; 105862306a36Sopenharmony_ci return act; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci default: 106162306a36Sopenharmony_ci bpf_warn_invalid_xdp_action(dev, xdp_prog, act); 106262306a36Sopenharmony_ci fallthrough; 106362306a36Sopenharmony_ci case XDP_ABORTED: 106462306a36Sopenharmony_ci trace_xdp_exception(dev, xdp_prog, act); 106562306a36Sopenharmony_ci fallthrough; 106662306a36Sopenharmony_ci case XDP_DROP: 106762306a36Sopenharmony_ci return XDP_DROP; 106862306a36Sopenharmony_ci } 106962306a36Sopenharmony_ci} 107062306a36Sopenharmony_ci 107162306a36Sopenharmony_cistatic unsigned int virtnet_get_headroom(struct virtnet_info *vi) 107262306a36Sopenharmony_ci{ 107362306a36Sopenharmony_ci return vi->xdp_enabled ? VIRTIO_XDP_HEADROOM : 0; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci/* We copy the packet for XDP in the following cases: 107762306a36Sopenharmony_ci * 107862306a36Sopenharmony_ci * 1) Packet is scattered across multiple rx buffers. 107962306a36Sopenharmony_ci * 2) Headroom space is insufficient. 108062306a36Sopenharmony_ci * 108162306a36Sopenharmony_ci * This is inefficient but it's a temporary condition that 108262306a36Sopenharmony_ci * we hit right after XDP is enabled and until queue is refilled 108362306a36Sopenharmony_ci * with large buffers with sufficient headroom - so it should affect 108462306a36Sopenharmony_ci * at most queue size packets. 108562306a36Sopenharmony_ci * Afterwards, the conditions to enable 108662306a36Sopenharmony_ci * XDP should preclude the underlying device from sending packets 108762306a36Sopenharmony_ci * across multiple buffers (num_buf > 1), and we make sure buffers 108862306a36Sopenharmony_ci * have enough headroom. 108962306a36Sopenharmony_ci */ 109062306a36Sopenharmony_cistatic struct page *xdp_linearize_page(struct receive_queue *rq, 109162306a36Sopenharmony_ci int *num_buf, 109262306a36Sopenharmony_ci struct page *p, 109362306a36Sopenharmony_ci int offset, 109462306a36Sopenharmony_ci int page_off, 109562306a36Sopenharmony_ci unsigned int *len) 109662306a36Sopenharmony_ci{ 109762306a36Sopenharmony_ci int tailroom = SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 109862306a36Sopenharmony_ci struct page *page; 109962306a36Sopenharmony_ci 110062306a36Sopenharmony_ci if (page_off + *len + tailroom > PAGE_SIZE) 110162306a36Sopenharmony_ci return NULL; 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci page = alloc_page(GFP_ATOMIC); 110462306a36Sopenharmony_ci if (!page) 110562306a36Sopenharmony_ci return NULL; 110662306a36Sopenharmony_ci 110762306a36Sopenharmony_ci memcpy(page_address(page) + page_off, page_address(p) + offset, *len); 110862306a36Sopenharmony_ci page_off += *len; 110962306a36Sopenharmony_ci 111062306a36Sopenharmony_ci while (--*num_buf) { 111162306a36Sopenharmony_ci unsigned int buflen; 111262306a36Sopenharmony_ci void *buf; 111362306a36Sopenharmony_ci int off; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci buf = virtnet_rq_get_buf(rq, &buflen, NULL); 111662306a36Sopenharmony_ci if (unlikely(!buf)) 111762306a36Sopenharmony_ci goto err_buf; 111862306a36Sopenharmony_ci 111962306a36Sopenharmony_ci p = virt_to_head_page(buf); 112062306a36Sopenharmony_ci off = buf - page_address(p); 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci /* guard against a misconfigured or uncooperative backend that 112362306a36Sopenharmony_ci * is sending packet larger than the MTU. 112462306a36Sopenharmony_ci */ 112562306a36Sopenharmony_ci if ((page_off + buflen + tailroom) > PAGE_SIZE) { 112662306a36Sopenharmony_ci put_page(p); 112762306a36Sopenharmony_ci goto err_buf; 112862306a36Sopenharmony_ci } 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_ci memcpy(page_address(page) + page_off, 113162306a36Sopenharmony_ci page_address(p) + off, buflen); 113262306a36Sopenharmony_ci page_off += buflen; 113362306a36Sopenharmony_ci put_page(p); 113462306a36Sopenharmony_ci } 113562306a36Sopenharmony_ci 113662306a36Sopenharmony_ci /* Headroom does not contribute to packet length */ 113762306a36Sopenharmony_ci *len = page_off - VIRTIO_XDP_HEADROOM; 113862306a36Sopenharmony_ci return page; 113962306a36Sopenharmony_cierr_buf: 114062306a36Sopenharmony_ci __free_pages(page, 0); 114162306a36Sopenharmony_ci return NULL; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cistatic struct sk_buff *receive_small_build_skb(struct virtnet_info *vi, 114562306a36Sopenharmony_ci unsigned int xdp_headroom, 114662306a36Sopenharmony_ci void *buf, 114762306a36Sopenharmony_ci unsigned int len) 114862306a36Sopenharmony_ci{ 114962306a36Sopenharmony_ci unsigned int header_offset; 115062306a36Sopenharmony_ci unsigned int headroom; 115162306a36Sopenharmony_ci unsigned int buflen; 115262306a36Sopenharmony_ci struct sk_buff *skb; 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci header_offset = VIRTNET_RX_PAD + xdp_headroom; 115562306a36Sopenharmony_ci headroom = vi->hdr_len + header_offset; 115662306a36Sopenharmony_ci buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + 115762306a36Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci skb = virtnet_build_skb(buf, buflen, headroom, len); 116062306a36Sopenharmony_ci if (unlikely(!skb)) 116162306a36Sopenharmony_ci return NULL; 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci buf += header_offset; 116462306a36Sopenharmony_ci memcpy(skb_vnet_common_hdr(skb), buf, vi->hdr_len); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci return skb; 116762306a36Sopenharmony_ci} 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_cistatic struct sk_buff *receive_small_xdp(struct net_device *dev, 117062306a36Sopenharmony_ci struct virtnet_info *vi, 117162306a36Sopenharmony_ci struct receive_queue *rq, 117262306a36Sopenharmony_ci struct bpf_prog *xdp_prog, 117362306a36Sopenharmony_ci void *buf, 117462306a36Sopenharmony_ci unsigned int xdp_headroom, 117562306a36Sopenharmony_ci unsigned int len, 117662306a36Sopenharmony_ci unsigned int *xdp_xmit, 117762306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci unsigned int header_offset = VIRTNET_RX_PAD + xdp_headroom; 118062306a36Sopenharmony_ci unsigned int headroom = vi->hdr_len + header_offset; 118162306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf *hdr = buf + header_offset; 118262306a36Sopenharmony_ci struct page *page = virt_to_head_page(buf); 118362306a36Sopenharmony_ci struct page *xdp_page; 118462306a36Sopenharmony_ci unsigned int buflen; 118562306a36Sopenharmony_ci struct xdp_buff xdp; 118662306a36Sopenharmony_ci struct sk_buff *skb; 118762306a36Sopenharmony_ci unsigned int metasize = 0; 118862306a36Sopenharmony_ci u32 act; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci if (unlikely(hdr->hdr.gso_type)) 119162306a36Sopenharmony_ci goto err_xdp; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_ci buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + 119462306a36Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 119562306a36Sopenharmony_ci 119662306a36Sopenharmony_ci if (unlikely(xdp_headroom < virtnet_get_headroom(vi))) { 119762306a36Sopenharmony_ci int offset = buf - page_address(page) + header_offset; 119862306a36Sopenharmony_ci unsigned int tlen = len + vi->hdr_len; 119962306a36Sopenharmony_ci int num_buf = 1; 120062306a36Sopenharmony_ci 120162306a36Sopenharmony_ci xdp_headroom = virtnet_get_headroom(vi); 120262306a36Sopenharmony_ci header_offset = VIRTNET_RX_PAD + xdp_headroom; 120362306a36Sopenharmony_ci headroom = vi->hdr_len + header_offset; 120462306a36Sopenharmony_ci buflen = SKB_DATA_ALIGN(GOOD_PACKET_LEN + headroom) + 120562306a36Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 120662306a36Sopenharmony_ci xdp_page = xdp_linearize_page(rq, &num_buf, page, 120762306a36Sopenharmony_ci offset, header_offset, 120862306a36Sopenharmony_ci &tlen); 120962306a36Sopenharmony_ci if (!xdp_page) 121062306a36Sopenharmony_ci goto err_xdp; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci buf = page_address(xdp_page); 121362306a36Sopenharmony_ci put_page(page); 121462306a36Sopenharmony_ci page = xdp_page; 121562306a36Sopenharmony_ci } 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci xdp_init_buff(&xdp, buflen, &rq->xdp_rxq); 121862306a36Sopenharmony_ci xdp_prepare_buff(&xdp, buf + VIRTNET_RX_PAD + vi->hdr_len, 121962306a36Sopenharmony_ci xdp_headroom, len, true); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci act = virtnet_xdp_handler(xdp_prog, &xdp, dev, xdp_xmit, stats); 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci switch (act) { 122462306a36Sopenharmony_ci case XDP_PASS: 122562306a36Sopenharmony_ci /* Recalculate length in case bpf program changed it */ 122662306a36Sopenharmony_ci len = xdp.data_end - xdp.data; 122762306a36Sopenharmony_ci metasize = xdp.data - xdp.data_meta; 122862306a36Sopenharmony_ci break; 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_ci case XDP_TX: 123162306a36Sopenharmony_ci case XDP_REDIRECT: 123262306a36Sopenharmony_ci goto xdp_xmit; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci default: 123562306a36Sopenharmony_ci goto err_xdp; 123662306a36Sopenharmony_ci } 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci skb = virtnet_build_skb(buf, buflen, xdp.data - buf, len); 123962306a36Sopenharmony_ci if (unlikely(!skb)) 124062306a36Sopenharmony_ci goto err; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci if (metasize) 124362306a36Sopenharmony_ci skb_metadata_set(skb, metasize); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci return skb; 124662306a36Sopenharmony_ci 124762306a36Sopenharmony_cierr_xdp: 124862306a36Sopenharmony_ci u64_stats_inc(&stats->xdp_drops); 124962306a36Sopenharmony_cierr: 125062306a36Sopenharmony_ci u64_stats_inc(&stats->drops); 125162306a36Sopenharmony_ci put_page(page); 125262306a36Sopenharmony_cixdp_xmit: 125362306a36Sopenharmony_ci return NULL; 125462306a36Sopenharmony_ci} 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_cistatic struct sk_buff *receive_small(struct net_device *dev, 125762306a36Sopenharmony_ci struct virtnet_info *vi, 125862306a36Sopenharmony_ci struct receive_queue *rq, 125962306a36Sopenharmony_ci void *buf, void *ctx, 126062306a36Sopenharmony_ci unsigned int len, 126162306a36Sopenharmony_ci unsigned int *xdp_xmit, 126262306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 126362306a36Sopenharmony_ci{ 126462306a36Sopenharmony_ci unsigned int xdp_headroom = (unsigned long)ctx; 126562306a36Sopenharmony_ci struct page *page = virt_to_head_page(buf); 126662306a36Sopenharmony_ci struct sk_buff *skb; 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci len -= vi->hdr_len; 126962306a36Sopenharmony_ci u64_stats_add(&stats->bytes, len); 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci if (unlikely(len > GOOD_PACKET_LEN)) { 127262306a36Sopenharmony_ci pr_debug("%s: rx error: len %u exceeds max size %d\n", 127362306a36Sopenharmony_ci dev->name, len, GOOD_PACKET_LEN); 127462306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 127562306a36Sopenharmony_ci goto err; 127662306a36Sopenharmony_ci } 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci if (unlikely(vi->xdp_enabled)) { 127962306a36Sopenharmony_ci struct bpf_prog *xdp_prog; 128062306a36Sopenharmony_ci 128162306a36Sopenharmony_ci rcu_read_lock(); 128262306a36Sopenharmony_ci xdp_prog = rcu_dereference(rq->xdp_prog); 128362306a36Sopenharmony_ci if (xdp_prog) { 128462306a36Sopenharmony_ci skb = receive_small_xdp(dev, vi, rq, xdp_prog, buf, 128562306a36Sopenharmony_ci xdp_headroom, len, xdp_xmit, 128662306a36Sopenharmony_ci stats); 128762306a36Sopenharmony_ci rcu_read_unlock(); 128862306a36Sopenharmony_ci return skb; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci rcu_read_unlock(); 129162306a36Sopenharmony_ci } 129262306a36Sopenharmony_ci 129362306a36Sopenharmony_ci skb = receive_small_build_skb(vi, xdp_headroom, buf, len); 129462306a36Sopenharmony_ci if (likely(skb)) 129562306a36Sopenharmony_ci return skb; 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cierr: 129862306a36Sopenharmony_ci u64_stats_inc(&stats->drops); 129962306a36Sopenharmony_ci put_page(page); 130062306a36Sopenharmony_ci return NULL; 130162306a36Sopenharmony_ci} 130262306a36Sopenharmony_ci 130362306a36Sopenharmony_cistatic struct sk_buff *receive_big(struct net_device *dev, 130462306a36Sopenharmony_ci struct virtnet_info *vi, 130562306a36Sopenharmony_ci struct receive_queue *rq, 130662306a36Sopenharmony_ci void *buf, 130762306a36Sopenharmony_ci unsigned int len, 130862306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 130962306a36Sopenharmony_ci{ 131062306a36Sopenharmony_ci struct page *page = buf; 131162306a36Sopenharmony_ci struct sk_buff *skb = 131262306a36Sopenharmony_ci page_to_skb(vi, rq, page, 0, len, PAGE_SIZE, 0); 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci u64_stats_add(&stats->bytes, len - vi->hdr_len); 131562306a36Sopenharmony_ci if (unlikely(!skb)) 131662306a36Sopenharmony_ci goto err; 131762306a36Sopenharmony_ci 131862306a36Sopenharmony_ci return skb; 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cierr: 132162306a36Sopenharmony_ci u64_stats_inc(&stats->drops); 132262306a36Sopenharmony_ci give_pages(rq, page); 132362306a36Sopenharmony_ci return NULL; 132462306a36Sopenharmony_ci} 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic void mergeable_buf_free(struct receive_queue *rq, int num_buf, 132762306a36Sopenharmony_ci struct net_device *dev, 132862306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 132962306a36Sopenharmony_ci{ 133062306a36Sopenharmony_ci struct page *page; 133162306a36Sopenharmony_ci void *buf; 133262306a36Sopenharmony_ci int len; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci while (num_buf-- > 1) { 133562306a36Sopenharmony_ci buf = virtnet_rq_get_buf(rq, &len, NULL); 133662306a36Sopenharmony_ci if (unlikely(!buf)) { 133762306a36Sopenharmony_ci pr_debug("%s: rx error: %d buffers missing\n", 133862306a36Sopenharmony_ci dev->name, num_buf); 133962306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci } 134262306a36Sopenharmony_ci u64_stats_add(&stats->bytes, len); 134362306a36Sopenharmony_ci page = virt_to_head_page(buf); 134462306a36Sopenharmony_ci put_page(page); 134562306a36Sopenharmony_ci } 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_ci/* Why not use xdp_build_skb_from_frame() ? 134962306a36Sopenharmony_ci * XDP core assumes that xdp frags are PAGE_SIZE in length, while in 135062306a36Sopenharmony_ci * virtio-net there are 2 points that do not match its requirements: 135162306a36Sopenharmony_ci * 1. The size of the prefilled buffer is not fixed before xdp is set. 135262306a36Sopenharmony_ci * 2. xdp_build_skb_from_frame() does more checks that we don't need, 135362306a36Sopenharmony_ci * like eth_type_trans() (which virtio-net does in receive_buf()). 135462306a36Sopenharmony_ci */ 135562306a36Sopenharmony_cistatic struct sk_buff *build_skb_from_xdp_buff(struct net_device *dev, 135662306a36Sopenharmony_ci struct virtnet_info *vi, 135762306a36Sopenharmony_ci struct xdp_buff *xdp, 135862306a36Sopenharmony_ci unsigned int xdp_frags_truesz) 135962306a36Sopenharmony_ci{ 136062306a36Sopenharmony_ci struct skb_shared_info *sinfo = xdp_get_shared_info_from_buff(xdp); 136162306a36Sopenharmony_ci unsigned int headroom, data_len; 136262306a36Sopenharmony_ci struct sk_buff *skb; 136362306a36Sopenharmony_ci int metasize; 136462306a36Sopenharmony_ci u8 nr_frags; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci if (unlikely(xdp->data_end > xdp_data_hard_end(xdp))) { 136762306a36Sopenharmony_ci pr_debug("Error building skb as missing reserved tailroom for xdp"); 136862306a36Sopenharmony_ci return NULL; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_ci if (unlikely(xdp_buff_has_frags(xdp))) 137262306a36Sopenharmony_ci nr_frags = sinfo->nr_frags; 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci skb = build_skb(xdp->data_hard_start, xdp->frame_sz); 137562306a36Sopenharmony_ci if (unlikely(!skb)) 137662306a36Sopenharmony_ci return NULL; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci headroom = xdp->data - xdp->data_hard_start; 137962306a36Sopenharmony_ci data_len = xdp->data_end - xdp->data; 138062306a36Sopenharmony_ci skb_reserve(skb, headroom); 138162306a36Sopenharmony_ci __skb_put(skb, data_len); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci metasize = xdp->data - xdp->data_meta; 138462306a36Sopenharmony_ci metasize = metasize > 0 ? metasize : 0; 138562306a36Sopenharmony_ci if (metasize) 138662306a36Sopenharmony_ci skb_metadata_set(skb, metasize); 138762306a36Sopenharmony_ci 138862306a36Sopenharmony_ci if (unlikely(xdp_buff_has_frags(xdp))) 138962306a36Sopenharmony_ci xdp_update_skb_shared_info(skb, nr_frags, 139062306a36Sopenharmony_ci sinfo->xdp_frags_size, 139162306a36Sopenharmony_ci xdp_frags_truesz, 139262306a36Sopenharmony_ci xdp_buff_is_frag_pfmemalloc(xdp)); 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci return skb; 139562306a36Sopenharmony_ci} 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci/* TODO: build xdp in big mode */ 139862306a36Sopenharmony_cistatic int virtnet_build_xdp_buff_mrg(struct net_device *dev, 139962306a36Sopenharmony_ci struct virtnet_info *vi, 140062306a36Sopenharmony_ci struct receive_queue *rq, 140162306a36Sopenharmony_ci struct xdp_buff *xdp, 140262306a36Sopenharmony_ci void *buf, 140362306a36Sopenharmony_ci unsigned int len, 140462306a36Sopenharmony_ci unsigned int frame_sz, 140562306a36Sopenharmony_ci int *num_buf, 140662306a36Sopenharmony_ci unsigned int *xdp_frags_truesize, 140762306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 140862306a36Sopenharmony_ci{ 140962306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf *hdr = buf; 141062306a36Sopenharmony_ci unsigned int headroom, tailroom, room; 141162306a36Sopenharmony_ci unsigned int truesize, cur_frag_size; 141262306a36Sopenharmony_ci struct skb_shared_info *shinfo; 141362306a36Sopenharmony_ci unsigned int xdp_frags_truesz = 0; 141462306a36Sopenharmony_ci struct page *page; 141562306a36Sopenharmony_ci skb_frag_t *frag; 141662306a36Sopenharmony_ci int offset; 141762306a36Sopenharmony_ci void *ctx; 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci xdp_init_buff(xdp, frame_sz, &rq->xdp_rxq); 142062306a36Sopenharmony_ci xdp_prepare_buff(xdp, buf - VIRTIO_XDP_HEADROOM, 142162306a36Sopenharmony_ci VIRTIO_XDP_HEADROOM + vi->hdr_len, len - vi->hdr_len, true); 142262306a36Sopenharmony_ci 142362306a36Sopenharmony_ci if (!*num_buf) 142462306a36Sopenharmony_ci return 0; 142562306a36Sopenharmony_ci 142662306a36Sopenharmony_ci if (*num_buf > 1) { 142762306a36Sopenharmony_ci /* If we want to build multi-buffer xdp, we need 142862306a36Sopenharmony_ci * to specify that the flags of xdp_buff have the 142962306a36Sopenharmony_ci * XDP_FLAGS_HAS_FRAG bit. 143062306a36Sopenharmony_ci */ 143162306a36Sopenharmony_ci if (!xdp_buff_has_frags(xdp)) 143262306a36Sopenharmony_ci xdp_buff_set_frags_flag(xdp); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci shinfo = xdp_get_shared_info_from_buff(xdp); 143562306a36Sopenharmony_ci shinfo->nr_frags = 0; 143662306a36Sopenharmony_ci shinfo->xdp_frags_size = 0; 143762306a36Sopenharmony_ci } 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci if (*num_buf > MAX_SKB_FRAGS + 1) 144062306a36Sopenharmony_ci return -EINVAL; 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci while (--*num_buf > 0) { 144362306a36Sopenharmony_ci buf = virtnet_rq_get_buf(rq, &len, &ctx); 144462306a36Sopenharmony_ci if (unlikely(!buf)) { 144562306a36Sopenharmony_ci pr_debug("%s: rx error: %d buffers out of %d missing\n", 144662306a36Sopenharmony_ci dev->name, *num_buf, 144762306a36Sopenharmony_ci virtio16_to_cpu(vi->vdev, hdr->num_buffers)); 144862306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 144962306a36Sopenharmony_ci goto err; 145062306a36Sopenharmony_ci } 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci u64_stats_add(&stats->bytes, len); 145362306a36Sopenharmony_ci page = virt_to_head_page(buf); 145462306a36Sopenharmony_ci offset = buf - page_address(page); 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci truesize = mergeable_ctx_to_truesize(ctx); 145762306a36Sopenharmony_ci headroom = mergeable_ctx_to_headroom(ctx); 145862306a36Sopenharmony_ci tailroom = headroom ? sizeof(struct skb_shared_info) : 0; 145962306a36Sopenharmony_ci room = SKB_DATA_ALIGN(headroom + tailroom); 146062306a36Sopenharmony_ci 146162306a36Sopenharmony_ci cur_frag_size = truesize; 146262306a36Sopenharmony_ci xdp_frags_truesz += cur_frag_size; 146362306a36Sopenharmony_ci if (unlikely(len > truesize - room || cur_frag_size > PAGE_SIZE)) { 146462306a36Sopenharmony_ci put_page(page); 146562306a36Sopenharmony_ci pr_debug("%s: rx error: len %u exceeds truesize %lu\n", 146662306a36Sopenharmony_ci dev->name, len, (unsigned long)(truesize - room)); 146762306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 146862306a36Sopenharmony_ci goto err; 146962306a36Sopenharmony_ci } 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci frag = &shinfo->frags[shinfo->nr_frags++]; 147262306a36Sopenharmony_ci skb_frag_fill_page_desc(frag, page, offset, len); 147362306a36Sopenharmony_ci if (page_is_pfmemalloc(page)) 147462306a36Sopenharmony_ci xdp_buff_set_frag_pfmemalloc(xdp); 147562306a36Sopenharmony_ci 147662306a36Sopenharmony_ci shinfo->xdp_frags_size += len; 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ci *xdp_frags_truesize = xdp_frags_truesz; 148062306a36Sopenharmony_ci return 0; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cierr: 148362306a36Sopenharmony_ci put_xdp_frags(xdp); 148462306a36Sopenharmony_ci return -EINVAL; 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic void *mergeable_xdp_get_buf(struct virtnet_info *vi, 148862306a36Sopenharmony_ci struct receive_queue *rq, 148962306a36Sopenharmony_ci struct bpf_prog *xdp_prog, 149062306a36Sopenharmony_ci void *ctx, 149162306a36Sopenharmony_ci unsigned int *frame_sz, 149262306a36Sopenharmony_ci int *num_buf, 149362306a36Sopenharmony_ci struct page **page, 149462306a36Sopenharmony_ci int offset, 149562306a36Sopenharmony_ci unsigned int *len, 149662306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf *hdr) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci unsigned int truesize = mergeable_ctx_to_truesize(ctx); 149962306a36Sopenharmony_ci unsigned int headroom = mergeable_ctx_to_headroom(ctx); 150062306a36Sopenharmony_ci struct page *xdp_page; 150162306a36Sopenharmony_ci unsigned int xdp_room; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci /* Transient failure which in theory could occur if 150462306a36Sopenharmony_ci * in-flight packets from before XDP was enabled reach 150562306a36Sopenharmony_ci * the receive path after XDP is loaded. 150662306a36Sopenharmony_ci */ 150762306a36Sopenharmony_ci if (unlikely(hdr->hdr.gso_type)) 150862306a36Sopenharmony_ci return NULL; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci /* Now XDP core assumes frag size is PAGE_SIZE, but buffers 151162306a36Sopenharmony_ci * with headroom may add hole in truesize, which 151262306a36Sopenharmony_ci * make their length exceed PAGE_SIZE. So we disabled the 151362306a36Sopenharmony_ci * hole mechanism for xdp. See add_recvbuf_mergeable(). 151462306a36Sopenharmony_ci */ 151562306a36Sopenharmony_ci *frame_sz = truesize; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci if (likely(headroom >= virtnet_get_headroom(vi) && 151862306a36Sopenharmony_ci (*num_buf == 1 || xdp_prog->aux->xdp_has_frags))) { 151962306a36Sopenharmony_ci return page_address(*page) + offset; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci /* This happens when headroom is not enough because 152362306a36Sopenharmony_ci * of the buffer was prefilled before XDP is set. 152462306a36Sopenharmony_ci * This should only happen for the first several packets. 152562306a36Sopenharmony_ci * In fact, vq reset can be used here to help us clean up 152662306a36Sopenharmony_ci * the prefilled buffers, but many existing devices do not 152762306a36Sopenharmony_ci * support it, and we don't want to bother users who are 152862306a36Sopenharmony_ci * using xdp normally. 152962306a36Sopenharmony_ci */ 153062306a36Sopenharmony_ci if (!xdp_prog->aux->xdp_has_frags) { 153162306a36Sopenharmony_ci /* linearize data for XDP */ 153262306a36Sopenharmony_ci xdp_page = xdp_linearize_page(rq, num_buf, 153362306a36Sopenharmony_ci *page, offset, 153462306a36Sopenharmony_ci VIRTIO_XDP_HEADROOM, 153562306a36Sopenharmony_ci len); 153662306a36Sopenharmony_ci if (!xdp_page) 153762306a36Sopenharmony_ci return NULL; 153862306a36Sopenharmony_ci } else { 153962306a36Sopenharmony_ci xdp_room = SKB_DATA_ALIGN(VIRTIO_XDP_HEADROOM + 154062306a36Sopenharmony_ci sizeof(struct skb_shared_info)); 154162306a36Sopenharmony_ci if (*len + xdp_room > PAGE_SIZE) 154262306a36Sopenharmony_ci return NULL; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci xdp_page = alloc_page(GFP_ATOMIC); 154562306a36Sopenharmony_ci if (!xdp_page) 154662306a36Sopenharmony_ci return NULL; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci memcpy(page_address(xdp_page) + VIRTIO_XDP_HEADROOM, 154962306a36Sopenharmony_ci page_address(*page) + offset, *len); 155062306a36Sopenharmony_ci } 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci *frame_sz = PAGE_SIZE; 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_ci put_page(*page); 155562306a36Sopenharmony_ci 155662306a36Sopenharmony_ci *page = xdp_page; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci return page_address(*page) + VIRTIO_XDP_HEADROOM; 155962306a36Sopenharmony_ci} 156062306a36Sopenharmony_ci 156162306a36Sopenharmony_cistatic struct sk_buff *receive_mergeable_xdp(struct net_device *dev, 156262306a36Sopenharmony_ci struct virtnet_info *vi, 156362306a36Sopenharmony_ci struct receive_queue *rq, 156462306a36Sopenharmony_ci struct bpf_prog *xdp_prog, 156562306a36Sopenharmony_ci void *buf, 156662306a36Sopenharmony_ci void *ctx, 156762306a36Sopenharmony_ci unsigned int len, 156862306a36Sopenharmony_ci unsigned int *xdp_xmit, 156962306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 157062306a36Sopenharmony_ci{ 157162306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf *hdr = buf; 157262306a36Sopenharmony_ci int num_buf = virtio16_to_cpu(vi->vdev, hdr->num_buffers); 157362306a36Sopenharmony_ci struct page *page = virt_to_head_page(buf); 157462306a36Sopenharmony_ci int offset = buf - page_address(page); 157562306a36Sopenharmony_ci unsigned int xdp_frags_truesz = 0; 157662306a36Sopenharmony_ci struct sk_buff *head_skb; 157762306a36Sopenharmony_ci unsigned int frame_sz; 157862306a36Sopenharmony_ci struct xdp_buff xdp; 157962306a36Sopenharmony_ci void *data; 158062306a36Sopenharmony_ci u32 act; 158162306a36Sopenharmony_ci int err; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci data = mergeable_xdp_get_buf(vi, rq, xdp_prog, ctx, &frame_sz, &num_buf, &page, 158462306a36Sopenharmony_ci offset, &len, hdr); 158562306a36Sopenharmony_ci if (unlikely(!data)) 158662306a36Sopenharmony_ci goto err_xdp; 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci err = virtnet_build_xdp_buff_mrg(dev, vi, rq, &xdp, data, len, frame_sz, 158962306a36Sopenharmony_ci &num_buf, &xdp_frags_truesz, stats); 159062306a36Sopenharmony_ci if (unlikely(err)) 159162306a36Sopenharmony_ci goto err_xdp; 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci act = virtnet_xdp_handler(xdp_prog, &xdp, dev, xdp_xmit, stats); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci switch (act) { 159662306a36Sopenharmony_ci case XDP_PASS: 159762306a36Sopenharmony_ci head_skb = build_skb_from_xdp_buff(dev, vi, &xdp, xdp_frags_truesz); 159862306a36Sopenharmony_ci if (unlikely(!head_skb)) 159962306a36Sopenharmony_ci break; 160062306a36Sopenharmony_ci return head_skb; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci case XDP_TX: 160362306a36Sopenharmony_ci case XDP_REDIRECT: 160462306a36Sopenharmony_ci return NULL; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci default: 160762306a36Sopenharmony_ci break; 160862306a36Sopenharmony_ci } 160962306a36Sopenharmony_ci 161062306a36Sopenharmony_ci put_xdp_frags(&xdp); 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_cierr_xdp: 161362306a36Sopenharmony_ci put_page(page); 161462306a36Sopenharmony_ci mergeable_buf_free(rq, num_buf, dev, stats); 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_ci u64_stats_inc(&stats->xdp_drops); 161762306a36Sopenharmony_ci u64_stats_inc(&stats->drops); 161862306a36Sopenharmony_ci return NULL; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic struct sk_buff *receive_mergeable(struct net_device *dev, 162262306a36Sopenharmony_ci struct virtnet_info *vi, 162362306a36Sopenharmony_ci struct receive_queue *rq, 162462306a36Sopenharmony_ci void *buf, 162562306a36Sopenharmony_ci void *ctx, 162662306a36Sopenharmony_ci unsigned int len, 162762306a36Sopenharmony_ci unsigned int *xdp_xmit, 162862306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 162962306a36Sopenharmony_ci{ 163062306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf *hdr = buf; 163162306a36Sopenharmony_ci int num_buf = virtio16_to_cpu(vi->vdev, hdr->num_buffers); 163262306a36Sopenharmony_ci struct page *page = virt_to_head_page(buf); 163362306a36Sopenharmony_ci int offset = buf - page_address(page); 163462306a36Sopenharmony_ci struct sk_buff *head_skb, *curr_skb; 163562306a36Sopenharmony_ci unsigned int truesize = mergeable_ctx_to_truesize(ctx); 163662306a36Sopenharmony_ci unsigned int headroom = mergeable_ctx_to_headroom(ctx); 163762306a36Sopenharmony_ci unsigned int tailroom = headroom ? sizeof(struct skb_shared_info) : 0; 163862306a36Sopenharmony_ci unsigned int room = SKB_DATA_ALIGN(headroom + tailroom); 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci head_skb = NULL; 164162306a36Sopenharmony_ci u64_stats_add(&stats->bytes, len - vi->hdr_len); 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci if (unlikely(len > truesize - room)) { 164462306a36Sopenharmony_ci pr_debug("%s: rx error: len %u exceeds truesize %lu\n", 164562306a36Sopenharmony_ci dev->name, len, (unsigned long)(truesize - room)); 164662306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 164762306a36Sopenharmony_ci goto err_skb; 164862306a36Sopenharmony_ci } 164962306a36Sopenharmony_ci 165062306a36Sopenharmony_ci if (unlikely(vi->xdp_enabled)) { 165162306a36Sopenharmony_ci struct bpf_prog *xdp_prog; 165262306a36Sopenharmony_ci 165362306a36Sopenharmony_ci rcu_read_lock(); 165462306a36Sopenharmony_ci xdp_prog = rcu_dereference(rq->xdp_prog); 165562306a36Sopenharmony_ci if (xdp_prog) { 165662306a36Sopenharmony_ci head_skb = receive_mergeable_xdp(dev, vi, rq, xdp_prog, buf, ctx, 165762306a36Sopenharmony_ci len, xdp_xmit, stats); 165862306a36Sopenharmony_ci rcu_read_unlock(); 165962306a36Sopenharmony_ci return head_skb; 166062306a36Sopenharmony_ci } 166162306a36Sopenharmony_ci rcu_read_unlock(); 166262306a36Sopenharmony_ci } 166362306a36Sopenharmony_ci 166462306a36Sopenharmony_ci head_skb = page_to_skb(vi, rq, page, offset, len, truesize, headroom); 166562306a36Sopenharmony_ci curr_skb = head_skb; 166662306a36Sopenharmony_ci 166762306a36Sopenharmony_ci if (unlikely(!curr_skb)) 166862306a36Sopenharmony_ci goto err_skb; 166962306a36Sopenharmony_ci while (--num_buf) { 167062306a36Sopenharmony_ci int num_skb_frags; 167162306a36Sopenharmony_ci 167262306a36Sopenharmony_ci buf = virtnet_rq_get_buf(rq, &len, &ctx); 167362306a36Sopenharmony_ci if (unlikely(!buf)) { 167462306a36Sopenharmony_ci pr_debug("%s: rx error: %d buffers out of %d missing\n", 167562306a36Sopenharmony_ci dev->name, num_buf, 167662306a36Sopenharmony_ci virtio16_to_cpu(vi->vdev, 167762306a36Sopenharmony_ci hdr->num_buffers)); 167862306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 167962306a36Sopenharmony_ci goto err_buf; 168062306a36Sopenharmony_ci } 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci u64_stats_add(&stats->bytes, len); 168362306a36Sopenharmony_ci page = virt_to_head_page(buf); 168462306a36Sopenharmony_ci 168562306a36Sopenharmony_ci truesize = mergeable_ctx_to_truesize(ctx); 168662306a36Sopenharmony_ci headroom = mergeable_ctx_to_headroom(ctx); 168762306a36Sopenharmony_ci tailroom = headroom ? sizeof(struct skb_shared_info) : 0; 168862306a36Sopenharmony_ci room = SKB_DATA_ALIGN(headroom + tailroom); 168962306a36Sopenharmony_ci if (unlikely(len > truesize - room)) { 169062306a36Sopenharmony_ci pr_debug("%s: rx error: len %u exceeds truesize %lu\n", 169162306a36Sopenharmony_ci dev->name, len, (unsigned long)(truesize - room)); 169262306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 169362306a36Sopenharmony_ci goto err_skb; 169462306a36Sopenharmony_ci } 169562306a36Sopenharmony_ci 169662306a36Sopenharmony_ci num_skb_frags = skb_shinfo(curr_skb)->nr_frags; 169762306a36Sopenharmony_ci if (unlikely(num_skb_frags == MAX_SKB_FRAGS)) { 169862306a36Sopenharmony_ci struct sk_buff *nskb = alloc_skb(0, GFP_ATOMIC); 169962306a36Sopenharmony_ci 170062306a36Sopenharmony_ci if (unlikely(!nskb)) 170162306a36Sopenharmony_ci goto err_skb; 170262306a36Sopenharmony_ci if (curr_skb == head_skb) 170362306a36Sopenharmony_ci skb_shinfo(curr_skb)->frag_list = nskb; 170462306a36Sopenharmony_ci else 170562306a36Sopenharmony_ci curr_skb->next = nskb; 170662306a36Sopenharmony_ci curr_skb = nskb; 170762306a36Sopenharmony_ci head_skb->truesize += nskb->truesize; 170862306a36Sopenharmony_ci num_skb_frags = 0; 170962306a36Sopenharmony_ci } 171062306a36Sopenharmony_ci if (curr_skb != head_skb) { 171162306a36Sopenharmony_ci head_skb->data_len += len; 171262306a36Sopenharmony_ci head_skb->len += len; 171362306a36Sopenharmony_ci head_skb->truesize += truesize; 171462306a36Sopenharmony_ci } 171562306a36Sopenharmony_ci offset = buf - page_address(page); 171662306a36Sopenharmony_ci if (skb_can_coalesce(curr_skb, num_skb_frags, page, offset)) { 171762306a36Sopenharmony_ci put_page(page); 171862306a36Sopenharmony_ci skb_coalesce_rx_frag(curr_skb, num_skb_frags - 1, 171962306a36Sopenharmony_ci len, truesize); 172062306a36Sopenharmony_ci } else { 172162306a36Sopenharmony_ci skb_add_rx_frag(curr_skb, num_skb_frags, page, 172262306a36Sopenharmony_ci offset, len, truesize); 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci } 172562306a36Sopenharmony_ci 172662306a36Sopenharmony_ci ewma_pkt_len_add(&rq->mrg_avg_pkt_len, head_skb->len); 172762306a36Sopenharmony_ci return head_skb; 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_cierr_skb: 173062306a36Sopenharmony_ci put_page(page); 173162306a36Sopenharmony_ci mergeable_buf_free(rq, num_buf, dev, stats); 173262306a36Sopenharmony_ci 173362306a36Sopenharmony_cierr_buf: 173462306a36Sopenharmony_ci u64_stats_inc(&stats->drops); 173562306a36Sopenharmony_ci dev_kfree_skb(head_skb); 173662306a36Sopenharmony_ci return NULL; 173762306a36Sopenharmony_ci} 173862306a36Sopenharmony_ci 173962306a36Sopenharmony_cistatic void virtio_skb_set_hash(const struct virtio_net_hdr_v1_hash *hdr_hash, 174062306a36Sopenharmony_ci struct sk_buff *skb) 174162306a36Sopenharmony_ci{ 174262306a36Sopenharmony_ci enum pkt_hash_types rss_hash_type; 174362306a36Sopenharmony_ci 174462306a36Sopenharmony_ci if (!hdr_hash || !skb) 174562306a36Sopenharmony_ci return; 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci switch (__le16_to_cpu(hdr_hash->hash_report)) { 174862306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_TCPv4: 174962306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_UDPv4: 175062306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_TCPv6: 175162306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_UDPv6: 175262306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_TCPv6_EX: 175362306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_UDPv6_EX: 175462306a36Sopenharmony_ci rss_hash_type = PKT_HASH_TYPE_L4; 175562306a36Sopenharmony_ci break; 175662306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_IPv4: 175762306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_IPv6: 175862306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_IPv6_EX: 175962306a36Sopenharmony_ci rss_hash_type = PKT_HASH_TYPE_L3; 176062306a36Sopenharmony_ci break; 176162306a36Sopenharmony_ci case VIRTIO_NET_HASH_REPORT_NONE: 176262306a36Sopenharmony_ci default: 176362306a36Sopenharmony_ci rss_hash_type = PKT_HASH_TYPE_NONE; 176462306a36Sopenharmony_ci } 176562306a36Sopenharmony_ci skb_set_hash(skb, __le32_to_cpu(hdr_hash->hash_value), rss_hash_type); 176662306a36Sopenharmony_ci} 176762306a36Sopenharmony_ci 176862306a36Sopenharmony_cistatic void receive_buf(struct virtnet_info *vi, struct receive_queue *rq, 176962306a36Sopenharmony_ci void *buf, unsigned int len, void **ctx, 177062306a36Sopenharmony_ci unsigned int *xdp_xmit, 177162306a36Sopenharmony_ci struct virtnet_rq_stats *stats) 177262306a36Sopenharmony_ci{ 177362306a36Sopenharmony_ci struct net_device *dev = vi->dev; 177462306a36Sopenharmony_ci struct sk_buff *skb; 177562306a36Sopenharmony_ci struct virtio_net_common_hdr *hdr; 177662306a36Sopenharmony_ci 177762306a36Sopenharmony_ci if (unlikely(len < vi->hdr_len + ETH_HLEN)) { 177862306a36Sopenharmony_ci pr_debug("%s: short packet %i\n", dev->name, len); 177962306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_length_errors); 178062306a36Sopenharmony_ci virtnet_rq_free_buf(vi, rq, buf); 178162306a36Sopenharmony_ci return; 178262306a36Sopenharmony_ci } 178362306a36Sopenharmony_ci 178462306a36Sopenharmony_ci if (vi->mergeable_rx_bufs) 178562306a36Sopenharmony_ci skb = receive_mergeable(dev, vi, rq, buf, ctx, len, xdp_xmit, 178662306a36Sopenharmony_ci stats); 178762306a36Sopenharmony_ci else if (vi->big_packets) 178862306a36Sopenharmony_ci skb = receive_big(dev, vi, rq, buf, len, stats); 178962306a36Sopenharmony_ci else 179062306a36Sopenharmony_ci skb = receive_small(dev, vi, rq, buf, ctx, len, xdp_xmit, stats); 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci if (unlikely(!skb)) 179362306a36Sopenharmony_ci return; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci hdr = skb_vnet_common_hdr(skb); 179662306a36Sopenharmony_ci if (dev->features & NETIF_F_RXHASH && vi->has_rss_hash_report) 179762306a36Sopenharmony_ci virtio_skb_set_hash(&hdr->hash_v1_hdr, skb); 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci if (hdr->hdr.flags & VIRTIO_NET_HDR_F_DATA_VALID) 180062306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci if (virtio_net_hdr_to_skb(skb, &hdr->hdr, 180362306a36Sopenharmony_ci virtio_is_little_endian(vi->vdev))) { 180462306a36Sopenharmony_ci net_warn_ratelimited("%s: bad gso: type: %u, size: %u\n", 180562306a36Sopenharmony_ci dev->name, hdr->hdr.gso_type, 180662306a36Sopenharmony_ci hdr->hdr.gso_size); 180762306a36Sopenharmony_ci goto frame_err; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci 181062306a36Sopenharmony_ci skb_record_rx_queue(skb, vq2rxq(rq->vq)); 181162306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, dev); 181262306a36Sopenharmony_ci pr_debug("Receiving skb proto 0x%04x len %i type %i\n", 181362306a36Sopenharmony_ci ntohs(skb->protocol), skb->len, skb->pkt_type); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci napi_gro_receive(&rq->napi, skb); 181662306a36Sopenharmony_ci return; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ciframe_err: 181962306a36Sopenharmony_ci DEV_STATS_INC(dev, rx_frame_errors); 182062306a36Sopenharmony_ci dev_kfree_skb(skb); 182162306a36Sopenharmony_ci} 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci/* Unlike mergeable buffers, all buffers are allocated to the 182462306a36Sopenharmony_ci * same size, except for the headroom. For this reason we do 182562306a36Sopenharmony_ci * not need to use mergeable_len_to_ctx here - it is enough 182662306a36Sopenharmony_ci * to store the headroom as the context ignoring the truesize. 182762306a36Sopenharmony_ci */ 182862306a36Sopenharmony_cistatic int add_recvbuf_small(struct virtnet_info *vi, struct receive_queue *rq, 182962306a36Sopenharmony_ci gfp_t gfp) 183062306a36Sopenharmony_ci{ 183162306a36Sopenharmony_ci char *buf; 183262306a36Sopenharmony_ci unsigned int xdp_headroom = virtnet_get_headroom(vi); 183362306a36Sopenharmony_ci void *ctx = (void *)(unsigned long)xdp_headroom; 183462306a36Sopenharmony_ci int len = vi->hdr_len + VIRTNET_RX_PAD + GOOD_PACKET_LEN + xdp_headroom; 183562306a36Sopenharmony_ci int err; 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_ci len = SKB_DATA_ALIGN(len) + 183862306a36Sopenharmony_ci SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci buf = virtnet_rq_alloc(rq, len, gfp); 184162306a36Sopenharmony_ci if (unlikely(!buf)) 184262306a36Sopenharmony_ci return -ENOMEM; 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci virtnet_rq_init_one_sg(rq, buf + VIRTNET_RX_PAD + xdp_headroom, 184562306a36Sopenharmony_ci vi->hdr_len + GOOD_PACKET_LEN); 184662306a36Sopenharmony_ci 184762306a36Sopenharmony_ci err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp); 184862306a36Sopenharmony_ci if (err < 0) { 184962306a36Sopenharmony_ci if (rq->do_dma) 185062306a36Sopenharmony_ci virtnet_rq_unmap(rq, buf, 0); 185162306a36Sopenharmony_ci put_page(virt_to_head_page(buf)); 185262306a36Sopenharmony_ci } 185362306a36Sopenharmony_ci 185462306a36Sopenharmony_ci return err; 185562306a36Sopenharmony_ci} 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_cistatic int add_recvbuf_big(struct virtnet_info *vi, struct receive_queue *rq, 185862306a36Sopenharmony_ci gfp_t gfp) 185962306a36Sopenharmony_ci{ 186062306a36Sopenharmony_ci struct page *first, *list = NULL; 186162306a36Sopenharmony_ci char *p; 186262306a36Sopenharmony_ci int i, err, offset; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci sg_init_table(rq->sg, vi->big_packets_num_skbfrags + 2); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci /* page in rq->sg[vi->big_packets_num_skbfrags + 1] is list tail */ 186762306a36Sopenharmony_ci for (i = vi->big_packets_num_skbfrags + 1; i > 1; --i) { 186862306a36Sopenharmony_ci first = get_a_page(rq, gfp); 186962306a36Sopenharmony_ci if (!first) { 187062306a36Sopenharmony_ci if (list) 187162306a36Sopenharmony_ci give_pages(rq, list); 187262306a36Sopenharmony_ci return -ENOMEM; 187362306a36Sopenharmony_ci } 187462306a36Sopenharmony_ci sg_set_buf(&rq->sg[i], page_address(first), PAGE_SIZE); 187562306a36Sopenharmony_ci 187662306a36Sopenharmony_ci /* chain new page in list head to match sg */ 187762306a36Sopenharmony_ci first->private = (unsigned long)list; 187862306a36Sopenharmony_ci list = first; 187962306a36Sopenharmony_ci } 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci first = get_a_page(rq, gfp); 188262306a36Sopenharmony_ci if (!first) { 188362306a36Sopenharmony_ci give_pages(rq, list); 188462306a36Sopenharmony_ci return -ENOMEM; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci p = page_address(first); 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci /* rq->sg[0], rq->sg[1] share the same page */ 188962306a36Sopenharmony_ci /* a separated rq->sg[0] for header - required in case !any_header_sg */ 189062306a36Sopenharmony_ci sg_set_buf(&rq->sg[0], p, vi->hdr_len); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci /* rq->sg[1] for data packet, from offset */ 189362306a36Sopenharmony_ci offset = sizeof(struct padded_vnet_hdr); 189462306a36Sopenharmony_ci sg_set_buf(&rq->sg[1], p + offset, PAGE_SIZE - offset); 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_ci /* chain first in list head */ 189762306a36Sopenharmony_ci first->private = (unsigned long)list; 189862306a36Sopenharmony_ci err = virtqueue_add_inbuf(rq->vq, rq->sg, vi->big_packets_num_skbfrags + 2, 189962306a36Sopenharmony_ci first, gfp); 190062306a36Sopenharmony_ci if (err < 0) 190162306a36Sopenharmony_ci give_pages(rq, first); 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return err; 190462306a36Sopenharmony_ci} 190562306a36Sopenharmony_ci 190662306a36Sopenharmony_cistatic unsigned int get_mergeable_buf_len(struct receive_queue *rq, 190762306a36Sopenharmony_ci struct ewma_pkt_len *avg_pkt_len, 190862306a36Sopenharmony_ci unsigned int room) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci struct virtnet_info *vi = rq->vq->vdev->priv; 191162306a36Sopenharmony_ci const size_t hdr_len = vi->hdr_len; 191262306a36Sopenharmony_ci unsigned int len; 191362306a36Sopenharmony_ci 191462306a36Sopenharmony_ci if (room) 191562306a36Sopenharmony_ci return PAGE_SIZE - room; 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_ci len = hdr_len + clamp_t(unsigned int, ewma_pkt_len_read(avg_pkt_len), 191862306a36Sopenharmony_ci rq->min_buf_len, PAGE_SIZE - hdr_len); 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_ci return ALIGN(len, L1_CACHE_BYTES); 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_cistatic int add_recvbuf_mergeable(struct virtnet_info *vi, 192462306a36Sopenharmony_ci struct receive_queue *rq, gfp_t gfp) 192562306a36Sopenharmony_ci{ 192662306a36Sopenharmony_ci struct page_frag *alloc_frag = &rq->alloc_frag; 192762306a36Sopenharmony_ci unsigned int headroom = virtnet_get_headroom(vi); 192862306a36Sopenharmony_ci unsigned int tailroom = headroom ? sizeof(struct skb_shared_info) : 0; 192962306a36Sopenharmony_ci unsigned int room = SKB_DATA_ALIGN(headroom + tailroom); 193062306a36Sopenharmony_ci unsigned int len, hole; 193162306a36Sopenharmony_ci void *ctx; 193262306a36Sopenharmony_ci char *buf; 193362306a36Sopenharmony_ci int err; 193462306a36Sopenharmony_ci 193562306a36Sopenharmony_ci /* Extra tailroom is needed to satisfy XDP's assumption. This 193662306a36Sopenharmony_ci * means rx frags coalescing won't work, but consider we've 193762306a36Sopenharmony_ci * disabled GSO for XDP, it won't be a big issue. 193862306a36Sopenharmony_ci */ 193962306a36Sopenharmony_ci len = get_mergeable_buf_len(rq, &rq->mrg_avg_pkt_len, room); 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci buf = virtnet_rq_alloc(rq, len + room, gfp); 194262306a36Sopenharmony_ci if (unlikely(!buf)) 194362306a36Sopenharmony_ci return -ENOMEM; 194462306a36Sopenharmony_ci 194562306a36Sopenharmony_ci buf += headroom; /* advance address leaving hole at front of pkt */ 194662306a36Sopenharmony_ci hole = alloc_frag->size - alloc_frag->offset; 194762306a36Sopenharmony_ci if (hole < len + room) { 194862306a36Sopenharmony_ci /* To avoid internal fragmentation, if there is very likely not 194962306a36Sopenharmony_ci * enough space for another buffer, add the remaining space to 195062306a36Sopenharmony_ci * the current buffer. 195162306a36Sopenharmony_ci * XDP core assumes that frame_size of xdp_buff and the length 195262306a36Sopenharmony_ci * of the frag are PAGE_SIZE, so we disable the hole mechanism. 195362306a36Sopenharmony_ci */ 195462306a36Sopenharmony_ci if (!headroom) 195562306a36Sopenharmony_ci len += hole; 195662306a36Sopenharmony_ci alloc_frag->offset += hole; 195762306a36Sopenharmony_ci } 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci virtnet_rq_init_one_sg(rq, buf, len); 196062306a36Sopenharmony_ci 196162306a36Sopenharmony_ci ctx = mergeable_len_to_ctx(len + room, headroom); 196262306a36Sopenharmony_ci err = virtqueue_add_inbuf_ctx(rq->vq, rq->sg, 1, buf, ctx, gfp); 196362306a36Sopenharmony_ci if (err < 0) { 196462306a36Sopenharmony_ci if (rq->do_dma) 196562306a36Sopenharmony_ci virtnet_rq_unmap(rq, buf, 0); 196662306a36Sopenharmony_ci put_page(virt_to_head_page(buf)); 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_ci return err; 197062306a36Sopenharmony_ci} 197162306a36Sopenharmony_ci 197262306a36Sopenharmony_ci/* 197362306a36Sopenharmony_ci * Returns false if we couldn't fill entirely (OOM). 197462306a36Sopenharmony_ci * 197562306a36Sopenharmony_ci * Normally run in the receive path, but can also be run from ndo_open 197662306a36Sopenharmony_ci * before we're receiving packets, or from refill_work which is 197762306a36Sopenharmony_ci * careful to disable receiving (using napi_disable). 197862306a36Sopenharmony_ci */ 197962306a36Sopenharmony_cistatic bool try_fill_recv(struct virtnet_info *vi, struct receive_queue *rq, 198062306a36Sopenharmony_ci gfp_t gfp) 198162306a36Sopenharmony_ci{ 198262306a36Sopenharmony_ci int err; 198362306a36Sopenharmony_ci bool oom; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci do { 198662306a36Sopenharmony_ci if (vi->mergeable_rx_bufs) 198762306a36Sopenharmony_ci err = add_recvbuf_mergeable(vi, rq, gfp); 198862306a36Sopenharmony_ci else if (vi->big_packets) 198962306a36Sopenharmony_ci err = add_recvbuf_big(vi, rq, gfp); 199062306a36Sopenharmony_ci else 199162306a36Sopenharmony_ci err = add_recvbuf_small(vi, rq, gfp); 199262306a36Sopenharmony_ci 199362306a36Sopenharmony_ci oom = err == -ENOMEM; 199462306a36Sopenharmony_ci if (err) 199562306a36Sopenharmony_ci break; 199662306a36Sopenharmony_ci } while (rq->vq->num_free); 199762306a36Sopenharmony_ci if (virtqueue_kick_prepare(rq->vq) && virtqueue_notify(rq->vq)) { 199862306a36Sopenharmony_ci unsigned long flags; 199962306a36Sopenharmony_ci 200062306a36Sopenharmony_ci flags = u64_stats_update_begin_irqsave(&rq->stats.syncp); 200162306a36Sopenharmony_ci u64_stats_inc(&rq->stats.kicks); 200262306a36Sopenharmony_ci u64_stats_update_end_irqrestore(&rq->stats.syncp, flags); 200362306a36Sopenharmony_ci } 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci return !oom; 200662306a36Sopenharmony_ci} 200762306a36Sopenharmony_ci 200862306a36Sopenharmony_cistatic void skb_recv_done(struct virtqueue *rvq) 200962306a36Sopenharmony_ci{ 201062306a36Sopenharmony_ci struct virtnet_info *vi = rvq->vdev->priv; 201162306a36Sopenharmony_ci struct receive_queue *rq = &vi->rq[vq2rxq(rvq)]; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci virtqueue_napi_schedule(&rq->napi, rvq); 201462306a36Sopenharmony_ci} 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_cistatic void virtnet_napi_enable(struct virtqueue *vq, struct napi_struct *napi) 201762306a36Sopenharmony_ci{ 201862306a36Sopenharmony_ci napi_enable(napi); 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci /* If all buffers were filled by other side before we napi_enabled, we 202162306a36Sopenharmony_ci * won't get another interrupt, so process any outstanding packets now. 202262306a36Sopenharmony_ci * Call local_bh_enable after to trigger softIRQ processing. 202362306a36Sopenharmony_ci */ 202462306a36Sopenharmony_ci local_bh_disable(); 202562306a36Sopenharmony_ci virtqueue_napi_schedule(napi, vq); 202662306a36Sopenharmony_ci local_bh_enable(); 202762306a36Sopenharmony_ci} 202862306a36Sopenharmony_ci 202962306a36Sopenharmony_cistatic void virtnet_napi_tx_enable(struct virtnet_info *vi, 203062306a36Sopenharmony_ci struct virtqueue *vq, 203162306a36Sopenharmony_ci struct napi_struct *napi) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci if (!napi->weight) 203462306a36Sopenharmony_ci return; 203562306a36Sopenharmony_ci 203662306a36Sopenharmony_ci /* Tx napi touches cachelines on the cpu handling tx interrupts. Only 203762306a36Sopenharmony_ci * enable the feature if this is likely affine with the transmit path. 203862306a36Sopenharmony_ci */ 203962306a36Sopenharmony_ci if (!vi->affinity_hint_set) { 204062306a36Sopenharmony_ci napi->weight = 0; 204162306a36Sopenharmony_ci return; 204262306a36Sopenharmony_ci } 204362306a36Sopenharmony_ci 204462306a36Sopenharmony_ci return virtnet_napi_enable(vq, napi); 204562306a36Sopenharmony_ci} 204662306a36Sopenharmony_ci 204762306a36Sopenharmony_cistatic void virtnet_napi_tx_disable(struct napi_struct *napi) 204862306a36Sopenharmony_ci{ 204962306a36Sopenharmony_ci if (napi->weight) 205062306a36Sopenharmony_ci napi_disable(napi); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic void refill_work(struct work_struct *work) 205462306a36Sopenharmony_ci{ 205562306a36Sopenharmony_ci struct virtnet_info *vi = 205662306a36Sopenharmony_ci container_of(work, struct virtnet_info, refill.work); 205762306a36Sopenharmony_ci bool still_empty; 205862306a36Sopenharmony_ci int i; 205962306a36Sopenharmony_ci 206062306a36Sopenharmony_ci for (i = 0; i < vi->curr_queue_pairs; i++) { 206162306a36Sopenharmony_ci struct receive_queue *rq = &vi->rq[i]; 206262306a36Sopenharmony_ci 206362306a36Sopenharmony_ci napi_disable(&rq->napi); 206462306a36Sopenharmony_ci still_empty = !try_fill_recv(vi, rq, GFP_KERNEL); 206562306a36Sopenharmony_ci virtnet_napi_enable(rq->vq, &rq->napi); 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci /* In theory, this can happen: if we don't get any buffers in 206862306a36Sopenharmony_ci * we will *never* try to fill again. 206962306a36Sopenharmony_ci */ 207062306a36Sopenharmony_ci if (still_empty) 207162306a36Sopenharmony_ci schedule_delayed_work(&vi->refill, HZ/2); 207262306a36Sopenharmony_ci } 207362306a36Sopenharmony_ci} 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_cistatic int virtnet_receive(struct receive_queue *rq, int budget, 207662306a36Sopenharmony_ci unsigned int *xdp_xmit) 207762306a36Sopenharmony_ci{ 207862306a36Sopenharmony_ci struct virtnet_info *vi = rq->vq->vdev->priv; 207962306a36Sopenharmony_ci struct virtnet_rq_stats stats = {}; 208062306a36Sopenharmony_ci unsigned int len; 208162306a36Sopenharmony_ci int packets = 0; 208262306a36Sopenharmony_ci void *buf; 208362306a36Sopenharmony_ci int i; 208462306a36Sopenharmony_ci 208562306a36Sopenharmony_ci if (!vi->big_packets || vi->mergeable_rx_bufs) { 208662306a36Sopenharmony_ci void *ctx; 208762306a36Sopenharmony_ci 208862306a36Sopenharmony_ci while (packets < budget && 208962306a36Sopenharmony_ci (buf = virtnet_rq_get_buf(rq, &len, &ctx))) { 209062306a36Sopenharmony_ci receive_buf(vi, rq, buf, len, ctx, xdp_xmit, &stats); 209162306a36Sopenharmony_ci packets++; 209262306a36Sopenharmony_ci } 209362306a36Sopenharmony_ci } else { 209462306a36Sopenharmony_ci while (packets < budget && 209562306a36Sopenharmony_ci (buf = virtnet_rq_get_buf(rq, &len, NULL)) != NULL) { 209662306a36Sopenharmony_ci receive_buf(vi, rq, buf, len, NULL, xdp_xmit, &stats); 209762306a36Sopenharmony_ci packets++; 209862306a36Sopenharmony_ci } 209962306a36Sopenharmony_ci } 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci if (rq->vq->num_free > min((unsigned int)budget, virtqueue_get_vring_size(rq->vq)) / 2) { 210262306a36Sopenharmony_ci if (!try_fill_recv(vi, rq, GFP_ATOMIC)) { 210362306a36Sopenharmony_ci spin_lock(&vi->refill_lock); 210462306a36Sopenharmony_ci if (vi->refill_enabled) 210562306a36Sopenharmony_ci schedule_delayed_work(&vi->refill, 0); 210662306a36Sopenharmony_ci spin_unlock(&vi->refill_lock); 210762306a36Sopenharmony_ci } 210862306a36Sopenharmony_ci } 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci u64_stats_set(&stats.packets, packets); 211162306a36Sopenharmony_ci u64_stats_update_begin(&rq->stats.syncp); 211262306a36Sopenharmony_ci for (i = 0; i < VIRTNET_RQ_STATS_LEN; i++) { 211362306a36Sopenharmony_ci size_t offset = virtnet_rq_stats_desc[i].offset; 211462306a36Sopenharmony_ci u64_stats_t *item, *src; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci item = (u64_stats_t *)((u8 *)&rq->stats + offset); 211762306a36Sopenharmony_ci src = (u64_stats_t *)((u8 *)&stats + offset); 211862306a36Sopenharmony_ci u64_stats_add(item, u64_stats_read(src)); 211962306a36Sopenharmony_ci } 212062306a36Sopenharmony_ci u64_stats_update_end(&rq->stats.syncp); 212162306a36Sopenharmony_ci 212262306a36Sopenharmony_ci return packets; 212362306a36Sopenharmony_ci} 212462306a36Sopenharmony_ci 212562306a36Sopenharmony_cistatic void virtnet_poll_cleantx(struct receive_queue *rq) 212662306a36Sopenharmony_ci{ 212762306a36Sopenharmony_ci struct virtnet_info *vi = rq->vq->vdev->priv; 212862306a36Sopenharmony_ci unsigned int index = vq2rxq(rq->vq); 212962306a36Sopenharmony_ci struct send_queue *sq = &vi->sq[index]; 213062306a36Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(vi->dev, index); 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_ci if (!sq->napi.weight || is_xdp_raw_buffer_queue(vi, index)) 213362306a36Sopenharmony_ci return; 213462306a36Sopenharmony_ci 213562306a36Sopenharmony_ci if (__netif_tx_trylock(txq)) { 213662306a36Sopenharmony_ci if (sq->reset) { 213762306a36Sopenharmony_ci __netif_tx_unlock(txq); 213862306a36Sopenharmony_ci return; 213962306a36Sopenharmony_ci } 214062306a36Sopenharmony_ci 214162306a36Sopenharmony_ci do { 214262306a36Sopenharmony_ci virtqueue_disable_cb(sq->vq); 214362306a36Sopenharmony_ci free_old_xmit_skbs(sq, true); 214462306a36Sopenharmony_ci } while (unlikely(!virtqueue_enable_cb_delayed(sq->vq))); 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (sq->vq->num_free >= 2 + MAX_SKB_FRAGS) 214762306a36Sopenharmony_ci netif_tx_wake_queue(txq); 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci __netif_tx_unlock(txq); 215062306a36Sopenharmony_ci } 215162306a36Sopenharmony_ci} 215262306a36Sopenharmony_ci 215362306a36Sopenharmony_cistatic int virtnet_poll(struct napi_struct *napi, int budget) 215462306a36Sopenharmony_ci{ 215562306a36Sopenharmony_ci struct receive_queue *rq = 215662306a36Sopenharmony_ci container_of(napi, struct receive_queue, napi); 215762306a36Sopenharmony_ci struct virtnet_info *vi = rq->vq->vdev->priv; 215862306a36Sopenharmony_ci struct send_queue *sq; 215962306a36Sopenharmony_ci unsigned int received; 216062306a36Sopenharmony_ci unsigned int xdp_xmit = 0; 216162306a36Sopenharmony_ci 216262306a36Sopenharmony_ci virtnet_poll_cleantx(rq); 216362306a36Sopenharmony_ci 216462306a36Sopenharmony_ci received = virtnet_receive(rq, budget, &xdp_xmit); 216562306a36Sopenharmony_ci 216662306a36Sopenharmony_ci if (xdp_xmit & VIRTIO_XDP_REDIR) 216762306a36Sopenharmony_ci xdp_do_flush(); 216862306a36Sopenharmony_ci 216962306a36Sopenharmony_ci /* Out of packets? */ 217062306a36Sopenharmony_ci if (received < budget) 217162306a36Sopenharmony_ci virtqueue_napi_complete(napi, rq->vq, received); 217262306a36Sopenharmony_ci 217362306a36Sopenharmony_ci if (xdp_xmit & VIRTIO_XDP_TX) { 217462306a36Sopenharmony_ci sq = virtnet_xdp_get_sq(vi); 217562306a36Sopenharmony_ci if (virtqueue_kick_prepare(sq->vq) && virtqueue_notify(sq->vq)) { 217662306a36Sopenharmony_ci u64_stats_update_begin(&sq->stats.syncp); 217762306a36Sopenharmony_ci u64_stats_inc(&sq->stats.kicks); 217862306a36Sopenharmony_ci u64_stats_update_end(&sq->stats.syncp); 217962306a36Sopenharmony_ci } 218062306a36Sopenharmony_ci virtnet_xdp_put_sq(vi, sq); 218162306a36Sopenharmony_ci } 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci return received; 218462306a36Sopenharmony_ci} 218562306a36Sopenharmony_ci 218662306a36Sopenharmony_cistatic void virtnet_disable_queue_pair(struct virtnet_info *vi, int qp_index) 218762306a36Sopenharmony_ci{ 218862306a36Sopenharmony_ci virtnet_napi_tx_disable(&vi->sq[qp_index].napi); 218962306a36Sopenharmony_ci napi_disable(&vi->rq[qp_index].napi); 219062306a36Sopenharmony_ci xdp_rxq_info_unreg(&vi->rq[qp_index].xdp_rxq); 219162306a36Sopenharmony_ci} 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_cistatic int virtnet_enable_queue_pair(struct virtnet_info *vi, int qp_index) 219462306a36Sopenharmony_ci{ 219562306a36Sopenharmony_ci struct net_device *dev = vi->dev; 219662306a36Sopenharmony_ci int err; 219762306a36Sopenharmony_ci 219862306a36Sopenharmony_ci err = xdp_rxq_info_reg(&vi->rq[qp_index].xdp_rxq, dev, qp_index, 219962306a36Sopenharmony_ci vi->rq[qp_index].napi.napi_id); 220062306a36Sopenharmony_ci if (err < 0) 220162306a36Sopenharmony_ci return err; 220262306a36Sopenharmony_ci 220362306a36Sopenharmony_ci err = xdp_rxq_info_reg_mem_model(&vi->rq[qp_index].xdp_rxq, 220462306a36Sopenharmony_ci MEM_TYPE_PAGE_SHARED, NULL); 220562306a36Sopenharmony_ci if (err < 0) 220662306a36Sopenharmony_ci goto err_xdp_reg_mem_model; 220762306a36Sopenharmony_ci 220862306a36Sopenharmony_ci virtnet_napi_enable(vi->rq[qp_index].vq, &vi->rq[qp_index].napi); 220962306a36Sopenharmony_ci virtnet_napi_tx_enable(vi, vi->sq[qp_index].vq, &vi->sq[qp_index].napi); 221062306a36Sopenharmony_ci 221162306a36Sopenharmony_ci return 0; 221262306a36Sopenharmony_ci 221362306a36Sopenharmony_cierr_xdp_reg_mem_model: 221462306a36Sopenharmony_ci xdp_rxq_info_unreg(&vi->rq[qp_index].xdp_rxq); 221562306a36Sopenharmony_ci return err; 221662306a36Sopenharmony_ci} 221762306a36Sopenharmony_ci 221862306a36Sopenharmony_cistatic int virtnet_open(struct net_device *dev) 221962306a36Sopenharmony_ci{ 222062306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 222162306a36Sopenharmony_ci int i, err; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci enable_delayed_refill(vi); 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 222662306a36Sopenharmony_ci if (i < vi->curr_queue_pairs) 222762306a36Sopenharmony_ci /* Make sure we have some buffers: if oom use wq. */ 222862306a36Sopenharmony_ci if (!try_fill_recv(vi, &vi->rq[i], GFP_KERNEL)) 222962306a36Sopenharmony_ci schedule_delayed_work(&vi->refill, 0); 223062306a36Sopenharmony_ci 223162306a36Sopenharmony_ci err = virtnet_enable_queue_pair(vi, i); 223262306a36Sopenharmony_ci if (err < 0) 223362306a36Sopenharmony_ci goto err_enable_qp; 223462306a36Sopenharmony_ci } 223562306a36Sopenharmony_ci 223662306a36Sopenharmony_ci return 0; 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_cierr_enable_qp: 223962306a36Sopenharmony_ci disable_delayed_refill(vi); 224062306a36Sopenharmony_ci cancel_delayed_work_sync(&vi->refill); 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci for (i--; i >= 0; i--) 224362306a36Sopenharmony_ci virtnet_disable_queue_pair(vi, i); 224462306a36Sopenharmony_ci return err; 224562306a36Sopenharmony_ci} 224662306a36Sopenharmony_ci 224762306a36Sopenharmony_cistatic int virtnet_poll_tx(struct napi_struct *napi, int budget) 224862306a36Sopenharmony_ci{ 224962306a36Sopenharmony_ci struct send_queue *sq = container_of(napi, struct send_queue, napi); 225062306a36Sopenharmony_ci struct virtnet_info *vi = sq->vq->vdev->priv; 225162306a36Sopenharmony_ci unsigned int index = vq2txq(sq->vq); 225262306a36Sopenharmony_ci struct netdev_queue *txq; 225362306a36Sopenharmony_ci int opaque; 225462306a36Sopenharmony_ci bool done; 225562306a36Sopenharmony_ci 225662306a36Sopenharmony_ci if (unlikely(is_xdp_raw_buffer_queue(vi, index))) { 225762306a36Sopenharmony_ci /* We don't need to enable cb for XDP */ 225862306a36Sopenharmony_ci napi_complete_done(napi, 0); 225962306a36Sopenharmony_ci return 0; 226062306a36Sopenharmony_ci } 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci txq = netdev_get_tx_queue(vi->dev, index); 226362306a36Sopenharmony_ci __netif_tx_lock(txq, raw_smp_processor_id()); 226462306a36Sopenharmony_ci virtqueue_disable_cb(sq->vq); 226562306a36Sopenharmony_ci free_old_xmit_skbs(sq, true); 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_ci if (sq->vq->num_free >= 2 + MAX_SKB_FRAGS) 226862306a36Sopenharmony_ci netif_tx_wake_queue(txq); 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci opaque = virtqueue_enable_cb_prepare(sq->vq); 227162306a36Sopenharmony_ci 227262306a36Sopenharmony_ci done = napi_complete_done(napi, 0); 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci if (!done) 227562306a36Sopenharmony_ci virtqueue_disable_cb(sq->vq); 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_ci __netif_tx_unlock(txq); 227862306a36Sopenharmony_ci 227962306a36Sopenharmony_ci if (done) { 228062306a36Sopenharmony_ci if (unlikely(virtqueue_poll(sq->vq, opaque))) { 228162306a36Sopenharmony_ci if (napi_schedule_prep(napi)) { 228262306a36Sopenharmony_ci __netif_tx_lock(txq, raw_smp_processor_id()); 228362306a36Sopenharmony_ci virtqueue_disable_cb(sq->vq); 228462306a36Sopenharmony_ci __netif_tx_unlock(txq); 228562306a36Sopenharmony_ci __napi_schedule(napi); 228662306a36Sopenharmony_ci } 228762306a36Sopenharmony_ci } 228862306a36Sopenharmony_ci } 228962306a36Sopenharmony_ci 229062306a36Sopenharmony_ci return 0; 229162306a36Sopenharmony_ci} 229262306a36Sopenharmony_ci 229362306a36Sopenharmony_cistatic int xmit_skb(struct send_queue *sq, struct sk_buff *skb) 229462306a36Sopenharmony_ci{ 229562306a36Sopenharmony_ci struct virtio_net_hdr_mrg_rxbuf *hdr; 229662306a36Sopenharmony_ci const unsigned char *dest = ((struct ethhdr *)skb->data)->h_dest; 229762306a36Sopenharmony_ci struct virtnet_info *vi = sq->vq->vdev->priv; 229862306a36Sopenharmony_ci int num_sg; 229962306a36Sopenharmony_ci unsigned hdr_len = vi->hdr_len; 230062306a36Sopenharmony_ci bool can_push; 230162306a36Sopenharmony_ci 230262306a36Sopenharmony_ci pr_debug("%s: xmit %p %pM\n", vi->dev->name, skb, dest); 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci can_push = vi->any_header_sg && 230562306a36Sopenharmony_ci !((unsigned long)skb->data & (__alignof__(*hdr) - 1)) && 230662306a36Sopenharmony_ci !skb_header_cloned(skb) && skb_headroom(skb) >= hdr_len; 230762306a36Sopenharmony_ci /* Even if we can, don't push here yet as this would skew 230862306a36Sopenharmony_ci * csum_start offset below. */ 230962306a36Sopenharmony_ci if (can_push) 231062306a36Sopenharmony_ci hdr = (struct virtio_net_hdr_mrg_rxbuf *)(skb->data - hdr_len); 231162306a36Sopenharmony_ci else 231262306a36Sopenharmony_ci hdr = &skb_vnet_common_hdr(skb)->mrg_hdr; 231362306a36Sopenharmony_ci 231462306a36Sopenharmony_ci if (virtio_net_hdr_from_skb(skb, &hdr->hdr, 231562306a36Sopenharmony_ci virtio_is_little_endian(vi->vdev), false, 231662306a36Sopenharmony_ci 0)) 231762306a36Sopenharmony_ci return -EPROTO; 231862306a36Sopenharmony_ci 231962306a36Sopenharmony_ci if (vi->mergeable_rx_bufs) 232062306a36Sopenharmony_ci hdr->num_buffers = 0; 232162306a36Sopenharmony_ci 232262306a36Sopenharmony_ci sg_init_table(sq->sg, skb_shinfo(skb)->nr_frags + (can_push ? 1 : 2)); 232362306a36Sopenharmony_ci if (can_push) { 232462306a36Sopenharmony_ci __skb_push(skb, hdr_len); 232562306a36Sopenharmony_ci num_sg = skb_to_sgvec(skb, sq->sg, 0, skb->len); 232662306a36Sopenharmony_ci if (unlikely(num_sg < 0)) 232762306a36Sopenharmony_ci return num_sg; 232862306a36Sopenharmony_ci /* Pull header back to avoid skew in tx bytes calculations. */ 232962306a36Sopenharmony_ci __skb_pull(skb, hdr_len); 233062306a36Sopenharmony_ci } else { 233162306a36Sopenharmony_ci sg_set_buf(sq->sg, hdr, hdr_len); 233262306a36Sopenharmony_ci num_sg = skb_to_sgvec(skb, sq->sg + 1, 0, skb->len); 233362306a36Sopenharmony_ci if (unlikely(num_sg < 0)) 233462306a36Sopenharmony_ci return num_sg; 233562306a36Sopenharmony_ci num_sg++; 233662306a36Sopenharmony_ci } 233762306a36Sopenharmony_ci return virtqueue_add_outbuf(sq->vq, sq->sg, num_sg, skb, GFP_ATOMIC); 233862306a36Sopenharmony_ci} 233962306a36Sopenharmony_ci 234062306a36Sopenharmony_cistatic netdev_tx_t start_xmit(struct sk_buff *skb, struct net_device *dev) 234162306a36Sopenharmony_ci{ 234262306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 234362306a36Sopenharmony_ci int qnum = skb_get_queue_mapping(skb); 234462306a36Sopenharmony_ci struct send_queue *sq = &vi->sq[qnum]; 234562306a36Sopenharmony_ci int err; 234662306a36Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(dev, qnum); 234762306a36Sopenharmony_ci bool kick = !netdev_xmit_more(); 234862306a36Sopenharmony_ci bool use_napi = sq->napi.weight; 234962306a36Sopenharmony_ci 235062306a36Sopenharmony_ci /* Free up any pending old buffers before queueing new ones. */ 235162306a36Sopenharmony_ci do { 235262306a36Sopenharmony_ci if (use_napi) 235362306a36Sopenharmony_ci virtqueue_disable_cb(sq->vq); 235462306a36Sopenharmony_ci 235562306a36Sopenharmony_ci free_old_xmit_skbs(sq, false); 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci } while (use_napi && kick && 235862306a36Sopenharmony_ci unlikely(!virtqueue_enable_cb_delayed(sq->vq))); 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci /* timestamp packet in software */ 236162306a36Sopenharmony_ci skb_tx_timestamp(skb); 236262306a36Sopenharmony_ci 236362306a36Sopenharmony_ci /* Try to transmit */ 236462306a36Sopenharmony_ci err = xmit_skb(sq, skb); 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci /* This should not happen! */ 236762306a36Sopenharmony_ci if (unlikely(err)) { 236862306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_fifo_errors); 236962306a36Sopenharmony_ci if (net_ratelimit()) 237062306a36Sopenharmony_ci dev_warn(&dev->dev, 237162306a36Sopenharmony_ci "Unexpected TXQ (%d) queue failure: %d\n", 237262306a36Sopenharmony_ci qnum, err); 237362306a36Sopenharmony_ci DEV_STATS_INC(dev, tx_dropped); 237462306a36Sopenharmony_ci dev_kfree_skb_any(skb); 237562306a36Sopenharmony_ci return NETDEV_TX_OK; 237662306a36Sopenharmony_ci } 237762306a36Sopenharmony_ci 237862306a36Sopenharmony_ci /* Don't wait up for transmitted skbs to be freed. */ 237962306a36Sopenharmony_ci if (!use_napi) { 238062306a36Sopenharmony_ci skb_orphan(skb); 238162306a36Sopenharmony_ci nf_reset_ct(skb); 238262306a36Sopenharmony_ci } 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci check_sq_full_and_disable(vi, dev, sq); 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci if (kick || netif_xmit_stopped(txq)) { 238762306a36Sopenharmony_ci if (virtqueue_kick_prepare(sq->vq) && virtqueue_notify(sq->vq)) { 238862306a36Sopenharmony_ci u64_stats_update_begin(&sq->stats.syncp); 238962306a36Sopenharmony_ci u64_stats_inc(&sq->stats.kicks); 239062306a36Sopenharmony_ci u64_stats_update_end(&sq->stats.syncp); 239162306a36Sopenharmony_ci } 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci 239462306a36Sopenharmony_ci return NETDEV_TX_OK; 239562306a36Sopenharmony_ci} 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_cistatic int virtnet_rx_resize(struct virtnet_info *vi, 239862306a36Sopenharmony_ci struct receive_queue *rq, u32 ring_num) 239962306a36Sopenharmony_ci{ 240062306a36Sopenharmony_ci bool running = netif_running(vi->dev); 240162306a36Sopenharmony_ci int err, qindex; 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci qindex = rq - vi->rq; 240462306a36Sopenharmony_ci 240562306a36Sopenharmony_ci if (running) 240662306a36Sopenharmony_ci napi_disable(&rq->napi); 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci err = virtqueue_resize(rq->vq, ring_num, virtnet_rq_unmap_free_buf); 240962306a36Sopenharmony_ci if (err) 241062306a36Sopenharmony_ci netdev_err(vi->dev, "resize rx fail: rx queue index: %d err: %d\n", qindex, err); 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci if (!try_fill_recv(vi, rq, GFP_KERNEL)) 241362306a36Sopenharmony_ci schedule_delayed_work(&vi->refill, 0); 241462306a36Sopenharmony_ci 241562306a36Sopenharmony_ci if (running) 241662306a36Sopenharmony_ci virtnet_napi_enable(rq->vq, &rq->napi); 241762306a36Sopenharmony_ci return err; 241862306a36Sopenharmony_ci} 241962306a36Sopenharmony_ci 242062306a36Sopenharmony_cistatic int virtnet_tx_resize(struct virtnet_info *vi, 242162306a36Sopenharmony_ci struct send_queue *sq, u32 ring_num) 242262306a36Sopenharmony_ci{ 242362306a36Sopenharmony_ci bool running = netif_running(vi->dev); 242462306a36Sopenharmony_ci struct netdev_queue *txq; 242562306a36Sopenharmony_ci int err, qindex; 242662306a36Sopenharmony_ci 242762306a36Sopenharmony_ci qindex = sq - vi->sq; 242862306a36Sopenharmony_ci 242962306a36Sopenharmony_ci if (running) 243062306a36Sopenharmony_ci virtnet_napi_tx_disable(&sq->napi); 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci txq = netdev_get_tx_queue(vi->dev, qindex); 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci /* 1. wait all ximt complete 243562306a36Sopenharmony_ci * 2. fix the race of netif_stop_subqueue() vs netif_start_subqueue() 243662306a36Sopenharmony_ci */ 243762306a36Sopenharmony_ci __netif_tx_lock_bh(txq); 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci /* Prevent rx poll from accessing sq. */ 244062306a36Sopenharmony_ci sq->reset = true; 244162306a36Sopenharmony_ci 244262306a36Sopenharmony_ci /* Prevent the upper layer from trying to send packets. */ 244362306a36Sopenharmony_ci netif_stop_subqueue(vi->dev, qindex); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci __netif_tx_unlock_bh(txq); 244662306a36Sopenharmony_ci 244762306a36Sopenharmony_ci err = virtqueue_resize(sq->vq, ring_num, virtnet_sq_free_unused_buf); 244862306a36Sopenharmony_ci if (err) 244962306a36Sopenharmony_ci netdev_err(vi->dev, "resize tx fail: tx queue index: %d err: %d\n", qindex, err); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci __netif_tx_lock_bh(txq); 245262306a36Sopenharmony_ci sq->reset = false; 245362306a36Sopenharmony_ci netif_tx_wake_queue(txq); 245462306a36Sopenharmony_ci __netif_tx_unlock_bh(txq); 245562306a36Sopenharmony_ci 245662306a36Sopenharmony_ci if (running) 245762306a36Sopenharmony_ci virtnet_napi_tx_enable(vi, sq->vq, &sq->napi); 245862306a36Sopenharmony_ci return err; 245962306a36Sopenharmony_ci} 246062306a36Sopenharmony_ci 246162306a36Sopenharmony_ci/* 246262306a36Sopenharmony_ci * Send command via the control virtqueue and check status. Commands 246362306a36Sopenharmony_ci * supported by the hypervisor, as indicated by feature bits, should 246462306a36Sopenharmony_ci * never fail unless improperly formatted. 246562306a36Sopenharmony_ci */ 246662306a36Sopenharmony_cistatic bool virtnet_send_command(struct virtnet_info *vi, u8 class, u8 cmd, 246762306a36Sopenharmony_ci struct scatterlist *out) 246862306a36Sopenharmony_ci{ 246962306a36Sopenharmony_ci struct scatterlist *sgs[4], hdr, stat; 247062306a36Sopenharmony_ci unsigned out_num = 0, tmp; 247162306a36Sopenharmony_ci int ret; 247262306a36Sopenharmony_ci 247362306a36Sopenharmony_ci /* Caller should know better */ 247462306a36Sopenharmony_ci BUG_ON(!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ)); 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci vi->ctrl->status = ~0; 247762306a36Sopenharmony_ci vi->ctrl->hdr.class = class; 247862306a36Sopenharmony_ci vi->ctrl->hdr.cmd = cmd; 247962306a36Sopenharmony_ci /* Add header */ 248062306a36Sopenharmony_ci sg_init_one(&hdr, &vi->ctrl->hdr, sizeof(vi->ctrl->hdr)); 248162306a36Sopenharmony_ci sgs[out_num++] = &hdr; 248262306a36Sopenharmony_ci 248362306a36Sopenharmony_ci if (out) 248462306a36Sopenharmony_ci sgs[out_num++] = out; 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci /* Add return status. */ 248762306a36Sopenharmony_ci sg_init_one(&stat, &vi->ctrl->status, sizeof(vi->ctrl->status)); 248862306a36Sopenharmony_ci sgs[out_num] = &stat; 248962306a36Sopenharmony_ci 249062306a36Sopenharmony_ci BUG_ON(out_num + 1 > ARRAY_SIZE(sgs)); 249162306a36Sopenharmony_ci ret = virtqueue_add_sgs(vi->cvq, sgs, out_num, 1, vi, GFP_ATOMIC); 249262306a36Sopenharmony_ci if (ret < 0) { 249362306a36Sopenharmony_ci dev_warn(&vi->vdev->dev, 249462306a36Sopenharmony_ci "Failed to add sgs for command vq: %d\n.", ret); 249562306a36Sopenharmony_ci return false; 249662306a36Sopenharmony_ci } 249762306a36Sopenharmony_ci 249862306a36Sopenharmony_ci if (unlikely(!virtqueue_kick(vi->cvq))) 249962306a36Sopenharmony_ci return vi->ctrl->status == VIRTIO_NET_OK; 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci /* Spin for a response, the kick causes an ioport write, trapping 250262306a36Sopenharmony_ci * into the hypervisor, so the request should be handled immediately. 250362306a36Sopenharmony_ci */ 250462306a36Sopenharmony_ci while (!virtqueue_get_buf(vi->cvq, &tmp) && 250562306a36Sopenharmony_ci !virtqueue_is_broken(vi->cvq)) 250662306a36Sopenharmony_ci cpu_relax(); 250762306a36Sopenharmony_ci 250862306a36Sopenharmony_ci return vi->ctrl->status == VIRTIO_NET_OK; 250962306a36Sopenharmony_ci} 251062306a36Sopenharmony_ci 251162306a36Sopenharmony_cistatic int virtnet_set_mac_address(struct net_device *dev, void *p) 251262306a36Sopenharmony_ci{ 251362306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 251462306a36Sopenharmony_ci struct virtio_device *vdev = vi->vdev; 251562306a36Sopenharmony_ci int ret; 251662306a36Sopenharmony_ci struct sockaddr *addr; 251762306a36Sopenharmony_ci struct scatterlist sg; 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STANDBY)) 252062306a36Sopenharmony_ci return -EOPNOTSUPP; 252162306a36Sopenharmony_ci 252262306a36Sopenharmony_ci addr = kmemdup(p, sizeof(*addr), GFP_KERNEL); 252362306a36Sopenharmony_ci if (!addr) 252462306a36Sopenharmony_ci return -ENOMEM; 252562306a36Sopenharmony_ci 252662306a36Sopenharmony_ci ret = eth_prepare_mac_addr_change(dev, addr); 252762306a36Sopenharmony_ci if (ret) 252862306a36Sopenharmony_ci goto out; 252962306a36Sopenharmony_ci 253062306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR)) { 253162306a36Sopenharmony_ci sg_init_one(&sg, addr->sa_data, dev->addr_len); 253262306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MAC, 253362306a36Sopenharmony_ci VIRTIO_NET_CTRL_MAC_ADDR_SET, &sg)) { 253462306a36Sopenharmony_ci dev_warn(&vdev->dev, 253562306a36Sopenharmony_ci "Failed to set mac address by vq command.\n"); 253662306a36Sopenharmony_ci ret = -EINVAL; 253762306a36Sopenharmony_ci goto out; 253862306a36Sopenharmony_ci } 253962306a36Sopenharmony_ci } else if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC) && 254062306a36Sopenharmony_ci !virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) { 254162306a36Sopenharmony_ci unsigned int i; 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci /* Naturally, this has an atomicity problem. */ 254462306a36Sopenharmony_ci for (i = 0; i < dev->addr_len; i++) 254562306a36Sopenharmony_ci virtio_cwrite8(vdev, 254662306a36Sopenharmony_ci offsetof(struct virtio_net_config, mac) + 254762306a36Sopenharmony_ci i, addr->sa_data[i]); 254862306a36Sopenharmony_ci } 254962306a36Sopenharmony_ci 255062306a36Sopenharmony_ci eth_commit_mac_addr_change(dev, p); 255162306a36Sopenharmony_ci ret = 0; 255262306a36Sopenharmony_ci 255362306a36Sopenharmony_ciout: 255462306a36Sopenharmony_ci kfree(addr); 255562306a36Sopenharmony_ci return ret; 255662306a36Sopenharmony_ci} 255762306a36Sopenharmony_ci 255862306a36Sopenharmony_cistatic void virtnet_stats(struct net_device *dev, 255962306a36Sopenharmony_ci struct rtnl_link_stats64 *tot) 256062306a36Sopenharmony_ci{ 256162306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 256262306a36Sopenharmony_ci unsigned int start; 256362306a36Sopenharmony_ci int i; 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 256662306a36Sopenharmony_ci u64 tpackets, tbytes, terrors, rpackets, rbytes, rdrops; 256762306a36Sopenharmony_ci struct receive_queue *rq = &vi->rq[i]; 256862306a36Sopenharmony_ci struct send_queue *sq = &vi->sq[i]; 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci do { 257162306a36Sopenharmony_ci start = u64_stats_fetch_begin(&sq->stats.syncp); 257262306a36Sopenharmony_ci tpackets = u64_stats_read(&sq->stats.packets); 257362306a36Sopenharmony_ci tbytes = u64_stats_read(&sq->stats.bytes); 257462306a36Sopenharmony_ci terrors = u64_stats_read(&sq->stats.tx_timeouts); 257562306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&sq->stats.syncp, start)); 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci do { 257862306a36Sopenharmony_ci start = u64_stats_fetch_begin(&rq->stats.syncp); 257962306a36Sopenharmony_ci rpackets = u64_stats_read(&rq->stats.packets); 258062306a36Sopenharmony_ci rbytes = u64_stats_read(&rq->stats.bytes); 258162306a36Sopenharmony_ci rdrops = u64_stats_read(&rq->stats.drops); 258262306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&rq->stats.syncp, start)); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci tot->rx_packets += rpackets; 258562306a36Sopenharmony_ci tot->tx_packets += tpackets; 258662306a36Sopenharmony_ci tot->rx_bytes += rbytes; 258762306a36Sopenharmony_ci tot->tx_bytes += tbytes; 258862306a36Sopenharmony_ci tot->rx_dropped += rdrops; 258962306a36Sopenharmony_ci tot->tx_errors += terrors; 259062306a36Sopenharmony_ci } 259162306a36Sopenharmony_ci 259262306a36Sopenharmony_ci tot->tx_dropped = DEV_STATS_READ(dev, tx_dropped); 259362306a36Sopenharmony_ci tot->tx_fifo_errors = DEV_STATS_READ(dev, tx_fifo_errors); 259462306a36Sopenharmony_ci tot->rx_length_errors = DEV_STATS_READ(dev, rx_length_errors); 259562306a36Sopenharmony_ci tot->rx_frame_errors = DEV_STATS_READ(dev, rx_frame_errors); 259662306a36Sopenharmony_ci} 259762306a36Sopenharmony_ci 259862306a36Sopenharmony_cistatic void virtnet_ack_link_announce(struct virtnet_info *vi) 259962306a36Sopenharmony_ci{ 260062306a36Sopenharmony_ci rtnl_lock(); 260162306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_ANNOUNCE, 260262306a36Sopenharmony_ci VIRTIO_NET_CTRL_ANNOUNCE_ACK, NULL)) 260362306a36Sopenharmony_ci dev_warn(&vi->dev->dev, "Failed to ack link announce.\n"); 260462306a36Sopenharmony_ci rtnl_unlock(); 260562306a36Sopenharmony_ci} 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_cistatic int _virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) 260862306a36Sopenharmony_ci{ 260962306a36Sopenharmony_ci struct scatterlist sg; 261062306a36Sopenharmony_ci struct net_device *dev = vi->dev; 261162306a36Sopenharmony_ci 261262306a36Sopenharmony_ci if (!vi->has_cvq || !virtio_has_feature(vi->vdev, VIRTIO_NET_F_MQ)) 261362306a36Sopenharmony_ci return 0; 261462306a36Sopenharmony_ci 261562306a36Sopenharmony_ci vi->ctrl->mq.virtqueue_pairs = cpu_to_virtio16(vi->vdev, queue_pairs); 261662306a36Sopenharmony_ci sg_init_one(&sg, &vi->ctrl->mq, sizeof(vi->ctrl->mq)); 261762306a36Sopenharmony_ci 261862306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MQ, 261962306a36Sopenharmony_ci VIRTIO_NET_CTRL_MQ_VQ_PAIRS_SET, &sg)) { 262062306a36Sopenharmony_ci dev_warn(&dev->dev, "Fail to set num of queue pairs to %d\n", 262162306a36Sopenharmony_ci queue_pairs); 262262306a36Sopenharmony_ci return -EINVAL; 262362306a36Sopenharmony_ci } else { 262462306a36Sopenharmony_ci vi->curr_queue_pairs = queue_pairs; 262562306a36Sopenharmony_ci /* virtnet_open() will refill when device is going to up. */ 262662306a36Sopenharmony_ci if (dev->flags & IFF_UP) 262762306a36Sopenharmony_ci schedule_delayed_work(&vi->refill, 0); 262862306a36Sopenharmony_ci } 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_ci return 0; 263162306a36Sopenharmony_ci} 263262306a36Sopenharmony_ci 263362306a36Sopenharmony_cistatic int virtnet_set_queues(struct virtnet_info *vi, u16 queue_pairs) 263462306a36Sopenharmony_ci{ 263562306a36Sopenharmony_ci int err; 263662306a36Sopenharmony_ci 263762306a36Sopenharmony_ci rtnl_lock(); 263862306a36Sopenharmony_ci err = _virtnet_set_queues(vi, queue_pairs); 263962306a36Sopenharmony_ci rtnl_unlock(); 264062306a36Sopenharmony_ci return err; 264162306a36Sopenharmony_ci} 264262306a36Sopenharmony_ci 264362306a36Sopenharmony_cistatic int virtnet_close(struct net_device *dev) 264462306a36Sopenharmony_ci{ 264562306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 264662306a36Sopenharmony_ci int i; 264762306a36Sopenharmony_ci 264862306a36Sopenharmony_ci /* Make sure NAPI doesn't schedule refill work */ 264962306a36Sopenharmony_ci disable_delayed_refill(vi); 265062306a36Sopenharmony_ci /* Make sure refill_work doesn't re-enable napi! */ 265162306a36Sopenharmony_ci cancel_delayed_work_sync(&vi->refill); 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) 265462306a36Sopenharmony_ci virtnet_disable_queue_pair(vi, i); 265562306a36Sopenharmony_ci 265662306a36Sopenharmony_ci return 0; 265762306a36Sopenharmony_ci} 265862306a36Sopenharmony_ci 265962306a36Sopenharmony_cistatic void virtnet_set_rx_mode(struct net_device *dev) 266062306a36Sopenharmony_ci{ 266162306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 266262306a36Sopenharmony_ci struct scatterlist sg[2]; 266362306a36Sopenharmony_ci struct virtio_net_ctrl_mac *mac_data; 266462306a36Sopenharmony_ci struct netdev_hw_addr *ha; 266562306a36Sopenharmony_ci int uc_count; 266662306a36Sopenharmony_ci int mc_count; 266762306a36Sopenharmony_ci void *buf; 266862306a36Sopenharmony_ci int i; 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci /* We can't dynamically set ndo_set_rx_mode, so return gracefully */ 267162306a36Sopenharmony_ci if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_RX)) 267262306a36Sopenharmony_ci return; 267362306a36Sopenharmony_ci 267462306a36Sopenharmony_ci vi->ctrl->promisc = ((dev->flags & IFF_PROMISC) != 0); 267562306a36Sopenharmony_ci vi->ctrl->allmulti = ((dev->flags & IFF_ALLMULTI) != 0); 267662306a36Sopenharmony_ci 267762306a36Sopenharmony_ci sg_init_one(sg, &vi->ctrl->promisc, sizeof(vi->ctrl->promisc)); 267862306a36Sopenharmony_ci 267962306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, 268062306a36Sopenharmony_ci VIRTIO_NET_CTRL_RX_PROMISC, sg)) 268162306a36Sopenharmony_ci dev_warn(&dev->dev, "Failed to %sable promisc mode.\n", 268262306a36Sopenharmony_ci vi->ctrl->promisc ? "en" : "dis"); 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_ci sg_init_one(sg, &vi->ctrl->allmulti, sizeof(vi->ctrl->allmulti)); 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_RX, 268762306a36Sopenharmony_ci VIRTIO_NET_CTRL_RX_ALLMULTI, sg)) 268862306a36Sopenharmony_ci dev_warn(&dev->dev, "Failed to %sable allmulti mode.\n", 268962306a36Sopenharmony_ci vi->ctrl->allmulti ? "en" : "dis"); 269062306a36Sopenharmony_ci 269162306a36Sopenharmony_ci uc_count = netdev_uc_count(dev); 269262306a36Sopenharmony_ci mc_count = netdev_mc_count(dev); 269362306a36Sopenharmony_ci /* MAC filter - use one buffer for both lists */ 269462306a36Sopenharmony_ci buf = kzalloc(((uc_count + mc_count) * ETH_ALEN) + 269562306a36Sopenharmony_ci (2 * sizeof(mac_data->entries)), GFP_ATOMIC); 269662306a36Sopenharmony_ci mac_data = buf; 269762306a36Sopenharmony_ci if (!buf) 269862306a36Sopenharmony_ci return; 269962306a36Sopenharmony_ci 270062306a36Sopenharmony_ci sg_init_table(sg, 2); 270162306a36Sopenharmony_ci 270262306a36Sopenharmony_ci /* Store the unicast list and count in the front of the buffer */ 270362306a36Sopenharmony_ci mac_data->entries = cpu_to_virtio32(vi->vdev, uc_count); 270462306a36Sopenharmony_ci i = 0; 270562306a36Sopenharmony_ci netdev_for_each_uc_addr(ha, dev) 270662306a36Sopenharmony_ci memcpy(&mac_data->macs[i++][0], ha->addr, ETH_ALEN); 270762306a36Sopenharmony_ci 270862306a36Sopenharmony_ci sg_set_buf(&sg[0], mac_data, 270962306a36Sopenharmony_ci sizeof(mac_data->entries) + (uc_count * ETH_ALEN)); 271062306a36Sopenharmony_ci 271162306a36Sopenharmony_ci /* multicast list and count fill the end */ 271262306a36Sopenharmony_ci mac_data = (void *)&mac_data->macs[uc_count][0]; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci mac_data->entries = cpu_to_virtio32(vi->vdev, mc_count); 271562306a36Sopenharmony_ci i = 0; 271662306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, dev) 271762306a36Sopenharmony_ci memcpy(&mac_data->macs[i++][0], ha->addr, ETH_ALEN); 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci sg_set_buf(&sg[1], mac_data, 272062306a36Sopenharmony_ci sizeof(mac_data->entries) + (mc_count * ETH_ALEN)); 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MAC, 272362306a36Sopenharmony_ci VIRTIO_NET_CTRL_MAC_TABLE_SET, sg)) 272462306a36Sopenharmony_ci dev_warn(&dev->dev, "Failed to set MAC filter table.\n"); 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci kfree(buf); 272762306a36Sopenharmony_ci} 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_cistatic int virtnet_vlan_rx_add_vid(struct net_device *dev, 273062306a36Sopenharmony_ci __be16 proto, u16 vid) 273162306a36Sopenharmony_ci{ 273262306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 273362306a36Sopenharmony_ci struct scatterlist sg; 273462306a36Sopenharmony_ci 273562306a36Sopenharmony_ci vi->ctrl->vid = cpu_to_virtio16(vi->vdev, vid); 273662306a36Sopenharmony_ci sg_init_one(&sg, &vi->ctrl->vid, sizeof(vi->ctrl->vid)); 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN, 273962306a36Sopenharmony_ci VIRTIO_NET_CTRL_VLAN_ADD, &sg)) 274062306a36Sopenharmony_ci dev_warn(&dev->dev, "Failed to add VLAN ID %d.\n", vid); 274162306a36Sopenharmony_ci return 0; 274262306a36Sopenharmony_ci} 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_cistatic int virtnet_vlan_rx_kill_vid(struct net_device *dev, 274562306a36Sopenharmony_ci __be16 proto, u16 vid) 274662306a36Sopenharmony_ci{ 274762306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 274862306a36Sopenharmony_ci struct scatterlist sg; 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci vi->ctrl->vid = cpu_to_virtio16(vi->vdev, vid); 275162306a36Sopenharmony_ci sg_init_one(&sg, &vi->ctrl->vid, sizeof(vi->ctrl->vid)); 275262306a36Sopenharmony_ci 275362306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_VLAN, 275462306a36Sopenharmony_ci VIRTIO_NET_CTRL_VLAN_DEL, &sg)) 275562306a36Sopenharmony_ci dev_warn(&dev->dev, "Failed to kill VLAN ID %d.\n", vid); 275662306a36Sopenharmony_ci return 0; 275762306a36Sopenharmony_ci} 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_cistatic void virtnet_clean_affinity(struct virtnet_info *vi) 276062306a36Sopenharmony_ci{ 276162306a36Sopenharmony_ci int i; 276262306a36Sopenharmony_ci 276362306a36Sopenharmony_ci if (vi->affinity_hint_set) { 276462306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 276562306a36Sopenharmony_ci virtqueue_set_affinity(vi->rq[i].vq, NULL); 276662306a36Sopenharmony_ci virtqueue_set_affinity(vi->sq[i].vq, NULL); 276762306a36Sopenharmony_ci } 276862306a36Sopenharmony_ci 276962306a36Sopenharmony_ci vi->affinity_hint_set = false; 277062306a36Sopenharmony_ci } 277162306a36Sopenharmony_ci} 277262306a36Sopenharmony_ci 277362306a36Sopenharmony_cistatic void virtnet_set_affinity(struct virtnet_info *vi) 277462306a36Sopenharmony_ci{ 277562306a36Sopenharmony_ci cpumask_var_t mask; 277662306a36Sopenharmony_ci int stragglers; 277762306a36Sopenharmony_ci int group_size; 277862306a36Sopenharmony_ci int i, j, cpu; 277962306a36Sopenharmony_ci int num_cpu; 278062306a36Sopenharmony_ci int stride; 278162306a36Sopenharmony_ci 278262306a36Sopenharmony_ci if (!zalloc_cpumask_var(&mask, GFP_KERNEL)) { 278362306a36Sopenharmony_ci virtnet_clean_affinity(vi); 278462306a36Sopenharmony_ci return; 278562306a36Sopenharmony_ci } 278662306a36Sopenharmony_ci 278762306a36Sopenharmony_ci num_cpu = num_online_cpus(); 278862306a36Sopenharmony_ci stride = max_t(int, num_cpu / vi->curr_queue_pairs, 1); 278962306a36Sopenharmony_ci stragglers = num_cpu >= vi->curr_queue_pairs ? 279062306a36Sopenharmony_ci num_cpu % vi->curr_queue_pairs : 279162306a36Sopenharmony_ci 0; 279262306a36Sopenharmony_ci cpu = cpumask_first(cpu_online_mask); 279362306a36Sopenharmony_ci 279462306a36Sopenharmony_ci for (i = 0; i < vi->curr_queue_pairs; i++) { 279562306a36Sopenharmony_ci group_size = stride + (i < stragglers ? 1 : 0); 279662306a36Sopenharmony_ci 279762306a36Sopenharmony_ci for (j = 0; j < group_size; j++) { 279862306a36Sopenharmony_ci cpumask_set_cpu(cpu, mask); 279962306a36Sopenharmony_ci cpu = cpumask_next_wrap(cpu, cpu_online_mask, 280062306a36Sopenharmony_ci nr_cpu_ids, false); 280162306a36Sopenharmony_ci } 280262306a36Sopenharmony_ci virtqueue_set_affinity(vi->rq[i].vq, mask); 280362306a36Sopenharmony_ci virtqueue_set_affinity(vi->sq[i].vq, mask); 280462306a36Sopenharmony_ci __netif_set_xps_queue(vi->dev, cpumask_bits(mask), i, XPS_CPUS); 280562306a36Sopenharmony_ci cpumask_clear(mask); 280662306a36Sopenharmony_ci } 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci vi->affinity_hint_set = true; 280962306a36Sopenharmony_ci free_cpumask_var(mask); 281062306a36Sopenharmony_ci} 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_cistatic int virtnet_cpu_online(unsigned int cpu, struct hlist_node *node) 281362306a36Sopenharmony_ci{ 281462306a36Sopenharmony_ci struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info, 281562306a36Sopenharmony_ci node); 281662306a36Sopenharmony_ci virtnet_set_affinity(vi); 281762306a36Sopenharmony_ci return 0; 281862306a36Sopenharmony_ci} 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_cistatic int virtnet_cpu_dead(unsigned int cpu, struct hlist_node *node) 282162306a36Sopenharmony_ci{ 282262306a36Sopenharmony_ci struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info, 282362306a36Sopenharmony_ci node_dead); 282462306a36Sopenharmony_ci virtnet_set_affinity(vi); 282562306a36Sopenharmony_ci return 0; 282662306a36Sopenharmony_ci} 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_cistatic int virtnet_cpu_down_prep(unsigned int cpu, struct hlist_node *node) 282962306a36Sopenharmony_ci{ 283062306a36Sopenharmony_ci struct virtnet_info *vi = hlist_entry_safe(node, struct virtnet_info, 283162306a36Sopenharmony_ci node); 283262306a36Sopenharmony_ci 283362306a36Sopenharmony_ci virtnet_clean_affinity(vi); 283462306a36Sopenharmony_ci return 0; 283562306a36Sopenharmony_ci} 283662306a36Sopenharmony_ci 283762306a36Sopenharmony_cistatic enum cpuhp_state virtionet_online; 283862306a36Sopenharmony_ci 283962306a36Sopenharmony_cistatic int virtnet_cpu_notif_add(struct virtnet_info *vi) 284062306a36Sopenharmony_ci{ 284162306a36Sopenharmony_ci int ret; 284262306a36Sopenharmony_ci 284362306a36Sopenharmony_ci ret = cpuhp_state_add_instance_nocalls(virtionet_online, &vi->node); 284462306a36Sopenharmony_ci if (ret) 284562306a36Sopenharmony_ci return ret; 284662306a36Sopenharmony_ci ret = cpuhp_state_add_instance_nocalls(CPUHP_VIRT_NET_DEAD, 284762306a36Sopenharmony_ci &vi->node_dead); 284862306a36Sopenharmony_ci if (!ret) 284962306a36Sopenharmony_ci return ret; 285062306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls(virtionet_online, &vi->node); 285162306a36Sopenharmony_ci return ret; 285262306a36Sopenharmony_ci} 285362306a36Sopenharmony_ci 285462306a36Sopenharmony_cistatic void virtnet_cpu_notif_remove(struct virtnet_info *vi) 285562306a36Sopenharmony_ci{ 285662306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls(virtionet_online, &vi->node); 285762306a36Sopenharmony_ci cpuhp_state_remove_instance_nocalls(CPUHP_VIRT_NET_DEAD, 285862306a36Sopenharmony_ci &vi->node_dead); 285962306a36Sopenharmony_ci} 286062306a36Sopenharmony_ci 286162306a36Sopenharmony_cistatic void virtnet_get_ringparam(struct net_device *dev, 286262306a36Sopenharmony_ci struct ethtool_ringparam *ring, 286362306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 286462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 286562306a36Sopenharmony_ci{ 286662306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_ci ring->rx_max_pending = vi->rq[0].vq->num_max; 286962306a36Sopenharmony_ci ring->tx_max_pending = vi->sq[0].vq->num_max; 287062306a36Sopenharmony_ci ring->rx_pending = virtqueue_get_vring_size(vi->rq[0].vq); 287162306a36Sopenharmony_ci ring->tx_pending = virtqueue_get_vring_size(vi->sq[0].vq); 287262306a36Sopenharmony_ci} 287362306a36Sopenharmony_ci 287462306a36Sopenharmony_cistatic int virtnet_send_ctrl_coal_vq_cmd(struct virtnet_info *vi, 287562306a36Sopenharmony_ci u16 vqn, u32 max_usecs, u32 max_packets); 287662306a36Sopenharmony_ci 287762306a36Sopenharmony_cistatic int virtnet_set_ringparam(struct net_device *dev, 287862306a36Sopenharmony_ci struct ethtool_ringparam *ring, 287962306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_ring, 288062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 288162306a36Sopenharmony_ci{ 288262306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 288362306a36Sopenharmony_ci u32 rx_pending, tx_pending; 288462306a36Sopenharmony_ci struct receive_queue *rq; 288562306a36Sopenharmony_ci struct send_queue *sq; 288662306a36Sopenharmony_ci int i, err; 288762306a36Sopenharmony_ci 288862306a36Sopenharmony_ci if (ring->rx_mini_pending || ring->rx_jumbo_pending) 288962306a36Sopenharmony_ci return -EINVAL; 289062306a36Sopenharmony_ci 289162306a36Sopenharmony_ci rx_pending = virtqueue_get_vring_size(vi->rq[0].vq); 289262306a36Sopenharmony_ci tx_pending = virtqueue_get_vring_size(vi->sq[0].vq); 289362306a36Sopenharmony_ci 289462306a36Sopenharmony_ci if (ring->rx_pending == rx_pending && 289562306a36Sopenharmony_ci ring->tx_pending == tx_pending) 289662306a36Sopenharmony_ci return 0; 289762306a36Sopenharmony_ci 289862306a36Sopenharmony_ci if (ring->rx_pending > vi->rq[0].vq->num_max) 289962306a36Sopenharmony_ci return -EINVAL; 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci if (ring->tx_pending > vi->sq[0].vq->num_max) 290262306a36Sopenharmony_ci return -EINVAL; 290362306a36Sopenharmony_ci 290462306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 290562306a36Sopenharmony_ci rq = vi->rq + i; 290662306a36Sopenharmony_ci sq = vi->sq + i; 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci if (ring->tx_pending != tx_pending) { 290962306a36Sopenharmony_ci err = virtnet_tx_resize(vi, sq, ring->tx_pending); 291062306a36Sopenharmony_ci if (err) 291162306a36Sopenharmony_ci return err; 291262306a36Sopenharmony_ci 291362306a36Sopenharmony_ci /* Upon disabling and re-enabling a transmit virtqueue, the device must 291462306a36Sopenharmony_ci * set the coalescing parameters of the virtqueue to those configured 291562306a36Sopenharmony_ci * through the VIRTIO_NET_CTRL_NOTF_COAL_TX_SET command, or, if the driver 291662306a36Sopenharmony_ci * did not set any TX coalescing parameters, to 0. 291762306a36Sopenharmony_ci */ 291862306a36Sopenharmony_ci err = virtnet_send_ctrl_coal_vq_cmd(vi, txq2vq(i), 291962306a36Sopenharmony_ci vi->intr_coal_tx.max_usecs, 292062306a36Sopenharmony_ci vi->intr_coal_tx.max_packets); 292162306a36Sopenharmony_ci if (err) 292262306a36Sopenharmony_ci return err; 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_ci vi->sq[i].intr_coal.max_usecs = vi->intr_coal_tx.max_usecs; 292562306a36Sopenharmony_ci vi->sq[i].intr_coal.max_packets = vi->intr_coal_tx.max_packets; 292662306a36Sopenharmony_ci } 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_ci if (ring->rx_pending != rx_pending) { 292962306a36Sopenharmony_ci err = virtnet_rx_resize(vi, rq, ring->rx_pending); 293062306a36Sopenharmony_ci if (err) 293162306a36Sopenharmony_ci return err; 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci /* The reason is same as the transmit virtqueue reset */ 293462306a36Sopenharmony_ci err = virtnet_send_ctrl_coal_vq_cmd(vi, rxq2vq(i), 293562306a36Sopenharmony_ci vi->intr_coal_rx.max_usecs, 293662306a36Sopenharmony_ci vi->intr_coal_rx.max_packets); 293762306a36Sopenharmony_ci if (err) 293862306a36Sopenharmony_ci return err; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci vi->rq[i].intr_coal.max_usecs = vi->intr_coal_rx.max_usecs; 294162306a36Sopenharmony_ci vi->rq[i].intr_coal.max_packets = vi->intr_coal_rx.max_packets; 294262306a36Sopenharmony_ci } 294362306a36Sopenharmony_ci } 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci return 0; 294662306a36Sopenharmony_ci} 294762306a36Sopenharmony_ci 294862306a36Sopenharmony_cistatic bool virtnet_commit_rss_command(struct virtnet_info *vi) 294962306a36Sopenharmony_ci{ 295062306a36Sopenharmony_ci struct net_device *dev = vi->dev; 295162306a36Sopenharmony_ci struct scatterlist sgs[4]; 295262306a36Sopenharmony_ci unsigned int sg_buf_size; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci /* prepare sgs */ 295562306a36Sopenharmony_ci sg_init_table(sgs, 4); 295662306a36Sopenharmony_ci 295762306a36Sopenharmony_ci sg_buf_size = offsetof(struct virtio_net_ctrl_rss, indirection_table); 295862306a36Sopenharmony_ci sg_set_buf(&sgs[0], &vi->ctrl->rss, sg_buf_size); 295962306a36Sopenharmony_ci 296062306a36Sopenharmony_ci sg_buf_size = sizeof(uint16_t) * (vi->ctrl->rss.indirection_table_mask + 1); 296162306a36Sopenharmony_ci sg_set_buf(&sgs[1], vi->ctrl->rss.indirection_table, sg_buf_size); 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci sg_buf_size = offsetof(struct virtio_net_ctrl_rss, key) 296462306a36Sopenharmony_ci - offsetof(struct virtio_net_ctrl_rss, max_tx_vq); 296562306a36Sopenharmony_ci sg_set_buf(&sgs[2], &vi->ctrl->rss.max_tx_vq, sg_buf_size); 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci sg_buf_size = vi->rss_key_size; 296862306a36Sopenharmony_ci sg_set_buf(&sgs[3], vi->ctrl->rss.key, sg_buf_size); 296962306a36Sopenharmony_ci 297062306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MQ, 297162306a36Sopenharmony_ci vi->has_rss ? VIRTIO_NET_CTRL_MQ_RSS_CONFIG 297262306a36Sopenharmony_ci : VIRTIO_NET_CTRL_MQ_HASH_CONFIG, sgs)) { 297362306a36Sopenharmony_ci dev_warn(&dev->dev, "VIRTIONET issue with committing RSS sgs\n"); 297462306a36Sopenharmony_ci return false; 297562306a36Sopenharmony_ci } 297662306a36Sopenharmony_ci return true; 297762306a36Sopenharmony_ci} 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_cistatic void virtnet_init_default_rss(struct virtnet_info *vi) 298062306a36Sopenharmony_ci{ 298162306a36Sopenharmony_ci u32 indir_val = 0; 298262306a36Sopenharmony_ci int i = 0; 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_ci vi->ctrl->rss.hash_types = vi->rss_hash_types_supported; 298562306a36Sopenharmony_ci vi->rss_hash_types_saved = vi->rss_hash_types_supported; 298662306a36Sopenharmony_ci vi->ctrl->rss.indirection_table_mask = vi->rss_indir_table_size 298762306a36Sopenharmony_ci ? vi->rss_indir_table_size - 1 : 0; 298862306a36Sopenharmony_ci vi->ctrl->rss.unclassified_queue = 0; 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci for (; i < vi->rss_indir_table_size; ++i) { 299162306a36Sopenharmony_ci indir_val = ethtool_rxfh_indir_default(i, vi->curr_queue_pairs); 299262306a36Sopenharmony_ci vi->ctrl->rss.indirection_table[i] = indir_val; 299362306a36Sopenharmony_ci } 299462306a36Sopenharmony_ci 299562306a36Sopenharmony_ci vi->ctrl->rss.max_tx_vq = vi->has_rss ? vi->curr_queue_pairs : 0; 299662306a36Sopenharmony_ci vi->ctrl->rss.hash_key_length = vi->rss_key_size; 299762306a36Sopenharmony_ci 299862306a36Sopenharmony_ci netdev_rss_key_fill(vi->ctrl->rss.key, vi->rss_key_size); 299962306a36Sopenharmony_ci} 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_cistatic void virtnet_get_hashflow(const struct virtnet_info *vi, struct ethtool_rxnfc *info) 300262306a36Sopenharmony_ci{ 300362306a36Sopenharmony_ci info->data = 0; 300462306a36Sopenharmony_ci switch (info->flow_type) { 300562306a36Sopenharmony_ci case TCP_V4_FLOW: 300662306a36Sopenharmony_ci if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_TCPv4) { 300762306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 300862306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 300962306a36Sopenharmony_ci } else if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_IPv4) { 301062306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 301162306a36Sopenharmony_ci } 301262306a36Sopenharmony_ci break; 301362306a36Sopenharmony_ci case TCP_V6_FLOW: 301462306a36Sopenharmony_ci if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_TCPv6) { 301562306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 301662306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 301762306a36Sopenharmony_ci } else if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_IPv6) { 301862306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 301962306a36Sopenharmony_ci } 302062306a36Sopenharmony_ci break; 302162306a36Sopenharmony_ci case UDP_V4_FLOW: 302262306a36Sopenharmony_ci if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_UDPv4) { 302362306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 302462306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 302562306a36Sopenharmony_ci } else if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_IPv4) { 302662306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 302762306a36Sopenharmony_ci } 302862306a36Sopenharmony_ci break; 302962306a36Sopenharmony_ci case UDP_V6_FLOW: 303062306a36Sopenharmony_ci if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_UDPv6) { 303162306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST | 303262306a36Sopenharmony_ci RXH_L4_B_0_1 | RXH_L4_B_2_3; 303362306a36Sopenharmony_ci } else if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_IPv6) { 303462306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 303562306a36Sopenharmony_ci } 303662306a36Sopenharmony_ci break; 303762306a36Sopenharmony_ci case IPV4_FLOW: 303862306a36Sopenharmony_ci if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_IPv4) 303962306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 304062306a36Sopenharmony_ci 304162306a36Sopenharmony_ci break; 304262306a36Sopenharmony_ci case IPV6_FLOW: 304362306a36Sopenharmony_ci if (vi->rss_hash_types_saved & VIRTIO_NET_RSS_HASH_TYPE_IPv6) 304462306a36Sopenharmony_ci info->data = RXH_IP_SRC | RXH_IP_DST; 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci break; 304762306a36Sopenharmony_ci default: 304862306a36Sopenharmony_ci info->data = 0; 304962306a36Sopenharmony_ci break; 305062306a36Sopenharmony_ci } 305162306a36Sopenharmony_ci} 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_cistatic bool virtnet_set_hashflow(struct virtnet_info *vi, struct ethtool_rxnfc *info) 305462306a36Sopenharmony_ci{ 305562306a36Sopenharmony_ci u32 new_hashtypes = vi->rss_hash_types_saved; 305662306a36Sopenharmony_ci bool is_disable = info->data & RXH_DISCARD; 305762306a36Sopenharmony_ci bool is_l4 = info->data == (RXH_IP_SRC | RXH_IP_DST | RXH_L4_B_0_1 | RXH_L4_B_2_3); 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_ci /* supports only 'sd', 'sdfn' and 'r' */ 306062306a36Sopenharmony_ci if (!((info->data == (RXH_IP_SRC | RXH_IP_DST)) | is_l4 | is_disable)) 306162306a36Sopenharmony_ci return false; 306262306a36Sopenharmony_ci 306362306a36Sopenharmony_ci switch (info->flow_type) { 306462306a36Sopenharmony_ci case TCP_V4_FLOW: 306562306a36Sopenharmony_ci new_hashtypes &= ~(VIRTIO_NET_RSS_HASH_TYPE_IPv4 | VIRTIO_NET_RSS_HASH_TYPE_TCPv4); 306662306a36Sopenharmony_ci if (!is_disable) 306762306a36Sopenharmony_ci new_hashtypes |= VIRTIO_NET_RSS_HASH_TYPE_IPv4 306862306a36Sopenharmony_ci | (is_l4 ? VIRTIO_NET_RSS_HASH_TYPE_TCPv4 : 0); 306962306a36Sopenharmony_ci break; 307062306a36Sopenharmony_ci case UDP_V4_FLOW: 307162306a36Sopenharmony_ci new_hashtypes &= ~(VIRTIO_NET_RSS_HASH_TYPE_IPv4 | VIRTIO_NET_RSS_HASH_TYPE_UDPv4); 307262306a36Sopenharmony_ci if (!is_disable) 307362306a36Sopenharmony_ci new_hashtypes |= VIRTIO_NET_RSS_HASH_TYPE_IPv4 307462306a36Sopenharmony_ci | (is_l4 ? VIRTIO_NET_RSS_HASH_TYPE_UDPv4 : 0); 307562306a36Sopenharmony_ci break; 307662306a36Sopenharmony_ci case IPV4_FLOW: 307762306a36Sopenharmony_ci new_hashtypes &= ~VIRTIO_NET_RSS_HASH_TYPE_IPv4; 307862306a36Sopenharmony_ci if (!is_disable) 307962306a36Sopenharmony_ci new_hashtypes = VIRTIO_NET_RSS_HASH_TYPE_IPv4; 308062306a36Sopenharmony_ci break; 308162306a36Sopenharmony_ci case TCP_V6_FLOW: 308262306a36Sopenharmony_ci new_hashtypes &= ~(VIRTIO_NET_RSS_HASH_TYPE_IPv6 | VIRTIO_NET_RSS_HASH_TYPE_TCPv6); 308362306a36Sopenharmony_ci if (!is_disable) 308462306a36Sopenharmony_ci new_hashtypes |= VIRTIO_NET_RSS_HASH_TYPE_IPv6 308562306a36Sopenharmony_ci | (is_l4 ? VIRTIO_NET_RSS_HASH_TYPE_TCPv6 : 0); 308662306a36Sopenharmony_ci break; 308762306a36Sopenharmony_ci case UDP_V6_FLOW: 308862306a36Sopenharmony_ci new_hashtypes &= ~(VIRTIO_NET_RSS_HASH_TYPE_IPv6 | VIRTIO_NET_RSS_HASH_TYPE_UDPv6); 308962306a36Sopenharmony_ci if (!is_disable) 309062306a36Sopenharmony_ci new_hashtypes |= VIRTIO_NET_RSS_HASH_TYPE_IPv6 309162306a36Sopenharmony_ci | (is_l4 ? VIRTIO_NET_RSS_HASH_TYPE_UDPv6 : 0); 309262306a36Sopenharmony_ci break; 309362306a36Sopenharmony_ci case IPV6_FLOW: 309462306a36Sopenharmony_ci new_hashtypes &= ~VIRTIO_NET_RSS_HASH_TYPE_IPv6; 309562306a36Sopenharmony_ci if (!is_disable) 309662306a36Sopenharmony_ci new_hashtypes = VIRTIO_NET_RSS_HASH_TYPE_IPv6; 309762306a36Sopenharmony_ci break; 309862306a36Sopenharmony_ci default: 309962306a36Sopenharmony_ci /* unsupported flow */ 310062306a36Sopenharmony_ci return false; 310162306a36Sopenharmony_ci } 310262306a36Sopenharmony_ci 310362306a36Sopenharmony_ci /* if unsupported hashtype was set */ 310462306a36Sopenharmony_ci if (new_hashtypes != (new_hashtypes & vi->rss_hash_types_supported)) 310562306a36Sopenharmony_ci return false; 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci if (new_hashtypes != vi->rss_hash_types_saved) { 310862306a36Sopenharmony_ci vi->rss_hash_types_saved = new_hashtypes; 310962306a36Sopenharmony_ci vi->ctrl->rss.hash_types = vi->rss_hash_types_saved; 311062306a36Sopenharmony_ci if (vi->dev->features & NETIF_F_RXHASH) 311162306a36Sopenharmony_ci return virtnet_commit_rss_command(vi); 311262306a36Sopenharmony_ci } 311362306a36Sopenharmony_ci 311462306a36Sopenharmony_ci return true; 311562306a36Sopenharmony_ci} 311662306a36Sopenharmony_ci 311762306a36Sopenharmony_cistatic void virtnet_get_drvinfo(struct net_device *dev, 311862306a36Sopenharmony_ci struct ethtool_drvinfo *info) 311962306a36Sopenharmony_ci{ 312062306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 312162306a36Sopenharmony_ci struct virtio_device *vdev = vi->vdev; 312262306a36Sopenharmony_ci 312362306a36Sopenharmony_ci strscpy(info->driver, KBUILD_MODNAME, sizeof(info->driver)); 312462306a36Sopenharmony_ci strscpy(info->version, VIRTNET_DRIVER_VERSION, sizeof(info->version)); 312562306a36Sopenharmony_ci strscpy(info->bus_info, virtio_bus_name(vdev), sizeof(info->bus_info)); 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci} 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_ci/* TODO: Eliminate OOO packets during switching */ 313062306a36Sopenharmony_cistatic int virtnet_set_channels(struct net_device *dev, 313162306a36Sopenharmony_ci struct ethtool_channels *channels) 313262306a36Sopenharmony_ci{ 313362306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 313462306a36Sopenharmony_ci u16 queue_pairs = channels->combined_count; 313562306a36Sopenharmony_ci int err; 313662306a36Sopenharmony_ci 313762306a36Sopenharmony_ci /* We don't support separate rx/tx channels. 313862306a36Sopenharmony_ci * We don't allow setting 'other' channels. 313962306a36Sopenharmony_ci */ 314062306a36Sopenharmony_ci if (channels->rx_count || channels->tx_count || channels->other_count) 314162306a36Sopenharmony_ci return -EINVAL; 314262306a36Sopenharmony_ci 314362306a36Sopenharmony_ci if (queue_pairs > vi->max_queue_pairs || queue_pairs == 0) 314462306a36Sopenharmony_ci return -EINVAL; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci /* For now we don't support modifying channels while XDP is loaded 314762306a36Sopenharmony_ci * also when XDP is loaded all RX queues have XDP programs so we only 314862306a36Sopenharmony_ci * need to check a single RX queue. 314962306a36Sopenharmony_ci */ 315062306a36Sopenharmony_ci if (vi->rq[0].xdp_prog) 315162306a36Sopenharmony_ci return -EINVAL; 315262306a36Sopenharmony_ci 315362306a36Sopenharmony_ci cpus_read_lock(); 315462306a36Sopenharmony_ci err = _virtnet_set_queues(vi, queue_pairs); 315562306a36Sopenharmony_ci if (err) { 315662306a36Sopenharmony_ci cpus_read_unlock(); 315762306a36Sopenharmony_ci goto err; 315862306a36Sopenharmony_ci } 315962306a36Sopenharmony_ci virtnet_set_affinity(vi); 316062306a36Sopenharmony_ci cpus_read_unlock(); 316162306a36Sopenharmony_ci 316262306a36Sopenharmony_ci netif_set_real_num_tx_queues(dev, queue_pairs); 316362306a36Sopenharmony_ci netif_set_real_num_rx_queues(dev, queue_pairs); 316462306a36Sopenharmony_ci err: 316562306a36Sopenharmony_ci return err; 316662306a36Sopenharmony_ci} 316762306a36Sopenharmony_ci 316862306a36Sopenharmony_cistatic void virtnet_get_strings(struct net_device *dev, u32 stringset, u8 *data) 316962306a36Sopenharmony_ci{ 317062306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 317162306a36Sopenharmony_ci unsigned int i, j; 317262306a36Sopenharmony_ci u8 *p = data; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci switch (stringset) { 317562306a36Sopenharmony_ci case ETH_SS_STATS: 317662306a36Sopenharmony_ci for (i = 0; i < vi->curr_queue_pairs; i++) { 317762306a36Sopenharmony_ci for (j = 0; j < VIRTNET_RQ_STATS_LEN; j++) 317862306a36Sopenharmony_ci ethtool_sprintf(&p, "rx_queue_%u_%s", i, 317962306a36Sopenharmony_ci virtnet_rq_stats_desc[j].desc); 318062306a36Sopenharmony_ci } 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci for (i = 0; i < vi->curr_queue_pairs; i++) { 318362306a36Sopenharmony_ci for (j = 0; j < VIRTNET_SQ_STATS_LEN; j++) 318462306a36Sopenharmony_ci ethtool_sprintf(&p, "tx_queue_%u_%s", i, 318562306a36Sopenharmony_ci virtnet_sq_stats_desc[j].desc); 318662306a36Sopenharmony_ci } 318762306a36Sopenharmony_ci break; 318862306a36Sopenharmony_ci } 318962306a36Sopenharmony_ci} 319062306a36Sopenharmony_ci 319162306a36Sopenharmony_cistatic int virtnet_get_sset_count(struct net_device *dev, int sset) 319262306a36Sopenharmony_ci{ 319362306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 319462306a36Sopenharmony_ci 319562306a36Sopenharmony_ci switch (sset) { 319662306a36Sopenharmony_ci case ETH_SS_STATS: 319762306a36Sopenharmony_ci return vi->curr_queue_pairs * (VIRTNET_RQ_STATS_LEN + 319862306a36Sopenharmony_ci VIRTNET_SQ_STATS_LEN); 319962306a36Sopenharmony_ci default: 320062306a36Sopenharmony_ci return -EOPNOTSUPP; 320162306a36Sopenharmony_ci } 320262306a36Sopenharmony_ci} 320362306a36Sopenharmony_ci 320462306a36Sopenharmony_cistatic void virtnet_get_ethtool_stats(struct net_device *dev, 320562306a36Sopenharmony_ci struct ethtool_stats *stats, u64 *data) 320662306a36Sopenharmony_ci{ 320762306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 320862306a36Sopenharmony_ci unsigned int idx = 0, start, i, j; 320962306a36Sopenharmony_ci const u8 *stats_base; 321062306a36Sopenharmony_ci const u64_stats_t *p; 321162306a36Sopenharmony_ci size_t offset; 321262306a36Sopenharmony_ci 321362306a36Sopenharmony_ci for (i = 0; i < vi->curr_queue_pairs; i++) { 321462306a36Sopenharmony_ci struct receive_queue *rq = &vi->rq[i]; 321562306a36Sopenharmony_ci 321662306a36Sopenharmony_ci stats_base = (const u8 *)&rq->stats; 321762306a36Sopenharmony_ci do { 321862306a36Sopenharmony_ci start = u64_stats_fetch_begin(&rq->stats.syncp); 321962306a36Sopenharmony_ci for (j = 0; j < VIRTNET_RQ_STATS_LEN; j++) { 322062306a36Sopenharmony_ci offset = virtnet_rq_stats_desc[j].offset; 322162306a36Sopenharmony_ci p = (const u64_stats_t *)(stats_base + offset); 322262306a36Sopenharmony_ci data[idx + j] = u64_stats_read(p); 322362306a36Sopenharmony_ci } 322462306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&rq->stats.syncp, start)); 322562306a36Sopenharmony_ci idx += VIRTNET_RQ_STATS_LEN; 322662306a36Sopenharmony_ci } 322762306a36Sopenharmony_ci 322862306a36Sopenharmony_ci for (i = 0; i < vi->curr_queue_pairs; i++) { 322962306a36Sopenharmony_ci struct send_queue *sq = &vi->sq[i]; 323062306a36Sopenharmony_ci 323162306a36Sopenharmony_ci stats_base = (const u8 *)&sq->stats; 323262306a36Sopenharmony_ci do { 323362306a36Sopenharmony_ci start = u64_stats_fetch_begin(&sq->stats.syncp); 323462306a36Sopenharmony_ci for (j = 0; j < VIRTNET_SQ_STATS_LEN; j++) { 323562306a36Sopenharmony_ci offset = virtnet_sq_stats_desc[j].offset; 323662306a36Sopenharmony_ci p = (const u64_stats_t *)(stats_base + offset); 323762306a36Sopenharmony_ci data[idx + j] = u64_stats_read(p); 323862306a36Sopenharmony_ci } 323962306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&sq->stats.syncp, start)); 324062306a36Sopenharmony_ci idx += VIRTNET_SQ_STATS_LEN; 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci} 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_cistatic void virtnet_get_channels(struct net_device *dev, 324562306a36Sopenharmony_ci struct ethtool_channels *channels) 324662306a36Sopenharmony_ci{ 324762306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci channels->combined_count = vi->curr_queue_pairs; 325062306a36Sopenharmony_ci channels->max_combined = vi->max_queue_pairs; 325162306a36Sopenharmony_ci channels->max_other = 0; 325262306a36Sopenharmony_ci channels->rx_count = 0; 325362306a36Sopenharmony_ci channels->tx_count = 0; 325462306a36Sopenharmony_ci channels->other_count = 0; 325562306a36Sopenharmony_ci} 325662306a36Sopenharmony_ci 325762306a36Sopenharmony_cistatic int virtnet_set_link_ksettings(struct net_device *dev, 325862306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 325962306a36Sopenharmony_ci{ 326062306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_ci return ethtool_virtdev_set_link_ksettings(dev, cmd, 326362306a36Sopenharmony_ci &vi->speed, &vi->duplex); 326462306a36Sopenharmony_ci} 326562306a36Sopenharmony_ci 326662306a36Sopenharmony_cistatic int virtnet_get_link_ksettings(struct net_device *dev, 326762306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 326862306a36Sopenharmony_ci{ 326962306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci cmd->base.speed = vi->speed; 327262306a36Sopenharmony_ci cmd->base.duplex = vi->duplex; 327362306a36Sopenharmony_ci cmd->base.port = PORT_OTHER; 327462306a36Sopenharmony_ci 327562306a36Sopenharmony_ci return 0; 327662306a36Sopenharmony_ci} 327762306a36Sopenharmony_ci 327862306a36Sopenharmony_cistatic int virtnet_send_notf_coal_cmds(struct virtnet_info *vi, 327962306a36Sopenharmony_ci struct ethtool_coalesce *ec) 328062306a36Sopenharmony_ci{ 328162306a36Sopenharmony_ci struct scatterlist sgs_tx, sgs_rx; 328262306a36Sopenharmony_ci int i; 328362306a36Sopenharmony_ci 328462306a36Sopenharmony_ci vi->ctrl->coal_tx.tx_usecs = cpu_to_le32(ec->tx_coalesce_usecs); 328562306a36Sopenharmony_ci vi->ctrl->coal_tx.tx_max_packets = cpu_to_le32(ec->tx_max_coalesced_frames); 328662306a36Sopenharmony_ci sg_init_one(&sgs_tx, &vi->ctrl->coal_tx, sizeof(vi->ctrl->coal_tx)); 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, 328962306a36Sopenharmony_ci VIRTIO_NET_CTRL_NOTF_COAL_TX_SET, 329062306a36Sopenharmony_ci &sgs_tx)) 329162306a36Sopenharmony_ci return -EINVAL; 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci /* Save parameters */ 329462306a36Sopenharmony_ci vi->intr_coal_tx.max_usecs = ec->tx_coalesce_usecs; 329562306a36Sopenharmony_ci vi->intr_coal_tx.max_packets = ec->tx_max_coalesced_frames; 329662306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 329762306a36Sopenharmony_ci vi->sq[i].intr_coal.max_usecs = ec->tx_coalesce_usecs; 329862306a36Sopenharmony_ci vi->sq[i].intr_coal.max_packets = ec->tx_max_coalesced_frames; 329962306a36Sopenharmony_ci } 330062306a36Sopenharmony_ci 330162306a36Sopenharmony_ci vi->ctrl->coal_rx.rx_usecs = cpu_to_le32(ec->rx_coalesce_usecs); 330262306a36Sopenharmony_ci vi->ctrl->coal_rx.rx_max_packets = cpu_to_le32(ec->rx_max_coalesced_frames); 330362306a36Sopenharmony_ci sg_init_one(&sgs_rx, &vi->ctrl->coal_rx, sizeof(vi->ctrl->coal_rx)); 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, 330662306a36Sopenharmony_ci VIRTIO_NET_CTRL_NOTF_COAL_RX_SET, 330762306a36Sopenharmony_ci &sgs_rx)) 330862306a36Sopenharmony_ci return -EINVAL; 330962306a36Sopenharmony_ci 331062306a36Sopenharmony_ci /* Save parameters */ 331162306a36Sopenharmony_ci vi->intr_coal_rx.max_usecs = ec->rx_coalesce_usecs; 331262306a36Sopenharmony_ci vi->intr_coal_rx.max_packets = ec->rx_max_coalesced_frames; 331362306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 331462306a36Sopenharmony_ci vi->rq[i].intr_coal.max_usecs = ec->rx_coalesce_usecs; 331562306a36Sopenharmony_ci vi->rq[i].intr_coal.max_packets = ec->rx_max_coalesced_frames; 331662306a36Sopenharmony_ci } 331762306a36Sopenharmony_ci 331862306a36Sopenharmony_ci return 0; 331962306a36Sopenharmony_ci} 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_cistatic int virtnet_send_ctrl_coal_vq_cmd(struct virtnet_info *vi, 332262306a36Sopenharmony_ci u16 vqn, u32 max_usecs, u32 max_packets) 332362306a36Sopenharmony_ci{ 332462306a36Sopenharmony_ci struct scatterlist sgs; 332562306a36Sopenharmony_ci 332662306a36Sopenharmony_ci vi->ctrl->coal_vq.vqn = cpu_to_le16(vqn); 332762306a36Sopenharmony_ci vi->ctrl->coal_vq.coal.max_usecs = cpu_to_le32(max_usecs); 332862306a36Sopenharmony_ci vi->ctrl->coal_vq.coal.max_packets = cpu_to_le32(max_packets); 332962306a36Sopenharmony_ci sg_init_one(&sgs, &vi->ctrl->coal_vq, sizeof(vi->ctrl->coal_vq)); 333062306a36Sopenharmony_ci 333162306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_NOTF_COAL, 333262306a36Sopenharmony_ci VIRTIO_NET_CTRL_NOTF_COAL_VQ_SET, 333362306a36Sopenharmony_ci &sgs)) 333462306a36Sopenharmony_ci return -EINVAL; 333562306a36Sopenharmony_ci 333662306a36Sopenharmony_ci return 0; 333762306a36Sopenharmony_ci} 333862306a36Sopenharmony_ci 333962306a36Sopenharmony_cistatic int virtnet_send_notf_coal_vq_cmds(struct virtnet_info *vi, 334062306a36Sopenharmony_ci struct ethtool_coalesce *ec, 334162306a36Sopenharmony_ci u16 queue) 334262306a36Sopenharmony_ci{ 334362306a36Sopenharmony_ci int err; 334462306a36Sopenharmony_ci 334562306a36Sopenharmony_ci err = virtnet_send_ctrl_coal_vq_cmd(vi, rxq2vq(queue), 334662306a36Sopenharmony_ci ec->rx_coalesce_usecs, 334762306a36Sopenharmony_ci ec->rx_max_coalesced_frames); 334862306a36Sopenharmony_ci if (err) 334962306a36Sopenharmony_ci return err; 335062306a36Sopenharmony_ci 335162306a36Sopenharmony_ci vi->rq[queue].intr_coal.max_usecs = ec->rx_coalesce_usecs; 335262306a36Sopenharmony_ci vi->rq[queue].intr_coal.max_packets = ec->rx_max_coalesced_frames; 335362306a36Sopenharmony_ci 335462306a36Sopenharmony_ci err = virtnet_send_ctrl_coal_vq_cmd(vi, txq2vq(queue), 335562306a36Sopenharmony_ci ec->tx_coalesce_usecs, 335662306a36Sopenharmony_ci ec->tx_max_coalesced_frames); 335762306a36Sopenharmony_ci if (err) 335862306a36Sopenharmony_ci return err; 335962306a36Sopenharmony_ci 336062306a36Sopenharmony_ci vi->sq[queue].intr_coal.max_usecs = ec->tx_coalesce_usecs; 336162306a36Sopenharmony_ci vi->sq[queue].intr_coal.max_packets = ec->tx_max_coalesced_frames; 336262306a36Sopenharmony_ci 336362306a36Sopenharmony_ci return 0; 336462306a36Sopenharmony_ci} 336562306a36Sopenharmony_ci 336662306a36Sopenharmony_cistatic int virtnet_coal_params_supported(struct ethtool_coalesce *ec) 336762306a36Sopenharmony_ci{ 336862306a36Sopenharmony_ci /* usecs coalescing is supported only if VIRTIO_NET_F_NOTF_COAL 336962306a36Sopenharmony_ci * feature is negotiated. 337062306a36Sopenharmony_ci */ 337162306a36Sopenharmony_ci if (ec->rx_coalesce_usecs || ec->tx_coalesce_usecs) 337262306a36Sopenharmony_ci return -EOPNOTSUPP; 337362306a36Sopenharmony_ci 337462306a36Sopenharmony_ci if (ec->tx_max_coalesced_frames > 1 || 337562306a36Sopenharmony_ci ec->rx_max_coalesced_frames != 1) 337662306a36Sopenharmony_ci return -EINVAL; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci return 0; 337962306a36Sopenharmony_ci} 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_cistatic int virtnet_should_update_vq_weight(int dev_flags, int weight, 338262306a36Sopenharmony_ci int vq_weight, bool *should_update) 338362306a36Sopenharmony_ci{ 338462306a36Sopenharmony_ci if (weight ^ vq_weight) { 338562306a36Sopenharmony_ci if (dev_flags & IFF_UP) 338662306a36Sopenharmony_ci return -EBUSY; 338762306a36Sopenharmony_ci *should_update = true; 338862306a36Sopenharmony_ci } 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci return 0; 339162306a36Sopenharmony_ci} 339262306a36Sopenharmony_ci 339362306a36Sopenharmony_cistatic int virtnet_set_coalesce(struct net_device *dev, 339462306a36Sopenharmony_ci struct ethtool_coalesce *ec, 339562306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 339662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 339762306a36Sopenharmony_ci{ 339862306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 339962306a36Sopenharmony_ci int ret, queue_number, napi_weight; 340062306a36Sopenharmony_ci bool update_napi = false; 340162306a36Sopenharmony_ci 340262306a36Sopenharmony_ci /* Can't change NAPI weight if the link is up */ 340362306a36Sopenharmony_ci napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0; 340462306a36Sopenharmony_ci for (queue_number = 0; queue_number < vi->max_queue_pairs; queue_number++) { 340562306a36Sopenharmony_ci ret = virtnet_should_update_vq_weight(dev->flags, napi_weight, 340662306a36Sopenharmony_ci vi->sq[queue_number].napi.weight, 340762306a36Sopenharmony_ci &update_napi); 340862306a36Sopenharmony_ci if (ret) 340962306a36Sopenharmony_ci return ret; 341062306a36Sopenharmony_ci 341162306a36Sopenharmony_ci if (update_napi) { 341262306a36Sopenharmony_ci /* All queues that belong to [queue_number, vi->max_queue_pairs] will be 341362306a36Sopenharmony_ci * updated for the sake of simplicity, which might not be necessary 341462306a36Sopenharmony_ci */ 341562306a36Sopenharmony_ci break; 341662306a36Sopenharmony_ci } 341762306a36Sopenharmony_ci } 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) 342062306a36Sopenharmony_ci ret = virtnet_send_notf_coal_cmds(vi, ec); 342162306a36Sopenharmony_ci else 342262306a36Sopenharmony_ci ret = virtnet_coal_params_supported(ec); 342362306a36Sopenharmony_ci 342462306a36Sopenharmony_ci if (ret) 342562306a36Sopenharmony_ci return ret; 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_ci if (update_napi) { 342862306a36Sopenharmony_ci for (; queue_number < vi->max_queue_pairs; queue_number++) 342962306a36Sopenharmony_ci vi->sq[queue_number].napi.weight = napi_weight; 343062306a36Sopenharmony_ci } 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci return ret; 343362306a36Sopenharmony_ci} 343462306a36Sopenharmony_ci 343562306a36Sopenharmony_cistatic int virtnet_get_coalesce(struct net_device *dev, 343662306a36Sopenharmony_ci struct ethtool_coalesce *ec, 343762306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 343862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 343962306a36Sopenharmony_ci{ 344062306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) { 344362306a36Sopenharmony_ci ec->rx_coalesce_usecs = vi->intr_coal_rx.max_usecs; 344462306a36Sopenharmony_ci ec->tx_coalesce_usecs = vi->intr_coal_tx.max_usecs; 344562306a36Sopenharmony_ci ec->tx_max_coalesced_frames = vi->intr_coal_tx.max_packets; 344662306a36Sopenharmony_ci ec->rx_max_coalesced_frames = vi->intr_coal_rx.max_packets; 344762306a36Sopenharmony_ci } else { 344862306a36Sopenharmony_ci ec->rx_max_coalesced_frames = 1; 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci if (vi->sq[0].napi.weight) 345162306a36Sopenharmony_ci ec->tx_max_coalesced_frames = 1; 345262306a36Sopenharmony_ci } 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci return 0; 345562306a36Sopenharmony_ci} 345662306a36Sopenharmony_ci 345762306a36Sopenharmony_cistatic int virtnet_set_per_queue_coalesce(struct net_device *dev, 345862306a36Sopenharmony_ci u32 queue, 345962306a36Sopenharmony_ci struct ethtool_coalesce *ec) 346062306a36Sopenharmony_ci{ 346162306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 346262306a36Sopenharmony_ci int ret, napi_weight; 346362306a36Sopenharmony_ci bool update_napi = false; 346462306a36Sopenharmony_ci 346562306a36Sopenharmony_ci if (queue >= vi->max_queue_pairs) 346662306a36Sopenharmony_ci return -EINVAL; 346762306a36Sopenharmony_ci 346862306a36Sopenharmony_ci /* Can't change NAPI weight if the link is up */ 346962306a36Sopenharmony_ci napi_weight = ec->tx_max_coalesced_frames ? NAPI_POLL_WEIGHT : 0; 347062306a36Sopenharmony_ci ret = virtnet_should_update_vq_weight(dev->flags, napi_weight, 347162306a36Sopenharmony_ci vi->sq[queue].napi.weight, 347262306a36Sopenharmony_ci &update_napi); 347362306a36Sopenharmony_ci if (ret) 347462306a36Sopenharmony_ci return ret; 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) 347762306a36Sopenharmony_ci ret = virtnet_send_notf_coal_vq_cmds(vi, ec, queue); 347862306a36Sopenharmony_ci else 347962306a36Sopenharmony_ci ret = virtnet_coal_params_supported(ec); 348062306a36Sopenharmony_ci 348162306a36Sopenharmony_ci if (ret) 348262306a36Sopenharmony_ci return ret; 348362306a36Sopenharmony_ci 348462306a36Sopenharmony_ci if (update_napi) 348562306a36Sopenharmony_ci vi->sq[queue].napi.weight = napi_weight; 348662306a36Sopenharmony_ci 348762306a36Sopenharmony_ci return 0; 348862306a36Sopenharmony_ci} 348962306a36Sopenharmony_ci 349062306a36Sopenharmony_cistatic int virtnet_get_per_queue_coalesce(struct net_device *dev, 349162306a36Sopenharmony_ci u32 queue, 349262306a36Sopenharmony_ci struct ethtool_coalesce *ec) 349362306a36Sopenharmony_ci{ 349462306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 349562306a36Sopenharmony_ci 349662306a36Sopenharmony_ci if (queue >= vi->max_queue_pairs) 349762306a36Sopenharmony_ci return -EINVAL; 349862306a36Sopenharmony_ci 349962306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_VQ_NOTF_COAL)) { 350062306a36Sopenharmony_ci ec->rx_coalesce_usecs = vi->rq[queue].intr_coal.max_usecs; 350162306a36Sopenharmony_ci ec->tx_coalesce_usecs = vi->sq[queue].intr_coal.max_usecs; 350262306a36Sopenharmony_ci ec->tx_max_coalesced_frames = vi->sq[queue].intr_coal.max_packets; 350362306a36Sopenharmony_ci ec->rx_max_coalesced_frames = vi->rq[queue].intr_coal.max_packets; 350462306a36Sopenharmony_ci } else { 350562306a36Sopenharmony_ci ec->rx_max_coalesced_frames = 1; 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ci if (vi->sq[queue].napi.weight) 350862306a36Sopenharmony_ci ec->tx_max_coalesced_frames = 1; 350962306a36Sopenharmony_ci } 351062306a36Sopenharmony_ci 351162306a36Sopenharmony_ci return 0; 351262306a36Sopenharmony_ci} 351362306a36Sopenharmony_ci 351462306a36Sopenharmony_cistatic void virtnet_init_settings(struct net_device *dev) 351562306a36Sopenharmony_ci{ 351662306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 351762306a36Sopenharmony_ci 351862306a36Sopenharmony_ci vi->speed = SPEED_UNKNOWN; 351962306a36Sopenharmony_ci vi->duplex = DUPLEX_UNKNOWN; 352062306a36Sopenharmony_ci} 352162306a36Sopenharmony_ci 352262306a36Sopenharmony_cistatic void virtnet_update_settings(struct virtnet_info *vi) 352362306a36Sopenharmony_ci{ 352462306a36Sopenharmony_ci u32 speed; 352562306a36Sopenharmony_ci u8 duplex; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_SPEED_DUPLEX)) 352862306a36Sopenharmony_ci return; 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_net_config, speed, &speed); 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci if (ethtool_validate_speed(speed)) 353362306a36Sopenharmony_ci vi->speed = speed; 353462306a36Sopenharmony_ci 353562306a36Sopenharmony_ci virtio_cread_le(vi->vdev, struct virtio_net_config, duplex, &duplex); 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ci if (ethtool_validate_duplex(duplex)) 353862306a36Sopenharmony_ci vi->duplex = duplex; 353962306a36Sopenharmony_ci} 354062306a36Sopenharmony_ci 354162306a36Sopenharmony_cistatic u32 virtnet_get_rxfh_key_size(struct net_device *dev) 354262306a36Sopenharmony_ci{ 354362306a36Sopenharmony_ci return ((struct virtnet_info *)netdev_priv(dev))->rss_key_size; 354462306a36Sopenharmony_ci} 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_cistatic u32 virtnet_get_rxfh_indir_size(struct net_device *dev) 354762306a36Sopenharmony_ci{ 354862306a36Sopenharmony_ci return ((struct virtnet_info *)netdev_priv(dev))->rss_indir_table_size; 354962306a36Sopenharmony_ci} 355062306a36Sopenharmony_ci 355162306a36Sopenharmony_cistatic int virtnet_get_rxfh(struct net_device *dev, u32 *indir, u8 *key, u8 *hfunc) 355262306a36Sopenharmony_ci{ 355362306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 355462306a36Sopenharmony_ci int i; 355562306a36Sopenharmony_ci 355662306a36Sopenharmony_ci if (indir) { 355762306a36Sopenharmony_ci for (i = 0; i < vi->rss_indir_table_size; ++i) 355862306a36Sopenharmony_ci indir[i] = vi->ctrl->rss.indirection_table[i]; 355962306a36Sopenharmony_ci } 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci if (key) 356262306a36Sopenharmony_ci memcpy(key, vi->ctrl->rss.key, vi->rss_key_size); 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_ci if (hfunc) 356562306a36Sopenharmony_ci *hfunc = ETH_RSS_HASH_TOP; 356662306a36Sopenharmony_ci 356762306a36Sopenharmony_ci return 0; 356862306a36Sopenharmony_ci} 356962306a36Sopenharmony_ci 357062306a36Sopenharmony_cistatic int virtnet_set_rxfh(struct net_device *dev, const u32 *indir, const u8 *key, const u8 hfunc) 357162306a36Sopenharmony_ci{ 357262306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 357362306a36Sopenharmony_ci int i; 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci if (hfunc != ETH_RSS_HASH_NO_CHANGE && hfunc != ETH_RSS_HASH_TOP) 357662306a36Sopenharmony_ci return -EOPNOTSUPP; 357762306a36Sopenharmony_ci 357862306a36Sopenharmony_ci if (indir) { 357962306a36Sopenharmony_ci for (i = 0; i < vi->rss_indir_table_size; ++i) 358062306a36Sopenharmony_ci vi->ctrl->rss.indirection_table[i] = indir[i]; 358162306a36Sopenharmony_ci } 358262306a36Sopenharmony_ci if (key) 358362306a36Sopenharmony_ci memcpy(vi->ctrl->rss.key, key, vi->rss_key_size); 358462306a36Sopenharmony_ci 358562306a36Sopenharmony_ci virtnet_commit_rss_command(vi); 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci return 0; 358862306a36Sopenharmony_ci} 358962306a36Sopenharmony_ci 359062306a36Sopenharmony_cistatic int virtnet_get_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info, u32 *rule_locs) 359162306a36Sopenharmony_ci{ 359262306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 359362306a36Sopenharmony_ci int rc = 0; 359462306a36Sopenharmony_ci 359562306a36Sopenharmony_ci switch (info->cmd) { 359662306a36Sopenharmony_ci case ETHTOOL_GRXRINGS: 359762306a36Sopenharmony_ci info->data = vi->curr_queue_pairs; 359862306a36Sopenharmony_ci break; 359962306a36Sopenharmony_ci case ETHTOOL_GRXFH: 360062306a36Sopenharmony_ci virtnet_get_hashflow(vi, info); 360162306a36Sopenharmony_ci break; 360262306a36Sopenharmony_ci default: 360362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 360462306a36Sopenharmony_ci } 360562306a36Sopenharmony_ci 360662306a36Sopenharmony_ci return rc; 360762306a36Sopenharmony_ci} 360862306a36Sopenharmony_ci 360962306a36Sopenharmony_cistatic int virtnet_set_rxnfc(struct net_device *dev, struct ethtool_rxnfc *info) 361062306a36Sopenharmony_ci{ 361162306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 361262306a36Sopenharmony_ci int rc = 0; 361362306a36Sopenharmony_ci 361462306a36Sopenharmony_ci switch (info->cmd) { 361562306a36Sopenharmony_ci case ETHTOOL_SRXFH: 361662306a36Sopenharmony_ci if (!virtnet_set_hashflow(vi, info)) 361762306a36Sopenharmony_ci rc = -EINVAL; 361862306a36Sopenharmony_ci 361962306a36Sopenharmony_ci break; 362062306a36Sopenharmony_ci default: 362162306a36Sopenharmony_ci rc = -EOPNOTSUPP; 362262306a36Sopenharmony_ci } 362362306a36Sopenharmony_ci 362462306a36Sopenharmony_ci return rc; 362562306a36Sopenharmony_ci} 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_cistatic const struct ethtool_ops virtnet_ethtool_ops = { 362862306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_MAX_FRAMES | 362962306a36Sopenharmony_ci ETHTOOL_COALESCE_USECS, 363062306a36Sopenharmony_ci .get_drvinfo = virtnet_get_drvinfo, 363162306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 363262306a36Sopenharmony_ci .get_ringparam = virtnet_get_ringparam, 363362306a36Sopenharmony_ci .set_ringparam = virtnet_set_ringparam, 363462306a36Sopenharmony_ci .get_strings = virtnet_get_strings, 363562306a36Sopenharmony_ci .get_sset_count = virtnet_get_sset_count, 363662306a36Sopenharmony_ci .get_ethtool_stats = virtnet_get_ethtool_stats, 363762306a36Sopenharmony_ci .set_channels = virtnet_set_channels, 363862306a36Sopenharmony_ci .get_channels = virtnet_get_channels, 363962306a36Sopenharmony_ci .get_ts_info = ethtool_op_get_ts_info, 364062306a36Sopenharmony_ci .get_link_ksettings = virtnet_get_link_ksettings, 364162306a36Sopenharmony_ci .set_link_ksettings = virtnet_set_link_ksettings, 364262306a36Sopenharmony_ci .set_coalesce = virtnet_set_coalesce, 364362306a36Sopenharmony_ci .get_coalesce = virtnet_get_coalesce, 364462306a36Sopenharmony_ci .set_per_queue_coalesce = virtnet_set_per_queue_coalesce, 364562306a36Sopenharmony_ci .get_per_queue_coalesce = virtnet_get_per_queue_coalesce, 364662306a36Sopenharmony_ci .get_rxfh_key_size = virtnet_get_rxfh_key_size, 364762306a36Sopenharmony_ci .get_rxfh_indir_size = virtnet_get_rxfh_indir_size, 364862306a36Sopenharmony_ci .get_rxfh = virtnet_get_rxfh, 364962306a36Sopenharmony_ci .set_rxfh = virtnet_set_rxfh, 365062306a36Sopenharmony_ci .get_rxnfc = virtnet_get_rxnfc, 365162306a36Sopenharmony_ci .set_rxnfc = virtnet_set_rxnfc, 365262306a36Sopenharmony_ci}; 365362306a36Sopenharmony_ci 365462306a36Sopenharmony_cistatic void virtnet_freeze_down(struct virtio_device *vdev) 365562306a36Sopenharmony_ci{ 365662306a36Sopenharmony_ci struct virtnet_info *vi = vdev->priv; 365762306a36Sopenharmony_ci 365862306a36Sopenharmony_ci /* Make sure no work handler is accessing the device */ 365962306a36Sopenharmony_ci flush_work(&vi->config_work); 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci netif_tx_lock_bh(vi->dev); 366262306a36Sopenharmony_ci netif_device_detach(vi->dev); 366362306a36Sopenharmony_ci netif_tx_unlock_bh(vi->dev); 366462306a36Sopenharmony_ci if (netif_running(vi->dev)) 366562306a36Sopenharmony_ci virtnet_close(vi->dev); 366662306a36Sopenharmony_ci} 366762306a36Sopenharmony_ci 366862306a36Sopenharmony_cistatic int init_vqs(struct virtnet_info *vi); 366962306a36Sopenharmony_ci 367062306a36Sopenharmony_cistatic int virtnet_restore_up(struct virtio_device *vdev) 367162306a36Sopenharmony_ci{ 367262306a36Sopenharmony_ci struct virtnet_info *vi = vdev->priv; 367362306a36Sopenharmony_ci int err; 367462306a36Sopenharmony_ci 367562306a36Sopenharmony_ci err = init_vqs(vi); 367662306a36Sopenharmony_ci if (err) 367762306a36Sopenharmony_ci return err; 367862306a36Sopenharmony_ci 367962306a36Sopenharmony_ci virtio_device_ready(vdev); 368062306a36Sopenharmony_ci 368162306a36Sopenharmony_ci enable_delayed_refill(vi); 368262306a36Sopenharmony_ci 368362306a36Sopenharmony_ci if (netif_running(vi->dev)) { 368462306a36Sopenharmony_ci err = virtnet_open(vi->dev); 368562306a36Sopenharmony_ci if (err) 368662306a36Sopenharmony_ci return err; 368762306a36Sopenharmony_ci } 368862306a36Sopenharmony_ci 368962306a36Sopenharmony_ci netif_tx_lock_bh(vi->dev); 369062306a36Sopenharmony_ci netif_device_attach(vi->dev); 369162306a36Sopenharmony_ci netif_tx_unlock_bh(vi->dev); 369262306a36Sopenharmony_ci return err; 369362306a36Sopenharmony_ci} 369462306a36Sopenharmony_ci 369562306a36Sopenharmony_cistatic int virtnet_set_guest_offloads(struct virtnet_info *vi, u64 offloads) 369662306a36Sopenharmony_ci{ 369762306a36Sopenharmony_ci struct scatterlist sg; 369862306a36Sopenharmony_ci vi->ctrl->offloads = cpu_to_virtio64(vi->vdev, offloads); 369962306a36Sopenharmony_ci 370062306a36Sopenharmony_ci sg_init_one(&sg, &vi->ctrl->offloads, sizeof(vi->ctrl->offloads)); 370162306a36Sopenharmony_ci 370262306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_GUEST_OFFLOADS, 370362306a36Sopenharmony_ci VIRTIO_NET_CTRL_GUEST_OFFLOADS_SET, &sg)) { 370462306a36Sopenharmony_ci dev_warn(&vi->dev->dev, "Fail to set guest offload.\n"); 370562306a36Sopenharmony_ci return -EINVAL; 370662306a36Sopenharmony_ci } 370762306a36Sopenharmony_ci 370862306a36Sopenharmony_ci return 0; 370962306a36Sopenharmony_ci} 371062306a36Sopenharmony_ci 371162306a36Sopenharmony_cistatic int virtnet_clear_guest_offloads(struct virtnet_info *vi) 371262306a36Sopenharmony_ci{ 371362306a36Sopenharmony_ci u64 offloads = 0; 371462306a36Sopenharmony_ci 371562306a36Sopenharmony_ci if (!vi->guest_offloads) 371662306a36Sopenharmony_ci return 0; 371762306a36Sopenharmony_ci 371862306a36Sopenharmony_ci return virtnet_set_guest_offloads(vi, offloads); 371962306a36Sopenharmony_ci} 372062306a36Sopenharmony_ci 372162306a36Sopenharmony_cistatic int virtnet_restore_guest_offloads(struct virtnet_info *vi) 372262306a36Sopenharmony_ci{ 372362306a36Sopenharmony_ci u64 offloads = vi->guest_offloads; 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci if (!vi->guest_offloads) 372662306a36Sopenharmony_ci return 0; 372762306a36Sopenharmony_ci 372862306a36Sopenharmony_ci return virtnet_set_guest_offloads(vi, offloads); 372962306a36Sopenharmony_ci} 373062306a36Sopenharmony_ci 373162306a36Sopenharmony_cistatic int virtnet_xdp_set(struct net_device *dev, struct bpf_prog *prog, 373262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 373362306a36Sopenharmony_ci{ 373462306a36Sopenharmony_ci unsigned int room = SKB_DATA_ALIGN(VIRTIO_XDP_HEADROOM + 373562306a36Sopenharmony_ci sizeof(struct skb_shared_info)); 373662306a36Sopenharmony_ci unsigned int max_sz = PAGE_SIZE - room - ETH_HLEN; 373762306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 373862306a36Sopenharmony_ci struct bpf_prog *old_prog; 373962306a36Sopenharmony_ci u16 xdp_qp = 0, curr_qp; 374062306a36Sopenharmony_ci int i, err; 374162306a36Sopenharmony_ci 374262306a36Sopenharmony_ci if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) 374362306a36Sopenharmony_ci && (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) || 374462306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || 374562306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || 374662306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) || 374762306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_CSUM) || 374862306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO4) || 374962306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO6))) { 375062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Can't set XDP while host is implementing GRO_HW/CSUM, disable GRO_HW/CSUM first"); 375162306a36Sopenharmony_ci return -EOPNOTSUPP; 375262306a36Sopenharmony_ci } 375362306a36Sopenharmony_ci 375462306a36Sopenharmony_ci if (vi->mergeable_rx_bufs && !vi->any_header_sg) { 375562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "XDP expects header/data in single page, any_header_sg required"); 375662306a36Sopenharmony_ci return -EINVAL; 375762306a36Sopenharmony_ci } 375862306a36Sopenharmony_ci 375962306a36Sopenharmony_ci if (prog && !prog->aux->xdp_has_frags && dev->mtu > max_sz) { 376062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "MTU too large to enable XDP without frags"); 376162306a36Sopenharmony_ci netdev_warn(dev, "single-buffer XDP requires MTU less than %u\n", max_sz); 376262306a36Sopenharmony_ci return -EINVAL; 376362306a36Sopenharmony_ci } 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci curr_qp = vi->curr_queue_pairs - vi->xdp_queue_pairs; 376662306a36Sopenharmony_ci if (prog) 376762306a36Sopenharmony_ci xdp_qp = nr_cpu_ids; 376862306a36Sopenharmony_ci 376962306a36Sopenharmony_ci /* XDP requires extra queues for XDP_TX */ 377062306a36Sopenharmony_ci if (curr_qp + xdp_qp > vi->max_queue_pairs) { 377162306a36Sopenharmony_ci netdev_warn_once(dev, "XDP request %i queues but max is %i. XDP_TX and XDP_REDIRECT will operate in a slower locked tx mode.\n", 377262306a36Sopenharmony_ci curr_qp + xdp_qp, vi->max_queue_pairs); 377362306a36Sopenharmony_ci xdp_qp = 0; 377462306a36Sopenharmony_ci } 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci old_prog = rtnl_dereference(vi->rq[0].xdp_prog); 377762306a36Sopenharmony_ci if (!prog && !old_prog) 377862306a36Sopenharmony_ci return 0; 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_ci if (prog) 378162306a36Sopenharmony_ci bpf_prog_add(prog, vi->max_queue_pairs - 1); 378262306a36Sopenharmony_ci 378362306a36Sopenharmony_ci /* Make sure NAPI is not using any XDP TX queues for RX. */ 378462306a36Sopenharmony_ci if (netif_running(dev)) { 378562306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 378662306a36Sopenharmony_ci napi_disable(&vi->rq[i].napi); 378762306a36Sopenharmony_ci virtnet_napi_tx_disable(&vi->sq[i].napi); 378862306a36Sopenharmony_ci } 378962306a36Sopenharmony_ci } 379062306a36Sopenharmony_ci 379162306a36Sopenharmony_ci if (!prog) { 379262306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 379362306a36Sopenharmony_ci rcu_assign_pointer(vi->rq[i].xdp_prog, prog); 379462306a36Sopenharmony_ci if (i == 0) 379562306a36Sopenharmony_ci virtnet_restore_guest_offloads(vi); 379662306a36Sopenharmony_ci } 379762306a36Sopenharmony_ci synchronize_net(); 379862306a36Sopenharmony_ci } 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci err = _virtnet_set_queues(vi, curr_qp + xdp_qp); 380162306a36Sopenharmony_ci if (err) 380262306a36Sopenharmony_ci goto err; 380362306a36Sopenharmony_ci netif_set_real_num_rx_queues(dev, curr_qp + xdp_qp); 380462306a36Sopenharmony_ci vi->xdp_queue_pairs = xdp_qp; 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci if (prog) { 380762306a36Sopenharmony_ci vi->xdp_enabled = true; 380862306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 380962306a36Sopenharmony_ci rcu_assign_pointer(vi->rq[i].xdp_prog, prog); 381062306a36Sopenharmony_ci if (i == 0 && !old_prog) 381162306a36Sopenharmony_ci virtnet_clear_guest_offloads(vi); 381262306a36Sopenharmony_ci } 381362306a36Sopenharmony_ci if (!old_prog) 381462306a36Sopenharmony_ci xdp_features_set_redirect_target(dev, true); 381562306a36Sopenharmony_ci } else { 381662306a36Sopenharmony_ci xdp_features_clear_redirect_target(dev); 381762306a36Sopenharmony_ci vi->xdp_enabled = false; 381862306a36Sopenharmony_ci } 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 382162306a36Sopenharmony_ci if (old_prog) 382262306a36Sopenharmony_ci bpf_prog_put(old_prog); 382362306a36Sopenharmony_ci if (netif_running(dev)) { 382462306a36Sopenharmony_ci virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); 382562306a36Sopenharmony_ci virtnet_napi_tx_enable(vi, vi->sq[i].vq, 382662306a36Sopenharmony_ci &vi->sq[i].napi); 382762306a36Sopenharmony_ci } 382862306a36Sopenharmony_ci } 382962306a36Sopenharmony_ci 383062306a36Sopenharmony_ci return 0; 383162306a36Sopenharmony_ci 383262306a36Sopenharmony_cierr: 383362306a36Sopenharmony_ci if (!prog) { 383462306a36Sopenharmony_ci virtnet_clear_guest_offloads(vi); 383562306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) 383662306a36Sopenharmony_ci rcu_assign_pointer(vi->rq[i].xdp_prog, old_prog); 383762306a36Sopenharmony_ci } 383862306a36Sopenharmony_ci 383962306a36Sopenharmony_ci if (netif_running(dev)) { 384062306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 384162306a36Sopenharmony_ci virtnet_napi_enable(vi->rq[i].vq, &vi->rq[i].napi); 384262306a36Sopenharmony_ci virtnet_napi_tx_enable(vi, vi->sq[i].vq, 384362306a36Sopenharmony_ci &vi->sq[i].napi); 384462306a36Sopenharmony_ci } 384562306a36Sopenharmony_ci } 384662306a36Sopenharmony_ci if (prog) 384762306a36Sopenharmony_ci bpf_prog_sub(prog, vi->max_queue_pairs - 1); 384862306a36Sopenharmony_ci return err; 384962306a36Sopenharmony_ci} 385062306a36Sopenharmony_ci 385162306a36Sopenharmony_cistatic int virtnet_xdp(struct net_device *dev, struct netdev_bpf *xdp) 385262306a36Sopenharmony_ci{ 385362306a36Sopenharmony_ci switch (xdp->command) { 385462306a36Sopenharmony_ci case XDP_SETUP_PROG: 385562306a36Sopenharmony_ci return virtnet_xdp_set(dev, xdp->prog, xdp->extack); 385662306a36Sopenharmony_ci default: 385762306a36Sopenharmony_ci return -EINVAL; 385862306a36Sopenharmony_ci } 385962306a36Sopenharmony_ci} 386062306a36Sopenharmony_ci 386162306a36Sopenharmony_cistatic int virtnet_get_phys_port_name(struct net_device *dev, char *buf, 386262306a36Sopenharmony_ci size_t len) 386362306a36Sopenharmony_ci{ 386462306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 386562306a36Sopenharmony_ci int ret; 386662306a36Sopenharmony_ci 386762306a36Sopenharmony_ci if (!virtio_has_feature(vi->vdev, VIRTIO_NET_F_STANDBY)) 386862306a36Sopenharmony_ci return -EOPNOTSUPP; 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_ci ret = snprintf(buf, len, "sby"); 387162306a36Sopenharmony_ci if (ret >= len) 387262306a36Sopenharmony_ci return -EOPNOTSUPP; 387362306a36Sopenharmony_ci 387462306a36Sopenharmony_ci return 0; 387562306a36Sopenharmony_ci} 387662306a36Sopenharmony_ci 387762306a36Sopenharmony_cistatic int virtnet_set_features(struct net_device *dev, 387862306a36Sopenharmony_ci netdev_features_t features) 387962306a36Sopenharmony_ci{ 388062306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(dev); 388162306a36Sopenharmony_ci u64 offloads; 388262306a36Sopenharmony_ci int err; 388362306a36Sopenharmony_ci 388462306a36Sopenharmony_ci if ((dev->features ^ features) & NETIF_F_GRO_HW) { 388562306a36Sopenharmony_ci if (vi->xdp_enabled) 388662306a36Sopenharmony_ci return -EBUSY; 388762306a36Sopenharmony_ci 388862306a36Sopenharmony_ci if (features & NETIF_F_GRO_HW) 388962306a36Sopenharmony_ci offloads = vi->guest_offloads_capable; 389062306a36Sopenharmony_ci else 389162306a36Sopenharmony_ci offloads = vi->guest_offloads_capable & 389262306a36Sopenharmony_ci ~GUEST_OFFLOAD_GRO_HW_MASK; 389362306a36Sopenharmony_ci 389462306a36Sopenharmony_ci err = virtnet_set_guest_offloads(vi, offloads); 389562306a36Sopenharmony_ci if (err) 389662306a36Sopenharmony_ci return err; 389762306a36Sopenharmony_ci vi->guest_offloads = offloads; 389862306a36Sopenharmony_ci } 389962306a36Sopenharmony_ci 390062306a36Sopenharmony_ci if ((dev->features ^ features) & NETIF_F_RXHASH) { 390162306a36Sopenharmony_ci if (features & NETIF_F_RXHASH) 390262306a36Sopenharmony_ci vi->ctrl->rss.hash_types = vi->rss_hash_types_saved; 390362306a36Sopenharmony_ci else 390462306a36Sopenharmony_ci vi->ctrl->rss.hash_types = VIRTIO_NET_HASH_REPORT_NONE; 390562306a36Sopenharmony_ci 390662306a36Sopenharmony_ci if (!virtnet_commit_rss_command(vi)) 390762306a36Sopenharmony_ci return -EINVAL; 390862306a36Sopenharmony_ci } 390962306a36Sopenharmony_ci 391062306a36Sopenharmony_ci return 0; 391162306a36Sopenharmony_ci} 391262306a36Sopenharmony_ci 391362306a36Sopenharmony_cistatic void virtnet_tx_timeout(struct net_device *dev, unsigned int txqueue) 391462306a36Sopenharmony_ci{ 391562306a36Sopenharmony_ci struct virtnet_info *priv = netdev_priv(dev); 391662306a36Sopenharmony_ci struct send_queue *sq = &priv->sq[txqueue]; 391762306a36Sopenharmony_ci struct netdev_queue *txq = netdev_get_tx_queue(dev, txqueue); 391862306a36Sopenharmony_ci 391962306a36Sopenharmony_ci u64_stats_update_begin(&sq->stats.syncp); 392062306a36Sopenharmony_ci u64_stats_inc(&sq->stats.tx_timeouts); 392162306a36Sopenharmony_ci u64_stats_update_end(&sq->stats.syncp); 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci netdev_err(dev, "TX timeout on queue: %u, sq: %s, vq: 0x%x, name: %s, %u usecs ago\n", 392462306a36Sopenharmony_ci txqueue, sq->name, sq->vq->index, sq->vq->name, 392562306a36Sopenharmony_ci jiffies_to_usecs(jiffies - READ_ONCE(txq->trans_start))); 392662306a36Sopenharmony_ci} 392762306a36Sopenharmony_ci 392862306a36Sopenharmony_cistatic const struct net_device_ops virtnet_netdev = { 392962306a36Sopenharmony_ci .ndo_open = virtnet_open, 393062306a36Sopenharmony_ci .ndo_stop = virtnet_close, 393162306a36Sopenharmony_ci .ndo_start_xmit = start_xmit, 393262306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 393362306a36Sopenharmony_ci .ndo_set_mac_address = virtnet_set_mac_address, 393462306a36Sopenharmony_ci .ndo_set_rx_mode = virtnet_set_rx_mode, 393562306a36Sopenharmony_ci .ndo_get_stats64 = virtnet_stats, 393662306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = virtnet_vlan_rx_add_vid, 393762306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = virtnet_vlan_rx_kill_vid, 393862306a36Sopenharmony_ci .ndo_bpf = virtnet_xdp, 393962306a36Sopenharmony_ci .ndo_xdp_xmit = virtnet_xdp_xmit, 394062306a36Sopenharmony_ci .ndo_features_check = passthru_features_check, 394162306a36Sopenharmony_ci .ndo_get_phys_port_name = virtnet_get_phys_port_name, 394262306a36Sopenharmony_ci .ndo_set_features = virtnet_set_features, 394362306a36Sopenharmony_ci .ndo_tx_timeout = virtnet_tx_timeout, 394462306a36Sopenharmony_ci}; 394562306a36Sopenharmony_ci 394662306a36Sopenharmony_cistatic void virtnet_config_changed_work(struct work_struct *work) 394762306a36Sopenharmony_ci{ 394862306a36Sopenharmony_ci struct virtnet_info *vi = 394962306a36Sopenharmony_ci container_of(work, struct virtnet_info, config_work); 395062306a36Sopenharmony_ci u16 v; 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci if (virtio_cread_feature(vi->vdev, VIRTIO_NET_F_STATUS, 395362306a36Sopenharmony_ci struct virtio_net_config, status, &v) < 0) 395462306a36Sopenharmony_ci return; 395562306a36Sopenharmony_ci 395662306a36Sopenharmony_ci if (v & VIRTIO_NET_S_ANNOUNCE) { 395762306a36Sopenharmony_ci netdev_notify_peers(vi->dev); 395862306a36Sopenharmony_ci virtnet_ack_link_announce(vi); 395962306a36Sopenharmony_ci } 396062306a36Sopenharmony_ci 396162306a36Sopenharmony_ci /* Ignore unknown (future) status bits */ 396262306a36Sopenharmony_ci v &= VIRTIO_NET_S_LINK_UP; 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci if (vi->status == v) 396562306a36Sopenharmony_ci return; 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_ci vi->status = v; 396862306a36Sopenharmony_ci 396962306a36Sopenharmony_ci if (vi->status & VIRTIO_NET_S_LINK_UP) { 397062306a36Sopenharmony_ci virtnet_update_settings(vi); 397162306a36Sopenharmony_ci netif_carrier_on(vi->dev); 397262306a36Sopenharmony_ci netif_tx_wake_all_queues(vi->dev); 397362306a36Sopenharmony_ci } else { 397462306a36Sopenharmony_ci netif_carrier_off(vi->dev); 397562306a36Sopenharmony_ci netif_tx_stop_all_queues(vi->dev); 397662306a36Sopenharmony_ci } 397762306a36Sopenharmony_ci} 397862306a36Sopenharmony_ci 397962306a36Sopenharmony_cistatic void virtnet_config_changed(struct virtio_device *vdev) 398062306a36Sopenharmony_ci{ 398162306a36Sopenharmony_ci struct virtnet_info *vi = vdev->priv; 398262306a36Sopenharmony_ci 398362306a36Sopenharmony_ci schedule_work(&vi->config_work); 398462306a36Sopenharmony_ci} 398562306a36Sopenharmony_ci 398662306a36Sopenharmony_cistatic void virtnet_free_queues(struct virtnet_info *vi) 398762306a36Sopenharmony_ci{ 398862306a36Sopenharmony_ci int i; 398962306a36Sopenharmony_ci 399062306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 399162306a36Sopenharmony_ci __netif_napi_del(&vi->rq[i].napi); 399262306a36Sopenharmony_ci __netif_napi_del(&vi->sq[i].napi); 399362306a36Sopenharmony_ci } 399462306a36Sopenharmony_ci 399562306a36Sopenharmony_ci /* We called __netif_napi_del(), 399662306a36Sopenharmony_ci * we need to respect an RCU grace period before freeing vi->rq 399762306a36Sopenharmony_ci */ 399862306a36Sopenharmony_ci synchronize_net(); 399962306a36Sopenharmony_ci 400062306a36Sopenharmony_ci kfree(vi->rq); 400162306a36Sopenharmony_ci kfree(vi->sq); 400262306a36Sopenharmony_ci kfree(vi->ctrl); 400362306a36Sopenharmony_ci} 400462306a36Sopenharmony_ci 400562306a36Sopenharmony_cistatic void _free_receive_bufs(struct virtnet_info *vi) 400662306a36Sopenharmony_ci{ 400762306a36Sopenharmony_ci struct bpf_prog *old_prog; 400862306a36Sopenharmony_ci int i; 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 401162306a36Sopenharmony_ci while (vi->rq[i].pages) 401262306a36Sopenharmony_ci __free_pages(get_a_page(&vi->rq[i], GFP_KERNEL), 0); 401362306a36Sopenharmony_ci 401462306a36Sopenharmony_ci old_prog = rtnl_dereference(vi->rq[i].xdp_prog); 401562306a36Sopenharmony_ci RCU_INIT_POINTER(vi->rq[i].xdp_prog, NULL); 401662306a36Sopenharmony_ci if (old_prog) 401762306a36Sopenharmony_ci bpf_prog_put(old_prog); 401862306a36Sopenharmony_ci } 401962306a36Sopenharmony_ci} 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_cistatic void free_receive_bufs(struct virtnet_info *vi) 402262306a36Sopenharmony_ci{ 402362306a36Sopenharmony_ci rtnl_lock(); 402462306a36Sopenharmony_ci _free_receive_bufs(vi); 402562306a36Sopenharmony_ci rtnl_unlock(); 402662306a36Sopenharmony_ci} 402762306a36Sopenharmony_ci 402862306a36Sopenharmony_cistatic void free_receive_page_frags(struct virtnet_info *vi) 402962306a36Sopenharmony_ci{ 403062306a36Sopenharmony_ci int i; 403162306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) 403262306a36Sopenharmony_ci if (vi->rq[i].alloc_frag.page) { 403362306a36Sopenharmony_ci if (vi->rq[i].do_dma && vi->rq[i].last_dma) 403462306a36Sopenharmony_ci virtnet_rq_unmap(&vi->rq[i], vi->rq[i].last_dma, 0); 403562306a36Sopenharmony_ci put_page(vi->rq[i].alloc_frag.page); 403662306a36Sopenharmony_ci } 403762306a36Sopenharmony_ci} 403862306a36Sopenharmony_ci 403962306a36Sopenharmony_cistatic void virtnet_sq_free_unused_buf(struct virtqueue *vq, void *buf) 404062306a36Sopenharmony_ci{ 404162306a36Sopenharmony_ci if (!is_xdp_frame(buf)) 404262306a36Sopenharmony_ci dev_kfree_skb(buf); 404362306a36Sopenharmony_ci else 404462306a36Sopenharmony_ci xdp_return_frame(ptr_to_xdp(buf)); 404562306a36Sopenharmony_ci} 404662306a36Sopenharmony_ci 404762306a36Sopenharmony_cistatic void free_unused_bufs(struct virtnet_info *vi) 404862306a36Sopenharmony_ci{ 404962306a36Sopenharmony_ci void *buf; 405062306a36Sopenharmony_ci int i; 405162306a36Sopenharmony_ci 405262306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 405362306a36Sopenharmony_ci struct virtqueue *vq = vi->sq[i].vq; 405462306a36Sopenharmony_ci while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) 405562306a36Sopenharmony_ci virtnet_sq_free_unused_buf(vq, buf); 405662306a36Sopenharmony_ci cond_resched(); 405762306a36Sopenharmony_ci } 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 406062306a36Sopenharmony_ci struct virtqueue *vq = vi->rq[i].vq; 406162306a36Sopenharmony_ci 406262306a36Sopenharmony_ci while ((buf = virtqueue_detach_unused_buf(vq)) != NULL) 406362306a36Sopenharmony_ci virtnet_rq_unmap_free_buf(vq, buf); 406462306a36Sopenharmony_ci cond_resched(); 406562306a36Sopenharmony_ci } 406662306a36Sopenharmony_ci} 406762306a36Sopenharmony_ci 406862306a36Sopenharmony_cistatic void virtnet_del_vqs(struct virtnet_info *vi) 406962306a36Sopenharmony_ci{ 407062306a36Sopenharmony_ci struct virtio_device *vdev = vi->vdev; 407162306a36Sopenharmony_ci 407262306a36Sopenharmony_ci virtnet_clean_affinity(vi); 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci vdev->config->del_vqs(vdev); 407562306a36Sopenharmony_ci 407662306a36Sopenharmony_ci virtnet_free_queues(vi); 407762306a36Sopenharmony_ci} 407862306a36Sopenharmony_ci 407962306a36Sopenharmony_ci/* How large should a single buffer be so a queue full of these can fit at 408062306a36Sopenharmony_ci * least one full packet? 408162306a36Sopenharmony_ci * Logic below assumes the mergeable buffer header is used. 408262306a36Sopenharmony_ci */ 408362306a36Sopenharmony_cistatic unsigned int mergeable_min_buf_len(struct virtnet_info *vi, struct virtqueue *vq) 408462306a36Sopenharmony_ci{ 408562306a36Sopenharmony_ci const unsigned int hdr_len = vi->hdr_len; 408662306a36Sopenharmony_ci unsigned int rq_size = virtqueue_get_vring_size(vq); 408762306a36Sopenharmony_ci unsigned int packet_len = vi->big_packets ? IP_MAX_MTU : vi->dev->max_mtu; 408862306a36Sopenharmony_ci unsigned int buf_len = hdr_len + ETH_HLEN + VLAN_HLEN + packet_len; 408962306a36Sopenharmony_ci unsigned int min_buf_len = DIV_ROUND_UP(buf_len, rq_size); 409062306a36Sopenharmony_ci 409162306a36Sopenharmony_ci return max(max(min_buf_len, hdr_len) - hdr_len, 409262306a36Sopenharmony_ci (unsigned int)GOOD_PACKET_LEN); 409362306a36Sopenharmony_ci} 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_cistatic int virtnet_find_vqs(struct virtnet_info *vi) 409662306a36Sopenharmony_ci{ 409762306a36Sopenharmony_ci vq_callback_t **callbacks; 409862306a36Sopenharmony_ci struct virtqueue **vqs; 409962306a36Sopenharmony_ci const char **names; 410062306a36Sopenharmony_ci int ret = -ENOMEM; 410162306a36Sopenharmony_ci int total_vqs; 410262306a36Sopenharmony_ci bool *ctx; 410362306a36Sopenharmony_ci u16 i; 410462306a36Sopenharmony_ci 410562306a36Sopenharmony_ci /* We expect 1 RX virtqueue followed by 1 TX virtqueue, followed by 410662306a36Sopenharmony_ci * possible N-1 RX/TX queue pairs used in multiqueue mode, followed by 410762306a36Sopenharmony_ci * possible control vq. 410862306a36Sopenharmony_ci */ 410962306a36Sopenharmony_ci total_vqs = vi->max_queue_pairs * 2 + 411062306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VQ); 411162306a36Sopenharmony_ci 411262306a36Sopenharmony_ci /* Allocate space for find_vqs parameters */ 411362306a36Sopenharmony_ci vqs = kcalloc(total_vqs, sizeof(*vqs), GFP_KERNEL); 411462306a36Sopenharmony_ci if (!vqs) 411562306a36Sopenharmony_ci goto err_vq; 411662306a36Sopenharmony_ci callbacks = kmalloc_array(total_vqs, sizeof(*callbacks), GFP_KERNEL); 411762306a36Sopenharmony_ci if (!callbacks) 411862306a36Sopenharmony_ci goto err_callback; 411962306a36Sopenharmony_ci names = kmalloc_array(total_vqs, sizeof(*names), GFP_KERNEL); 412062306a36Sopenharmony_ci if (!names) 412162306a36Sopenharmony_ci goto err_names; 412262306a36Sopenharmony_ci if (!vi->big_packets || vi->mergeable_rx_bufs) { 412362306a36Sopenharmony_ci ctx = kcalloc(total_vqs, sizeof(*ctx), GFP_KERNEL); 412462306a36Sopenharmony_ci if (!ctx) 412562306a36Sopenharmony_ci goto err_ctx; 412662306a36Sopenharmony_ci } else { 412762306a36Sopenharmony_ci ctx = NULL; 412862306a36Sopenharmony_ci } 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_ci /* Parameters for control virtqueue, if any */ 413162306a36Sopenharmony_ci if (vi->has_cvq) { 413262306a36Sopenharmony_ci callbacks[total_vqs - 1] = NULL; 413362306a36Sopenharmony_ci names[total_vqs - 1] = "control"; 413462306a36Sopenharmony_ci } 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci /* Allocate/initialize parameters for send/receive virtqueues */ 413762306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 413862306a36Sopenharmony_ci callbacks[rxq2vq(i)] = skb_recv_done; 413962306a36Sopenharmony_ci callbacks[txq2vq(i)] = skb_xmit_done; 414062306a36Sopenharmony_ci sprintf(vi->rq[i].name, "input.%u", i); 414162306a36Sopenharmony_ci sprintf(vi->sq[i].name, "output.%u", i); 414262306a36Sopenharmony_ci names[rxq2vq(i)] = vi->rq[i].name; 414362306a36Sopenharmony_ci names[txq2vq(i)] = vi->sq[i].name; 414462306a36Sopenharmony_ci if (ctx) 414562306a36Sopenharmony_ci ctx[rxq2vq(i)] = true; 414662306a36Sopenharmony_ci } 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_ci ret = virtio_find_vqs_ctx(vi->vdev, total_vqs, vqs, callbacks, 414962306a36Sopenharmony_ci names, ctx, NULL); 415062306a36Sopenharmony_ci if (ret) 415162306a36Sopenharmony_ci goto err_find; 415262306a36Sopenharmony_ci 415362306a36Sopenharmony_ci if (vi->has_cvq) { 415462306a36Sopenharmony_ci vi->cvq = vqs[total_vqs - 1]; 415562306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_VLAN)) 415662306a36Sopenharmony_ci vi->dev->features |= NETIF_F_HW_VLAN_CTAG_FILTER; 415762306a36Sopenharmony_ci } 415862306a36Sopenharmony_ci 415962306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 416062306a36Sopenharmony_ci vi->rq[i].vq = vqs[rxq2vq(i)]; 416162306a36Sopenharmony_ci vi->rq[i].min_buf_len = mergeable_min_buf_len(vi, vi->rq[i].vq); 416262306a36Sopenharmony_ci vi->sq[i].vq = vqs[txq2vq(i)]; 416362306a36Sopenharmony_ci } 416462306a36Sopenharmony_ci 416562306a36Sopenharmony_ci /* run here: ret == 0. */ 416662306a36Sopenharmony_ci 416762306a36Sopenharmony_ci 416862306a36Sopenharmony_cierr_find: 416962306a36Sopenharmony_ci kfree(ctx); 417062306a36Sopenharmony_cierr_ctx: 417162306a36Sopenharmony_ci kfree(names); 417262306a36Sopenharmony_cierr_names: 417362306a36Sopenharmony_ci kfree(callbacks); 417462306a36Sopenharmony_cierr_callback: 417562306a36Sopenharmony_ci kfree(vqs); 417662306a36Sopenharmony_cierr_vq: 417762306a36Sopenharmony_ci return ret; 417862306a36Sopenharmony_ci} 417962306a36Sopenharmony_ci 418062306a36Sopenharmony_cistatic int virtnet_alloc_queues(struct virtnet_info *vi) 418162306a36Sopenharmony_ci{ 418262306a36Sopenharmony_ci int i; 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_ci if (vi->has_cvq) { 418562306a36Sopenharmony_ci vi->ctrl = kzalloc(sizeof(*vi->ctrl), GFP_KERNEL); 418662306a36Sopenharmony_ci if (!vi->ctrl) 418762306a36Sopenharmony_ci goto err_ctrl; 418862306a36Sopenharmony_ci } else { 418962306a36Sopenharmony_ci vi->ctrl = NULL; 419062306a36Sopenharmony_ci } 419162306a36Sopenharmony_ci vi->sq = kcalloc(vi->max_queue_pairs, sizeof(*vi->sq), GFP_KERNEL); 419262306a36Sopenharmony_ci if (!vi->sq) 419362306a36Sopenharmony_ci goto err_sq; 419462306a36Sopenharmony_ci vi->rq = kcalloc(vi->max_queue_pairs, sizeof(*vi->rq), GFP_KERNEL); 419562306a36Sopenharmony_ci if (!vi->rq) 419662306a36Sopenharmony_ci goto err_rq; 419762306a36Sopenharmony_ci 419862306a36Sopenharmony_ci INIT_DELAYED_WORK(&vi->refill, refill_work); 419962306a36Sopenharmony_ci for (i = 0; i < vi->max_queue_pairs; i++) { 420062306a36Sopenharmony_ci vi->rq[i].pages = NULL; 420162306a36Sopenharmony_ci netif_napi_add_weight(vi->dev, &vi->rq[i].napi, virtnet_poll, 420262306a36Sopenharmony_ci napi_weight); 420362306a36Sopenharmony_ci netif_napi_add_tx_weight(vi->dev, &vi->sq[i].napi, 420462306a36Sopenharmony_ci virtnet_poll_tx, 420562306a36Sopenharmony_ci napi_tx ? napi_weight : 0); 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_ci sg_init_table(vi->rq[i].sg, ARRAY_SIZE(vi->rq[i].sg)); 420862306a36Sopenharmony_ci ewma_pkt_len_init(&vi->rq[i].mrg_avg_pkt_len); 420962306a36Sopenharmony_ci sg_init_table(vi->sq[i].sg, ARRAY_SIZE(vi->sq[i].sg)); 421062306a36Sopenharmony_ci 421162306a36Sopenharmony_ci u64_stats_init(&vi->rq[i].stats.syncp); 421262306a36Sopenharmony_ci u64_stats_init(&vi->sq[i].stats.syncp); 421362306a36Sopenharmony_ci } 421462306a36Sopenharmony_ci 421562306a36Sopenharmony_ci return 0; 421662306a36Sopenharmony_ci 421762306a36Sopenharmony_cierr_rq: 421862306a36Sopenharmony_ci kfree(vi->sq); 421962306a36Sopenharmony_cierr_sq: 422062306a36Sopenharmony_ci kfree(vi->ctrl); 422162306a36Sopenharmony_cierr_ctrl: 422262306a36Sopenharmony_ci return -ENOMEM; 422362306a36Sopenharmony_ci} 422462306a36Sopenharmony_ci 422562306a36Sopenharmony_cistatic int init_vqs(struct virtnet_info *vi) 422662306a36Sopenharmony_ci{ 422762306a36Sopenharmony_ci int ret; 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci /* Allocate send & receive queues */ 423062306a36Sopenharmony_ci ret = virtnet_alloc_queues(vi); 423162306a36Sopenharmony_ci if (ret) 423262306a36Sopenharmony_ci goto err; 423362306a36Sopenharmony_ci 423462306a36Sopenharmony_ci ret = virtnet_find_vqs(vi); 423562306a36Sopenharmony_ci if (ret) 423662306a36Sopenharmony_ci goto err_free; 423762306a36Sopenharmony_ci 423862306a36Sopenharmony_ci virtnet_rq_set_premapped(vi); 423962306a36Sopenharmony_ci 424062306a36Sopenharmony_ci cpus_read_lock(); 424162306a36Sopenharmony_ci virtnet_set_affinity(vi); 424262306a36Sopenharmony_ci cpus_read_unlock(); 424362306a36Sopenharmony_ci 424462306a36Sopenharmony_ci return 0; 424562306a36Sopenharmony_ci 424662306a36Sopenharmony_cierr_free: 424762306a36Sopenharmony_ci virtnet_free_queues(vi); 424862306a36Sopenharmony_cierr: 424962306a36Sopenharmony_ci return ret; 425062306a36Sopenharmony_ci} 425162306a36Sopenharmony_ci 425262306a36Sopenharmony_ci#ifdef CONFIG_SYSFS 425362306a36Sopenharmony_cistatic ssize_t mergeable_rx_buffer_size_show(struct netdev_rx_queue *queue, 425462306a36Sopenharmony_ci char *buf) 425562306a36Sopenharmony_ci{ 425662306a36Sopenharmony_ci struct virtnet_info *vi = netdev_priv(queue->dev); 425762306a36Sopenharmony_ci unsigned int queue_index = get_netdev_rx_queue_index(queue); 425862306a36Sopenharmony_ci unsigned int headroom = virtnet_get_headroom(vi); 425962306a36Sopenharmony_ci unsigned int tailroom = headroom ? sizeof(struct skb_shared_info) : 0; 426062306a36Sopenharmony_ci struct ewma_pkt_len *avg; 426162306a36Sopenharmony_ci 426262306a36Sopenharmony_ci BUG_ON(queue_index >= vi->max_queue_pairs); 426362306a36Sopenharmony_ci avg = &vi->rq[queue_index].mrg_avg_pkt_len; 426462306a36Sopenharmony_ci return sprintf(buf, "%u\n", 426562306a36Sopenharmony_ci get_mergeable_buf_len(&vi->rq[queue_index], avg, 426662306a36Sopenharmony_ci SKB_DATA_ALIGN(headroom + tailroom))); 426762306a36Sopenharmony_ci} 426862306a36Sopenharmony_ci 426962306a36Sopenharmony_cistatic struct rx_queue_attribute mergeable_rx_buffer_size_attribute = 427062306a36Sopenharmony_ci __ATTR_RO(mergeable_rx_buffer_size); 427162306a36Sopenharmony_ci 427262306a36Sopenharmony_cistatic struct attribute *virtio_net_mrg_rx_attrs[] = { 427362306a36Sopenharmony_ci &mergeable_rx_buffer_size_attribute.attr, 427462306a36Sopenharmony_ci NULL 427562306a36Sopenharmony_ci}; 427662306a36Sopenharmony_ci 427762306a36Sopenharmony_cistatic const struct attribute_group virtio_net_mrg_rx_group = { 427862306a36Sopenharmony_ci .name = "virtio_net", 427962306a36Sopenharmony_ci .attrs = virtio_net_mrg_rx_attrs 428062306a36Sopenharmony_ci}; 428162306a36Sopenharmony_ci#endif 428262306a36Sopenharmony_ci 428362306a36Sopenharmony_cistatic bool virtnet_fail_on_feature(struct virtio_device *vdev, 428462306a36Sopenharmony_ci unsigned int fbit, 428562306a36Sopenharmony_ci const char *fname, const char *dname) 428662306a36Sopenharmony_ci{ 428762306a36Sopenharmony_ci if (!virtio_has_feature(vdev, fbit)) 428862306a36Sopenharmony_ci return false; 428962306a36Sopenharmony_ci 429062306a36Sopenharmony_ci dev_err(&vdev->dev, "device advertises feature %s but not %s", 429162306a36Sopenharmony_ci fname, dname); 429262306a36Sopenharmony_ci 429362306a36Sopenharmony_ci return true; 429462306a36Sopenharmony_ci} 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci#define VIRTNET_FAIL_ON(vdev, fbit, dbit) \ 429762306a36Sopenharmony_ci virtnet_fail_on_feature(vdev, fbit, #fbit, dbit) 429862306a36Sopenharmony_ci 429962306a36Sopenharmony_cistatic bool virtnet_validate_features(struct virtio_device *vdev) 430062306a36Sopenharmony_ci{ 430162306a36Sopenharmony_ci if (!virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ) && 430262306a36Sopenharmony_ci (VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_RX, 430362306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ") || 430462306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_VLAN, 430562306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ") || 430662306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_GUEST_ANNOUNCE, 430762306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ") || 430862306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_MQ, "VIRTIO_NET_F_CTRL_VQ") || 430962306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_CTRL_MAC_ADDR, 431062306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ") || 431162306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_RSS, 431262306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ") || 431362306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_HASH_REPORT, 431462306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ") || 431562306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_NOTF_COAL, 431662306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ") || 431762306a36Sopenharmony_ci VIRTNET_FAIL_ON(vdev, VIRTIO_NET_F_VQ_NOTF_COAL, 431862306a36Sopenharmony_ci "VIRTIO_NET_F_CTRL_VQ"))) { 431962306a36Sopenharmony_ci return false; 432062306a36Sopenharmony_ci } 432162306a36Sopenharmony_ci 432262306a36Sopenharmony_ci return true; 432362306a36Sopenharmony_ci} 432462306a36Sopenharmony_ci 432562306a36Sopenharmony_ci#define MIN_MTU ETH_MIN_MTU 432662306a36Sopenharmony_ci#define MAX_MTU ETH_MAX_MTU 432762306a36Sopenharmony_ci 432862306a36Sopenharmony_cistatic int virtnet_validate(struct virtio_device *vdev) 432962306a36Sopenharmony_ci{ 433062306a36Sopenharmony_ci if (!vdev->config->get) { 433162306a36Sopenharmony_ci dev_err(&vdev->dev, "%s failure: config access disabled\n", 433262306a36Sopenharmony_ci __func__); 433362306a36Sopenharmony_ci return -EINVAL; 433462306a36Sopenharmony_ci } 433562306a36Sopenharmony_ci 433662306a36Sopenharmony_ci if (!virtnet_validate_features(vdev)) 433762306a36Sopenharmony_ci return -EINVAL; 433862306a36Sopenharmony_ci 433962306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_MTU)) { 434062306a36Sopenharmony_ci int mtu = virtio_cread16(vdev, 434162306a36Sopenharmony_ci offsetof(struct virtio_net_config, 434262306a36Sopenharmony_ci mtu)); 434362306a36Sopenharmony_ci if (mtu < MIN_MTU) 434462306a36Sopenharmony_ci __virtio_clear_bit(vdev, VIRTIO_NET_F_MTU); 434562306a36Sopenharmony_ci } 434662306a36Sopenharmony_ci 434762306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_STANDBY) && 434862306a36Sopenharmony_ci !virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) { 434962306a36Sopenharmony_ci dev_warn(&vdev->dev, "device advertises feature VIRTIO_NET_F_STANDBY but not VIRTIO_NET_F_MAC, disabling standby"); 435062306a36Sopenharmony_ci __virtio_clear_bit(vdev, VIRTIO_NET_F_STANDBY); 435162306a36Sopenharmony_ci } 435262306a36Sopenharmony_ci 435362306a36Sopenharmony_ci return 0; 435462306a36Sopenharmony_ci} 435562306a36Sopenharmony_ci 435662306a36Sopenharmony_cistatic bool virtnet_check_guest_gso(const struct virtnet_info *vi) 435762306a36Sopenharmony_ci{ 435862306a36Sopenharmony_ci return virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO4) || 435962306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_TSO6) || 436062306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_ECN) || 436162306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_UFO) || 436262306a36Sopenharmony_ci (virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO4) && 436362306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_GUEST_USO6)); 436462306a36Sopenharmony_ci} 436562306a36Sopenharmony_ci 436662306a36Sopenharmony_cistatic void virtnet_set_big_packets(struct virtnet_info *vi, const int mtu) 436762306a36Sopenharmony_ci{ 436862306a36Sopenharmony_ci bool guest_gso = virtnet_check_guest_gso(vi); 436962306a36Sopenharmony_ci 437062306a36Sopenharmony_ci /* If device can receive ANY guest GSO packets, regardless of mtu, 437162306a36Sopenharmony_ci * allocate packets of maximum size, otherwise limit it to only 437262306a36Sopenharmony_ci * mtu size worth only. 437362306a36Sopenharmony_ci */ 437462306a36Sopenharmony_ci if (mtu > ETH_DATA_LEN || guest_gso) { 437562306a36Sopenharmony_ci vi->big_packets = true; 437662306a36Sopenharmony_ci vi->big_packets_num_skbfrags = guest_gso ? MAX_SKB_FRAGS : DIV_ROUND_UP(mtu, PAGE_SIZE); 437762306a36Sopenharmony_ci } 437862306a36Sopenharmony_ci} 437962306a36Sopenharmony_ci 438062306a36Sopenharmony_cistatic int virtnet_probe(struct virtio_device *vdev) 438162306a36Sopenharmony_ci{ 438262306a36Sopenharmony_ci int i, err = -ENOMEM; 438362306a36Sopenharmony_ci struct net_device *dev; 438462306a36Sopenharmony_ci struct virtnet_info *vi; 438562306a36Sopenharmony_ci u16 max_queue_pairs; 438662306a36Sopenharmony_ci int mtu = 0; 438762306a36Sopenharmony_ci 438862306a36Sopenharmony_ci /* Find if host supports multiqueue/rss virtio_net device */ 438962306a36Sopenharmony_ci max_queue_pairs = 1; 439062306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_MQ) || virtio_has_feature(vdev, VIRTIO_NET_F_RSS)) 439162306a36Sopenharmony_ci max_queue_pairs = 439262306a36Sopenharmony_ci virtio_cread16(vdev, offsetof(struct virtio_net_config, max_virtqueue_pairs)); 439362306a36Sopenharmony_ci 439462306a36Sopenharmony_ci /* We need at least 2 queue's */ 439562306a36Sopenharmony_ci if (max_queue_pairs < VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MIN || 439662306a36Sopenharmony_ci max_queue_pairs > VIRTIO_NET_CTRL_MQ_VQ_PAIRS_MAX || 439762306a36Sopenharmony_ci !virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) 439862306a36Sopenharmony_ci max_queue_pairs = 1; 439962306a36Sopenharmony_ci 440062306a36Sopenharmony_ci /* Allocate ourselves a network device with room for our info */ 440162306a36Sopenharmony_ci dev = alloc_etherdev_mq(sizeof(struct virtnet_info), max_queue_pairs); 440262306a36Sopenharmony_ci if (!dev) 440362306a36Sopenharmony_ci return -ENOMEM; 440462306a36Sopenharmony_ci 440562306a36Sopenharmony_ci /* Set up network device as normal. */ 440662306a36Sopenharmony_ci dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE | 440762306a36Sopenharmony_ci IFF_TX_SKB_NO_LINEAR; 440862306a36Sopenharmony_ci dev->netdev_ops = &virtnet_netdev; 440962306a36Sopenharmony_ci dev->features = NETIF_F_HIGHDMA; 441062306a36Sopenharmony_ci 441162306a36Sopenharmony_ci dev->ethtool_ops = &virtnet_ethtool_ops; 441262306a36Sopenharmony_ci SET_NETDEV_DEV(dev, &vdev->dev); 441362306a36Sopenharmony_ci 441462306a36Sopenharmony_ci /* Do we support "hardware" checksums? */ 441562306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) { 441662306a36Sopenharmony_ci /* This opens up the world of extra features. */ 441762306a36Sopenharmony_ci dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_SG; 441862306a36Sopenharmony_ci if (csum) 441962306a36Sopenharmony_ci dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; 442062306a36Sopenharmony_ci 442162306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { 442262306a36Sopenharmony_ci dev->hw_features |= NETIF_F_TSO 442362306a36Sopenharmony_ci | NETIF_F_TSO_ECN | NETIF_F_TSO6; 442462306a36Sopenharmony_ci } 442562306a36Sopenharmony_ci /* Individual feature bits: what can host handle? */ 442662306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO4)) 442762306a36Sopenharmony_ci dev->hw_features |= NETIF_F_TSO; 442862306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_TSO6)) 442962306a36Sopenharmony_ci dev->hw_features |= NETIF_F_TSO6; 443062306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_ECN)) 443162306a36Sopenharmony_ci dev->hw_features |= NETIF_F_TSO_ECN; 443262306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_HOST_USO)) 443362306a36Sopenharmony_ci dev->hw_features |= NETIF_F_GSO_UDP_L4; 443462306a36Sopenharmony_ci 443562306a36Sopenharmony_ci dev->features |= NETIF_F_GSO_ROBUST; 443662306a36Sopenharmony_ci 443762306a36Sopenharmony_ci if (gso) 443862306a36Sopenharmony_ci dev->features |= dev->hw_features & NETIF_F_ALL_TSO; 443962306a36Sopenharmony_ci /* (!csum && gso) case will be fixed by register_netdev() */ 444062306a36Sopenharmony_ci } 444162306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_CSUM)) 444262306a36Sopenharmony_ci dev->features |= NETIF_F_RXCSUM; 444362306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO4) || 444462306a36Sopenharmony_ci virtio_has_feature(vdev, VIRTIO_NET_F_GUEST_TSO6)) 444562306a36Sopenharmony_ci dev->features |= NETIF_F_GRO_HW; 444662306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS)) 444762306a36Sopenharmony_ci dev->hw_features |= NETIF_F_GRO_HW; 444862306a36Sopenharmony_ci 444962306a36Sopenharmony_ci dev->vlan_features = dev->features; 445062306a36Sopenharmony_ci dev->xdp_features = NETDEV_XDP_ACT_BASIC | NETDEV_XDP_ACT_REDIRECT; 445162306a36Sopenharmony_ci 445262306a36Sopenharmony_ci /* MTU range: 68 - 65535 */ 445362306a36Sopenharmony_ci dev->min_mtu = MIN_MTU; 445462306a36Sopenharmony_ci dev->max_mtu = MAX_MTU; 445562306a36Sopenharmony_ci 445662306a36Sopenharmony_ci /* Configuration may specify what MAC to use. Otherwise random. */ 445762306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_MAC)) { 445862306a36Sopenharmony_ci u8 addr[ETH_ALEN]; 445962306a36Sopenharmony_ci 446062306a36Sopenharmony_ci virtio_cread_bytes(vdev, 446162306a36Sopenharmony_ci offsetof(struct virtio_net_config, mac), 446262306a36Sopenharmony_ci addr, ETH_ALEN); 446362306a36Sopenharmony_ci eth_hw_addr_set(dev, addr); 446462306a36Sopenharmony_ci } else { 446562306a36Sopenharmony_ci eth_hw_addr_random(dev); 446662306a36Sopenharmony_ci dev_info(&vdev->dev, "Assigned random MAC address %pM\n", 446762306a36Sopenharmony_ci dev->dev_addr); 446862306a36Sopenharmony_ci } 446962306a36Sopenharmony_ci 447062306a36Sopenharmony_ci /* Set up our device-specific information */ 447162306a36Sopenharmony_ci vi = netdev_priv(dev); 447262306a36Sopenharmony_ci vi->dev = dev; 447362306a36Sopenharmony_ci vi->vdev = vdev; 447462306a36Sopenharmony_ci vdev->priv = vi; 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_ci INIT_WORK(&vi->config_work, virtnet_config_changed_work); 447762306a36Sopenharmony_ci spin_lock_init(&vi->refill_lock); 447862306a36Sopenharmony_ci 447962306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF)) { 448062306a36Sopenharmony_ci vi->mergeable_rx_bufs = true; 448162306a36Sopenharmony_ci dev->xdp_features |= NETDEV_XDP_ACT_RX_SG; 448262306a36Sopenharmony_ci } 448362306a36Sopenharmony_ci 448462306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_NOTF_COAL)) { 448562306a36Sopenharmony_ci vi->intr_coal_rx.max_usecs = 0; 448662306a36Sopenharmony_ci vi->intr_coal_tx.max_usecs = 0; 448762306a36Sopenharmony_ci vi->intr_coal_tx.max_packets = 0; 448862306a36Sopenharmony_ci vi->intr_coal_rx.max_packets = 0; 448962306a36Sopenharmony_ci } 449062306a36Sopenharmony_ci 449162306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_HASH_REPORT)) 449262306a36Sopenharmony_ci vi->has_rss_hash_report = true; 449362306a36Sopenharmony_ci 449462306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_RSS)) 449562306a36Sopenharmony_ci vi->has_rss = true; 449662306a36Sopenharmony_ci 449762306a36Sopenharmony_ci if (vi->has_rss || vi->has_rss_hash_report) { 449862306a36Sopenharmony_ci vi->rss_indir_table_size = 449962306a36Sopenharmony_ci virtio_cread16(vdev, offsetof(struct virtio_net_config, 450062306a36Sopenharmony_ci rss_max_indirection_table_length)); 450162306a36Sopenharmony_ci vi->rss_key_size = 450262306a36Sopenharmony_ci virtio_cread8(vdev, offsetof(struct virtio_net_config, rss_max_key_size)); 450362306a36Sopenharmony_ci 450462306a36Sopenharmony_ci vi->rss_hash_types_supported = 450562306a36Sopenharmony_ci virtio_cread32(vdev, offsetof(struct virtio_net_config, supported_hash_types)); 450662306a36Sopenharmony_ci vi->rss_hash_types_supported &= 450762306a36Sopenharmony_ci ~(VIRTIO_NET_RSS_HASH_TYPE_IP_EX | 450862306a36Sopenharmony_ci VIRTIO_NET_RSS_HASH_TYPE_TCP_EX | 450962306a36Sopenharmony_ci VIRTIO_NET_RSS_HASH_TYPE_UDP_EX); 451062306a36Sopenharmony_ci 451162306a36Sopenharmony_ci dev->hw_features |= NETIF_F_RXHASH; 451262306a36Sopenharmony_ci } 451362306a36Sopenharmony_ci 451462306a36Sopenharmony_ci if (vi->has_rss_hash_report) 451562306a36Sopenharmony_ci vi->hdr_len = sizeof(struct virtio_net_hdr_v1_hash); 451662306a36Sopenharmony_ci else if (virtio_has_feature(vdev, VIRTIO_NET_F_MRG_RXBUF) || 451762306a36Sopenharmony_ci virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) 451862306a36Sopenharmony_ci vi->hdr_len = sizeof(struct virtio_net_hdr_mrg_rxbuf); 451962306a36Sopenharmony_ci else 452062306a36Sopenharmony_ci vi->hdr_len = sizeof(struct virtio_net_hdr); 452162306a36Sopenharmony_ci 452262306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_F_ANY_LAYOUT) || 452362306a36Sopenharmony_ci virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) 452462306a36Sopenharmony_ci vi->any_header_sg = true; 452562306a36Sopenharmony_ci 452662306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_CTRL_VQ)) 452762306a36Sopenharmony_ci vi->has_cvq = true; 452862306a36Sopenharmony_ci 452962306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_MTU)) { 453062306a36Sopenharmony_ci mtu = virtio_cread16(vdev, 453162306a36Sopenharmony_ci offsetof(struct virtio_net_config, 453262306a36Sopenharmony_ci mtu)); 453362306a36Sopenharmony_ci if (mtu < dev->min_mtu) { 453462306a36Sopenharmony_ci /* Should never trigger: MTU was previously validated 453562306a36Sopenharmony_ci * in virtnet_validate. 453662306a36Sopenharmony_ci */ 453762306a36Sopenharmony_ci dev_err(&vdev->dev, 453862306a36Sopenharmony_ci "device MTU appears to have changed it is now %d < %d", 453962306a36Sopenharmony_ci mtu, dev->min_mtu); 454062306a36Sopenharmony_ci err = -EINVAL; 454162306a36Sopenharmony_ci goto free; 454262306a36Sopenharmony_ci } 454362306a36Sopenharmony_ci 454462306a36Sopenharmony_ci dev->mtu = mtu; 454562306a36Sopenharmony_ci dev->max_mtu = mtu; 454662306a36Sopenharmony_ci } 454762306a36Sopenharmony_ci 454862306a36Sopenharmony_ci virtnet_set_big_packets(vi, mtu); 454962306a36Sopenharmony_ci 455062306a36Sopenharmony_ci if (vi->any_header_sg) 455162306a36Sopenharmony_ci dev->needed_headroom = vi->hdr_len; 455262306a36Sopenharmony_ci 455362306a36Sopenharmony_ci /* Enable multiqueue by default */ 455462306a36Sopenharmony_ci if (num_online_cpus() >= max_queue_pairs) 455562306a36Sopenharmony_ci vi->curr_queue_pairs = max_queue_pairs; 455662306a36Sopenharmony_ci else 455762306a36Sopenharmony_ci vi->curr_queue_pairs = num_online_cpus(); 455862306a36Sopenharmony_ci vi->max_queue_pairs = max_queue_pairs; 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci /* Allocate/initialize the rx/tx queues, and invoke find_vqs */ 456162306a36Sopenharmony_ci err = init_vqs(vi); 456262306a36Sopenharmony_ci if (err) 456362306a36Sopenharmony_ci goto free; 456462306a36Sopenharmony_ci 456562306a36Sopenharmony_ci#ifdef CONFIG_SYSFS 456662306a36Sopenharmony_ci if (vi->mergeable_rx_bufs) 456762306a36Sopenharmony_ci dev->sysfs_rx_queue_group = &virtio_net_mrg_rx_group; 456862306a36Sopenharmony_ci#endif 456962306a36Sopenharmony_ci netif_set_real_num_tx_queues(dev, vi->curr_queue_pairs); 457062306a36Sopenharmony_ci netif_set_real_num_rx_queues(dev, vi->curr_queue_pairs); 457162306a36Sopenharmony_ci 457262306a36Sopenharmony_ci virtnet_init_settings(dev); 457362306a36Sopenharmony_ci 457462306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_NET_F_STANDBY)) { 457562306a36Sopenharmony_ci vi->failover = net_failover_create(vi->dev); 457662306a36Sopenharmony_ci if (IS_ERR(vi->failover)) { 457762306a36Sopenharmony_ci err = PTR_ERR(vi->failover); 457862306a36Sopenharmony_ci goto free_vqs; 457962306a36Sopenharmony_ci } 458062306a36Sopenharmony_ci } 458162306a36Sopenharmony_ci 458262306a36Sopenharmony_ci if (vi->has_rss || vi->has_rss_hash_report) 458362306a36Sopenharmony_ci virtnet_init_default_rss(vi); 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci /* serialize netdev register + virtio_device_ready() with ndo_open() */ 458662306a36Sopenharmony_ci rtnl_lock(); 458762306a36Sopenharmony_ci 458862306a36Sopenharmony_ci err = register_netdevice(dev); 458962306a36Sopenharmony_ci if (err) { 459062306a36Sopenharmony_ci pr_debug("virtio_net: registering device failed\n"); 459162306a36Sopenharmony_ci rtnl_unlock(); 459262306a36Sopenharmony_ci goto free_failover; 459362306a36Sopenharmony_ci } 459462306a36Sopenharmony_ci 459562306a36Sopenharmony_ci virtio_device_ready(vdev); 459662306a36Sopenharmony_ci 459762306a36Sopenharmony_ci _virtnet_set_queues(vi, vi->curr_queue_pairs); 459862306a36Sopenharmony_ci 459962306a36Sopenharmony_ci /* a random MAC address has been assigned, notify the device. 460062306a36Sopenharmony_ci * We don't fail probe if VIRTIO_NET_F_CTRL_MAC_ADDR is not there 460162306a36Sopenharmony_ci * because many devices work fine without getting MAC explicitly 460262306a36Sopenharmony_ci */ 460362306a36Sopenharmony_ci if (!virtio_has_feature(vdev, VIRTIO_NET_F_MAC) && 460462306a36Sopenharmony_ci virtio_has_feature(vi->vdev, VIRTIO_NET_F_CTRL_MAC_ADDR)) { 460562306a36Sopenharmony_ci struct scatterlist sg; 460662306a36Sopenharmony_ci 460762306a36Sopenharmony_ci sg_init_one(&sg, dev->dev_addr, dev->addr_len); 460862306a36Sopenharmony_ci if (!virtnet_send_command(vi, VIRTIO_NET_CTRL_MAC, 460962306a36Sopenharmony_ci VIRTIO_NET_CTRL_MAC_ADDR_SET, &sg)) { 461062306a36Sopenharmony_ci pr_debug("virtio_net: setting MAC address failed\n"); 461162306a36Sopenharmony_ci rtnl_unlock(); 461262306a36Sopenharmony_ci err = -EINVAL; 461362306a36Sopenharmony_ci goto free_unregister_netdev; 461462306a36Sopenharmony_ci } 461562306a36Sopenharmony_ci } 461662306a36Sopenharmony_ci 461762306a36Sopenharmony_ci rtnl_unlock(); 461862306a36Sopenharmony_ci 461962306a36Sopenharmony_ci err = virtnet_cpu_notif_add(vi); 462062306a36Sopenharmony_ci if (err) { 462162306a36Sopenharmony_ci pr_debug("virtio_net: registering cpu notifier failed\n"); 462262306a36Sopenharmony_ci goto free_unregister_netdev; 462362306a36Sopenharmony_ci } 462462306a36Sopenharmony_ci 462562306a36Sopenharmony_ci /* Assume link up if device can't report link status, 462662306a36Sopenharmony_ci otherwise get link status from config. */ 462762306a36Sopenharmony_ci netif_carrier_off(dev); 462862306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, VIRTIO_NET_F_STATUS)) { 462962306a36Sopenharmony_ci schedule_work(&vi->config_work); 463062306a36Sopenharmony_ci } else { 463162306a36Sopenharmony_ci vi->status = VIRTIO_NET_S_LINK_UP; 463262306a36Sopenharmony_ci virtnet_update_settings(vi); 463362306a36Sopenharmony_ci netif_carrier_on(dev); 463462306a36Sopenharmony_ci } 463562306a36Sopenharmony_ci 463662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(guest_offloads); i++) 463762306a36Sopenharmony_ci if (virtio_has_feature(vi->vdev, guest_offloads[i])) 463862306a36Sopenharmony_ci set_bit(guest_offloads[i], &vi->guest_offloads); 463962306a36Sopenharmony_ci vi->guest_offloads_capable = vi->guest_offloads; 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_ci pr_debug("virtnet: registered device %s with %d RX and TX vq's\n", 464262306a36Sopenharmony_ci dev->name, max_queue_pairs); 464362306a36Sopenharmony_ci 464462306a36Sopenharmony_ci return 0; 464562306a36Sopenharmony_ci 464662306a36Sopenharmony_cifree_unregister_netdev: 464762306a36Sopenharmony_ci unregister_netdev(dev); 464862306a36Sopenharmony_cifree_failover: 464962306a36Sopenharmony_ci net_failover_destroy(vi->failover); 465062306a36Sopenharmony_cifree_vqs: 465162306a36Sopenharmony_ci virtio_reset_device(vdev); 465262306a36Sopenharmony_ci cancel_delayed_work_sync(&vi->refill); 465362306a36Sopenharmony_ci free_receive_page_frags(vi); 465462306a36Sopenharmony_ci virtnet_del_vqs(vi); 465562306a36Sopenharmony_cifree: 465662306a36Sopenharmony_ci free_netdev(dev); 465762306a36Sopenharmony_ci return err; 465862306a36Sopenharmony_ci} 465962306a36Sopenharmony_ci 466062306a36Sopenharmony_cistatic void remove_vq_common(struct virtnet_info *vi) 466162306a36Sopenharmony_ci{ 466262306a36Sopenharmony_ci virtio_reset_device(vi->vdev); 466362306a36Sopenharmony_ci 466462306a36Sopenharmony_ci /* Free unused buffers in both send and recv, if any. */ 466562306a36Sopenharmony_ci free_unused_bufs(vi); 466662306a36Sopenharmony_ci 466762306a36Sopenharmony_ci free_receive_bufs(vi); 466862306a36Sopenharmony_ci 466962306a36Sopenharmony_ci free_receive_page_frags(vi); 467062306a36Sopenharmony_ci 467162306a36Sopenharmony_ci virtnet_del_vqs(vi); 467262306a36Sopenharmony_ci} 467362306a36Sopenharmony_ci 467462306a36Sopenharmony_cistatic void virtnet_remove(struct virtio_device *vdev) 467562306a36Sopenharmony_ci{ 467662306a36Sopenharmony_ci struct virtnet_info *vi = vdev->priv; 467762306a36Sopenharmony_ci 467862306a36Sopenharmony_ci virtnet_cpu_notif_remove(vi); 467962306a36Sopenharmony_ci 468062306a36Sopenharmony_ci /* Make sure no work handler is accessing the device. */ 468162306a36Sopenharmony_ci flush_work(&vi->config_work); 468262306a36Sopenharmony_ci 468362306a36Sopenharmony_ci unregister_netdev(vi->dev); 468462306a36Sopenharmony_ci 468562306a36Sopenharmony_ci net_failover_destroy(vi->failover); 468662306a36Sopenharmony_ci 468762306a36Sopenharmony_ci remove_vq_common(vi); 468862306a36Sopenharmony_ci 468962306a36Sopenharmony_ci free_netdev(vi->dev); 469062306a36Sopenharmony_ci} 469162306a36Sopenharmony_ci 469262306a36Sopenharmony_cistatic __maybe_unused int virtnet_freeze(struct virtio_device *vdev) 469362306a36Sopenharmony_ci{ 469462306a36Sopenharmony_ci struct virtnet_info *vi = vdev->priv; 469562306a36Sopenharmony_ci 469662306a36Sopenharmony_ci virtnet_cpu_notif_remove(vi); 469762306a36Sopenharmony_ci virtnet_freeze_down(vdev); 469862306a36Sopenharmony_ci remove_vq_common(vi); 469962306a36Sopenharmony_ci 470062306a36Sopenharmony_ci return 0; 470162306a36Sopenharmony_ci} 470262306a36Sopenharmony_ci 470362306a36Sopenharmony_cistatic __maybe_unused int virtnet_restore(struct virtio_device *vdev) 470462306a36Sopenharmony_ci{ 470562306a36Sopenharmony_ci struct virtnet_info *vi = vdev->priv; 470662306a36Sopenharmony_ci int err; 470762306a36Sopenharmony_ci 470862306a36Sopenharmony_ci err = virtnet_restore_up(vdev); 470962306a36Sopenharmony_ci if (err) 471062306a36Sopenharmony_ci return err; 471162306a36Sopenharmony_ci virtnet_set_queues(vi, vi->curr_queue_pairs); 471262306a36Sopenharmony_ci 471362306a36Sopenharmony_ci err = virtnet_cpu_notif_add(vi); 471462306a36Sopenharmony_ci if (err) { 471562306a36Sopenharmony_ci virtnet_freeze_down(vdev); 471662306a36Sopenharmony_ci remove_vq_common(vi); 471762306a36Sopenharmony_ci return err; 471862306a36Sopenharmony_ci } 471962306a36Sopenharmony_ci 472062306a36Sopenharmony_ci return 0; 472162306a36Sopenharmony_ci} 472262306a36Sopenharmony_ci 472362306a36Sopenharmony_cistatic struct virtio_device_id id_table[] = { 472462306a36Sopenharmony_ci { VIRTIO_ID_NET, VIRTIO_DEV_ANY_ID }, 472562306a36Sopenharmony_ci { 0 }, 472662306a36Sopenharmony_ci}; 472762306a36Sopenharmony_ci 472862306a36Sopenharmony_ci#define VIRTNET_FEATURES \ 472962306a36Sopenharmony_ci VIRTIO_NET_F_CSUM, VIRTIO_NET_F_GUEST_CSUM, \ 473062306a36Sopenharmony_ci VIRTIO_NET_F_MAC, \ 473162306a36Sopenharmony_ci VIRTIO_NET_F_HOST_TSO4, VIRTIO_NET_F_HOST_UFO, VIRTIO_NET_F_HOST_TSO6, \ 473262306a36Sopenharmony_ci VIRTIO_NET_F_HOST_ECN, VIRTIO_NET_F_GUEST_TSO4, VIRTIO_NET_F_GUEST_TSO6, \ 473362306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_ECN, VIRTIO_NET_F_GUEST_UFO, \ 473462306a36Sopenharmony_ci VIRTIO_NET_F_HOST_USO, VIRTIO_NET_F_GUEST_USO4, VIRTIO_NET_F_GUEST_USO6, \ 473562306a36Sopenharmony_ci VIRTIO_NET_F_MRG_RXBUF, VIRTIO_NET_F_STATUS, VIRTIO_NET_F_CTRL_VQ, \ 473662306a36Sopenharmony_ci VIRTIO_NET_F_CTRL_RX, VIRTIO_NET_F_CTRL_VLAN, \ 473762306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_ANNOUNCE, VIRTIO_NET_F_MQ, \ 473862306a36Sopenharmony_ci VIRTIO_NET_F_CTRL_MAC_ADDR, \ 473962306a36Sopenharmony_ci VIRTIO_NET_F_MTU, VIRTIO_NET_F_CTRL_GUEST_OFFLOADS, \ 474062306a36Sopenharmony_ci VIRTIO_NET_F_SPEED_DUPLEX, VIRTIO_NET_F_STANDBY, \ 474162306a36Sopenharmony_ci VIRTIO_NET_F_RSS, VIRTIO_NET_F_HASH_REPORT, VIRTIO_NET_F_NOTF_COAL, \ 474262306a36Sopenharmony_ci VIRTIO_NET_F_VQ_NOTF_COAL, \ 474362306a36Sopenharmony_ci VIRTIO_NET_F_GUEST_HDRLEN 474462306a36Sopenharmony_ci 474562306a36Sopenharmony_cistatic unsigned int features[] = { 474662306a36Sopenharmony_ci VIRTNET_FEATURES, 474762306a36Sopenharmony_ci}; 474862306a36Sopenharmony_ci 474962306a36Sopenharmony_cistatic unsigned int features_legacy[] = { 475062306a36Sopenharmony_ci VIRTNET_FEATURES, 475162306a36Sopenharmony_ci VIRTIO_NET_F_GSO, 475262306a36Sopenharmony_ci VIRTIO_F_ANY_LAYOUT, 475362306a36Sopenharmony_ci}; 475462306a36Sopenharmony_ci 475562306a36Sopenharmony_cistatic struct virtio_driver virtio_net_driver = { 475662306a36Sopenharmony_ci .feature_table = features, 475762306a36Sopenharmony_ci .feature_table_size = ARRAY_SIZE(features), 475862306a36Sopenharmony_ci .feature_table_legacy = features_legacy, 475962306a36Sopenharmony_ci .feature_table_size_legacy = ARRAY_SIZE(features_legacy), 476062306a36Sopenharmony_ci .driver.name = KBUILD_MODNAME, 476162306a36Sopenharmony_ci .driver.owner = THIS_MODULE, 476262306a36Sopenharmony_ci .id_table = id_table, 476362306a36Sopenharmony_ci .validate = virtnet_validate, 476462306a36Sopenharmony_ci .probe = virtnet_probe, 476562306a36Sopenharmony_ci .remove = virtnet_remove, 476662306a36Sopenharmony_ci .config_changed = virtnet_config_changed, 476762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 476862306a36Sopenharmony_ci .freeze = virtnet_freeze, 476962306a36Sopenharmony_ci .restore = virtnet_restore, 477062306a36Sopenharmony_ci#endif 477162306a36Sopenharmony_ci}; 477262306a36Sopenharmony_ci 477362306a36Sopenharmony_cistatic __init int virtio_net_driver_init(void) 477462306a36Sopenharmony_ci{ 477562306a36Sopenharmony_ci int ret; 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "virtio/net:online", 477862306a36Sopenharmony_ci virtnet_cpu_online, 477962306a36Sopenharmony_ci virtnet_cpu_down_prep); 478062306a36Sopenharmony_ci if (ret < 0) 478162306a36Sopenharmony_ci goto out; 478262306a36Sopenharmony_ci virtionet_online = ret; 478362306a36Sopenharmony_ci ret = cpuhp_setup_state_multi(CPUHP_VIRT_NET_DEAD, "virtio/net:dead", 478462306a36Sopenharmony_ci NULL, virtnet_cpu_dead); 478562306a36Sopenharmony_ci if (ret) 478662306a36Sopenharmony_ci goto err_dead; 478762306a36Sopenharmony_ci ret = register_virtio_driver(&virtio_net_driver); 478862306a36Sopenharmony_ci if (ret) 478962306a36Sopenharmony_ci goto err_virtio; 479062306a36Sopenharmony_ci return 0; 479162306a36Sopenharmony_cierr_virtio: 479262306a36Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_VIRT_NET_DEAD); 479362306a36Sopenharmony_cierr_dead: 479462306a36Sopenharmony_ci cpuhp_remove_multi_state(virtionet_online); 479562306a36Sopenharmony_ciout: 479662306a36Sopenharmony_ci return ret; 479762306a36Sopenharmony_ci} 479862306a36Sopenharmony_cimodule_init(virtio_net_driver_init); 479962306a36Sopenharmony_ci 480062306a36Sopenharmony_cistatic __exit void virtio_net_driver_exit(void) 480162306a36Sopenharmony_ci{ 480262306a36Sopenharmony_ci unregister_virtio_driver(&virtio_net_driver); 480362306a36Sopenharmony_ci cpuhp_remove_multi_state(CPUHP_VIRT_NET_DEAD); 480462306a36Sopenharmony_ci cpuhp_remove_multi_state(virtionet_online); 480562306a36Sopenharmony_ci} 480662306a36Sopenharmony_cimodule_exit(virtio_net_driver_exit); 480762306a36Sopenharmony_ci 480862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table); 480962306a36Sopenharmony_ciMODULE_DESCRIPTION("Virtio network driver"); 481062306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 4811