struct fc_exch *ep = NULL;
 
        if (mp->max_read) {
-               if (fc_frame_is_read(fp)) {
+               if (fc_fcp_is_read(fr_fsp(fp))) {
                        min = mp->min_xid;
                        max = mp->max_read;
                        plast = &mp->last_read;
        fc_exch_setup_hdr(ep, fp, ep->f_ctl);
        sp->cnt++;
 
+       fc_fcp_ddp_setup(fr_fsp(fp), ep->xid);
+
        if (unlikely(lp->tt.frame_send(lp, fp)))
                goto err;
 
 
        fc_fcp_complete_locked(fsp);
 }
 
+/*
+ * fc_fcp_ddp_setup - calls to LLD's ddp_setup to set up DDP
+ * transfer for a read I/O indicated by the fc_fcp_pkt.
+ * @fsp: ptr to the fc_fcp_pkt
+ *
+ * This is called in exch_seq_send() when we have a newly allocated
+ * exchange with a valid exchange id to setup ddp.
+ *
+ * returns: none
+ */
+void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid)
+{
+       struct fc_lport *lp;
+
+       if (!fsp)
+               return;
+
+       lp = fsp->lp;
+       if ((fsp->req_flags & FC_SRB_READ) &&
+           (lp->lro_enabled) && (lp->tt.ddp_setup)) {
+               if (lp->tt.ddp_setup(lp, xid, scsi_sglist(fsp->cmd),
+                                    scsi_sg_count(fsp->cmd)))
+                       fsp->xfer_ddp = xid;
+       }
+}
+EXPORT_SYMBOL(fc_fcp_ddp_setup);
+
+/*
+ * fc_fcp_ddp_done - calls to LLD's ddp_done to release any
+ * DDP related resources for this I/O if it is initialized
+ * as a ddp transfer
+ * @fsp: ptr to the fc_fcp_pkt
+ *
+ * returns: none
+ */
+static void fc_fcp_ddp_done(struct fc_fcp_pkt *fsp)
+{
+       struct fc_lport *lp;
+
+       if (!fsp)
+               return;
+
+       lp = fsp->lp;
+       if (fsp->xfer_ddp && lp->tt.ddp_done) {
+               fsp->xfer_len = lp->tt.ddp_done(lp, fsp->xfer_ddp);
+               fsp->xfer_ddp = 0;
+       }
+}
+
+
 /*
  * Receive SCSI data from target.
  * Called after receiving solicited data.
        len = fr_len(fp) - sizeof(*fh);
        buf = fc_frame_payload_get(fp, 0);
 
+       /* if this I/O is ddped, update xfer len */
+       fc_fcp_ddp_done(fsp);
+
        if (offset + len > fsp->data_len) {
                /* this should never happen */
                if ((fr_flags(fp) & FCPHF_CRC_UNCHECKED) &&
        fsp->scsi_comp_flags = flags;
        expected_len = fsp->data_len;
 
+       /* if ddp, update xfer len */
+       fc_fcp_ddp_done(fsp);
+
        if (unlikely((flags & ~FCP_CONF_REQ) || fc_rp->fr_status)) {
                rp_ex = (void *)(fc_rp + 1);
                if (flags & (FCP_RSP_LEN_VAL | FCP_SNS_LEN_VAL)) {
        }
 
        memcpy(fc_frame_payload_get(fp, len), &fsp->cdb_cmd, len);
-       fr_cmd(fp) = fsp->cmd;
+       fr_fsp(fp) = fsp;
        rport = fsp->rport;
        fsp->max_payload = rport->maxframe_size;
        rp = rport->dd_data;
        struct fc_lport *lp;
        unsigned long flags;
 
+       /* release outstanding ddp context */
+       fc_fcp_ddp_done(fsp);
+
        fsp->state |= FC_SRB_COMPL;
        if (!(fsp->state & FC_SRB_FCP_PROCESSING_TMO)) {
                spin_unlock_bh(&fsp->scsi_pkt_lock);
 
 #define fr_eof(fp)     (fr_cb(fp)->fr_eof)
 #define fr_flags(fp)   (fr_cb(fp)->fr_flags)
 #define fr_max_payload(fp)     (fr_cb(fp)->fr_max_payload)
-#define fr_cmd(fp)     (fr_cb(fp)->fr_cmd)
-#define fr_dir(fp)     (fr_cmd(fp)->sc_data_direction)
+#define fr_fsp(fp)     (fr_cb(fp)->fr_fsp)
 #define fr_crc(fp)     (fr_cb(fp)->fr_crc)
 
 struct fc_frame {
        struct packet_type  *ptype;
        struct fc_lport *fr_dev;        /* transport layer private pointer */
        struct fc_seq   *fr_seq;        /* for use with exchange manager */
-       struct scsi_cmnd *fr_cmd;       /* for use of scsi command */
+       struct fc_fcp_pkt *fr_fsp;      /* for the corresponding fcp I/O */
        u32             fr_crc;
        u16             fr_max_payload; /* max FC payload */
        enum fc_sof     fr_sof;         /* start of frame delimiter */
        return fc_frame_rctl(fp) == FC_RCTL_DD_UNSOL_CMD;
 }
 
-static inline bool fc_frame_is_read(const struct fc_frame *fp)
-{
-       if (fc_frame_is_cmd(fp) && fr_cmd(fp))
-               return fr_dir(fp) == DMA_FROM_DEVICE;
-       return false;
-}
-
-static inline bool fc_frame_is_write(const struct fc_frame *fp)
-{
-       if (fc_frame_is_cmd(fp) && fr_cmd(fp))
-               return fr_dir(fp) == DMA_TO_DEVICE;
-       return false;
-}
-
 /*
  * Check for leaks.
  * Print the frame header of any currently allocated frame, assuming there
 
         */
        struct fcp_cmnd cdb_cmd;
        size_t          xfer_len;
+       u16             xfer_ddp;       /* this xfer is ddped */
        u32             xfer_contig_end; /* offset of end of contiguous xfer */
        u16             max_payload;    /* max payload size in bytes */
 
        u8              recov_retry;    /* count of recovery retries */
        struct fc_seq   *recov_seq;     /* sequence for REC or SRR */
 };
+/*
+ * FC_FCP HELPER FUNCTIONS
+ *****************************/
+static inline bool fc_fcp_is_read(const struct fc_fcp_pkt *fsp)
+{
+       if (fsp && fsp->cmd)
+               return fsp->cmd->sc_data_direction == DMA_FROM_DEVICE;
+       return false;
+}
 
 /*
  * Structure and function definitions for managing Fibre Channel Exchanges
                                                           void *arg),
                                        void *arg, unsigned int timer_msec);
 
+       /*
+        * Sets up the DDP context for a given exchange id on the given
+        * scatterlist if LLD supports DDP for large receive.
+        *
+        * STATUS: OPTIONAL
+        */
+       int (*ddp_setup)(struct fc_lport *lp, u16 xid,
+                        struct scatterlist *sgl, unsigned int sgc);
+       /*
+        * Completes the DDP transfer and returns the length of data DDPed
+        * for the given exchange id.
+        *
+        * STATUS: OPTIONAL
+        */
+       int (*ddp_done)(struct fc_lport *lp, u16 xid);
        /*
         * Send a frame using an existing sequence and exchange.
         *
  */
 void fc_fcp_destroy(struct fc_lport *);
 
+/*
+ * Set up direct-data placement for this I/O request
+ */
+void fc_fcp_ddp_setup(struct fc_fcp_pkt *fsp, u16 xid);
+
 /*
  * ELS/CT interface
  *****************************/
 
        return be16_to_cpu(skb_fc_header(skb)->fh_rx_id);
 }
 
-/* FIXME - DMA_BIDIRECTIONAL ? */
-#define skb_cb(skb)    ((struct fcoe_rcv_info *)&((skb)->cb[0]))
-#define skb_cmd(skb)   (skb_cb(skb)->fr_cmd)
-#define skb_dir(skb)   (skb_cmd(skb)->sc_data_direction)
-static inline bool skb_fc_is_read(const struct sk_buff *skb)
-{
-       if (skb_fc_is_cmd(skb) && skb_cmd(skb))
-               return skb_dir(skb) == DMA_FROM_DEVICE;
-       return false;
-}
-
-static inline bool skb_fc_is_write(const struct sk_buff *skb)
-{
-       if (skb_fc_is_cmd(skb) && skb_cmd(skb))
-               return skb_dir(skb) == DMA_TO_DEVICE;
-       return false;
-}
-
 /* libfcoe funcs */
 int fcoe_reset(struct Scsi_Host *shost);
 u64 fcoe_wwn_from_mac(unsigned char mac[MAX_ADDR_LEN],