18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*******************************************************************************
38c2ecf20Sopenharmony_ci * SCSI RDMA Protocol lib functions
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006 FUJITA Tomonori <tomof@acm.org>
68c2ecf20Sopenharmony_ci * Copyright (C) 2016 Bryant G. Ly <bryantly@linux.vnet.ibm.com> IBM Corp.
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci ***********************************************************************/
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt)	"libsrp: " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/printk.h>
138c2ecf20Sopenharmony_ci#include <linux/err.h>
148c2ecf20Sopenharmony_ci#include <linux/slab.h>
158c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
168c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
178c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
188c2ecf20Sopenharmony_ci#include <linux/module.h>
198c2ecf20Sopenharmony_ci#include <scsi/srp.h>
208c2ecf20Sopenharmony_ci#include <target/target_core_base.h>
218c2ecf20Sopenharmony_ci#include "libsrp.h"
228c2ecf20Sopenharmony_ci#include "ibmvscsi_tgt.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic int srp_iu_pool_alloc(struct srp_queue *q, size_t max,
258c2ecf20Sopenharmony_ci			     struct srp_buf **ring)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct iu_entry *iue;
288c2ecf20Sopenharmony_ci	int i;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	q->pool = kcalloc(max, sizeof(struct iu_entry *), GFP_KERNEL);
318c2ecf20Sopenharmony_ci	if (!q->pool)
328c2ecf20Sopenharmony_ci		return -ENOMEM;
338c2ecf20Sopenharmony_ci	q->items = kcalloc(max, sizeof(struct iu_entry), GFP_KERNEL);
348c2ecf20Sopenharmony_ci	if (!q->items)
358c2ecf20Sopenharmony_ci		goto free_pool;
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	spin_lock_init(&q->lock);
388c2ecf20Sopenharmony_ci	kfifo_init(&q->queue, (void *)q->pool, max * sizeof(void *));
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	for (i = 0, iue = q->items; i < max; i++) {
418c2ecf20Sopenharmony_ci		kfifo_in(&q->queue, (void *)&iue, sizeof(void *));
428c2ecf20Sopenharmony_ci		iue->sbuf = ring[i];
438c2ecf20Sopenharmony_ci		iue++;
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci	return 0;
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cifree_pool:
488c2ecf20Sopenharmony_ci	kfree(q->pool);
498c2ecf20Sopenharmony_ci	return -ENOMEM;
508c2ecf20Sopenharmony_ci}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_cistatic void srp_iu_pool_free(struct srp_queue *q)
538c2ecf20Sopenharmony_ci{
548c2ecf20Sopenharmony_ci	kfree(q->items);
558c2ecf20Sopenharmony_ci	kfree(q->pool);
568c2ecf20Sopenharmony_ci}
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_cistatic struct srp_buf **srp_ring_alloc(struct device *dev,
598c2ecf20Sopenharmony_ci				       size_t max, size_t size)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct srp_buf **ring;
628c2ecf20Sopenharmony_ci	int i;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	ring = kcalloc(max, sizeof(struct srp_buf *), GFP_KERNEL);
658c2ecf20Sopenharmony_ci	if (!ring)
668c2ecf20Sopenharmony_ci		return NULL;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	for (i = 0; i < max; i++) {
698c2ecf20Sopenharmony_ci		ring[i] = kzalloc(sizeof(*ring[i]), GFP_KERNEL);
708c2ecf20Sopenharmony_ci		if (!ring[i])
718c2ecf20Sopenharmony_ci			goto out;
728c2ecf20Sopenharmony_ci		ring[i]->buf = dma_alloc_coherent(dev, size, &ring[i]->dma,
738c2ecf20Sopenharmony_ci						  GFP_KERNEL);
748c2ecf20Sopenharmony_ci		if (!ring[i]->buf)
758c2ecf20Sopenharmony_ci			goto out;
768c2ecf20Sopenharmony_ci	}
778c2ecf20Sopenharmony_ci	return ring;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ciout:
808c2ecf20Sopenharmony_ci	for (i = 0; i < max && ring[i]; i++) {
818c2ecf20Sopenharmony_ci		if (ring[i]->buf) {
828c2ecf20Sopenharmony_ci			dma_free_coherent(dev, size, ring[i]->buf,
838c2ecf20Sopenharmony_ci					  ring[i]->dma);
848c2ecf20Sopenharmony_ci		}
858c2ecf20Sopenharmony_ci		kfree(ring[i]);
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci	kfree(ring);
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	return NULL;
908c2ecf20Sopenharmony_ci}
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_cistatic void srp_ring_free(struct device *dev, struct srp_buf **ring,
938c2ecf20Sopenharmony_ci			  size_t max, size_t size)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	int i;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	for (i = 0; i < max; i++) {
988c2ecf20Sopenharmony_ci		dma_free_coherent(dev, size, ring[i]->buf, ring[i]->dma);
998c2ecf20Sopenharmony_ci		kfree(ring[i]);
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci	kfree(ring);
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ciint srp_target_alloc(struct srp_target *target, struct device *dev,
1058c2ecf20Sopenharmony_ci		     size_t nr, size_t iu_size)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	int err;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	spin_lock_init(&target->lock);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	target->dev = dev;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	target->srp_iu_size = iu_size;
1148c2ecf20Sopenharmony_ci	target->rx_ring_size = nr;
1158c2ecf20Sopenharmony_ci	target->rx_ring = srp_ring_alloc(target->dev, nr, iu_size);
1168c2ecf20Sopenharmony_ci	if (!target->rx_ring)
1178c2ecf20Sopenharmony_ci		return -ENOMEM;
1188c2ecf20Sopenharmony_ci	err = srp_iu_pool_alloc(&target->iu_queue, nr, target->rx_ring);
1198c2ecf20Sopenharmony_ci	if (err)
1208c2ecf20Sopenharmony_ci		goto free_ring;
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	dev_set_drvdata(target->dev, target);
1238c2ecf20Sopenharmony_ci	return 0;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_cifree_ring:
1268c2ecf20Sopenharmony_ci	srp_ring_free(target->dev, target->rx_ring, nr, iu_size);
1278c2ecf20Sopenharmony_ci	return -ENOMEM;
1288c2ecf20Sopenharmony_ci}
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_civoid srp_target_free(struct srp_target *target)
1318c2ecf20Sopenharmony_ci{
1328c2ecf20Sopenharmony_ci	dev_set_drvdata(target->dev, NULL);
1338c2ecf20Sopenharmony_ci	srp_ring_free(target->dev, target->rx_ring, target->rx_ring_size,
1348c2ecf20Sopenharmony_ci		      target->srp_iu_size);
1358c2ecf20Sopenharmony_ci	srp_iu_pool_free(&target->iu_queue);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistruct iu_entry *srp_iu_get(struct srp_target *target)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct iu_entry *iue = NULL;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	if (kfifo_out_locked(&target->iu_queue.queue, (void *)&iue,
1438c2ecf20Sopenharmony_ci			     sizeof(void *),
1448c2ecf20Sopenharmony_ci			     &target->iu_queue.lock) != sizeof(void *)) {
1458c2ecf20Sopenharmony_ci		WARN_ONCE(1, "unexpected fifo state");
1468c2ecf20Sopenharmony_ci		return NULL;
1478c2ecf20Sopenharmony_ci	}
1488c2ecf20Sopenharmony_ci	if (!iue)
1498c2ecf20Sopenharmony_ci		return iue;
1508c2ecf20Sopenharmony_ci	iue->target = target;
1518c2ecf20Sopenharmony_ci	iue->flags = 0;
1528c2ecf20Sopenharmony_ci	return iue;
1538c2ecf20Sopenharmony_ci}
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_civoid srp_iu_put(struct iu_entry *iue)
1568c2ecf20Sopenharmony_ci{
1578c2ecf20Sopenharmony_ci	kfifo_in_locked(&iue->target->iu_queue.queue, (void *)&iue,
1588c2ecf20Sopenharmony_ci			sizeof(void *), &iue->target->iu_queue.lock);
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_cistatic int srp_direct_data(struct ibmvscsis_cmd *cmd, struct srp_direct_buf *md,
1628c2ecf20Sopenharmony_ci			   enum dma_data_direction dir, srp_rdma_t rdma_io,
1638c2ecf20Sopenharmony_ci			   int dma_map, int ext_desc)
1648c2ecf20Sopenharmony_ci{
1658c2ecf20Sopenharmony_ci	struct iu_entry *iue = NULL;
1668c2ecf20Sopenharmony_ci	struct scatterlist *sg = NULL;
1678c2ecf20Sopenharmony_ci	int err, nsg = 0, len;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (dma_map) {
1708c2ecf20Sopenharmony_ci		iue = cmd->iue;
1718c2ecf20Sopenharmony_ci		sg = cmd->se_cmd.t_data_sg;
1728c2ecf20Sopenharmony_ci		nsg = dma_map_sg(iue->target->dev, sg, cmd->se_cmd.t_data_nents,
1738c2ecf20Sopenharmony_ci				 DMA_BIDIRECTIONAL);
1748c2ecf20Sopenharmony_ci		if (!nsg) {
1758c2ecf20Sopenharmony_ci			pr_err("fail to map %p %d\n", iue,
1768c2ecf20Sopenharmony_ci			       cmd->se_cmd.t_data_nents);
1778c2ecf20Sopenharmony_ci			return 0;
1788c2ecf20Sopenharmony_ci		}
1798c2ecf20Sopenharmony_ci		len = min(cmd->se_cmd.data_length, be32_to_cpu(md->len));
1808c2ecf20Sopenharmony_ci	} else {
1818c2ecf20Sopenharmony_ci		len = be32_to_cpu(md->len);
1828c2ecf20Sopenharmony_ci	}
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	err = rdma_io(cmd, sg, nsg, md, 1, dir, len);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (dma_map)
1878c2ecf20Sopenharmony_ci		dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL);
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	return err;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_cistatic int srp_indirect_data(struct ibmvscsis_cmd *cmd, struct srp_cmd *srp_cmd,
1938c2ecf20Sopenharmony_ci			     struct srp_indirect_buf *id,
1948c2ecf20Sopenharmony_ci			     enum dma_data_direction dir, srp_rdma_t rdma_io,
1958c2ecf20Sopenharmony_ci			     int dma_map, int ext_desc)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	struct iu_entry *iue = NULL;
1988c2ecf20Sopenharmony_ci	struct srp_direct_buf *md = NULL;
1998c2ecf20Sopenharmony_ci	struct scatterlist dummy, *sg = NULL;
2008c2ecf20Sopenharmony_ci	dma_addr_t token = 0;
2018c2ecf20Sopenharmony_ci	int err = 0;
2028c2ecf20Sopenharmony_ci	int nmd, nsg = 0, len;
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_ci	if (dma_map || ext_desc) {
2058c2ecf20Sopenharmony_ci		iue = cmd->iue;
2068c2ecf20Sopenharmony_ci		sg = cmd->se_cmd.t_data_sg;
2078c2ecf20Sopenharmony_ci	}
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	nmd = be32_to_cpu(id->table_desc.len) / sizeof(struct srp_direct_buf);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	if ((dir == DMA_FROM_DEVICE && nmd == srp_cmd->data_in_desc_cnt) ||
2128c2ecf20Sopenharmony_ci	    (dir == DMA_TO_DEVICE && nmd == srp_cmd->data_out_desc_cnt)) {
2138c2ecf20Sopenharmony_ci		md = &id->desc_list[0];
2148c2ecf20Sopenharmony_ci		goto rdma;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	if (ext_desc && dma_map) {
2188c2ecf20Sopenharmony_ci		md = dma_alloc_coherent(iue->target->dev,
2198c2ecf20Sopenharmony_ci					be32_to_cpu(id->table_desc.len),
2208c2ecf20Sopenharmony_ci					&token, GFP_KERNEL);
2218c2ecf20Sopenharmony_ci		if (!md) {
2228c2ecf20Sopenharmony_ci			pr_err("Can't get dma memory %u\n",
2238c2ecf20Sopenharmony_ci			       be32_to_cpu(id->table_desc.len));
2248c2ecf20Sopenharmony_ci			return -ENOMEM;
2258c2ecf20Sopenharmony_ci		}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci		sg_init_one(&dummy, md, be32_to_cpu(id->table_desc.len));
2288c2ecf20Sopenharmony_ci		sg_dma_address(&dummy) = token;
2298c2ecf20Sopenharmony_ci		sg_dma_len(&dummy) = be32_to_cpu(id->table_desc.len);
2308c2ecf20Sopenharmony_ci		err = rdma_io(cmd, &dummy, 1, &id->table_desc, 1, DMA_TO_DEVICE,
2318c2ecf20Sopenharmony_ci			      be32_to_cpu(id->table_desc.len));
2328c2ecf20Sopenharmony_ci		if (err) {
2338c2ecf20Sopenharmony_ci			pr_err("Error copying indirect table %d\n", err);
2348c2ecf20Sopenharmony_ci			goto free_mem;
2358c2ecf20Sopenharmony_ci		}
2368c2ecf20Sopenharmony_ci	} else {
2378c2ecf20Sopenharmony_ci		pr_err("This command uses external indirect buffer\n");
2388c2ecf20Sopenharmony_ci		return -EINVAL;
2398c2ecf20Sopenharmony_ci	}
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_cirdma:
2428c2ecf20Sopenharmony_ci	if (dma_map) {
2438c2ecf20Sopenharmony_ci		nsg = dma_map_sg(iue->target->dev, sg, cmd->se_cmd.t_data_nents,
2448c2ecf20Sopenharmony_ci				 DMA_BIDIRECTIONAL);
2458c2ecf20Sopenharmony_ci		if (!nsg) {
2468c2ecf20Sopenharmony_ci			pr_err("fail to map %p %d\n", iue,
2478c2ecf20Sopenharmony_ci			       cmd->se_cmd.t_data_nents);
2488c2ecf20Sopenharmony_ci			err = -EIO;
2498c2ecf20Sopenharmony_ci			goto free_mem;
2508c2ecf20Sopenharmony_ci		}
2518c2ecf20Sopenharmony_ci		len = min(cmd->se_cmd.data_length, be32_to_cpu(id->len));
2528c2ecf20Sopenharmony_ci	} else {
2538c2ecf20Sopenharmony_ci		len = be32_to_cpu(id->len);
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	err = rdma_io(cmd, sg, nsg, md, nmd, dir, len);
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci	if (dma_map)
2598c2ecf20Sopenharmony_ci		dma_unmap_sg(iue->target->dev, sg, nsg, DMA_BIDIRECTIONAL);
2608c2ecf20Sopenharmony_ci
2618c2ecf20Sopenharmony_cifree_mem:
2628c2ecf20Sopenharmony_ci	if (token && dma_map) {
2638c2ecf20Sopenharmony_ci		dma_free_coherent(iue->target->dev,
2648c2ecf20Sopenharmony_ci				  be32_to_cpu(id->table_desc.len), md, token);
2658c2ecf20Sopenharmony_ci	}
2668c2ecf20Sopenharmony_ci	return err;
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int data_out_desc_size(struct srp_cmd *cmd)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	int size = 0;
2728c2ecf20Sopenharmony_ci	u8 fmt = cmd->buf_fmt >> 4;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	switch (fmt) {
2758c2ecf20Sopenharmony_ci	case SRP_NO_DATA_DESC:
2768c2ecf20Sopenharmony_ci		break;
2778c2ecf20Sopenharmony_ci	case SRP_DATA_DESC_DIRECT:
2788c2ecf20Sopenharmony_ci		size = sizeof(struct srp_direct_buf);
2798c2ecf20Sopenharmony_ci		break;
2808c2ecf20Sopenharmony_ci	case SRP_DATA_DESC_INDIRECT:
2818c2ecf20Sopenharmony_ci		size = sizeof(struct srp_indirect_buf) +
2828c2ecf20Sopenharmony_ci			sizeof(struct srp_direct_buf) * cmd->data_out_desc_cnt;
2838c2ecf20Sopenharmony_ci		break;
2848c2ecf20Sopenharmony_ci	default:
2858c2ecf20Sopenharmony_ci		pr_err("client error. Invalid data_out_format %x\n", fmt);
2868c2ecf20Sopenharmony_ci		break;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	return size;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci/*
2928c2ecf20Sopenharmony_ci * TODO: this can be called multiple times for a single command if it
2938c2ecf20Sopenharmony_ci * has very long data.
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_ciint srp_transfer_data(struct ibmvscsis_cmd *cmd, struct srp_cmd *srp_cmd,
2968c2ecf20Sopenharmony_ci		      srp_rdma_t rdma_io, int dma_map, int ext_desc)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	struct srp_direct_buf *md;
2998c2ecf20Sopenharmony_ci	struct srp_indirect_buf *id;
3008c2ecf20Sopenharmony_ci	enum dma_data_direction dir;
3018c2ecf20Sopenharmony_ci	int offset, err = 0;
3028c2ecf20Sopenharmony_ci	u8 format;
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	if (!cmd->se_cmd.t_data_nents)
3058c2ecf20Sopenharmony_ci		return 0;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	offset = srp_cmd->add_cdb_len & ~3;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	dir = srp_cmd_direction(srp_cmd);
3108c2ecf20Sopenharmony_ci	if (dir == DMA_FROM_DEVICE)
3118c2ecf20Sopenharmony_ci		offset += data_out_desc_size(srp_cmd);
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (dir == DMA_TO_DEVICE)
3148c2ecf20Sopenharmony_ci		format = srp_cmd->buf_fmt >> 4;
3158c2ecf20Sopenharmony_ci	else
3168c2ecf20Sopenharmony_ci		format = srp_cmd->buf_fmt & ((1U << 4) - 1);
3178c2ecf20Sopenharmony_ci
3188c2ecf20Sopenharmony_ci	switch (format) {
3198c2ecf20Sopenharmony_ci	case SRP_NO_DATA_DESC:
3208c2ecf20Sopenharmony_ci		break;
3218c2ecf20Sopenharmony_ci	case SRP_DATA_DESC_DIRECT:
3228c2ecf20Sopenharmony_ci		md = (struct srp_direct_buf *)(srp_cmd->add_data + offset);
3238c2ecf20Sopenharmony_ci		err = srp_direct_data(cmd, md, dir, rdma_io, dma_map, ext_desc);
3248c2ecf20Sopenharmony_ci		break;
3258c2ecf20Sopenharmony_ci	case SRP_DATA_DESC_INDIRECT:
3268c2ecf20Sopenharmony_ci		id = (struct srp_indirect_buf *)(srp_cmd->add_data + offset);
3278c2ecf20Sopenharmony_ci		err = srp_indirect_data(cmd, srp_cmd, id, dir, rdma_io, dma_map,
3288c2ecf20Sopenharmony_ci					ext_desc);
3298c2ecf20Sopenharmony_ci		break;
3308c2ecf20Sopenharmony_ci	default:
3318c2ecf20Sopenharmony_ci		pr_err("Unknown format %d %x\n", dir, format);
3328c2ecf20Sopenharmony_ci		err = -EINVAL;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	return err;
3368c2ecf20Sopenharmony_ci}
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ciu64 srp_data_length(struct srp_cmd *cmd, enum dma_data_direction dir)
3398c2ecf20Sopenharmony_ci{
3408c2ecf20Sopenharmony_ci	struct srp_direct_buf *md;
3418c2ecf20Sopenharmony_ci	struct srp_indirect_buf *id;
3428c2ecf20Sopenharmony_ci	u64 len = 0;
3438c2ecf20Sopenharmony_ci	uint offset = cmd->add_cdb_len & ~3;
3448c2ecf20Sopenharmony_ci	u8 fmt;
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	if (dir == DMA_TO_DEVICE) {
3478c2ecf20Sopenharmony_ci		fmt = cmd->buf_fmt >> 4;
3488c2ecf20Sopenharmony_ci	} else {
3498c2ecf20Sopenharmony_ci		fmt = cmd->buf_fmt & ((1U << 4) - 1);
3508c2ecf20Sopenharmony_ci		offset += data_out_desc_size(cmd);
3518c2ecf20Sopenharmony_ci	}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	switch (fmt) {
3548c2ecf20Sopenharmony_ci	case SRP_NO_DATA_DESC:
3558c2ecf20Sopenharmony_ci		break;
3568c2ecf20Sopenharmony_ci	case SRP_DATA_DESC_DIRECT:
3578c2ecf20Sopenharmony_ci		md = (struct srp_direct_buf *)(cmd->add_data + offset);
3588c2ecf20Sopenharmony_ci		len = be32_to_cpu(md->len);
3598c2ecf20Sopenharmony_ci		break;
3608c2ecf20Sopenharmony_ci	case SRP_DATA_DESC_INDIRECT:
3618c2ecf20Sopenharmony_ci		id = (struct srp_indirect_buf *)(cmd->add_data + offset);
3628c2ecf20Sopenharmony_ci		len = be32_to_cpu(id->len);
3638c2ecf20Sopenharmony_ci		break;
3648c2ecf20Sopenharmony_ci	default:
3658c2ecf20Sopenharmony_ci		pr_err("invalid data format %x\n", fmt);
3668c2ecf20Sopenharmony_ci		break;
3678c2ecf20Sopenharmony_ci	}
3688c2ecf20Sopenharmony_ci	return len;
3698c2ecf20Sopenharmony_ci}
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ciint srp_get_desc_table(struct srp_cmd *srp_cmd, enum dma_data_direction *dir,
3728c2ecf20Sopenharmony_ci		       u64 *data_len)
3738c2ecf20Sopenharmony_ci{
3748c2ecf20Sopenharmony_ci	struct srp_indirect_buf *idb;
3758c2ecf20Sopenharmony_ci	struct srp_direct_buf *db;
3768c2ecf20Sopenharmony_ci	uint add_cdb_offset;
3778c2ecf20Sopenharmony_ci	int rc;
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/*
3808c2ecf20Sopenharmony_ci	 * The pointer computations below will only be compiled correctly
3818c2ecf20Sopenharmony_ci	 * if srp_cmd::add_data is declared as s8*, u8*, s8[] or u8[], so check
3828c2ecf20Sopenharmony_ci	 * whether srp_cmd::add_data has been declared as a byte pointer.
3838c2ecf20Sopenharmony_ci	 */
3848c2ecf20Sopenharmony_ci	BUILD_BUG_ON(!__same_type(srp_cmd->add_data[0], (s8)0)
3858c2ecf20Sopenharmony_ci		     && !__same_type(srp_cmd->add_data[0], (u8)0));
3868c2ecf20Sopenharmony_ci
3878c2ecf20Sopenharmony_ci	BUG_ON(!dir);
3888c2ecf20Sopenharmony_ci	BUG_ON(!data_len);
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	rc = 0;
3918c2ecf20Sopenharmony_ci	*data_len = 0;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	*dir = DMA_NONE;
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (srp_cmd->buf_fmt & 0xf)
3968c2ecf20Sopenharmony_ci		*dir = DMA_FROM_DEVICE;
3978c2ecf20Sopenharmony_ci	else if (srp_cmd->buf_fmt >> 4)
3988c2ecf20Sopenharmony_ci		*dir = DMA_TO_DEVICE;
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	add_cdb_offset = srp_cmd->add_cdb_len & ~3;
4018c2ecf20Sopenharmony_ci	if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_DIRECT) ||
4028c2ecf20Sopenharmony_ci	    ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_DIRECT)) {
4038c2ecf20Sopenharmony_ci		db = (struct srp_direct_buf *)(srp_cmd->add_data
4048c2ecf20Sopenharmony_ci					       + add_cdb_offset);
4058c2ecf20Sopenharmony_ci		*data_len = be32_to_cpu(db->len);
4068c2ecf20Sopenharmony_ci	} else if (((srp_cmd->buf_fmt & 0xf) == SRP_DATA_DESC_INDIRECT) ||
4078c2ecf20Sopenharmony_ci		   ((srp_cmd->buf_fmt >> 4) == SRP_DATA_DESC_INDIRECT)) {
4088c2ecf20Sopenharmony_ci		idb = (struct srp_indirect_buf *)(srp_cmd->add_data
4098c2ecf20Sopenharmony_ci						  + add_cdb_offset);
4108c2ecf20Sopenharmony_ci
4118c2ecf20Sopenharmony_ci		*data_len = be32_to_cpu(idb->len);
4128c2ecf20Sopenharmony_ci	}
4138c2ecf20Sopenharmony_ci	return rc;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("SCSI RDMA Protocol lib functions");
4178c2ecf20Sopenharmony_ciMODULE_AUTHOR("FUJITA Tomonori");
4188c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
419