This patch adds code for ndo_fcoe_ddp_target and
ndo_fcoe_ddp_done.

Signed-off-by: Varun Prakash <[email protected]>
---
 drivers/net/ethernet/chelsio/cxgb4/cxgb4.h      |    1 +
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_fcoe.c |  600 +++++++++++++++++++++++
 drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c |   39 ++-
 drivers/net/ethernet/chelsio/cxgb4/sge.c        |  328 ++++++++++++-
 drivers/net/ethernet/chelsio/cxgb4/t4_hw.c      |    1 +
 5 files changed, 963 insertions(+), 6 deletions(-)

diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h 
b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
index 524d110..8cc53f9 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4.h
@@ -247,6 +247,7 @@ struct tp_params {
         * places we store their offsets here, or a -1 if the field isn't
         * present.
         */
+       int fcoe_shift;
        int vlan_shift;
        int vnic_shift;
        int port_shift;
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_fcoe.c 
b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_fcoe.c
index 6c8a62e..f78d632 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_fcoe.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_fcoe.c
@@ -34,8 +34,10 @@
 
 #ifdef CONFIG_CHELSIO_T4_FCOE
 
+#include <linux/if_vlan.h>
 #include <scsi/fc/fc_fs.h>
 #include <scsi/libfcoe.h>
+#include "t4_msg.h"
 #include "cxgb4.h"
 
 bool cxgb_fcoe_sof_eof_supported(struct adapter *adap, struct sk_buff *skb)
@@ -59,6 +61,528 @@ bool cxgb_fcoe_sof_eof_supported(struct adapter *adap, 
struct sk_buff *skb)
        return true;
 }
 
