cregit-Linux how code gets into the kernel

Release 4.11 drivers/scsi/libfc/fc_fcp.c

/*
 * Copyright(c) 2007 Intel Corporation. All rights reserved.
 * Copyright(c) 2008 Red Hat, Inc.  All rights reserved.
 * Copyright(c) 2008 Mike Christie
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms and conditions of the GNU General Public License,
 * version 2, as published by the Free Software Foundation.
 *
 * This program is distributed in the hope it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Maintained at www.Open-FCoE.org
 */

#include <linux/module.h>
#include <linux/delay.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/scatterlist.h>
#include <linux/err.h>
#include <linux/crc32.h>
#include <linux/slab.h>

#include <scsi/scsi_tcq.h>
#include <scsi/scsi.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_cmnd.h>

#include <scsi/fc/fc_fc2.h>

#include <scsi/libfc.h>
#include <scsi/fc_encode.h>

#include "fc_libfc.h"


static struct kmem_cache *scsi_pkt_cachep;

/* SRB state definitions */

#define FC_SRB_FREE		0		
/* cmd is free */

#define FC_SRB_CMD_SENT		(1 << 0)	
/* cmd has been sent */

#define FC_SRB_RCV_STATUS	(1 << 1)	
/* response has arrived */

#define FC_SRB_ABORT_PENDING	(1 << 2)	
/* cmd abort sent to device */

#define FC_SRB_ABORTED		(1 << 3)	
/* abort acknowledged */

#define FC_SRB_DISCONTIG	(1 << 4)	
/* non-sequential data recvd */

#define FC_SRB_COMPL		(1 << 5)	
/* fc_io_compl has been run */

#define FC_SRB_FCP_PROCESSING_TMO (1 << 6)	
/* timer function processing */


#define FC_SRB_READ		(1 << 1)

#define FC_SRB_WRITE		(1 << 0)

/*
 * The SCp.ptr should be tested and set under the scsi_pkt_queue lock
 */

#define CMD_SP(Cmnd)		    ((struct fc_fcp_pkt *)(Cmnd)->SCp.ptr)

#define CMD_ENTRY_STATUS(Cmnd)	    ((Cmnd)->SCp.have_data_in)

#define CMD_COMPL_STATUS(Cmnd)	    ((Cmnd)->SCp.this_residual)

#define CMD_SCSI_STATUS(Cmnd)	    ((Cmnd)->SCp.Status)

#define CMD_RESID_LEN(Cmnd)	    ((Cmnd)->SCp.buffers_residual)

/**
 * struct fc_fcp_internal - FCP layer internal data
 * @scsi_pkt_pool: Memory pool to draw FCP packets from
 * @scsi_queue_lock: Protects the scsi_pkt_queue
 * @scsi_pkt_queue: Current FCP packets
 * @last_can_queue_ramp_down_time: ramp down time
 * @last_can_queue_ramp_up_time: ramp up time
 * @max_can_queue: max can_queue size
 */

struct fc_fcp_internal {
	
mempool_t		*scsi_pkt_pool;
	
spinlock_t		scsi_queue_lock;
	
struct list_head	scsi_pkt_queue;
	
unsigned long		last_can_queue_ramp_down_time;
	
unsigned long		last_can_queue_ramp_up_time;
	
int			max_can_queue;
};


#define fc_get_scsi_internal(x)	((struct fc_fcp_internal *)(x)->scsi_priv)

/*
 * function prototypes
 * FC scsi I/O related functions
 */
