diff mbox

[v3,net-next,5/8] net/ncsi: Dump NCSI packet statistics

Message ID 1492498295-14385-6-git-send-email-gwshan@linux.vnet.ibm.com
State Changes Requested, archived
Delegated to: David Miller
Headers show

Commit Message

Gavin Shan April 18, 2017, 6:51 a.m. UTC
This creates /sys/kernel/debug/ncsi/<eth0>/stats to dump the NCSI
packets sent and received over all packages and channels. It's useful
to diagnose NCSI problems, especially when NCSI packages and channels
aren't probed properly. The statistics can be gained from debugfs file
as below:

 # cat /sys/kernel/debug/ncsi/eth0/stats

 CMD          OK       TIMEOUT  ERROR
 =======================================
 CIS          32       29       0
 SP           10       7        0
 DP           17       14       0
 EC           1        0        0
 ECNT         1        0        0
 AE           1        0        0
 GLS          11       0        0
 SMA          1        0        0
 EBF          1        0        0
 GVI          2        0        0
 GC           2        0        0

 RSP          OK       TIMEOUT  ERROR
 =======================================
 CIS          3        0        0
 SP           3        0        0
 DP           2        0        1
 EC           1        0        0
 ECNT         1        0        0
 AE           1        0        0
 GLS          11       0        0
 SMA          1        0        0
 EBF          1        0        0
 GVI          0        0        2
 GC           2        0        0

 AEN          OK       TIMEOUT  ERROR
 =======================================

Signed-off-by: Gavin Shan <gwshan@linux.vnet.ibm.com>
---
 net/ncsi/internal.h    |  17 ++++
 net/ncsi/ncsi-aen.c    |  13 ++-
 net/ncsi/ncsi-cmd.c    |  12 ++-
 net/ncsi/ncsi-debug.c  | 252 +++++++++++++++++++++++++++++++++++++++++++++++++
 net/ncsi/ncsi-manage.c |   4 +
 net/ncsi/ncsi-rsp.c    |  11 ++-
 6 files changed, 306 insertions(+), 3 deletions(-)

Comments

David Miller April 20, 2017, 5:21 p.m. UTC | #1
From: Gavin Shan <gwshan@linux.vnet.ibm.com>
Date: Tue, 18 Apr 2017 16:51:32 +1000

> This creates /sys/kernel/debug/ncsi/<eth0>/stats to dump the NCSI
> packets sent and received over all packages and channels. It's useful
> to diagnose NCSI problems, especially when NCSI packages and channels
> aren't probed properly. The statistics can be gained from debugfs file
> as below:
> 
>  # cat /sys/kernel/debug/ncsi/eth0/stats

There is no reason you cannot use ethtool statistics to provide this
information to the user.
Gavin Shan April 20, 2017, 11:38 p.m. UTC | #2
On Thu, Apr 20, 2017 at 01:21:03PM -0400, David Miller wrote:
>From: Gavin Shan <gwshan@linux.vnet.ibm.com>
>Date: Tue, 18 Apr 2017 16:51:32 +1000
>
>> This creates /sys/kernel/debug/ncsi/<eth0>/stats to dump the NCSI
>> packets sent and received over all packages and channels. It's useful
>> to diagnose NCSI problems, especially when NCSI packages and channels
>> aren't probed properly. The statistics can be gained from debugfs file
>> as below:
>> 
>>  # cat /sys/kernel/debug/ncsi/eth0/stats
>
>There is no reason you cannot use ethtool statistics to provide this
>information to the user.
>

It can be dumped by ethtool, but it's more reasonable to dump them
through debugfs for couple of reasons: (1) ethtool usually dumps
statistics collected by hardware, but this debugfs file dumps the
statistics of packets seen (collected) by software. They are different
things. Note that NCSI channel collects statistics in hardware and it's
not exposed or dumped by this patchset. They are candidates for ethtool.
(2) To expose this through ethtool relies on the availability of the tool.
It's nicer not to depend on it. (3) This interface can be used to check
the debug packet has been sent successfully through /sys/kernel/debug/ncsi/eth0/pkt,
dumping the statistics through debugfs make this (debugging) mechanism
consistent.

