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