162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 or BSD-3-Clause 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright(c) 2017 - 2018 Intel Corporation. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* 762306a36Sopenharmony_ci * This file contains HFI1 support for VNIC SDMA functionality 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include "sdma.h" 1162306a36Sopenharmony_ci#include "vnic.h" 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#define HFI1_VNIC_SDMA_Q_ACTIVE BIT(0) 1462306a36Sopenharmony_ci#define HFI1_VNIC_SDMA_Q_DEFERRED BIT(1) 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#define HFI1_VNIC_TXREQ_NAME_LEN 32 1762306a36Sopenharmony_ci#define HFI1_VNIC_SDMA_DESC_WTRMRK 64 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci/* 2062306a36Sopenharmony_ci * struct vnic_txreq - VNIC transmit descriptor 2162306a36Sopenharmony_ci * @txreq: sdma transmit request 2262306a36Sopenharmony_ci * @sdma: vnic sdma pointer 2362306a36Sopenharmony_ci * @skb: skb to send 2462306a36Sopenharmony_ci * @pad: pad buffer 2562306a36Sopenharmony_ci * @plen: pad length 2662306a36Sopenharmony_ci * @pbc_val: pbc value 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_cistruct vnic_txreq { 2962306a36Sopenharmony_ci struct sdma_txreq txreq; 3062306a36Sopenharmony_ci struct hfi1_vnic_sdma *sdma; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci struct sk_buff *skb; 3362306a36Sopenharmony_ci unsigned char pad[HFI1_VNIC_MAX_PAD]; 3462306a36Sopenharmony_ci u16 plen; 3562306a36Sopenharmony_ci __le64 pbc_val; 3662306a36Sopenharmony_ci}; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_cistatic void vnic_sdma_complete(struct sdma_txreq *txreq, 3962306a36Sopenharmony_ci int status) 4062306a36Sopenharmony_ci{ 4162306a36Sopenharmony_ci struct vnic_txreq *tx = container_of(txreq, struct vnic_txreq, txreq); 4262306a36Sopenharmony_ci struct hfi1_vnic_sdma *vnic_sdma = tx->sdma; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci sdma_txclean(vnic_sdma->dd, txreq); 4562306a36Sopenharmony_ci dev_kfree_skb_any(tx->skb); 4662306a36Sopenharmony_ci kmem_cache_free(vnic_sdma->dd->vnic.txreq_cache, tx); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic noinline int build_vnic_ulp_payload(struct sdma_engine *sde, 5062306a36Sopenharmony_ci struct vnic_txreq *tx) 5162306a36Sopenharmony_ci{ 5262306a36Sopenharmony_ci int i, ret = 0; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci ret = sdma_txadd_kvaddr( 5562306a36Sopenharmony_ci sde->dd, 5662306a36Sopenharmony_ci &tx->txreq, 5762306a36Sopenharmony_ci tx->skb->data, 5862306a36Sopenharmony_ci skb_headlen(tx->skb)); 5962306a36Sopenharmony_ci if (unlikely(ret)) 6062306a36Sopenharmony_ci goto bail_txadd; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(tx->skb)->nr_frags; i++) { 6362306a36Sopenharmony_ci skb_frag_t *frag = &skb_shinfo(tx->skb)->frags[i]; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* combine physically continuous fragments later? */ 6662306a36Sopenharmony_ci ret = sdma_txadd_page(sde->dd, 6762306a36Sopenharmony_ci &tx->txreq, 6862306a36Sopenharmony_ci skb_frag_page(frag), 6962306a36Sopenharmony_ci skb_frag_off(frag), 7062306a36Sopenharmony_ci skb_frag_size(frag), 7162306a36Sopenharmony_ci NULL, NULL, NULL); 7262306a36Sopenharmony_ci if (unlikely(ret)) 7362306a36Sopenharmony_ci goto bail_txadd; 7462306a36Sopenharmony_ci } 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (tx->plen) 7762306a36Sopenharmony_ci ret = sdma_txadd_kvaddr(sde->dd, &tx->txreq, 7862306a36Sopenharmony_ci tx->pad + HFI1_VNIC_MAX_PAD - tx->plen, 7962306a36Sopenharmony_ci tx->plen); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cibail_txadd: 8262306a36Sopenharmony_ci return ret; 8362306a36Sopenharmony_ci} 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cistatic int build_vnic_tx_desc(struct sdma_engine *sde, 8662306a36Sopenharmony_ci struct vnic_txreq *tx, 8762306a36Sopenharmony_ci u64 pbc) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci int ret = 0; 9062306a36Sopenharmony_ci u16 hdrbytes = 2 << 2; /* PBC */ 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci ret = sdma_txinit_ahg( 9362306a36Sopenharmony_ci &tx->txreq, 9462306a36Sopenharmony_ci 0, 9562306a36Sopenharmony_ci hdrbytes + tx->skb->len + tx->plen, 9662306a36Sopenharmony_ci 0, 9762306a36Sopenharmony_ci 0, 9862306a36Sopenharmony_ci NULL, 9962306a36Sopenharmony_ci 0, 10062306a36Sopenharmony_ci vnic_sdma_complete); 10162306a36Sopenharmony_ci if (unlikely(ret)) 10262306a36Sopenharmony_ci goto bail_txadd; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci /* add pbc */ 10562306a36Sopenharmony_ci tx->pbc_val = cpu_to_le64(pbc); 10662306a36Sopenharmony_ci ret = sdma_txadd_kvaddr( 10762306a36Sopenharmony_ci sde->dd, 10862306a36Sopenharmony_ci &tx->txreq, 10962306a36Sopenharmony_ci &tx->pbc_val, 11062306a36Sopenharmony_ci hdrbytes); 11162306a36Sopenharmony_ci if (unlikely(ret)) 11262306a36Sopenharmony_ci goto bail_txadd; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci /* add the ulp payload */ 11562306a36Sopenharmony_ci ret = build_vnic_ulp_payload(sde, tx); 11662306a36Sopenharmony_cibail_txadd: 11762306a36Sopenharmony_ci return ret; 11862306a36Sopenharmony_ci} 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* setup the last plen bypes of pad */ 12162306a36Sopenharmony_cistatic inline void hfi1_vnic_update_pad(unsigned char *pad, u8 plen) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci pad[HFI1_VNIC_MAX_PAD - 1] = plen - OPA_VNIC_ICRC_TAIL_LEN; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciint hfi1_vnic_send_dma(struct hfi1_devdata *dd, u8 q_idx, 12762306a36Sopenharmony_ci struct hfi1_vnic_vport_info *vinfo, 12862306a36Sopenharmony_ci struct sk_buff *skb, u64 pbc, u8 plen) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct hfi1_vnic_sdma *vnic_sdma = &vinfo->sdma[q_idx]; 13162306a36Sopenharmony_ci struct sdma_engine *sde = vnic_sdma->sde; 13262306a36Sopenharmony_ci struct vnic_txreq *tx; 13362306a36Sopenharmony_ci int ret = -ECOMM; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci if (unlikely(READ_ONCE(vnic_sdma->state) != HFI1_VNIC_SDMA_Q_ACTIVE)) 13662306a36Sopenharmony_ci goto tx_err; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci if (unlikely(!sde || !sdma_running(sde))) 13962306a36Sopenharmony_ci goto tx_err; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci tx = kmem_cache_alloc(dd->vnic.txreq_cache, GFP_ATOMIC); 14262306a36Sopenharmony_ci if (unlikely(!tx)) { 14362306a36Sopenharmony_ci ret = -ENOMEM; 14462306a36Sopenharmony_ci goto tx_err; 14562306a36Sopenharmony_ci } 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci tx->sdma = vnic_sdma; 14862306a36Sopenharmony_ci tx->skb = skb; 14962306a36Sopenharmony_ci hfi1_vnic_update_pad(tx->pad, plen); 15062306a36Sopenharmony_ci tx->plen = plen; 15162306a36Sopenharmony_ci ret = build_vnic_tx_desc(sde, tx, pbc); 15262306a36Sopenharmony_ci if (unlikely(ret)) 15362306a36Sopenharmony_ci goto free_desc; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci ret = sdma_send_txreq(sde, iowait_get_ib_work(&vnic_sdma->wait), 15662306a36Sopenharmony_ci &tx->txreq, vnic_sdma->pkts_sent); 15762306a36Sopenharmony_ci /* When -ECOMM, sdma callback will be called with ABORT status */ 15862306a36Sopenharmony_ci if (unlikely(ret && unlikely(ret != -ECOMM))) 15962306a36Sopenharmony_ci goto free_desc; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if (!ret) { 16262306a36Sopenharmony_ci vnic_sdma->pkts_sent = true; 16362306a36Sopenharmony_ci iowait_starve_clear(vnic_sdma->pkts_sent, &vnic_sdma->wait); 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci return ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_cifree_desc: 16862306a36Sopenharmony_ci sdma_txclean(dd, &tx->txreq); 16962306a36Sopenharmony_ci kmem_cache_free(dd->vnic.txreq_cache, tx); 17062306a36Sopenharmony_citx_err: 17162306a36Sopenharmony_ci if (ret != -EBUSY) 17262306a36Sopenharmony_ci dev_kfree_skb_any(skb); 17362306a36Sopenharmony_ci else 17462306a36Sopenharmony_ci vnic_sdma->pkts_sent = false; 17562306a36Sopenharmony_ci return ret; 17662306a36Sopenharmony_ci} 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* 17962306a36Sopenharmony_ci * hfi1_vnic_sdma_sleep - vnic sdma sleep function 18062306a36Sopenharmony_ci * 18162306a36Sopenharmony_ci * This function gets called from sdma_send_txreq() when there are not enough 18262306a36Sopenharmony_ci * sdma descriptors available to send the packet. It adds Tx queue's wait 18362306a36Sopenharmony_ci * structure to sdma engine's dmawait list to be woken up when descriptors 18462306a36Sopenharmony_ci * become available. 18562306a36Sopenharmony_ci */ 18662306a36Sopenharmony_cistatic int hfi1_vnic_sdma_sleep(struct sdma_engine *sde, 18762306a36Sopenharmony_ci struct iowait_work *wait, 18862306a36Sopenharmony_ci struct sdma_txreq *txreq, 18962306a36Sopenharmony_ci uint seq, 19062306a36Sopenharmony_ci bool pkts_sent) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci struct hfi1_vnic_sdma *vnic_sdma = 19362306a36Sopenharmony_ci container_of(wait->iow, struct hfi1_vnic_sdma, wait); 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci write_seqlock(&sde->waitlock); 19662306a36Sopenharmony_ci if (sdma_progress(sde, seq, txreq)) { 19762306a36Sopenharmony_ci write_sequnlock(&sde->waitlock); 19862306a36Sopenharmony_ci return -EAGAIN; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci vnic_sdma->state = HFI1_VNIC_SDMA_Q_DEFERRED; 20262306a36Sopenharmony_ci if (list_empty(&vnic_sdma->wait.list)) { 20362306a36Sopenharmony_ci iowait_get_priority(wait->iow); 20462306a36Sopenharmony_ci iowait_queue(pkts_sent, wait->iow, &sde->dmawait); 20562306a36Sopenharmony_ci } 20662306a36Sopenharmony_ci write_sequnlock(&sde->waitlock); 20762306a36Sopenharmony_ci return -EBUSY; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/* 21162306a36Sopenharmony_ci * hfi1_vnic_sdma_wakeup - vnic sdma wakeup function 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * This function gets called when SDMA descriptors becomes available and Tx 21462306a36Sopenharmony_ci * queue's wait structure was previously added to sdma engine's dmawait list. 21562306a36Sopenharmony_ci * It notifies the upper driver about Tx queue wakeup. 21662306a36Sopenharmony_ci */ 21762306a36Sopenharmony_cistatic void hfi1_vnic_sdma_wakeup(struct iowait *wait, int reason) 21862306a36Sopenharmony_ci{ 21962306a36Sopenharmony_ci struct hfi1_vnic_sdma *vnic_sdma = 22062306a36Sopenharmony_ci container_of(wait, struct hfi1_vnic_sdma, wait); 22162306a36Sopenharmony_ci struct hfi1_vnic_vport_info *vinfo = vnic_sdma->vinfo; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci vnic_sdma->state = HFI1_VNIC_SDMA_Q_ACTIVE; 22462306a36Sopenharmony_ci if (__netif_subqueue_stopped(vinfo->netdev, vnic_sdma->q_idx)) 22562306a36Sopenharmony_ci netif_wake_subqueue(vinfo->netdev, vnic_sdma->q_idx); 22662306a36Sopenharmony_ci}; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ciinline bool hfi1_vnic_sdma_write_avail(struct hfi1_vnic_vport_info *vinfo, 22962306a36Sopenharmony_ci u8 q_idx) 23062306a36Sopenharmony_ci{ 23162306a36Sopenharmony_ci struct hfi1_vnic_sdma *vnic_sdma = &vinfo->sdma[q_idx]; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci return (READ_ONCE(vnic_sdma->state) == HFI1_VNIC_SDMA_Q_ACTIVE); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_civoid hfi1_vnic_sdma_init(struct hfi1_vnic_vport_info *vinfo) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci int i; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci for (i = 0; i < vinfo->num_tx_q; i++) { 24162306a36Sopenharmony_ci struct hfi1_vnic_sdma *vnic_sdma = &vinfo->sdma[i]; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci iowait_init(&vnic_sdma->wait, 0, NULL, NULL, 24462306a36Sopenharmony_ci hfi1_vnic_sdma_sleep, 24562306a36Sopenharmony_ci hfi1_vnic_sdma_wakeup, NULL, NULL); 24662306a36Sopenharmony_ci vnic_sdma->sde = &vinfo->dd->per_sdma[i]; 24762306a36Sopenharmony_ci vnic_sdma->dd = vinfo->dd; 24862306a36Sopenharmony_ci vnic_sdma->vinfo = vinfo; 24962306a36Sopenharmony_ci vnic_sdma->q_idx = i; 25062306a36Sopenharmony_ci vnic_sdma->state = HFI1_VNIC_SDMA_Q_ACTIVE; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci /* Add a free descriptor watermark for wakeups */ 25362306a36Sopenharmony_ci if (vnic_sdma->sde->descq_cnt > HFI1_VNIC_SDMA_DESC_WTRMRK) { 25462306a36Sopenharmony_ci struct iowait_work *work; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci INIT_LIST_HEAD(&vnic_sdma->stx.list); 25762306a36Sopenharmony_ci vnic_sdma->stx.num_desc = HFI1_VNIC_SDMA_DESC_WTRMRK; 25862306a36Sopenharmony_ci work = iowait_get_ib_work(&vnic_sdma->wait); 25962306a36Sopenharmony_ci list_add_tail(&vnic_sdma->stx.list, &work->tx_head); 26062306a36Sopenharmony_ci } 26162306a36Sopenharmony_ci } 26262306a36Sopenharmony_ci} 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ciint hfi1_vnic_txreq_init(struct hfi1_devdata *dd) 26562306a36Sopenharmony_ci{ 26662306a36Sopenharmony_ci char buf[HFI1_VNIC_TXREQ_NAME_LEN]; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "hfi1_%u_vnic_txreq_cache", dd->unit); 26962306a36Sopenharmony_ci dd->vnic.txreq_cache = kmem_cache_create(buf, 27062306a36Sopenharmony_ci sizeof(struct vnic_txreq), 27162306a36Sopenharmony_ci 0, SLAB_HWCACHE_ALIGN, 27262306a36Sopenharmony_ci NULL); 27362306a36Sopenharmony_ci if (!dd->vnic.txreq_cache) 27462306a36Sopenharmony_ci return -ENOMEM; 27562306a36Sopenharmony_ci return 0; 27662306a36Sopenharmony_ci} 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_civoid hfi1_vnic_txreq_deinit(struct hfi1_devdata *dd) 27962306a36Sopenharmony_ci{ 28062306a36Sopenharmony_ci kmem_cache_destroy(dd->vnic.txreq_cache); 28162306a36Sopenharmony_ci dd->vnic.txreq_cache = NULL; 28262306a36Sopenharmony_ci} 283