+static inline struct cxgb_fcoe_ddp *
+cxgb_fcoe_lookup_ddp(struct port_info *pi, unsigned int tid)
+{
+       struct adapter *adap = pi->adapter;
+       struct cxgb_fcoe *fcoe = &pi->fcoe;
+       struct cxgb_fcoe_ddp *ddp;
+       u16 xid;
+
+       if (tid >= adap->tids.ntids) {
+               dev_err(adap->pdev_dev, "tid %x out of bounds\n", tid);
+               return NULL;
+       }
+
+       xid = adap->vres.tid2xid[tid];
+
+       if (xid >= CXGB_FCOE_MAX_XCHGS_PORT) {
+               dev_err(adap->pdev_dev, "xid %x out of bounds, tid:%x\n",
+                       xid, tid);
+               return NULL;
+       }
+
+       ddp = &fcoe->ddp[xid];
+
+       if ((fcoe->flags & CXGB_FCOE_ENABLED) && (ddp->tid == tid) && ddp->sgl)
+               return ddp;
+
+       return NULL;
+}
+
+static inline struct sk_buff *
+cxgb_fcoe_init_skb(struct adapter *adapter, u16 xid, struct port_info *pi,
+                  struct cxgb_fcoe_ddp *ddp, struct cpl_fcoe_hdr *cfcoe_hdr,
+                  struct sge_eth_rxq *rxq)
+{
+       struct sk_buff *skb;
+       struct ethhdr *eh;
+       struct fcoe_crc_eof *cp;
+       struct fc_frame_header *fh;
+       unsigned int hlen;              /* fcoe header length */
+       unsigned int tlen;              /* fcoe trailer length */
+       unsigned int elen;              /* eth header excluding vlan */
+       unsigned int fclen;             /* fc header len */
+       u8 rctl;
+       struct fcoe_hdr *hp;
+
+       elen = sizeof(struct ethhdr);
+       hlen = sizeof(struct fcoe_hdr);
+       fclen = sizeof(struct fc_frame_header);
+       tlen = sizeof(struct fcoe_crc_eof);
+
+       skb = dev_alloc_skb(elen + hlen + fclen + tlen);
+       if (!skb)
+               return NULL;
+
+       rctl = FCOE_FCHDR_RCTL_G(be32_to_cpu(cfcoe_hdr->rctl_fctl));
+
+       skb->ip_summed = CHECKSUM_UNNECESSARY;
+       skb->protocol = htons(ETH_P_FCOE);
+       skb->dev = adapter->port[pi->port_id];
+
+       eh = (struct ethhdr *)skb_put(skb, elen);
+       ether_addr_copy(eh->h_source, ddp->h_dest);
+       ether_addr_copy(eh->h_dest, ddp->h_source);
+       eh->h_proto = htons(ETH_P_FCOE);
+
+       hp = (struct fcoe_hdr *)skb_put(skb, hlen);
+       memset(hp, 0, sizeof(*hp));
+       if (FC_FCOE_VER)
+               FC_FCOE_ENCAPS_VER(hp, FC_FCOE_VER);
+       hp->fcoe_sof = cfcoe_hdr->sof;
+
+       fh = (struct fc_frame_header *)skb_put(skb, fclen);
+       fh->fh_r_ctl = rctl;
+       memcpy(fh->fh_d_id, &ddp->h_source[3], 3);
+       memcpy(fh->fh_s_id, ddp->d_id, 3);
+
+       fh->fh_cs_ctl = cfcoe_hdr->cs_ctl;
+       fh->fh_type = cfcoe_hdr->type;
+       memcpy(fh->fh_f_ctl, ((char *)&cfcoe_hdr->rctl_fctl) + 1, 3);
+       fh->fh_seq_id = cfcoe_hdr->seq_id;
+       fh->fh_df_ctl = cfcoe_hdr->df_ctl;
+       fh->fh_seq_cnt = cfcoe_hdr->seq_cnt;
+       fh->fh_ox_id = cfcoe_hdr->oxid;
+       fh->fh_rx_id = htons(xid);
+       fh->fh_parm_offset = cfcoe_hdr->param;
+
+       cp = (struct fcoe_crc_eof *)skb_put(skb, tlen);
+
+       memset(cp, 0, sizeof(*cp));
+       cp->fcoe_eof = cfcoe_hdr->eof;
+
+       skb_reset_mac_header(skb);
+       skb_set_network_header(skb, sizeof(*eh));
+       __skb_pull(skb, sizeof(*eh));
+       skb_record_rx_queue(skb, rxq->rspq.idx);
+
+       return skb;
+}
+
+static inline void
+cxgb_fcoe_cpl_fcoe_hdr(struct port_info *pi, struct sge_rspq *q,
+                      struct cpl_fcoe_hdr *cfcoe_hdr)
+{
+       struct adapter *adap = pi->adapter;
+       struct sk_buff *skb;
+       struct cxgb_fcoe_ddp *ddp;
+       struct sge_eth_rxq *rxq = container_of(q, struct sge_eth_rxq, rspq);
+       unsigned int tid = GET_TID(cfcoe_hdr);
+       u32 fctl;
+
+       ddp = cxgb_fcoe_lookup_ddp(pi, tid);
+       if (!ddp)
+               return;
+
+       if (ddp->flags & CXGB_FCOE_DDP_ERROR)
+               return;
+
+       fctl = G_FCOE_FCHDR_FCTL(be32_to_cpu(cfcoe_hdr->rctl_fctl));
+
+       ddp->ddp_len += ntohs(cfcoe_hdr->len);
+
+       /* Send skb only on transfer of sequence initiative (last frame) */
+       if ((fctl & (FC_FC_SEQ_INIT | FC_FC_END_SEQ)) !=
+                                       (FC_FC_SEQ_INIT | FC_FC_END_SEQ))
+               return;
+
+       /* Synth a skb */
+       skb = cxgb_fcoe_init_skb(adap, ddp->xid, pi, ddp, cfcoe_hdr, rxq);
+       if (unlikely(!skb)) {
+               ddp->flags |= CXGB_FCOE_DDP_ERROR;
+               return;
+       }
+
+       if (ddp->vlan_tci)
+               __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), ddp->vlan_tci);
+
+       netif_receive_skb(skb);
+}
+
+static void cxgb_fcoe_cpl_rx_fcoe_ddp(struct port_info *pi,
+                                     struct cpl_rx_fcoe_ddp *cfcoe_ddp)
+{
+       struct adapter *adap = pi->adapter;
+       struct cxgb_fcoe_ddp *ddp;
+       unsigned int tid = GET_TID(cfcoe_ddp);
+
+       ddp = cxgb_fcoe_lookup_ddp(pi, tid);
+       if (!ddp)
+               return;
+
+       dev_warn(adap->pdev_dev, "DDP Error, xid:%x tid:%x report:%x"
+                " vld:%x\n", ddp->xid, tid,
+                be32_to_cpu(cfcoe_ddp->ddp_report),
+                be32_to_cpu(cfcoe_ddp->ddpvld));
+
+       ddp->flags |= CXGB_FCOE_DDP_ERROR;
+}
+
+int cxgb_fcoe_rx_handler(struct sge_rspq *q, const __be64 *rsp)
+{
+       struct port_info *pi = netdev_priv(q->netdev);
+
+       switch (*(u8 *)rsp) {
+       case CPL_FCOE_HDR:
+               cxgb_fcoe_cpl_fcoe_hdr(pi, q,
+                                      (struct cpl_fcoe_hdr *)&rsp[1]);
+               break;
+       case CPL_RX_FCOE_DDP:
+               cxgb_fcoe_cpl_rx_fcoe_ddp(pi,
+                                         (struct cpl_rx_fcoe_ddp *)&rsp[1]);
+               break;
+       case CPL_FCOE_DATA:
+               break;
+       default:
+               return 0;
+       }
+
+       return 1;
+}
+
+/**
+ * cxgb_fcoe_alloc_ppods - Allocate page pods
+ * @adap: adapter
+ * @n: number of page pods to allocate
+ *
+ * Returns -1 on failure or the page pod tag
+ */
+static inline int
+cxgb_fcoe_alloc_ppods(struct adapter *adap, unsigned int n)
+{
+       unsigned int i, j;
+       struct cxgb4_virt_res *vres = &adap->vres;
+
+       if (unlikely(!vres->ppod_map))
+               return -1;
+
+       spin_lock_bh(&vres->ppod_map_lock);
+
+       /* Look for n consecutive available page pods.
+        * Make sure to guard from scanning beyond the table.
+        */
+       for (i = 0; i + n - 1 < vres->fcoe_nppods; ) {
+               for (j = 0; j < n; ++j)         /* scan ppod_map[i..i+n-1] */
+                       if (vres->ppod_map[i + j]) {
+                               i = i + j + 1;
+                               goto next;
+                       }
+
+               memset(&vres->ppod_map[i], 1, n);   /* allocate range */
+               spin_unlock_bh(&vres->ppod_map_lock);
+               return i;
+next:
+               continue;
+       }
+
+       spin_unlock_bh(&vres->ppod_map_lock);
+       return -1;
+}
+
+void
+cxgb_fcoe_free_ppods(struct adapter *adap, unsigned int tag, unsigned int n)
+{
+       struct cxgb4_virt_res *vres = &adap->vres;
+
+       spin_lock_bh(&vres->ppod_map_lock);
+       memset(&vres->ppod_map[tag], 0, n);
+       spin_unlock_bh(&vres->ppod_map_lock);
+}
+
+static inline void cxgb_fcoe_clear_ddp(struct cxgb_fcoe_ddp *ddp)
+{
+       ddp->sgl = NULL;
+       ddp->sgc = 0;
+       ddp->first_pg_off = 0;
+       ddp->nppods = 0;
+       ddp->ppod_tag = 0;
+       ddp->xfer_len = 0;
+       ddp->ddp_len = 0;
+       ddp->npages = 0;
+       ddp->flags = 0;
+}
+
+void cxgb_fcoe_cpl_act_open_rpl(struct adapter *adap, unsigned int atid,
+                               unsigned int tid, unsigned int status)
+{
+       u16 xid = CXGB_FCOE_GET_XID(atid);
+       u8 port_id = CXGB_FCOE_GET_PORTID(atid);
+       struct port_info *pi = adap2pinfo(adap, port_id);
+       struct cxgb_fcoe *fcoe = &pi->fcoe;
+       struct cxgb_fcoe_ddp *ddp = &fcoe->ddp[xid];
+
+       if ((status == CPL_ERR_NONE) &&
+           (tid < adap->tids.ntids)) {
+               ddp->tid = tid;
+               ddp->flags |= CXGB_FCOE_DDP_TID_VALID;
+               adap->vres.tid2xid[tid] = xid;
+       } else
+               dev_err(adap->pdev_dev, "tid allocation failed xid 0x%x status 
0x%x\n",
+                       xid, status);
+
+       complete(fcoe->cmpl);
+}
+
+static int cxgb_fcoe_alloc_tid(struct port_info *pi, u16 xid)
+{
+       struct adapter *adap = pi->adapter;
+       struct cxgb_fcoe *fcoe = &pi->fcoe;
+       struct cxgb_fcoe_ddp *ddp = &fcoe->ddp[xid];
+       struct tp_params *tp = &adap->params.tp;
+       struct cpl_t5_act_open_req *req;
+       struct sk_buff *skb;
+       unsigned int qid_atid = xid;
+
+       skb = alloc_skb(sizeof(*req), GFP_KERNEL);
+       if (!skb)
+               return 1;
+
+       qid_atid |= BIT(CXGB_FCOE_ATID);
+       qid_atid |= (pi->port_id << CXGB_FCOE_SHIFT_PORTID);
+       qid_atid |= (adap->sge.fw_evtq.abs_id << 14);
+
+       req = (struct cpl_t5_act_open_req *)__skb_put(skb, sizeof(*req));
+       memset(req, 0, sizeof(*req));
+
+       INIT_TP_WR(req, 0);
+       OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_ACT_OPEN_REQ, qid_atid));
+
+       req->peer_port = cpu_to_be16(xid);
+       req->opt0 = cpu_to_be64(ULP_MODE_V(ULP_MODE_FCOE) |
+                       NON_OFFLOAD_F | NO_CONG_F | TX_CHAN_V(pi->tx_chan) |
+                       RCV_BUFSIZ_V(RCV_BUFSIZ_M) | L2T_IDX_V(0));
+
+       req->params = cpu_to_be64(FILTER_TUPLE_V(
+                               (pi->port_id << tp->port_shift) |
+                               (1 << tp->fcoe_shift)) | AOPEN_FCOEMASK_F);
+
+       if (t4_mgmt_tx(adap, skb) == NET_XMIT_DROP)
+               return 1;
+
+       wait_for_completion(fcoe->cmpl);
+
+       reinit_completion(fcoe->cmpl);
+
+       if (!(ddp->flags & CXGB_FCOE_DDP_TID_VALID))
+               return 1;
+
+       return 0;
+}
+
+static void cxgb_fcoe_free_tid(struct port_info *pi, u16 xid)
+{
+       struct adapter *adap = pi->adapter;
+       struct cxgb_fcoe *fcoe = &pi->fcoe;
+       struct cxgb_fcoe_ddp *ddp = &fcoe->ddp[xid];
+       struct cpl_tid_release *req;
+       struct sk_buff *skb;
+       unsigned int len = ALIGN(sizeof(*req), 16);
+
+       skb = alloc_skb(len, GFP_KERNEL);
+       if (!skb)
+               return;
+
+       req = (struct cpl_tid_release *)__skb_put(skb, len);
+       memset(req, 0, len);
+
+       INIT_TP_WR(req, 0);
+       OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_TID_RELEASE, ddp->tid));
+
+       t4_mgmt_tx(adap, skb);
+}
+
+static void cxgb_fcoe_free_ddp(struct port_info *pi, u16 xid)
+{
+       struct cxgb_fcoe *fcoe = &pi->fcoe;
+       struct cxgb_fcoe_ddp *ddp;
+       u16 i;
+
+       for (i = 0; i < xid; i++) {
+               ddp = &fcoe->ddp[i];
+               kfree(ddp->ppod_gl);
+               cxgb_fcoe_free_tid(pi, i);
+       }
+}
+
+/* Return the # of page pods needed to accommodate a # of pages.
+ */
+static inline unsigned int pages2ppods(unsigned int pages)
+{
+       return (pages + PPOD_PAGES - 1) / PPOD_PAGES +
+                       CXGB_FCOE_NUM_SENTINEL_PPODS;
+}
+
+/**
+ * cxgb_fcoe_ddp_setup - setup ddp in target mode
+ * @netdev: net device
+ * @xid: exchange id
+ * @sgl: scatterlist
+ * @sgc: number of scatterlist elements
+ *
+ * Returns 1 on success or 0 on failure.
+ */
+int cxgb_fcoe_ddp_setup(struct net_device *netdev, u16 xid,
+                       struct scatterlist *sgl, unsigned int sgc)
+{
+       struct port_info *pi;
+       struct adapter *adap;
+       struct cxgb_fcoe *fcoe;
+       struct cxgb_fcoe_ddp *ddp;
+       struct scatterlist *sg;
+       unsigned int nsge, i, j, len, nppods;
+       static const unsigned int bufflen = PAGE_SIZE;
+       unsigned int firstoff = 0;
+       unsigned int thisoff = 0;
+       unsigned int thislen = 0;
+       unsigned int totlen = 0;
+       int tag;
+       dma_addr_t addr;
+
+       if (!netdev || !sgl)
+               return 0;
+
+       pi = netdev_priv(netdev);
+       adap = pi->adapter;
+
+       if (xid >= CXGB_FCOE_MAX_XCHGS_PORT) {
+               dev_warn(adap->pdev_dev, "xid=0x%x out-of-range\n", xid);
+               return 0;
+       }
+
+       fcoe = &pi->fcoe;
+       ddp = &fcoe->ddp[xid];
+       if (ddp->sgl) {
+               dev_err(adap->pdev_dev, "xid 0x%x w/ non-null sgl%p nents=%d\n",
+                       xid, ddp->sgl, ddp->sgc);
+               return 0;
+       }
+
+       cxgb_fcoe_clear_ddp(ddp);
+
+       nsge = pci_map_sg(adap->pdev, sgl, sgc, DMA_FROM_DEVICE);
+       if (nsge == 0) {
+               dev_err(adap->pdev_dev, "xid 0x%x DMA map error\n", xid);
+               return 0;
+       }
+
+       j = 0;
+       for_each_sg(sgl, sg, nsge, i) {
+               addr = sg_dma_address(sg);
+               len = sg_dma_len(sg);
+               totlen += len;
+               while (len) {
+                       /* max number of pages allowed in one DDP transfer */
+                       if (j >= CXGB_FCOE_MAX_PAGE_CNT) {
+                               dev_err(adap->pdev_dev,
+                                       "xid=%x:%d,%d,%d:addr=%llx "
+                                       "not enough descriptors\n",
+                                       xid, i, j, nsge, (u64)addr);
+                               goto out_noddp;
+                       }
+
+                       /* get the offset of length of current buffer */
+                       thisoff = addr & ((dma_addr_t)bufflen - 1);
+                       thislen = min((bufflen - thisoff), len);
+
+                       /* all but the 1st buffer (j == 0)
+                        * must be aligned on bufflen
+                        */
+                       if ((j != 0) && (thisoff))
+                               goto out_noddp;
+                       /* all but the last buffer
+                        * ((i == (nsge - 1)) && (thislen == len))
+                        * must end at bufflen
+                        */
+                       if (((i != (nsge - 1)) || (thislen != len)) &&
+                           ((thislen + thisoff) != bufflen))
+                               goto out_noddp;
+
+                       ddp->ppod_gl[j] = (dma_addr_t)(addr - thisoff);
+
+                       /* only the first buffer may have none-zero offset */
+                       if (j == 0)
+                               firstoff = thisoff;
+                       len -= thislen;
+                       addr += thislen;
+                       j++;
+               }
+       }
+
+       nppods = pages2ppods(j);
+       tag = cxgb_fcoe_alloc_ppods(adap, nppods);
+       if (tag < 0) {
+               dev_err(adap->pdev_dev, "Failed to allocate %d ppods"
+                                       " xid:0x%x\n", nppods, xid);
+               goto out_noddp;
+       }
+
+       /* Should be offset by TOE's ppods */
+       tag += adap->vres.toe_nppods;
+
+       ddp->sgl = sgl;
+       ddp->sgc = sgc;
+       ddp->xfer_len = totlen;
+       ddp->first_pg_off = firstoff;
+       ddp->nppods = nppods;
+       ddp->npages = j;
+       ddp->ppod_tag = tag;
+
+       return 1;
+
+out_noddp:
+       pci_unmap_sg(adap->pdev, sgl, sgc, DMA_FROM_DEVICE);
+       return 0;
+}
+
+/**
+ * cxgb_fcoe_ddp_done - complete DDP
+ * @netdev: net device
+ * @xid: exchange id
+ *
+ * Returns length of data directly placed in bytes.
+ */
+int cxgb_fcoe_ddp_done(struct net_device *netdev, u16 xid)
+{
+       struct port_info *pi;
+       struct adapter *adap;
+       struct cxgb_fcoe *fcoe;
+       struct cxgb_fcoe_ddp *ddp;
+       int len = 0;
+
+       if (!netdev)
+               return 0;
+
+       pi = netdev_priv(netdev);
+       adap = pi->adapter;
+
+       if (xid >= CXGB_FCOE_MAX_XCHGS_PORT) {
+               dev_warn(adap->pdev_dev, "ddp_done: xid%x out-of-range\n", xid);
+               return 0;
+       }
+
+       fcoe = &pi->fcoe;
+       ddp = &fcoe->ddp[xid];
+       if (!ddp->sgl) {
+               dev_err(adap->pdev_dev, "ddp_done: xid %x with null sgl\n",
+                       xid);
+               return 0;
+       }
+
+       if (!(ddp->flags & CXGB_FCOE_DDP_ERROR))
+               len = ddp->ddp_len;
+
+       cxgb_fcoe_free_ppods(adap, ddp->ppod_tag - adap->vres.toe_nppods,
+                            ddp->nppods);
+
+       if (ddp->sgl)
+               pci_unmap_sg(adap->pdev, ddp->sgl, ddp->sgc, DMA_FROM_DEVICE);
+
+       cxgb_fcoe_clear_ddp(ddp);
+
+       return len;
+}
+
 /**
  * cxgb_fcoe_enable - enable FCoE offload features
  * @netdev: net device
@@ -70,6 +594,10 @@ int cxgb_fcoe_enable(struct net_device *netdev)
        struct port_info *pi = netdev_priv(netdev);
        struct adapter *adap = pi->adapter;
        struct cxgb_fcoe *fcoe = &pi->fcoe;
+       struct tp_params *tp = &adap->params.tp;
+       struct cxgb_fcoe_ddp *ddp;
+       struct completion cmpl;
+       u16 xid;
 
        if (is_t4(adap->params.chip))
                return -EINVAL;
@@ -77,12 +605,48 @@ int cxgb_fcoe_enable(struct net_device *netdev)
        if (!(adap->flags & FULL_INIT_DONE))
                return -EINVAL;
 
+       if (adap->tids.natids > 8192)
+               return -EINVAL;
+
+       if ((tp->port_shift < 0) || (tp->fcoe_shift < 0))
+               return -EINVAL;
+
+       if (!adap->vres.ppod_map || !adap->vres.tid2xid) {
+               dev_warn(adap->pdev_dev, "FCoE Offload resources "
+                        " unavailable\n");
+               return -EINVAL;
+       }
+
        dev_info(adap->pdev_dev, "Enabling FCoE offload features\n");
 
+       init_completion(&cmpl);
+       fcoe->cmpl = &cmpl;
+       memset(fcoe->ddp, 0, sizeof(*ddp) * CXGB_FCOE_MAX_XCHGS_PORT);
+
+       for (xid = 0; xid < CXGB_FCOE_MAX_XCHGS_PORT; xid++) {
+               ddp = &fcoe->ddp[xid];
+               ddp->xid = xid;
+               ddp->ppod_gl = kcalloc(CXGB_FCOE_MAX_PAGE_CNT,
+                                       sizeof(dma_addr_t), GFP_KERNEL);
+               if (!ddp->ppod_gl) {
+                       cxgb_fcoe_free_ddp(pi, xid);
+                       return -EINVAL;
+               }
+
+               if (cxgb_fcoe_alloc_tid(pi, xid)) {
+                       dev_warn(adap->pdev_dev, "Unable to allocate "
+                                "tid xid 0x%x\n", xid);
+                       kfree(ddp->ppod_gl);
+                       cxgb_fcoe_free_ddp(pi, xid);
+                       return -EINVAL;
+               }
+       }
+
        netdev->features |= NETIF_F_FCOE_CRC;
        netdev->vlan_features |= NETIF_F_FCOE_CRC;
        netdev->features |= NETIF_F_FCOE_MTU;
        netdev->vlan_features |= NETIF_F_FCOE_MTU;
+       netdev->fcoe_ddp_xid = CXGB_FCOE_MAX_XCHGS_PORT - 1;
 
        netdev_features_change(netdev);
 
@@ -114,9 +678,45 @@ int cxgb_fcoe_disable(struct net_device *netdev)
        netdev->vlan_features &= ~NETIF_F_FCOE_CRC;
        netdev->features &= ~NETIF_F_FCOE_MTU;
        netdev->vlan_features &= ~NETIF_F_FCOE_MTU;
+       netdev->fcoe_ddp_xid = 0;
 
        netdev_features_change(netdev);
 
+       cxgb_fcoe_free_ddp(pi, CXGB_FCOE_MAX_XCHGS_PORT);
+
        return 0;
 }
+
+void cxgb_fcoe_init_ddp(struct adapter *adap)
+{
+       u32 tot_ppods = adap->vres.ddp.size / CXGB_FCOE_PPOD_SIZE;
+       u32 fcoe_ddp_size, fcoe_ddp_start;
+
+       adap->vres.fcoe_nppods = tot_ppods / 2;
+       adap->vres.toe_nppods = tot_ppods - adap->vres.fcoe_nppods;
+
+       adap->vres.ddp.size = adap->vres.toe_nppods * CXGB_FCOE_PPOD_SIZE;
+       fcoe_ddp_size = adap->vres.fcoe_nppods * CXGB_FCOE_PPOD_SIZE;
+       fcoe_ddp_start = adap->vres.ddp.start + adap->vres.ddp.size;
+
+       dev_info(adap->pdev_dev, "TOE ddp start:0x%x size:%d"
+                " nppods:%d\n", adap->vres.ddp.start,
+                adap->vres.ddp.size, adap->vres.toe_nppods);
+       dev_info(adap->pdev_dev, "FCoE ddp start:0x%x size:%d"
+                " nppods:%d tids:%d\n",
+                fcoe_ddp_start, fcoe_ddp_size,
+                adap->vres.fcoe_nppods, adap->tids.ntids);
+
+       spin_lock_init(&adap->vres.ppod_map_lock);
+
+       adap->vres.ppod_map = kzalloc(adap->vres.fcoe_nppods, GFP_KERNEL);
+       adap->vres.tid2xid = kcalloc(adap->tids.ntids, sizeof(u16),
+                               GFP_KERNEL);
+}
+
+void cxgb_fcoe_exit_ddp(struct adapter *adap)
+{
+       kfree(adap->vres.ppod_map);
+       kfree(adap->vres.tid2xid);
+}
 #endif /* CONFIG_CHELSIO_T4_FCOE */
diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c 
b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
index 24e10ea..95e7527 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
@@ -544,6 +544,23 @@ static void clear_filter(struct adapter *adap, struct 
filter_entry *f)
        memset(f, 0, sizeof(*f));
 }
 
+#ifdef CONFIG_CHELSIO_T4_FCOE
+static void hash_filter_rpl(struct adapter *adap,
+                           const struct cpl_act_open_rpl *rpl)
+{
+       unsigned int tid = GET_TID(rpl);
+       unsigned int ftid = TID_TID_G(AOPEN_ATID_G(ntohl(rpl->atid_status)));
+       unsigned int status  = AOPEN_STATUS_G(ntohl(rpl->atid_status));
+
+       /* ATID is 14 bit value [0..13], MAX_ATIDS is 8192
+        * ATID needs max 13 bits [0..12], using 13th bit in
+        * ATID for FCoE CPL_ACT_OPEN_REQ.
+        */
+       if (ftid & BIT(CXGB_FCOE_ATID))
+               cxgb_fcoe_cpl_act_open_rpl(adap, ftid, tid, status);
+}
+#endif /* CONFIG_CHELSIO_T4_FCOE */
+
 /* Handle a filter write/deletion reply.
  */
 static void filter_rpl(struct adapter *adap, const struct cpl_set_tcb_rpl *rpl)
