cxgb4/iw_cxgb4: display TPTE on errors
authorHariprasad Shenai <hariprasad@chelsio.com>
Mon, 14 Jul 2014 16:04:53 +0000 (21:34 +0530)
committerDavid S. Miller <davem@davemloft.net>
Tue, 15 Jul 2014 23:25:16 +0000 (16:25 -0700)
With ingress WRITE or READ RESPONSE errors, HW provides the offending
stag from the packet.  This patch adds logic to log the parsed TPTE
in this case. cxgb4 now exports a function to read a TPTE entry
from adapter memory.

Signed-off-by: Steve Wise <swise@opengridcomputing.com>
Signed-off-by: Hariprasad Shenai <hariprasad@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/infiniband/hw/cxgb4/device.c
drivers/infiniband/hw/cxgb4/ev.c
drivers/infiniband/hw/cxgb4/t4.h
drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c
drivers/net/ethernet/chelsio/cxgb4/cxgb4_uld.h

index e76358efcaa1db90853c698517e4901b0b70145e..8386678f11590316e8983bc920afe4b4428421c0 100644 (file)
@@ -241,12 +241,32 @@ static int dump_stag(int id, void *p, void *data)
        struct c4iw_debugfs_data *stagd = data;
        int space;
        int cc;
+       struct fw_ri_tpte tpte;
+       int ret;
 
        space = stagd->bufsize - stagd->pos - 1;
        if (space == 0)
                return 1;
 
-       cc = snprintf(stagd->buf + stagd->pos, space, "0x%x\n", id<<8);
+       ret = cxgb4_read_tpte(stagd->devp->rdev.lldi.ports[0], (u32)id<<8,
+                             (__be32 *)&tpte);
+       if (ret) {
+               dev_err(&stagd->devp->rdev.lldi.pdev->dev,
+                       "%s cxgb4_read_tpte err %d\n", __func__, ret);
+               return ret;
+       }
+       cc = snprintf(stagd->buf + stagd->pos, space,
+                     "stag: idx 0x%x valid %d key 0x%x state %d pdid %d "
+                     "perm 0x%x ps %d len 0x%llx va 0x%llx\n",
+                     (u32)id<<8,
+                     G_FW_RI_TPTE_VALID(ntohl(tpte.valid_to_pdid)),
+                     G_FW_RI_TPTE_STAGKEY(ntohl(tpte.valid_to_pdid)),
+                     G_FW_RI_TPTE_STAGSTATE(ntohl(tpte.valid_to_pdid)),
+                     G_FW_RI_TPTE_PDID(ntohl(tpte.valid_to_pdid)),
+                     G_FW_RI_TPTE_PERM(ntohl(tpte.locread_to_qpid)),
+                     G_FW_RI_TPTE_PS(ntohl(tpte.locread_to_qpid)),
+                     ((u64)ntohl(tpte.len_hi) << 32) | ntohl(tpte.len_lo),
+                     ((u64)ntohl(tpte.va_hi) << 32) | ntohl(tpte.va_lo_fbo));
        if (cc < space)
                stagd->pos += cc;
        return 0;
@@ -259,7 +279,7 @@ static int stag_release(struct inode *inode, struct file *file)
                printk(KERN_INFO "%s null stagd?\n", __func__);
                return 0;
        }
-       kfree(stagd->buf);
+       vfree(stagd->buf);
        kfree(stagd);
        return 0;
 }
@@ -282,8 +302,8 @@ static int stag_open(struct inode *inode, struct file *file)
        idr_for_each(&stagd->devp->mmidr, count_idrs, &count);
        spin_unlock_irq(&stagd->devp->lock);
 