Thanks,
Gavin
Florian Fainelli April 21, 2017, 12:26 a.m. UTC | #3
On 04/20/2017 04:38 PM, Gavin Shan wrote:
> On Thu, Apr 20, 2017 at 01:21:03PM -0400, David Miller wrote:
>> From: Gavin Shan <gwshan@linux.vnet.ibm.com>
>> Date: Tue, 18 Apr 2017 16:51:32 +1000
>>
>>> This creates /sys/kernel/debug/ncsi/<eth0>/stats to dump the NCSI
>>> packets sent and received over all packages and channels. It's useful
>>> to diagnose NCSI problems, especially when NCSI packages and channels
>>> aren't probed properly. The statistics can be gained from debugfs file
>>> as below:
>>>
>>>  # cat /sys/kernel/debug/ncsi/eth0/stats
>>
>> There is no reason you cannot use ethtool statistics to provide this
>> information to the user.
>>
> 
> It can be dumped by ethtool, but it's more reasonable to dump them
> through debugfs for couple of reasons: (1) ethtool usually dumps
> statistics collected by hardware, but this debugfs file dumps the
> statistics of packets seen (collected) by software. They are different
> things. Note that NCSI channel collects statistics in hardware and it's
> not exposed or dumped by this patchset. They are candidates for ethtool.
> (2) To expose this through ethtool relies on the availability of the tool.
> It's nicer not to depend on it. (3) This interface can be used to check
> the debug packet has been sent successfully through /sys/kernel/debug/ncsi/eth0/pkt,
> dumping the statistics through debugfs make this (debugging) mechanism
> consistent.

Can't you create a ncsi folder under /sys/class/net/eth0/nsci/ and then
put your stats in there? That would at least look slightly consistent
with what is already existing for the non-NC-SI networking stack.
Gavin Shan April 21, 2017, 12:44 a.m. UTC | #4
On Thu, Apr 20, 2017 at 05:26:14PM -0700, Florian Fainelli wrote:
>On 04/20/2017 04:38 PM, Gavin Shan wrote:
>> On Thu, Apr 20, 2017 at 01:21:03PM -0400, David Miller wrote:
>>> From: Gavin Shan <gwshan@linux.vnet.ibm.com>
>>> Date: Tue, 18 Apr 2017 16:51:32 +1000
>>>
>>>> This creates /sys/kernel/debug/ncsi/<eth0>/stats to dump the NCSI
>>>> packets sent and received over all packages and channels. It's useful
>>>> to diagnose NCSI problems, especially when NCSI packages and channels
>>>> aren't probed properly. The statistics can be gained from debugfs file
>>>> as below:
>>>>
>>>>  # cat /sys/kernel/debug/ncsi/eth0/stats
>>>
>>> There is no reason you cannot use ethtool statistics to provide this
>>> information to the user.
>>>
>> 
>> It can be dumped by ethtool, but it's more reasonable to dump them
>> through debugfs for couple of reasons: (1) ethtool usually dumps
>> statistics collected by hardware, but this debugfs file dumps the
>> statistics of packets seen (collected) by software. They are different
>> things. Note that NCSI channel collects statistics in hardware and it's
>> not exposed or dumped by this patchset. They are candidates for ethtool.
>> (2) To expose this through ethtool relies on the availability of the tool.
>> It's nicer not to depend on it. (3) This interface can be used to check
>> the debug packet has been sent successfully through /sys/kernel/debug/ncsi/eth0/pkt,
>> dumping the statistics through debugfs make this (debugging) mechanism
>> consistent.
>
>Can't you create a ncsi folder under /sys/class/net/eth0/nsci/ and then
>put your stats in there? That would at least look slightly consistent
>with what is already existing for the non-NC-SI networking stack.

Do you think it's good place to have /sys/class/net/eth0/ncsi/pkt?
Note this file accepts commands to send NCSI comands and dumps the
corresponding response on read. Also, it's a debugging interface
and disabled (invisible) for the most time. I think all directories
and files in /sys/class/net/eth0/ should be visible and the structure
is stable all the time.

Actually, I used procfs initially and replaced by debugfs according
to Joe's suggestion. I think debugfs is good enough for this case.

Cheers,
Gavin
David Miller April 21, 2017, 12:58 a.m. UTC | #5
From: Gavin Shan <gwshan@linux.vnet.ibm.com>
Date: Fri, 21 Apr 2017 09:38:12 +1000

> (1) ethtool usually dumps statistics collected by hardware, but this
> debugfs file dumps the statistics of packets seen (collected) by
> software.