@@ -661,7 +678,15 @@ static int fwevtq_handler(struct sge_rspq *q, const __be64 
*rsp,
                const struct cpl_set_tcb_rpl *p = (void *)rsp;
 
                filter_rpl(q->adap, p);
-       } else
+       }
+#ifdef CONFIG_CHELSIO_T4_FCOE
+       else if (opcode == CPL_ACT_OPEN_RPL) {
+               const struct cpl_act_open_rpl *p = (void *)rsp;
+
+               hash_filter_rpl(q->adap, p);
+       }
+#endif /* CONFIG_CHELSIO_T4_FCOE */
+       else
                dev_err(q->adap->pdev_dev,
                        "unexpected CPL %#x on FW event queue\n", opcode);
 out:
@@ -3010,6 +3035,8 @@ static const struct net_device_ops cxgb4_netdev_ops = {
 #ifdef CONFIG_CHELSIO_T4_FCOE
        .ndo_fcoe_enable      = cxgb_fcoe_enable,
        .ndo_fcoe_disable     = cxgb_fcoe_disable,
+       .ndo_fcoe_ddp_target  = cxgb_fcoe_ddp_setup,
+       .ndo_fcoe_ddp_done    = cxgb_fcoe_ddp_done,
 #endif /* CONFIG_CHELSIO_T4_FCOE */
 #ifdef CONFIG_NET_RX_BUSY_POLL
        .ndo_busy_poll        = cxgb_busy_poll,
@@ -3845,6 +3872,10 @@ static int adap_init0(struct adapter *adap)
                adap->vres.ddp.start = val[3];
                adap->vres.ddp.size = val[4] - val[3] + 1;
                adap->params.ofldq_wr_cred = val[5];
+#ifdef CONFIG_CHELSIO_T4_FCOE
+               if (ntohs(caps_cmd.fcoecaps) & FW_CAPS_CONFIG_POFCOE_TARGET)
+                       cxgb_fcoe_init_ddp(adap);
+#endif /* CONFIG_CHELSIO_T4_FCOE */
 
                adap->params.offload = 1;
        }
