// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2010 Cisco Systems, Inc. * * Portions based on tcm_loop_fabric_scsi.c and 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 * Copyright (c) 2009 Rising Tide, Inc. * Copyright (c) 2009 Linux-iSCSI.org * Copyright (c) 2009 Nicholas A. Bellinger <nab@linux-iscsi.org>
*/
if (se_cmd->scsi_status == SAM_STAT_TASK_SET_FULL) goto queue_status;
ep = fc_seq_exch(cmd->seq);
lport = ep->lp;
cmd->seq = fc_seq_start_next(cmd->seq);
remaining = se_cmd->data_length;
/* * Setup to use first mem list entry, unless no data.
*/
BUG_ON(remaining && !se_cmd->t_data_sg); if (remaining) {
sg = se_cmd->t_data_sg;
mem_len = sg->length;
mem_off = sg->offset;
page = sg_page(sg);
}
/* no scatter/gather in skb for odd word length due to fc_seq_send() */
use_sg = !(remaining % 4);
while (remaining) { struct fc_seq *seq = cmd->seq;
if (!seq) {
pr_debug("%s: Command aborted, xid 0x%x\n",
__func__, ep->xid); break;
} if (!mem_len) {
sg = sg_next(sg);
mem_len = min((size_t)sg->length, remaining);
mem_off = sg->offset;
page = sg_page(sg);
} if (!frame_len) { /* * If lport's has capability of Large Send Offload LSO) * , then allow 'frame_len' to be as big as 'lso_max' * if indicated transfer length is >= lport->lso_max
*/
frame_len = (lport->seq_offload) ? lport->lso_max :
cmd->sess->max_frame;
frame_len = min(frame_len, remaining);
fp = fc_frame_alloc(lport, use_sg ? 0 : frame_len); if (!fp) return -ENOMEM;
to = fc_frame_payload_get(fp, 0);
fh_off = frame_off;
frame_off += frame_len; /* * Setup the frame's max payload which is used by base * driver to indicate HW about max frame size, so that * HW can do fragmentation appropriately based on * "gso_max_size" of underline netdev.
*/
fr_max_payload(fp) = cmd->sess->max_frame;
}
tlen = min(mem_len, frame_len);
if (frame_len &&
(skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN)) continue; if (!remaining)
f_ctl |= FC_FC_END_SEQ;
fc_fill_fc_hdr(fp, FC_RCTL_DD_SOL_DATA, ep->did, ep->sid,
FC_TYPE_FCP, f_ctl, fh_off);
error = fc_seq_send(lport, seq, fp); if (error) {
pr_info_ratelimited("%s: Failed to send frame %p, " "xid <0x%x>, remaining %zu, " "lso_max <0x%x>\n",
__func__, fp, ep->xid,
remaining, lport->lso_max); /* * Go ahead and set TASK_SET_FULL status ignoring the * rest of the DataIN, and immediately attempt to * send the response via ft_queue_status() in order * to notify the initiator that it should reduce it's * per LUN queue_depth.
*/
se_cmd->scsi_status = SAM_STAT_TASK_SET_FULL; break;
}
}
queue_status: return ft_queue_status(se_cmd);
}
fh = fc_frame_header_get(fp); if (!(ntoh24(fh->fh_f_ctl) & FC_FC_REL_OFF)) goto drop;
f_ctl = ntoh24(fh->fh_f_ctl);
ep = fc_seq_exch(seq);
lport = ep->lp; if (cmd->was_ddp_setup) {
BUG_ON(!lport); /* * Since DDP (Large Rx offload) was setup for this request, * payload is expected to be copied directly to user buffers.
*/
buf = fc_frame_payload_get(fp, 1); if (buf)
pr_err("%s: xid 0x%x, f_ctl 0x%x, cmd->sg %p, " "cmd->sg_cnt 0x%x. DDP was setup" " hence not expected to receive frame with " "payload, Frame will be dropped if" "'Sequence Initiative' bit in f_ctl is" "not set\n", __func__, ep->xid, f_ctl,
se_cmd->t_data_sg, se_cmd->t_data_nents); /* * Invalidate HW DDP context if it was setup for respective * command. Invalidation of HW DDP context is requited in both * situation (success and error).
*/
ft_invl_hw_context(cmd);
/* * If "Sequence Initiative (TSI)" bit set in f_ctl, means last * write data frame is received successfully where payload is * posted directly to user buffer and only the last frame's * header is posted in receive queue. * * If "Sequence Initiative (TSI)" bit is not set, means error * condition w.r.t. DDP, hence drop the packet and let explict * ABORTS from other end of exchange timer trigger the recovery.
*/ if (f_ctl & FC_FC_SEQ_INIT) goto last_frame; else goto drop;
}
rel_off = ntohl(fh->fh_parm_offset);
frame_len = fr_len(fp); if (frame_len <= sizeof(*fh)) goto drop;
frame_len -= sizeof(*fh);
from = fc_frame_payload_get(fp, 0); if (rel_off >= se_cmd->data_length) goto drop; if (frame_len + rel_off > se_cmd->data_length)
frame_len = se_cmd->data_length - rel_off;
/* * Setup to use first mem list entry, unless no data.
*/
BUG_ON(frame_len && !se_cmd->t_data_sg); if (frame_len) {
sg = se_cmd->t_data_sg;
mem_len = sg->length;
mem_off = sg->offset;
page = sg_page(sg);
}
/* * Handle and cleanup any HW specific resources if * received ABORTS, errors, timeouts.
*/ void ft_invl_hw_context(struct ft_cmd *cmd)
{ struct fc_seq *seq; struct fc_exch *ep = NULL; struct fc_lport *lport = NULL;
BUG_ON(!cmd);
seq = cmd->seq;
/* Cleanup the DDP context in HW if DDP was setup */ if (cmd->was_ddp_setup && seq) {
ep = fc_seq_exch(seq); if (ep) {
lport = ep->lp; if (lport && (ep->xid <= lport->lro_xid)) { /* * "ddp_done" trigger invalidation of HW * specific DDP context
*/
cmd->write_data_len = lport->tt.ddp_done(lport,
ep->xid);
/* * Resetting same variable to indicate HW's * DDP context has been invalidated to avoid * re_invalidation of same context (context is * identified using ep->xid)
*/
cmd->was_ddp_setup = 0;
}
}
}
}
Messung V0.5
¤ Dauer der Verarbeitung: 0.12 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.