ethtool is not strictly for hardware statistics, it's often used for
software maintained values
Florian Fainelli April 21, 2017, 12:59 a.m. UTC | #6
On 04/20/2017 05:44 PM, Gavin Shan wrote:
> On Thu, Apr 20, 2017 at 05:26:14PM -0700, Florian Fainelli wrote:
>> On 04/20/2017 04:38 PM, Gavin Shan wrote:
>>> On Thu, Apr 20, 2017 at 01:21:03PM -0400, David Miller wrote:
>>>> From: Gavin Shan <gwshan@linux.vnet.ibm.com>
>>>> Date: Tue, 18 Apr 2017 16:51:32 +1000
>>>>
>>>>> This creates /sys/kernel/debug/ncsi/<eth0>/stats to dump the NCSI
>>>>> packets sent and received over all packages and channels. It's useful
>>>>> to diagnose NCSI problems, especially when NCSI packages and channels
>>>>> aren't probed properly. The statistics can be gained from debugfs file
>>>>> as below:
>>>>>
>>>>>  # cat /sys/kernel/debug/ncsi/eth0/stats
>>>>
>>>> There is no reason you cannot use ethtool statistics to provide this
>>>> information to the user.
>>>>
>>>
>>> It can be dumped by ethtool, but it's more reasonable to dump them
>>> through debugfs for couple of reasons: (1) ethtool usually dumps
>>> statistics collected by hardware, but this debugfs file dumps the
>>> statistics of packets seen (collected) by software. They are different
>>> things. Note that NCSI channel collects statistics in hardware and it's
>>> not exposed or dumped by this patchset. They are candidates for ethtool.
>>> (2) To expose this through ethtool relies on the availability of the tool.
>>> It's nicer not to depend on it. (3) This interface can be used to check
>>> the debug packet has been sent successfully through /sys/kernel/debug/ncsi/eth0/pkt,
>>> dumping the statistics through debugfs make this (debugging) mechanism
>>> consistent.
>>
>> Can't you create a ncsi folder under /sys/class/net/eth0/nsci/ and then
>> put your stats in there? That would at least look slightly consistent
>> with what is already existing for the non-NC-SI networking stack.
> 
> Do you think it's good place to have /sys/class/net/eth0/ncsi/pkt?
> Note this file accepts commands to send NCSI comands and dumps the
> corresponding response on read. Also, it's a debugging interface
> and disabled (invisible) for the most time. I think all directories
> and files in /sys/class/net/eth0/ should be visible and the structure
> is stable all the time.

Statistics should not be debugging features having them all the time is
invaluable, so in that regard they could probably be part of a sysfs
interface of some kind. If this "pkt" file is special, then yes, maybe
debugfs is appropriate here.

It sounds like you may also want to consider doing a NC-SI generic
netlink family at some point, because chances are that are you going to
export more and more information over time, just like there could be
additional commands/actions to be performed as well as asynchronous
events. Netlink is great for that, downside is that you have to write a
custom user-space tool (or extend iproute2).
Gavin Shan April 21, 2017, 1:35 a.m. UTC | #7
On Thu, Apr 20, 2017 at 05:59:27PM -0700, Florian Fainelli wrote:
>On 04/20/2017 05:44 PM, Gavin Shan wrote:
>> On Thu, Apr 20, 2017 at 05:26:14PM -0700, Florian Fainelli wrote:
>>> On 04/20/2017 04:38 PM, Gavin Shan wrote:
>>>> On Thu, Apr 20, 2017 at 01:21:03PM -0400, David Miller wrote:
>>>>> From: Gavin Shan <gwshan@linux.vnet.ibm.com>
>>>>> Date: Tue, 18 Apr 2017 16:51:32 +1000
>>>>>
>>>>>> This creates /sys/kernel/debug/ncsi/<eth0>/stats to dump the NCSI
>>>>>> packets sent and received over all packages and channels. It's useful
>>>>>> to diagnose NCSI problems, especially when NCSI packages and channels
>>>>>> aren't probed properly. The statistics can be gained from debugfs file
>>>>>> as below:
>>>>>>
>>>>>>  # cat /sys/kernel/debug/ncsi/eth0/stats
>>>>>
>>>>> There is no reason you cannot use ethtool statistics to provide this
>>>>> information to the user.
>>>>>
>>>>
>>>> It can be dumped by ethtool, but it's more reasonable to dump them
>>>> through debugfs for couple of reasons: (1) ethtool usually dumps
>>>> statistics collected by hardware, but this debugfs file dumps the
>>>> statistics of packets seen (collected) by software. They are different
>>>> things. Note that NCSI channel collects statistics in hardware and it's
>>>> not exposed or dumped by this patchset. They are candidates for ethtool.
>>>> (2) To expose this through ethtool relies on the availability of the tool.
>>>> It's nicer not to depend on it. (3) This interface can be used to check
>>>> the debug packet has been sent successfully through /sys/kernel/debug/ncsi/eth0/pkt,
>>>> dumping the statistics through debugfs make this (debugging) mechanism
>>>> consistent.
>>>
>>> Can't you create a ncsi folder under /sys/class/net/eth0/nsci/ and then
>>> put your stats in there? That would at least look slightly consistent
>>> with what is already existing for the non-NC-SI networking stack.
>> 
>> Do you think it's good place to have /sys/class/net/eth0/ncsi/pkt?
>> Note this file accepts commands to send NCSI comands and dumps the
>> corresponding response on read. Also, it's a debugging interface
>> and disabled (invisible) for the most time. I think all directories
>> and files in /sys/class/net/eth0/ should be visible and the structure
>> is stable all the time.
>
>Statistics should not be debugging features having them all the time is
>invaluable, so in that regard they could probably be part of a sysfs
>interface of some kind. If this "pkt" file is special, then yes, maybe
>debugfs is appropriate here.
>
>It sounds like you may also want to consider doing a NC-SI generic
>netlink family at some point, because chances are that are you going to
>export more and more information over time, just like there could be
>additional commands/actions to be performed as well as asynchronous
>events. Netlink is great for that, downside is that you have to write a
>custom user-space tool (or extend iproute2).