@@ -3965,6 +3996,9 @@ bye:
        kfree(adap->sge.txq_maperr);
        if (ret != -ETIMEDOUT && ret != -EIO)
                t4_fw_bye(adap, adap->mbox);
+#ifdef CONFIG_CHELSIO_T4_FCOE
+       cxgb_fcoe_exit_ddp(adap);
+#endif /* CONFIG_CHELSIO_T4_FCOE */
        return ret;
 }
 
@@ -4757,6 +4791,9 @@ static void remove_one(struct pci_dev *pdev)
                iounmap(adapter->regs);
                if (!is_t4(adapter->params.chip))
                        iounmap(adapter->bar2);
+#ifdef CONFIG_CHELSIO_T4_FCOE
+               cxgb_fcoe_exit_ddp(adapter);
+#endif /* CONFIG_CHELSIO_T4_FCOE */
                pci_disable_pcie_error_reporting(pdev);
                if ((adapter->flags & DEV_ENABLED)) {
                        pci_disable_device(pdev);
diff --git a/drivers/net/ethernet/chelsio/cxgb4/sge.c 
b/drivers/net/ethernet/chelsio/cxgb4/sge.c
index e622214..05e8c93 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/sge.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/sge.c
@@ -48,6 +48,8 @@
 #endif /* CONFIG_NET_RX_BUSY_POLL */
 #ifdef CONFIG_CHELSIO_T4_FCOE
 #include <scsi/fc/fc_fcoe.h>
+#include <scsi/libfcoe.h>
+#include "t4_tcb.h"
 #endif /* CONFIG_CHELSIO_T4_FCOE */
 #include "cxgb4.h"
 #include "t4_regs.h"
@@ -1048,11 +1050,260 @@ static inline void txq_advance(struct sge_txq *q, 
unsigned int n)
 }
 
 #ifdef CONFIG_CHELSIO_T4_FCOE