-       stagd->bufsize = count * sizeof("0x12345678\n");
-       stagd->buf = kmalloc(stagd->bufsize, GFP_KERNEL);
+       stagd->bufsize = count * 256;
+       stagd->buf = vmalloc(stagd->bufsize);
        if (!stagd->buf) {
                ret = -ENOMEM;
                goto err1;
index d61d0a18f784c9d1b032be277a87524f89449e41..fbe6051af254bac612de62dd36e1fa66d3150396 100644 (file)
 
 #include "iw_cxgb4.h"
 
+static void print_tpte(struct c4iw_dev *dev, u32 stag)
+{
+       int ret;
+       struct fw_ri_tpte tpte;
+
+       ret = cxgb4_read_tpte(dev->rdev.lldi.ports[0], stag,
+                             (__be32 *)&tpte);
+       if (ret) {
+               dev_err(&dev->rdev.lldi.pdev->dev,
+                       "%s cxgb4_read_tpte err %d\n", __func__, ret);
+               return;
+       }
+       PDBG("stag idx 0x%x valid %d key 0x%x state %d pdid %d "
+              "perm 0x%x ps %d len 0x%llx va 0x%llx\n",
+              stag & 0xffffff00,
+              G_FW_RI_TPTE_VALID(ntohl(tpte.valid_to_pdid)),
+              G_FW_RI_TPTE_STAGKEY(ntohl(tpte.valid_to_pdid)),
+              G_FW_RI_TPTE_STAGSTATE(ntohl(tpte.valid_to_pdid)),
+              G_FW_RI_TPTE_PDID(ntohl(tpte.valid_to_pdid)),
+              G_FW_RI_TPTE_PERM(ntohl(tpte.locread_to_qpid)),
+              G_FW_RI_TPTE_PS(ntohl(tpte.locread_to_qpid)),
+              ((u64)ntohl(tpte.len_hi) << 32) | ntohl(tpte.len_lo),
+              ((u64)ntohl(tpte.va_hi) << 32) | ntohl(tpte.va_lo_fbo));
+}
+
+static void dump_err_cqe(struct c4iw_dev *dev, struct t4_cqe *err_cqe)
+{
+       __be64 *p = (void *)err_cqe;
+
+       dev_err(&dev->rdev.lldi.pdev->dev,
+               "AE qpid %d opcode %d status 0x%x "
+               "type %d len 0x%x wrid.hi 0x%x wrid.lo 0x%x\n",
+               CQE_QPID(err_cqe), CQE_OPCODE(err_cqe),
+               CQE_STATUS(err_cqe), CQE_TYPE(err_cqe), ntohl(err_cqe->len),
+               CQE_WRID_HI(err_cqe), CQE_WRID_LOW(err_cqe));
+
+       PDBG("%016llx %016llx %016llx %016llx\n",
+            be64_to_cpu(p[0]), be64_to_cpu(p[1]), be64_to_cpu(p[2]),
+            be64_to_cpu(p[3]));
+
+       /*
+        * Ingress WRITE and READ_RESP errors provide
+        * the offending stag, so parse and log it.
+        */
+       if (RQ_TYPE(err_cqe) && (CQE_OPCODE(err_cqe) == FW_RI_RDMA_WRITE ||
+                                CQE_OPCODE(err_cqe) == FW_RI_READ_RESP))
+               print_tpte(dev, CQE_WRID_STAG(err_cqe));
+}
+
 static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
                          struct c4iw_qp *qhp,
                          struct t4_cqe *err_cqe,
@@ -44,11 +93,7 @@ static void post_qp_event(struct c4iw_dev *dev, struct c4iw_cq *chp,
        struct c4iw_qp_attributes attrs;
        unsigned long flag;
 
-       printk(KERN_ERR MOD "AE qpid 0x%x opcode %d status 0x%x "
-              "type %d wrid.hi 0x%x wrid.lo 0x%x\n",
-              CQE_QPID(err_cqe), CQE_OPCODE(err_cqe),
-              CQE_STATUS(err_cqe), CQE_TYPE(err_cqe),
-              CQE_WRID_HI(err_cqe), CQE_WRID_LOW(err_cqe));
+       dump_err_cqe(dev, err_cqe);
 
        if (qhp->attr.state == C4IW_QP_STATE_RTS) {
                attrs.next_state = C4IW_QP_STATE_TERMINATE;
index e64fa8b2be066cb2ee3345256546f63a9f34f3be..dd45186d1c55bea0374769174a63ecf65b7781e4 100644 (file)
@@ -236,8 +236,8 @@ struct t4_cqe {
 #define CQE_WRID_SQ_IDX(x)     ((x)->u.scqe.cidx)
 
 /* generic accessor macros */
-#define CQE_WRID_HI(x)         ((x)->u.gen.wrid_hi)
-#define CQE_WRID_LOW(x)                ((x)->u.gen.wrid_low)
+#define CQE_WRID_HI(x)         (be32_to_cpu((x)->u.gen.wrid_hi))
+#define CQE_WRID_LOW(x)                (be32_to_cpu((x)->u.gen.wrid_low))
 
 /* macros for flit 3 of the cqe */
 #define S_CQE_GENBIT   63
index 767cbbaa3d1ef2cf0eb9d3e752e763da478074e7..ba7d13de5e83fc31d4b7d3c68031c67646e6ebec 100644 (file)
@@ -3832,6 +3832,72 @@ void cxgb4_enable_db_coalescing(struct net_device *dev)
 }
 EXPORT_SYMBOL(cxgb4_enable_db_coalescing);
 
+int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte)
+{
+       struct adapter *adap;
+       u32 offset, memtype, memaddr;
+       u32 edc0_size, edc1_size, mc0_size, mc1_size;
+       u32 edc0_end, edc1_end, mc0_end, mc1_end;
+       int ret;
+
+       adap = netdev2adap(dev);
+
+       offset = ((stag >> 8) * 32) + adap->vres.stag.start;
+
+       /* Figure out where the offset lands in the Memory Type/Address scheme.
+        * This code assumes that the memory is laid out starting at offset 0
+        * with no breaks as: EDC0, EDC1, MC0, MC1. All cards have both EDC0
+        * and EDC1.  Some cards will have neither MC0 nor MC1, most cards have
+        * MC0, and some have both MC0 and MC1.
+        */
+       edc0_size = EDRAM_SIZE_GET(t4_read_reg(adap, MA_EDRAM0_BAR)) << 20;
+       edc1_size = EDRAM_SIZE_GET(t4_read_reg(adap, MA_EDRAM1_BAR)) << 20;
+       mc0_size = EXT_MEM_SIZE_GET(t4_read_reg(adap, MA_EXT_MEMORY_BAR)) << 20;
+
+       edc0_end = edc0_size;
+       edc1_end = edc0_end + edc1_size;
+       mc0_end = edc1_end + mc0_size;
+
+       if (offset < edc0_end) {
+               memtype = MEM_EDC0;
+               memaddr = offset;
+       } else if (offset < edc1_end) {
+               memtype = MEM_EDC1;
+               memaddr = offset - edc0_end;
+       } else {
+               if (offset < mc0_end) {
+                       memtype = MEM_MC0;
+                       memaddr = offset - edc1_end;
+               } else if (is_t4(adap->params.chip)) {
+                       /* T4 only has a single memory channel */
+                       goto err;
+               } else {
+                       mc1_size = EXT_MEM_SIZE_GET(
+                                       t4_read_reg(adap,
+                                                   MA_EXT_MEMORY1_BAR)) << 20;
+                       mc1_end = mc0_end + mc1_size;
+                       if (offset < mc1_end) {
+                               memtype = MEM_MC1;
+                               memaddr = offset - mc0_end;
+                       } else {
+                               /* offset beyond the end of any memory */
+                               goto err;
+                       }
+               }
+       }
+
+       spin_lock(&adap->win0_lock);
+       ret = t4_memory_rw(adap, 0, memtype, memaddr, 32, tpte, T4_MEMORY_READ);
+       spin_unlock(&adap->win0_lock);
+       return ret;
+
+err:
+       dev_err(adap->pdev_dev, "stag %#x, offset %#x out of range\n",
+               stag, offset);
+       return -EINVAL;
+}
+EXPORT_SYMBOL(cxgb4_read_tpte);
+
 static struct pci_driver cxgb4_driver;
 
 static void check_neigh_update(struct neighbour *neigh)
index df1d9446768ab4ab97f893da50fe42b404df0a36..c7170d68bf63263ab6184eccca92bc91cc70899c 100644 (file)
@@ -296,5 +296,6 @@ int cxgb4_sync_txq_pidx(struct net_device *dev, u16 qid, u16 pidx, u16 size);
 int cxgb4_flush_eq_cache(struct net_device *dev);
 void cxgb4_disable_db_coalescing(struct net_device *dev);
 void cxgb4_enable_db_coalescing(struct net_device *dev);
+int cxgb4_read_tpte(struct net_device *dev, u32 stag, __be32 *tpte);
 
 #endif  /* !__CXGB4_OFLD_H */