Yes, it's definitely good idea to cover everything using one interface.
It's for sure that the statistics collected by hardware should be dumped
by ethtool in future. I will move functionalities introduced by this
patchset to ethtool as well, as Dave suggested in another thread. Hope
Joe has no objection on this.

Cheers,
Gavin
Joe Perches April 21, 2017, 1:43 a.m. UTC | #8
On Fri, 2017-04-21 at 11:35 +1000, Gavin Shan wrote:
> Yes, it's definitely good idea to cover everything using one interface.
> It's for sure that the statistics collected by hardware should be dumped
> by ethtool in future. I will move functionalities introduced by this
> patchset to ethtool as well, as Dave suggested in another thread. Hope
> Joe has no objection on this.

Of course not.
Gavin Shan April 21, 2017, 2:33 a.m. UTC | #9
On Thu, Apr 20, 2017 at 05:58:58PM -0700, David Miller wrote:
>From: Gavin Shan <gwshan@linux.vnet.ibm.com>
>Date: Fri, 21 Apr 2017 09:38:12 +1000
>
>> (1) ethtool usually dumps statistics collected by hardware, but this
>> debugfs file dumps the statistics of packets seen (collected) by
>> software.
>
>ethtool is not strictly for hardware statistics, it's often used for
>software maintained values
>

Thanks for the confirm. I'll move the functionalities introduced by
this patchset to ethtool in next respin, as discussed in another
thread.

Cheers,
Gavin
diff mbox

Patch

diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h
index e9ede4f..c0b50a9 100644
--- a/net/ncsi/internal.h
+++ b/net/ncsi/internal.h
@@ -282,7 +282,17 @@  struct ncsi_dev_priv {
 	struct work_struct  work;            /* For channel management     */
 	struct packet_type  ptype;           /* NCSI packet Rx handler     */
 	struct list_head    node;            /* Form NCSI device list      */
+#define NCSI_PKT_STAT_OK	0
+#define NCSI_PKT_STAT_TIMEOUT	1
+#define NCSI_PKT_STAT_ERROR	2
+#define NCSI_PKT_STAT_MAX	3
 #ifdef CONFIG_NET_NCSI_DEBUG
+	struct {
+		struct dentry  *dentry;
+		unsigned long  cmd[128][NCSI_PKT_STAT_MAX];
+		unsigned long  rsp[128][NCSI_PKT_STAT_MAX];
+		unsigned long  aen[256][NCSI_PKT_STAT_MAX];
+	} stats;
 	struct dentry       *dentry;         /* Procfs directory           */
 #endif
 };
@@ -349,6 +359,8 @@  int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb);
 /* Debugging functionality */
 #ifdef CONFIG_NET_NCSI_DEBUG
 int ncsi_dev_init_debug(struct ncsi_dev_priv *ndp);