+
+#define CXGB_FCOE_NUM_IMM_PPODS                4
+
+#define CXGB_FCOE_NUM_IMM_PPOD_BYTES   \
+       (CXGB_FCOE_NUM_IMM_PPODS * CXGB_FCOE_PPOD_SIZE)
+
+#define WR_LEN_MAX_PPODS       \
+       (sizeof(struct ulp_mem_io) + \
+       sizeof(struct ulptx_idata) + \
+       CXGB_FCOE_NUM_IMM_PPOD_BYTES)
+
+#define WR_CRED_MAX_PPODS      (DIV_ROUND_UP(WR_LEN_MAX_PPODS, IDXSIZE_UNIT_X))
+
+#define WR_LEN_SET_TCBS \
+       (sizeof(struct fw_pofcoe_ulptx_wr) + \
+        (5 * ALIGN(sizeof(struct cpl_set_tcb_field), 16)))
+
+#define WR_LEN16_SET_TCBS DIV_ROUND_UP(WR_LEN_SET_TCBS, 16)
+
+#define WR_NDESC_SET_TCBS DIV_ROUND_UP(WR_LEN_SET_TCBS, IDXSIZE_UNIT_X)
+
+static inline int calc_ddp_credits(struct sk_buff *skb, unsigned int nppods)
+{
+       unsigned int n_full = (nppods / CXGB_FCOE_NUM_IMM_PPODS);
+       int credits = n_full * WR_CRED_MAX_PPODS;
+       unsigned int last_ppod_len = (nppods % CXGB_FCOE_NUM_IMM_PPODS) *
+                                       CXGB_FCOE_PPOD_SIZE;
+       unsigned int last_len;
+       unsigned int flits;
+
+       if (last_ppod_len) {
+               last_len = sizeof(struct ulp_mem_io) +
+                               sizeof(struct ulptx_idata) + last_ppod_len;
+               credits += DIV_ROUND_UP(last_len, IDXSIZE_UNIT_X);
+       }
+
+       credits += WR_NDESC_SET_TCBS;
+
+       flits = calc_tx_flits(skb);
+       credits += flits_to_desc(flits);
+
+       return credits;
+}
+
+static inline void
+cxgb_fcoe_set_tcb_field(struct cpl_set_tcb_field *req, unsigned int tid,
+                       unsigned int word, u64 mask, u64 val)
+{
+       struct ulp_txpkt *txpkt = (struct ulp_txpkt *)req;
+       struct ulptx_idata *sc = (struct ulptx_idata *)(txpkt + 1);
+
+       txpkt->cmd_dest = htonl(ULPTX_CMD_V(ULP_TX_PKT) | ULP_TXPKT_DEST_V(0));
+       txpkt->len = htonl((tid << 8) | DIV_ROUND_UP(sizeof(*req), 16));
+       sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM));
+       sc->len = htonl(sizeof(*req) - sizeof(struct work_request_hdr));
+       OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, tid));
+       req->reply_ctrl = htons(NO_REPLY_V(1) | REPLY_CHAN_V(0) |
+                               QUEUENO_V(0));
+       req->word_cookie = htons(TCB_WORD(word) | TCB_COOKIE_V(0));
+       req->mask = cpu_to_be64(mask);
+       req->val = cpu_to_be64(val);
+       sc = (struct ulptx_idata *)(req + 1);
+       sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_NOOP));
+       sc->len = htonl(0);
+}
+
+static inline void
+cxgb_fcoe_set_tcbs(struct adapter *adap, const struct port_info *pi,
+                  struct sge_eth_txq *q,
+                  struct cxgb_fcoe_ddp *ddp, u16 iqid)
+{
+       struct cpl_set_tcb_field *req;
+       struct fw_pofcoe_ulptx_wr *wr;
+       u8 buf[WR_LEN_SET_TCBS] = {0};
+       u8 *end, *wrp = (u8 *)&q->q.desc[q->q.pidx];
+       unsigned int len = ALIGN(sizeof(struct cpl_set_tcb_field), 16);
+
+       end = wrp + WR_LEN_SET_TCBS;
+       wr = (struct fw_pofcoe_ulptx_wr *)
+               ((u8 *)end > (u8 *)q->q.stat ? buf : wrp);
+
+       wr->op_pkd = htonl(FW_WR_OP_V(FW_POFCOE_ULPTX_WR));
+       wr->equiq_to_len16 = htonl(FW_WR_LEN16_V(WR_LEN16_SET_TCBS));
+
+       req = (struct cpl_set_tcb_field *)(wr + 1);
+       cxgb_fcoe_set_tcb_field(req, ddp->tid, TCB_RX_DDP_BUF0_TAG_W,
+                               TCB_RX_DDP_BUF0_TAG_V(TCB_RX_DDP_BUF0_TAG_M),
+                               TCB_RX_DDP_BUF0_TAG_V(
+                                       PPOD_TAG_V(ddp->ppod_tag)));
+
+       req = (struct cpl_set_tcb_field *)((u8 *)req + len);
+       cxgb_fcoe_set_tcb_field(req, ddp->tid, TCB_RX_DDP_BUF0_OFFSET_W,
+                               TCB_RX_DDP_BUF0_OFFSET_V(
+                                       TCB_RX_DDP_BUF0_OFFSET_M) |
+                               TCB_RX_DDP_BUF0_LEN_V(TCB_RX_DDP_BUF0_LEN_M),
+                               TCB_RX_DDP_BUF0_OFFSET_V(0) |
+                               TCB_RX_DDP_BUF0_LEN_V(ddp->xfer_len));
+
+       req = (struct cpl_set_tcb_field *)((u8 *)req + len);
+       cxgb_fcoe_set_tcb_field(req, ddp->tid, TCB_T_STATE_W,
+                               TCB_T_STATE_V(TCB_T_STATE_M) |
+                               TCB_RSS_INFO_V(TCB_RSS_INFO_M),
+                               TCB_T_STATE_V(0x4) |
+                               TCB_RSS_INFO_V(iqid));
+
+       req = (struct cpl_set_tcb_field *)((u8 *)req + len);
+       cxgb_fcoe_set_tcb_field(req, ddp->tid, TCB_T_FLAGS_W,
+                               TF_NON_OFFLOAD_V(1), 0);
+
+       req = (struct cpl_set_tcb_field *)((u8 *)req + len);
+       cxgb_fcoe_set_tcb_field(req, ddp->tid, TCB_RX_DDP_FLAGS_W,
+                               TF_DDP_BUF_INF_V(1) |
+                               TF_DDP_INDICATE_OUT_V(1) |
+                               TF_DDP_BUF0_INDICATE_V(1) |
+                               TF_DDP_BUF0_VALID_V(1) |
+                               TF_DDP_OFF_V(1),
+                               TF_DDP_BUF_INF_V(1) |
+                               TF_DDP_INDICATE_OUT_V(1) |
+                               TF_DDP_BUF0_INDICATE_V(1) |
+                               TF_DDP_BUF0_VALID_V(1) |
+                               TF_DDP_OFF_V(0));
+
+       if (unlikely((u8 *)end > (u8 *)q->q.stat)) {
+               unsigned int part0 = (u8 *)q->q.stat - (u8 *)wrp, part1;
+
+               if (likely(part0))
+                       memcpy(wrp, buf, part0);
+               part1 = (u8 *)end - (u8 *)q->q.stat;
+               memcpy(q->q.desc, (u8 *)buf + part0, part1);
+               end = (void *)q->q.desc + part1;
+       }
+
+       if ((uintptr_t)end & 8)         /* 0-pad to multiple of 16 */
+               *(u64 *)end = 0;
+
+       /* Post this WR */
+       txq_advance(&q->q, WR_NDESC_SET_TCBS);
+       ring_tx_db(adap, &q->q, WR_NDESC_SET_TCBS);
+}
+
+static inline void
+cxgb_setup_ppods(struct adapter *adap, const struct port_info *pi,
+                struct sge_eth_txq *q, struct cxgb_fcoe_ddp *ddp)
+{
+       unsigned int i, j, pidx;
+       struct pagepod *p;
+       u8 *wrp = (u8 *)&q->q.desc[q->q.pidx];
+       struct fw_pofcoe_ulptx_wr *mwr;
+       struct ulp_mem_io *wr;
+       struct ulptx_idata *sc;
+       unsigned int tid = ddp->tid;
+       unsigned int color = 0;
+       unsigned int nppods = ddp->nppods;
+       unsigned int tag = ddp->ppod_tag;
+       unsigned int maxoff = ddp->xfer_len;
+       unsigned int pg_off = ddp->first_pg_off;
+       unsigned int ppod_addr = tag * CXGB_FCOE_PPOD_SIZE +
+                                       adap->vres.ddp.start;
+       unsigned int len, podchunk, ndesc;
+       u8 buf[WR_LEN_MAX_PPODS];
+       u8 *end, *to;
+       __be32 cmd = htonl(ULPTX_CMD_V(ULP_TX_MEM_WRITE));
+
+       if (is_t4(adap->params.chip))
+               cmd |= htonl(ULP_MEMIO_ORDER_V(1));
+       else
+               cmd |= htonl(T5_ULP_MEMIO_IMM_V(1));
+
+       for (i = 0; i < nppods; ppod_addr += podchunk) {
+               unsigned int ppodout = 0;
+
+               podchunk = ((nppods - i) >= CXGB_FCOE_NUM_IMM_PPODS) ?
+                               CXGB_FCOE_NUM_IMM_PPODS : (nppods - i);
+               podchunk *= CXGB_FCOE_PPOD_SIZE;
+
+               len = roundup(sizeof(*wr) + sizeof(*sc) + podchunk, 16);
+               end = wrp + len;
+               to = (u8 *)end > (u8 *)q->q.stat ? buf : wrp;
+
+               mwr = (struct fw_pofcoe_ulptx_wr *)to;
+               mwr->op_pkd = htonl(FW_WR_OP_V(FW_POFCOE_ULPTX_WR));
+               mwr->equiq_to_len16 = htonl(FW_WR_LEN16_V(
+                                               DIV_ROUND_UP(len, 16)));
+               wr = (struct ulp_mem_io *)to;
+               wr->cmd = cmd;
+               wr->dlen = htonl(ULP_MEMIO_DATA_LEN_V(podchunk / 32));
+               wr->len16 = htonl((ddp->tid << 8) |
+                                       DIV_ROUND_UP(len - sizeof(wr->wr), 16));
+               wr->lock_addr = htonl(ULP_MEMIO_ADDR_V(ppod_addr >> 5));
+               sc = (struct ulptx_idata *)(wr + 1);
+               sc->cmd_more = htonl(ULPTX_CMD_V(ULP_TX_SC_IMM));
+               sc->len = htonl(podchunk);
+               p = (struct pagepod *)(sc + 1);
+
+               do {
+                       pidx = 4 * i;
+                       if (likely(i < nppods - CXGB_FCOE_NUM_SENTINEL_PPODS)) {
+                               p->vld_tid_pgsz_tag_color =
+                                       cpu_to_be64(PPOD_VALID_F |
+                                                       PPOD_TID_V(tid) |
+                                                       PPOD_TAG_V(tag) |
+                                                       PPOD_COLOR_V(color));
+                               p->len_offset = cpu_to_be64(PPOD_LEN_V(maxoff) |
+                                                       PPOD_OFST_V(pg_off));
+                               p->rsvd = 0;
+                               for (j = 0; j < 5; ++j, ++pidx)
+                                       p->addr[j] = pidx < ddp->npages ?
+                                           cpu_to_be64(ddp->ppod_gl[pidx]) : 0;
+                       } else {
+                               /* mark sentinel page pods invalid */
+                               p->vld_tid_pgsz_tag_color = 0;
+                       }
+                       p++;
+                       ppodout += CXGB_FCOE_PPOD_SIZE;
+                       i++;
+
+               } while (ppodout < podchunk);
+
+               if (unlikely((u8 *)end > (u8 *)q->q.stat)) {
+                       unsigned int part0 = (u8 *)q->q.stat - (u8 *)wrp, part1;
+
+                       if (likely(part0))
+                               memcpy(wrp, buf, part0);
+                       part1 = (u8 *)end - (u8 *)q->q.stat;
+                       memcpy(q->q.desc, (u8 *)buf + part0, part1);
+                       end = (void *)q->q.desc + part1;
+               }
+
+               if ((uintptr_t)end & 8)         /* 0-pad to multiple of 16 */
+                       *(u64 *)end = 0;
+
+               /* Post this WR */
+               ndesc = DIV_ROUND_UP(len, IDXSIZE_UNIT_X);
+               txq_advance(&q->q, ndesc);
+               ring_tx_db(adap, &q->q, ndesc);
+
+               wrp = (u8 *)&q->q.desc[q->q.pidx];
+       } /* for all pagepod chunks */
+}
+
 static inline int
