xref: /openbmc/linux/drivers/target/tcm_fc/tfc_io.c (revision e31ac898)
1a61127c2SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
23699d92aSKiran Patil /*
33699d92aSKiran Patil  * Copyright (c) 2010 Cisco Systems, Inc.
43699d92aSKiran Patil  *
53699d92aSKiran Patil  * Portions based on tcm_loop_fabric_scsi.c and libfc/fc_fcp.c
63699d92aSKiran Patil  *
73699d92aSKiran Patil  * Copyright (c) 2007 Intel Corporation. All rights reserved.
83699d92aSKiran Patil  * Copyright (c) 2008 Red Hat, Inc.  All rights reserved.
93699d92aSKiran Patil  * Copyright (c) 2008 Mike Christie
103699d92aSKiran Patil  * Copyright (c) 2009 Rising Tide, Inc.
113699d92aSKiran Patil  * Copyright (c) 2009 Linux-iSCSI.org
123699d92aSKiran Patil  * Copyright (c) 2009 Nicholas A. Bellinger <nab@linux-iscsi.org>
133699d92aSKiran Patil  */
143699d92aSKiran Patil 
153699d92aSKiran Patil /* XXX TBD some includes may be extraneous */
163699d92aSKiran Patil 
173699d92aSKiran Patil #include <linux/module.h>
183699d92aSKiran Patil #include <linux/moduleparam.h>
193699d92aSKiran Patil #include <linux/utsname.h>
203699d92aSKiran Patil #include <linux/init.h>
213699d92aSKiran Patil #include <linux/slab.h>
223699d92aSKiran Patil #include <linux/kthread.h>
233699d92aSKiran Patil #include <linux/types.h>
243699d92aSKiran Patil #include <linux/string.h>
253699d92aSKiran Patil #include <linux/configfs.h>
263699d92aSKiran Patil #include <linux/ctype.h>
273699d92aSKiran Patil #include <linux/hash.h>
286708bb27SAndy Grover #include <linux/ratelimit.h>
293699d92aSKiran Patil #include <asm/unaligned.h>
303699d92aSKiran Patil #include <scsi/libfc.h>
313699d92aSKiran Patil 
323699d92aSKiran Patil #include <target/target_core_base.h>
33c4795fb2SChristoph Hellwig #include <target/target_core_fabric.h>
343699d92aSKiran Patil 
353699d92aSKiran Patil #include "tcm_fc.h"
363699d92aSKiran Patil 
373699d92aSKiran Patil /*
383699d92aSKiran Patil  * Deliver read data back to initiator.
393699d92aSKiran Patil  * XXX TBD handle resource problems later.
403699d92aSKiran Patil  */
ft_queue_data_in(struct se_cmd * se_cmd)413699d92aSKiran Patil int ft_queue_data_in(struct se_cmd *se_cmd)
423699d92aSKiran Patil {
433699d92aSKiran Patil 	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
443699d92aSKiran Patil 	struct fc_frame *fp = NULL;
453699d92aSKiran Patil 	struct fc_exch *ep;
463699d92aSKiran Patil 	struct fc_lport *lport;
47ec98f782SAndy Grover 	struct scatterlist *sg = NULL;
483699d92aSKiran Patil 	size_t remaining;
493699d92aSKiran Patil 	u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF;
50ec98f782SAndy Grover 	u32 mem_off = 0;
513699d92aSKiran Patil 	u32 fh_off = 0;
523699d92aSKiran Patil 	u32 frame_off = 0;
533699d92aSKiran Patil 	size_t frame_len = 0;
54ec98f782SAndy Grover 	size_t mem_len = 0;
553699d92aSKiran Patil 	size_t tlen;
563699d92aSKiran Patil 	size_t off_in_page;
57ec98f782SAndy Grover 	struct page *page = NULL;
583699d92aSKiran Patil 	int use_sg;
593699d92aSKiran Patil 	int error;
603699d92aSKiran Patil 	void *page_addr;
613699d92aSKiran Patil 	void *from;
623699d92aSKiran Patil 	void *to = NULL;
633699d92aSKiran Patil 
64e1c40382SMark Rustad 	if (cmd->aborted)
65e1c40382SMark Rustad 		return 0;
66b3e5fe16SNicholas Bellinger 
67b3e5fe16SNicholas Bellinger 	if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL)
68b3e5fe16SNicholas Bellinger 		goto queue_status;
69b3e5fe16SNicholas Bellinger 
703699d92aSKiran Patil 	ep = fc_seq_exch(cmd->seq);
713699d92aSKiran Patil 	lport = ep->lp;
72c6865b30SHannes Reinecke 	cmd->seq = fc_seq_start_next(cmd->seq);
733699d92aSKiran Patil 
743699d92aSKiran Patil 	remaining = se_cmd->data_length;
753699d92aSKiran Patil 
763699d92aSKiran Patil 	/*
7705d1c7c0SAndy Grover 	 * Setup to use first mem list entry, unless no data.
783699d92aSKiran Patil 	 */
79ec98f782SAndy Grover 	BUG_ON(remaining && !se_cmd->t_data_sg);
8005d1c7c0SAndy Grover 	if (remaining) {
81ec98f782SAndy Grover 		sg = se_cmd->t_data_sg;
82ec98f782SAndy Grover 		mem_len = sg->length;
83ec98f782SAndy Grover 		mem_off = sg->offset;
84ec98f782SAndy Grover 		page = sg_page(sg);
853699d92aSKiran Patil 	}
863699d92aSKiran Patil 
873699d92aSKiran Patil 	/* no scatter/gather in skb for odd word length due to fc_seq_send() */
883699d92aSKiran Patil 	use_sg = !(remaining % 4);
893699d92aSKiran Patil 
903699d92aSKiran Patil 	while (remaining) {
91d3682b1aSMark Rustad 		struct fc_seq *seq = cmd->seq;
92d3682b1aSMark Rustad 
93d3682b1aSMark Rustad 		if (!seq) {
94d3682b1aSMark Rustad 			pr_debug("%s: Command aborted, xid 0x%x\n",
95d3682b1aSMark Rustad 				 __func__, ep->xid);
96d3682b1aSMark Rustad 			break;
97d3682b1aSMark Rustad 		}
983699d92aSKiran Patil 		if (!mem_len) {
99ec98f782SAndy Grover 			sg = sg_next(sg);
100ec98f782SAndy Grover 			mem_len = min((size_t)sg->length, remaining);
101ec98f782SAndy Grover 			mem_off = sg->offset;
102ec98f782SAndy Grover 			page = sg_page(sg);
1033699d92aSKiran Patil 		}
1043699d92aSKiran Patil 		if (!frame_len) {
1053699d92aSKiran Patil 			/*
1063699d92aSKiran Patil 			 * If lport's has capability of Large Send Offload LSO)
1073699d92aSKiran Patil 			 * , then allow 'frame_len' to be as big as 'lso_max'
1083699d92aSKiran Patil 			 * if indicated transfer length is >= lport->lso_max
1093699d92aSKiran Patil 			 */
1103699d92aSKiran Patil 			frame_len = (lport->seq_offload) ? lport->lso_max :
1113699d92aSKiran Patil 							  cmd->sess->max_frame;
1123699d92aSKiran Patil 			frame_len = min(frame_len, remaining);
1133699d92aSKiran Patil 			fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len);
1143699d92aSKiran Patil 			if (!fp)
1153699d92aSKiran Patil 				return -ENOMEM;
1163699d92aSKiran Patil 			to = fc_frame_payload_get(fp, 0);
1173699d92aSKiran Patil 			fh_off = frame_off;
1183699d92aSKiran Patil 			frame_off += frame_len;
1193699d92aSKiran Patil 			/*
1203699d92aSKiran Patil 			 * Setup the frame's max payload which is used by base
1213699d92aSKiran Patil 			 * driver to indicate HW about max frame size, so that
1223699d92aSKiran Patil 			 * HW can do fragmentation appropriately based on
1233699d92aSKiran Patil 			 * "gso_max_size" of underline netdev.
1243699d92aSKiran Patil 			 */
1253699d92aSKiran Patil 			fr_max_payload(fp) = cmd->sess->max_frame;
1263699d92aSKiran Patil 		}
1273699d92aSKiran Patil 		tlen = min(mem_len, frame_len);
1283699d92aSKiran Patil 
1293699d92aSKiran Patil 		if (use_sg) {
1303699d92aSKiran Patil 			off_in_page = mem_off;
1313699d92aSKiran Patil 			BUG_ON(!page);
1323699d92aSKiran Patil 			get_page(page);
1333699d92aSKiran Patil 			skb_fill_page_desc(fp_skb(fp),
1343699d92aSKiran Patil 					   skb_shinfo(fp_skb(fp))->nr_frags,
1353699d92aSKiran Patil 					   page, off_in_page, tlen);
1363699d92aSKiran Patil 			fr_len(fp) += tlen;
1373699d92aSKiran Patil 			fp_skb(fp)->data_len += tlen;
138a50b854eSMatthew Wilcox (Oracle) 			fp_skb(fp)->truesize += page_size(page);
13905d1c7c0SAndy Grover 		} else {
1403699d92aSKiran Patil 			BUG_ON(!page);
141ca747d61SCong Wang 			from = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
1423699d92aSKiran Patil 			page_addr = from;
143b75d8063SGeliang Tang 			from += offset_in_page(mem_off);
1443699d92aSKiran Patil 			tlen = min(tlen, (size_t)(PAGE_SIZE -
145b75d8063SGeliang Tang 						offset_in_page(mem_off)));
1463699d92aSKiran Patil 			memcpy(to, from, tlen);
147ca747d61SCong Wang 			kunmap_atomic(page_addr);
1483699d92aSKiran Patil 			to += tlen;
1493699d92aSKiran Patil 		}
1503699d92aSKiran Patil 
1513699d92aSKiran Patil 		mem_off += tlen;
1523699d92aSKiran Patil 		mem_len -= tlen;
1533699d92aSKiran Patil 		frame_len -= tlen;
1543699d92aSKiran Patil 		remaining -= tlen;
1553699d92aSKiran Patil 
1563699d92aSKiran Patil 		if (frame_len &&
1573699d92aSKiran Patil 		    (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN))
1583699d92aSKiran Patil 			continue;
1593699d92aSKiran Patil 		if (!remaining)
1603699d92aSKiran Patil 			f_ctl |= FC_FC_END_SEQ;
1613699d92aSKiran Patil 		fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
1623699d92aSKiran Patil 			       FC_TYPE_FCP, f_ctl, fh_off);
1630cac937dSHannes Reinecke 		error = fc_seq_send(lport, seq, fp);
1643699d92aSKiran Patil 		if (error) {
165b3e5fe16SNicholas Bellinger 			pr_info_ratelimited("%s: Failed to send frame %p, "
16695efa286SNicholas Bellinger 						"xid <0x%x>, remaining %zu, "
1673699d92aSKiran Patil 						"lso_max <0x%x>\n",
1683699d92aSKiran Patil 						__func__, fp, ep->xid,
1693699d92aSKiran Patil 						remaining, lport->lso_max);
170b3e5fe16SNicholas Bellinger 			/*
171b3e5fe16SNicholas Bellinger 			 * Go ahead and set TASK_SET_FULL status ignoring the
172b3e5fe16SNicholas Bellinger 			 * rest of the DataIN, and immediately attempt to
173b3e5fe16SNicholas Bellinger 			 * send the response via ft_queue_status() in order
174b3e5fe16SNicholas Bellinger 			 * to notify the initiator that it should reduce it's
175b3e5fe16SNicholas Bellinger 			 * per LUN queue_depth.
176b3e5fe16SNicholas Bellinger 			 */
177b3e5fe16SNicholas Bellinger 			se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
178b3e5fe16SNicholas Bellinger 			break;
1793699d92aSKiran Patil 		}
1803699d92aSKiran Patil 	}
181b3e5fe16SNicholas Bellinger queue_status:
1823699d92aSKiran Patil 	return ft_queue_status(se_cmd);
1833699d92aSKiran Patil }
1843699d92aSKiran Patil 
ft_execute_work(struct work_struct * work)185b8b22533SChristoph Hellwig static void ft_execute_work(struct work_struct *work)
186b8b22533SChristoph Hellwig {
187b8b22533SChristoph Hellwig 	struct ft_cmd *cmd = container_of(work, struct ft_cmd, work);
188b8b22533SChristoph Hellwig 
189b8b22533SChristoph Hellwig 	target_execute_cmd(&cmd->se_cmd);
190b8b22533SChristoph Hellwig }
191b8b22533SChristoph Hellwig 
1923699d92aSKiran Patil /*
1933699d92aSKiran Patil  * Receive write data frame.
1943699d92aSKiran Patil  */
ft_recv_write_data(struct ft_cmd * cmd,struct fc_frame * fp)1953699d92aSKiran Patil void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
1963699d92aSKiran Patil {
1973699d92aSKiran Patil 	struct se_cmd *se_cmd = &cmd->se_cmd;
1983699d92aSKiran Patil 	struct fc_seq *seq = cmd->seq;
1993699d92aSKiran Patil 	struct fc_exch *ep;
2003699d92aSKiran Patil 	struct fc_lport *lport;
2013699d92aSKiran Patil 	struct fc_frame_header *fh;
202ec98f782SAndy Grover 	struct scatterlist *sg = NULL;
203ec98f782SAndy Grover 	u32 mem_off = 0;
2043699d92aSKiran Patil 	u32 rel_off;
2053699d92aSKiran Patil 	size_t frame_len;
206ec98f782SAndy Grover 	size_t mem_len = 0;
2073699d92aSKiran Patil 	size_t tlen;
208ec98f782SAndy Grover 	struct page *page = NULL;
2093699d92aSKiran Patil 	void *page_addr;
2103699d92aSKiran Patil 	void *from;
2113699d92aSKiran Patil 	void *to;
2123699d92aSKiran Patil 	u32 f_ctl;
2133699d92aSKiran Patil 	void *buf;
2143699d92aSKiran Patil 
2153699d92aSKiran Patil 	fh = fc_frame_header_get(fp);
2163699d92aSKiran Patil 	if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
2173699d92aSKiran Patil 		goto drop;
2183699d92aSKiran Patil 
219dcd998ccSKiran Patil 	f_ctl = ntoh24(fh->fh_f_ctl);
220dcd998ccSKiran Patil 	ep = fc_seq_exch(seq);
221dcd998ccSKiran Patil 	lport = ep->lp;
222dcd998ccSKiran Patil 	if (cmd->was_ddp_setup) {
223dcd998ccSKiran Patil 		BUG_ON(!lport);
2243699d92aSKiran Patil 		/*
225079587b4SKiran Patil 		 * Since DDP (Large Rx offload) was setup for this request,
226079587b4SKiran Patil 		 * payload is expected to be copied directly to user buffers.
2273699d92aSKiran Patil 		 */
228dcd998ccSKiran Patil 		buf = fc_frame_payload_get(fp, 1);
229dcd998ccSKiran Patil 		if (buf)
230dcd998ccSKiran Patil 			pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
231dcd998ccSKiran Patil 				"cmd->sg_cnt 0x%x. DDP was setup"
232dcd998ccSKiran Patil 				" hence not expected to receive frame with "
233dcd998ccSKiran Patil 				"payload, Frame will be dropped if"
234dcd998ccSKiran Patil 				"'Sequence Initiative' bit in f_ctl is"
235dcd998ccSKiran Patil 				"not set\n", __func__, ep->xid, f_ctl,
236e182d682SRoland Dreier 				se_cmd->t_data_sg, se_cmd->t_data_nents);
2373699d92aSKiran Patil 		/*
238dcd998ccSKiran Patil 		 * Invalidate HW DDP context if it was setup for respective
239dcd998ccSKiran Patil 		 * command. Invalidation of HW DDP context is requited in both
240dcd998ccSKiran Patil 		 * situation (success and error).
2413699d92aSKiran Patil 		 */
242dcd998ccSKiran Patil 		ft_invl_hw_context(cmd);
243dcd998ccSKiran Patil 
244dcd998ccSKiran Patil 		/*
245dcd998ccSKiran Patil 		 * If "Sequence Initiative (TSI)" bit set in f_ctl, means last
246dcd998ccSKiran Patil 		 * write data frame is received successfully where payload is
247dcd998ccSKiran Patil 		 * posted directly to user buffer and only the last frame's
248dcd998ccSKiran Patil 		 * header is posted in receive queue.
249dcd998ccSKiran Patil 		 *
250dcd998ccSKiran Patil 		 * If "Sequence Initiative (TSI)" bit is not set, means error
251dcd998ccSKiran Patil 		 * condition w.r.t. DDP, hence drop the packet and let explict
252dcd998ccSKiran Patil 		 * ABORTS from other end of exchange timer trigger the recovery.
253dcd998ccSKiran Patil 		 */
254dcd998ccSKiran Patil 		if (f_ctl & FC_FC_SEQ_INIT)
2553699d92aSKiran Patil 			goto last_frame;
256dcd998ccSKiran Patil 		else
2573699d92aSKiran Patil 			goto drop;
258079587b4SKiran Patil 	}
2593699d92aSKiran Patil 
2603699d92aSKiran Patil 	rel_off = ntohl(fh->fh_parm_offset);
2613699d92aSKiran Patil 	frame_len = fr_len(fp);
2623699d92aSKiran Patil 	if (frame_len <= sizeof(*fh))
2633699d92aSKiran Patil 		goto drop;
2643699d92aSKiran Patil 	frame_len -= sizeof(*fh);
2653699d92aSKiran Patil 	from = fc_frame_payload_get(fp, 0);
2663699d92aSKiran Patil 	if (rel_off >= se_cmd->data_length)
2673699d92aSKiran Patil 		goto drop;
2683699d92aSKiran Patil 	if (frame_len + rel_off > se_cmd->data_length)
2693699d92aSKiran Patil 		frame_len = se_cmd->data_length - rel_off;
2703699d92aSKiran Patil 
2713699d92aSKiran Patil 	/*
27205d1c7c0SAndy Grover 	 * Setup to use first mem list entry, unless no data.
2733699d92aSKiran Patil 	 */
274ec98f782SAndy Grover 	BUG_ON(frame_len && !se_cmd->t_data_sg);
27505d1c7c0SAndy Grover 	if (frame_len) {
276ec98f782SAndy Grover 		sg = se_cmd->t_data_sg;
277ec98f782SAndy Grover 		mem_len = sg->length;
278ec98f782SAndy Grover 		mem_off = sg->offset;
279ec98f782SAndy Grover 		page = sg_page(sg);
2803699d92aSKiran Patil 	}
2813699d92aSKiran Patil 
2823699d92aSKiran Patil 	while (frame_len) {
2833699d92aSKiran Patil 		if (!mem_len) {
284ec98f782SAndy Grover 			sg = sg_next(sg);
285ec98f782SAndy Grover 			mem_len = sg->length;
286ec98f782SAndy Grover 			mem_off = sg->offset;
287ec98f782SAndy Grover 			page = sg_page(sg);
2883699d92aSKiran Patil 		}
2893699d92aSKiran Patil 		if (rel_off >= mem_len) {
2903699d92aSKiran Patil 			rel_off -= mem_len;
2913699d92aSKiran Patil 			mem_len = 0;
2923699d92aSKiran Patil 			continue;
2933699d92aSKiran Patil 		}
2943699d92aSKiran Patil 		mem_off += rel_off;
2953699d92aSKiran Patil 		mem_len -= rel_off;
2963699d92aSKiran Patil 		rel_off = 0;
2973699d92aSKiran Patil 
2983699d92aSKiran Patil 		tlen = min(mem_len, frame_len);
2993699d92aSKiran Patil 
300ca747d61SCong Wang 		to = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
3013699d92aSKiran Patil 		page_addr = to;
302b75d8063SGeliang Tang 		to += offset_in_page(mem_off);
3033699d92aSKiran Patil 		tlen = min(tlen, (size_t)(PAGE_SIZE -
304b75d8063SGeliang Tang 					  offset_in_page(mem_off)));
3053699d92aSKiran Patil 		memcpy(to, from, tlen);
306ca747d61SCong Wang 		kunmap_atomic(page_addr);
30705d1c7c0SAndy Grover 
3083699d92aSKiran Patil 		from += tlen;
3093699d92aSKiran Patil 		frame_len -= tlen;
3103699d92aSKiran Patil 		mem_off += tlen;
3113699d92aSKiran Patil 		mem_len -= tlen;
3123699d92aSKiran Patil 		cmd->write_data_len += tlen;
3133699d92aSKiran Patil 	}
3143699d92aSKiran Patil last_frame:
315b8b22533SChristoph Hellwig 	if (cmd->write_data_len == se_cmd->data_length) {
316b8b22533SChristoph Hellwig 		INIT_WORK(&cmd->work, ft_execute_work);
317b8b22533SChristoph Hellwig 		queue_work(cmd->sess->tport->tpg->workqueue, &cmd->work);
318b8b22533SChristoph Hellwig 	}
3193699d92aSKiran Patil drop:
3203699d92aSKiran Patil 	fc_frame_free(fp);
3213699d92aSKiran Patil }
322dcd998ccSKiran Patil 
323dcd998ccSKiran Patil /*
324dcd998ccSKiran Patil  * Handle and cleanup any HW specific resources if
325dcd998ccSKiran Patil  * received ABORTS, errors, timeouts.
326dcd998ccSKiran Patil  */
ft_invl_hw_context(struct ft_cmd * cmd)327dcd998ccSKiran Patil void ft_invl_hw_context(struct ft_cmd *cmd)
328dcd998ccSKiran Patil {
3297875f179SWei Yongjun 	struct fc_seq *seq;
330dcd998ccSKiran Patil 	struct fc_exch *ep = NULL;
331dcd998ccSKiran Patil 	struct fc_lport *lport = NULL;
332dcd998ccSKiran Patil 
333dcd998ccSKiran Patil 	BUG_ON(!cmd);
3347875f179SWei Yongjun 	seq = cmd->seq;
335dcd998ccSKiran Patil 
336dcd998ccSKiran Patil 	/* Cleanup the DDP context in HW if DDP was setup */
337dcd998ccSKiran Patil 	if (cmd->was_ddp_setup && seq) {
338dcd998ccSKiran Patil 		ep = fc_seq_exch(seq);
339dcd998ccSKiran Patil 		if (ep) {
340dcd998ccSKiran Patil 			lport = ep->lp;
341d556546eSDan Carpenter 			if (lport && (ep->xid <= lport->lro_xid)) {
342dcd998ccSKiran Patil 				/*
343dcd998ccSKiran Patil 				 * "ddp_done" trigger invalidation of HW
344dcd998ccSKiran Patil 				 * specific DDP context
345dcd998ccSKiran Patil 				 */
346dcd998ccSKiran Patil 				cmd->write_data_len = lport->tt.ddp_done(lport,
347dcd998ccSKiran Patil 								      ep->xid);
348dcd998ccSKiran Patil 
349dcd998ccSKiran Patil 				/*
350dcd998ccSKiran Patil 				 * Resetting same variable to indicate HW's
351dcd998ccSKiran Patil 				 * DDP context has been invalidated to avoid
352dcd998ccSKiran Patil 				 * re_invalidation of same context (context is
353dcd998ccSKiran Patil 				 * identified using ep->xid)
354dcd998ccSKiran Patil 				 */
355dcd998ccSKiran Patil 				cmd->was_ddp_setup = 0;
356dcd998ccSKiran Patil 			}
357dcd998ccSKiran Patil 		}
358dcd998ccSKiran Patil 	}
359d556546eSDan Carpenter }
360