+void ncsi_dev_update_stats(struct ncsi_dev_priv *ndp,
+			   int type, int subtype, int errno);
 void ncsi_dev_release_debug(struct ncsi_dev_priv *ndp);
 int ncsi_package_init_debug(struct ncsi_package *np);
 void ncsi_package_release_debug(struct ncsi_package *np);
@@ -360,6 +372,11 @@  static inline int ncsi_dev_init_debug(struct ncsi_dev_priv *ndp)
 	return -ENOTTY;
 }
 
+static inline void ncsi_dev_update_stats(struct ncsi_dev_priv *ndp,
+					 int type, int subtype, int errno)
+{
+}
+
 static inline void ncsi_dev_release_debug(struct ncsi_dev_priv *ndp)
 {
 }
diff --git a/net/ncsi/ncsi-aen.c b/net/ncsi/ncsi-aen.c
index 6898e72..72bac7c 100644
--- a/net/ncsi/ncsi-aen.c
+++ b/net/ncsi/ncsi-aen.c
@@ -206,16 +206,27 @@  int ncsi_aen_handler(struct ncsi_dev_priv *ndp, struct sk_buff *skb)
 	}
 
 	if (!nah) {
+		ncsi_dev_update_stats(ndp, NCSI_PKT_AEN,
+				      h->type, NCSI_PKT_STAT_ERROR);
 		netdev_warn(ndp->ndev.dev, "Invalid AEN (0x%x) received\n",
 			    h->type);
 		return -ENOENT;
 	}
 
 	ret = ncsi_validate_aen_pkt(h, nah->payload);
-	if (ret)
+	if (ret) {
+		ncsi_dev_update_stats(ndp, NCSI_PKT_AEN,
+				      h->type, NCSI_PKT_STAT_ERROR);
 		goto out;
+	}
 
 	ret = nah->handler(ndp, h);
+	if (ret)
+		ncsi_dev_update_stats(ndp, NCSI_PKT_AEN,
+				      h->type, NCSI_PKT_STAT_ERROR);
+	else
+		ncsi_dev_update_stats(ndp, NCSI_PKT_AEN,
+				      h->type, NCSI_PKT_STAT_OK);
 out:
 	consume_skb(skb);
 	return ret;
diff --git a/net/ncsi/ncsi-cmd.c b/net/ncsi/ncsi-cmd.c
index db7083b..9a8dac2 100644
--- a/net/ncsi/ncsi-cmd.c
+++ b/net/ncsi/ncsi-cmd.c
@@ -323,6 +323,8 @@  int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
 	}
 
 	if (!nch) {
+		ncsi_dev_update_stats(nca->ndp, nca->type,
+				      0, NCSI_PKT_STAT_ERROR);
 		netdev_err(nca->ndp->ndev.dev,
 			   "Cannot send packet with type 0x%02x\n", nca->type);
 		return -ENOENT;
@@ -331,13 +333,18 @@  int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
 	/* Get packet payload length and allocate the request */
 	nca->payload = nch->payload;
 	nr = ncsi_alloc_command(nca);
-	if (!nr)
+	if (!nr) {
+		ncsi_dev_update_stats(nca->ndp, nca->type,
+				      0, NCSI_PKT_STAT_ERROR);
 		return -ENOMEM;
+	}
 
 	/* Prepare the packet */
 	nca->id = nr->id;
 	ret = nch->handler(nr->cmd, nca);
 	if (ret) {
+		ncsi_dev_update_stats(nca->ndp, nca->type,
+				      0, NCSI_PKT_STAT_ERROR);
 		ncsi_free_request(nr);
 		return ret;
 	}
@@ -359,9 +366,12 @@  int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
 	skb_get(nr->cmd);
 	ret = dev_queue_xmit(nr->cmd);
 	if (ret < 0) {
+		ncsi_dev_update_stats(nca->ndp, nca->type,
+				      0, NCSI_PKT_STAT_ERROR);
 		ncsi_free_request(nr);
 		return ret;
 	}
 
+	ncsi_dev_update_stats(nca->ndp, nca->type, 0, NCSI_PKT_STAT_OK);
 	return 0;
 }
diff --git a/net/ncsi/ncsi-debug.c b/net/ncsi/ncsi-debug.c
index f38483d..b6df895 100644
--- a/net/ncsi/ncsi-debug.c
+++ b/net/ncsi/ncsi-debug.c
@@ -23,6 +23,235 @@ 
 #include "ncsi-pkt.h"
 
 static struct dentry *ncsi_dentry;
