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