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