+static const struct ncsi_pkt_handler {
+	unsigned char	type;
+	const char	*name;
+} ncsi_pkt_handlers[] = {
+	{ NCSI_PKT_CMD_CIS,    "CIS"    },
+	{ NCSI_PKT_CMD_SP,     "SP"     },
+	{ NCSI_PKT_CMD_DP,     "DP"     },
+	{ NCSI_PKT_CMD_EC,     "EC"     },
+	{ NCSI_PKT_CMD_DC,     "DC"     },
+	{ NCSI_PKT_CMD_RC,     "RC"     },
+	{ NCSI_PKT_CMD_ECNT,   "ECNT"   },
+	{ NCSI_PKT_CMD_DCNT,   "DCNT"   },
+	{ NCSI_PKT_CMD_AE,     "AE"     },
+	{ NCSI_PKT_CMD_SL,     "SL"     },
+	{ NCSI_PKT_CMD_GLS,    "GLS"    },
+	{ NCSI_PKT_CMD_SVF,    "SVF"    },
+	{ NCSI_PKT_CMD_EV,     "EV"     },
+	{ NCSI_PKT_CMD_DV,     "DV"     },
+	{ NCSI_PKT_CMD_SMA,    "SMA"    },
+	{ NCSI_PKT_CMD_EBF,    "EBF"    },
+	{ NCSI_PKT_CMD_DBF,    "DBF"    },
+	{ NCSI_PKT_CMD_EGMF,   "EGMF"   },
+	{ NCSI_PKT_CMD_DGMF,   "DGMF"   },
+	{ NCSI_PKT_CMD_SNFC,   "SNFC"   },
+	{ NCSI_PKT_CMD_GVI,    "GVI"    },
+	{ NCSI_PKT_CMD_GC,     "GC"     },
+	{ NCSI_PKT_CMD_GP,     "GP"     },
+	{ NCSI_PKT_CMD_GCPS,   "GCPS"   },
+	{ NCSI_PKT_CMD_GNS,    "GNS"    },
+	{ NCSI_PKT_CMD_GNPTS,  "GNPTS"  },
+	{ NCSI_PKT_CMD_GPS,    "GPS"    },
+	{ NCSI_PKT_CMD_OEM,    "OEM"    },
+	{ NCSI_PKT_CMD_PLDM,   "PLDM"   },
+	{ NCSI_PKT_CMD_GPUUID, "GPUUID" },
+};
+
+static bool ncsi_dev_stats_index(struct ncsi_dev_priv *ndp, loff_t pos,
+				 unsigned long *type, unsigned long *index,
+				 unsigned long *entries)
+{
+	const unsigned long ranges[3][2] = {
+		{ 1,
+		  ARRAY_SIZE(ndp->stats.cmd) - 1		},
+		{ ranges[0][1] + 2,
+		  ranges[1][0] + ARRAY_SIZE(ndp->stats.rsp) - 1	},
+		{ ranges[1][1] + 2,
+		  ranges[2][0] + ARRAY_SIZE(ndp->stats.aen) - 1 }
+	};
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		if (pos == (ranges[i][0] - 1)) {
+			*index = i;
+			*entries = 0;
+			return true;
+		}
+
+		if (pos >= ranges[i][0] && pos <= ranges[i][1]) {
+			*type = i;
+			*index = (pos - ranges[i][0]);
+			*entries = NCSI_PKT_STAT_MAX;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+static void *ncsi_dev_stats_data(struct ncsi_dev_priv *ndp, loff_t pos)
+{
+	unsigned long type, index, entries;
+	bool valid;
+
+	valid = ncsi_dev_stats_index(ndp, pos, &type, &index, &entries);
+	if (!valid)
+		return NULL;
+
+	/* The bits in return value are assigned as below:
+	 *
+	 * Bit[7:0]:   Number of ulong entries
+	 * Bit[23:8]:  Offset to that specific data entry
+	 * Bit[30:24]: Type of packet statistics
+	 * Bit[31]:    0x1 as valid flag.
+	 */
+	if (!entries)
+		index += ((unsigned long)SEQ_START_TOKEN);
+	else
+		index = (1 << 31) | (type << 24) | (index << 8) | entries;
+
+	return (void *)index;
+}
+
+static void *ncsi_dev_stats_seq_start(struct seq_file *seq, loff_t *pos)
+{
+	struct ncsi_dev_priv *ndp = seq->private;
+	void *data;
+
+	data = ncsi_dev_stats_data(ndp, *pos);
+	++(*pos);
+
+	return data;
+}
+
+static void *ncsi_dev_stats_seq_next(struct seq_file *seq, void *v, loff_t *pos)
+{
+	struct ncsi_dev_priv *ndp = seq->private;
+	void *data;
+
+	data = ncsi_dev_stats_data(ndp, *pos);
+	++(*pos);
+	return data;
+}
+
+static void ncsi_dev_stats_seq_stop(struct seq_file *seq, void *v)
+{
+}
+
+static const char *ncsi_pkt_type_name(unsigned int type)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(ncsi_pkt_handlers); i++) {
+		if (ncsi_pkt_handlers[i].type == type)
+			return ncsi_pkt_handlers[i].name;
+	}
+
+	return "N/A";
+}
+
+static const char *ncsi_dev_stats_pkt_name(unsigned long type,
+					   unsigned long index)
+{
+	switch (type) {
+	case 0: /* Command */
+	case 1: /* Response */
+		return ncsi_pkt_type_name(index);
+	case 2: /* AEN */
+		switch (index) {
+		case NCSI_PKT_AEN_LSC:
+			return "LSC";
+		case NCSI_PKT_AEN_CR:
+			return "CR";
+		case NCSI_PKT_AEN_HNCDSC:
+			return "HNCDSC";
+		default:
+			return "N/A";
+		}
+	}
+
+	return "N/A";
+}
+
+static int ncsi_dev_stats_seq_show(struct seq_file *seq, void *v)
+{
+	struct ncsi_dev_priv *ndp = seq->private;
+	unsigned long type, index, entries, *data;
+	const char *name;
+
+	if (v >= SEQ_START_TOKEN && v <= (SEQ_START_TOKEN + 2)) {
+		static const char * const header[] = { "CMD", "RSP", "AEN" };
+
+		seq_puts(seq, "\n");
+		seq_printf(seq, "%-12s %-8s %-8s %-8s\n",
+			   header[v - SEQ_START_TOKEN],
+			   "OK", "TIMEOUT", "ERROR");
+		seq_puts(seq, "=======================================\n");
+		return 0;
+	}
+
+	index = (unsigned long)v;
+	type = (index >> 24) & 0x7F;
+	entries = (index & 0xFF);
+	index = ((index >> 8) & 0xFFFF);
+	name = ncsi_dev_stats_pkt_name(type, index);
+	if (WARN_ON_ONCE(entries != NCSI_PKT_STAT_MAX))
+		return 0;
+
+	switch (type) {
+	case 0: /* Command */
+		data = &ndp->stats.cmd[0][0];
+		break;
+	case 1: /* Response */
+		data = &ndp->stats.rsp[0][0];
+		break;
+	case 2: /* AEN */
+		data = &ndp->stats.aen[0][0];
+		break;
+	default:
+		pr_warn("%s: Unsupported type %ld\n", __func__, type);
+		return 0;
+	}
+
+	data += (index * NCSI_PKT_STAT_MAX);
+	if (*data || *(data + 1) || *(data + 2)) {
+		seq_printf(seq, "%-12s %-8ld %-8ld %-8ld\n",
+			   name, *data, *(data + 1), *(data + 2));
+	}
+
+	return 0;
+}
+
+static const struct seq_operations ncsi_dev_stats_seq_ops = {
+	.start = ncsi_dev_stats_seq_start,
+	.next  = ncsi_dev_stats_seq_next,
+	.stop  = ncsi_dev_stats_seq_stop,
+	.show  = ncsi_dev_stats_seq_show,
+};
+
+static int ncsi_dev_stats_seq_open(struct inode *inode, struct file *file)
+{
+	struct seq_file *sf;
+	int ret;
+
+	ret = seq_open(file, &ncsi_dev_stats_seq_ops);
+	if (!ret) {
+		sf = file->private_data;
+		sf->private = inode->i_private;
+	}
+
+	return ret;
+}
+
+static const struct file_operations ncsi_dev_stats_fops = {
+	.owner   = THIS_MODULE,
+	.open    = ncsi_dev_stats_seq_open,
+	.read    = seq_read,
+	.llseek  = seq_lseek,
+	.release = seq_release,
+};
 
 int ncsi_dev_init_debug(struct ncsi_dev_priv *ndp)
 {
@@ -45,11 +274,34 @@  int ncsi_dev_init_debug(struct ncsi_dev_priv *ndp)
 		return -ENOMEM;
 	}
 
