13699d92aSKiran Patil /* 23699d92aSKiran Patil * Copyright (c) 2010 Cisco Systems, Inc. 33699d92aSKiran Patil * 43699d92aSKiran Patil * Portions based on tcm_loop_fabric_scsi.c and libfc/fc_fcp.c 53699d92aSKiran Patil * 63699d92aSKiran Patil * Copyright (c) 2007 Intel Corporation. All rights reserved. 73699d92aSKiran Patil * Copyright (c) 2008 Red Hat, Inc. All rights reserved. 83699d92aSKiran Patil * Copyright (c) 2008 Mike Christie 93699d92aSKiran Patil * Copyright (c) 2009 Rising Tide, Inc. 103699d92aSKiran Patil * Copyright (c) 2009 Linux-iSCSI.org 113699d92aSKiran Patil * Copyright (c) 2009 Nicholas A. Bellinger <nab@linux-iscsi.org> 123699d92aSKiran Patil * 133699d92aSKiran Patil * This program is free software; you can redistribute it and/or modify it 143699d92aSKiran Patil * under the terms and conditions of the GNU General Public License, 153699d92aSKiran Patil * version 2, as published by the Free Software Foundation. 163699d92aSKiran Patil * 173699d92aSKiran Patil * This program is distributed in the hope it will be useful, but WITHOUT 183699d92aSKiran Patil * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 193699d92aSKiran Patil * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 203699d92aSKiran Patil * more details. 213699d92aSKiran Patil * 223699d92aSKiran Patil * You should have received a copy of the GNU General Public License along with 233699d92aSKiran Patil * this program; if not, write to the Free Software Foundation, Inc., 243699d92aSKiran Patil * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. 253699d92aSKiran Patil */ 263699d92aSKiran Patil 273699d92aSKiran Patil /* XXX TBD some includes may be extraneous */ 283699d92aSKiran Patil 293699d92aSKiran Patil #include <linux/module.h> 303699d92aSKiran Patil #include <linux/moduleparam.h> 313699d92aSKiran Patil #include <linux/utsname.h> 323699d92aSKiran Patil #include <linux/init.h> 333699d92aSKiran Patil #include <linux/slab.h> 343699d92aSKiran Patil #include <linux/kthread.h> 353699d92aSKiran Patil #include <linux/types.h> 363699d92aSKiran Patil #include <linux/string.h> 373699d92aSKiran Patil #include <linux/configfs.h> 383699d92aSKiran Patil #include <linux/ctype.h> 393699d92aSKiran Patil #include <linux/hash.h> 406708bb27SAndy Grover #include <linux/ratelimit.h> 413699d92aSKiran Patil #include <asm/unaligned.h> 423699d92aSKiran Patil #include <scsi/scsi.h> 433699d92aSKiran Patil #include <scsi/scsi_host.h> 443699d92aSKiran Patil #include <scsi/scsi_device.h> 453699d92aSKiran Patil #include <scsi/scsi_cmnd.h> 463699d92aSKiran Patil #include <scsi/libfc.h> 473699d92aSKiran Patil #include <scsi/fc_encode.h> 483699d92aSKiran Patil 493699d92aSKiran Patil #include <target/target_core_base.h> 50c4795fb2SChristoph Hellwig #include <target/target_core_fabric.h> 513699d92aSKiran Patil #include <target/target_core_configfs.h> 523699d92aSKiran Patil #include <target/configfs_macros.h> 533699d92aSKiran Patil 543699d92aSKiran Patil #include "tcm_fc.h" 553699d92aSKiran Patil 563699d92aSKiran Patil /* 573699d92aSKiran Patil * Deliver read data back to initiator. 583699d92aSKiran Patil * XXX TBD handle resource problems later. 593699d92aSKiran Patil */ 603699d92aSKiran Patil int ft_queue_data_in(struct se_cmd *se_cmd) 613699d92aSKiran Patil { 623699d92aSKiran Patil struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd); 633699d92aSKiran Patil struct fc_frame *fp = NULL; 643699d92aSKiran Patil struct fc_exch *ep; 653699d92aSKiran Patil struct fc_lport *lport; 66ec98f782SAndy Grover struct scatterlist *sg = NULL; 673699d92aSKiran Patil size_t remaining; 683699d92aSKiran Patil u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF; 69ec98f782SAndy Grover u32 mem_off = 0; 703699d92aSKiran Patil u32 fh_off = 0; 713699d92aSKiran Patil u32 frame_off = 0; 723699d92aSKiran Patil size_t frame_len = 0; 73ec98f782SAndy Grover size_t mem_len = 0; 743699d92aSKiran Patil size_t tlen; 753699d92aSKiran Patil size_t off_in_page; 76ec98f782SAndy Grover struct page *page = NULL; 773699d92aSKiran Patil int use_sg; 783699d92aSKiran Patil int error; 793699d92aSKiran Patil void *page_addr; 803699d92aSKiran Patil void *from; 813699d92aSKiran Patil void *to = NULL; 823699d92aSKiran Patil 83e1c40382SMark Rustad if (cmd->aborted) 84e1c40382SMark Rustad return 0; 85b3e5fe16SNicholas Bellinger 86b3e5fe16SNicholas Bellinger if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL) 87b3e5fe16SNicholas Bellinger goto queue_status; 88b3e5fe16SNicholas Bellinger 893699d92aSKiran Patil ep = fc_seq_exch(cmd->seq); 903699d92aSKiran Patil lport = ep->lp; 913699d92aSKiran Patil cmd->seq = lport->tt.seq_start_next(cmd->seq); 923699d92aSKiran Patil 933699d92aSKiran Patil remaining = se_cmd->data_length; 943699d92aSKiran Patil 953699d92aSKiran Patil /* 9605d1c7c0SAndy Grover * Setup to use first mem list entry, unless no data. 973699d92aSKiran Patil */ 98ec98f782SAndy Grover BUG_ON(remaining && !se_cmd->t_data_sg); 9905d1c7c0SAndy Grover if (remaining) { 100ec98f782SAndy Grover sg = se_cmd->t_data_sg; 101ec98f782SAndy Grover mem_len = sg->length; 102ec98f782SAndy Grover mem_off = sg->offset; 103ec98f782SAndy Grover page = sg_page(sg); 1043699d92aSKiran Patil } 1053699d92aSKiran Patil 1063699d92aSKiran Patil /* no scatter/gather in skb for odd word length due to fc_seq_send() */ 1073699d92aSKiran Patil use_sg = !(remaining % 4); 1083699d92aSKiran Patil 1093699d92aSKiran Patil while (remaining) { 110d3682b1aSMark Rustad struct fc_seq *seq = cmd->seq; 111d3682b1aSMark Rustad 112d3682b1aSMark Rustad if (!seq) { 113d3682b1aSMark Rustad pr_debug("%s: Command aborted, xid 0x%x\n", 114d3682b1aSMark Rustad __func__, ep->xid); 115d3682b1aSMark Rustad break; 116d3682b1aSMark Rustad } 1173699d92aSKiran Patil if (!mem_len) { 118ec98f782SAndy Grover sg = sg_next(sg); 119ec98f782SAndy Grover mem_len = min((size_t)sg->length, remaining); 120ec98f782SAndy Grover mem_off = sg->offset; 121ec98f782SAndy Grover page = sg_page(sg); 1223699d92aSKiran Patil } 1233699d92aSKiran Patil if (!frame_len) { 1243699d92aSKiran Patil /* 1253699d92aSKiran Patil * If lport's has capability of Large Send Offload LSO) 1263699d92aSKiran Patil * , then allow 'frame_len' to be as big as 'lso_max' 1273699d92aSKiran Patil * if indicated transfer length is >= lport->lso_max 1283699d92aSKiran Patil */ 1293699d92aSKiran Patil frame_len = (lport->seq_offload) ? lport->lso_max : 1303699d92aSKiran Patil cmd->sess->max_frame; 1313699d92aSKiran Patil frame_len = min(frame_len, remaining); 1323699d92aSKiran Patil fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len); 1333699d92aSKiran Patil if (!fp) 1343699d92aSKiran Patil return -ENOMEM; 1353699d92aSKiran Patil to = fc_frame_payload_get(fp, 0); 1363699d92aSKiran Patil fh_off = frame_off; 1373699d92aSKiran Patil frame_off += frame_len; 1383699d92aSKiran Patil /* 1393699d92aSKiran Patil * Setup the frame's max payload which is used by base 1403699d92aSKiran Patil * driver to indicate HW about max frame size, so that 1413699d92aSKiran Patil * HW can do fragmentation appropriately based on 1423699d92aSKiran Patil * "gso_max_size" of underline netdev. 1433699d92aSKiran Patil */ 1443699d92aSKiran Patil fr_max_payload(fp) = cmd->sess->max_frame; 1453699d92aSKiran Patil } 1463699d92aSKiran Patil tlen = min(mem_len, frame_len); 1473699d92aSKiran Patil 1483699d92aSKiran Patil if (use_sg) { 1493699d92aSKiran Patil off_in_page = mem_off; 1503699d92aSKiran Patil BUG_ON(!page); 1513699d92aSKiran Patil get_page(page); 1523699d92aSKiran Patil skb_fill_page_desc(fp_skb(fp), 1533699d92aSKiran Patil skb_shinfo(fp_skb(fp))->nr_frags, 1543699d92aSKiran Patil page, off_in_page, tlen); 1553699d92aSKiran Patil fr_len(fp) += tlen; 1563699d92aSKiran Patil fp_skb(fp)->data_len += tlen; 1573699d92aSKiran Patil fp_skb(fp)->truesize += 1583699d92aSKiran Patil PAGE_SIZE << compound_order(page); 15905d1c7c0SAndy Grover } else { 1603699d92aSKiran Patil BUG_ON(!page); 161ca747d61SCong Wang from = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); 1623699d92aSKiran Patil page_addr = from; 1633699d92aSKiran Patil from += mem_off & ~PAGE_MASK; 1643699d92aSKiran Patil tlen = min(tlen, (size_t)(PAGE_SIZE - 1653699d92aSKiran Patil (mem_off & ~PAGE_MASK))); 1663699d92aSKiran Patil memcpy(to, from, tlen); 167ca747d61SCong Wang kunmap_atomic(page_addr); 1683699d92aSKiran Patil to += tlen; 1693699d92aSKiran Patil } 1703699d92aSKiran Patil 1713699d92aSKiran Patil mem_off += tlen; 1723699d92aSKiran Patil mem_len -= tlen; 1733699d92aSKiran Patil frame_len -= tlen; 1743699d92aSKiran Patil remaining -= tlen; 1753699d92aSKiran Patil 1763699d92aSKiran Patil if (frame_len && 1773699d92aSKiran Patil (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN)) 1783699d92aSKiran Patil continue; 1793699d92aSKiran Patil if (!remaining) 1803699d92aSKiran Patil f_ctl |= FC_FC_END_SEQ; 1813699d92aSKiran Patil fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, 1823699d92aSKiran Patil FC_TYPE_FCP, f_ctl, fh_off); 183d3682b1aSMark Rustad error = lport->tt.seq_send(lport, seq, fp); 1843699d92aSKiran Patil if (error) { 185b3e5fe16SNicholas Bellinger pr_info_ratelimited("%s: Failed to send frame %p, " 18695efa286SNicholas Bellinger "xid <0x%x>, remaining %zu, " 1873699d92aSKiran Patil "lso_max <0x%x>\n", 1883699d92aSKiran Patil __func__, fp, ep->xid, 1893699d92aSKiran Patil remaining, lport->lso_max); 190b3e5fe16SNicholas Bellinger /* 191b3e5fe16SNicholas Bellinger * Go ahead and set TASK_SET_FULL status ignoring the 192b3e5fe16SNicholas Bellinger * rest of the DataIN, and immediately attempt to 193b3e5fe16SNicholas Bellinger * send the response via ft_queue_status() in order 194b3e5fe16SNicholas Bellinger * to notify the initiator that it should reduce it's 195b3e5fe16SNicholas Bellinger * per LUN queue_depth. 196b3e5fe16SNicholas Bellinger */ 197b3e5fe16SNicholas Bellinger se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; 198b3e5fe16SNicholas Bellinger break; 1993699d92aSKiran Patil } 2003699d92aSKiran Patil } 201b3e5fe16SNicholas Bellinger queue_status: 2023699d92aSKiran Patil return ft_queue_status(se_cmd); 2033699d92aSKiran Patil } 2043699d92aSKiran Patil 205b8b22533SChristoph Hellwig static void ft_execute_work(struct work_struct *work) 206b8b22533SChristoph Hellwig { 207b8b22533SChristoph Hellwig struct ft_cmd *cmd = container_of(work, struct ft_cmd, work); 208b8b22533SChristoph Hellwig 209b8b22533SChristoph Hellwig target_execute_cmd(&cmd->se_cmd); 210b8b22533SChristoph Hellwig } 211b8b22533SChristoph Hellwig 2123699d92aSKiran Patil /* 2133699d92aSKiran Patil * Receive write data frame. 2143699d92aSKiran Patil */ 2153699d92aSKiran Patil void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp) 2163699d92aSKiran Patil { 2173699d92aSKiran Patil struct se_cmd *se_cmd = &cmd->se_cmd; 2183699d92aSKiran Patil struct fc_seq *seq = cmd->seq; 2193699d92aSKiran Patil struct fc_exch *ep; 2203699d92aSKiran Patil struct fc_lport *lport; 2213699d92aSKiran Patil struct fc_frame_header *fh; 222ec98f782SAndy Grover struct scatterlist *sg = NULL; 223ec98f782SAndy Grover u32 mem_off = 0; 2243699d92aSKiran Patil u32 rel_off; 2253699d92aSKiran Patil size_t frame_len; 226ec98f782SAndy Grover size_t mem_len = 0; 2273699d92aSKiran Patil size_t tlen; 228ec98f782SAndy Grover struct page *page = NULL; 2293699d92aSKiran Patil void *page_addr; 2303699d92aSKiran Patil void *from; 2313699d92aSKiran Patil void *to; 2323699d92aSKiran Patil u32 f_ctl; 2333699d92aSKiran Patil void *buf; 2343699d92aSKiran Patil 2353699d92aSKiran Patil fh = fc_frame_header_get(fp); 2363699d92aSKiran Patil if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF)) 2373699d92aSKiran Patil goto drop; 2383699d92aSKiran Patil 239dcd998ccSKiran Patil f_ctl = ntoh24(fh->fh_f_ctl); 240dcd998ccSKiran Patil ep = fc_seq_exch(seq); 241dcd998ccSKiran Patil lport = ep->lp; 242dcd998ccSKiran Patil if (cmd->was_ddp_setup) { 243dcd998ccSKiran Patil BUG_ON(!ep); 244dcd998ccSKiran Patil BUG_ON(!lport); 2453699d92aSKiran Patil /* 246079587b4SKiran Patil * Since DDP (Large Rx offload) was setup for this request, 247079587b4SKiran Patil * payload is expected to be copied directly to user buffers. 2483699d92aSKiran Patil */ 249dcd998ccSKiran Patil buf = fc_frame_payload_get(fp, 1); 250dcd998ccSKiran Patil if (buf) 251dcd998ccSKiran Patil pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, " 252dcd998ccSKiran Patil "cmd->sg_cnt 0x%x. DDP was setup" 253dcd998ccSKiran Patil " hence not expected to receive frame with " 254dcd998ccSKiran Patil "payload, Frame will be dropped if" 255dcd998ccSKiran Patil "'Sequence Initiative' bit in f_ctl is" 256dcd998ccSKiran Patil "not set\n", __func__, ep->xid, f_ctl, 257e182d682SRoland Dreier se_cmd->t_data_sg, se_cmd->t_data_nents); 2583699d92aSKiran Patil /* 259dcd998ccSKiran Patil * Invalidate HW DDP context if it was setup for respective 260dcd998ccSKiran Patil * command. Invalidation of HW DDP context is requited in both 261dcd998ccSKiran Patil * situation (success and error). 2623699d92aSKiran Patil */ 263dcd998ccSKiran Patil ft_invl_hw_context(cmd); 264dcd998ccSKiran Patil 265dcd998ccSKiran Patil /* 266dcd998ccSKiran Patil * If "Sequence Initiative (TSI)" bit set in f_ctl, means last 267dcd998ccSKiran Patil * write data frame is received successfully where payload is 268dcd998ccSKiran Patil * posted directly to user buffer and only the last frame's 269dcd998ccSKiran Patil * header is posted in receive queue. 270dcd998ccSKiran Patil * 271dcd998ccSKiran Patil * If "Sequence Initiative (TSI)" bit is not set, means error 272dcd998ccSKiran Patil * condition w.r.t. DDP, hence drop the packet and let explict 273dcd998ccSKiran Patil * ABORTS from other end of exchange timer trigger the recovery. 274dcd998ccSKiran Patil */ 275dcd998ccSKiran Patil if (f_ctl & FC_FC_SEQ_INIT) 2763699d92aSKiran Patil goto last_frame; 277dcd998ccSKiran Patil else 2783699d92aSKiran Patil goto drop; 279079587b4SKiran Patil } 2803699d92aSKiran Patil 2813699d92aSKiran Patil rel_off = ntohl(fh->fh_parm_offset); 2823699d92aSKiran Patil frame_len = fr_len(fp); 2833699d92aSKiran Patil if (frame_len <= sizeof(*fh)) 2843699d92aSKiran Patil goto drop; 2853699d92aSKiran Patil frame_len -= sizeof(*fh); 2863699d92aSKiran Patil from = fc_frame_payload_get(fp, 0); 2873699d92aSKiran Patil if (rel_off >= se_cmd->data_length) 2883699d92aSKiran Patil goto drop; 2893699d92aSKiran Patil if (frame_len + rel_off > se_cmd->data_length) 2903699d92aSKiran Patil frame_len = se_cmd->data_length - rel_off; 2913699d92aSKiran Patil 2923699d92aSKiran Patil /* 29305d1c7c0SAndy Grover * Setup to use first mem list entry, unless no data. 2943699d92aSKiran Patil */ 295ec98f782SAndy Grover BUG_ON(frame_len && !se_cmd->t_data_sg); 29605d1c7c0SAndy Grover if (frame_len) { 297ec98f782SAndy Grover sg = se_cmd->t_data_sg; 298ec98f782SAndy Grover mem_len = sg->length; 299ec98f782SAndy Grover mem_off = sg->offset; 300ec98f782SAndy Grover page = sg_page(sg); 3013699d92aSKiran Patil } 3023699d92aSKiran Patil 3033699d92aSKiran Patil while (frame_len) { 3043699d92aSKiran Patil if (!mem_len) { 305ec98f782SAndy Grover sg = sg_next(sg); 306ec98f782SAndy Grover mem_len = sg->length; 307ec98f782SAndy Grover mem_off = sg->offset; 308ec98f782SAndy Grover page = sg_page(sg); 3093699d92aSKiran Patil } 3103699d92aSKiran Patil if (rel_off >= mem_len) { 3113699d92aSKiran Patil rel_off -= mem_len; 3123699d92aSKiran Patil mem_len = 0; 3133699d92aSKiran Patil continue; 3143699d92aSKiran Patil } 3153699d92aSKiran Patil mem_off += rel_off; 3163699d92aSKiran Patil mem_len -= rel_off; 3173699d92aSKiran Patil rel_off = 0; 3183699d92aSKiran Patil 3193699d92aSKiran Patil tlen = min(mem_len, frame_len); 3203699d92aSKiran Patil 321ca747d61SCong Wang to = kmap_atomic(page + (mem_off >> PAGE_SHIFT)); 3223699d92aSKiran Patil page_addr = to; 3233699d92aSKiran Patil to += mem_off & ~PAGE_MASK; 3243699d92aSKiran Patil tlen = min(tlen, (size_t)(PAGE_SIZE - 3253699d92aSKiran Patil (mem_off & ~PAGE_MASK))); 3263699d92aSKiran Patil memcpy(to, from, tlen); 327ca747d61SCong Wang kunmap_atomic(page_addr); 32805d1c7c0SAndy Grover 3293699d92aSKiran Patil from += tlen; 3303699d92aSKiran Patil frame_len -= tlen; 3313699d92aSKiran Patil mem_off += tlen; 3323699d92aSKiran Patil mem_len -= tlen; 3333699d92aSKiran Patil cmd->write_data_len += tlen; 3343699d92aSKiran Patil } 3353699d92aSKiran Patil last_frame: 336b8b22533SChristoph Hellwig if (cmd->write_data_len == se_cmd->data_length) { 337b8b22533SChristoph Hellwig INIT_WORK(&cmd->work, ft_execute_work); 338b8b22533SChristoph Hellwig queue_work(cmd->sess->tport->tpg->workqueue, &cmd->work); 339b8b22533SChristoph Hellwig } 3403699d92aSKiran Patil drop: 3413699d92aSKiran Patil fc_frame_free(fp); 3423699d92aSKiran Patil } 343dcd998ccSKiran Patil 344dcd998ccSKiran Patil /* 345dcd998ccSKiran Patil * Handle and cleanup any HW specific resources if 346dcd998ccSKiran Patil * received ABORTS, errors, timeouts. 347dcd998ccSKiran Patil */ 348dcd998ccSKiran Patil void ft_invl_hw_context(struct ft_cmd *cmd) 349dcd998ccSKiran Patil { 3507875f179SWei Yongjun struct fc_seq *seq; 351dcd998ccSKiran Patil struct fc_exch *ep = NULL; 352dcd998ccSKiran Patil struct fc_lport *lport = NULL; 353dcd998ccSKiran Patil 354dcd998ccSKiran Patil BUG_ON(!cmd); 3557875f179SWei Yongjun seq = cmd->seq; 356dcd998ccSKiran Patil 357dcd998ccSKiran Patil /* Cleanup the DDP context in HW if DDP was setup */ 358dcd998ccSKiran Patil if (cmd->was_ddp_setup && seq) { 359dcd998ccSKiran Patil ep = fc_seq_exch(seq); 360dcd998ccSKiran Patil if (ep) { 361dcd998ccSKiran Patil lport = ep->lp; 362d556546eSDan Carpenter if (lport && (ep->xid <= lport->lro_xid)) { 363dcd998ccSKiran Patil /* 364dcd998ccSKiran Patil * "ddp_done" trigger invalidation of HW 365dcd998ccSKiran Patil * specific DDP context 366dcd998ccSKiran Patil */ 367dcd998ccSKiran Patil cmd->write_data_len = lport->tt.ddp_done(lport, 368dcd998ccSKiran Patil ep->xid); 369dcd998ccSKiran Patil 370dcd998ccSKiran Patil /* 371dcd998ccSKiran Patil * Resetting same variable to indicate HW's 372dcd998ccSKiran Patil * DDP context has been invalidated to avoid 373dcd998ccSKiran Patil * re_invalidation of same context (context is 374dcd998ccSKiran Patil * identified using ep->xid) 375dcd998ccSKiran Patil */ 376dcd998ccSKiran Patil cmd->was_ddp_setup = 0; 377dcd998ccSKiran Patil } 378dcd998ccSKiran Patil } 379dcd998ccSKiran Patil } 380d556546eSDan Carpenter } 381