18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
38c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Mellanox Technologies. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two
68c2ecf20Sopenharmony_ci * licenses.  You may choose to be licensed under the terms of the GNU
78c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file
88c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the
98c2ecf20Sopenharmony_ci * OpenIB.org BSD license below:
108c2ecf20Sopenharmony_ci *
118c2ecf20Sopenharmony_ci *     Redistribution and use in source and binary forms, with or
128c2ecf20Sopenharmony_ci *     without modification, are permitted provided that the following
138c2ecf20Sopenharmony_ci *     conditions are met:
148c2ecf20Sopenharmony_ci *
158c2ecf20Sopenharmony_ci *	- Redistributions of source code must retain the above
168c2ecf20Sopenharmony_ci *	  copyright notice, this list of conditions and the following
178c2ecf20Sopenharmony_ci *	  disclaimer.
188c2ecf20Sopenharmony_ci *
198c2ecf20Sopenharmony_ci *	- Redistributions in binary form must reproduce the above
208c2ecf20Sopenharmony_ci *	  copyright notice, this list of conditions and the following
218c2ecf20Sopenharmony_ci *	  disclaimer in the documentation and/or other materials
228c2ecf20Sopenharmony_ci *	  provided with the distribution.
238c2ecf20Sopenharmony_ci *
248c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
258c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
268c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
278c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
288c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
298c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
308c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
318c2ecf20Sopenharmony_ci * SOFTWARE.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_ci#include <linux/kernel.h>
348c2ecf20Sopenharmony_ci#include <linux/slab.h>
358c2ecf20Sopenharmony_ci#include <linux/mm.h>
368c2ecf20Sopenharmony_ci#include <linux/scatterlist.h>
378c2ecf20Sopenharmony_ci#include <linux/kfifo.h>
388c2ecf20Sopenharmony_ci#include <scsi/scsi_cmnd.h>
398c2ecf20Sopenharmony_ci#include <scsi/scsi_host.h>
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci#include "iscsi_iser.h"
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci/* Register user buffer memory and initialize passive rdma
448c2ecf20Sopenharmony_ci *  dto descriptor. Data size is stored in
458c2ecf20Sopenharmony_ci *  task->data[ISER_DIR_IN].data_len, Protection size
468c2ecf20Sopenharmony_ci *  os stored in task->prot[ISER_DIR_IN].data_len
478c2ecf20Sopenharmony_ci */
488c2ecf20Sopenharmony_cistatic int iser_prepare_read_cmd(struct iscsi_task *task)
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	struct iscsi_iser_task *iser_task = task->dd_data;
528c2ecf20Sopenharmony_ci	struct iser_mem_reg *mem_reg;
538c2ecf20Sopenharmony_ci	int err;
548c2ecf20Sopenharmony_ci	struct iser_ctrl *hdr = &iser_task->desc.iser_header;
558c2ecf20Sopenharmony_ci	struct iser_data_buf *buf_in = &iser_task->data[ISER_DIR_IN];
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	err = iser_dma_map_task_data(iser_task,
588c2ecf20Sopenharmony_ci				     buf_in,
598c2ecf20Sopenharmony_ci				     ISER_DIR_IN,
608c2ecf20Sopenharmony_ci				     DMA_FROM_DEVICE);
618c2ecf20Sopenharmony_ci	if (err)
628c2ecf20Sopenharmony_ci		return err;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	if (scsi_prot_sg_count(iser_task->sc)) {
658c2ecf20Sopenharmony_ci		struct iser_data_buf *pbuf_in = &iser_task->prot[ISER_DIR_IN];
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci		err = iser_dma_map_task_data(iser_task,
688c2ecf20Sopenharmony_ci					     pbuf_in,
698c2ecf20Sopenharmony_ci					     ISER_DIR_IN,
708c2ecf20Sopenharmony_ci					     DMA_FROM_DEVICE);
718c2ecf20Sopenharmony_ci		if (err)
728c2ecf20Sopenharmony_ci			return err;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	err = iser_reg_mem_fastreg(iser_task, ISER_DIR_IN, false);
768c2ecf20Sopenharmony_ci	if (err) {
778c2ecf20Sopenharmony_ci		iser_err("Failed to set up Data-IN RDMA\n");
788c2ecf20Sopenharmony_ci		return err;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci	mem_reg = &iser_task->rdma_reg[ISER_DIR_IN];
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	hdr->flags    |= ISER_RSV;
838c2ecf20Sopenharmony_ci	hdr->read_stag = cpu_to_be32(mem_reg->rkey);
848c2ecf20Sopenharmony_ci	hdr->read_va   = cpu_to_be64(mem_reg->sge.addr);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n",
878c2ecf20Sopenharmony_ci		 task->itt, mem_reg->rkey,
888c2ecf20Sopenharmony_ci		 (unsigned long long)mem_reg->sge.addr);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	return 0;
918c2ecf20Sopenharmony_ci}
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci/* Register user buffer memory and initialize passive rdma
948c2ecf20Sopenharmony_ci *  dto descriptor. Data size is stored in
958c2ecf20Sopenharmony_ci *  task->data[ISER_DIR_OUT].data_len, Protection size
968c2ecf20Sopenharmony_ci *  is stored at task->prot[ISER_DIR_OUT].data_len
978c2ecf20Sopenharmony_ci */
988c2ecf20Sopenharmony_cistatic int
998c2ecf20Sopenharmony_ciiser_prepare_write_cmd(struct iscsi_task *task,
1008c2ecf20Sopenharmony_ci		       unsigned int imm_sz,
1018c2ecf20Sopenharmony_ci		       unsigned int unsol_sz,
1028c2ecf20Sopenharmony_ci		       unsigned int edtl)
1038c2ecf20Sopenharmony_ci{
1048c2ecf20Sopenharmony_ci	struct iscsi_iser_task *iser_task = task->dd_data;
1058c2ecf20Sopenharmony_ci	struct iser_mem_reg *mem_reg;
1068c2ecf20Sopenharmony_ci	int err;
1078c2ecf20Sopenharmony_ci	struct iser_ctrl *hdr = &iser_task->desc.iser_header;
1088c2ecf20Sopenharmony_ci	struct iser_data_buf *buf_out = &iser_task->data[ISER_DIR_OUT];
1098c2ecf20Sopenharmony_ci	struct ib_sge *tx_dsg = &iser_task->desc.tx_sg[1];
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	err = iser_dma_map_task_data(iser_task,
1128c2ecf20Sopenharmony_ci				     buf_out,
1138c2ecf20Sopenharmony_ci				     ISER_DIR_OUT,
1148c2ecf20Sopenharmony_ci				     DMA_TO_DEVICE);
1158c2ecf20Sopenharmony_ci	if (err)
1168c2ecf20Sopenharmony_ci		return err;
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci	if (scsi_prot_sg_count(iser_task->sc)) {
1198c2ecf20Sopenharmony_ci		struct iser_data_buf *pbuf_out = &iser_task->prot[ISER_DIR_OUT];
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci		err = iser_dma_map_task_data(iser_task,
1228c2ecf20Sopenharmony_ci					     pbuf_out,
1238c2ecf20Sopenharmony_ci					     ISER_DIR_OUT,
1248c2ecf20Sopenharmony_ci					     DMA_TO_DEVICE);
1258c2ecf20Sopenharmony_ci		if (err)
1268c2ecf20Sopenharmony_ci			return err;
1278c2ecf20Sopenharmony_ci	}
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	err = iser_reg_mem_fastreg(iser_task, ISER_DIR_OUT,
1308c2ecf20Sopenharmony_ci				   buf_out->data_len == imm_sz);
1318c2ecf20Sopenharmony_ci	if (err != 0) {
1328c2ecf20Sopenharmony_ci		iser_err("Failed to register write cmd RDMA mem\n");
1338c2ecf20Sopenharmony_ci		return err;
1348c2ecf20Sopenharmony_ci	}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci	mem_reg = &iser_task->rdma_reg[ISER_DIR_OUT];
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci	if (unsol_sz < edtl) {
1398c2ecf20Sopenharmony_ci		hdr->flags     |= ISER_WSV;
1408c2ecf20Sopenharmony_ci		if (buf_out->data_len > imm_sz) {
1418c2ecf20Sopenharmony_ci			hdr->write_stag = cpu_to_be32(mem_reg->rkey);
1428c2ecf20Sopenharmony_ci			hdr->write_va = cpu_to_be64(mem_reg->sge.addr + unsol_sz);
1438c2ecf20Sopenharmony_ci		}
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci		iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X VA:%#llX + unsol:%d\n",
1468c2ecf20Sopenharmony_ci			 task->itt, mem_reg->rkey,
1478c2ecf20Sopenharmony_ci			 (unsigned long long)mem_reg->sge.addr, unsol_sz);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	if (imm_sz > 0) {
1518c2ecf20Sopenharmony_ci		iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n",
1528c2ecf20Sopenharmony_ci			 task->itt, imm_sz);
1538c2ecf20Sopenharmony_ci		tx_dsg->addr = mem_reg->sge.addr;
1548c2ecf20Sopenharmony_ci		tx_dsg->length = imm_sz;
1558c2ecf20Sopenharmony_ci		tx_dsg->lkey = mem_reg->sge.lkey;
1568c2ecf20Sopenharmony_ci		iser_task->desc.num_sge = 2;
1578c2ecf20Sopenharmony_ci	}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_ci/* creates a new tx descriptor and adds header regd buffer */
1638c2ecf20Sopenharmony_cistatic void iser_create_send_desc(struct iser_conn	*iser_conn,
1648c2ecf20Sopenharmony_ci				  struct iser_tx_desc	*tx_desc)
1658c2ecf20Sopenharmony_ci{
1668c2ecf20Sopenharmony_ci	struct iser_device *device = iser_conn->ib_conn.device;
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	ib_dma_sync_single_for_cpu(device->ib_device,
1698c2ecf20Sopenharmony_ci		tx_desc->dma_addr, ISER_HEADERS_LEN, DMA_TO_DEVICE);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	memset(&tx_desc->iser_header, 0, sizeof(struct iser_ctrl));
1728c2ecf20Sopenharmony_ci	tx_desc->iser_header.flags = ISER_VER;
1738c2ecf20Sopenharmony_ci	tx_desc->num_sge = 1;
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void iser_free_login_buf(struct iser_conn *iser_conn)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct iser_device *device = iser_conn->ib_conn.device;
1798c2ecf20Sopenharmony_ci	struct iser_login_desc *desc = &iser_conn->login_desc;
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci	if (!desc->req)
1828c2ecf20Sopenharmony_ci		return;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	ib_dma_unmap_single(device->ib_device, desc->req_dma,
1858c2ecf20Sopenharmony_ci			    ISCSI_DEF_MAX_RECV_SEG_LEN, DMA_TO_DEVICE);
1868c2ecf20Sopenharmony_ci
1878c2ecf20Sopenharmony_ci	ib_dma_unmap_single(device->ib_device, desc->rsp_dma,
1888c2ecf20Sopenharmony_ci			    ISER_RX_LOGIN_SIZE, DMA_FROM_DEVICE);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	kfree(desc->req);
1918c2ecf20Sopenharmony_ci	kfree(desc->rsp);
1928c2ecf20Sopenharmony_ci
1938c2ecf20Sopenharmony_ci	/* make sure we never redo any unmapping */
1948c2ecf20Sopenharmony_ci	desc->req = NULL;
1958c2ecf20Sopenharmony_ci	desc->rsp = NULL;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic int iser_alloc_login_buf(struct iser_conn *iser_conn)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	struct iser_device *device = iser_conn->ib_conn.device;
2018c2ecf20Sopenharmony_ci	struct iser_login_desc *desc = &iser_conn->login_desc;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	desc->req = kmalloc(ISCSI_DEF_MAX_RECV_SEG_LEN, GFP_KERNEL);
2048c2ecf20Sopenharmony_ci	if (!desc->req)
2058c2ecf20Sopenharmony_ci		return -ENOMEM;
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	desc->req_dma = ib_dma_map_single(device->ib_device, desc->req,
2088c2ecf20Sopenharmony_ci					  ISCSI_DEF_MAX_RECV_SEG_LEN,
2098c2ecf20Sopenharmony_ci					  DMA_TO_DEVICE);
2108c2ecf20Sopenharmony_ci	if (ib_dma_mapping_error(device->ib_device,
2118c2ecf20Sopenharmony_ci				desc->req_dma))
2128c2ecf20Sopenharmony_ci		goto free_req;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	desc->rsp = kmalloc(ISER_RX_LOGIN_SIZE, GFP_KERNEL);
2158c2ecf20Sopenharmony_ci	if (!desc->rsp)
2168c2ecf20Sopenharmony_ci		goto unmap_req;
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci	desc->rsp_dma = ib_dma_map_single(device->ib_device, desc->rsp,
2198c2ecf20Sopenharmony_ci					   ISER_RX_LOGIN_SIZE,
2208c2ecf20Sopenharmony_ci					   DMA_FROM_DEVICE);
2218c2ecf20Sopenharmony_ci	if (ib_dma_mapping_error(device->ib_device,
2228c2ecf20Sopenharmony_ci				desc->rsp_dma))
2238c2ecf20Sopenharmony_ci		goto free_rsp;
2248c2ecf20Sopenharmony_ci
2258c2ecf20Sopenharmony_ci	return 0;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_cifree_rsp:
2288c2ecf20Sopenharmony_ci	kfree(desc->rsp);
2298c2ecf20Sopenharmony_ciunmap_req:
2308c2ecf20Sopenharmony_ci	ib_dma_unmap_single(device->ib_device, desc->req_dma,
2318c2ecf20Sopenharmony_ci			    ISCSI_DEF_MAX_RECV_SEG_LEN,
2328c2ecf20Sopenharmony_ci			    DMA_TO_DEVICE);
2338c2ecf20Sopenharmony_cifree_req:
2348c2ecf20Sopenharmony_ci	kfree(desc->req);
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	return -ENOMEM;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ciint iser_alloc_rx_descriptors(struct iser_conn *iser_conn,
2408c2ecf20Sopenharmony_ci			      struct iscsi_session *session)
2418c2ecf20Sopenharmony_ci{
2428c2ecf20Sopenharmony_ci	int i, j;
2438c2ecf20Sopenharmony_ci	u64 dma_addr;
2448c2ecf20Sopenharmony_ci	struct iser_rx_desc *rx_desc;
2458c2ecf20Sopenharmony_ci	struct ib_sge       *rx_sg;
2468c2ecf20Sopenharmony_ci	struct ib_conn *ib_conn = &iser_conn->ib_conn;
2478c2ecf20Sopenharmony_ci	struct iser_device *device = ib_conn->device;
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci	iser_conn->qp_max_recv_dtos = session->cmds_max;
2508c2ecf20Sopenharmony_ci	iser_conn->qp_max_recv_dtos_mask = session->cmds_max - 1; /* cmds_max is 2^N */
2518c2ecf20Sopenharmony_ci	iser_conn->min_posted_rx = iser_conn->qp_max_recv_dtos >> 2;
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci	if (iser_alloc_fastreg_pool(ib_conn, session->scsi_cmds_max,
2548c2ecf20Sopenharmony_ci				    iser_conn->pages_per_mr))
2558c2ecf20Sopenharmony_ci		goto create_rdma_reg_res_failed;
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	if (iser_alloc_login_buf(iser_conn))
2588c2ecf20Sopenharmony_ci		goto alloc_login_buf_fail;
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	iser_conn->num_rx_descs = session->cmds_max;
2618c2ecf20Sopenharmony_ci	iser_conn->rx_descs = kmalloc_array(iser_conn->num_rx_descs,
2628c2ecf20Sopenharmony_ci					    sizeof(struct iser_rx_desc),
2638c2ecf20Sopenharmony_ci					    GFP_KERNEL);
2648c2ecf20Sopenharmony_ci	if (!iser_conn->rx_descs)
2658c2ecf20Sopenharmony_ci		goto rx_desc_alloc_fail;
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_ci	rx_desc = iser_conn->rx_descs;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci	for (i = 0; i < iser_conn->qp_max_recv_dtos; i++, rx_desc++)  {
2708c2ecf20Sopenharmony_ci		dma_addr = ib_dma_map_single(device->ib_device, (void *)rx_desc,
2718c2ecf20Sopenharmony_ci					ISER_RX_PAYLOAD_SIZE, DMA_FROM_DEVICE);
2728c2ecf20Sopenharmony_ci		if (ib_dma_mapping_error(device->ib_device, dma_addr))
2738c2ecf20Sopenharmony_ci			goto rx_desc_dma_map_failed;
2748c2ecf20Sopenharmony_ci
2758c2ecf20Sopenharmony_ci		rx_desc->dma_addr = dma_addr;
2768c2ecf20Sopenharmony_ci		rx_desc->cqe.done = iser_task_rsp;
2778c2ecf20Sopenharmony_ci		rx_sg = &rx_desc->rx_sg;
2788c2ecf20Sopenharmony_ci		rx_sg->addr = rx_desc->dma_addr;
2798c2ecf20Sopenharmony_ci		rx_sg->length = ISER_RX_PAYLOAD_SIZE;
2808c2ecf20Sopenharmony_ci		rx_sg->lkey = device->pd->local_dma_lkey;
2818c2ecf20Sopenharmony_ci	}
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci	iser_conn->rx_desc_head = 0;
2848c2ecf20Sopenharmony_ci	return 0;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cirx_desc_dma_map_failed:
2878c2ecf20Sopenharmony_ci	rx_desc = iser_conn->rx_descs;
2888c2ecf20Sopenharmony_ci	for (j = 0; j < i; j++, rx_desc++)
2898c2ecf20Sopenharmony_ci		ib_dma_unmap_single(device->ib_device, rx_desc->dma_addr,
2908c2ecf20Sopenharmony_ci				    ISER_RX_PAYLOAD_SIZE, DMA_FROM_DEVICE);
2918c2ecf20Sopenharmony_ci	kfree(iser_conn->rx_descs);
2928c2ecf20Sopenharmony_ci	iser_conn->rx_descs = NULL;
2938c2ecf20Sopenharmony_cirx_desc_alloc_fail:
2948c2ecf20Sopenharmony_ci	iser_free_login_buf(iser_conn);
2958c2ecf20Sopenharmony_cialloc_login_buf_fail:
2968c2ecf20Sopenharmony_ci	iser_free_fastreg_pool(ib_conn);
2978c2ecf20Sopenharmony_cicreate_rdma_reg_res_failed:
2988c2ecf20Sopenharmony_ci	iser_err("failed allocating rx descriptors / data buffers\n");
2998c2ecf20Sopenharmony_ci	return -ENOMEM;
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_civoid iser_free_rx_descriptors(struct iser_conn *iser_conn)
3038c2ecf20Sopenharmony_ci{
3048c2ecf20Sopenharmony_ci	int i;
3058c2ecf20Sopenharmony_ci	struct iser_rx_desc *rx_desc;
3068c2ecf20Sopenharmony_ci	struct ib_conn *ib_conn = &iser_conn->ib_conn;
3078c2ecf20Sopenharmony_ci	struct iser_device *device = ib_conn->device;
3088c2ecf20Sopenharmony_ci
3098c2ecf20Sopenharmony_ci	iser_free_fastreg_pool(ib_conn);
3108c2ecf20Sopenharmony_ci
3118c2ecf20Sopenharmony_ci	rx_desc = iser_conn->rx_descs;
3128c2ecf20Sopenharmony_ci	for (i = 0; i < iser_conn->qp_max_recv_dtos; i++, rx_desc++)
3138c2ecf20Sopenharmony_ci		ib_dma_unmap_single(device->ib_device, rx_desc->dma_addr,
3148c2ecf20Sopenharmony_ci				    ISER_RX_PAYLOAD_SIZE, DMA_FROM_DEVICE);
3158c2ecf20Sopenharmony_ci	kfree(iser_conn->rx_descs);
3168c2ecf20Sopenharmony_ci	/* make sure we never redo any unmapping */
3178c2ecf20Sopenharmony_ci	iser_conn->rx_descs = NULL;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	iser_free_login_buf(iser_conn);
3208c2ecf20Sopenharmony_ci}
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_cistatic int iser_post_rx_bufs(struct iscsi_conn *conn, struct iscsi_hdr *req)
3238c2ecf20Sopenharmony_ci{
3248c2ecf20Sopenharmony_ci	struct iser_conn *iser_conn = conn->dd_data;
3258c2ecf20Sopenharmony_ci	struct ib_conn *ib_conn = &iser_conn->ib_conn;
3268c2ecf20Sopenharmony_ci	struct iscsi_session *session = conn->session;
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	iser_dbg("req op %x flags %x\n", req->opcode, req->flags);
3298c2ecf20Sopenharmony_ci	/* check if this is the last login - going to full feature phase */
3308c2ecf20Sopenharmony_ci	if ((req->flags & ISCSI_FULL_FEATURE_PHASE) != ISCSI_FULL_FEATURE_PHASE)
3318c2ecf20Sopenharmony_ci		return 0;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	/*
3348c2ecf20Sopenharmony_ci	 * Check that there is one posted recv buffer
3358c2ecf20Sopenharmony_ci	 * (for the last login response).
3368c2ecf20Sopenharmony_ci	 */
3378c2ecf20Sopenharmony_ci	WARN_ON(ib_conn->post_recv_buf_count != 1);
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	if (session->discovery_sess) {
3408c2ecf20Sopenharmony_ci		iser_info("Discovery session, re-using login RX buffer\n");
3418c2ecf20Sopenharmony_ci		return 0;
3428c2ecf20Sopenharmony_ci	} else
3438c2ecf20Sopenharmony_ci		iser_info("Normal session, posting batch of RX %d buffers\n",
3448c2ecf20Sopenharmony_ci			  iser_conn->min_posted_rx);
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_ci	/* Initial post receive buffers */
3478c2ecf20Sopenharmony_ci	if (iser_post_recvm(iser_conn, iser_conn->min_posted_rx))
3488c2ecf20Sopenharmony_ci		return -ENOMEM;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	return 0;
3518c2ecf20Sopenharmony_ci}
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_cistatic inline bool iser_signal_comp(u8 sig_count)
3548c2ecf20Sopenharmony_ci{
3558c2ecf20Sopenharmony_ci	return ((sig_count % ISER_SIGNAL_CMD_COUNT) == 0);
3568c2ecf20Sopenharmony_ci}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci/**
3598c2ecf20Sopenharmony_ci * iser_send_command - send command PDU
3608c2ecf20Sopenharmony_ci * @conn: link to matching iscsi connection
3618c2ecf20Sopenharmony_ci * @task: SCSI command task
3628c2ecf20Sopenharmony_ci */
3638c2ecf20Sopenharmony_ciint iser_send_command(struct iscsi_conn *conn,
3648c2ecf20Sopenharmony_ci		      struct iscsi_task *task)
3658c2ecf20Sopenharmony_ci{
3668c2ecf20Sopenharmony_ci	struct iser_conn *iser_conn = conn->dd_data;
3678c2ecf20Sopenharmony_ci	struct iscsi_iser_task *iser_task = task->dd_data;
3688c2ecf20Sopenharmony_ci	unsigned long edtl;
3698c2ecf20Sopenharmony_ci	int err;
3708c2ecf20Sopenharmony_ci	struct iser_data_buf *data_buf, *prot_buf;
3718c2ecf20Sopenharmony_ci	struct iscsi_scsi_req *hdr = (struct iscsi_scsi_req *)task->hdr;
3728c2ecf20Sopenharmony_ci	struct scsi_cmnd *sc  =  task->sc;
3738c2ecf20Sopenharmony_ci	struct iser_tx_desc *tx_desc = &iser_task->desc;
3748c2ecf20Sopenharmony_ci	u8 sig_count = ++iser_conn->ib_conn.sig_count;
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	edtl = ntohl(hdr->data_length);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	/* build the tx desc regd header and add it to the tx desc dto */
3798c2ecf20Sopenharmony_ci	tx_desc->type = ISCSI_TX_SCSI_COMMAND;
3808c2ecf20Sopenharmony_ci	tx_desc->cqe.done = iser_cmd_comp;
3818c2ecf20Sopenharmony_ci	iser_create_send_desc(iser_conn, tx_desc);
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_READ) {
3848c2ecf20Sopenharmony_ci		data_buf = &iser_task->data[ISER_DIR_IN];
3858c2ecf20Sopenharmony_ci		prot_buf = &iser_task->prot[ISER_DIR_IN];
3868c2ecf20Sopenharmony_ci	} else {
3878c2ecf20Sopenharmony_ci		data_buf = &iser_task->data[ISER_DIR_OUT];
3888c2ecf20Sopenharmony_ci		prot_buf = &iser_task->prot[ISER_DIR_OUT];
3898c2ecf20Sopenharmony_ci	}
3908c2ecf20Sopenharmony_ci
3918c2ecf20Sopenharmony_ci	if (scsi_sg_count(sc)) { /* using a scatter list */
3928c2ecf20Sopenharmony_ci		data_buf->sg = scsi_sglist(sc);
3938c2ecf20Sopenharmony_ci		data_buf->size = scsi_sg_count(sc);
3948c2ecf20Sopenharmony_ci	}
3958c2ecf20Sopenharmony_ci	data_buf->data_len = scsi_bufflen(sc);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (scsi_prot_sg_count(sc)) {
3988c2ecf20Sopenharmony_ci		prot_buf->sg  = scsi_prot_sglist(sc);
3998c2ecf20Sopenharmony_ci		prot_buf->size = scsi_prot_sg_count(sc);
4008c2ecf20Sopenharmony_ci		prot_buf->data_len = (data_buf->data_len >>
4018c2ecf20Sopenharmony_ci				     ilog2(sc->device->sector_size)) * 8;
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_READ) {
4058c2ecf20Sopenharmony_ci		err = iser_prepare_read_cmd(task);
4068c2ecf20Sopenharmony_ci		if (err)
4078c2ecf20Sopenharmony_ci			goto send_command_error;
4088c2ecf20Sopenharmony_ci	}
4098c2ecf20Sopenharmony_ci	if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
4108c2ecf20Sopenharmony_ci		err = iser_prepare_write_cmd(task,
4118c2ecf20Sopenharmony_ci					     task->imm_count,
4128c2ecf20Sopenharmony_ci				             task->imm_count +
4138c2ecf20Sopenharmony_ci					     task->unsol_r2t.data_length,
4148c2ecf20Sopenharmony_ci					     edtl);
4158c2ecf20Sopenharmony_ci		if (err)
4168c2ecf20Sopenharmony_ci			goto send_command_error;
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	iser_task->status = ISER_TASK_STATUS_STARTED;
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_ci	err = iser_post_send(&iser_conn->ib_conn, tx_desc,
4228c2ecf20Sopenharmony_ci			     iser_signal_comp(sig_count));
4238c2ecf20Sopenharmony_ci	if (!err)
4248c2ecf20Sopenharmony_ci		return 0;
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_cisend_command_error:
4278c2ecf20Sopenharmony_ci	iser_err("conn %p failed task->itt %d err %d\n",conn, task->itt, err);
4288c2ecf20Sopenharmony_ci	return err;
4298c2ecf20Sopenharmony_ci}
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci/**
4328c2ecf20Sopenharmony_ci * iser_send_data_out - send data out PDU
4338c2ecf20Sopenharmony_ci * @conn: link to matching iscsi connection
4348c2ecf20Sopenharmony_ci * @task: SCSI command task
4358c2ecf20Sopenharmony_ci * @hdr: pointer to the LLD's iSCSI message header
4368c2ecf20Sopenharmony_ci */
4378c2ecf20Sopenharmony_ciint iser_send_data_out(struct iscsi_conn *conn,
4388c2ecf20Sopenharmony_ci		       struct iscsi_task *task,
4398c2ecf20Sopenharmony_ci		       struct iscsi_data *hdr)
4408c2ecf20Sopenharmony_ci{
4418c2ecf20Sopenharmony_ci	struct iser_conn *iser_conn = conn->dd_data;
4428c2ecf20Sopenharmony_ci	struct iscsi_iser_task *iser_task = task->dd_data;
4438c2ecf20Sopenharmony_ci	struct iser_tx_desc *tx_desc;
4448c2ecf20Sopenharmony_ci	struct iser_mem_reg *mem_reg;
4458c2ecf20Sopenharmony_ci	unsigned long buf_offset;
4468c2ecf20Sopenharmony_ci	unsigned long data_seg_len;
4478c2ecf20Sopenharmony_ci	uint32_t itt;
4488c2ecf20Sopenharmony_ci	int err;
4498c2ecf20Sopenharmony_ci	struct ib_sge *tx_dsg;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	itt = (__force uint32_t)hdr->itt;
4528c2ecf20Sopenharmony_ci	data_seg_len = ntoh24(hdr->dlength);
4538c2ecf20Sopenharmony_ci	buf_offset   = ntohl(hdr->offset);
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ci	iser_dbg("%s itt %d dseg_len %d offset %d\n",
4568c2ecf20Sopenharmony_ci		 __func__,(int)itt,(int)data_seg_len,(int)buf_offset);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	tx_desc = kmem_cache_zalloc(ig.desc_cache, GFP_ATOMIC);
4598c2ecf20Sopenharmony_ci	if (!tx_desc)
4608c2ecf20Sopenharmony_ci		return -ENOMEM;
4618c2ecf20Sopenharmony_ci
4628c2ecf20Sopenharmony_ci	tx_desc->type = ISCSI_TX_DATAOUT;
4638c2ecf20Sopenharmony_ci	tx_desc->cqe.done = iser_dataout_comp;
4648c2ecf20Sopenharmony_ci	tx_desc->iser_header.flags = ISER_VER;
4658c2ecf20Sopenharmony_ci	memcpy(&tx_desc->iscsi_header, hdr, sizeof(struct iscsi_hdr));
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ci	/* build the tx desc */
4688c2ecf20Sopenharmony_ci	err = iser_initialize_task_headers(task, tx_desc);
4698c2ecf20Sopenharmony_ci	if (err)
4708c2ecf20Sopenharmony_ci		goto send_data_out_error;
4718c2ecf20Sopenharmony_ci
4728c2ecf20Sopenharmony_ci	mem_reg = &iser_task->rdma_reg[ISER_DIR_OUT];
4738c2ecf20Sopenharmony_ci	tx_dsg = &tx_desc->tx_sg[1];
4748c2ecf20Sopenharmony_ci	tx_dsg->addr = mem_reg->sge.addr + buf_offset;
4758c2ecf20Sopenharmony_ci	tx_dsg->length = data_seg_len;
4768c2ecf20Sopenharmony_ci	tx_dsg->lkey = mem_reg->sge.lkey;
4778c2ecf20Sopenharmony_ci	tx_desc->num_sge = 2;
4788c2ecf20Sopenharmony_ci
4798c2ecf20Sopenharmony_ci	if (buf_offset + data_seg_len > iser_task->data[ISER_DIR_OUT].data_len) {
4808c2ecf20Sopenharmony_ci		iser_err("Offset:%ld & DSL:%ld in Data-Out inconsistent with total len:%ld, itt:%d\n",
4818c2ecf20Sopenharmony_ci			 buf_offset, data_seg_len,
4828c2ecf20Sopenharmony_ci			 iser_task->data[ISER_DIR_OUT].data_len, itt);
4838c2ecf20Sopenharmony_ci		err = -EINVAL;
4848c2ecf20Sopenharmony_ci		goto send_data_out_error;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci	iser_dbg("data-out itt: %d, offset: %ld, sz: %ld\n",
4878c2ecf20Sopenharmony_ci		 itt, buf_offset, data_seg_len);
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	err = iser_post_send(&iser_conn->ib_conn, tx_desc, true);
4918c2ecf20Sopenharmony_ci	if (!err)
4928c2ecf20Sopenharmony_ci		return 0;
4938c2ecf20Sopenharmony_ci
4948c2ecf20Sopenharmony_cisend_data_out_error:
4958c2ecf20Sopenharmony_ci	kmem_cache_free(ig.desc_cache, tx_desc);
4968c2ecf20Sopenharmony_ci	iser_err("conn %p failed err %d\n", conn, err);
4978c2ecf20Sopenharmony_ci	return err;
4988c2ecf20Sopenharmony_ci}
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ciint iser_send_control(struct iscsi_conn *conn,
5018c2ecf20Sopenharmony_ci		      struct iscsi_task *task)
5028c2ecf20Sopenharmony_ci{
5038c2ecf20Sopenharmony_ci	struct iser_conn *iser_conn = conn->dd_data;
5048c2ecf20Sopenharmony_ci	struct iscsi_iser_task *iser_task = task->dd_data;
5058c2ecf20Sopenharmony_ci	struct iser_tx_desc *mdesc = &iser_task->desc;
5068c2ecf20Sopenharmony_ci	unsigned long data_seg_len;
5078c2ecf20Sopenharmony_ci	int err = 0;
5088c2ecf20Sopenharmony_ci	struct iser_device *device;
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	/* build the tx desc regd header and add it to the tx desc dto */
5118c2ecf20Sopenharmony_ci	mdesc->type = ISCSI_TX_CONTROL;
5128c2ecf20Sopenharmony_ci	mdesc->cqe.done = iser_ctrl_comp;
5138c2ecf20Sopenharmony_ci	iser_create_send_desc(iser_conn, mdesc);
5148c2ecf20Sopenharmony_ci
5158c2ecf20Sopenharmony_ci	device = iser_conn->ib_conn.device;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	data_seg_len = ntoh24(task->hdr->dlength);
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	if (data_seg_len > 0) {
5208c2ecf20Sopenharmony_ci		struct iser_login_desc *desc = &iser_conn->login_desc;
5218c2ecf20Sopenharmony_ci		struct ib_sge *tx_dsg = &mdesc->tx_sg[1];
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci		if (task != conn->login_task) {
5248c2ecf20Sopenharmony_ci			iser_err("data present on non login task!!!\n");
5258c2ecf20Sopenharmony_ci			goto send_control_error;
5268c2ecf20Sopenharmony_ci		}
5278c2ecf20Sopenharmony_ci
5288c2ecf20Sopenharmony_ci		ib_dma_sync_single_for_cpu(device->ib_device, desc->req_dma,
5298c2ecf20Sopenharmony_ci					   task->data_count, DMA_TO_DEVICE);
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_ci		memcpy(desc->req, task->data, task->data_count);
5328c2ecf20Sopenharmony_ci
5338c2ecf20Sopenharmony_ci		ib_dma_sync_single_for_device(device->ib_device, desc->req_dma,
5348c2ecf20Sopenharmony_ci					      task->data_count, DMA_TO_DEVICE);
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci		tx_dsg->addr = desc->req_dma;
5378c2ecf20Sopenharmony_ci		tx_dsg->length = task->data_count;
5388c2ecf20Sopenharmony_ci		tx_dsg->lkey = device->pd->local_dma_lkey;
5398c2ecf20Sopenharmony_ci		mdesc->num_sge = 2;
5408c2ecf20Sopenharmony_ci	}
5418c2ecf20Sopenharmony_ci
5428c2ecf20Sopenharmony_ci	if (task == conn->login_task) {
5438c2ecf20Sopenharmony_ci		iser_dbg("op %x dsl %lx, posting login rx buffer\n",
5448c2ecf20Sopenharmony_ci			 task->hdr->opcode, data_seg_len);
5458c2ecf20Sopenharmony_ci		err = iser_post_recvl(iser_conn);
5468c2ecf20Sopenharmony_ci		if (err)
5478c2ecf20Sopenharmony_ci			goto send_control_error;
5488c2ecf20Sopenharmony_ci		err = iser_post_rx_bufs(conn, task->hdr);
5498c2ecf20Sopenharmony_ci		if (err)
5508c2ecf20Sopenharmony_ci			goto send_control_error;
5518c2ecf20Sopenharmony_ci	}
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci	err = iser_post_send(&iser_conn->ib_conn, mdesc, true);
5548c2ecf20Sopenharmony_ci	if (!err)
5558c2ecf20Sopenharmony_ci		return 0;
5568c2ecf20Sopenharmony_ci
5578c2ecf20Sopenharmony_cisend_control_error:
5588c2ecf20Sopenharmony_ci	iser_err("conn %p failed err %d\n",conn, err);
5598c2ecf20Sopenharmony_ci	return err;
5608c2ecf20Sopenharmony_ci}
5618c2ecf20Sopenharmony_ci
5628c2ecf20Sopenharmony_civoid iser_login_rsp(struct ib_cq *cq, struct ib_wc *wc)
5638c2ecf20Sopenharmony_ci{
5648c2ecf20Sopenharmony_ci	struct ib_conn *ib_conn = wc->qp->qp_context;
5658c2ecf20Sopenharmony_ci	struct iser_conn *iser_conn = to_iser_conn(ib_conn);
5668c2ecf20Sopenharmony_ci	struct iser_login_desc *desc = iser_login(wc->wr_cqe);
5678c2ecf20Sopenharmony_ci	struct iscsi_hdr *hdr;
5688c2ecf20Sopenharmony_ci	char *data;
5698c2ecf20Sopenharmony_ci	int length;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	if (unlikely(wc->status != IB_WC_SUCCESS)) {
5728c2ecf20Sopenharmony_ci		iser_err_comp(wc, "login_rsp");
5738c2ecf20Sopenharmony_ci		return;
5748c2ecf20Sopenharmony_ci	}
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	ib_dma_sync_single_for_cpu(ib_conn->device->ib_device,
5778c2ecf20Sopenharmony_ci				   desc->rsp_dma, ISER_RX_LOGIN_SIZE,
5788c2ecf20Sopenharmony_ci				   DMA_FROM_DEVICE);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	hdr = desc->rsp + sizeof(struct iser_ctrl);
5818c2ecf20Sopenharmony_ci	data = desc->rsp + ISER_HEADERS_LEN;
5828c2ecf20Sopenharmony_ci	length = wc->byte_len - ISER_HEADERS_LEN;
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	iser_dbg("op 0x%x itt 0x%x dlen %d\n", hdr->opcode,
5858c2ecf20Sopenharmony_ci		 hdr->itt, length);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	iscsi_iser_recv(iser_conn->iscsi_conn, hdr, data, length);
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci	ib_dma_sync_single_for_device(ib_conn->device->ib_device,
5908c2ecf20Sopenharmony_ci				      desc->rsp_dma, ISER_RX_LOGIN_SIZE,
5918c2ecf20Sopenharmony_ci				      DMA_FROM_DEVICE);
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci	ib_conn->post_recv_buf_count--;
5948c2ecf20Sopenharmony_ci}
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_cistatic inline int
5978c2ecf20Sopenharmony_ciiser_inv_desc(struct iser_fr_desc *desc, u32 rkey)
5988c2ecf20Sopenharmony_ci{
5998c2ecf20Sopenharmony_ci	if (unlikely((!desc->sig_protected && rkey != desc->rsc.mr->rkey) ||
6008c2ecf20Sopenharmony_ci		     (desc->sig_protected && rkey != desc->rsc.sig_mr->rkey))) {
6018c2ecf20Sopenharmony_ci		iser_err("Bogus remote invalidation for rkey %#x\n", rkey);
6028c2ecf20Sopenharmony_ci		return -EINVAL;
6038c2ecf20Sopenharmony_ci	}
6048c2ecf20Sopenharmony_ci
6058c2ecf20Sopenharmony_ci	if (desc->sig_protected)
6068c2ecf20Sopenharmony_ci		desc->rsc.sig_mr->need_inval = false;
6078c2ecf20Sopenharmony_ci	else
6088c2ecf20Sopenharmony_ci		desc->rsc.mr->need_inval = false;
6098c2ecf20Sopenharmony_ci
6108c2ecf20Sopenharmony_ci	return 0;
6118c2ecf20Sopenharmony_ci}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cistatic int
6148c2ecf20Sopenharmony_ciiser_check_remote_inv(struct iser_conn *iser_conn,
6158c2ecf20Sopenharmony_ci		      struct ib_wc *wc,
6168c2ecf20Sopenharmony_ci		      struct iscsi_hdr *hdr)
6178c2ecf20Sopenharmony_ci{
6188c2ecf20Sopenharmony_ci	if (wc->wc_flags & IB_WC_WITH_INVALIDATE) {
6198c2ecf20Sopenharmony_ci		struct iscsi_task *task;
6208c2ecf20Sopenharmony_ci		u32 rkey = wc->ex.invalidate_rkey;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		iser_dbg("conn %p: remote invalidation for rkey %#x\n",
6238c2ecf20Sopenharmony_ci			 iser_conn, rkey);
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci		if (unlikely(!iser_conn->snd_w_inv)) {
6268c2ecf20Sopenharmony_ci			iser_err("conn %p: unexpected remote invalidation, terminating connection\n",
6278c2ecf20Sopenharmony_ci				 iser_conn);
6288c2ecf20Sopenharmony_ci			return -EPROTO;
6298c2ecf20Sopenharmony_ci		}
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_ci		task = iscsi_itt_to_ctask(iser_conn->iscsi_conn, hdr->itt);
6328c2ecf20Sopenharmony_ci		if (likely(task)) {
6338c2ecf20Sopenharmony_ci			struct iscsi_iser_task *iser_task = task->dd_data;
6348c2ecf20Sopenharmony_ci			struct iser_fr_desc *desc;
6358c2ecf20Sopenharmony_ci
6368c2ecf20Sopenharmony_ci			if (iser_task->dir[ISER_DIR_IN]) {
6378c2ecf20Sopenharmony_ci				desc = iser_task->rdma_reg[ISER_DIR_IN].mem_h;
6388c2ecf20Sopenharmony_ci				if (unlikely(iser_inv_desc(desc, rkey)))
6398c2ecf20Sopenharmony_ci					return -EINVAL;
6408c2ecf20Sopenharmony_ci			}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci			if (iser_task->dir[ISER_DIR_OUT]) {
6438c2ecf20Sopenharmony_ci				desc = iser_task->rdma_reg[ISER_DIR_OUT].mem_h;
6448c2ecf20Sopenharmony_ci				if (unlikely(iser_inv_desc(desc, rkey)))
6458c2ecf20Sopenharmony_ci					return -EINVAL;
6468c2ecf20Sopenharmony_ci			}
6478c2ecf20Sopenharmony_ci		} else {
6488c2ecf20Sopenharmony_ci			iser_err("failed to get task for itt=%d\n", hdr->itt);
6498c2ecf20Sopenharmony_ci			return -EINVAL;
6508c2ecf20Sopenharmony_ci		}
6518c2ecf20Sopenharmony_ci	}
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	return 0;
6548c2ecf20Sopenharmony_ci}
6558c2ecf20Sopenharmony_ci
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_civoid iser_task_rsp(struct ib_cq *cq, struct ib_wc *wc)
6588c2ecf20Sopenharmony_ci{
6598c2ecf20Sopenharmony_ci	struct ib_conn *ib_conn = wc->qp->qp_context;
6608c2ecf20Sopenharmony_ci	struct iser_conn *iser_conn = to_iser_conn(ib_conn);
6618c2ecf20Sopenharmony_ci	struct iser_rx_desc *desc = iser_rx(wc->wr_cqe);
6628c2ecf20Sopenharmony_ci	struct iscsi_hdr *hdr;
6638c2ecf20Sopenharmony_ci	int length;
6648c2ecf20Sopenharmony_ci	int outstanding, count, err;
6658c2ecf20Sopenharmony_ci
6668c2ecf20Sopenharmony_ci	if (unlikely(wc->status != IB_WC_SUCCESS)) {
6678c2ecf20Sopenharmony_ci		iser_err_comp(wc, "task_rsp");
6688c2ecf20Sopenharmony_ci		return;
6698c2ecf20Sopenharmony_ci	}
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	ib_dma_sync_single_for_cpu(ib_conn->device->ib_device,
6728c2ecf20Sopenharmony_ci				   desc->dma_addr, ISER_RX_PAYLOAD_SIZE,
6738c2ecf20Sopenharmony_ci				   DMA_FROM_DEVICE);
6748c2ecf20Sopenharmony_ci
6758c2ecf20Sopenharmony_ci	hdr = &desc->iscsi_header;
6768c2ecf20Sopenharmony_ci	length = wc->byte_len - ISER_HEADERS_LEN;
6778c2ecf20Sopenharmony_ci
6788c2ecf20Sopenharmony_ci	iser_dbg("op 0x%x itt 0x%x dlen %d\n", hdr->opcode,
6798c2ecf20Sopenharmony_ci		 hdr->itt, length);
6808c2ecf20Sopenharmony_ci
6818c2ecf20Sopenharmony_ci	if (iser_check_remote_inv(iser_conn, wc, hdr)) {
6828c2ecf20Sopenharmony_ci		iscsi_conn_failure(iser_conn->iscsi_conn,
6838c2ecf20Sopenharmony_ci				   ISCSI_ERR_CONN_FAILED);
6848c2ecf20Sopenharmony_ci		return;
6858c2ecf20Sopenharmony_ci	}
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci	iscsi_iser_recv(iser_conn->iscsi_conn, hdr, desc->data, length);
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	ib_dma_sync_single_for_device(ib_conn->device->ib_device,
6908c2ecf20Sopenharmony_ci				      desc->dma_addr, ISER_RX_PAYLOAD_SIZE,
6918c2ecf20Sopenharmony_ci				      DMA_FROM_DEVICE);
6928c2ecf20Sopenharmony_ci
6938c2ecf20Sopenharmony_ci	/* decrementing conn->post_recv_buf_count only --after-- freeing the   *
6948c2ecf20Sopenharmony_ci	 * task eliminates the need to worry on tasks which are completed in   *
6958c2ecf20Sopenharmony_ci	 * parallel to the execution of iser_conn_term. So the code that waits *
6968c2ecf20Sopenharmony_ci	 * for the posted rx bufs refcount to become zero handles everything   */
6978c2ecf20Sopenharmony_ci	ib_conn->post_recv_buf_count--;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	outstanding = ib_conn->post_recv_buf_count;
7008c2ecf20Sopenharmony_ci	if (outstanding + iser_conn->min_posted_rx <= iser_conn->qp_max_recv_dtos) {
7018c2ecf20Sopenharmony_ci		count = min(iser_conn->qp_max_recv_dtos - outstanding,
7028c2ecf20Sopenharmony_ci			    iser_conn->min_posted_rx);
7038c2ecf20Sopenharmony_ci		err = iser_post_recvm(iser_conn, count);
7048c2ecf20Sopenharmony_ci		if (err)
7058c2ecf20Sopenharmony_ci			iser_err("posting %d rx bufs err %d\n", count, err);
7068c2ecf20Sopenharmony_ci	}
7078c2ecf20Sopenharmony_ci}
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_civoid iser_cmd_comp(struct ib_cq *cq, struct ib_wc *wc)
7108c2ecf20Sopenharmony_ci{
7118c2ecf20Sopenharmony_ci	if (unlikely(wc->status != IB_WC_SUCCESS))
7128c2ecf20Sopenharmony_ci		iser_err_comp(wc, "command");
7138c2ecf20Sopenharmony_ci}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_civoid iser_ctrl_comp(struct ib_cq *cq, struct ib_wc *wc)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	struct iser_tx_desc *desc = iser_tx(wc->wr_cqe);
7188c2ecf20Sopenharmony_ci	struct iscsi_task *task;
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	if (unlikely(wc->status != IB_WC_SUCCESS)) {
7218c2ecf20Sopenharmony_ci		iser_err_comp(wc, "control");
7228c2ecf20Sopenharmony_ci		return;
7238c2ecf20Sopenharmony_ci	}
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	/* this arithmetic is legal by libiscsi dd_data allocation */
7268c2ecf20Sopenharmony_ci	task = (void *)desc - sizeof(struct iscsi_task);
7278c2ecf20Sopenharmony_ci	if (task->hdr->itt == RESERVED_ITT)
7288c2ecf20Sopenharmony_ci		iscsi_put_task(task);
7298c2ecf20Sopenharmony_ci}
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_civoid iser_dataout_comp(struct ib_cq *cq, struct ib_wc *wc)
7328c2ecf20Sopenharmony_ci{
7338c2ecf20Sopenharmony_ci	struct iser_tx_desc *desc = iser_tx(wc->wr_cqe);
7348c2ecf20Sopenharmony_ci	struct ib_conn *ib_conn = wc->qp->qp_context;
7358c2ecf20Sopenharmony_ci	struct iser_device *device = ib_conn->device;
7368c2ecf20Sopenharmony_ci
7378c2ecf20Sopenharmony_ci	if (unlikely(wc->status != IB_WC_SUCCESS))
7388c2ecf20Sopenharmony_ci		iser_err_comp(wc, "dataout");
7398c2ecf20Sopenharmony_ci
7408c2ecf20Sopenharmony_ci	ib_dma_unmap_single(device->ib_device, desc->dma_addr,
7418c2ecf20Sopenharmony_ci			    ISER_HEADERS_LEN, DMA_TO_DEVICE);
7428c2ecf20Sopenharmony_ci	kmem_cache_free(ig.desc_cache, desc);
7438c2ecf20Sopenharmony_ci}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_civoid iser_task_rdma_init(struct iscsi_iser_task *iser_task)
7468c2ecf20Sopenharmony_ci
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	iser_task->status = ISER_TASK_STATUS_INIT;
7498c2ecf20Sopenharmony_ci
7508c2ecf20Sopenharmony_ci	iser_task->dir[ISER_DIR_IN] = 0;
7518c2ecf20Sopenharmony_ci	iser_task->dir[ISER_DIR_OUT] = 0;
7528c2ecf20Sopenharmony_ci
7538c2ecf20Sopenharmony_ci	iser_task->data[ISER_DIR_IN].data_len  = 0;
7548c2ecf20Sopenharmony_ci	iser_task->data[ISER_DIR_OUT].data_len = 0;
7558c2ecf20Sopenharmony_ci
7568c2ecf20Sopenharmony_ci	iser_task->prot[ISER_DIR_IN].data_len  = 0;
7578c2ecf20Sopenharmony_ci	iser_task->prot[ISER_DIR_OUT].data_len = 0;
7588c2ecf20Sopenharmony_ci
7598c2ecf20Sopenharmony_ci	iser_task->prot[ISER_DIR_IN].dma_nents = 0;
7608c2ecf20Sopenharmony_ci	iser_task->prot[ISER_DIR_OUT].dma_nents = 0;
7618c2ecf20Sopenharmony_ci
7628c2ecf20Sopenharmony_ci	memset(&iser_task->rdma_reg[ISER_DIR_IN], 0,
7638c2ecf20Sopenharmony_ci	       sizeof(struct iser_mem_reg));
7648c2ecf20Sopenharmony_ci	memset(&iser_task->rdma_reg[ISER_DIR_OUT], 0,
7658c2ecf20Sopenharmony_ci	       sizeof(struct iser_mem_reg));
7668c2ecf20Sopenharmony_ci}
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_civoid iser_task_rdma_finalize(struct iscsi_iser_task *iser_task)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	int prot_count = scsi_prot_sg_count(iser_task->sc);
7718c2ecf20Sopenharmony_ci
7728c2ecf20Sopenharmony_ci	if (iser_task->dir[ISER_DIR_IN]) {
7738c2ecf20Sopenharmony_ci		iser_unreg_mem_fastreg(iser_task, ISER_DIR_IN);
7748c2ecf20Sopenharmony_ci		iser_dma_unmap_task_data(iser_task,
7758c2ecf20Sopenharmony_ci					 &iser_task->data[ISER_DIR_IN],
7768c2ecf20Sopenharmony_ci					 DMA_FROM_DEVICE);
7778c2ecf20Sopenharmony_ci		if (prot_count)
7788c2ecf20Sopenharmony_ci			iser_dma_unmap_task_data(iser_task,
7798c2ecf20Sopenharmony_ci						 &iser_task->prot[ISER_DIR_IN],
7808c2ecf20Sopenharmony_ci						 DMA_FROM_DEVICE);
7818c2ecf20Sopenharmony_ci	}
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci	if (iser_task->dir[ISER_DIR_OUT]) {
7848c2ecf20Sopenharmony_ci		iser_unreg_mem_fastreg(iser_task, ISER_DIR_OUT);
7858c2ecf20Sopenharmony_ci		iser_dma_unmap_task_data(iser_task,
7868c2ecf20Sopenharmony_ci					 &iser_task->data[ISER_DIR_OUT],
7878c2ecf20Sopenharmony_ci					 DMA_TO_DEVICE);
7888c2ecf20Sopenharmony_ci		if (prot_count)
7898c2ecf20Sopenharmony_ci			iser_dma_unmap_task_data(iser_task,
7908c2ecf20Sopenharmony_ci						 &iser_task->prot[ISER_DIR_OUT],
7918c2ecf20Sopenharmony_ci						 DMA_TO_DEVICE);
7928c2ecf20Sopenharmony_ci	}
7938c2ecf20Sopenharmony_ci}
794