+	ndp->stats.dentry = debugfs_create_file("stats", 0400, ndp->dentry,
+						ndp, &ncsi_dev_stats_fops);
+	if (!ndp->stats.dentry) {
+		pr_debug("Failed to create debugfs file 'ncsi/%s/stats'\n",
+			 netdev_name(ndp->ndev.dev));
+		return -ENOMEM;
+	}
+
 	return 0;
 }
 
+void ncsi_dev_update_stats(struct ncsi_dev_priv *ndp,
+			   int type, int subtype, int errno)
+{
+	if (errno >= NCSI_PKT_STAT_MAX)
+		return;
+
+	if (type == NCSI_PKT_AEN)
+		ndp->stats.aen[subtype][errno]++;
+	else if (type >= 0x80)
+		ndp->stats.rsp[type - 0x80][errno]++;
+	else
+		ndp->stats.cmd[type][errno]++;
+}
+
 void ncsi_dev_release_debug(struct ncsi_dev_priv *ndp)
 {
+	debugfs_remove(ndp->stats.dentry);
 	debugfs_remove(ndp->dentry);
 }
 
diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c
index 84f1405..be7d907 100644
--- a/net/ncsi/ncsi-manage.c
+++ b/net/ncsi/ncsi-manage.c
@@ -530,6 +530,7 @@  static void ncsi_request_timeout(unsigned long data)
 	struct ncsi_request *nr = (struct ncsi_request *)data;
 	struct ncsi_dev_priv *ndp = nr->ndp;
 	unsigned long flags;
