Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2010 Cisco Systems, Inc.
0004  *
0005  * Portions based on tcm_loop_fabric_scsi.c and libfc/fc_fcp.c
0006  *
0007  * Copyright (c) 2007 Intel Corporation. All rights reserved.
0008  * Copyright (c) 2008 Red Hat, Inc.  All rights reserved.
0009  * Copyright (c) 2008 Mike Christie
0010  * Copyright (c) 2009 Rising Tide, Inc.
0011  * Copyright (c) 2009 Linux-iSCSI.org
0012  * Copyright (c) 2009 Nicholas A. Bellinger <nab@linux-iscsi.org>
0013  */
0014 
0015 /* XXX TBD some includes may be extraneous */
0016 
0017 #include <linux/module.h>
0018 #include <linux/moduleparam.h>
0019 #include <linux/utsname.h>
0020 #include <linux/init.h>
0021 #include <linux/slab.h>
0022 #include <linux/kthread.h>
0023 #include <linux/types.h>
0024 #include <linux/string.h>
0025 #include <linux/configfs.h>
0026 #include <linux/ctype.h>
0027 #include <linux/hash.h>
0028 #include <linux/ratelimit.h>
0029 #include <asm/unaligned.h>
0030 #include <scsi/libfc.h>
0031 
0032 #include <target/target_core_base.h>
0033 #include <target/target_core_fabric.h>
0034 
0035 #include "tcm_fc.h"
0036 
0037 /*
0038  * Deliver read data back to initiator.
0039  * XXX TBD handle resource problems later.
0040  */
0041 int ft_queue_data_in(struct se_cmd *se_cmd)
0042 {
0043     struct ft_cmd *cmd = container_of(se_cmd, struct ft_cmd, se_cmd);
0044     struct fc_frame *fp = NULL;
0045     struct fc_exch *ep;
0046     struct fc_lport *lport;
0047     struct scatterlist *sg = NULL;
0048     size_t remaining;
0049     u32 f_ctl = FC_FC_EX_CTX | FC_FC_REL_OFF;
0050     u32 mem_off = 0;
0051     u32 fh_off = 0;
0052     u32 frame_off = 0;
0053     size_t frame_len = 0;
0054     size_t mem_len = 0;
0055     size_t tlen;
0056     size_t off_in_page;
0057     struct page *page = NULL;
0058     int use_sg;
0059     int error;
0060     void *page_addr;
0061     void *from;
0062     void *to = NULL;
0063 
0064     if (cmd->aborted)
0065         return 0;
0066 
0067     if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL)
0068         goto queue_status;
0069 
0070     ep = fc_seq_exch(cmd->seq);
0071     lport = ep->lp;
0072     cmd->seq = fc_seq_start_next(cmd->seq);
0073 
0074     remaining = se_cmd->data_length;
0075 
0076     /*
0077      * Setup to use first mem list entry, unless no data.
0078      */
0079     BUG_ON(remaining && !se_cmd->t_data_sg);
0080     if (remaining) {
0081         sg = se_cmd->t_data_sg;
0082         mem_len = sg->length;
0083         mem_off = sg->offset;
0084         page = sg_page(sg);
0085     }
0086 
0087     /* no scatter/gather in skb for odd word length due to fc_seq_send() */
0088     use_sg = !(remaining % 4);
0089 
0090     while (remaining) {
0091         struct fc_seq *seq = cmd->seq;
0092 
0093         if (!seq) {
0094             pr_debug("%s: Command aborted, xid 0x%x\n",
0095                  __func__, ep->xid);
0096             break;
0097         }
0098         if (!mem_len) {
0099             sg = sg_next(sg);
0100             mem_len = min((size_t)sg->length, remaining);
0101             mem_off = sg->offset;
0102             page = sg_page(sg);
0103         }
0104         if (!frame_len) {
0105             /*
0106              * If lport's has capability of Large Send Offload LSO)
0107              * , then allow 'frame_len' to be as big as 'lso_max'
0108              * if indicated transfer length is >= lport->lso_max
0109              */
0110             frame_len = (lport->seq_offload) ? lport->lso_max :
0111                               cmd->sess->max_frame;
0112             frame_len = min(frame_len, remaining);
0113             fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len);
0114             if (!fp)
0115                 return -ENOMEM;
0116             to = fc_frame_payload_get(fp, 0);
0117             fh_off = frame_off;
0118             frame_off += frame_len;
0119             /*
0120              * Setup the frame's max payload which is used by base
0121              * driver to indicate HW about max frame size, so that
0122              * HW can do fragmentation appropriately based on
0123              * "gso_max_size" of underline netdev.
0124              */
0125             fr_max_payload(fp) = cmd->sess->max_frame;
0126         }
0127         tlen = min(mem_len, frame_len);
0128 
0129         if (use_sg) {
0130             off_in_page = mem_off;
0131             BUG_ON(!page);
0132             get_page(page);
0133             skb_fill_page_desc(fp_skb(fp),
0134                        skb_shinfo(fp_skb(fp))->nr_frags,
0135                        page, off_in_page, tlen);
0136             fr_len(fp) += tlen;
0137             fp_skb(fp)->data_len += tlen;
0138             fp_skb(fp)->truesize += page_size(page);
0139         } else {
0140             BUG_ON(!page);
0141             from = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
0142             page_addr = from;
0143             from += offset_in_page(mem_off);
0144             tlen = min(tlen, (size_t)(PAGE_SIZE -
0145                         offset_in_page(mem_off)));
0146             memcpy(to, from, tlen);
0147             kunmap_atomic(page_addr);
0148             to += tlen;
0149         }
0150 
0151         mem_off += tlen;
0152         mem_len -= tlen;
0153         frame_len -= tlen;
0154         remaining -= tlen;
0155 
0156         if (frame_len &&
0157             (skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN))
0158             continue;
0159         if (!remaining)
0160             f_ctl |= FC_FC_END_SEQ;
0161         fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
0162                    FC_TYPE_FCP, f_ctl, fh_off);
0163         error = fc_seq_send(lport, seq, fp);
0164         if (error) {
0165             pr_info_ratelimited("%s: Failed to send frame %p, "
0166                         "xid <0x%x>, remaining %zu, "
0167                         "lso_max <0x%x>\n",
0168                         __func__, fp, ep->xid,
0169                         remaining, lport->lso_max);
0170             /*
0171              * Go ahead and set TASK_SET_FULL status ignoring the
0172              * rest of the DataIN, and immediately attempt to
0173              * send the response via ft_queue_status() in order
0174              * to notify the initiator that it should reduce it's
0175              * per LUN queue_depth.
0176              */
0177             se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL;
0178             break;
0179         }
0180     }
0181 queue_status:
0182     return ft_queue_status(se_cmd);
0183 }
0184 
0185 static void ft_execute_work(struct work_struct *work)
0186 {
0187     struct ft_cmd *cmd = container_of(work, struct ft_cmd, work);
0188 
0189     target_execute_cmd(&cmd->se_cmd);
0190 }
0191 
0192 /*
0193  * Receive write data frame.
0194  */
0195 void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *fp)
0196 {
0197     struct se_cmd *se_cmd = &cmd->se_cmd;
0198     struct fc_seq *seq = cmd->seq;
0199     struct fc_exch *ep;
0200     struct fc_lport *lport;
0201     struct fc_frame_header *fh;
0202     struct scatterlist *sg = NULL;
0203     u32 mem_off = 0;
0204     u32 rel_off;
0205     size_t frame_len;
0206     size_t mem_len = 0;
0207     size_t tlen;
0208     struct page *page = NULL;
0209     void *page_addr;
0210     void *from;
0211     void *to;
0212     u32 f_ctl;
0213     void *buf;
0214 
0215     fh = fc_frame_header_get(fp);
0216     if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF))
0217         goto drop;
0218 
0219     f_ctl = ntoh24(fh->fh_f_ctl);
0220     ep = fc_seq_exch(seq);
0221     lport = ep->lp;
0222     if (cmd->was_ddp_setup) {
0223         BUG_ON(!lport);
0224         /*
0225          * Since DDP (Large Rx offload) was setup for this request,
0226          * payload is expected to be copied directly to user buffers.
0227          */
0228         buf = fc_frame_payload_get(fp, 1);
0229         if (buf)
0230             pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, "
0231                 "cmd->sg_cnt 0x%x. DDP was setup"
0232                 " hence not expected to receive frame with "
0233                 "payload, Frame will be dropped if"
0234                 "'Sequence Initiative' bit in f_ctl is"
0235                 "not set\n", __func__, ep->xid, f_ctl,
0236                 se_cmd->t_data_sg, se_cmd->t_data_nents);
0237         /*
0238          * Invalidate HW DDP context if it was setup for respective
0239          * command. Invalidation of HW DDP context is requited in both
0240          * situation (success and error).
0241          */
0242         ft_invl_hw_context(cmd);
0243 
0244         /*
0245          * If "Sequence Initiative (TSI)" bit set in f_ctl, means last
0246          * write data frame is received successfully where payload is
0247          * posted directly to user buffer and only the last frame's
0248          * header is posted in receive queue.
0249          *
0250          * If "Sequence Initiative (TSI)" bit is not set, means error
0251          * condition w.r.t. DDP, hence drop the packet and let explict
0252          * ABORTS from other end of exchange timer trigger the recovery.
0253          */
0254         if (f_ctl & FC_FC_SEQ_INIT)
0255             goto last_frame;
0256         else
0257             goto drop;
0258     }
0259 
0260     rel_off = ntohl(fh->fh_parm_offset);
0261     frame_len = fr_len(fp);
0262     if (frame_len <= sizeof(*fh))
0263         goto drop;
0264     frame_len -= sizeof(*fh);
0265     from = fc_frame_payload_get(fp, 0);
0266     if (rel_off >= se_cmd->data_length)
0267         goto drop;
0268     if (frame_len + rel_off > se_cmd->data_length)
0269         frame_len = se_cmd->data_length - rel_off;
0270 
0271     /*
0272      * Setup to use first mem list entry, unless no data.
0273      */
0274     BUG_ON(frame_len && !se_cmd->t_data_sg);
0275     if (frame_len) {
0276         sg = se_cmd->t_data_sg;
0277         mem_len = sg->length;
0278         mem_off = sg->offset;
0279         page = sg_page(sg);
0280     }
0281 
0282     while (frame_len) {
0283         if (!mem_len) {
0284             sg = sg_next(sg);
0285             mem_len = sg->length;
0286             mem_off = sg->offset;
0287             page = sg_page(sg);
0288         }
0289         if (rel_off >= mem_len) {
0290             rel_off -= mem_len;
0291             mem_len = 0;
0292             continue;
0293         }
0294         mem_off += rel_off;
0295         mem_len -= rel_off;
0296         rel_off = 0;
0297 
0298         tlen = min(mem_len, frame_len);
0299 
0300         to = kmap_atomic(page + (mem_off >> PAGE_SHIFT));
0301         page_addr = to;
0302         to += offset_in_page(mem_off);
0303         tlen = min(tlen, (size_t)(PAGE_SIZE -
0304                       offset_in_page(mem_off)));
0305         memcpy(to, from, tlen);
0306         kunmap_atomic(page_addr);
0307 
0308         from += tlen;
0309         frame_len -= tlen;
0310         mem_off += tlen;
0311         mem_len -= tlen;
0312         cmd->write_data_len += tlen;
0313     }
0314 last_frame:
0315     if (cmd->write_data_len == se_cmd->data_length) {
0316         INIT_WORK(&cmd->work, ft_execute_work);
0317         queue_work(cmd->sess->tport->tpg->workqueue, &cmd->work);
0318     }
0319 drop:
0320     fc_frame_free(fp);
0321 }
0322 
0323 /*
0324  * Handle and cleanup any HW specific resources if
0325  * received ABORTS, errors, timeouts.
0326  */
0327 void ft_invl_hw_context(struct ft_cmd *cmd)
0328 {
0329     struct fc_seq *seq;
0330     struct fc_exch *ep = NULL;
0331     struct fc_lport *lport = NULL;
0332 
0333     BUG_ON(!cmd);
0334     seq = cmd->seq;
0335 
0336     /* Cleanup the DDP context in HW if DDP was setup */
0337     if (cmd->was_ddp_setup && seq) {
0338         ep = fc_seq_exch(seq);
0339         if (ep) {
0340             lport = ep->lp;
0341             if (lport && (ep->xid <= lport->lro_xid)) {
0342                 /*
0343                  * "ddp_done" trigger invalidation of HW
0344                  * specific DDP context
0345                  */
0346                 cmd->write_data_len = lport->tt.ddp_done(lport,
0347                                       ep->xid);
0348 
0349                 /*
0350                  * Resetting same variable to indicate HW's
0351                  * DDP context has been invalidated to avoid
0352                  * re_invalidation of same context (context is
0353                  * identified using ep->xid)
0354                  */
0355                 cmd->was_ddp_setup = 0;
0356             }
0357         }
0358     }
0359 }