-cxgb_fcoe_offload(struct sk_buff *skb, struct adapter *adap,
-                 const struct port_info *pi, u64 *cntrl)
+cxgb_fcoe_offload(struct sk_buff *skb, struct net_device *dev,
+                 struct adapter *adap, const struct port_info *pi,
+                 struct sge_eth_txq *q, u64 *cntrl)
 {
        const struct cxgb_fcoe *fcoe = &pi->fcoe;
+       struct cxgb_fcoe_ddp *ddp;
+       struct ethhdr *eh;
+       struct fc_frame_header *fh;
+       struct sge_eth_rxq *rxq;
+       unsigned int ndesc;
+       int qidx, credits;
+       u16 xid, vlan_tci = 0;
+       u32 fctl;
 
        if (!(fcoe->flags & CXGB_FCOE_ENABLED))
                return 0;
@@ -1075,6 +1326,64 @@ cxgb_fcoe_offload(struct sk_buff *skb, struct adapter 
*adap,
                     TXPKT_CSUM_START(CXGB_FCOE_TXPKT_CSUM_START) |
                     TXPKT_CSUM_END(CXGB_FCOE_TXPKT_CSUM_END) |
                     TXPKT_CSUM_LOC(CXGB_FCOE_TXPKT_CSUM_END);
+
+       if (skb_vlan_tag_present(skb)) {
+               vlan_tci = skb_vlan_tag_get(skb);
+               vlan_tci |= ((skb->priority & 0x7) << VLAN_PRIO_SHIFT);
+       }
+
+       fh = (struct fc_frame_header *)(skb_transport_header(skb));
+
+       /* Program DDP for XFER_RDY frames only */
+       if (fh->fh_r_ctl != FC_RCTL_DD_DATA_DESC)
+               return 0;
+
+       fctl = ntoh24(fh->fh_f_ctl);
+       if (!(fctl & FC_FC_EX_CTX))
+               return 0;
+
+       xid = be16_to_cpu(fh->fh_rx_id);
+
+       if (xid >= CXGB_FCOE_MAX_XCHGS_PORT)
+               return 0;
+
+       ddp = (struct cxgb_fcoe_ddp *)&fcoe->ddp[xid];
+
+       /* Upper layer may not have requested for ddp_setup */
+       if (!ddp->sgl)
+               return 0;
+
+       eh = (struct ethhdr *)skb_mac_header(skb);
+       /* Save d_id, smac, dmac, vlan */
+       ether_addr_copy(ddp->h_source, eh->h_source);
+       ether_addr_copy(ddp->h_dest, eh->h_dest);
+       memcpy(ddp->d_id, fh->fh_d_id, 3);
+       ddp->vlan_tci = vlan_tci;
+
+       /* program ppods on the card. They should already have been
+        * allocated in cxgb_fcoe_ddp_setup
+        */
+
+       /* Calculate number credits required for ddp */
+       ndesc = calc_ddp_credits(skb, ddp->nppods);
+
+       credits = txq_avail(&q->q) - ndesc;
+
+       if (unlikely(credits < 0))
+               return -EBUSY;
+
+       /* Get an associated iqid */
+       qidx = skb_get_queue_mapping(skb);
+       rxq = &adap->sge.ethrxq[qidx + pi->first_qset];
+
+       cxgb_fcoe_set_tcbs(adap, pi, q, ddp, rxq->rspq.abs_id);
+
+       cxgb_setup_ppods(adap, pi, q, ddp);
+
+       dev->trans_start = jiffies;
+
+       reclaim_completed_tx(adap, &q->q, true);
+
        return 0;
 }
 #endif /* CONFIG_CHELSIO_T4_FCOE */
@@ -1123,9 +1432,16 @@ out_free:        dev_kfree_skb_any(skb);
        cntrl = TXPKT_L4CSUM_DIS | TXPKT_IPCSUM_DIS;
 
 #ifdef CONFIG_CHELSIO_T4_FCOE
-       err = cxgb_fcoe_offload(skb, adap, pi, &cntrl);
-       if (unlikely(err == -ENOTSUPP))
-               goto out_free;
+       err = cxgb_fcoe_offload(skb, dev, adap, pi, q, &cntrl);
+       if (unlikely(err == -EBUSY)) {
+               eth_txq_stop(q);
+               dev_err(adap->pdev_dev,
+                       "%s: (fcoe) Tx ring %u full while queue awake!\n",
+                       dev->name, qidx);
+               return NETDEV_TX_BUSY;
+       } else if (unlikely(err == -ENOTSUPP)) {
+                       goto out_free;
+       }
 #endif /* CONFIG_CHELSIO_T4_FCOE */
 
        flits = calc_tx_flits(skb);
@@ -1810,6 +2126,8 @@ int t4_ethrx_handler(struct sge_rspq *q, const __be64 
*rsp,
                            CPL_TRACE_PKT : CPL_TRACE_PKT_T5;
 #ifdef CONFIG_CHELSIO_T4_FCOE
        struct port_info *pi;
+       if (cxgb_fcoe_rx_handler(q, rsp))
+               return 0;
 #endif
 
        if (unlikely(*(u8 *)rsp == cpl_trace_pkt))
diff --git a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c 
b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
index 5959e3a..bcb0567 100644
--- a/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
+++ b/drivers/net/ethernet/chelsio/cxgb4/t4_hw.c
@@ -5303,6 +5303,7 @@ int t4_init_tp_params(struct adapter *adap)
         * shift positions of several elements of the Compressed Filter Tuple
         * for this adapter which we need frequently ...
         */
+       adap->params.tp.fcoe_shift = t4_filter_field_shift(adap, FCOE_F);
        adap->params.tp.vlan_shift = t4_filter_field_shift(adap, VLAN_F);
        adap->params.tp.vnic_shift = t4_filter_field_shift(adap, VNIC_ID_F);
        adap->params.tp.port_shift = t4_filter_field_shift(adap, PORT_F);
-- 
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-rdma" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to