+	struct ncsi_pkt_hdr *hdr;
 
 	/* If the request already had associated response,
 	 * let the response handler to release it.
@@ -542,6 +543,9 @@  static void ncsi_request_timeout(unsigned long data)
 	}
 	spin_unlock_irqrestore(&ndp->lock, flags);
 
+	hdr = (struct ncsi_pkt_hdr *)skb_network_header(nr->cmd);
+	ncsi_dev_update_stats(ndp, hdr->type, 0, NCSI_PKT_STAT_TIMEOUT);
+
 	/* Release the request */
 	ncsi_free_request(nr);
 }
diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c
index 087db77..a164eb1 100644
--- a/net/ncsi/ncsi-rsp.c
+++ b/net/ncsi/ncsi-rsp.c
@@ -998,6 +998,7 @@  int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
 	}
 
 	if (!nrh) {
+		ncsi_dev_update_stats(ndp, hdr->type, 0, NCSI_PKT_STAT_ERROR);
 		netdev_err(nd->dev, "Received unrecognized packet (0x%x)\n",
 			   hdr->type);
 		return -ENOENT;
@@ -1007,12 +1008,14 @@  int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
 	spin_lock_irqsave(&ndp->lock, flags);
 	nr = &ndp->requests[hdr->id];
 	if (!nr->used) {
+		ncsi_dev_update_stats(ndp, hdr->type, 0, NCSI_PKT_STAT_TIMEOUT);
 		spin_unlock_irqrestore(&ndp->lock, flags);
 		return -ENODEV;
 	}
 
 	nr->rsp = skb;
 	if (!nr->enabled) {
+		ncsi_dev_update_stats(ndp, hdr->type, 0, NCSI_PKT_STAT_TIMEOUT);
 		spin_unlock_irqrestore(&ndp->lock, flags);
 		ret = -ENOENT;
 		goto out;
@@ -1024,11 +1027,17 @@  int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
 	if (payload < 0)
 		payload = ntohs(hdr->length);
 	ret = ncsi_validate_rsp_pkt(nr, payload);
-	if (ret)
+	if (ret) {
+		ncsi_dev_update_stats(ndp, hdr->type, 0, NCSI_PKT_STAT_ERROR);
 		goto out;
+	}
 
 	/* Process the packet */
 	ret = nrh->handler(nr);
+	if (ret)
+		ncsi_dev_update_stats(ndp, hdr->type, 0, NCSI_PKT_STAT_ERROR);
+	else
+		ncsi_dev_update_stats(ndp, hdr->type, 0, NCSI_PKT_STAT_OK);
 out:
 	ncsi_free_request(nr);
 	return ret;