xref: /openbmc/linux/drivers/target/tcm_fc/tfc_io.c (revision a61127c2)
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 #include <scsi/fc_encode.h>
323699d92aSKiran Patil 
333699d92aSKiran Patil #include <target/target_core_base.h>
34c4795fb2SChristoph Hellwig #include <target/target_core_fabric.h>
353699d92aSKiran Patil 
363699d92aSKiran Patil #include "tcm_fc.h"
373699d92aSKiran Patil 
383699d92aSKiran Patil /*
393699d92aSKiran Patil  * Deliver read data back to initiator.
403699d92aSKiran Patil  * XXX TBD handle resource problems later.
413699d92aSKiran Patil  */
423699d92aSKiran Patil int ft_queue_data_in(struct se_cmd *se_cmd)
433699d92aSKiran Patil {
443699d92aSKiran Patil 	struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
453699d92aSKiran Patil 	struct fc_frame *fp = NULL;
463699d92aSKiran Patil 	struct fc_exch *ep;
473699d92aSKiran Patil 	struct fc_lport *lport;
48ec98f782SAndy Grover 	struct scatterlist *sg = NULL;
493699d92aSKiran Patil 	size_t remaining;
503699d92aSKiran Patil 	u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF;
51ec98f782SAndy Grover 	u32 mem_off = 0;
523699d92aSKiran Patil 	u32 fh_off = 0;
533699d92aSKiran Patil 	u32 frame_off = 0;
543699d92aSKiran Patil 	size_t frame_len = 0;
55ec98f782SAndy Grover 	size_t mem_len = 0;
563699d92aSKiran Patil 	size_t tlen;
573699d92aSKiran Patil 	size_t off_in_page;
58ec98f782SAndy Grover 	struct page *page = NULL;
593699d92aSKiran Patil 	int use_sg;
603699d92aSKiran Patil 	int error;
613699d92aSKiran Patil 	void *page_addr;
623699d92aSKiran Patil 	void *from;
633699d92aSKiran Patil 	void *to = NULL;
643699d92aSKiran Patil 
65e1c40382SMark Rustad 	if (cmd->aborted)
66e1c40382SMark Rustad 		return 0;
67b3e5fe16SNicholas Bellinger 
68b3e5fe16SNicholas Bellinger 	if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL)
69b3e5fe16SNicholas Bellinger 		goto queue_status;
70b3e5fe16SNicholas Bellinger 
713699d92aSKiran Patil 	ep = fc_seq_exch(cmd->seq);
723699d92aSKiran Patil 	lport = ep->lp;
73c6865b30SHannes Reinecke 	cmd->seq = fc_seq_start_next(cmd->seq);
743699d92aSKiran Patil 
753699d92aSKiran Patil 	remaining = se_cmd->data_length;
763699d92aSKiran Patil 
773699d92aSKiran Patil 	/*
7805d1c7c0SAndy Grover 	 * Setup to use first mem list entry, unless no data.
793699d92aSKiran Patil 	 */
80ec98f782SAndy Grover 	BUG_ON(remaining && !se_cmd->t_data_sg);
8105d1c7c0SAndy Grover 	if (remaining) {
82ec98f782SAndy Grover 		sg = se_cmd->t_data_sg;
83ec98f782SAndy Grover 		mem_len = sg->length;
84ec98f782SAndy Grover 		mem_off = sg->offset;
85ec98f782SAndy Grover 		page = sg_page(sg);
863699d92aSKiran Patil 	}
873699d92aSKiran Patil 
883699d92aSKiran Patil 	/* no scatter/gather in skb for odd word length due to fc_seq_send() */
893699d92aSKiran Patil 	use_sg = !(remaining % 4);
903699d92aSKiran Patil 
913699d92aSKiran Patil 	while (remaining) {
92d3682b1aSMark Rustad 		struct fc_seq *seq = cmd->seq;
93d3682b1aSMark Rustad 
94d3682b1aSMark Rustad 		if (!seq) {
95d3682b1aSMark Rustad 			pr_debug("%s: Command aborted, xid 0x%x\n",
96d3682b1aSMark Rustad 				 __func__, ep->xid);
97d3682b1aSMark Rustad 			break;
98d3682b1aSMark Rustad 		}
993699d92aSKiran Patil 		if (!mem_len) {
100ec98f782SAndy Grover 			sg = sg_next(sg);
101ec98f782SAndy Grover 			mem_len = min((size_t)sg->length, remaining);
102ec98f782SAndy Grover 			mem_off = sg->offset;
103ec98f782SAndy Grover 			page = sg_page(sg);
1043699d92aSKiran Patil 		}
1053699d92aSKiran Patil 		if (!frame_len) {
1063699d92aSKiran Patil 			/*
1073699d92aSKiran Patil 			 * If lport's has capability of Large Send Offload LSO)
1083699d92aSKiran Patil 			 * , then allow 'frame_len' to be as big as 'lso_max'
1093699d92aSKiran Patil 			 * if indicated transfer length is >= lport->lso_max
1103699d92aSKiran Patil 			 */
1113699d92aSKiran Patil 			frame_len = (lport->seq_offload) ? lport->lso_max :
1123699d92aSKiran Patil 							  cmd->sess->max_frame;
1133699d92aSKiran Patil 			frame_len = min(frame_len, remaining);
1143699d92aSKiran Patil 			fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len);
1153699d92aSKiran Patil 			if (!fp)
1163699d92aSKiran Patil 				return -ENOMEM;
1173699d92aSKiran Patil 			to = fc_frame_payload_get(fp, 0);
1183699d92aSKiran Patil 			fh_off = frame_off;
1193699d92aSKiran Patil 			frame_off += frame_len;
1203699d92aSKiran Patil 			/*
1213699d92aSKiran Patil 			 * Setup the frame's max payload which is used by base
1223699d92aSKiran Patil 			 * driver to indicate HW about max frame size, so that
1233699d92aSKiran Patil 			 * HW can do fragmentation appropriately based on
1243699d92aSKiran Patil 			 * "gso_max_size" of underline netdev.
1253699d92aSKiran Patil 			 */
1263699d92aSKiran Patil 			fr_max_payload(fp) = cmd->sess->max_frame;
1273699d92aSKiran Patil 		}
1283699d92aSKiran Patil 		tlen = min(mem_len, frame_len);
1293699d92aSKiran Patil 
1303699d92aSKiran Patil 		if (use_sg) {
1313699d92aSKiran Patil 			off_in_page = mem_off;
1323699d92aSKiran Patil 			BUG_ON(!page);
1333699d92aSKiran Patil 			get_page(page);
1343699d92aSKiran Patil 			skb_fill_page_desc(fp_skb(fp),
1353699d92aSKiran Patil 					   skb_shinfo(fp_skb(fp))->nr_frags,
1363699d92aSKiran Patil 					   page, off_in_page, tlen);
1373699d92aSKiran Patil 			fr_len(fp) += tlen;
1383699d92aSKiran Patil 			fp_skb(fp)->data_len += tlen;
1393699d92aSKiran Patil 			fp_skb(fp)->truesize +=
1403699d92aSKiran Patil 					PAGE_SIZE << compound_order(page);
14105d1c7c0SAndy Grover 		} else {
1423699d92aSKiran Patil 			BUG_ON(!page);
143ca747d61SCong Wang 			from = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
1443699d92aSKiran Patil 			page_addr = from;
145b75d8063SGeliang Tang 			from += offset_in_page(mem_off);
1463699d92aSKiran Patil 			tlen = min(tlen, (size_t)(PAGE_SIZE -
147b75d8063SGeliang Tang 						offset_in_page(mem_off)));
1483699d92aSKiran Patil 			memcpy(to, from, tlen);
149ca747d61SCong Wang 			kunmap_atomic(page_addr);
1503699d92aSKiran Patil 			to += tlen;
1513699d92aSKiran Patil 		}
1523699d92aSKiran Patil 
1533699d92aSKiran Patil 		mem_off += tlen;
1543699d92aSKiran Patil 		mem_len -= tlen;
1553699d92aSKiran Patil 		frame_len -= tlen;
1563699d92aSKiran Patil 		remaining -= tlen;
1573699d92aSKiran Patil 
1583699d92aSKiran Patil 		if (frame_len &&
1593699d92aSKiran Patil 		    (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN))
1603699d92aSKiran Patil 			continue;
1613699d92aSKiran Patil 		if (!remaining)
1623699d92aSKiran Patil 			f_ctl |= FC_FC_END_SEQ;
1633699d92aSKiran Patil 		fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
1643699d92aSKiran Patil 			       FC_TYPE_FCP, f_ctl, fh_off);
1650cac937dSHannes Reinecke 		error = fc_seq_send(lport, seq, fp);
1663699d92aSKiran Patil 		if (error) {
167b3e5fe16SNicholas Bellinger 			pr_info_ratelimited("%s: Failed to send frame %p, "
16895efa286SNicholas Bellinger 						"xid <0x%x>, remaining %zu, "
1693699d92aSKiran Patil 						"lso_max <0x%x>\n",
1703699d92aSKiran Patil 						__func__, fp, ep->xid,
1713699d92aSKiran Patil 						remaining, lport->lso_max);
172b3e5fe16SNicholas Bellinger 			/*
173b3e5fe16SNicholas Bellinger 			 * Go ahead and set TASK_SET_FULL status ignoring the
174b3e5fe16SNicholas Bellinger 			 * rest of the DataIN, and immediately attempt to
175b3e5fe16SNicholas Bellinger 			 * send the response via ft_queue_status() in order
176b3e5fe16SNicholas Bellinger 			 * to notify the initiator that it should reduce it's
177b3e5fe16SNicholas Bellinger 			 * per LUN queue_depth.
178b3e5fe16SNicholas Bellinger 			 */
179b3e5fe16SNicholas Bellinger 			se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
180b3e5fe16SNicholas Bellinger 			break;
1813699d92aSKiran Patil 		}
1823699d92aSKiran Patil 	}
183b3e5fe16SNicholas Bellinger queue_status:
1843699d92aSKiran Patil 	return ft_queue_status(se_cmd);
1853699d92aSKiran Patil }
1863699d92aSKiran Patil 
187b8b22533SChristoph Hellwig static void ft_execute_work(struct work_struct *work)
188b8b22533SChristoph Hellwig {
189b8b22533SChristoph Hellwig 	struct ft_cmd *cmd = container_of(work, struct ft_cmd, work);
190b8b22533SChristoph Hellwig 
191b8b22533SChristoph Hellwig 	target_execute_cmd(&cmd->se_cmd);
192b8b22533SChristoph Hellwig }
193b8b22533SChristoph Hellwig 
1943699d92aSKiran Patil /*
1953699d92aSKiran Patil  * Receive write data frame.
1963699d92aSKiran Patil  */
1973699d92aSKiran Patil void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
1983699d92aSKiran Patil {
1993699d92aSKiran Patil 	struct se_cmd *se_cmd = &cmd->se_cmd;
2003699d92aSKiran Patil 	struct fc_seq *seq = cmd->seq;
2013699d92aSKiran Patil 	struct fc_exch *ep;
2023699d92aSKiran Patil 	struct fc_lport *lport;
2033699d92aSKiran Patil 	struct fc_frame_header *fh;
204ec98f782SAndy Grover 	struct scatterlist *sg = NULL;
205ec98f782SAndy Grover 	u32 mem_off = 0;
2063699d92aSKiran Patil 	u32 rel_off;
2073699d92aSKiran Patil 	size_t frame_len;
208ec98f782SAndy Grover 	size_t mem_len = 0;
2093699d92aSKiran Patil 	size_t tlen;
210ec98f782SAndy Grover 	struct page *page = NULL;
2113699d92aSKiran Patil 	void *page_addr;
2123699d92aSKiran Patil 	void *from;
2133699d92aSKiran Patil 	void *to;
2143699d92aSKiran Patil 	u32 f_ctl;
2153699d92aSKiran Patil 	void *buf;
2163699d92aSKiran Patil 
2173699d92aSKiran Patil 	fh = fc_frame_header_get(fp);
2183699d92aSKiran Patil 	if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
2193699d92aSKiran Patil 		goto drop;
2203699d92aSKiran Patil 
221dcd998ccSKiran Patil 	f_ctl = ntoh24(fh->fh_f_ctl);
222dcd998ccSKiran Patil 	ep = fc_seq_exch(seq);
223dcd998ccSKiran Patil 	lport = ep->lp;
224dcd998ccSKiran Patil 	if (cmd->was_ddp_setup) {
225dcd998ccSKiran Patil 		BUG_ON(!ep);
226dcd998ccSKiran Patil 		BUG_ON(!lport);
2273699d92aSKiran Patil 		/*
228079587b4SKiran Patil 		 * Since DDP (Large Rx offload) was setup for this request,
229079587b4SKiran Patil 		 * payload is expected to be copied directly to user buffers.
2303699d92aSKiran Patil 		 */
231dcd998ccSKiran Patil 		buf = fc_frame_payload_get(fp, 1);
232dcd998ccSKiran Patil 		if (buf)
233dcd998ccSKiran Patil 			pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
234dcd998ccSKiran Patil 				"cmd->sg_cnt 0x%x. DDP was setup"
235dcd998ccSKiran Patil 				" hence not expected to receive frame with "
236dcd998ccSKiran Patil 				"payload, Frame will be dropped if"
237dcd998ccSKiran Patil 				"'Sequence Initiative' bit in f_ctl is"
238dcd998ccSKiran Patil 				"not set\n", __func__, ep->xid, f_ctl,
239e182d682SRoland Dreier 				se_cmd->t_data_sg, se_cmd->t_data_nents);
2403699d92aSKiran Patil 		/*
241dcd998ccSKiran Patil 		 * Invalidate HW DDP context if it was setup for respective
242dcd998ccSKiran Patil 		 * command. Invalidation of HW DDP context is requited in both
243dcd998ccSKiran Patil 		 * situation (success and error).
2443699d92aSKiran Patil 		 */
245dcd998ccSKiran Patil 		ft_invl_hw_context(cmd);
246dcd998ccSKiran Patil 
247dcd998ccSKiran Patil 		/*
248dcd998ccSKiran Patil 		 * If "Sequence Initiative (TSI)" bit set in f_ctl, means last
249dcd998ccSKiran Patil 		 * write data frame is received successfully where payload is
250dcd998ccSKiran Patil 		 * posted directly to user buffer and only the last frame's
251dcd998ccSKiran Patil 		 * header is posted in receive queue.
252dcd998ccSKiran Patil 		 *
253dcd998ccSKiran Patil 		 * If "Sequence Initiative (TSI)" bit is not set, means error
254dcd998ccSKiran Patil 		 * condition w.r.t. DDP, hence drop the packet and let explict
255dcd998ccSKiran Patil 		 * ABORTS from other end of exchange timer trigger the recovery.
256dcd998ccSKiran Patil 		 */
257dcd998ccSKiran Patil 		if (f_ctl & FC_FC_SEQ_INIT)
2583699d92aSKiran Patil 			goto last_frame;
259dcd998ccSKiran Patil 		else
2603699d92aSKiran Patil 			goto drop;
261079587b4SKiran Patil 	}
2623699d92aSKiran Patil 
2633699d92aSKiran Patil 	rel_off = ntohl(fh->fh_parm_offset);
2643699d92aSKiran Patil 	frame_len = fr_len(fp);
2653699d92aSKiran Patil 	if (frame_len <= sizeof(*fh))
2663699d92aSKiran Patil 		goto drop;
2673699d92aSKiran Patil 	frame_len -= sizeof(*fh);
2683699d92aSKiran Patil 	from = fc_frame_payload_get(fp, 0);
2693699d92aSKiran Patil 	if (rel_off >= se_cmd->data_length)
2703699d92aSKiran Patil 		goto drop;
2713699d92aSKiran Patil 	if (frame_len + rel_off > se_cmd->data_length)
2723699d92aSKiran Patil 		frame_len = se_cmd->data_length - rel_off;
2733699d92aSKiran Patil 
2743699d92aSKiran Patil 	/*
27505d1c7c0SAndy Grover 	 * Setup to use first mem list entry, unless no data.
2763699d92aSKiran Patil 	 */
277ec98f782SAndy Grover 	BUG_ON(frame_len && !se_cmd->t_data_sg);
27805d1c7c0SAndy Grover 	if (frame_len) {
279ec98f782SAndy Grover 		sg = se_cmd->t_data_sg;
280ec98f782SAndy Grover 		mem_len = sg->length;
281ec98f782SAndy Grover 		mem_off = sg->offset;
282ec98f782SAndy Grover 		page = sg_page(sg);
2833699d92aSKiran Patil 	}
2843699d92aSKiran Patil 
2853699d92aSKiran Patil 	while (frame_len) {
2863699d92aSKiran Patil 		if (!mem_len) {
287ec98f782SAndy Grover 			sg = sg_next(sg);
288ec98f782SAndy Grover 			mem_len = sg->length;
289ec98f782SAndy Grover 			mem_off = sg->offset;
290ec98f782SAndy Grover 			page = sg_page(sg);
2913699d92aSKiran Patil 		}
2923699d92aSKiran Patil 		if (rel_off >= mem_len) {
2933699d92aSKiran Patil 			rel_off -= mem_len;
2943699d92aSKiran Patil 			mem_len = 0;
2953699d92aSKiran Patil 			continue;
2963699d92aSKiran Patil 		}
2973699d92aSKiran Patil 		mem_off += rel_off;
2983699d92aSKiran Patil 		mem_len -= rel_off;
2993699d92aSKiran Patil 		rel_off = 0;
3003699d92aSKiran Patil 
3013699d92aSKiran Patil 		tlen = min(mem_len, frame_len);
3023699d92aSKiran Patil 
303ca747d61SCong Wang 		to = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
3043699d92aSKiran Patil 		page_addr = to;
305b75d8063SGeliang Tang 		to += offset_in_page(mem_off);
3063699d92aSKiran Patil 		tlen = min(tlen, (size_t)(PAGE_SIZE -
307b75d8063SGeliang Tang 					  offset_in_page(mem_off)));
3083699d92aSKiran Patil 		memcpy(to, from, tlen);
309ca747d61SCong Wang 		kunmap_atomic(page_addr);
31005d1c7c0SAndy Grover 
3113699d92aSKiran Patil 		from += tlen;
3123699d92aSKiran Patil 		frame_len -= tlen;
3133699d92aSKiran Patil 		mem_off += tlen;
3143699d92aSKiran Patil 		mem_len -= tlen;
3153699d92aSKiran Patil 		cmd->write_data_len += tlen;
3163699d92aSKiran Patil 	}
3173699d92aSKiran Patil last_frame:
318b8b22533SChristoph Hellwig 	if (cmd->write_data_len == se_cmd->data_length) {
319b8b22533SChristoph Hellwig 		INIT_WORK(&cmd->work, ft_execute_work);
320b8b22533SChristoph Hellwig 		queue_work(cmd->sess->tport->tpg->workqueue, &cmd->work);
321b8b22533SChristoph Hellwig 	}
3223699d92aSKiran Patil drop:
3233699d92aSKiran Patil 	fc_frame_free(fp);
3243699d92aSKiran Patil }
325dcd998ccSKiran Patil 
326dcd998ccSKiran Patil /*
327dcd998ccSKiran Patil  * Handle and cleanup any HW specific resources if
328dcd998ccSKiran Patil  * received ABORTS, errors, timeouts.
329dcd998ccSKiran Patil  */
330dcd998ccSKiran Patil void ft_invl_hw_context(struct ft_cmd *cmd)
331dcd998ccSKiran Patil {
3327875f179SWei Yongjun 	struct fc_seq *seq;
333dcd998ccSKiran Patil 	struct fc_exch *ep = NULL;
334dcd998ccSKiran Patil 	struct fc_lport *lport = NULL;
335dcd998ccSKiran Patil 
336dcd998ccSKiran Patil 	BUG_ON(!cmd);
3377875f179SWei Yongjun 	seq = cmd->seq;
338dcd998ccSKiran Patil 
339dcd998ccSKiran Patil 	/* Cleanup the DDP context in HW if DDP was setup */
340dcd998ccSKiran Patil 	if (cmd->was_ddp_setup && seq) {
341dcd998ccSKiran Patil 		ep = fc_seq_exch(seq);
342dcd998ccSKiran Patil 		if (ep) {
343dcd998ccSKiran Patil 			lport = ep->lp;
344d556546eSDan Carpenter 			if (lport && (ep->xid <= lport->lro_xid)) {
345dcd998ccSKiran Patil 				/*
346dcd998ccSKiran Patil 				 * "ddp_done" trigger invalidation of HW
347dcd998ccSKiran Patil 				 * specific DDP context
348dcd998ccSKiran Patil 				 */
349dcd998ccSKiran Patil 				cmd->write_data_len = lport->tt.ddp_done(lport,
350dcd998ccSKiran Patil 								      ep->xid);
351dcd998ccSKiran Patil 
352dcd998ccSKiran Patil 				/*
353dcd998ccSKiran Patil 				 * Resetting same variable to indicate HW's
354dcd998ccSKiran Patil 				 * DDP context has been invalidated to avoid
355dcd998ccSKiran Patil 				 * re_invalidation of same context (context is
356dcd998ccSKiran Patil 				 * identified using ep->xid)
357dcd998ccSKiran Patil 				 */
358dcd998ccSKiran Patil 				cmd->was_ddp_setup = 0;
359dcd998ccSKiran Patil 			}
360dcd998ccSKiran Patil 		}
361dcd998ccSKiran Patil 	}
362d556546eSDan Carpenter }
363