162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2016 Chelsio Communications, Inc. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci#include "cxgbit.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_cistatic void 962306a36Sopenharmony_cicxgbit_set_one_ppod(struct cxgbi_pagepod *ppod, 1062306a36Sopenharmony_ci struct cxgbi_task_tag_info *ttinfo, 1162306a36Sopenharmony_ci struct scatterlist **sg_pp, unsigned int *sg_off) 1262306a36Sopenharmony_ci{ 1362306a36Sopenharmony_ci struct scatterlist *sg = sg_pp ? *sg_pp : NULL; 1462306a36Sopenharmony_ci unsigned int offset = sg_off ? *sg_off : 0; 1562306a36Sopenharmony_ci dma_addr_t addr = 0UL; 1662306a36Sopenharmony_ci unsigned int len = 0; 1762306a36Sopenharmony_ci int i; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci memcpy(ppod, &ttinfo->hdr, sizeof(struct cxgbi_pagepod_hdr)); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci if (sg) { 2262306a36Sopenharmony_ci addr = sg_dma_address(sg); 2362306a36Sopenharmony_ci len = sg_dma_len(sg); 2462306a36Sopenharmony_ci } 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci for (i = 0; i < PPOD_PAGES_MAX; i++) { 2762306a36Sopenharmony_ci if (sg) { 2862306a36Sopenharmony_ci ppod->addr[i] = cpu_to_be64(addr + offset); 2962306a36Sopenharmony_ci offset += PAGE_SIZE; 3062306a36Sopenharmony_ci if (offset == (len + sg->offset)) { 3162306a36Sopenharmony_ci offset = 0; 3262306a36Sopenharmony_ci sg = sg_next(sg); 3362306a36Sopenharmony_ci if (sg) { 3462306a36Sopenharmony_ci addr = sg_dma_address(sg); 3562306a36Sopenharmony_ci len = sg_dma_len(sg); 3662306a36Sopenharmony_ci } 3762306a36Sopenharmony_ci } 3862306a36Sopenharmony_ci } else { 3962306a36Sopenharmony_ci ppod->addr[i] = 0ULL; 4062306a36Sopenharmony_ci } 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci /* 4462306a36Sopenharmony_ci * the fifth address needs to be repeated in the next ppod, so do 4562306a36Sopenharmony_ci * not move sg 4662306a36Sopenharmony_ci */ 4762306a36Sopenharmony_ci if (sg_pp) { 4862306a36Sopenharmony_ci *sg_pp = sg; 4962306a36Sopenharmony_ci *sg_off = offset; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (offset == len) { 5362306a36Sopenharmony_ci offset = 0; 5462306a36Sopenharmony_ci if (sg) { 5562306a36Sopenharmony_ci sg = sg_next(sg); 5662306a36Sopenharmony_ci if (sg) 5762306a36Sopenharmony_ci addr = sg_dma_address(sg); 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci } 6062306a36Sopenharmony_ci ppod->addr[i] = sg ? cpu_to_be64(addr + offset) : 0ULL; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic struct sk_buff * 6462306a36Sopenharmony_cicxgbit_ppod_init_idata(struct cxgbit_device *cdev, struct cxgbi_ppm *ppm, 6562306a36Sopenharmony_ci unsigned int idx, unsigned int npods, unsigned int tid) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci struct ulp_mem_io *req; 6862306a36Sopenharmony_ci struct ulptx_idata *idata; 6962306a36Sopenharmony_ci unsigned int pm_addr = (idx << PPOD_SIZE_SHIFT) + ppm->llimit; 7062306a36Sopenharmony_ci unsigned int dlen = npods << PPOD_SIZE_SHIFT; 7162306a36Sopenharmony_ci unsigned int wr_len = roundup(sizeof(struct ulp_mem_io) + 7262306a36Sopenharmony_ci sizeof(struct ulptx_idata) + dlen, 16); 7362306a36Sopenharmony_ci struct sk_buff *skb; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci skb = alloc_skb(wr_len, GFP_KERNEL); 7662306a36Sopenharmony_ci if (!skb) 7762306a36Sopenharmony_ci return NULL; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci req = __skb_put(skb, wr_len); 8062306a36Sopenharmony_ci INIT_ULPTX_WR(req, wr_len, 0, tid); 8162306a36Sopenharmony_ci req->wr.wr_hi = htonl(FW_WR_OP_V(FW_ULPTX_WR) | 8262306a36Sopenharmony_ci FW_WR_ATOMIC_V(0)); 8362306a36Sopenharmony_ci req->cmd = htonl(ULPTX_CMD_V(ULP_TX_MEM_WRITE) | 8462306a36Sopenharmony_ci ULP_MEMIO_ORDER_V(0) | 8562306a36Sopenharmony_ci T5_ULP_MEMIO_IMM_V(1)); 8662306a36Sopenharmony_ci req->dlen = htonl(ULP_MEMIO_DATA_LEN_V(dlen >> 5)); 8762306a36Sopenharmony_ci req->lock_addr = htonl(ULP_MEMIO_ADDR_V(pm_addr >> 5)); 8862306a36Sopenharmony_ci req->len16 = htonl(DIV_ROUND_UP(wr_len - sizeof(req->wr), 16)); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci idata = (struct ulptx_idata *)(req + 1); 9162306a36Sopenharmony_ci idata->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM)); 9262306a36Sopenharmony_ci idata->len = htonl(dlen); 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci return skb; 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic int 9862306a36Sopenharmony_cicxgbit_ppod_write_idata(struct cxgbi_ppm *ppm, struct cxgbit_sock *csk, 9962306a36Sopenharmony_ci struct cxgbi_task_tag_info *ttinfo, unsigned int idx, 10062306a36Sopenharmony_ci unsigned int npods, struct scatterlist **sg_pp, 10162306a36Sopenharmony_ci unsigned int *sg_off) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci struct cxgbit_device *cdev = csk->com.cdev; 10462306a36Sopenharmony_ci struct sk_buff *skb; 10562306a36Sopenharmony_ci struct ulp_mem_io *req; 10662306a36Sopenharmony_ci struct ulptx_idata *idata; 10762306a36Sopenharmony_ci struct cxgbi_pagepod *ppod; 10862306a36Sopenharmony_ci unsigned int i; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci skb = cxgbit_ppod_init_idata(cdev, ppm, idx, npods, csk->tid); 11162306a36Sopenharmony_ci if (!skb) 11262306a36Sopenharmony_ci return -ENOMEM; 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci req = (struct ulp_mem_io *)skb->data; 11562306a36Sopenharmony_ci idata = (struct ulptx_idata *)(req + 1); 11662306a36Sopenharmony_ci ppod = (struct cxgbi_pagepod *)(idata + 1); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci for (i = 0; i < npods; i++, ppod++) 11962306a36Sopenharmony_ci cxgbit_set_one_ppod(ppod, ttinfo, sg_pp, sg_off); 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci __skb_queue_tail(&csk->ppodq, skb); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return 0; 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic int 12762306a36Sopenharmony_cicxgbit_ddp_set_map(struct cxgbi_ppm *ppm, struct cxgbit_sock *csk, 12862306a36Sopenharmony_ci struct cxgbi_task_tag_info *ttinfo) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci unsigned int pidx = ttinfo->idx; 13162306a36Sopenharmony_ci unsigned int npods = ttinfo->npods; 13262306a36Sopenharmony_ci unsigned int i, cnt; 13362306a36Sopenharmony_ci struct scatterlist *sg = ttinfo->sgl; 13462306a36Sopenharmony_ci unsigned int offset = 0; 13562306a36Sopenharmony_ci int ret = 0; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci for (i = 0; i < npods; i += cnt, pidx += cnt) { 13862306a36Sopenharmony_ci cnt = npods - i; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci if (cnt > ULPMEM_IDATA_MAX_NPPODS) 14162306a36Sopenharmony_ci cnt = ULPMEM_IDATA_MAX_NPPODS; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci ret = cxgbit_ppod_write_idata(ppm, csk, ttinfo, pidx, cnt, 14462306a36Sopenharmony_ci &sg, &offset); 14562306a36Sopenharmony_ci if (ret < 0) 14662306a36Sopenharmony_ci break; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return ret; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic int cxgbit_ddp_sgl_check(struct scatterlist *sg, 15362306a36Sopenharmony_ci unsigned int nents) 15462306a36Sopenharmony_ci{ 15562306a36Sopenharmony_ci unsigned int last_sgidx = nents - 1; 15662306a36Sopenharmony_ci unsigned int i; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci for (i = 0; i < nents; i++, sg = sg_next(sg)) { 15962306a36Sopenharmony_ci unsigned int len = sg->length + sg->offset; 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci if ((sg->offset & 0x3) || (i && sg->offset) || 16262306a36Sopenharmony_ci ((i != last_sgidx) && (len != PAGE_SIZE))) { 16362306a36Sopenharmony_ci return -EINVAL; 16462306a36Sopenharmony_ci } 16562306a36Sopenharmony_ci } 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci return 0; 16862306a36Sopenharmony_ci} 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int 17162306a36Sopenharmony_cicxgbit_ddp_reserve(struct cxgbit_sock *csk, struct cxgbi_task_tag_info *ttinfo, 17262306a36Sopenharmony_ci unsigned int xferlen) 17362306a36Sopenharmony_ci{ 17462306a36Sopenharmony_ci struct cxgbit_device *cdev = csk->com.cdev; 17562306a36Sopenharmony_ci struct cxgbi_ppm *ppm = cdev2ppm(cdev); 17662306a36Sopenharmony_ci struct scatterlist *sgl = ttinfo->sgl; 17762306a36Sopenharmony_ci unsigned int sgcnt = ttinfo->nents; 17862306a36Sopenharmony_ci unsigned int sg_offset = sgl->offset; 17962306a36Sopenharmony_ci int ret; 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci if ((xferlen < DDP_THRESHOLD) || (!sgcnt)) { 18262306a36Sopenharmony_ci pr_debug("ppm 0x%p, pgidx %u, xfer %u, sgcnt %u, NO ddp.\n", 18362306a36Sopenharmony_ci ppm, ppm->tformat.pgsz_idx_dflt, 18462306a36Sopenharmony_ci xferlen, ttinfo->nents); 18562306a36Sopenharmony_ci return -EINVAL; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci if (cxgbit_ddp_sgl_check(sgl, sgcnt) < 0) 18962306a36Sopenharmony_ci return -EINVAL; 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci ttinfo->nr_pages = (xferlen + sgl->offset + 19262306a36Sopenharmony_ci (1 << PAGE_SHIFT) - 1) >> PAGE_SHIFT; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* 19562306a36Sopenharmony_ci * the ddp tag will be used for the ttt in the outgoing r2t pdu 19662306a36Sopenharmony_ci */ 19762306a36Sopenharmony_ci ret = cxgbi_ppm_ppods_reserve(ppm, ttinfo->nr_pages, 0, &ttinfo->idx, 19862306a36Sopenharmony_ci &ttinfo->tag, 0); 19962306a36Sopenharmony_ci if (ret < 0) 20062306a36Sopenharmony_ci return ret; 20162306a36Sopenharmony_ci ttinfo->npods = ret; 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci sgl->offset = 0; 20462306a36Sopenharmony_ci ret = dma_map_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE); 20562306a36Sopenharmony_ci sgl->offset = sg_offset; 20662306a36Sopenharmony_ci if (!ret) { 20762306a36Sopenharmony_ci pr_debug("%s: 0x%x, xfer %u, sgl %u dma mapping err.\n", 20862306a36Sopenharmony_ci __func__, 0, xferlen, sgcnt); 20962306a36Sopenharmony_ci goto rel_ppods; 21062306a36Sopenharmony_ci } 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci cxgbi_ppm_make_ppod_hdr(ppm, ttinfo->tag, csk->tid, sgl->offset, 21362306a36Sopenharmony_ci xferlen, &ttinfo->hdr); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci ret = cxgbit_ddp_set_map(ppm, csk, ttinfo); 21662306a36Sopenharmony_ci if (ret < 0) { 21762306a36Sopenharmony_ci __skb_queue_purge(&csk->ppodq); 21862306a36Sopenharmony_ci dma_unmap_sg(&ppm->pdev->dev, sgl, sgcnt, DMA_FROM_DEVICE); 21962306a36Sopenharmony_ci goto rel_ppods; 22062306a36Sopenharmony_ci } 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_cirel_ppods: 22562306a36Sopenharmony_ci cxgbi_ppm_ppod_release(ppm, ttinfo->idx); 22662306a36Sopenharmony_ci return -EINVAL; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_civoid 23062306a36Sopenharmony_cicxgbit_get_r2t_ttt(struct iscsit_conn *conn, struct iscsit_cmd *cmd, 23162306a36Sopenharmony_ci struct iscsi_r2t *r2t) 23262306a36Sopenharmony_ci{ 23362306a36Sopenharmony_ci struct cxgbit_sock *csk = conn->context; 23462306a36Sopenharmony_ci struct cxgbit_device *cdev = csk->com.cdev; 23562306a36Sopenharmony_ci struct cxgbit_cmd *ccmd = iscsit_priv_cmd(cmd); 23662306a36Sopenharmony_ci struct cxgbi_task_tag_info *ttinfo = &ccmd->ttinfo; 23762306a36Sopenharmony_ci int ret; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci if ((!ccmd->setup_ddp) || 24062306a36Sopenharmony_ci (!test_bit(CSK_DDP_ENABLE, &csk->com.flags))) 24162306a36Sopenharmony_ci goto out; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci ccmd->setup_ddp = false; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci ttinfo->sgl = cmd->se_cmd.t_data_sg; 24662306a36Sopenharmony_ci ttinfo->nents = cmd->se_cmd.t_data_nents; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci ret = cxgbit_ddp_reserve(csk, ttinfo, cmd->se_cmd.data_length); 24962306a36Sopenharmony_ci if (ret < 0) { 25062306a36Sopenharmony_ci pr_debug("csk 0x%p, cmd 0x%p, xfer len %u, sgcnt %u no ddp.\n", 25162306a36Sopenharmony_ci csk, cmd, cmd->se_cmd.data_length, ttinfo->nents); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci ttinfo->sgl = NULL; 25462306a36Sopenharmony_ci ttinfo->nents = 0; 25562306a36Sopenharmony_ci } else { 25662306a36Sopenharmony_ci ccmd->release = true; 25762306a36Sopenharmony_ci } 25862306a36Sopenharmony_ciout: 25962306a36Sopenharmony_ci pr_debug("cdev 0x%p, cmd 0x%p, tag 0x%x\n", cdev, cmd, ttinfo->tag); 26062306a36Sopenharmony_ci r2t->targ_xfer_tag = ttinfo->tag; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_civoid cxgbit_unmap_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd) 26462306a36Sopenharmony_ci{ 26562306a36Sopenharmony_ci struct cxgbit_cmd *ccmd = iscsit_priv_cmd(cmd); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (ccmd->release) { 26862306a36Sopenharmony_ci if (cmd->se_cmd.se_cmd_flags & SCF_PASSTHROUGH_SG_TO_MEM_NOALLOC) { 26962306a36Sopenharmony_ci put_page(sg_page(&ccmd->sg)); 27062306a36Sopenharmony_ci } else { 27162306a36Sopenharmony_ci struct cxgbit_sock *csk = conn->context; 27262306a36Sopenharmony_ci struct cxgbit_device *cdev = csk->com.cdev; 27362306a36Sopenharmony_ci struct cxgbi_ppm *ppm = cdev2ppm(cdev); 27462306a36Sopenharmony_ci struct cxgbi_task_tag_info *ttinfo = &ccmd->ttinfo; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci /* Abort the TCP conn if DDP is not complete to 27762306a36Sopenharmony_ci * avoid any possibility of DDP after freeing 27862306a36Sopenharmony_ci * the cmd. 27962306a36Sopenharmony_ci */ 28062306a36Sopenharmony_ci if (unlikely(cmd->write_data_done != 28162306a36Sopenharmony_ci cmd->se_cmd.data_length)) 28262306a36Sopenharmony_ci cxgbit_abort_conn(csk); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci if (unlikely(ttinfo->sgl)) { 28562306a36Sopenharmony_ci dma_unmap_sg(&ppm->pdev->dev, ttinfo->sgl, 28662306a36Sopenharmony_ci ttinfo->nents, DMA_FROM_DEVICE); 28762306a36Sopenharmony_ci ttinfo->nents = 0; 28862306a36Sopenharmony_ci ttinfo->sgl = NULL; 28962306a36Sopenharmony_ci } 29062306a36Sopenharmony_ci cxgbi_ppm_ppod_release(ppm, ttinfo->idx); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci ccmd->release = false; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci} 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ciint cxgbit_ddp_init(struct cxgbit_device *cdev) 29762306a36Sopenharmony_ci{ 29862306a36Sopenharmony_ci struct cxgb4_lld_info *lldi = &cdev->lldi; 29962306a36Sopenharmony_ci struct net_device *ndev = cdev->lldi.ports[0]; 30062306a36Sopenharmony_ci struct cxgbi_tag_format tformat; 30162306a36Sopenharmony_ci int ret, i; 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci if (!lldi->vr->iscsi.size) { 30462306a36Sopenharmony_ci pr_warn("%s, iscsi NOT enabled, check config!\n", ndev->name); 30562306a36Sopenharmony_ci return -EACCES; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci memset(&tformat, 0, sizeof(struct cxgbi_tag_format)); 30962306a36Sopenharmony_ci for (i = 0; i < 4; i++) 31062306a36Sopenharmony_ci tformat.pgsz_order[i] = (lldi->iscsi_pgsz_order >> (i << 3)) 31162306a36Sopenharmony_ci & 0xF; 31262306a36Sopenharmony_ci cxgbi_tagmask_check(lldi->iscsi_tagmask, &tformat); 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci ret = cxgbi_ppm_init(lldi->iscsi_ppm, cdev->lldi.ports[0], 31562306a36Sopenharmony_ci cdev->lldi.pdev, &cdev->lldi, &tformat, 31662306a36Sopenharmony_ci lldi->vr->iscsi.size, lldi->iscsi_llimit, 31762306a36Sopenharmony_ci lldi->vr->iscsi.start, 2, 31862306a36Sopenharmony_ci lldi->vr->ppod_edram.start, 31962306a36Sopenharmony_ci lldi->vr->ppod_edram.size); 32062306a36Sopenharmony_ci if (ret >= 0) { 32162306a36Sopenharmony_ci struct cxgbi_ppm *ppm = (struct cxgbi_ppm *)(*lldi->iscsi_ppm); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if ((ppm->tformat.pgsz_idx_dflt < DDP_PGIDX_MAX) && 32462306a36Sopenharmony_ci (ppm->ppmax >= 1024)) 32562306a36Sopenharmony_ci set_bit(CDEV_DDP_ENABLE, &cdev->flags); 32662306a36Sopenharmony_ci ret = 0; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_ci return ret; 33062306a36Sopenharmony_ci} 331