diff --git a/platform/linux-generic/pktio/ethtool.c b/platform/linux-generic/pktio/ethtool.c
new file mode 100644
index 0000000..573cfe6
--- /dev/null
+++ b/platform/linux-generic/pktio/ethtool.c
@@ -0,0 +1,163 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <sys/ioctl.h>
+#include <netinet/in.h>
+#include <linux/sockios.h>
+#include <linux/if.h>
+#include <linux/ethtool.h>
+#include <errno.h>
+#include <net/if.h>
+
+#include <odp.h>
+#include <odp_packet_socket.h>
+#include <odp_debug_internal.h>
+
+static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
+{
+	struct {
+		struct ethtool_sset_info hdr;
+		uint32_t buf[1];
+	} sset_info;
+	struct ethtool_drvinfo drvinfo;
+	uint32_t len;
+	struct ethtool_gstrings *strings;
+	ptrdiff_t drvinfo_offset = offsetof(struct ethtool_drvinfo, n_stats);
+
+	sset_info.hdr.cmd = ETHTOOL_GSSET_INFO;
+	sset_info.hdr.reserved = 0;
+	sset_info.hdr.sset_mask = 1ULL << ETH_SS_STATS;
+	ifr->ifr_data =  &sset_info;
+	if (ioctl(fd, SIOCETHTOOL, ifr) == 0) {
+		len = sset_info.hdr.sset_mask ? sset_info.hdr.data[0] : 0;
+	} else if (errno == EOPNOTSUPP && drvinfo_offset != 0) {
+		/* Fallback for old kernel versions */
+		drvinfo.cmd = ETHTOOL_GDRVINFO;
+		ifr->ifr_data = &drvinfo;
+		if (ioctl(fd, SIOCETHTOOL, ifr)) {
+			__odp_errno = errno;
+			ODP_ERR("Cannot get stats information\n");
+			return NULL;
+		}
+		len = *(uint32_t *)((char *)&drvinfo + drvinfo_offset);
+	} else {
+		__odp_errno = errno;
+		return NULL;
+	}
+
+	if (!len) {
+		ODP_ERR("len is zero");
+		return NULL;
+	}
+
+	strings = calloc(1, sizeof(*strings) + len * ETH_GSTRING_LEN);
+	if (!strings) {
+		ODP_ERR("alloc failed\n");
+		return NULL;
+	}
+
+	strings->cmd = ETHTOOL_GSTRINGS;
+	strings->string_set = ETH_SS_STATS;
+	strings->len = len;
+	ifr->ifr_data = strings;
+	if (ioctl(fd, SIOCETHTOOL, ifr)) {
+		__odp_errno = errno;
+		ODP_ERR("Cannot get stats information\n");
+		free(strings);
+		return NULL;
+	}
+
+	return strings;
+}
+
+static int ethtool_stats(int fd, struct ifreq *ifr, odp_pktio_stats_t *stats)
+{
+	struct ethtool_gstrings *strings;
+	struct ethtool_stats *estats;
+	unsigned int n_stats, i;
+	int err;
+	int cnts;
+
+	strings = get_stringset(fd, ifr);
+	if (!strings)
+		return -1;
+
+	n_stats = strings->len;
+	if (n_stats < 1) {
+		ODP_ERR("no stats available\n");
+		free(strings);
+		return -1;
+	}
+
+	estats = calloc(1, n_stats * sizeof(uint64_t) +
+			sizeof(struct ethtool_stats));
+	if (!estats) {
+		free(strings);
+		return -1;
+	}
+
+	estats->cmd = ETHTOOL_GSTATS;
+	estats->n_stats = n_stats;
+	ifr->ifr_data = estats;
+	err = ioctl(fd, SIOCETHTOOL, ifr);
+	if (err < 0) {
+		__odp_errno = errno;
+		free(strings);
+		free(estats);
+		return -1;
+	}
+
+	cnts = 0;
+	for (i = 0; i < n_stats; i++) {
+		char *cnt = (char *)&strings->data[i * ETH_GSTRING_LEN];
+		uint64_t val = estats->data[i];
+
+		if (!strcmp(cnt, "rx_octets")) {
+			stats->in_octets = val;
+			cnts++;
+		} else if (!strcmp(cnt, "rx_ucast_packets")) {
+			stats->in_ucast_pkts = val;
+			cnts++;
+		} else if (!strcmp(cnt, "rx_discards")) {
+			stats->in_discards = val;
+			cnts++;
+		} else if (!strcmp(cnt, "rx_errors")) {
+			stats->in_errors = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_octets")) {
+			stats->out_octets = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_ucast_packets")) {
+			stats->out_ucast_pkts = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_discards")) {
+			stats->out_discards = val;
+			cnts++;
+		} else if (!strcmp(cnt, "tx_errors")) {
+			stats->out_errors = val;
+			cnts++;
+		}
+	}
+
+	free(strings);
+	free(estats);
+
+	/* Ethtool strings came from kernel driver. Name of that
+	 * strings is not universal. Current function needs to be updated
+	 * if your driver has different names for counters */
+	if (cnts < 8)
+		return -1;
+
+	return 0;
+}
+
+int ethtool_stats_get_fd(int fd, const char *name, odp_pktio_stats_t *stats)
+{
+	struct ifreq ifr;
+
+	snprintf(ifr.ifr_name, IF_NAMESIZE, "%s", name);
+	return ethtool_stats(fd, &ifr, stats);
+}
diff --git a/platform/linux-generic/pktio/sysfs.c b/platform/linux-generic/pktio/sysfs.c
new file mode 100644
index 0000000..4e5c028
--- /dev/null
+++ b/platform/linux-generic/pktio/sysfs.c
@@ -0,0 +1,76 @@
+/* Copyright (c) 2015, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#include <odp.h>
+#include <odp_packet_io_internal.h>
+#include <errno.h>
+#include <string.h>
+
+static int sysfs_get_val(const char *fname, uint64_t *val)
+{
+	FILE  *file;
+	char str[128];
+	int ret = -1;
+
+	file = fopen(fname, "rt");
+	if (file == NULL) {
+		__odp_errno = errno;
+		/* do not print debug err if sysfs is not supported by
+		 * kernel driver.
+		 */
+		if (errno != ENOENT)
+			ODP_ERR("fopen %s: %s\n", fname, strerror(errno));
+		return 0;
+	}
+
+	if (fgets(str, sizeof(str), file) != NULL)
+		ret = sscanf(str, "%" SCNx64, val);
+
+	(void)fclose(file);
+
+	if (ret != 1) {
+		ODP_ERR("read %s\n", fname);
+		return -1;
+	}
+
+	return 0;
+}
+
+int sysfs_stats(pktio_entry_t *pktio_entry,
+		odp_pktio_stats_t *stats)
+{
+	char fname[256];
+	const char *dev = pktio_entry->s.name;
+	int ret = 0;
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_bytes", dev);
+	ret -= sysfs_get_val(fname, &stats->in_octets);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_packets", dev);
+	ret -= sysfs_get_val(fname, &stats->in_ucast_pkts);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_droppped", dev);
+	ret -= sysfs_get_val(fname, &stats->in_discards);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/rx_errors", dev);
+	ret -= sysfs_get_val(fname, &stats->in_errors);
+
+	/* stats->in_unknown_protos is not supported in sysfs */
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_bytes", dev);
+	ret -= sysfs_get_val(fname, &stats->out_octets);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_packets", dev);
+	ret -= sysfs_get_val(fname, &stats->out_ucast_pkts);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_dropped", dev);
+	ret -= sysfs_get_val(fname, &stats->out_discards);
+
+	sprintf(fname, "/sys/class/net/%s/statistics/tx_errors", dev);
+	ret -= sysfs_get_val(fname, &stats->out_errors);
+
+	return ret;
+}