static void fc_fcp_recv_data(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_recv(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_resp(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_complete_locked(struct fc_fcp_pkt *);
static void fc_tm_done(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_recovery(struct fc_fcp_pkt *, u8 code);
static void fc_fcp_timeout(unsigned long);
static void fc_fcp_rec(struct fc_fcp_pkt *);
static void fc_fcp_rec_error(struct fc_fcp_pkt *, struct fc_frame *);
static void fc_fcp_rec_resp(struct fc_seq *, struct fc_frame *, void *);
static void fc_io_compl(struct fc_fcp_pkt *);

static void fc_fcp_srr(struct fc_fcp_pkt *, enum fc_rctl, u32);
static void fc_fcp_srr_resp(struct fc_seq *, struct fc_frame *, void *);
static void fc_fcp_srr_error(struct fc_fcp_pkt *, struct fc_frame *);

/*
 * command status codes
 */

#define FC_COMPLETE		0

#define FC_CMD_ABORTED		1

#define FC_CMD_RESET		2

#define FC_CMD_PLOGO		3

#define FC_SNS_RCV		4

#define FC_TRANS_ERR		5

#define FC_DATA_OVRRUN		6

#define FC_DATA_UNDRUN		7

#define FC_ERROR		8

#define FC_HRD_ERROR		9

#define FC_CRC_ERROR		10

#define FC_TIMED_OUT		11

#define FC_TRANS_RESET		12

/*
 * Error recovery timeout values.
 */

#define FC_SCSI_TM_TOV		(10 * HZ)

#define FC_HOST_RESET_TIMEOUT	(30 * HZ)

#define FC_CAN_QUEUE_PERIOD	(60 * HZ)


#define FC_MAX_ERROR_CNT	5

#define FC_MAX_RECOV_RETRY	3


#define FC_FCP_DFLT_QUEUE_DEPTH 32

/**
 * fc_fcp_pkt_alloc() - Allocate a fcp_pkt
 * @lport: The local port that the FCP packet is for
 * @gfp:   GFP flags for allocation
 *
 * Return value: fcp_pkt structure or null on allocation failure.
 * Context:      Can be called from process context, no lock is required.
 */

static struct fc_fcp_pkt *fc_fcp_pkt_alloc(struct fc_lport *lport, gfp_t gfp) { struct fc_fcp_internal *si = fc_get_scsi_internal(lport); struct fc_fcp_pkt *fsp; fsp = mempool_alloc(si->scsi_pkt_pool, gfp); if (fsp) { memset(fsp, 0, sizeof(*fsp)); fsp->lp = lport; fsp->xfer_ddp = FC_XID_UNKNOWN; atomic_set(&fsp->ref_cnt, 1); init_timer(&fsp->timer); fsp->timer.data = (unsigned long)fsp; INIT_LIST_HEAD(&fsp->list); spin_lock_init(&fsp->scsi_pkt_lock); } else { per_cpu_ptr(lport->stats, get_cpu())->FcpPktAllocFails++; put_cpu(); } return fsp; }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love10473.76%240.00%
Vasu Dev1913.48%120.00%
Yi Zou1812.77%240.00%
Total141100.00%5100.00%

/** * fc_fcp_pkt_release() - Release hold on a fcp_pkt * @fsp: The FCP packet to be released * * Context: Can be called from process or interrupt context, * no lock is required. */
static void fc_fcp_pkt_release(struct fc_fcp_pkt *fsp) { if (atomic_dec_and_test(&fsp->ref_cnt)) { struct fc_fcp_internal *si = fc_get_scsi_internal(fsp->lp); mempool_free(fsp, si->scsi_pkt_pool); } }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love44100.00%1100.00%
Total44100.00%1100.00%

/** * fc_fcp_pkt_hold() - Hold a fcp_pkt * @fsp: The FCP packet to be held */
static void fc_fcp_pkt_hold(struct fc_fcp_pkt *fsp) { atomic_inc(&fsp->ref_cnt); }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love19100.00%1100.00%
Total19100.00%1100.00%

/** * fc_fcp_pkt_destroy() - Release hold on a fcp_pkt * @seq: The sequence that the FCP packet is on (required by destructor API) * @fsp: The FCP packet to be released * * This routine is called by a destructor callback in the fc_exch_seq_send() * routine of the libfc Transport Template. The 'struct fc_seq' is a required * argument even though it is not used by this routine. * * Context: No locking required. */
static void fc_fcp_pkt_destroy(struct fc_seq *seq, void *fsp) { fc_fcp_pkt_release(fsp); }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love20100.00%1100.00%
Total20100.00%1100.00%

/** * fc_fcp_lock_pkt() - Lock a fcp_pkt and increase its reference count * @fsp: The FCP packet to be locked and incremented * * We should only return error if we return a command to SCSI-ml before * getting a response. This can happen in cases where we send a abort, but * do not wait for the response and the abort and command can be passing * each other on the wire/network-layer. * * Note: this function locks the packet and gets a reference to allow * callers to call the completion function while the lock is held and * not have to worry about the packets refcount. * * TODO: Maybe we should just have callers grab/release the lock and * have a function that they call to verify the fsp and grab a ref if * needed. */
static inline int fc_fcp_lock_pkt(struct fc_fcp_pkt *fsp) { spin_lock_bh(&fsp->scsi_pkt_lock); if (fsp->state & FC_SRB_COMPL) { spin_unlock_bh(&fsp->scsi_pkt_lock); return -EPERM; } fc_fcp_pkt_hold(fsp); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love50100.00%1100.00%
Total50100.00%1100.00%

/** * fc_fcp_unlock_pkt() - Release a fcp_pkt's lock and decrement its * reference count * @fsp: The FCP packet to be unlocked and decremented */
static inline void fc_fcp_unlock_pkt(struct fc_fcp_pkt *fsp) { spin_unlock_bh(&fsp->scsi_pkt_lock); fc_fcp_pkt_release(fsp); }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love25100.00%1100.00%
Total25100.00%1100.00%

/** * fc_fcp_timer_set() - Start a timer for a fcp_pkt * @fsp: The FCP packet to start a timer for * @delay: The timeout period in jiffies */
static void fc_fcp_timer_set(struct fc_fcp_pkt *fsp, unsigned long delay) { if (!(fsp->state & FC_SRB_COMPL)) { mod_timer(&fsp->timer, jiffies + delay); fsp->timer_delay = delay; } }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love3882.61%150.00%
Hannes Reinecke817.39%150.00%
Total46100.00%2100.00%


static void fc_fcp_abort_done(struct fc_fcp_pkt *fsp) { fsp->state |= FC_SRB_ABORTED; fsp->state &= ~FC_SRB_ABORT_PENDING; if (fsp->wait_for_comp) complete(&fsp->tm_done); else fc_fcp_complete_locked(fsp); }

Contributors

PersonTokensPropCommitsCommitProp
Hannes Reinecke44100.00%1100.00%
Total44100.00%1100.00%

/** * fc_fcp_send_abort() - Send an abort for exchanges associated with a * fcp_pkt * @fsp: The FCP packet to abort exchanges on */
static int fc_fcp_send_abort(struct fc_fcp_pkt *fsp) { int rc; if (!fsp->seq_ptr) return -EINVAL; per_cpu_ptr(fsp->lp->stats, get_cpu())->FcpPktAborts++; put_cpu(); fsp->state |= FC_SRB_ABORT_PENDING; rc = fc_seq_exch_abort(fsp->seq_ptr, 0); /* * fc_seq_exch_abort() might return -ENXIO if * the sequence is already completed */ if (rc == -ENXIO) { fc_fcp_abort_done(fsp); rc = 0; } return rc; }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love3643.90%125.00%
Hannes Reinecke2834.15%250.00%
Vasu Dev1821.95%125.00%
Total82100.00%4100.00%

/** * fc_fcp_retry_cmd() - Retry a fcp_pkt * @fsp: The FCP packet to be retried * * Sets the status code to be FC_ERROR and then calls * fc_fcp_complete_locked() which in turn calls fc_io_compl(). * fc_io_compl() will notify the SCSI-ml that the I/O is done. * The SCSI-ml will retry the command. */
static void fc_fcp_retry_cmd(struct fc_fcp_pkt *fsp, int status_code) { if (fsp->seq_ptr) { fc_exch_done(fsp->seq_ptr); fsp->seq_ptr = NULL; } fsp->state &= ~FC_SRB_ABORT_PENDING; fsp->io_status = 0; fsp->status_code = status_code; fc_fcp_complete_locked(fsp); }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love5389.83%125.00%
Hannes Reinecke58.47%250.00%
Martin K. Petersen11.69%125.00%
Total59100.00%4100.00%

/** * fc_fcp_ddp_setup() - Calls a LLD's ddp_setup routine to set up DDP context * @fsp: The FCP packet that will manage the DDP frames * @xid: The XID that will be used for the DDP exchange */
void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid) { struct fc_lport *lport; lport = fsp->lp; if ((fsp->req_flags & FC_SRB_READ) && (lport->lro_enabled) && (lport->tt.ddp_setup)) { if (lport->tt.ddp_setup(lport, xid, scsi_sglist(fsp->cmd), scsi_sg_count(fsp->cmd))) fsp->xfer_ddp = xid; } }

Contributors

PersonTokensPropCommitsCommitProp
Yi Zou7792.77%150.00%
Robert Love67.23%150.00%
Total83100.00%2100.00%

/** * fc_fcp_ddp_done() - Calls a LLD's ddp_done routine to release any * DDP related resources for a fcp_pkt * @fsp: The FCP packet that DDP had been used on */
void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp) { struct fc_lport *lport; if (!fsp) return; if (fsp->xfer_ddp == FC_XID_UNKNOWN) return; lport = fsp->lp; if (lport->tt.ddp_done) { fsp->xfer_len = lport->tt.ddp_done(lport, fsp->xfer_ddp); fsp->xfer_ddp = FC_XID_UNKNOWN; } }

Contributors

PersonTokensPropCommitsCommitProp
Yi Zou6492.75%266.67%
Robert Love57.25%133.33%
Total69100.00%3100.00%

/** * fc_fcp_can_queue_ramp_up() - increases can_queue * @lport: lport to ramp up can_queue */
static void fc_fcp_can_queue_ramp_up(struct fc_lport *lport) { struct fc_fcp_internal *si = fc_get_scsi_internal(lport); unsigned long flags; int can_queue; spin_lock_irqsave(lport->host->host_lock, flags); if (si->last_can_queue_ramp_up_time && (time_before(jiffies, si->last_can_queue_ramp_up_time + FC_CAN_QUEUE_PERIOD))) goto unlock; if (time_before(jiffies, si->last_can_queue_ramp_down_time + FC_CAN_QUEUE_PERIOD)) goto unlock; si->last_can_queue_ramp_up_time = jiffies; can_queue = lport->host->can_queue << 1; if (can_queue >= si->max_can_queue) { can_queue = si->max_can_queue; si->last_can_queue_ramp_down_time = 0; } lport->host->can_queue = can_queue; shost_printk(KERN_ERR, lport->host, "libfc: increased " "can_queue to %d.\n", can_queue); unlock: spin_unlock_irqrestore(lport->host->host_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Vasu Dev11677.33%266.67%
Robert Love3422.67%133.33%
Total150100.00%3100.00%

/** * fc_fcp_can_queue_ramp_down() - reduces can_queue * @lport: lport to reduce can_queue * * If we are getting memory allocation failures, then we may * be trying to execute too many commands. We let the running * commands complete or timeout, then try again with a reduced * can_queue. Eventually we will hit the point where we run * on all reserved structs. */
static void fc_fcp_can_queue_ramp_down(struct fc_lport *lport) { struct fc_fcp_internal *si = fc_get_scsi_internal(lport); unsigned long flags; int can_queue; spin_lock_irqsave(lport->host->host_lock, flags); if (si->last_can_queue_ramp_down_time && (time_before(jiffies, si->last_can_queue_ramp_down_time + FC_CAN_QUEUE_PERIOD))) goto unlock; si->last_can_queue_ramp_down_time = jiffies; can_queue = lport->host->can_queue; can_queue >>= 1; if (!can_queue) can_queue = 1; lport->host->can_queue = can_queue; unlock: spin_unlock_irqrestore(lport->host->host_lock, flags); }

Contributors

PersonTokensPropCommitsCommitProp
Vasu Dev7871.56%266.67%
Robert Love3128.44%133.33%
Total109100.00%3100.00%

/* * fc_fcp_frame_alloc() - Allocates fc_frame structure and buffer. * @lport: fc lport struct * @len: payload length * * Allocates fc_frame structure and buffer but if fails to allocate * then reduce can_queue. */
static inline struct fc_frame *fc_fcp_frame_alloc(struct fc_lport *lport, size_t len) { struct fc_frame *fp; fp = fc_frame_alloc(lport, len); if (likely(fp)) return fp; per_cpu_ptr(lport->stats, get_cpu())->FcpFrameAllocFails++; put_cpu(); /* error case */ fc_fcp_can_queue_ramp_down(lport); shost_printk(KERN_ERR, lport->host, "libfc: Could not allocate frame, " "reducing can_queue to %d.\n", lport->host->can_queue); return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
Vasu Dev5869.05%250.00%
Hannes Reinecke1821.43%125.00%
Christopher Leech89.52%125.00%
Total84100.00%4100.00%

/** * get_fsp_rec_tov() - Helper function to get REC_TOV * @fsp: the FCP packet * * Returns rec tov in jiffies as rpriv->e_d_tov + 1 second */
static inline unsigned int get_fsp_rec_tov(struct fc_fcp_pkt *fsp) { struct fc_rport_libfc_priv *rpriv = fsp->rport->dd_data; unsigned int e_d_tov = FC_DEF_E_D_TOV; if (rpriv && rpriv->e_d_tov > e_d_tov) e_d_tov = rpriv->e_d_tov; return msecs_to_jiffies(e_d_tov) + HZ; }

Contributors

PersonTokensPropCommitsCommitProp
Hannes Reinecke54100.00%1100.00%
Total54100.00%1100.00%

/** * fc_fcp_recv_data() - Handler for receiving SCSI-FCP data from a target * @fsp: The FCP packet the data is on * @fp: The data frame */
static void fc_fcp_recv_data(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { struct scsi_cmnd *sc = fsp->cmd; struct fc_lport *lport = fsp->lp; struct fc_stats *stats; struct fc_frame_header *fh; size_t start_offset; size_t offset; u32 crc; u32 copy_len = 0; size_t len; void *buf; struct scatterlist *sg; u32 nents; u8 host_bcode = FC_COMPLETE; fh = fc_frame_header_get(fp); offset = ntohl(fh->fh_parm_offset); start_offset = offset; len = fr_len(fp) - sizeof(*fh); buf = fc_frame_payload_get(fp, 0); /* * if this I/O is ddped then clear it and initiate recovery since data * frames are expected to be placed directly in that case. * * Indicate error to scsi-ml because something went wrong with the * ddp handling to get us here. */ if (fsp->xfer_ddp != FC_XID_UNKNOWN) { fc_fcp_ddp_done(fsp); FC_FCP_DBG(fsp, "DDP I/O in fc_fcp_recv_data set ERROR\n"); host_bcode = FC_ERROR; goto err; } if (offset + len > fsp->data_len) { /* this should never happen */ if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) && fc_frame_crc_check(fp)) goto crc_err; FC_FCP_DBG(fsp, "data received past end. len %zx offset %zx " "data_len %x\n", len, offset, fsp->data_len); /* Data is corrupted indicate scsi-ml should retry */ host_bcode = FC_DATA_OVRRUN; goto err; } if (offset != fsp->xfer_len) fsp->state |= FC_SRB_DISCONTIG; sg = scsi_sglist(sc); nents = scsi_sg_count(sc); if (!(fr_flags(fp) & FCPHF_CRC_UNCHECKED)) { copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents, &offset, NULL); } else { crc = crc32(~0, (u8 *) fh, sizeof(*fh)); copy_len = fc_copy_buffer_to_sglist(buf, len, sg, &nents, &offset, &crc); buf = fc_frame_payload_get(fp, 0); if (len % 4) crc = crc32(crc, buf + len, 4 - (len % 4)); if (~crc != le32_to_cpu(fr_crc(fp))) { crc_err: stats = per_cpu_ptr(lport->stats, get_cpu()); stats->ErrorFrames++; /* per cpu count, not total count, but OK for limit */ if (stats->InvalidCRCCount++ < FC_MAX_ERROR_CNT) printk(KERN_WARNING "libfc: CRC error on data " "frame for port (%6.6x)\n", lport->port_id); put_cpu(); /* * Assume the frame is total garbage. * We may have copied it over the good part * of the buffer. * If so, we need to retry the entire operation. * Otherwise, ignore it. */ if (fsp->state & FC_SRB_DISCONTIG) { host_bcode = FC_CRC_ERROR; goto err; } return; } } if (fsp->xfer_contig_end == start_offset) fsp->xfer_contig_end += copy_len; fsp->xfer_len += copy_len; /* * In the very rare event that this data arrived after the response * and completes the transfer, call the completion handler. */ if (unlikely(fsp->state & FC_SRB_RCV_STATUS) && fsp->xfer_len == fsp->data_len - fsp->scsi_resid) { FC_FCP_DBG( fsp, "complete out-of-order sequence\n" ); fc_fcp_complete_locked(fsp); } return; err: fc_fcp_recovery(fsp, host_bcode); }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love40582.82%746.67%
John Fastabend306.13%16.67%
Vasu Dev295.93%213.33%
Joe Eykholt91.84%16.67%
Hannes Reinecke91.84%16.67%
Yi Zou51.02%16.67%
Christopher Leech10.20%16.67%
Hillf Danton10.20%16.67%
Total489100.00%15100.00%

/** * fc_fcp_send_data() - Send SCSI data to a target * @fsp: The FCP packet the data is on * @sp: The sequence the data is to be sent on * @offset: The starting offset for this data request * @seq_blen: The burst length for this data request * * Called after receiving a Transfer Ready data descriptor. * If the LLD is capable of sequence offload then send down the * seq_blen amount of data in single frame, otherwise send * multiple frames of the maximum frame payload supported by * the target port. */
static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq, size_t offset, size_t seq_blen) { struct fc_exch *ep; struct scsi_cmnd *sc; struct scatterlist *sg; struct fc_frame *fp = NULL; struct fc_lport *lport = fsp->lp; struct page *page; size_t remaining; size_t t_blen; size_t tlen; size_t sg_bytes; size_t frame_offset, fh_parm_offset; size_t off; int error; void *data = NULL; void *page_addr; int using_sg = lport->sg_supp; u32 f_ctl; WARN_ON(seq_blen <= 0); if (unlikely(offset + seq_blen > fsp->data_len)) { /* this should never happen */ FC_FCP_DBG(fsp, "xfer-ready past end. seq_blen %zx " "offset %zx\n", seq_blen, offset); fc_fcp_send_abort(fsp); return 0; } else if (offset != fsp->xfer_len) { /* Out of Order Data Request - no problem, but unexpected. */ FC_FCP_DBG(fsp, "xfer-ready non-contiguous. " "seq_blen %zx offset %zx\n", seq_blen, offset); } /* * if LLD is capable of seq_offload then set transport * burst length (t_blen) to seq_blen, otherwise set t_blen * to max FC frame payload previously set in fsp->max_payload. */ t_blen = fsp->max_payload; if (lport->seq_offload) { t_blen = min(seq_blen, (size_t)lport->lso_max); FC_FCP_DBG(fsp, "fsp=%p:lso:blen=%zx lso_max=0x%x t_blen=%zx\n", fsp, seq_blen, lport->lso_max, t_blen); } if (t_blen > 512) t_blen &= ~(512 - 1); /* round down to block size */ sc = fsp->cmd; remaining = seq_blen; fh_parm_offset = frame_offset = offset; tlen = 0; seq = fc_seq_start_next(seq); f_ctl = FC_FC_REL_OFF; WARN_ON(!seq); sg = scsi_sglist(sc); while (remaining > 0 && sg) { if (offset >= sg->length) { offset -= sg->length; sg = sg_next(sg); continue; } if (!fp) { tlen = min(t_blen, remaining); /* * TODO. Temporary workaround. fc_seq_send() can't * handle odd lengths in non-linear skbs. * This will be the final fragment only. */ if (tlen % 4) using_sg = 0; fp = fc_frame_alloc(lport, using_sg ? 0 : tlen); if (!fp) return -ENOMEM; data = fc_frame_header_get(fp) + 1; fh_parm_offset = frame_offset; fr_max_payload(fp) = fsp->max_payload; } off = offset + sg->offset; sg_bytes = min(tlen, sg->length - offset); sg_bytes = min(sg_bytes, (size_t) (PAGE_SIZE - (off & ~PAGE_MASK))); page = sg_page(sg) + (off >> PAGE_SHIFT); if (using_sg) { get_page(page); skb_fill_page_desc(fp_skb(fp), skb_shinfo(fp_skb(fp))->nr_frags, page, off & ~PAGE_MASK, sg_bytes); fp_skb(fp)->data_len += sg_bytes; fr_len(fp) += sg_bytes; fp_skb(fp)->truesize += PAGE_SIZE; } else { /* * The scatterlist item may be bigger than PAGE_SIZE, * but we must not cross pages inside the kmap. */ page_addr = kmap_atomic(page); memcpy(data, (char *)page_addr + (off & ~PAGE_MASK), sg_bytes); kunmap_atomic(page_addr); data += sg_bytes; } offset += sg_bytes; frame_offset += sg_bytes; tlen -= sg_bytes; remaining -= sg_bytes; if ((skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN) && (tlen)) continue; /* * Send sequence with transfer sequence initiative in case * this is last FCP frame of the sequence. */ if (remaining == 0) f_ctl |= FC_FC_SEQ_INIT | FC_FC_END_SEQ; ep = fc_seq_exch(seq); fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid, FC_TYPE_FCP, f_ctl, fh_parm_offset); /* * send fragment using for a sequence. */ error = fc_seq_send(lport, seq, fp); if (error) { WARN_ON(1); /* send error should be rare */ return error; } fp = NULL; } fsp->xfer_len += seq_blen; /* premature count? */ return 0; }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love54382.77%330.00%
Christopher Leech578.69%110.00%
Yi Zou487.32%330.00%
Vasu Dev60.91%110.00%
Hannes Reinecke20.30%220.00%
Total656100.00%10100.00%

/** * fc_fcp_abts_resp() - Receive an ABTS response * @fsp: The FCP packet that is being aborted * @fp: The response frame */
static void fc_fcp_abts_resp(struct fc_fcp_pkt *fsp, struct fc_frame *fp) { int ba_done = 1; struct fc_ba_rjt *brp; struct fc_frame_header *fh; fh = fc_frame_header_get(fp); switch (fh->fh_r_ctl) { case FC_RCTL_BA_ACC: break; case FC_RCTL_BA_RJT: brp = fc_frame_payload_get(fp, sizeof(*brp)); if (brp && brp->br_reason == FC_BA_RJT_LOG_ERR) break; /* fall thru */ default: /* * we will let the command timeout * and scsi-ml recover in this case, * therefore cleared the ba_done flag. */ ba_done = 0; } if (ba_done) fc_fcp_abort_done(fsp); }

Contributors

PersonTokensPropCommitsCommitProp
Robert Love9298.92%150.00%
Hannes Reinecke11.08%150.00%
Total93100.00%2100.00%

/** * fc_fcp_recv() - Receive an FCP frame * @seq: The sequence the frame is on * @fp: The received frame * @arg: The related FCP packet * * Context: Called from Soft IRQ context. Can not be called * holding the FCP packet list lock. */
static void fc_fcp_recv(struct fc_seq *seq, struct fc_frame *fp, void *arg) { struct fc_fcp_pkt *fsp = (struct fc_fcp_pkt *)arg; struct fc_lport *lport = fsp->lp; struct fc_frame_header *fh; struct fcp_txrdy *dd; u8 r_ctl; int rc = 0; if (IS_ERR(fp)) { fc_fcp_error(fsp, fp); return; } fh = fc_frame_header_get(fp); r_ctl = fh->fh_r_ctl; if (lport->state != LPORT_ST_READY) { FC_FCP_DBG(fsp, "lport state %d, ignoring r_ctl %x\n", lport->state, r_ctl); goto out; } if (fc_fcp_lock_pkt(fsp)) goto out; if (fh->fh_type == FC_TYPE_BLS) { fc_fcp_abts_resp(fsp, fp); goto unlock; } if (fsp->state & (FC_SRB_ABORTED | FC_SRB_ABORT_PENDING)) { FC_FCP_DBG(fsp, "command aborted, ignoring r_ctl %x\n", r_ctl); goto unlock; } if (r_ctl == FC_RCTL_DD_DATA_DESC) { /* * received XFER RDY from the target * need to send data to the target */ WARN_ON