@@ -227,6 +227,60 @@ struct be_tx_stats {
struct u64_stats_sync sync_compl;
};
+/* Macros to read/write the 'features' word of be_wrb_params structure.
+ */
+#define BE_WRB_F_BIT(name) BE_WRB_F_##name##_BIT
+#define BE_WRB_F_MASK(name) BIT_MASK(BE_WRB_F_##name##_BIT)
+
+#define BE_WRB_F_GET(word, name) \
+ (((word) & (BE_WRB_F_MASK(name))) >> BE_WRB_F_BIT(name))
+
+#define BE_WRB_F_SET(word, name, val) \
+ ((word) |= (((val) << BE_WRB_F_BIT(name)) & BE_WRB_F_MASK(name)))
+
+/* Feature/offload bits */
+enum {
+ BE_WRB_F_CRC_BIT, /* Ethernet CRC */
+ BE_WRB_F_IPCS_BIT, /* IP csum */
+ BE_WRB_F_TCPCS_BIT, /* TCP csum */
+ BE_WRB_F_UDPCS_BIT, /* UDP csum */
+ BE_WRB_F_LSO_BIT, /* LSO */
+ BE_WRB_F_LSO6_BIT, /* LSO6 */
+ BE_WRB_F_VLAN_BIT, /* VLAN */
+ BE_WRB_F_VLAN_SKIP_HW_BIT, /* Skip VLAN tag (workaround) */
+ BE_WRB_F_OS2BMC_BIT /* Send packet to the management ring */
+};
+
+/* The structure below provides a HW-agnostic abstraction of WRB params
+ * retrieved from a TX skb. This is in turn passed to chip specific routines
+ * during transmit, to set the corresponding params in the WRB.
+ */
+struct be_wrb_params {
+ u32 features; /* Feature bits */
+ u16 vlan_tag; /* VLAN tag */
+ u16 lso_mss; /* MSS for LSO */
+};
+
+/* Store latest 200 occurrences */
+#define BE_TXQ_INFO_LEN 200
+#define PKT_DUMP_SIZE 128
+
+struct be_tx_pktinfo {
+ u16 head;
+ u16 tail;
+ u16 used;
+ struct be_wrb_params wqe_hdr;
+ u8 skb_data[PKT_DUMP_SIZE];
+ u32 len;
+ u32 skb_len;
+ bool valid;
+};
+
+struct be_tx_dump_cmpl {
+ u32 info[32];
+ bool valid;
+};
+
/* Structure to hold some data of interest obtained from a TX CQE */
struct be_tx_compl_info {
u8 status; /* Completion status */
@@ -244,6 +298,10 @@ struct be_tx_obj {
u16 pend_wrb_cnt; /* Number of WRBs yet to be given to HW */
u16 last_req_wrb_cnt; /* wrb cnt of the last req in the Q */
u16 last_req_hdr; /* index of the last req's hdr-wrb */
+ struct be_tx_pktinfo tx_pktinfo[BE_TXQ_INFO_LEN];
+ struct be_tx_dump_cmpl cmpl_info[BE_TXQ_INFO_LEN];
+ u32 tx_wqe_offset;
+ u32 tx_cmpl_idx;
} ____cacheline_aligned_in_smp;
/* Struct to remember the pages posted for rx frags */
@@ -444,40 +502,6 @@ struct be_hwmon {
u8 be_on_die_temp; /* Unit: millidegree Celsius */
};
-/* Macros to read/write the 'features' word of be_wrb_params structure.
- */
-#define BE_WRB_F_BIT(name) BE_WRB_F_##name##_BIT
-#define BE_WRB_F_MASK(name) BIT_MASK(BE_WRB_F_##name##_BIT)
-
-#define BE_WRB_F_GET(word, name) \
- (((word) & (BE_WRB_F_MASK(name))) >> BE_WRB_F_BIT(name))
-
-#define BE_WRB_F_SET(word, name, val) \
- ((word) |= (((val) << BE_WRB_F_BIT(name)) & BE_WRB_F_MASK(name)))
-
-/* Feature/offload bits */
-enum {
- BE_WRB_F_CRC_BIT, /* Ethernet CRC */
- BE_WRB_F_IPCS_BIT, /* IP csum */
- BE_WRB_F_TCPCS_BIT, /* TCP csum */
- BE_WRB_F_UDPCS_BIT, /* UDP csum */
- BE_WRB_F_LSO_BIT, /* LSO */
- BE_WRB_F_LSO6_BIT, /* LSO6 */
- BE_WRB_F_VLAN_BIT, /* VLAN */
- BE_WRB_F_VLAN_SKIP_HW_BIT, /* Skip VLAN tag (workaround) */
- BE_WRB_F_OS2BMC_BIT /* Send packet to the management ring */
-};
-
-/* The structure below provides a HW-agnostic abstraction of WRB params
- * retrieved from a TX skb. This is in turn passed to chip specific routines
- * during transmit, to set the corresponding params in the WRB.
- */
-struct be_wrb_params {
- u32 features; /* Feature bits */
- u16 vlan_tag; /* VLAN tag */
- u16 lso_mss; /* MSS for LSO */
-};
-
struct be_eth_addr {
unsigned char mac[ETH_ALEN];
};
@@ -1121,6 +1121,43 @@ static int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb)
return BE3_chip(adapter) && be_ipv6_exthdr_check(skb);
}
+void be_record_tx_cmpl(struct be_tx_obj *txo,
+ struct be_eth_tx_compl *cmpl)
+{
+ u32 offset = txo->tx_cmpl_idx;
+ struct be_tx_dump_cmpl *cmpl_dump = &txo->cmpl_info[offset];
+
+ memset(cmpl_dump, 0, sizeof(*cmpl_dump));
+
+ memcpy(&cmpl_dump->info, cmpl, sizeof(cmpl_dump->info));
+ cmpl_dump->valid = 1;
+
+ txo->tx_cmpl_idx = ((txo->tx_cmpl_idx + 1) % BE_TXQ_INFO_LEN);
+}
+
+void be_record_tx_wqes(struct be_tx_obj *txo,
+ struct be_wrb_params *wrb_params,
+ struct sk_buff *skb)
+{
+ u32 offset = txo->tx_wqe_offset;
+ struct be_tx_pktinfo *pkt_info = &txo->tx_pktinfo[offset];
+
+ memset(pkt_info, 0, sizeof(*pkt_info));
+
+ pkt_info->tail = txo->q.tail;
+ pkt_info->head = txo->q.head;
+ pkt_info->used = atomic_read(&txo->q.used);
+ pkt_info->valid = 1;
+ pkt_info->skb_len = skb->len;
+ pkt_info->len = min_t(u32, PKT_DUMP_SIZE, skb->len);
+
+ memcpy(&pkt_info->wqe_hdr, wrb_params, sizeof(*wrb_params));
+
+ memcpy(&pkt_info->skb_data, skb->data, pkt_info->len);
+
+ txo->tx_wqe_offset = ((txo->tx_wqe_offset + 1) % BE_TXQ_INFO_LEN);
+}
+
static struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter,
struct sk_buff *skb,
struct be_wrb_params
@@ -1399,6 +1436,10 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev)
skb_get(skb);
}
+ /* Dump TX WQEs and SKB data */
+ if (adapter->msg_enable)
+ be_record_tx_wqes(txo, &wrb_params, skb);
+
if (be_is_txq_full(txo)) {
netif_stop_subqueue(netdev, q_idx);
tx_stats(txo)->tx_stops++;
@@ -1417,6 +1458,75 @@ static netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev)
return NETDEV_TX_OK;
}
+void
+be_print_tx_wqes(struct be_adapter *adapter, struct be_tx_obj *txo)
+{
+ struct device *dev = &adapter->pdev->dev;
+ struct be_tx_pktinfo *pkt_info;
+ u8 *data;
+ int i, j;
+
+ dev_info(dev, "Dumping WQES of TXQ id %d\n", txo->q.id);
+
+ for (i = 0; i < BE_TXQ_INFO_LEN; i++) {
+ pkt_info = &txo->tx_pktinfo[i];
+ if (!pkt_info->valid)
+ continue;
+
+ dev_info(dev, "TXQ head %d tail %d used %d\n",
+ pkt_info->head, pkt_info->tail, pkt_info->used);
+
+ dev_info(dev, "WRB params: feature:0x%x vlan_tag:0x%x lso_mss:0x%x\n",
+ pkt_info->wqe_hdr.features, pkt_info->wqe_hdr.vlan_tag,
+ pkt_info->wqe_hdr.lso_mss);
+
+ dev_info(dev, "SKB len: %d\n", pkt_info->skb_len);
+ data = pkt_info->skb_data;
+ for (j = 0 ; j < pkt_info->len; j++) {
+ printk("%02x ", data[j]);
+ if (j % 8 == 7)
+ printk(KERN_INFO "\n");
+ }
+ }
+}
+
+void
+be_print_tx_cmpls(struct be_adapter *adapter, struct be_tx_obj *txo)
+{
+ struct device *dev = &adapter->pdev->dev;
+ struct be_tx_dump_cmpl *cmpl_info;
+ int i;
+
+ dev_info(dev, "TX CQ id %d head %d tail %d used %d\n",
+ txo->cq.id, txo->cq.head, txo->cq.tail,
+ atomic_read(&txo->cq.used));
+
+ for (i = 0; i < BE_TXQ_INFO_LEN; i++) {
+ cmpl_info = &txo->cmpl_info[i];
+ if (!cmpl_info->valid)
+ continue;
+
+ printk(KERN_INFO "0x%x 0x%x 0x%x 0x%x\n",
+ cmpl_info->info[0], cmpl_info->info[1],
+ cmpl_info->info[2], cmpl_info->info[3]);
+ }
+}
+
+/* be_dump_info - Print tx-wqes, tx-cmpls and skb-data */
+void be_dump_info(struct be_adapter *adapter)
+{
+ struct be_tx_obj *txo;
+ int i;
+
+ if (!adapter->msg_enable)
+ return;
+
+ for_all_tx_queues(adapter, txo, i) {
+ be_print_tx_wqes(adapter, txo);
+ be_print_tx_cmpls(adapter, txo);
+ }
+}
+
static void be_tx_timeout(struct net_device *netdev)
{
struct be_adapter *adapter = netdev_priv(netdev);
@@ -1429,6 +1539,8 @@ static void be_tx_timeout(struct net_device *netdev)
int status;
int i, j;
+ be_dump_info(adapter);
+
for_all_tx_queues(adapter, txo, i) {
dev_info(dev, "TXQ Dump: %d H: %d T: %d used: %d, qid: 0x%x\n",
i, txo->q.head, txo->q.tail,
@@ -2719,6 +2831,10 @@ static struct be_tx_compl_info *be_tx_compl_get(struct be_adapter *adapter,
rmb();
be_dws_le_to_cpu(compl, sizeof(*compl));
+ /* Dump completion info */
+ if (adapter->msg_enable)
+ be_record_tx_cmpl(txo, compl);
+
txcp->status = GET_TX_COMPL_BITS(status, compl);
txcp->end_index = GET_TX_COMPL_BITS(wrb_index, compl);