162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) ST-Ericsson AB 2013 462306a36Sopenharmony_ci * Authors: Vicram Arv 562306a36Sopenharmony_ci * Dmitry Tarnyagin <dmitry.tarnyagin@lockless.no> 662306a36Sopenharmony_ci * Sjur Brendeland 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <linux/module.h> 962306a36Sopenharmony_ci#include <linux/if_arp.h> 1062306a36Sopenharmony_ci#include <linux/virtio.h> 1162306a36Sopenharmony_ci#include <linux/vringh.h> 1262306a36Sopenharmony_ci#include <linux/debugfs.h> 1362306a36Sopenharmony_ci#include <linux/spinlock.h> 1462306a36Sopenharmony_ci#include <linux/genalloc.h> 1562306a36Sopenharmony_ci#include <linux/interrupt.h> 1662306a36Sopenharmony_ci#include <linux/netdevice.h> 1762306a36Sopenharmony_ci#include <linux/rtnetlink.h> 1862306a36Sopenharmony_ci#include <linux/virtio_ids.h> 1962306a36Sopenharmony_ci#include <linux/virtio_caif.h> 2062306a36Sopenharmony_ci#include <linux/virtio_ring.h> 2162306a36Sopenharmony_ci#include <linux/dma-mapping.h> 2262306a36Sopenharmony_ci#include <net/caif/caif_dev.h> 2362306a36Sopenharmony_ci#include <linux/virtio_config.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2662306a36Sopenharmony_ciMODULE_AUTHOR("Vicram Arv"); 2762306a36Sopenharmony_ciMODULE_AUTHOR("Sjur Brendeland"); 2862306a36Sopenharmony_ciMODULE_DESCRIPTION("Virtio CAIF Driver"); 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci/* NAPI schedule quota */ 3162306a36Sopenharmony_ci#define CFV_DEFAULT_QUOTA 32 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci/* Defaults used if virtio config space is unavailable */ 3462306a36Sopenharmony_ci#define CFV_DEF_MTU_SIZE 4096 3562306a36Sopenharmony_ci#define CFV_DEF_HEADROOM 32 3662306a36Sopenharmony_ci#define CFV_DEF_TAILROOM 32 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Required IP header alignment */ 3962306a36Sopenharmony_ci#define IP_HDR_ALIGN 4 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci/* struct cfv_napi_contxt - NAPI context info 4262306a36Sopenharmony_ci * @riov: IOV holding data read from the ring. Note that riov may 4362306a36Sopenharmony_ci * still hold data when cfv_rx_poll() returns. 4462306a36Sopenharmony_ci * @head: Last descriptor ID we received from vringh_getdesc_kern. 4562306a36Sopenharmony_ci * We use this to put descriptor back on the used ring. USHRT_MAX is 4662306a36Sopenharmony_ci * used to indicate invalid head-id. 4762306a36Sopenharmony_ci */ 4862306a36Sopenharmony_cistruct cfv_napi_context { 4962306a36Sopenharmony_ci struct vringh_kiov riov; 5062306a36Sopenharmony_ci unsigned short head; 5162306a36Sopenharmony_ci}; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci/* struct cfv_stats - statistics for debugfs 5462306a36Sopenharmony_ci * @rx_napi_complete: Number of NAPI completions (RX) 5562306a36Sopenharmony_ci * @rx_napi_resched: Number of calls where the full quota was used (RX) 5662306a36Sopenharmony_ci * @rx_nomem: Number of SKB alloc failures (RX) 5762306a36Sopenharmony_ci * @rx_kicks: Number of RX kicks 5862306a36Sopenharmony_ci * @tx_full_ring: Number times TX ring was full 5962306a36Sopenharmony_ci * @tx_no_mem: Number of times TX went out of memory 6062306a36Sopenharmony_ci * @tx_flow_on: Number of flow on (TX) 6162306a36Sopenharmony_ci * @tx_kicks: Number of TX kicks 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistruct cfv_stats { 6462306a36Sopenharmony_ci u32 rx_napi_complete; 6562306a36Sopenharmony_ci u32 rx_napi_resched; 6662306a36Sopenharmony_ci u32 rx_nomem; 6762306a36Sopenharmony_ci u32 rx_kicks; 6862306a36Sopenharmony_ci u32 tx_full_ring; 6962306a36Sopenharmony_ci u32 tx_no_mem; 7062306a36Sopenharmony_ci u32 tx_flow_on; 7162306a36Sopenharmony_ci u32 tx_kicks; 7262306a36Sopenharmony_ci}; 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci/* struct cfv_info - Caif Virtio control structure 7562306a36Sopenharmony_ci * @cfdev: caif common header 7662306a36Sopenharmony_ci * @vdev: Associated virtio device 7762306a36Sopenharmony_ci * @vr_rx: rx/downlink host vring 7862306a36Sopenharmony_ci * @vq_tx: tx/uplink virtqueue 7962306a36Sopenharmony_ci * @ndev: CAIF link layer device 8062306a36Sopenharmony_ci * @watermark_tx: indicates number of free descriptors we need 8162306a36Sopenharmony_ci * to reopen the tx-queues after overload. 8262306a36Sopenharmony_ci * @tx_lock: protects vq_tx from concurrent use 8362306a36Sopenharmony_ci * @tx_release_tasklet: Tasklet for freeing consumed TX buffers 8462306a36Sopenharmony_ci * @napi: Napi context used in cfv_rx_poll() 8562306a36Sopenharmony_ci * @ctx: Context data used in cfv_rx_poll() 8662306a36Sopenharmony_ci * @tx_hr: transmit headroom 8762306a36Sopenharmony_ci * @rx_hr: receive headroom 8862306a36Sopenharmony_ci * @tx_tr: transmit tail room 8962306a36Sopenharmony_ci * @rx_tr: receive tail room 9062306a36Sopenharmony_ci * @mtu: transmit max size 9162306a36Sopenharmony_ci * @mru: receive max size 9262306a36Sopenharmony_ci * @allocsz: size of dma memory reserved for TX buffers 9362306a36Sopenharmony_ci * @alloc_addr: virtual address to dma memory for TX buffers 9462306a36Sopenharmony_ci * @alloc_dma: dma address to dma memory for TX buffers 9562306a36Sopenharmony_ci * @genpool: Gen Pool used for allocating TX buffers 9662306a36Sopenharmony_ci * @reserved_mem: Pointer to memory reserve allocated from genpool 9762306a36Sopenharmony_ci * @reserved_size: Size of memory reserve allocated from genpool 9862306a36Sopenharmony_ci * @stats: Statistics exposed in sysfs 9962306a36Sopenharmony_ci * @debugfs: Debugfs dentry for statistic counters 10062306a36Sopenharmony_ci */ 10162306a36Sopenharmony_cistruct cfv_info { 10262306a36Sopenharmony_ci struct caif_dev_common cfdev; 10362306a36Sopenharmony_ci struct virtio_device *vdev; 10462306a36Sopenharmony_ci struct vringh *vr_rx; 10562306a36Sopenharmony_ci struct virtqueue *vq_tx; 10662306a36Sopenharmony_ci struct net_device *ndev; 10762306a36Sopenharmony_ci unsigned int watermark_tx; 10862306a36Sopenharmony_ci /* Protect access to vq_tx */ 10962306a36Sopenharmony_ci spinlock_t tx_lock; 11062306a36Sopenharmony_ci struct tasklet_struct tx_release_tasklet; 11162306a36Sopenharmony_ci struct napi_struct napi; 11262306a36Sopenharmony_ci struct cfv_napi_context ctx; 11362306a36Sopenharmony_ci u16 tx_hr; 11462306a36Sopenharmony_ci u16 rx_hr; 11562306a36Sopenharmony_ci u16 tx_tr; 11662306a36Sopenharmony_ci u16 rx_tr; 11762306a36Sopenharmony_ci u32 mtu; 11862306a36Sopenharmony_ci u32 mru; 11962306a36Sopenharmony_ci size_t allocsz; 12062306a36Sopenharmony_ci void *alloc_addr; 12162306a36Sopenharmony_ci dma_addr_t alloc_dma; 12262306a36Sopenharmony_ci struct gen_pool *genpool; 12362306a36Sopenharmony_ci unsigned long reserved_mem; 12462306a36Sopenharmony_ci size_t reserved_size; 12562306a36Sopenharmony_ci struct cfv_stats stats; 12662306a36Sopenharmony_ci struct dentry *debugfs; 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* struct buf_info - maintains transmit buffer data handle 13062306a36Sopenharmony_ci * @size: size of transmit buffer 13162306a36Sopenharmony_ci * @dma_handle: handle to allocated dma device memory area 13262306a36Sopenharmony_ci * @vaddr: virtual address mapping to allocated memory area 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_cistruct buf_info { 13562306a36Sopenharmony_ci size_t size; 13662306a36Sopenharmony_ci u8 *vaddr; 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* Called from virtio device, in IRQ context */ 14062306a36Sopenharmony_cistatic void cfv_release_cb(struct virtqueue *vq_tx) 14162306a36Sopenharmony_ci{ 14262306a36Sopenharmony_ci struct cfv_info *cfv = vq_tx->vdev->priv; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci ++cfv->stats.tx_kicks; 14562306a36Sopenharmony_ci tasklet_schedule(&cfv->tx_release_tasklet); 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic void free_buf_info(struct cfv_info *cfv, struct buf_info *buf_info) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci if (!buf_info) 15162306a36Sopenharmony_ci return; 15262306a36Sopenharmony_ci gen_pool_free(cfv->genpool, (unsigned long) buf_info->vaddr, 15362306a36Sopenharmony_ci buf_info->size); 15462306a36Sopenharmony_ci kfree(buf_info); 15562306a36Sopenharmony_ci} 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* This is invoked whenever the remote processor completed processing 15862306a36Sopenharmony_ci * a TX msg we just sent, and the buffer is put back to the used ring. 15962306a36Sopenharmony_ci */ 16062306a36Sopenharmony_cistatic void cfv_release_used_buf(struct virtqueue *vq_tx) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct cfv_info *cfv = vq_tx->vdev->priv; 16362306a36Sopenharmony_ci unsigned long flags; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci BUG_ON(vq_tx != cfv->vq_tx); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci for (;;) { 16862306a36Sopenharmony_ci unsigned int len; 16962306a36Sopenharmony_ci struct buf_info *buf_info; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci /* Get used buffer from used ring to recycle used descriptors */ 17262306a36Sopenharmony_ci spin_lock_irqsave(&cfv->tx_lock, flags); 17362306a36Sopenharmony_ci buf_info = virtqueue_get_buf(vq_tx, &len); 17462306a36Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* Stop looping if there are no more buffers to free */ 17762306a36Sopenharmony_ci if (!buf_info) 17862306a36Sopenharmony_ci break; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci free_buf_info(cfv, buf_info); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci /* watermark_tx indicates if we previously stopped the tx 18362306a36Sopenharmony_ci * queues. If we have enough free stots in the virtio ring, 18462306a36Sopenharmony_ci * re-establish memory reserved and open up tx queues. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_ci if (cfv->vq_tx->num_free <= cfv->watermark_tx) 18762306a36Sopenharmony_ci continue; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci /* Re-establish memory reserve */ 19062306a36Sopenharmony_ci if (cfv->reserved_mem == 0 && cfv->genpool) 19162306a36Sopenharmony_ci cfv->reserved_mem = 19262306a36Sopenharmony_ci gen_pool_alloc(cfv->genpool, 19362306a36Sopenharmony_ci cfv->reserved_size); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci /* Open up the tx queues */ 19662306a36Sopenharmony_ci if (cfv->reserved_mem) { 19762306a36Sopenharmony_ci cfv->watermark_tx = 19862306a36Sopenharmony_ci virtqueue_get_vring_size(cfv->vq_tx); 19962306a36Sopenharmony_ci netif_tx_wake_all_queues(cfv->ndev); 20062306a36Sopenharmony_ci /* Buffers are recycled in cfv_netdev_tx, so 20162306a36Sopenharmony_ci * disable notifications when queues are opened. 20262306a36Sopenharmony_ci */ 20362306a36Sopenharmony_ci virtqueue_disable_cb(cfv->vq_tx); 20462306a36Sopenharmony_ci ++cfv->stats.tx_flow_on; 20562306a36Sopenharmony_ci } else { 20662306a36Sopenharmony_ci /* if no memory reserve, wait for more free slots */ 20762306a36Sopenharmony_ci WARN_ON(cfv->watermark_tx > 20862306a36Sopenharmony_ci virtqueue_get_vring_size(cfv->vq_tx)); 20962306a36Sopenharmony_ci cfv->watermark_tx += 21062306a36Sopenharmony_ci virtqueue_get_vring_size(cfv->vq_tx) / 4; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci } 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci/* Allocate a SKB and copy packet data to it */ 21662306a36Sopenharmony_cistatic struct sk_buff *cfv_alloc_and_copy_skb(int *err, 21762306a36Sopenharmony_ci struct cfv_info *cfv, 21862306a36Sopenharmony_ci u8 *frm, u32 frm_len) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci struct sk_buff *skb; 22162306a36Sopenharmony_ci u32 cfpkt_len, pad_len; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci *err = 0; 22462306a36Sopenharmony_ci /* Verify that packet size with down-link header and mtu size */ 22562306a36Sopenharmony_ci if (frm_len > cfv->mru || frm_len <= cfv->rx_hr + cfv->rx_tr) { 22662306a36Sopenharmony_ci netdev_err(cfv->ndev, 22762306a36Sopenharmony_ci "Invalid frmlen:%u mtu:%u hr:%d tr:%d\n", 22862306a36Sopenharmony_ci frm_len, cfv->mru, cfv->rx_hr, 22962306a36Sopenharmony_ci cfv->rx_tr); 23062306a36Sopenharmony_ci *err = -EPROTO; 23162306a36Sopenharmony_ci return NULL; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci cfpkt_len = frm_len - (cfv->rx_hr + cfv->rx_tr); 23562306a36Sopenharmony_ci pad_len = (unsigned long)(frm + cfv->rx_hr) & (IP_HDR_ALIGN - 1); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci skb = netdev_alloc_skb(cfv->ndev, frm_len + pad_len); 23862306a36Sopenharmony_ci if (!skb) { 23962306a36Sopenharmony_ci *err = -ENOMEM; 24062306a36Sopenharmony_ci return NULL; 24162306a36Sopenharmony_ci } 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci skb_reserve(skb, cfv->rx_hr + pad_len); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci skb_put_data(skb, frm + cfv->rx_hr, cfpkt_len); 24662306a36Sopenharmony_ci return skb; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci/* Get packets from the host vring */ 25062306a36Sopenharmony_cistatic int cfv_rx_poll(struct napi_struct *napi, int quota) 25162306a36Sopenharmony_ci{ 25262306a36Sopenharmony_ci struct cfv_info *cfv = container_of(napi, struct cfv_info, napi); 25362306a36Sopenharmony_ci int rxcnt = 0; 25462306a36Sopenharmony_ci int err = 0; 25562306a36Sopenharmony_ci void *buf; 25662306a36Sopenharmony_ci struct sk_buff *skb; 25762306a36Sopenharmony_ci struct vringh_kiov *riov = &cfv->ctx.riov; 25862306a36Sopenharmony_ci unsigned int skb_len; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci do { 26162306a36Sopenharmony_ci skb = NULL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci /* Put the previous iovec back on the used ring and 26462306a36Sopenharmony_ci * fetch a new iovec if we have processed all elements. 26562306a36Sopenharmony_ci */ 26662306a36Sopenharmony_ci if (riov->i == riov->used) { 26762306a36Sopenharmony_ci if (cfv->ctx.head != USHRT_MAX) { 26862306a36Sopenharmony_ci vringh_complete_kern(cfv->vr_rx, 26962306a36Sopenharmony_ci cfv->ctx.head, 27062306a36Sopenharmony_ci 0); 27162306a36Sopenharmony_ci cfv->ctx.head = USHRT_MAX; 27262306a36Sopenharmony_ci } 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci err = vringh_getdesc_kern( 27562306a36Sopenharmony_ci cfv->vr_rx, 27662306a36Sopenharmony_ci riov, 27762306a36Sopenharmony_ci NULL, 27862306a36Sopenharmony_ci &cfv->ctx.head, 27962306a36Sopenharmony_ci GFP_ATOMIC); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci if (err <= 0) 28262306a36Sopenharmony_ci goto exit; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci buf = phys_to_virt((unsigned long) riov->iov[riov->i].iov_base); 28662306a36Sopenharmony_ci /* TODO: Add check on valid buffer address */ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci skb = cfv_alloc_and_copy_skb(&err, cfv, buf, 28962306a36Sopenharmony_ci riov->iov[riov->i].iov_len); 29062306a36Sopenharmony_ci if (unlikely(err)) 29162306a36Sopenharmony_ci goto exit; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* Push received packet up the stack. */ 29462306a36Sopenharmony_ci skb_len = skb->len; 29562306a36Sopenharmony_ci skb->protocol = htons(ETH_P_CAIF); 29662306a36Sopenharmony_ci skb_reset_mac_header(skb); 29762306a36Sopenharmony_ci skb->dev = cfv->ndev; 29862306a36Sopenharmony_ci err = netif_receive_skb(skb); 29962306a36Sopenharmony_ci if (unlikely(err)) { 30062306a36Sopenharmony_ci ++cfv->ndev->stats.rx_dropped; 30162306a36Sopenharmony_ci } else { 30262306a36Sopenharmony_ci ++cfv->ndev->stats.rx_packets; 30362306a36Sopenharmony_ci cfv->ndev->stats.rx_bytes += skb_len; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ++riov->i; 30762306a36Sopenharmony_ci ++rxcnt; 30862306a36Sopenharmony_ci } while (rxcnt < quota); 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci ++cfv->stats.rx_napi_resched; 31162306a36Sopenharmony_ci goto out; 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ciexit: 31462306a36Sopenharmony_ci switch (err) { 31562306a36Sopenharmony_ci case 0: 31662306a36Sopenharmony_ci ++cfv->stats.rx_napi_complete; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Really out of packets? (stolen from virtio_net)*/ 31962306a36Sopenharmony_ci napi_complete(napi); 32062306a36Sopenharmony_ci if (unlikely(!vringh_notify_enable_kern(cfv->vr_rx)) && 32162306a36Sopenharmony_ci napi_schedule_prep(napi)) { 32262306a36Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 32362306a36Sopenharmony_ci __napi_schedule(napi); 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci break; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci case -ENOMEM: 32862306a36Sopenharmony_ci ++cfv->stats.rx_nomem; 32962306a36Sopenharmony_ci dev_kfree_skb(skb); 33062306a36Sopenharmony_ci /* Stop NAPI poll on OOM, we hope to be polled later */ 33162306a36Sopenharmony_ci napi_complete(napi); 33262306a36Sopenharmony_ci vringh_notify_enable_kern(cfv->vr_rx); 33362306a36Sopenharmony_ci break; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci default: 33662306a36Sopenharmony_ci /* We're doomed, any modem fault is fatal */ 33762306a36Sopenharmony_ci netdev_warn(cfv->ndev, "Bad ring, disable device\n"); 33862306a36Sopenharmony_ci cfv->ndev->stats.rx_dropped = riov->used - riov->i; 33962306a36Sopenharmony_ci napi_complete(napi); 34062306a36Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 34162306a36Sopenharmony_ci netif_carrier_off(cfv->ndev); 34262306a36Sopenharmony_ci break; 34362306a36Sopenharmony_ci } 34462306a36Sopenharmony_ciout: 34562306a36Sopenharmony_ci if (rxcnt && vringh_need_notify_kern(cfv->vr_rx) > 0) 34662306a36Sopenharmony_ci vringh_notify(cfv->vr_rx); 34762306a36Sopenharmony_ci return rxcnt; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic void cfv_recv(struct virtio_device *vdev, struct vringh *vr_rx) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct cfv_info *cfv = vdev->priv; 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_ci ++cfv->stats.rx_kicks; 35562306a36Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 35662306a36Sopenharmony_ci napi_schedule(&cfv->napi); 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic void cfv_destroy_genpool(struct cfv_info *cfv) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci if (cfv->alloc_addr) 36262306a36Sopenharmony_ci dma_free_coherent(cfv->vdev->dev.parent->parent, 36362306a36Sopenharmony_ci cfv->allocsz, cfv->alloc_addr, 36462306a36Sopenharmony_ci cfv->alloc_dma); 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci if (!cfv->genpool) 36762306a36Sopenharmony_ci return; 36862306a36Sopenharmony_ci gen_pool_free(cfv->genpool, cfv->reserved_mem, 36962306a36Sopenharmony_ci cfv->reserved_size); 37062306a36Sopenharmony_ci gen_pool_destroy(cfv->genpool); 37162306a36Sopenharmony_ci cfv->genpool = NULL; 37262306a36Sopenharmony_ci} 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic int cfv_create_genpool(struct cfv_info *cfv) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci int err; 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* dma_alloc can only allocate whole pages, and we need a more 37962306a36Sopenharmony_ci * fine graned allocation so we use genpool. We ask for space needed 38062306a36Sopenharmony_ci * by IP and a full ring. If the dma allcoation fails we retry with a 38162306a36Sopenharmony_ci * smaller allocation size. 38262306a36Sopenharmony_ci */ 38362306a36Sopenharmony_ci err = -ENOMEM; 38462306a36Sopenharmony_ci cfv->allocsz = (virtqueue_get_vring_size(cfv->vq_tx) * 38562306a36Sopenharmony_ci (ETH_DATA_LEN + cfv->tx_hr + cfv->tx_tr) * 11)/10; 38662306a36Sopenharmony_ci if (cfv->allocsz <= (num_possible_cpus() + 1) * cfv->ndev->mtu) 38762306a36Sopenharmony_ci return -EINVAL; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci for (;;) { 39062306a36Sopenharmony_ci if (cfv->allocsz <= num_possible_cpus() * cfv->ndev->mtu) { 39162306a36Sopenharmony_ci netdev_info(cfv->ndev, "Not enough device memory\n"); 39262306a36Sopenharmony_ci return -ENOMEM; 39362306a36Sopenharmony_ci } 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci cfv->alloc_addr = dma_alloc_coherent( 39662306a36Sopenharmony_ci cfv->vdev->dev.parent->parent, 39762306a36Sopenharmony_ci cfv->allocsz, &cfv->alloc_dma, 39862306a36Sopenharmony_ci GFP_ATOMIC); 39962306a36Sopenharmony_ci if (cfv->alloc_addr) 40062306a36Sopenharmony_ci break; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci cfv->allocsz = (cfv->allocsz * 3) >> 2; 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci netdev_dbg(cfv->ndev, "Allocated %zd bytes from dma-memory\n", 40662306a36Sopenharmony_ci cfv->allocsz); 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci /* Allocate on 128 bytes boundaries (1 << 7)*/ 40962306a36Sopenharmony_ci cfv->genpool = gen_pool_create(7, -1); 41062306a36Sopenharmony_ci if (!cfv->genpool) 41162306a36Sopenharmony_ci goto err; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci err = gen_pool_add_virt(cfv->genpool, (unsigned long)cfv->alloc_addr, 41462306a36Sopenharmony_ci (phys_addr_t)virt_to_phys(cfv->alloc_addr), 41562306a36Sopenharmony_ci cfv->allocsz, -1); 41662306a36Sopenharmony_ci if (err) 41762306a36Sopenharmony_ci goto err; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci /* Reserve some memory for low memory situations. If we hit the roof 42062306a36Sopenharmony_ci * in the memory pool, we stop TX flow and release the reserve. 42162306a36Sopenharmony_ci */ 42262306a36Sopenharmony_ci cfv->reserved_size = num_possible_cpus() * cfv->ndev->mtu; 42362306a36Sopenharmony_ci cfv->reserved_mem = gen_pool_alloc(cfv->genpool, 42462306a36Sopenharmony_ci cfv->reserved_size); 42562306a36Sopenharmony_ci if (!cfv->reserved_mem) { 42662306a36Sopenharmony_ci err = -ENOMEM; 42762306a36Sopenharmony_ci goto err; 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx); 43162306a36Sopenharmony_ci return 0; 43262306a36Sopenharmony_cierr: 43362306a36Sopenharmony_ci cfv_destroy_genpool(cfv); 43462306a36Sopenharmony_ci return err; 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci/* Enable the CAIF interface and allocate the memory-pool */ 43862306a36Sopenharmony_cistatic int cfv_netdev_open(struct net_device *netdev) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct cfv_info *cfv = netdev_priv(netdev); 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci if (cfv_create_genpool(cfv)) 44362306a36Sopenharmony_ci return -ENOMEM; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci netif_carrier_on(netdev); 44662306a36Sopenharmony_ci napi_enable(&cfv->napi); 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci /* Schedule NAPI to read any pending packets */ 44962306a36Sopenharmony_ci napi_schedule(&cfv->napi); 45062306a36Sopenharmony_ci return 0; 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci/* Disable the CAIF interface and free the memory-pool */ 45462306a36Sopenharmony_cistatic int cfv_netdev_close(struct net_device *netdev) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct cfv_info *cfv = netdev_priv(netdev); 45762306a36Sopenharmony_ci unsigned long flags; 45862306a36Sopenharmony_ci struct buf_info *buf_info; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci /* Disable interrupts, queues and NAPI polling */ 46162306a36Sopenharmony_ci netif_carrier_off(netdev); 46262306a36Sopenharmony_ci virtqueue_disable_cb(cfv->vq_tx); 46362306a36Sopenharmony_ci vringh_notify_disable_kern(cfv->vr_rx); 46462306a36Sopenharmony_ci napi_disable(&cfv->napi); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci /* Release any TX buffers on both used and available rings */ 46762306a36Sopenharmony_ci cfv_release_used_buf(cfv->vq_tx); 46862306a36Sopenharmony_ci spin_lock_irqsave(&cfv->tx_lock, flags); 46962306a36Sopenharmony_ci while ((buf_info = virtqueue_detach_unused_buf(cfv->vq_tx))) 47062306a36Sopenharmony_ci free_buf_info(cfv, buf_info); 47162306a36Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci /* Release all dma allocated memory and destroy the pool */ 47462306a36Sopenharmony_ci cfv_destroy_genpool(cfv); 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* Allocate a buffer in dma-memory and copy skb to it */ 47962306a36Sopenharmony_cistatic struct buf_info *cfv_alloc_and_copy_to_shm(struct cfv_info *cfv, 48062306a36Sopenharmony_ci struct sk_buff *skb, 48162306a36Sopenharmony_ci struct scatterlist *sg) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct caif_payload_info *info = (void *)&skb->cb; 48462306a36Sopenharmony_ci struct buf_info *buf_info = NULL; 48562306a36Sopenharmony_ci u8 pad_len, hdr_ofs; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci if (!cfv->genpool) 48862306a36Sopenharmony_ci goto err; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci if (unlikely(cfv->tx_hr + skb->len + cfv->tx_tr > cfv->mtu)) { 49162306a36Sopenharmony_ci netdev_warn(cfv->ndev, "Invalid packet len (%d > %d)\n", 49262306a36Sopenharmony_ci cfv->tx_hr + skb->len + cfv->tx_tr, cfv->mtu); 49362306a36Sopenharmony_ci goto err; 49462306a36Sopenharmony_ci } 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci buf_info = kmalloc(sizeof(struct buf_info), GFP_ATOMIC); 49762306a36Sopenharmony_ci if (unlikely(!buf_info)) 49862306a36Sopenharmony_ci goto err; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Make the IP header aligned in the buffer */ 50162306a36Sopenharmony_ci hdr_ofs = cfv->tx_hr + info->hdr_len; 50262306a36Sopenharmony_ci pad_len = hdr_ofs & (IP_HDR_ALIGN - 1); 50362306a36Sopenharmony_ci buf_info->size = cfv->tx_hr + skb->len + cfv->tx_tr + pad_len; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* allocate dma memory buffer */ 50662306a36Sopenharmony_ci buf_info->vaddr = (void *)gen_pool_alloc(cfv->genpool, buf_info->size); 50762306a36Sopenharmony_ci if (unlikely(!buf_info->vaddr)) 50862306a36Sopenharmony_ci goto err; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci /* copy skbuf contents to send buffer */ 51162306a36Sopenharmony_ci skb_copy_bits(skb, 0, buf_info->vaddr + cfv->tx_hr + pad_len, skb->len); 51262306a36Sopenharmony_ci sg_init_one(sg, buf_info->vaddr + pad_len, 51362306a36Sopenharmony_ci skb->len + cfv->tx_hr + cfv->rx_hr); 51462306a36Sopenharmony_ci 51562306a36Sopenharmony_ci return buf_info; 51662306a36Sopenharmony_cierr: 51762306a36Sopenharmony_ci kfree(buf_info); 51862306a36Sopenharmony_ci return NULL; 51962306a36Sopenharmony_ci} 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci/* Put the CAIF packet on the virtio ring and kick the receiver */ 52262306a36Sopenharmony_cistatic netdev_tx_t cfv_netdev_tx(struct sk_buff *skb, struct net_device *netdev) 52362306a36Sopenharmony_ci{ 52462306a36Sopenharmony_ci struct cfv_info *cfv = netdev_priv(netdev); 52562306a36Sopenharmony_ci struct buf_info *buf_info; 52662306a36Sopenharmony_ci struct scatterlist sg; 52762306a36Sopenharmony_ci unsigned long flags; 52862306a36Sopenharmony_ci bool flow_off = false; 52962306a36Sopenharmony_ci int ret; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci /* garbage collect released buffers */ 53262306a36Sopenharmony_ci cfv_release_used_buf(cfv->vq_tx); 53362306a36Sopenharmony_ci spin_lock_irqsave(&cfv->tx_lock, flags); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci /* Flow-off check takes into account number of cpus to make sure 53662306a36Sopenharmony_ci * virtqueue will not be overfilled in any possible smp conditions. 53762306a36Sopenharmony_ci * 53862306a36Sopenharmony_ci * Flow-on is triggered when sufficient buffers are freed 53962306a36Sopenharmony_ci */ 54062306a36Sopenharmony_ci if (unlikely(cfv->vq_tx->num_free <= num_present_cpus())) { 54162306a36Sopenharmony_ci flow_off = true; 54262306a36Sopenharmony_ci cfv->stats.tx_full_ring++; 54362306a36Sopenharmony_ci } 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci /* If we run out of memory, we release the memory reserve and retry 54662306a36Sopenharmony_ci * allocation. 54762306a36Sopenharmony_ci */ 54862306a36Sopenharmony_ci buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); 54962306a36Sopenharmony_ci if (unlikely(!buf_info)) { 55062306a36Sopenharmony_ci cfv->stats.tx_no_mem++; 55162306a36Sopenharmony_ci flow_off = true; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci if (cfv->reserved_mem && cfv->genpool) { 55462306a36Sopenharmony_ci gen_pool_free(cfv->genpool, cfv->reserved_mem, 55562306a36Sopenharmony_ci cfv->reserved_size); 55662306a36Sopenharmony_ci cfv->reserved_mem = 0; 55762306a36Sopenharmony_ci buf_info = cfv_alloc_and_copy_to_shm(cfv, skb, &sg); 55862306a36Sopenharmony_ci } 55962306a36Sopenharmony_ci } 56062306a36Sopenharmony_ci 56162306a36Sopenharmony_ci if (unlikely(flow_off)) { 56262306a36Sopenharmony_ci /* Turn flow on when a 1/4 of the descriptors are released */ 56362306a36Sopenharmony_ci cfv->watermark_tx = virtqueue_get_vring_size(cfv->vq_tx) / 4; 56462306a36Sopenharmony_ci /* Enable notifications of recycled TX buffers */ 56562306a36Sopenharmony_ci virtqueue_enable_cb(cfv->vq_tx); 56662306a36Sopenharmony_ci netif_tx_stop_all_queues(netdev); 56762306a36Sopenharmony_ci } 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci if (unlikely(!buf_info)) { 57062306a36Sopenharmony_ci /* If the memory reserve does it's job, this shouldn't happen */ 57162306a36Sopenharmony_ci netdev_warn(cfv->ndev, "Out of gen_pool memory\n"); 57262306a36Sopenharmony_ci goto err; 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci 57562306a36Sopenharmony_ci ret = virtqueue_add_outbuf(cfv->vq_tx, &sg, 1, buf_info, GFP_ATOMIC); 57662306a36Sopenharmony_ci if (unlikely((ret < 0))) { 57762306a36Sopenharmony_ci /* If flow control works, this shouldn't happen */ 57862306a36Sopenharmony_ci netdev_warn(cfv->ndev, "Failed adding buffer to TX vring:%d\n", 57962306a36Sopenharmony_ci ret); 58062306a36Sopenharmony_ci goto err; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* update netdev statistics */ 58462306a36Sopenharmony_ci cfv->ndev->stats.tx_packets++; 58562306a36Sopenharmony_ci cfv->ndev->stats.tx_bytes += skb->len; 58662306a36Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci /* tell the remote processor it has a pending message to read */ 58962306a36Sopenharmony_ci virtqueue_kick(cfv->vq_tx); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci dev_kfree_skb(skb); 59262306a36Sopenharmony_ci return NETDEV_TX_OK; 59362306a36Sopenharmony_cierr: 59462306a36Sopenharmony_ci spin_unlock_irqrestore(&cfv->tx_lock, flags); 59562306a36Sopenharmony_ci cfv->ndev->stats.tx_dropped++; 59662306a36Sopenharmony_ci free_buf_info(cfv, buf_info); 59762306a36Sopenharmony_ci dev_kfree_skb(skb); 59862306a36Sopenharmony_ci return NETDEV_TX_OK; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void cfv_tx_release_tasklet(struct tasklet_struct *t) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci struct cfv_info *cfv = from_tasklet(cfv, t, tx_release_tasklet); 60462306a36Sopenharmony_ci cfv_release_used_buf(cfv->vq_tx); 60562306a36Sopenharmony_ci} 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_cistatic const struct net_device_ops cfv_netdev_ops = { 60862306a36Sopenharmony_ci .ndo_open = cfv_netdev_open, 60962306a36Sopenharmony_ci .ndo_stop = cfv_netdev_close, 61062306a36Sopenharmony_ci .ndo_start_xmit = cfv_netdev_tx, 61162306a36Sopenharmony_ci}; 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void cfv_netdev_setup(struct net_device *netdev) 61462306a36Sopenharmony_ci{ 61562306a36Sopenharmony_ci netdev->netdev_ops = &cfv_netdev_ops; 61662306a36Sopenharmony_ci netdev->type = ARPHRD_CAIF; 61762306a36Sopenharmony_ci netdev->tx_queue_len = 100; 61862306a36Sopenharmony_ci netdev->flags = IFF_POINTOPOINT | IFF_NOARP; 61962306a36Sopenharmony_ci netdev->mtu = CFV_DEF_MTU_SIZE; 62062306a36Sopenharmony_ci netdev->needs_free_netdev = true; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* Create debugfs counters for the device */ 62462306a36Sopenharmony_cistatic inline void debugfs_init(struct cfv_info *cfv) 62562306a36Sopenharmony_ci{ 62662306a36Sopenharmony_ci cfv->debugfs = debugfs_create_dir(netdev_name(cfv->ndev), NULL); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci debugfs_create_u32("rx-napi-complete", 0400, cfv->debugfs, 62962306a36Sopenharmony_ci &cfv->stats.rx_napi_complete); 63062306a36Sopenharmony_ci debugfs_create_u32("rx-napi-resched", 0400, cfv->debugfs, 63162306a36Sopenharmony_ci &cfv->stats.rx_napi_resched); 63262306a36Sopenharmony_ci debugfs_create_u32("rx-nomem", 0400, cfv->debugfs, 63362306a36Sopenharmony_ci &cfv->stats.rx_nomem); 63462306a36Sopenharmony_ci debugfs_create_u32("rx-kicks", 0400, cfv->debugfs, 63562306a36Sopenharmony_ci &cfv->stats.rx_kicks); 63662306a36Sopenharmony_ci debugfs_create_u32("tx-full-ring", 0400, cfv->debugfs, 63762306a36Sopenharmony_ci &cfv->stats.tx_full_ring); 63862306a36Sopenharmony_ci debugfs_create_u32("tx-no-mem", 0400, cfv->debugfs, 63962306a36Sopenharmony_ci &cfv->stats.tx_no_mem); 64062306a36Sopenharmony_ci debugfs_create_u32("tx-kicks", 0400, cfv->debugfs, 64162306a36Sopenharmony_ci &cfv->stats.tx_kicks); 64262306a36Sopenharmony_ci debugfs_create_u32("tx-flow-on", 0400, cfv->debugfs, 64362306a36Sopenharmony_ci &cfv->stats.tx_flow_on); 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci/* Setup CAIF for the a virtio device */ 64762306a36Sopenharmony_cistatic int cfv_probe(struct virtio_device *vdev) 64862306a36Sopenharmony_ci{ 64962306a36Sopenharmony_ci vq_callback_t *vq_cbs = cfv_release_cb; 65062306a36Sopenharmony_ci vrh_callback_t *vrh_cbs = cfv_recv; 65162306a36Sopenharmony_ci const char *names = "output"; 65262306a36Sopenharmony_ci const char *cfv_netdev_name = "cfvrt"; 65362306a36Sopenharmony_ci struct net_device *netdev; 65462306a36Sopenharmony_ci struct cfv_info *cfv; 65562306a36Sopenharmony_ci int err; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci netdev = alloc_netdev(sizeof(struct cfv_info), cfv_netdev_name, 65862306a36Sopenharmony_ci NET_NAME_UNKNOWN, cfv_netdev_setup); 65962306a36Sopenharmony_ci if (!netdev) 66062306a36Sopenharmony_ci return -ENOMEM; 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci cfv = netdev_priv(netdev); 66362306a36Sopenharmony_ci cfv->vdev = vdev; 66462306a36Sopenharmony_ci cfv->ndev = netdev; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci spin_lock_init(&cfv->tx_lock); 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci /* Get the RX virtio ring. This is a "host side vring". */ 66962306a36Sopenharmony_ci err = -ENODEV; 67062306a36Sopenharmony_ci if (!vdev->vringh_config || !vdev->vringh_config->find_vrhs) 67162306a36Sopenharmony_ci goto err; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci err = vdev->vringh_config->find_vrhs(vdev, 1, &cfv->vr_rx, &vrh_cbs); 67462306a36Sopenharmony_ci if (err) 67562306a36Sopenharmony_ci goto err; 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci /* Get the TX virtio ring. This is a "guest side vring". */ 67862306a36Sopenharmony_ci err = virtio_find_vqs(vdev, 1, &cfv->vq_tx, &vq_cbs, &names, NULL); 67962306a36Sopenharmony_ci if (err) 68062306a36Sopenharmony_ci goto err; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci /* Get the CAIF configuration from virtio config space, if available */ 68362306a36Sopenharmony_ci if (vdev->config->get) { 68462306a36Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, headroom, 68562306a36Sopenharmony_ci &cfv->tx_hr); 68662306a36Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, headroom, 68762306a36Sopenharmony_ci &cfv->rx_hr); 68862306a36Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, 68962306a36Sopenharmony_ci &cfv->tx_tr); 69062306a36Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, tailroom, 69162306a36Sopenharmony_ci &cfv->rx_tr); 69262306a36Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, mtu, 69362306a36Sopenharmony_ci &cfv->mtu); 69462306a36Sopenharmony_ci virtio_cread(vdev, struct virtio_caif_transf_config, mtu, 69562306a36Sopenharmony_ci &cfv->mru); 69662306a36Sopenharmony_ci } else { 69762306a36Sopenharmony_ci cfv->tx_hr = CFV_DEF_HEADROOM; 69862306a36Sopenharmony_ci cfv->rx_hr = CFV_DEF_HEADROOM; 69962306a36Sopenharmony_ci cfv->tx_tr = CFV_DEF_TAILROOM; 70062306a36Sopenharmony_ci cfv->rx_tr = CFV_DEF_TAILROOM; 70162306a36Sopenharmony_ci cfv->mtu = CFV_DEF_MTU_SIZE; 70262306a36Sopenharmony_ci cfv->mru = CFV_DEF_MTU_SIZE; 70362306a36Sopenharmony_ci } 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci netdev->needed_headroom = cfv->tx_hr; 70662306a36Sopenharmony_ci netdev->needed_tailroom = cfv->tx_tr; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci /* Disable buffer release interrupts unless we have stopped TX queues */ 70962306a36Sopenharmony_ci virtqueue_disable_cb(cfv->vq_tx); 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci netdev->mtu = cfv->mtu - cfv->tx_tr; 71262306a36Sopenharmony_ci vdev->priv = cfv; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci /* Initialize NAPI poll context data */ 71562306a36Sopenharmony_ci vringh_kiov_init(&cfv->ctx.riov, NULL, 0); 71662306a36Sopenharmony_ci cfv->ctx.head = USHRT_MAX; 71762306a36Sopenharmony_ci netif_napi_add_weight(netdev, &cfv->napi, cfv_rx_poll, 71862306a36Sopenharmony_ci CFV_DEFAULT_QUOTA); 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci tasklet_setup(&cfv->tx_release_tasklet, cfv_tx_release_tasklet); 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_ci /* Carrier is off until netdevice is opened */ 72362306a36Sopenharmony_ci netif_carrier_off(netdev); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci /* serialize netdev register + virtio_device_ready() with ndo_open() */ 72662306a36Sopenharmony_ci rtnl_lock(); 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci /* register Netdev */ 72962306a36Sopenharmony_ci err = register_netdevice(netdev); 73062306a36Sopenharmony_ci if (err) { 73162306a36Sopenharmony_ci rtnl_unlock(); 73262306a36Sopenharmony_ci dev_err(&vdev->dev, "Unable to register netdev (%d)\n", err); 73362306a36Sopenharmony_ci goto err; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci virtio_device_ready(vdev); 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci rtnl_unlock(); 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci debugfs_init(cfv); 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci return 0; 74362306a36Sopenharmony_cierr: 74462306a36Sopenharmony_ci netdev_warn(cfv->ndev, "CAIF Virtio probe failed:%d\n", err); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (cfv->vr_rx) 74762306a36Sopenharmony_ci vdev->vringh_config->del_vrhs(cfv->vdev); 74862306a36Sopenharmony_ci if (cfv->vdev) 74962306a36Sopenharmony_ci vdev->config->del_vqs(cfv->vdev); 75062306a36Sopenharmony_ci free_netdev(netdev); 75162306a36Sopenharmony_ci return err; 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic void cfv_remove(struct virtio_device *vdev) 75562306a36Sopenharmony_ci{ 75662306a36Sopenharmony_ci struct cfv_info *cfv = vdev->priv; 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_ci rtnl_lock(); 75962306a36Sopenharmony_ci dev_close(cfv->ndev); 76062306a36Sopenharmony_ci rtnl_unlock(); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci tasklet_kill(&cfv->tx_release_tasklet); 76362306a36Sopenharmony_ci debugfs_remove_recursive(cfv->debugfs); 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci vringh_kiov_cleanup(&cfv->ctx.riov); 76662306a36Sopenharmony_ci virtio_reset_device(vdev); 76762306a36Sopenharmony_ci vdev->vringh_config->del_vrhs(cfv->vdev); 76862306a36Sopenharmony_ci cfv->vr_rx = NULL; 76962306a36Sopenharmony_ci vdev->config->del_vqs(cfv->vdev); 77062306a36Sopenharmony_ci unregister_netdev(cfv->ndev); 77162306a36Sopenharmony_ci} 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_cistatic struct virtio_device_id id_table[] = { 77462306a36Sopenharmony_ci { VIRTIO_ID_CAIF, VIRTIO_DEV_ANY_ID }, 77562306a36Sopenharmony_ci { 0 }, 77662306a36Sopenharmony_ci}; 77762306a36Sopenharmony_ci 77862306a36Sopenharmony_cistatic unsigned int features[] = { 77962306a36Sopenharmony_ci}; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_cistatic struct virtio_driver caif_virtio_driver = { 78262306a36Sopenharmony_ci .feature_table = features, 78362306a36Sopenharmony_ci .feature_table_size = ARRAY_SIZE(features), 78462306a36Sopenharmony_ci .driver.name = KBUILD_MODNAME, 78562306a36Sopenharmony_ci .driver.owner = THIS_MODULE, 78662306a36Sopenharmony_ci .id_table = id_table, 78762306a36Sopenharmony_ci .probe = cfv_probe, 78862306a36Sopenharmony_ci .remove = cfv_remove, 78962306a36Sopenharmony_ci}; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_cimodule_virtio_driver(caif_virtio_driver); 79262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table); 793