|
@@ -213,62 +213,49 @@ void ft_recv_write_data(struct ft_cmd *cmd, struct fc_frame *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(!ep);
|
|
|
+ BUG_ON(!lport);
|
|
|
+ }
|
|
|
+
|
|
|
/*
|
|
|
- * Doesn't expect even single byte of payload. Payload
|
|
|
+ * Doesn't expect payload if DDP is setup. Payload
|
|
|
* is expected to be copied directly to user buffers
|
|
|
- * due to DDP (Large Rx offload) feature, hence
|
|
|
- * BUG_ON if BUF is non-NULL
|
|
|
+ * due to DDP (Large Rx offload),
|
|
|
*/
|
|
|
buf = fc_frame_payload_get(fp, 1);
|
|
|
- if (cmd->was_ddp_setup && buf) {
|
|
|
- pr_debug("%s: When DDP was setup, not expected to"
|
|
|
- "receive frame with payload, Payload shall be"
|
|
|
- "copied directly to buffer instead of coming "
|
|
|
- "via. legacy receive queues\n", __func__);
|
|
|
- BUG_ON(buf);
|
|
|
- }
|
|
|
+ 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,
|
|
|
+ cmd->sg, cmd->sg_cnt);
|
|
|
+ /*
|
|
|
+ * 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 ft_cmd indicated 'ddp_setup', in that case only the last frame
|
|
|
- * should come with 'TSI bit being set'. If 'TSI bit is not set and if
|
|
|
- * data frame appears here, means error condition. In both the cases
|
|
|
- * release the DDP context (ddp_put) and in error case, as well
|
|
|
- * initiate error recovery mechanism.
|
|
|
+ * 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.
|
|
|
*/
|
|
|
- ep = fc_seq_exch(seq);
|
|
|
- if (cmd->was_ddp_setup) {
|
|
|
- BUG_ON(!ep);
|
|
|
- lport = ep->lp;
|
|
|
- BUG_ON(!lport);
|
|
|
- }
|
|
|
- if (cmd->was_ddp_setup && ep->xid != FC_XID_UNKNOWN) {
|
|
|
- f_ctl = ntoh24(fh->fh_f_ctl);
|
|
|
- /*
|
|
|
- * If 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 legacy receive queue
|
|
|
- */
|
|
|
- if (f_ctl & FC_FC_SEQ_INIT) { /* TSI bit set in FC frame */
|
|
|
- cmd->write_data_len = lport->tt.ddp_done(lport,
|
|
|
- ep->xid);
|
|
|
- goto last_frame;
|
|
|
- } else {
|
|
|
- /*
|
|
|
- * Updating the write_data_len may be meaningless at
|
|
|
- * this point, but just in case if required in future
|
|
|
- * for debugging or any other purpose
|
|
|
- */
|
|
|
- pr_err("%s: Received frame with TSI bit not"
|
|
|
- " being SET, dropping the frame, "
|
|
|
- "cmd->sg <%p>, cmd->sg_cnt <0x%x>\n",
|
|
|
- __func__, cmd->sg, cmd->sg_cnt);
|
|
|
- cmd->write_data_len = lport->tt.ddp_done(lport,
|
|
|
- ep->xid);
|
|
|
- lport->tt.seq_exch_abort(cmd->seq, 0);
|
|
|
- goto drop;
|
|
|
- }
|
|
|
- }
|
|
|
+ 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);
|
|
@@ -331,3 +318,39 @@ last_frame:
|
|
|
drop:
|
|
|
fc_frame_free(fp);
|
|
|
}
|
|
|
+
|
|
|
+/*
|
|
|
+ * 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 = cmd->seq;
|
|
|
+ struct fc_exch *ep = NULL;
|
|
|
+ struct fc_lport *lport = NULL;
|
|
|
+
|
|
|
+ BUG_ON(!cmd);
|
|
|
+
|
|
|
+ /* 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|