[SCSI] libfc: fix memory corruption caused by double frees and bad error handling
[linux-2.6-block.git] / drivers / scsi / libfc / fc_fcp.c
index 59a4408b27b5b4d4bd3eb248e66442a9c7c98ba7..a67f53a5026c9fe1eba9cd958c85b236d46370b3 100644 (file)
@@ -285,7 +285,6 @@ void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)
                        fsp->xfer_ddp = xid;
        }
 }
-EXPORT_SYMBOL(fc_fcp_ddp_setup);
 
 /*
  * fc_fcp_ddp_done - calls to LLD's ddp_done to release any
@@ -302,10 +301,13 @@ static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)
        if (!fsp)
                return;
 
+       if (fsp->xfer_ddp == FC_XID_UNKNOWN)
+               return;
+
        lp = fsp->lp;
-       if (fsp->xfer_ddp && lp->tt.ddp_done) {
+       if (lp->tt.ddp_done) {
                fsp->xfer_len = lp->tt.ddp_done(lp, fsp->xfer_ddp);
-               fsp->xfer_ddp = 0;
+               fsp->xfer_ddp = FC_XID_UNKNOWN;
        }
 }
 
@@ -572,7 +574,8 @@ static int fc_fcp_send_data(struct fc_fcp_pkt *fsp, struct fc_seq *seq,
                tlen -= sg_bytes;
                remaining -= sg_bytes;
 
-               if (tlen)
+               if ((skb_shinfo(fp_skb(fp))->nr_frags < FC_FRAME_SG_LEN) &&
+                   (tlen))
                        continue;
 
                /*
@@ -1048,7 +1051,6 @@ static int fc_fcp_cmd_send(struct fc_lport *lp, struct fc_fcp_pkt *fsp,
 
        seq = lp->tt.exch_seq_send(lp, fp, resp, fc_fcp_pkt_destroy, fsp, 0);
        if (!seq) {
-               fc_frame_free(fp);
                rc = -1;
                goto unlock;
        }
@@ -1095,7 +1097,7 @@ unlock:
  * Scsi abort handler- calls to send an abort
  * and then wait for abort completion
  */
-static int fc_fcp_pkt_abort(struct fc_lport *lp, struct fc_fcp_pkt *fsp)
+static int fc_fcp_pkt_abort(struct fc_fcp_pkt *fsp)
 {
        int rc = FAILED;
 
@@ -1313,7 +1315,6 @@ static void fc_fcp_rec(struct fc_fcp_pkt *fsp)
                fc_fcp_pkt_hold(fsp);           /* hold while REC outstanding */
                return;
        }
-       fc_frame_free(fp);
 retry:
        if (fsp->recov_retry++ < FC_MAX_RECOV_RETRY)
                fc_fcp_timer_set(fsp, FC_SCSI_REC_TOV);
@@ -1561,10 +1562,9 @@ static void fc_fcp_srr(struct fc_fcp_pkt *fsp, enum fc_rctl r_ctl, u32 offset)
 
        seq = lp->tt.exch_seq_send(lp, fp, fc_fcp_srr_resp, NULL,
                                   fsp, jiffies_to_msecs(FC_SCSI_REC_TOV));
-       if (!seq) {
-               fc_frame_free(fp);
+       if (!seq)
                goto retry;
-       }
+
        fsp->recov_seq = seq;
        fsp->xfer_len = offset;
        fsp->xfer_contig_end = offset;
@@ -1708,6 +1708,7 @@ int fc_queuecommand(struct scsi_cmnd *sc_cmd, void (*done)(struct scsi_cmnd *))
        fsp->cmd = sc_cmd;      /* save the cmd */
        fsp->lp = lp;           /* save the softc ptr */
        fsp->rport = rport;     /* set the remote port ptr */
+       fsp->xfer_ddp = FC_XID_UNKNOWN;
        sc_cmd->scsi_done = done;
 
        /*
@@ -1846,7 +1847,8 @@ static void fc_io_compl(struct fc_fcp_pkt *fsp)
                         * scsi status is good but transport level
                         * underrun.
                         */
-                       sc_cmd->result = DID_OK << 16;
+                       sc_cmd->result = (fsp->state & FC_SRB_RCV_STATUS ?
+                                         DID_OK : DID_ERROR) << 16;
                } else {
                        /*
                         * scsi got underrun, this is an error
@@ -1942,7 +1944,7 @@ int fc_eh_abort(struct scsi_cmnd *sc_cmd)
                goto release_pkt;
        }
 
-       rc = fc_fcp_pkt_abort(lp, fsp);
+       rc = fc_fcp_pkt_abort(fsp);
        fc_fcp_unlock_pkt(fsp);
 
 release_pkt: