diff --git a/platform/linux-generic/pktio/common.c b/platform/linux-generic/pktio/common.c
index 900dcbba2..e6eb09900 100644
--- a/platform/linux-generic/pktio/common.c
+++ b/platform/linux-generic/pktio/common.c
@@ -45,7 +45,7 @@ int sock_stats_reset_fd(pktio_entry_t *pktio_entry, int fd)
 					   pktio_entry->s.name,
 					   &cur_stats);
 	} else if (pktio_entry->s.stats_type == STATS_SYSFS) {
-		err = sysfs_stats(pktio_entry, &cur_stats);
+		err = sysfs_netif_stats(pktio_entry->s.name, &cur_stats);
 		if (err != 0)
 			ODP_ERR("stats error\n");
 	}
@@ -78,7 +78,7 @@ int sock_stats_fd(pktio_entry_t *pktio_entry,
 					   pktio_entry->s.name,
 					   &cur_stats);
 	} else if (pktio_entry->s.stats_type == STATS_SYSFS) {
-		sysfs_stats(pktio_entry, &cur_stats);
+		sysfs_netif_stats(pktio_entry->s.name, &cur_stats);
 	}
 
 	stats->in_octets = cur_stats.in_octets -
diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c
index 7e0485b99..320de973c 100644
--- a/platform/linux-generic/pktio/socket.c
+++ b/platform/linux-generic/pktio/socket.c
@@ -204,7 +204,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 				   pktio_entry->s.name,
 				   &cur_stats);
 	if (err != 0) {
-		err = sysfs_stats(pktio_entry, &cur_stats);
+		err = sysfs_netif_stats(pktio_entry->s.name, &cur_stats);
 		if (err != 0) {
 			pktio_entry->s.stats_type = STATS_UNSUPPORTED;
 			ODP_DBG("pktio: %s unsupported stats\n",
diff --git a/platform/linux-generic/pktio/socket_mmap.c b/platform/linux-generic/pktio/socket_mmap.c
index 271c2f0f7..26d0a2957 100644
--- a/platform/linux-generic/pktio/socket_mmap.c
+++ b/platform/linux-generic/pktio/socket_mmap.c
@@ -593,7 +593,7 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
 				   pktio_entry->s.name,
 				   &cur_stats);
 	if (ret != 0) {
-		ret = sysfs_stats(pktio_entry, &cur_stats);
+		ret = sysfs_netif_stats(pktio_entry->s.name, &cur_stats);
 		if (ret != 0) {
 			pktio_entry->s.stats_type = STATS_UNSUPPORTED;
 			ODP_DBG("pktio: %s unsupported stats\n",
diff --git a/platform/linux-generic/pktio/sysfs.c b/platform/linux-generic/pktio/sysfs.c
index f674ac42c..ae66296fb 100644
--- a/platform/linux-generic/pktio/sysfs.c
+++ b/platform/linux-generic/pktio/sysfs.c
@@ -7,74 +7,157 @@
 #include "config.h"
 
 #include <odp_api.h>
-#include <odp_packet_io_internal.h>
+#include <odp_errno_define.h>
 #include <pktio/sysfs.h>
+
 #include <errno.h>
 #include <string.h>
 #include <inttypes.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
 
-static int sysfs_get_val(const char *fname, uint64_t *val)
+static int _sysfs_attr_raw_get(char *buf, size_t buf_size, const char *fmt,
+			       va_list args)
 {
-	FILE  *file;
-	char str[128];
-	int ret = -1;
+	char path[256];
+	FILE *file;
+	int ret;
 
-	file = fopen(fname, "rt");
-	if (file == NULL) {
+	ret = vsnprintf(path, sizeof(path), fmt, args);
+
+	if (ret < 0) {
 		__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;
+		return -1;
+	}
+
+	if (ret >= (ssize_t)sizeof(path)) {
+		__odp_errno = EINVAL;
+		return -1;
 	}
 
-	if (fgets(str, sizeof(str), file) != NULL)
-		ret = sscanf(str, "%" SCNx64, val);
+	file = fopen(path, "rt");
+	if (file == NULL) {
+		__odp_errno = errno;
+		return -1;
+	}
 
+	buf = fgets(buf, buf_size, file);
 	(void)fclose(file);
 
-	if (ret != 1) {
-		ODP_ERR("read %s\n", fname);
+	if (buf == NULL) {
+		__odp_errno = errno;
 		return -1;
 	}
 
 	return 0;
 }
 
-int sysfs_stats(pktio_entry_t *pktio_entry,
-		odp_pktio_stats_t *stats)
+ODP_PRINTF_FORMAT(3, 4)
+int sysfs_attr_raw_get(char *buf, size_t buf_size, const char *fmt, ...)
 {
-	char fname[256];
-	const char *dev = pktio_entry->s.name;
-	int ret = 0;
+	va_list args;
+	int ret;
 
-	sprintf(fname, "/sys/class/net/%s/statistics/rx_bytes", dev);
-	ret -= sysfs_get_val(fname, &stats->in_octets);
+	va_start(args, fmt);
+	ret = _sysfs_attr_raw_get(buf, buf_size, fmt, args);
+	va_end(args);
 
-	sprintf(fname, "/sys/class/net/%s/statistics/rx_packets", dev);
-	ret -= sysfs_get_val(fname, &stats->in_ucast_pkts);
+	return ret;
+}
+
+ODP_PRINTF_FORMAT(2, 3)
+int sysfs_attr_u64_get(uint64_t *value, const char *fmt, ...)
+{
+	char buf[20 + 1 + 1]; /* 20 digits (UINT64_MAX) + '\n' + '\0' */
+	va_list args;
+	char *endptr;
+	int ret;
 
-	sprintf(fname, "/sys/class/net/%s/statistics/rx_droppped", dev);
-	ret -= sysfs_get_val(fname, &stats->in_discards);
+	va_start(args, fmt);
+	ret = _sysfs_attr_raw_get(buf, sizeof(buf), fmt, args);
+	va_end(args);
 
-	sprintf(fname, "/sys/class/net/%s/statistics/rx_errors", dev);
-	ret -= sysfs_get_val(fname, &stats->in_errors);
+	if (ret < 0)
+		return -1;
+
+	if (buf[0] == '\0') {
+		__odp_errno = EINVAL;
+		return -1;
+	}
+
+	*value = strtoull(buf, &endptr, 0);
+
+	/* It is OK to have '\n' in sysfs */
+	if (*endptr == '\n')
+		endptr++;
+
+	if (*endptr != '\0') {
+		__odp_errno = EINVAL;
+		return -1;
+	}
+
+	return 0;
+}
+
+int sysfs_netif_stats(const char *netif_name, odp_pktio_stats_t *stats)
+{
+	int ret;
+
+	/*
+	 * Do not print debug err if sysfs is not supported by
+	 * kernel driver.
+	 */
+
+	ret = sysfs_attr_u64_get(&stats->in_octets,
+				 "/sys/class/net/%s/statistics/rx_bytes",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
+
+	ret = sysfs_attr_u64_get(&stats->in_ucast_pkts,
+				 "/sys/class/net/%s/statistics/rx_packets",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
+
+	ret = sysfs_attr_u64_get(&stats->in_discards,
+				 "/sys/class/net/%s/statistics/rx_droppped",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
+
+	ret = sysfs_attr_u64_get(&stats->in_errors,
+				 "/sys/class/net/%s/statistics/rx_errors",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
 
 	/* 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);
+	ret = sysfs_attr_u64_get(&stats->out_octets,
+				 "/sys/class/net/%s/statistics/tx_bytes",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
 
-	sprintf(fname, "/sys/class/net/%s/statistics/tx_packets", dev);
-	ret -= sysfs_get_val(fname, &stats->out_ucast_pkts);
+	ret = sysfs_attr_u64_get(&stats->out_ucast_pkts,
+				 "/sys/class/net/%s/statistics/tx_packets",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
 
-	sprintf(fname, "/sys/class/net/%s/statistics/tx_dropped", dev);
-	ret -= sysfs_get_val(fname, &stats->out_discards);
+	ret = sysfs_attr_u64_get(&stats->out_discards,
+				 "/sys/class/net/%s/statistics/tx_dropped",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
 
-	sprintf(fname, "/sys/class/net/%s/statistics/tx_errors", dev);
-	ret -= sysfs_get_val(fname, &stats->out_errors);
+	ret = sysfs_attr_u64_get(&stats->out_errors,
+				 "/sys/class/net/%s/statistics/tx_errors",
+				 netif_name);
+	if (ret < 0 && __odp_errno != ENOENT)
+		return -1;
 
-	return ret;
+	return 0;
 }
diff --git a/platform/linux-generic/pktio/sysfs.h b/platform/linux-generic/pktio/sysfs.h
index f8fe3072a..71ac4726f 100644
--- a/platform/linux-generic/pktio/sysfs.h
+++ b/platform/linux-generic/pktio/sysfs.h
@@ -8,16 +8,39 @@
 #define ODP_PKTIO_SYSFS_H_
 
 /**
- * Get statistics for a pktio entry
+ * Read an attribute from /sysfs as raw string
  *
- * @param pktio_entry     Packet IO entry
- * @param stats[out]	   Output buffer for counters
+ * @param buf[out]	Attribute value
+ * @param buf_size	Maximum number of bytes to read (including '\0')
+ * @param fmt		printf-like path to the attribute
+ *
+ * @retval 0 on success, buf[] is guaranteed to be '\0'-terminated
+ * @retval != 0 on failure
+ */
+ODP_PRINTF_FORMAT(3, 4)
+int sysfs_attr_raw_get(char *buf, size_t buf_size, const char *fmt, ...);
+
+/**
+ * Read an attribute from /sysfs as uint64_t
+ *
+ * @param value[out]	Attribute value
+ * @param fmt		printf-like path to the attribute
  *
  * @retval 0 on success
  * @retval != 0 on failure
  */
+ODP_PRINTF_FORMAT(2, 3)
+int sysfs_attr_u64_get(uint64_t *value, const char *fmt, ...);
 
-int sysfs_stats(pktio_entry_t *pktio_entry,
-		odp_pktio_stats_t *stats);
+/**
+ * Get statistics for a network interface
+ *
+ * @param netif_name	Network interface name
+ * @param stats[out]	Output buffer for counters
+ *
+ * @retval 0 on success
+ * @retval != 0 on failure
+ */
+int sysfs_netif_stats(const char *netif_name, odp_pktio_stats_t *stats);
 
 #endif /* ODP_PKTIO_SYSFS_H_ */
