diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 3fc032da..7d9da2e7 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -174,13 +174,16 @@ noinst_HEADERS = \
 		  ${srcdir}/include/odp_name_table_internal.h \
 		  ${srcdir}/include/odp_packet_internal.h \
 		  ${srcdir}/include/odp_packet_io_internal.h \
-		  ${srcdir}/include/odp_packet_io_ipc_internal.h \
 		  ${srcdir}/include/odp_packet_io_queue.h \
 		  ${srcdir}/include/odp_packet_io_ring_internal.h \
-		  ${srcdir}/include/odp_packet_netmap.h \
-		  ${srcdir}/include/odp_packet_dpdk.h \
-		  ${srcdir}/include/odp_packet_socket.h \
-		  ${srcdir}/include/odp_packet_tap.h \
+		  ${srcdir}/include/odp_pktio_ops_subsystem.h \
+		  ${srcdir}/include/odp_pktio_ops_dpdk.h \
+		  ${srcdir}/include/odp_pktio_ops_ipc.h \
+		  ${srcdir}/include/odp_pktio_ops_loopback.h \
+		  ${srcdir}/include/odp_pktio_ops_netmap.h \
+		  ${srcdir}/include/odp_pktio_ops_pcap.h \
+		  ${srcdir}/include/odp_pktio_ops_socket.h \
+		  ${srcdir}/include/odp_pktio_ops_tap.h \
 		  ${srcdir}/include/odp_pkt_queue_internal.h \
 		  ${srcdir}/include/odp_pool_internal.h \
 		  ${srcdir}/include/odp_posix_extensions.h \
@@ -228,7 +231,7 @@ __LIB__libodp_linux_la_SOURCES = \
 			   odp_packet_flags.c \
 			   odp_packet_io.c \
 			   pktio/ethtool.c \
-			   pktio/io_ops.c \
+			   pktio/ops_subsystem.c \
 			   pktio/ipc.c \
 			   pktio/pktio_common.c \
 			   pktio/loop.c \
diff --git a/platform/linux-generic/include/odp_packet_io_internal.h b/platform/linux-generic/include/odp_packet_io_internal.h
index 89bb6f3a..456f51e6 100644
--- a/platform/linux-generic/include/odp_packet_io_internal.h
+++ b/platform/linux-generic/include/odp_packet_io_internal.h
@@ -4,7 +4,6 @@
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
-
 /**
  * @file
  *
@@ -23,109 +22,33 @@ extern "C" {
 #include <odp_classification_datamodel.h>
 #include <odp_align_internal.h>
 #include <odp_debug_internal.h>
-#include <odp_packet_io_ring_internal.h>
-
 #include <odp_config_internal.h>
 #include <odp/api/hints.h>
-#include <net/if.h>
 
 #define PKTIO_MAX_QUEUES 64
-#include <odp_packet_socket.h>
-#include <odp_packet_netmap.h>
-#include <odp_packet_tap.h>
-#include <odp_packet_dpdk.h>
-
 #define PKTIO_NAME_LEN 256
 
 #define PKTIN_INVALID  ((odp_pktin_queue_t) {ODP_PKTIO_INVALID, 0})
 #define PKTOUT_INVALID ((odp_pktout_queue_t) {ODP_PKTIO_INVALID, 0})
 
+/* Forward declaration */
+typedef union pktio_entry_un pktio_entry_t;
+#include <odp_pktio_ops_subsystem.h>
+
 /** Determine if a socket read/write error should be reported. Transient errors
  *  that simply require the caller to retry are ignored, the _send/_recv APIs
  *  are non-blocking and it is the caller's responsibility to retry if the
  *  requested number of packets were not handled. */
 #define SOCK_ERR_REPORT(e) (e != EAGAIN && e != EWOULDBLOCK && e != EINTR)
 
-/* Forward declaration */
-struct pktio_if_ops;
-
-typedef struct {
-	odp_queue_t loopq;		/**< loopback queue for "loop" device */
-	odp_bool_t promisc;		/**< promiscuous mode state */
-} pkt_loop_t;
-
-#ifdef HAVE_PCAP
-typedef struct {
-	char *fname_rx;		/**< name of pcap file for rx */
-	char *fname_tx;		/**< name of pcap file for tx */
-	void *rx;		/**< rx pcap handle */
-	void *tx;		/**< tx pcap handle */
-	void *tx_dump;		/**< tx pcap dumper handle */
-	odp_pool_t pool;	/**< rx pool */
-	unsigned char *buf;	/**< per-pktio temp buffer */
-	int loops;		/**< number of times to loop rx pcap */
-	int loop_cnt;		/**< number of loops completed */
-	odp_bool_t promisc;	/**< promiscuous mode state */
-} pkt_pcap_t;
-#endif
-
-typedef	struct {
-	/* TX */
-	struct  {
-		_ring_t	*send; /**< ODP ring for IPC msg packets
-					    indexes transmitted to shared
-					    memory */
-		_ring_t	*free; /**< ODP ring for IPC msg packets
-					    indexes already processed by remote
-					    process */
-	} tx;
-	/* RX */
-	struct {
-		_ring_t	*recv; /**< ODP ring for IPC msg packets
-					    indexes received from shared
-					     memory (from remote process) */
-		_ring_t	*free; /**< ODP ring for IPC msg packets
-					    indexes already processed by
-					    current process */
-	} rx; /* slave */
-	void		*pool_base;		/**< Remote pool base addr */
-	void		*pool_mdata_base;	/**< Remote pool mdata base addr */
-	uint64_t	pkt_size;		/**< Packet size in remote pool */
-	odp_pool_t	pool;			/**< Pool of main process */
-	enum {
-		PKTIO_TYPE_IPC_MASTER = 0, /**< Master is the process which
-						creates shm */
-		PKTIO_TYPE_IPC_SLAVE	   /**< Slave is the process which
-						connects to shm */
-	} type; /**< define if it's master or slave process */
-	odp_atomic_u32_t ready; /**< 1 - pktio is ready and can recv/send
-				     packet, 0 - not yet ready */
-	void *pinfo;
-	odp_shm_t pinfo_shm;
-	odp_shm_t remote_pool_shm; /**< shm of remote pool get with
-					_ipc_map_remote_pool() */
-} _ipc_pktio_t;
-
 struct pktio_entry {
-	const struct pktio_if_ops *ops; /**< Implementation specific methods */
+	const pktio_ops_module_t *ops;	/**< Implementation specific methods */
+	pktio_ops_data_t ops_data;	/**< IO operation specific data */
 	/* These two locks together lock the whole pktio device */
 	odp_ticketlock_t rxl;		/**< RX ticketlock */
 	odp_ticketlock_t txl;		/**< TX ticketlock */
 	int cls_enabled;		/**< is classifier enabled */
 	odp_pktio_t handle;		/**< pktio handle */
-	union {
-		pkt_loop_t pkt_loop;            /**< Using loopback for IO */
-		pkt_sock_t pkt_sock;		/**< using socket API for IO */
-		pkt_sock_mmap_t pkt_sock_mmap;	/**< using socket mmap
-						 *   API for IO */
-		pkt_netmap_t pkt_nm;		/**< using netmap API for IO */
-		pkt_dpdk_t pkt_dpdk;		/**< using DPDK for IO */
-#ifdef HAVE_PCAP
-		pkt_pcap_t pkt_pcap;		/**< Using pcap for IO */
-#endif
-		pkt_tap_t pkt_tap;		/**< using TAP for IO */
-		_ipc_pktio_t ipc;		/**< IPC pktio data */
-	};
 	enum {
 		/* Not allocated */
 		PKTIO_STATE_FREE = 0,
@@ -174,50 +97,16 @@ struct pktio_entry {
 	} out_queue[PKTIO_MAX_QUEUES];
 };
 
-typedef union {
+union pktio_entry_un {
 	struct pktio_entry s;
 	uint8_t pad[ROUNDUP_CACHE_LINE(sizeof(struct pktio_entry))];
-} pktio_entry_t;
+};
 
 typedef struct {
 	odp_spinlock_t lock;
 	pktio_entry_t entries[ODP_CONFIG_PKTIO_ENTRIES];
 } pktio_table_t;
 
-typedef struct pktio_if_ops {
-	const char *name;
-	void (*print)(pktio_entry_t *pktio_entry);
-	int (*init_global)(void);
-	int (*init_local)(void);
-	int (*term)(void);
-	int (*open)(odp_pktio_t pktio, pktio_entry_t *pktio_entry,
-		    const char *devname, odp_pool_t pool);
-	int (*close)(pktio_entry_t *pktio_entry);
-	int (*start)(pktio_entry_t *pktio_entry);
-	int (*stop)(pktio_entry_t *pktio_entry);
-	int (*stats)(pktio_entry_t *pktio_entry, odp_pktio_stats_t *stats);
-	int (*stats_reset)(pktio_entry_t *pktio_entry);
-	uint64_t (*pktin_ts_res)(pktio_entry_t *pktio_entry);
-	odp_time_t (*pktin_ts_from_ns)(pktio_entry_t *pktio_entry, uint64_t ns);
-	int (*recv)(pktio_entry_t *entry, int index, odp_packet_t packets[],
-		    int num);
-	int (*send)(pktio_entry_t *entry, int index,
-		    const odp_packet_t packets[], int num);
-	uint32_t (*mtu_get)(pktio_entry_t *pktio_entry);
-	int (*promisc_mode_set)(pktio_entry_t *pktio_entry,  int enable);
-	int (*promisc_mode_get)(pktio_entry_t *pktio_entry);
-	int (*mac_get)(pktio_entry_t *pktio_entry, void *mac_addr);
-	int (*link_status)(pktio_entry_t *pktio_entry);
-	int (*capability)(pktio_entry_t *pktio_entry,
-			  odp_pktio_capability_t *capa);
-	int (*config)(pktio_entry_t *pktio_entry,
-		      const odp_pktio_config_t *config);
-	int (*input_queues_config)(pktio_entry_t *pktio_entry,
-				   const odp_pktin_queue_param_t *param);
-	int (*output_queues_config)(pktio_entry_t *pktio_entry,
-				    const odp_pktout_queue_param_t *p);
-} pktio_if_ops_t;
-
 extern void *pktio_entry_ptr[];
 
 static inline int pktio_to_id(odp_pktio_t pktio)
@@ -262,18 +151,6 @@ int single_recv_queue(pktio_entry_t *entry, int index, odp_packet_t packets[],
 int single_send_queue(pktio_entry_t *entry, int index,
 		      const odp_packet_t packets[], int num);
 
-extern const pktio_if_ops_t netmap_pktio_ops;
-extern const pktio_if_ops_t dpdk_pktio_ops;
-extern const pktio_if_ops_t sock_mmsg_pktio_ops;
-extern const pktio_if_ops_t sock_mmap_pktio_ops;
-extern const pktio_if_ops_t loopback_pktio_ops;
-#ifdef HAVE_PCAP
-extern const pktio_if_ops_t pcap_pktio_ops;
-#endif
-extern const pktio_if_ops_t tap_pktio_ops;
-extern const pktio_if_ops_t ipc_pktio_ops;
-extern const pktio_if_ops_t * const pktio_if_ops[];
-
 int sysfs_stats(pktio_entry_t *pktio_entry,
 		odp_pktio_stats_t *stats);
 int sock_stats_fd(pktio_entry_t *pktio_entry,
diff --git a/platform/linux-generic/include/odp_packet_dpdk.h b/platform/linux-generic/include/odp_pktio_ops_dpdk.h
similarity index 96%
rename from platform/linux-generic/include/odp_packet_dpdk.h
rename to platform/linux-generic/include/odp_pktio_ops_dpdk.h
index 4d7e0fc4..61c96d55 100644
--- a/platform/linux-generic/include/odp_packet_dpdk.h
+++ b/platform/linux-generic/include/odp_pktio_ops_dpdk.h
@@ -4,8 +4,8 @@
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
-#ifndef ODP_PACKET_DPDK_H
-#define ODP_PACKET_DPDK_H
+#ifndef ODP_PKTIO_OPS_DPDK_H
+#define ODP_PKTIO_OPS_DPDK_H
 
 #include <odp/api/packet_io.h>
 #include <odp/api/pool.h>
@@ -65,6 +65,6 @@ typedef struct {
 	odp_ticketlock_t tx_lock[PKTIO_MAX_QUEUES];  /**< TX queue locks */
 	/** cache for storing extra RX packets */
 	pkt_cache_t rx_cache[PKTIO_MAX_QUEUES];
-} pkt_dpdk_t;
+} pktio_ops_dpdk_data_t;
 
 #endif
diff --git a/platform/linux-generic/include/odp_packet_io_ipc_internal.h b/platform/linux-generic/include/odp_pktio_ops_ipc.h
similarity index 51%
rename from platform/linux-generic/include/odp_packet_io_ipc_internal.h
rename to platform/linux-generic/include/odp_pktio_ops_ipc.h
index 7cd29488..ba302ca7 100644
--- a/platform/linux-generic/include/odp_packet_io_ipc_internal.h
+++ b/platform/linux-generic/include/odp_pktio_ops_ipc.h
@@ -4,6 +4,9 @@
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
+#ifndef ODP_PKTIO_OPS_IPC_H
+#define ODP_PKTIO_OPS_IPC_H
+
 #include <odp/api/packet_io.h>
 #include <odp_packet_io_internal.h>
 #include <odp/api/packet.h>
@@ -54,3 +57,42 @@ struct pktio_info {
 		int init_done;
 	} slave;
 } ODP_PACKED;
+
+typedef	struct {
+	/* TX */
+	struct  {
+		_ring_t	*send; /**< ODP ring for IPC msg packets
+					    indexes transmitted to shared
+					    memory */
+		_ring_t	*free; /**< ODP ring for IPC msg packets
+					    indexes already processed by remote
+					    process */
+	} tx;
+	/* RX */
+	struct {
+		_ring_t	*recv; /**< ODP ring for IPC msg packets
+					    indexes received from shared
+					     memory (from remote process) */
+		_ring_t	*free; /**< ODP ring for IPC msg packets
+					    indexes already processed by
+					    current process */
+	} rx; /* slave */
+	void		*pool_base;		/**< Remote pool base addr */
+	void		*pool_mdata_base;	/**< Remote pool mdata base addr */
+	uint64_t	pkt_size;		/**< Packet size in remote pool */
+	odp_pool_t	pool;			/**< Pool of main process */
+	enum {
+		PKTIO_TYPE_IPC_MASTER = 0, /**< Master is the process which
+						creates shm */
+		PKTIO_TYPE_IPC_SLAVE	   /**< Slave is the process which
+						connects to shm */
+	} type; /**< define if it's master or slave process */
+	odp_atomic_u32_t ready; /**< 1 - pktio is ready and can recv/send
+				     packet, 0 - not yet ready */
+	void *pinfo;
+	odp_shm_t pinfo_shm;
+	odp_shm_t remote_pool_shm; /**< shm of remote pool get with
+					_ipc_map_remote_pool() */
+} pktio_ops_ipc_data_t;
+
+#endif
diff --git a/platform/linux-generic/include/odp_pktio_ops_loopback.h b/platform/linux-generic/include/odp_pktio_ops_loopback.h
new file mode 100644
index 00000000..6b58f8e4
--- /dev/null
+++ b/platform/linux-generic/include/odp_pktio_ops_loopback.h
@@ -0,0 +1,17 @@
+/* Copyright (c) 2017, ARM Limited. All rights reserved.
+ *
+ * Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ODP_PKTIO_OPS_LOOPBACK_H
+#define ODP_PKTIO_OPS_LOOPBACK_H
+
+typedef struct {
+	odp_queue_t loopq;  /**< loopback queue for "loop" device */
+	odp_bool_t promisc; /**< promiscuous mode state */
+} pktio_ops_loopback_data_t;
+
+#endif
diff --git a/platform/linux-generic/include/odp_packet_netmap.h b/platform/linux-generic/include/odp_pktio_ops_netmap.h
similarity index 96%
rename from platform/linux-generic/include/odp_packet_netmap.h
rename to platform/linux-generic/include/odp_pktio_ops_netmap.h
index a6f68d56..4f36eaa7 100644
--- a/platform/linux-generic/include/odp_packet_netmap.h
+++ b/platform/linux-generic/include/odp_pktio_ops_netmap.h
@@ -4,8 +4,8 @@
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
-#ifndef ODP_PACKET_NETMAP_H
-#define ODP_PACKET_NETMAP_H
+#ifndef ODP_PKTIO_OPS_NETMAP_H
+#define ODP_PKTIO_OPS_NETMAP_H
 
 #include <odp/api/align.h>
 #include <odp/api/debug.h>
@@ -63,6 +63,6 @@ typedef struct {
 	netmap_ring_t rx_desc_ring[PKTIO_MAX_QUEUES];
 	/** mapping of pktout queues to netmap tx descriptors */
 	netmap_ring_t tx_desc_ring[PKTIO_MAX_QUEUES];
-} pkt_netmap_t;
+} pktio_ops_netmap_data_t;
 
 #endif
diff --git a/platform/linux-generic/include/odp_pktio_ops_pcap.h b/platform/linux-generic/include/odp_pktio_ops_pcap.h
new file mode 100644
index 00000000..80c979c1
--- /dev/null
+++ b/platform/linux-generic/include/odp_pktio_ops_pcap.h
@@ -0,0 +1,25 @@
+/* Copyright (c) 2017, ARM Limited. All rights reserved.
+ *
+ * Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ODP_PKTIO_OPS_PCAP_H
+#define ODP_PKTIO_OPS_PCAP_H
+
+typedef struct {
+	char *fname_rx;		/**< name of pcap file for rx */
+	char *fname_tx;		/**< name of pcap file for tx */
+	void *rx;		/**< rx pcap handle */
+	void *tx;		/**< tx pcap handle */
+	void *tx_dump;		/**< tx pcap dumper handle */
+	odp_pool_t pool;	/**< rx pool */
+	unsigned char *buf;	/**< per-pktio temp buffer */
+	int loops;		/**< number of times to loop rx pcap */
+	int loop_cnt;		/**< number of loops completed */
+	odp_bool_t promisc;	/**< promiscuous mode state */
+} pktio_ops_pcap_data_t;
+
+#endif
diff --git a/platform/linux-generic/include/odp_packet_socket.h b/platform/linux-generic/include/odp_pktio_ops_socket.h
similarity index 97%
rename from platform/linux-generic/include/odp_packet_socket.h
rename to platform/linux-generic/include/odp_pktio_ops_socket.h
index 0e61f6f0..7c97f03d 100644
--- a/platform/linux-generic/include/odp_packet_socket.h
+++ b/platform/linux-generic/include/odp_pktio_ops_socket.h
@@ -5,8 +5,8 @@
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
-#ifndef ODP_PACKET_SOCKET_H
-#define ODP_PACKET_SOCKET_H
+#ifndef ODP_PKTIO_OPS_SOCKET_H
+#define ODP_PKTIO_OPS_SOCKET_H
 
 #include <linux/if_packet.h>
 #include <linux/if_ether.h>
@@ -42,7 +42,7 @@ typedef struct {
 	odp_pool_t pool; /**< pool to alloc packets from */
 	uint32_t mtu;    /**< maximum transmission unit */
 	unsigned char if_mac[ETH_ALEN];	/**< IF eth mac addr */
-} pkt_sock_t;
+} pktio_ops_socket_data_t;
 
 /** packet mmap ring */
 struct ring {
@@ -79,7 +79,7 @@ typedef struct {
 	unsigned char if_mac[ETH_ALEN];
 	struct sockaddr_ll ll;
 	int fanout;
-} pkt_sock_mmap_t;
+} pktio_ops_socket_mmap_data_t;
 
 static inline void
 ethaddr_copy(unsigned char mac_dst[], unsigned char mac_src[])
diff --git a/platform/linux-generic/include/odp_pktio_ops_subsystem.h b/platform/linux-generic/include/odp_pktio_ops_subsystem.h
new file mode 100644
index 00000000..cdaeb2cf
--- /dev/null
+++ b/platform/linux-generic/include/odp_pktio_ops_subsystem.h
@@ -0,0 +1,109 @@
+/* Copyright (c) 2017, ARM Limited. All rights reserved.
+ *
+ * Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef ODP_SUBSYSTEM_PKTIO_OPS_H
+#define ODP_SUBSYSTEM_PKTIO_OPS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <module.h>
+#include <odp/api/packet_io.h>
+
+/* ODP packet IO operations subsystem declaration */
+extern SUBSYSTEM(pktio_ops);
+
+/* Subsystem APIs declarations */
+SUBSYSTEM_API(pktio_ops, int, open, odp_pktio_t,
+	      pktio_entry_t *, const char *, odp_pool_t);
+SUBSYSTEM_API(pktio_ops, int, close, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, int, start, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, int, stop, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, int, stats, pktio_entry_t *,
+	      odp_pktio_stats_t *stats);
+SUBSYSTEM_API(pktio_ops, int, stats_reset, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, uint64_t, pktin_ts_res, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, odp_time_t, pktin_ts_from_ns,
+	      pktio_entry_t *, uint64_t ns);
+SUBSYSTEM_API(pktio_ops, int, recv, pktio_entry_t *,
+	      int index, odp_packet_t packets[], int count);
+SUBSYSTEM_API(pktio_ops, int, send, pktio_entry_t *,
+	      int index, const odp_packet_t packets[], int count);
+SUBSYSTEM_API(pktio_ops, uint32_t, mtu_get, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, int, promisc_mode_set,
+	      pktio_entry_t *, int enable);
+SUBSYSTEM_API(pktio_ops, int, promisc_mode_get, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, int, mac_get, pktio_entry_t *, void *);
+SUBSYSTEM_API(pktio_ops, int, link_status, pktio_entry_t *);
+SUBSYSTEM_API(pktio_ops, int, capability, pktio_entry_t *,
+	      odp_pktio_capability_t *);
+SUBSYSTEM_API(pktio_ops, int, config, pktio_entry_t *,
+	      const odp_pktio_config_t *);
+SUBSYSTEM_API(pktio_ops, int, input_queues_config,
+	      pktio_entry_t *, const odp_pktin_queue_param_t *);
+SUBSYSTEM_API(pktio_ops, int, output_queues_config,
+	      pktio_entry_t *, const odp_pktout_queue_param_t *);
+SUBSYSTEM_API(pktio_ops, void, print, pktio_entry_t *);
+
+typedef MODULE_CLASS(pktio_ops)
+	api_proto(pktio_ops, open) open;
+	api_proto(pktio_ops, close) close;
+	api_proto(pktio_ops, start) start;
+	api_proto(pktio_ops, stop) stop;
+	api_proto(pktio_ops, stats) stats;
+	api_proto(pktio_ops, stats_reset) stats_reset;
+	api_proto(pktio_ops, pktin_ts_res) pktin_ts_res;
+	api_proto(pktio_ops, pktin_ts_from_ns) pktin_ts_from_ns;
+	api_proto(pktio_ops, recv) recv;
+	api_proto(pktio_ops, send) send;
+	api_proto(pktio_ops, mtu_get) mtu_get;
+	api_proto(pktio_ops, promisc_mode_set) promisc_mode_set;
+	api_proto(pktio_ops, promisc_mode_get) promisc_mode_get;
+	api_proto(pktio_ops, mac_get) mac_get;
+	api_proto(pktio_ops, link_status) link_status;
+	api_proto(pktio_ops, capability) capability;
+	api_proto(pktio_ops, config) config;
+	api_proto(pktio_ops, input_queues_config) input_queues_config;
+	api_proto(pktio_ops, output_queues_config) output_queues_config;
+	api_proto(pktio_ops, print) print;
+} pktio_ops_module_t;
+
+/* All implementations of this subsystem */
+#include <odp_pktio_ops_dpdk.h>
+#include <odp_pktio_ops_ipc.h>
+#include <odp_pktio_ops_loopback.h>
+#include <odp_pktio_ops_netmap.h>
+#ifdef HAVE_PCAP
+#include <odp_pktio_ops_pcap.h>
+#endif
+#include <odp_pktio_ops_socket.h>
+#include <odp_pktio_ops_tap.h>
+
+/* Per pktio instance data used by each implementation */
+typedef union {
+	pktio_ops_dpdk_data_t dpdk;
+	pktio_ops_ipc_data_t ipc;
+	pktio_ops_loopback_data_t loopback;
+	pktio_ops_netmap_data_t netmap;
+#ifdef HAVE_PCAP
+	pktio_ops_pcap_data_t pcap;
+#endif
+	pktio_ops_socket_data_t _socket;
+	pktio_ops_socket_mmap_data_t _mmap;
+	pktio_ops_tap_data_t tap;
+} pktio_ops_data_t;
+
+/* Extract pktio ops data from pktio entry structure */
+#define ops_data(mod) s.ops_data.mod
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_packet_tap.h b/platform/linux-generic/include/odp_pktio_ops_tap.h
similarity index 85%
rename from platform/linux-generic/include/odp_packet_tap.h
rename to platform/linux-generic/include/odp_pktio_ops_tap.h
index a90bfbce..1cbc31cf 100644
--- a/platform/linux-generic/include/odp_packet_tap.h
+++ b/platform/linux-generic/include/odp_pktio_ops_tap.h
@@ -4,8 +4,8 @@
  * SPDX-License-Identifier:     BSD-3-Clause
  */
 
-#ifndef ODP_PACKET_TAP_H_
-#define ODP_PACKET_TAP_H_
+#ifndef ODP_PACKET_OPS_TAP_H
+#define ODP_PACKET_OPS_TAP_H
 
 #include <odp/api/pool.h>
 
@@ -16,6 +16,6 @@ typedef struct {
 	unsigned char if_mac[ETH_ALEN];	/**< MAC address of pktio side (not a
 					     MAC address of kernel interface)*/
 	odp_pool_t pool;		/**< pool to alloc packets from */
-} pkt_tap_t;
+} pktio_ops_tap_data_t;
 
 #endif
diff --git a/platform/linux-generic/odp_packet_io.c b/platform/linux-generic/odp_packet_io.c
index 4dd28549..45124567 100644
--- a/platform/linux-generic/odp_packet_io.c
+++ b/platform/linux-generic/odp_packet_io.c
@@ -14,13 +14,11 @@
 #include <odp/api/spinlock.h>
 #include <odp/api/ticketlock.h>
 #include <odp/api/shared_memory.h>
-#include <odp_packet_socket.h>
 #include <odp_config_internal.h>
 #include <odp_queue_if.h>
 #include <odp_schedule_if.h>
 #include <odp_classification_internal.h>
 #include <odp_debug_internal.h>
-#include <odp_packet_io_ipc_internal.h>
 #include <odp/api/time.h>
 
 #include <string.h>
@@ -47,12 +45,14 @@ static inline pktio_entry_t *pktio_entry_by_index(int index)
 	return pktio_entry_ptr[index];
 }
 
+SUBSYSTEM_INITERM_TEMPLATE(pktio_ops, init_local, ODP_ERR)
+SUBSYSTEM_INITERM_TEMPLATE(pktio_ops, init_global, ODP_ERR)
+
 int odp_pktio_init_global(void)
 {
 	pktio_entry_t *pktio_entry;
 	int i;
 	odp_shm_t shm;
-	int pktio_if;
 
 	shm = odp_shm_reserve("odp_pktio_entries",
 			      sizeof(pktio_table_t),
@@ -77,33 +77,11 @@ int odp_pktio_init_global(void)
 		pktio_entry_ptr[i] = pktio_entry;
 	}
 
-	for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
-		if (pktio_if_ops[pktio_if]->init_global)
-			if (pktio_if_ops[pktio_if]->init_global()) {
-				ODP_ERR("failed to initialized pktio type %d",
-					pktio_if);
-				return -1;
-			}
-	}
-
-	return 0;
+	return pktio_ops_subsystem_init_global();
 }
 
-int odp_pktio_init_local(void)
-{
-	int pktio_if;
-
-	for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
-		if (pktio_if_ops[pktio_if]->init_local)
-			if (pktio_if_ops[pktio_if]->init_local()) {
-				ODP_ERR("failed to initialized pktio type %d",
-					pktio_if);
-				return -1;
-			}
-	}
-
-	return 0;
-}
+int __attribute__((alias("pktio_ops_subsystem_init_local")))
+	odp_pktio_init_local(void);
 
 static inline int is_free(pktio_entry_t *entry)
 {
@@ -181,8 +159,7 @@ static odp_pktio_t setup_pktio_entry(const char *name, odp_pool_t pool,
 {
 	odp_pktio_t hdl;
 	pktio_entry_t *pktio_entry;
-	int ret = -1;
-	int pktio_if;
+	pktio_ops_module_t *mod;
 
 	if (strlen(name) >= PKTIO_NAME_LEN - 1) {
 		/* ioctl names limitation */
@@ -208,19 +185,18 @@ static odp_pktio_t setup_pktio_entry(const char *name, odp_pool_t pool,
 
 	odp_pktio_config_init(&pktio_entry->s.config);
 
-	for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
-		ret = pktio_if_ops[pktio_if]->open(hdl, pktio_entry, name,
-						   pool);
-
-		if (!ret) {
-			pktio_entry->s.ops = pktio_if_ops[pktio_if];
-			ODP_DBG("%s uses %s\n",
-				name, pktio_if_ops[pktio_if]->name);
+	subsystem_lock(read, pktio_ops);
+	subsystem_foreach_module(pktio_ops, mod) {
+		if (mod->open != NULL &&
+		    mod->open(hdl, pktio_entry, name, pool)) {
+			pktio_entry->s.ops = mod;
+			ODP_DBG("%s uses %s\n", name, mod->name);
 			break;
 		}
 	}
+	subsystem_unlock(read, pktio_ops);
 
-	if (ret != 0) {
+	if (pktio_entry->s.ops == NULL) {
 		pktio_entry->s.state = PKTIO_STATE_FREE;
 		hdl = ODP_PKTIO_INVALID;
 		ODP_ERR("Unable to init any I/O type.\n");
@@ -1060,11 +1036,12 @@ void odp_pktio_print(odp_pktio_t hdl)
 	ODP_PRINT("\n");
 }
 
+SUBSYSTEM_INITERM_TEMPLATE(pktio_ops, term_global, ODP_ABORT)
+
 int odp_pktio_term_global(void)
 {
 	int ret = 0;
 	int i;
-	int pktio_if;
 
 	for (i = 0; i < ODP_CONFIG_PKTIO_ENTRIES; ++i) {
 		pktio_entry_t *pktio_entry;
@@ -1090,12 +1067,7 @@ int odp_pktio_term_global(void)
 		unlock_entry(pktio_entry);
 	}
 
-	for (pktio_if = 0; pktio_if_ops[pktio_if]; ++pktio_if) {
-		if (pktio_if_ops[pktio_if]->term)
-			if (pktio_if_ops[pktio_if]->term())
-				ODP_ABORT("failed to terminate pktio type %d",
-					  pktio_if);
-	}
+	ret = pktio_ops_subsystem_term_global();
 
 	ret = odp_shm_free(odp_shm_lookup("odp_pktio_entries"));
 	if (ret != 0)
diff --git a/platform/linux-generic/pktio/ethtool.c b/platform/linux-generic/pktio/ethtool.c
index d8f9e12c..de35fd9c 100644
--- a/platform/linux-generic/pktio/ethtool.c
+++ b/platform/linux-generic/pktio/ethtool.c
@@ -13,7 +13,7 @@
 #include <net/if.h>
 
 #include <odp_api.h>
-#include <odp_packet_socket.h>
+#include <odp_pktio_ops_socket.h>
 #include <odp_debug_internal.h>
 
 static struct ethtool_gstrings *get_stringset(int fd, struct ifreq *ifr)
diff --git a/platform/linux-generic/pktio/io_ops.c b/platform/linux-generic/pktio/io_ops.c
deleted file mode 100644
index fbf30ca7..00000000
--- a/platform/linux-generic/pktio/io_ops.c
+++ /dev/null
@@ -1,31 +0,0 @@
-/* Copyright (c) 2013, Linaro Limited
- * All rights reserved.
- *
- * SPDX-License-Identifier:     BSD-3-Clause
- */
-
-#include <odp_packet_io_internal.h>
-
-/* Ops for all implementation of pktio.
- * Order matters. The first implementation to setup successfully
- * will be picked.
- * Array must be NULL terminated */
-const pktio_if_ops_t * const pktio_if_ops[]  = {
-	&loopback_pktio_ops,
-#ifdef ODP_PKTIO_DPDK
-	&dpdk_pktio_ops,
-#endif
-#ifdef ODP_NETMAP
-	&netmap_pktio_ops,
-#endif
-#ifdef HAVE_PCAP
-	&pcap_pktio_ops,
-#endif
-#ifdef _ODP_PKTIO_IPC
-	&ipc_pktio_ops,
-#endif
-	&tap_pktio_ops,
-	&sock_mmap_pktio_ops,
-	&sock_mmsg_pktio_ops,
-	NULL
-};
diff --git a/platform/linux-generic/pktio/ipc.c b/platform/linux-generic/pktio/ipc.c
index 06175e5a..5bd5fb95 100644
--- a/platform/linux-generic/pktio/ipc.c
+++ b/platform/linux-generic/pktio/ipc.c
@@ -3,7 +3,6 @@
  *
  * SPDX-License-Identifier:     BSD-3-Clause
  */
-#include <odp_packet_io_ipc_internal.h>
 #include <odp_debug_internal.h>
 #include <odp_packet_io_internal.h>
 #include <odp/api/system_info.h>
@@ -43,7 +42,7 @@ static const char *_ipc_odp_buffer_pool_shm_name(odp_pool_t pool_hdl)
 
 static int _ipc_master_start(pktio_entry_t *pktio_entry)
 {
-	struct pktio_info *pinfo = pktio_entry->s.ipc.pinfo;
+	struct pktio_info *pinfo = pktio_entry->ops_data(ipc).pinfo;
 	odp_shm_t shm;
 
 	if (pinfo->slave.init_done == 0)
@@ -57,12 +56,12 @@ static int _ipc_master_start(pktio_entry_t *pktio_entry)
 		return -1;
 	}
 
-	pktio_entry->s.ipc.remote_pool_shm = shm;
-	pktio_entry->s.ipc.pool_base = odp_shm_addr(shm);
-	pktio_entry->s.ipc.pool_mdata_base = (char *)odp_shm_addr(shm) +
+	pktio_entry->ops_data(ipc).remote_pool_shm = shm;
+	pktio_entry->ops_data(ipc).pool_base = odp_shm_addr(shm);
+	pktio_entry->ops_data(ipc).pool_mdata_base = (char *)odp_shm_addr(shm) +
 					     pinfo->slave.base_addr_offset;
 
-	odp_atomic_store_u32(&pktio_entry->s.ipc.ready, 1);
+	odp_atomic_store_u32(&pktio_entry->ops_data(ipc).ready, 1);
 
 	IPC_ODP_DBG("%s started.\n",  pktio_entry->s.name);
 	return 0;
@@ -89,62 +88,62 @@ static int _ipc_init_master(pktio_entry_t *pktio_entry,
 	 * to be processed packets ring.
 	 */
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_prod", dev);
-	pktio_entry->s.ipc.tx.send = _ring_create(ipc_shm_name,
+	pktio_entry->ops_data(ipc).tx.send = _ring_create(ipc_shm_name,
 			PKTIO_IPC_ENTRIES,
 			_RING_SHM_PROC | _RING_NO_LIST);
-	if (!pktio_entry->s.ipc.tx.send) {
+	if (!pktio_entry->ops_data(ipc).tx.send) {
 		ODP_ERR("pid %d unable to create ipc ring %s name\n",
 			getpid(), ipc_shm_name);
 		return -1;
 	}
 	ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.send),
-		_ring_free_count(pktio_entry->s.ipc.tx.send));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).tx.send),
+		_ring_free_count(pktio_entry->ops_data(ipc).tx.send));
 
 	/* generate name in shm like ipc_pktio_p for
 	 * already processed packets
 	 */
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_cons", dev);
-	pktio_entry->s.ipc.tx.free = _ring_create(ipc_shm_name,
+	pktio_entry->ops_data(ipc).tx.free = _ring_create(ipc_shm_name,
 			PKTIO_IPC_ENTRIES,
 			_RING_SHM_PROC | _RING_NO_LIST);
-	if (!pktio_entry->s.ipc.tx.free) {
+	if (!pktio_entry->ops_data(ipc).tx.free) {
 		ODP_ERR("pid %d unable to create ipc ring %s name\n",
 			getpid(), ipc_shm_name);
 		goto free_m_prod;
 	}
 	ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.free),
-		_ring_free_count(pktio_entry->s.ipc.tx.free));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).tx.free),
+		_ring_free_count(pktio_entry->ops_data(ipc).tx.free));
 
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_prod", dev);
-	pktio_entry->s.ipc.rx.recv = _ring_create(ipc_shm_name,
+	pktio_entry->ops_data(ipc).rx.recv = _ring_create(ipc_shm_name,
 			PKTIO_IPC_ENTRIES,
 			_RING_SHM_PROC | _RING_NO_LIST);
-	if (!pktio_entry->s.ipc.rx.recv) {
+	if (!pktio_entry->ops_data(ipc).rx.recv) {
 		ODP_ERR("pid %d unable to create ipc ring %s name\n",
 			getpid(), ipc_shm_name);
 		goto free_m_cons;
 	}
 	ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.recv),
-		_ring_free_count(pktio_entry->s.ipc.rx.recv));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).rx.recv),
+		_ring_free_count(pktio_entry->ops_data(ipc).rx.recv));
 
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_cons", dev);
-	pktio_entry->s.ipc.rx.free = _ring_create(ipc_shm_name,
+	pktio_entry->ops_data(ipc).rx.free = _ring_create(ipc_shm_name,
 			PKTIO_IPC_ENTRIES,
 			_RING_SHM_PROC | _RING_NO_LIST);
-	if (!pktio_entry->s.ipc.rx.free) {
+	if (!pktio_entry->ops_data(ipc).rx.free) {
 		ODP_ERR("pid %d unable to create ipc ring %s name\n",
 			getpid(), ipc_shm_name);
 		goto free_s_prod;
 	}
 	ODP_DBG("Created IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.free),
-		_ring_free_count(pktio_entry->s.ipc.rx.free));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).rx.free),
+		_ring_free_count(pktio_entry->ops_data(ipc).rx.free));
 
 	/* Set up pool name for remote info */
-	pinfo = pktio_entry->s.ipc.pinfo;
+	pinfo = pktio_entry->ops_data(ipc).pinfo;
 	pool_name = _ipc_odp_buffer_pool_shm_name(pool_hdl);
 	if (strlen(pool_name) > ODP_POOL_NAME_LEN) {
 		ODP_ERR("pid %d ipc pool name %s is too big %d\n",
@@ -158,7 +157,7 @@ static int _ipc_init_master(pktio_entry_t *pktio_entry,
 	pinfo->slave.pid = 0;
 	pinfo->slave.init_done = 0;
 
-	pktio_entry->s.ipc.pool = pool_hdl;
+	pktio_entry->ops_data(ipc).pool = pool_hdl;
 
 	ODP_DBG("Pre init... DONE.\n");
 	pinfo->master.init_done = 1;
@@ -227,7 +226,7 @@ static int _ipc_init_slave(const char *dev,
 	if (strlen(dev) > (ODP_POOL_NAME_LEN - sizeof("_slave_r")))
 		ODP_ABORT("too big ipc name\n");
 
-	pktio_entry->s.ipc.pool = pool;
+	pktio_entry->ops_data(ipc).pool = pool;
 	return 0;
 }
 
@@ -248,62 +247,62 @@ static int _ipc_slave_start(pktio_entry_t *pktio_entry)
 	sprintf(dev, "ipc:%s", tail);
 
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_prod", dev);
-	pktio_entry->s.ipc.rx.recv  = _ipc_shm_map(ipc_shm_name, pid);
-	if (!pktio_entry->s.ipc.rx.recv) {
+	pktio_entry->ops_data(ipc).rx.recv  = _ipc_shm_map(ipc_shm_name, pid);
+	if (!pktio_entry->ops_data(ipc).rx.recv) {
 		ODP_DBG("pid %d unable to find ipc ring %s name\n",
 			getpid(), dev);
 		sleep(1);
 		return -1;
 	}
 	ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.recv),
-		_ring_free_count(pktio_entry->s.ipc.rx.recv));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).rx.recv),
+		_ring_free_count(pktio_entry->ops_data(ipc).rx.recv));
 
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_m_cons", dev);
-	pktio_entry->s.ipc.rx.free = _ipc_shm_map(ipc_shm_name, pid);
-	if (!pktio_entry->s.ipc.rx.free) {
+	pktio_entry->ops_data(ipc).rx.free = _ipc_shm_map(ipc_shm_name, pid);
+	if (!pktio_entry->ops_data(ipc).rx.free) {
 		ODP_ERR("pid %d unable to find ipc ring %s name\n",
 			getpid(), dev);
 		goto free_m_prod;
 	}
 	ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.rx.free),
-		_ring_free_count(pktio_entry->s.ipc.rx.free));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).rx.free),
+		_ring_free_count(pktio_entry->ops_data(ipc).rx.free));
 
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_prod", dev);
-	pktio_entry->s.ipc.tx.send = _ipc_shm_map(ipc_shm_name, pid);
-	if (!pktio_entry->s.ipc.tx.send) {
+	pktio_entry->ops_data(ipc).tx.send = _ipc_shm_map(ipc_shm_name, pid);
+	if (!pktio_entry->ops_data(ipc).tx.send) {
 		ODP_ERR("pid %d unable to find ipc ring %s name\n",
 			getpid(), dev);
 		goto free_m_cons;
 	}
 	ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.send),
-		_ring_free_count(pktio_entry->s.ipc.tx.send));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).tx.send),
+		_ring_free_count(pktio_entry->ops_data(ipc).tx.send));
 
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_cons", dev);
-	pktio_entry->s.ipc.tx.free = _ipc_shm_map(ipc_shm_name, pid);
-	if (!pktio_entry->s.ipc.tx.free) {
+	pktio_entry->ops_data(ipc).tx.free = _ipc_shm_map(ipc_shm_name, pid);
+	if (!pktio_entry->ops_data(ipc).tx.free) {
 		ODP_ERR("pid %d unable to find ipc ring %s name\n",
 			getpid(), dev);
 		goto free_s_prod;
 	}
 	ODP_DBG("Connected IPC ring: %s, count %d, free %d\n",
-		ipc_shm_name, _ring_count(pktio_entry->s.ipc.tx.free),
-		_ring_free_count(pktio_entry->s.ipc.tx.free));
+		ipc_shm_name, _ring_count(pktio_entry->ops_data(ipc).tx.free),
+		_ring_free_count(pktio_entry->ops_data(ipc).tx.free));
 
 	/* Get info about remote pool */
-	pinfo = pktio_entry->s.ipc.pinfo;
+	pinfo = pktio_entry->ops_data(ipc).pinfo;
 	shm = _ipc_map_remote_pool(pinfo->master.pool_name,
 				   pid);
-	pktio_entry->s.ipc.remote_pool_shm = shm;
-	pktio_entry->s.ipc.pool_mdata_base = (char *)odp_shm_addr(shm) +
+	pktio_entry->ops_data(ipc).remote_pool_shm = shm;
+	pktio_entry->ops_data(ipc).pool_mdata_base = (char *)odp_shm_addr(shm) +
 					     pinfo->master.base_addr_offset;
-	pktio_entry->s.ipc.pkt_size = pinfo->master.block_size;
+	pktio_entry->ops_data(ipc).pkt_size = pinfo->master.block_size;
 
-	_ipc_export_pool(pinfo, pktio_entry->s.ipc.pool);
+	_ipc_export_pool(pinfo, pktio_entry->ops_data(ipc).pool);
 
-	odp_atomic_store_u32(&pktio_entry->s.ipc.ready, 1);
+	odp_atomic_store_u32(&pktio_entry->ops_data(ipc).ready, 1);
 	pinfo->slave.init_done = 1;
 
 	ODP_DBG("%s started.\n",  pktio_entry->s.name);
@@ -342,11 +341,11 @@ static int ipc_pktio_open(odp_pktio_t id ODP_UNUSED,
 	if (strncmp(dev, "ipc", 3))
 		return -1;
 
-	odp_atomic_init_u32(&pktio_entry->s.ipc.ready, 0);
+	odp_atomic_init_u32(&pktio_entry->ops_data(ipc).ready, 0);
 
 	/* Shared info about remote pktio */
 	if (sscanf(dev, "ipc:%d:%s", &pid, tail) == 2) {
-		pktio_entry->s.ipc.type = PKTIO_TYPE_IPC_SLAVE;
+		pktio_entry->ops_data(ipc).type = PKTIO_TYPE_IPC_SLAVE;
 
 		snprintf(name, sizeof(name), "ipc:%s_info", tail);
 		IPC_ODP_DBG("lookup for name %s for pid %d\n", name, pid);
@@ -359,12 +358,12 @@ static int ipc_pktio_open(odp_pktio_t id ODP_UNUSED,
 			odp_shm_free(shm);
 			return -1;
 		}
-		pktio_entry->s.ipc.pinfo = pinfo;
-		pktio_entry->s.ipc.pinfo_shm = shm;
+		pktio_entry->ops_data(ipc).pinfo = pinfo;
+		pktio_entry->ops_data(ipc).pinfo_shm = shm;
 		ODP_DBG("process %d is slave\n", getpid());
 		ret = _ipc_init_slave(name, pktio_entry, pool);
 	} else {
-		pktio_entry->s.ipc.type = PKTIO_TYPE_IPC_MASTER;
+		pktio_entry->ops_data(ipc).type = PKTIO_TYPE_IPC_MASTER;
 		snprintf(name, sizeof(name), "%s_info", dev);
 		shm = odp_shm_reserve(name, sizeof(struct pktio_info),
 				      ODP_CACHE_LINE_SIZE,
@@ -377,8 +376,8 @@ static int ipc_pktio_open(odp_pktio_t id ODP_UNUSED,
 		pinfo = odp_shm_addr(shm);
 		pinfo->master.init_done = 0;
 		pinfo->master.pool_name[0] = 0;
-		pktio_entry->s.ipc.pinfo = pinfo;
-		pktio_entry->s.ipc.pinfo_shm = shm;
+		pktio_entry->ops_data(ipc).pinfo = pinfo;
+		pktio_entry->ops_data(ipc).pinfo_shm = shm;
 		ODP_DBG("process %d is master\n", getpid());
 		ret = _ipc_init_master(pktio_entry, dev, pool);
 	}
@@ -406,7 +405,7 @@ static void _ipc_free_ring_packets(pktio_entry_t *pktio_entry, _ring_t *r)
 		for (i = 0; i < ret; i++) {
 			odp_packet_hdr_t *phdr;
 			odp_packet_t pkt;
-			void *mbase = pktio_entry->s.ipc.pool_mdata_base;
+			void *mbase = pktio_entry->ops_data(ipc).pool_mdata_base;
 
 			phdr = (void *)((uint8_t *)mbase + offsets[i]);
 			pkt = packet_handle(phdr);
@@ -427,15 +426,15 @@ static int ipc_pktio_recv_lockless(pktio_entry_t *pktio_entry,
 	uint32_t ready;
 	int pkts_ring;
 
-	ready = odp_atomic_load_u32(&pktio_entry->s.ipc.ready);
+	ready = odp_atomic_load_u32(&pktio_entry->ops_data(ipc).ready);
 	if (odp_unlikely(!ready)) {
 		IPC_ODP_DBG("start pktio is missing before usage?\n");
 		return 0;
 	}
 
-	_ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.free);
+	_ipc_free_ring_packets(pktio_entry, pktio_entry->ops_data(ipc).tx.free);
 
-	r = pktio_entry->s.ipc.rx.recv;
+	r = pktio_entry->ops_data(ipc).rx.recv;
 	pkts = _ring_mc_dequeue_burst(r, ipcbufs_p, len);
 	if (odp_unlikely(pkts < 0))
 		ODP_ABORT("internal error dequeue\n");
@@ -452,10 +451,10 @@ static int ipc_pktio_recv_lockless(pktio_entry_t *pktio_entry,
 		uint64_t data_pool_off;
 		void *rmt_data_ptr;
 
-		phdr = (void *)((uint8_t *)pktio_entry->s.ipc.pool_mdata_base +
+		phdr = (void *)((uint8_t *)pktio_entry->ops_data(ipc).pool_mdata_base +
 		       offsets[i]);
 
-		pool = pktio_entry->s.ipc.pool;
+		pool = pktio_entry->ops_data(ipc).pool;
 		if (odp_unlikely(pool == ODP_POOL_INVALID))
 			ODP_ABORT("invalid pool");
 
@@ -478,10 +477,10 @@ static int ipc_pktio_recv_lockless(pktio_entry_t *pktio_entry,
 		if (odp_unlikely(!pkt_data))
 			ODP_ABORT("unable to map pkt_data ipc_slave %d\n",
 				  (PKTIO_TYPE_IPC_SLAVE ==
-					pktio_entry->s.ipc.type));
+					pktio_entry->ops_data(ipc).type));
 
 		/* Copy packet data from shared pool to local pool. */
-		rmt_data_ptr = (uint8_t *)pktio_entry->s.ipc.pool_mdata_base +
+		rmt_data_ptr = (uint8_t *)pktio_entry->ops_data(ipc).pool_mdata_base +
 			       data_pool_off;
 		memcpy(pkt_data, rmt_data_ptr, phdr->frame_len);
 
@@ -499,7 +498,7 @@ static int ipc_pktio_recv_lockless(pktio_entry_t *pktio_entry,
 	}
 
 	/* Now tell other process that we no longer need that buffers.*/
-	r_p = pktio_entry->s.ipc.rx.free;
+	r_p = pktio_entry->ops_data(ipc).rx.free;
 
 repeat:
 	pkts_ring = _ring_mp_enqueue_burst(r_p, ipcbufs_p, pkts);
@@ -539,7 +538,7 @@ static int ipc_pktio_send_lockless(pktio_entry_t *pktio_entry,
 	void **rbuf_p;
 	int ret;
 	int i;
-	uint32_t ready = odp_atomic_load_u32(&pktio_entry->s.ipc.ready);
+	uint32_t ready = odp_atomic_load_u32(&pktio_entry->ops_data(ipc).ready);
 	odp_packet_t pkt_table_mapped[len]; /**< Ready to send packet has to be
 					      * in memory mapped pool. */
 	uintptr_t offsets[len];
@@ -547,12 +546,12 @@ static int ipc_pktio_send_lockless(pktio_entry_t *pktio_entry,
 	if (odp_unlikely(!ready))
 		return 0;
 
-	_ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.free);
+	_ipc_free_ring_packets(pktio_entry, pktio_entry->ops_data(ipc).tx.free);
 
 	/* Copy packets to shm shared pool if they are in different */
 	for (i = 0; i < len; i++) {
 		odp_packet_t pkt =  pkt_table[i];
-		pool_t *ipc_pool = pool_entry_from_hdl(pktio_entry->s.ipc.pool);
+		pool_t *ipc_pool = pool_entry_from_hdl(pktio_entry->ops_data(ipc).pool);
 		odp_buffer_bits_t handle;
 		uint32_t pkt_pool_id;
 
@@ -561,7 +560,7 @@ static int ipc_pktio_send_lockless(pktio_entry_t *pktio_entry,
 		if (pkt_pool_id != ipc_pool->pool_idx) {
 			odp_packet_t newpkt;
 
-			newpkt = odp_packet_copy(pkt, pktio_entry->s.ipc.pool);
+			newpkt = odp_packet_copy(pkt, pktio_entry->ops_data(ipc).pool);
 			if (newpkt == ODP_PACKET_INVALID)
 				ODP_ABORT("Unable to copy packet\n");
 
@@ -596,12 +595,12 @@ static int ipc_pktio_send_lockless(pktio_entry_t *pktio_entry,
 
 	/* Put packets to ring to be processed by other process. */
 	rbuf_p = (void *)&offsets[0];
-	r = pktio_entry->s.ipc.tx.send;
+	r = pktio_entry->ops_data(ipc).tx.send;
 	ret = _ring_mp_enqueue_burst(r, rbuf_p, len);
 	if (odp_unlikely(ret < 0)) {
 		ODP_ERR("pid %d odp_ring_mp_enqueue_bulk fail, ipc_slave %d, ret %d\n",
 			getpid(),
-			(PKTIO_TYPE_IPC_SLAVE == pktio_entry->s.ipc.type),
+			(PKTIO_TYPE_IPC_SLAVE == pktio_entry->ops_data(ipc).type),
 			ret);
 		ODP_ERR("odp_ring_full: %d, odp_ring_count %d, _ring_free_count %d\n",
 			_ring_full(r), _ring_count(r),
@@ -641,14 +640,14 @@ static int ipc_mac_addr_get(pktio_entry_t *pktio_entry ODP_UNUSED,
 
 static int ipc_start(pktio_entry_t *pktio_entry)
 {
-	uint32_t ready = odp_atomic_load_u32(&pktio_entry->s.ipc.ready);
+	uint32_t ready = odp_atomic_load_u32(&pktio_entry->ops_data(ipc).ready);
 
 	if (ready) {
 		ODP_ABORT("%s Already started\n", pktio_entry->s.name);
 		return -1;
 	}
 
-	if (pktio_entry->s.ipc.type == PKTIO_TYPE_IPC_MASTER)
+	if (pktio_entry->ops_data(ipc).type == PKTIO_TYPE_IPC_MASTER)
 		return _ipc_master_start(pktio_entry);
 	else
 		return _ipc_slave_start(pktio_entry);
@@ -658,20 +657,20 @@ static int ipc_stop(pktio_entry_t *pktio_entry)
 {
 	unsigned tx_send = 0, tx_free = 0;
 
-	odp_atomic_store_u32(&pktio_entry->s.ipc.ready, 0);
+	odp_atomic_store_u32(&pktio_entry->ops_data(ipc).ready, 0);
 
-	if (pktio_entry->s.ipc.tx.send)
-		_ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.send);
+	if (pktio_entry->ops_data(ipc).tx.send)
+		_ipc_free_ring_packets(pktio_entry, pktio_entry->ops_data(ipc).tx.send);
 	/* other process can transfer packets from one ring to
 	 * other, use delay here to free that packets. */
 	sleep(1);
-	if (pktio_entry->s.ipc.tx.free)
-		_ipc_free_ring_packets(pktio_entry, pktio_entry->s.ipc.tx.free);
+	if (pktio_entry->ops_data(ipc).tx.free)
+		_ipc_free_ring_packets(pktio_entry, pktio_entry->ops_data(ipc).tx.free);
 
-	if (pktio_entry->s.ipc.tx.send)
-		tx_send = _ring_count(pktio_entry->s.ipc.tx.send);
-	if (pktio_entry->s.ipc.tx.free)
-		tx_free = _ring_count(pktio_entry->s.ipc.tx.free);
+	if (pktio_entry->ops_data(ipc).tx.send)
+		tx_send = _ring_count(pktio_entry->ops_data(ipc).tx.send);
+	if (pktio_entry->ops_data(ipc).tx.free)
+		tx_free = _ring_count(pktio_entry->ops_data(ipc).tx.free);
 	if (tx_send | tx_free) {
 		ODP_DBG("IPC rings: tx send %d tx free %d\n",
 			tx_send, tx_free);
@@ -690,7 +689,7 @@ static int ipc_close(pktio_entry_t *pktio_entry)
 
 	ipc_stop(pktio_entry);
 
-	odp_shm_free(pktio_entry->s.ipc.remote_pool_shm);
+	odp_shm_free(pktio_entry->ops_data(ipc).remote_pool_shm);
 
 	if (sscanf(dev, "ipc:%d:%s", &pid, tail) == 2)
 		snprintf(name, sizeof(name), "ipc:%s", tail);
@@ -698,7 +697,7 @@ static int ipc_close(pktio_entry_t *pktio_entry)
 		snprintf(name, sizeof(name), "%s", dev);
 
 	/* unlink this pktio info for both master and slave */
-	odp_shm_free(pktio_entry->s.ipc.pinfo_shm);
+	odp_shm_free(pktio_entry->ops_data(ipc).pinfo_shm);
 
 	/* destroy rings */
 	snprintf(ipc_shm_name, sizeof(ipc_shm_name), "%s_s_cons", name);
@@ -720,23 +719,37 @@ static int ipc_pktio_init_global(void)
 	return 0;
 }
 
-const pktio_if_ops_t ipc_pktio_ops = {
+pktio_ops_module_t ipc_pktio_ops = {
 	.name = "ipc",
-	.print = NULL,
-	.init_global = ipc_pktio_init_global,
 	.init_local = NULL,
-	.term = NULL,
+	.term_local = NULL,
+	.init_global = ipc_pktio_init_global,
+	.term_global = NULL,
 	.open = ipc_pktio_open,
 	.close = ipc_close,
-	.recv =  ipc_pktio_recv,
-	.send = ipc_pktio_send,
 	.start = ipc_start,
 	.stop = ipc_stop,
+	.stats = NULL,
+	.stats_reset = NULL,
+	.pktin_ts_res = NULL,
+	.pktin_ts_from_ns = NULL,
+	.recv = ipc_pktio_recv,
+	.send = ipc_pktio_send,
 	.mtu_get = ipc_mtu_get,
 	.promisc_mode_set = NULL,
 	.promisc_mode_get = NULL,
 	.mac_get = ipc_mac_addr_get,
-	.pktin_ts_res = NULL,
-	.pktin_ts_from_ns = NULL,
-	.config = NULL
+	.link_status = NULL,
+	.capability = NULL,
+	.config = NULL,
+	.input_queues_config = NULL,
+	.output_queues_config = NULL,
+	.print = NULL,
 };
+
+MODULE_CONSTRUCTOR(ipc_pktio_ops)
+{
+        INIT_LIST_HEAD(&ipc_pktio_ops.list);
+
+        subsystem_register_module(pktio_ops, &ipc_pktio_ops);
+}
diff --git a/platform/linux-generic/pktio/loop.c b/platform/linux-generic/pktio/loop.c
index c825393a..56567ea5 100644
--- a/platform/linux-generic/pktio/loop.c
+++ b/platform/linux-generic/pktio/loop.c
@@ -35,10 +35,10 @@ static int loopback_open(odp_pktio_t id, pktio_entry_t *pktio_entry,
 
 	snprintf(loopq_name, sizeof(loopq_name), "%" PRIu64 "-pktio_loopq",
 		 odp_pktio_to_u64(id));
-	pktio_entry->s.pkt_loop.loopq =
+	pktio_entry->ops_data(loopback).loopq =
 		odp_queue_create(loopq_name, NULL);
 
-	if (pktio_entry->s.pkt_loop.loopq == ODP_QUEUE_INVALID)
+	if (pktio_entry->ops_data(loopback).loopq == ODP_QUEUE_INVALID)
 		return -1;
 
 	loopback_stats_reset(pktio_entry);
@@ -48,7 +48,7 @@ static int loopback_open(odp_pktio_t id, pktio_entry_t *pktio_entry,
 
 static int loopback_close(pktio_entry_t *pktio_entry)
 {
-	return odp_queue_destroy(pktio_entry->s.pkt_loop.loopq);
+	return odp_queue_destroy(pktio_entry->ops_data(loopback).loopq);
 }
 
 static int loopback_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
@@ -70,7 +70,7 @@ static int loopback_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 
 	odp_ticketlock_lock(&pktio_entry->s.rxl);
 
-	queue = queue_fn->from_ext(pktio_entry->s.pkt_loop.loopq);
+	queue = queue_fn->from_ext(pktio_entry->ops_data(loopback).loopq);
 	nbr = queue_fn->deq_multi(queue, hdr_tbl, len);
 
 	if (pktio_entry->s.config.pktin.bit.ts_all ||
@@ -170,7 +170,7 @@ static int loopback_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 
 	odp_ticketlock_lock(&pktio_entry->s.txl);
 
-	queue = queue_fn->from_ext(pktio_entry->s.pkt_loop.loopq);
+	queue = queue_fn->from_ext(pktio_entry->ops_data(loopback).loopq);
 	ret = queue_fn->enq_multi(queue, hdr_tbl, len);
 
 	if (ret > 0) {
@@ -223,13 +223,13 @@ static int loopback_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
 static int loopback_promisc_mode_set(pktio_entry_t *pktio_entry,
 				     odp_bool_t enable)
 {
-	pktio_entry->s.pkt_loop.promisc = enable;
+	pktio_entry->ops_data(loopback).promisc = enable;
 	return 0;
 }
 
 static int loopback_promisc_mode_get(pktio_entry_t *pktio_entry)
 {
-	return pktio_entry->s.pkt_loop.promisc ? 1 : 0;
+	return pktio_entry->ops_data(loopback).promisc ? 1 : 0;
 }
 
 static int loopback_stats(pktio_entry_t *pktio_entry,
@@ -251,18 +251,20 @@ static int loop_init_global(void)
 	return 0;
 }
 
-const pktio_if_ops_t loopback_pktio_ops = {
-	.name = "loop",
-	.print = NULL,
-	.init_global = loop_init_global,
+pktio_ops_module_t loopback_pktio_ops = {
+	.name = "loopback",
 	.init_local = NULL,
-	.term = NULL,
+	.term_local = NULL,
+	.init_global = loop_init_global,
+	.term_global = NULL,
 	.open = loopback_open,
 	.close = loopback_close,
 	.start = NULL,
 	.stop = NULL,
 	.stats = loopback_stats,
 	.stats_reset = loopback_stats_reset,
+	.pktin_ts_res = NULL,
+	.pktin_ts_from_ns = NULL,
 	.recv = loopback_recv,
 	.send = loopback_send,
 	.mtu_get = loopback_mtu_get,
@@ -271,9 +273,15 @@ const pktio_if_ops_t loopback_pktio_ops = {
 	.mac_get = loopback_mac_addr_get,
 	.link_status = loopback_link_status,
 	.capability = loopback_capability,
-	.pktin_ts_res = NULL,
-	.pktin_ts_from_ns = NULL,
 	.config = NULL,
 	.input_queues_config = NULL,
 	.output_queues_config = NULL,
+	.print = NULL,
 };
+
+MODULE_CONSTRUCTOR(loopback_pktio_ops)
+{
+        INIT_LIST_HEAD(&loopback_pktio_ops.list);
+
+        subsystem_register_module(pktio_ops, &loopback_pktio_ops);
+}
diff --git a/platform/linux-generic/pktio/ops_subsystem.c b/platform/linux-generic/pktio/ops_subsystem.c
new file mode 100644
index 00000000..de2fc3e5
--- /dev/null
+++ b/platform/linux-generic/pktio/ops_subsystem.c
@@ -0,0 +1,19 @@
+/* Copyright (c) 2017, ARM Limited. All rights reserved.
+ *
+ * Copyright (c) 2017, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <odp_packet_io_internal.h>
+
+#define SUBSYSTEM_VERSION 0x00010000UL
+SUBSYSTEM(pktio_ops, "packet IO operations", SUBSYSTEM_VERSION);
+
+SUBSYSTEM_CONSTRUCTOR(pktio_ops)
+{
+        subsystem_constructor(pktio_ops);
+
+        /* Further initialization per subsystem */
+}
diff --git a/platform/linux-generic/pktio/pcap.c b/platform/linux-generic/pktio/pcap.c
index a467b640..7743938b 100644
--- a/platform/linux-generic/pktio/pcap.c
+++ b/platform/linux-generic/pktio/pcap.c
@@ -51,7 +51,7 @@ static const char pcap_mac[] = {0x02, 0xe9, 0x34, 0x80, 0x73, 0x04};
 
 static int pcapif_stats_reset(pktio_entry_t *pktio_entry);
 
-static int _pcapif_parse_devname(pkt_pcap_t *pcap, const char *devname)
+static int _pcapif_parse_devname(pktio_ops_pcap_data_t *pcap, const char *devname)
 {
 	char *tok;
 	char in[PKTIO_NAME_LEN];
@@ -80,7 +80,7 @@ static int _pcapif_parse_devname(pkt_pcap_t *pcap, const char *devname)
 	return 0;
 }
 
-static int _pcapif_init_rx(pkt_pcap_t *pcap)
+static int _pcapif_init_rx(pktio_ops_pcap_data_t *pcap)
 {
 	char errbuf[PCAP_ERRBUF_SIZE];
 	int linktype;
@@ -101,7 +101,7 @@ static int _pcapif_init_rx(pkt_pcap_t *pcap)
 	return 0;
 }
 
-static int _pcapif_init_tx(pkt_pcap_t *pcap)
+static int _pcapif_init_tx(pktio_ops_pcap_data_t *pcap)
 {
 	pcap_t *tx = pcap->rx;
 
@@ -136,10 +136,10 @@ static int _pcapif_init_tx(pkt_pcap_t *pcap)
 static int pcapif_init(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
 		       const char *devname, odp_pool_t pool)
 {
-	pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+	pktio_ops_pcap_data_t *pcap = &pktio_entry->ops_data(pcap);
 	int ret;
 
-	memset(pcap, 0, sizeof(pkt_pcap_t));
+	memset(pcap, 0, sizeof(pktio_ops_pcap_data_t));
 	pcap->loop_cnt = 1;
 	pcap->loops = 1;
 	pcap->pool = pool;
@@ -163,7 +163,7 @@ static int pcapif_init(odp_pktio_t id ODP_UNUSED, pktio_entry_t *pktio_entry,
 
 static int pcapif_close(pktio_entry_t *pktio_entry)
 {
-	pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+	pktio_ops_pcap_data_t *pcap = &pktio_entry->ops_data(pcap);
 
 	if (pcap->tx_dump)
 		pcap_dump_close(pcap->tx_dump);
@@ -181,7 +181,7 @@ static int pcapif_close(pktio_entry_t *pktio_entry)
 	return 0;
 }
 
-static int _pcapif_reopen(pkt_pcap_t *pcap)
+static int _pcapif_reopen(pktio_ops_pcap_data_t *pcap)
 {
 	char errbuf[PCAP_ERRBUF_SIZE];
 
@@ -210,7 +210,7 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 	odp_packet_t pkt;
 	odp_packet_hdr_t *pkt_hdr;
 	uint32_t pkt_len;
-	pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+	pktio_ops_pcap_data_t *pcap = &pktio_entry->ops_data(pcap);
 	odp_time_t ts_val;
 	odp_time_t *ts = NULL;
 
@@ -270,7 +270,7 @@ static int pcapif_recv_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 	return i;
 }
 
-static int _pcapif_dump_pkt(pkt_pcap_t *pcap, odp_packet_t pkt)
+static int _pcapif_dump_pkt(pktio_ops_pcap_data_t *pcap, odp_packet_t pkt)
 {
 	struct pcap_pkthdr hdr;
 
@@ -293,7 +293,7 @@ static int _pcapif_dump_pkt(pkt_pcap_t *pcap, odp_packet_t pkt)
 static int pcapif_send_pkt(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 			   const odp_packet_t pkts[], int len)
 {
-	pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+	pktio_ops_pcap_data_t *pcap = &pktio_entry->ops_data(pcap);
 	int i;
 
 	odp_ticketlock_lock(&pktio_entry->s.txl);
@@ -361,7 +361,7 @@ static int pcapif_promisc_mode_set(pktio_entry_t *pktio_entry,
 {
 	char filter_exp[64] = {0};
 	struct bpf_program bpf;
-	pkt_pcap_t *pcap = &pktio_entry->s.pkt_pcap;
+	pktio_ops_pcap_data_t *pcap = &pktio_entry->ops_data(pcap);
 
 	if (!pcap->rx) {
 		pcap->promisc = enable;
@@ -401,7 +401,7 @@ static int pcapif_promisc_mode_set(pktio_entry_t *pktio_entry,
 
 static int pcapif_promisc_mode_get(pktio_entry_t *pktio_entry)
 {
-	return pktio_entry->s.pkt_pcap.promisc;
+	return pktio_entry->ops_data(pcap).promisc;
 }
 
 static int pcapif_stats_reset(pktio_entry_t *pktio_entry)
@@ -423,25 +423,37 @@ static int pcapif_init_global(void)
 	return 0;
 }
 
-const pktio_if_ops_t pcap_pktio_ops = {
+pktio_ops_module_t pcap_pktio_ops = {
 	.name = "pcap",
-	.print = NULL,
-	.init_global = pcapif_init_global,
 	.init_local = NULL,
+	.term_local = NULL,
+	.init_global = pcapif_init_global,
+	.term_global = NULL,
 	.open = pcapif_init,
 	.close = pcapif_close,
+	.start = NULL,
+	.stop = NULL,
 	.stats = pcapif_stats,
 	.stats_reset = pcapif_stats_reset,
+	.pktin_ts_res = NULL,
+	.pktin_ts_from_ns = NULL,
 	.recv = pcapif_recv_pkt,
 	.send = pcapif_send_pkt,
 	.mtu_get = pcapif_mtu_get,
 	.promisc_mode_set = pcapif_promisc_mode_set,
 	.promisc_mode_get = pcapif_promisc_mode_get,
 	.mac_get = pcapif_mac_addr_get,
+	.link_status = NULL,
 	.capability = pcapif_capability,
-	.pktin_ts_res = NULL,
-	.pktin_ts_from_ns = NULL,
 	.config = NULL,
 	.input_queues_config = NULL,
 	.output_queues_config = NULL,
+	.print = NULL,
 };
+
+MODULE_CONSTRUCTOR(pcap_pktio_ops)
+{
+        INIT_LIST_HEAD(&pcap_pktio_ops.list);
+
+        subsystem_register_module(pktio_ops, &pcap_pktio_ops);
+}
diff --git a/platform/linux-generic/pktio/socket.c b/platform/linux-generic/pktio/socket.c
index a383adc6..91c17812 100644
--- a/platform/linux-generic/pktio/socket.c
+++ b/platform/linux-generic/pktio/socket.c
@@ -33,7 +33,6 @@
 #include <linux/sockios.h>
 
 #include <odp_api.h>
-#include <odp_packet_socket.h>
 #include <odp_packet_internal.h>
 #include <odp_packet_io_internal.h>
 #include <odp_align_internal.h>
@@ -455,8 +454,8 @@ void rss_conf_print(const odp_pktin_hash_proto_t *hash_proto)
  */
 static int sock_close(pktio_entry_t *pktio_entry)
 {
-	pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
-	if (pkt_sock->sockfd != -1 && close(pkt_sock->sockfd) != 0) {
+	pktio_ops_socket_data_t *opdata = &pktio_entry->ops_data(_socket);
+	if (opdata->sockfd != -1 && close(opdata->sockfd) != 0) {
 		__odp_errno = errno;
 		ODP_ERR("close(sockfd): %s\n", strerror(errno));
 		return -1;
@@ -477,17 +476,17 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 	struct ifreq ethreq;
 	struct sockaddr_ll sa_ll;
 	char shm_name[ODP_SHM_NAME_LEN];
-	pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
+	pktio_ops_socket_data_t *opdata = &pktio_entry->ops_data(_socket);
 	odp_pktio_stats_t cur_stats;
 
 	/* Init pktio entry */
-	memset(pkt_sock, 0, sizeof(*pkt_sock));
+	memset(opdata, 0, sizeof(*opdata));
 	/* set sockfd to -1, because a valid socked might be initialized to 0 */
-	pkt_sock->sockfd = -1;
+	opdata->sockfd = -1;
 
 	if (pool == ODP_POOL_INVALID)
 		return -1;
-	pkt_sock->pool = pool;
+	opdata->pool = pool;
 	snprintf(shm_name, ODP_SHM_NAME_LEN, "%s-%s", "pktio", netdev);
 	shm_name[ODP_SHM_NAME_LEN - 1] = '\0';
 
@@ -497,7 +496,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 		ODP_ERR("socket(): %s\n", strerror(errno));
 		goto error;
 	}
-	pkt_sock->sockfd = sockfd;
+	opdata->sockfd = sockfd;
 
 	/* get if index */
 	memset(&ethreq, 0, sizeof(struct ifreq));
@@ -511,12 +510,12 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 	}
 	if_idx = ethreq.ifr_ifindex;
 
-	err = mac_addr_get_fd(sockfd, netdev, pkt_sock->if_mac);
+	err = mac_addr_get_fd(sockfd, netdev, opdata->if_mac);
 	if (err != 0)
 		goto error;
 
-	pkt_sock->mtu = mtu_get_fd(sockfd, netdev);
-	if (!pkt_sock->mtu)
+	opdata->mtu = mtu_get_fd(sockfd, netdev);
+	if (!opdata->mtu)
 		goto error;
 
 	/* bind socket to if */
@@ -530,7 +529,7 @@ static int sock_setup_pkt(pktio_entry_t *pktio_entry, const char *netdev,
 		goto error;
 	}
 
-	err = ethtool_stats_get_fd(pktio_entry->s.pkt_sock.sockfd,
+	err = ethtool_stats_get_fd(pktio_entry->ops_data(_socket).sockfd,
 				   pktio_entry->s.name,
 				   &cur_stats);
 	if (err != 0) {
@@ -601,11 +600,11 @@ static uint32_t _rx_pkt_to_iovec(odp_packet_t pkt,
 static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 			  odp_packet_t pkt_table[], int len)
 {
-	pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
-	odp_pool_t pool = pkt_sock->pool;
+	pktio_ops_socket_data_t *opdata = &pktio_entry->ops_data(_socket);
+	odp_pool_t pool = opdata->pool;
 	odp_time_t ts_val;
 	odp_time_t *ts = NULL;
-	const int sockfd = pkt_sock->sockfd;
+	const int sockfd = opdata->sockfd;
 	struct mmsghdr msgvec[len];
 	struct iovec iovecs[len][MAX_SEGS];
 	int nb_rx = 0;
@@ -621,7 +620,7 @@ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 
 	memset(msgvec, 0, sizeof(msgvec));
 
-	nb_pkts = packet_alloc_multi(pool, pkt_sock->mtu, pkt_table, len);
+	nb_pkts = packet_alloc_multi(pool, opdata->mtu, pkt_table, len);
 	for (i = 0; i < nb_pkts; i++) {
 		msgvec[i].msg_hdr.msg_iovlen =
 			_rx_pkt_to_iovec(pkt_table[i], iovecs[i]);
@@ -656,7 +655,7 @@ static int sock_mmsg_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 		}
 
 		/* Don't receive packets sent by ourselves */
-		if (odp_unlikely(ethaddrs_equal(pkt_sock->if_mac,
+		if (odp_unlikely(ethaddrs_equal(opdata->if_mac,
 						eth_hdr->h_source))) {
 			odp_packet_free(pkt);
 			continue;
@@ -716,7 +715,7 @@ static uint32_t _tx_pkt_to_iovec(odp_packet_t pkt,
 static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 			  const odp_packet_t pkt_table[], int len)
 {
-	pkt_sock_t *pkt_sock = &pktio_entry->s.pkt_sock;
+	pktio_ops_socket_data_t *opdata = &pktio_entry->ops_data(_socket);
 	struct mmsghdr msgvec[len];
 	struct iovec iovecs[len][MAX_SEGS];
 	int ret;
@@ -725,7 +724,7 @@ static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 
 	odp_ticketlock_lock(&pktio_entry->s.txl);
 
-	sockfd = pkt_sock->sockfd;
+	sockfd = opdata->sockfd;
 	memset(msgvec, 0, sizeof(msgvec));
 
 	for (i = 0; i < len; i++) {
@@ -762,7 +761,7 @@ static int sock_mmsg_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
  */
 static uint32_t sock_mtu_get(pktio_entry_t *pktio_entry)
 {
-	return pktio_entry->s.pkt_sock.mtu;
+	return pktio_entry->ops_data(_socket).mtu;
 }
 
 /*
@@ -771,7 +770,7 @@ static uint32_t sock_mtu_get(pktio_entry_t *pktio_entry)
 static int sock_mac_addr_get(pktio_entry_t *pktio_entry,
 			     void *mac_addr)
 {
-	memcpy(mac_addr, pktio_entry->s.pkt_sock.if_mac, ETH_ALEN);
+	memcpy(mac_addr, pktio_entry->ops_data(_socket).if_mac, ETH_ALEN);
 	return ETH_ALEN;
 }
 
@@ -781,7 +780,7 @@ static int sock_mac_addr_get(pktio_entry_t *pktio_entry,
 static int sock_promisc_mode_set(pktio_entry_t *pktio_entry,
 				 odp_bool_t enable)
 {
-	return promisc_mode_set_fd(pktio_entry->s.pkt_sock.sockfd,
+	return promisc_mode_set_fd(pktio_entry->ops_data(_socket).sockfd,
 				   pktio_entry->s.name, enable);
 }
 
@@ -790,13 +789,13 @@ static int sock_promisc_mode_set(pktio_entry_t *pktio_entry,
  */
 static int sock_promisc_mode_get(pktio_entry_t *pktio_entry)
 {
-	return promisc_mode_get_fd(pktio_entry->s.pkt_sock.sockfd,
+	return promisc_mode_get_fd(pktio_entry->ops_data(_socket).sockfd,
 				   pktio_entry->s.name);
 }
 
 static int sock_link_status(pktio_entry_t *pktio_entry)
 {
-	return link_status_fd(pktio_entry->s.pkt_sock.sockfd,
+	return link_status_fd(pktio_entry->ops_data(_socket).sockfd,
 			      pktio_entry->s.name);
 }
 
@@ -825,7 +824,7 @@ static int sock_stats(pktio_entry_t *pktio_entry,
 
 	return sock_stats_fd(pktio_entry,
 			     stats,
-			     pktio_entry->s.pkt_sock.sockfd);
+			     pktio_entry->ops_data(_socket).sockfd);
 }
 
 static int sock_stats_reset(pktio_entry_t *pktio_entry)
@@ -837,7 +836,7 @@ static int sock_stats_reset(pktio_entry_t *pktio_entry)
 	}
 
 	return sock_stats_reset_fd(pktio_entry,
-				   pktio_entry->s.pkt_sock.sockfd);
+				   pktio_entry->ops_data(_socket).sockfd);
 }
 
 static int sock_init_global(void)
@@ -853,18 +852,20 @@ static int sock_init_global(void)
 	return 0;
 }
 
-const pktio_if_ops_t sock_mmsg_pktio_ops = {
+pktio_ops_module_t socket_pktio_ops = {
 	.name = "socket",
-	.print = NULL,
-	.init_global = sock_init_global,
 	.init_local = NULL,
-	.term = NULL,
+	.term_local = NULL,
+	.init_global = sock_init_global,
+	.term_global = NULL,
 	.open = sock_mmsg_open,
 	.close = sock_close,
 	.start = NULL,
 	.stop = NULL,
 	.stats = sock_stats,
 	.stats_reset = sock_stats_reset,
+	.pktin_ts_res = NULL,
+	.pktin_ts_from_ns = NULL,
 	.recv = sock_mmsg_recv,
 	.send = sock_mmsg_send,
 	.mtu_get = sock_mtu_get,
@@ -873,9 +874,15 @@ const pktio_if_ops_t sock_mmsg_pktio_ops = {
 	.mac_get = sock_mac_addr_get,
 	.link_status = sock_link_status,
 	.capability = sock_capability,
-	.pktin_ts_res = NULL,
-	.pktin_ts_from_ns = NULL,
 	.config = NULL,
 	.input_queues_config = NULL,
 	.output_queues_config = NULL,
+	.print = NULL,
 };
+
+MODULE_CONSTRUCTOR(socket_pktio_ops)
+{
+        INIT_LIST_HEAD(&socket_pktio_ops.list);
+
+        subsystem_register_module(pktio_ops, &socket_pktio_ops);
+}
diff --git a/platform/linux-generic/pktio/socket_mmap.c b/platform/linux-generic/pktio/socket_mmap.c
index 2dba7b08..da53e697 100644
--- a/platform/linux-generic/pktio/socket_mmap.c
+++ b/platform/linux-generic/pktio/socket_mmap.c
@@ -23,7 +23,6 @@
 #include <errno.h>
 
 #include <odp_api.h>
-#include <odp_packet_socket.h>
 #include <odp_packet_internal.h>
 #include <odp_packet_io_internal.h>
 #include <odp_debug_internal.h>
@@ -37,10 +36,10 @@
 
 static int disable_pktio; /** !0 this pktio disabled, 0 enabled */
 
-static int set_pkt_sock_fanout_mmap(pkt_sock_mmap_t *const pkt_sock,
-				    int sock_group_idx)
+static int set_fanout_mmap(pktio_ops_socket_mmap_data_t *const opdata,
+			   int sock_group_idx)
 {
-	int sockfd = pkt_sock->sockfd;
+	int sockfd = opdata->sockfd;
 	int val;
 	int err;
 	uint16_t fanout_group;
@@ -144,7 +143,7 @@ static uint8_t *pkt_mmap_vlan_insert(uint8_t *l2_hdr_ptr,
 }
 
 static inline unsigned pkt_mmap_v2_rx(pktio_entry_t *pktio_entry,
-				      pkt_sock_mmap_t *pkt_sock,
+				      pktio_ops_socket_mmap_data_t *opdata,
 				      odp_packet_t pkt_table[], unsigned len,
 				      unsigned char if_mac[])
 {
@@ -164,13 +163,13 @@ static inline unsigned pkt_mmap_v2_rx(pktio_entry_t *pktio_entry,
 	    pktio_entry->s.config.pktin.bit.ts_ptp)
 		ts = &ts_val;
 
-	ring  = &pkt_sock->rx_ring;
+	ring  = &opdata->rx_ring;
 	frame_num = ring->frame_num;
 
 	for (i = 0, nb_rx = 0; i < len; i++) {
 		odp_packet_hdr_t *hdr;
 		odp_packet_hdr_t parsed_hdr;
-		odp_pool_t pool = pkt_sock->pool;
+		odp_pool_t pool = opdata->pool;
 		int num;
 
 		if (!mmap_rx_kernel_ready(ring->rd[frame_num].iov_base))
@@ -411,70 +410,71 @@ static int mmap_setup_ring(int sock, struct ring *ring, int type,
 	return 0;
 }
 
-static int mmap_sock(pkt_sock_mmap_t *pkt_sock)
+static int mmap_sock(pktio_ops_socket_mmap_data_t *opdata)
 {
 	int i;
-	int sock = pkt_sock->sockfd;
+	int sock = opdata->sockfd;
 
 	/* map rx + tx buffer to userspace : they are in this order */
-	pkt_sock->mmap_len =
-		pkt_sock->rx_ring.req.tp_block_size *
-		pkt_sock->rx_ring.req.tp_block_nr +
-		pkt_sock->tx_ring.req.tp_block_size *
-		pkt_sock->tx_ring.req.tp_block_nr;
-
-	pkt_sock->mmap_base =
-		mmap(NULL, pkt_sock->mmap_len, PROT_READ | PROT_WRITE,
+	opdata->mmap_len =
+		opdata->rx_ring.req.tp_block_size *
+		opdata->rx_ring.req.tp_block_nr +
+		opdata->tx_ring.req.tp_block_size *
+		opdata->tx_ring.req.tp_block_nr;
+
+	opdata->mmap_base =
+		mmap(NULL, opdata->mmap_len, PROT_READ | PROT_WRITE,
 		     MAP_SHARED | MAP_LOCKED | MAP_POPULATE, sock, 0);
 
-	if (pkt_sock->mmap_base == MAP_FAILED) {
+	if (opdata->mmap_base == MAP_FAILED) {
 		__odp_errno = errno;
 		ODP_ERR("mmap rx&tx buffer failed: %s\n", strerror(errno));
 		return -1;
 	}
 
-	pkt_sock->rx_ring.mm_space = pkt_sock->mmap_base;
-	memset(pkt_sock->rx_ring.rd, 0, pkt_sock->rx_ring.rd_len);
-	for (i = 0; i < pkt_sock->rx_ring.rd_num; ++i) {
-		pkt_sock->rx_ring.rd[i].iov_base =
-			pkt_sock->rx_ring.mm_space
-			+ (i * pkt_sock->rx_ring.flen);
-		pkt_sock->rx_ring.rd[i].iov_len = pkt_sock->rx_ring.flen;
+	opdata->rx_ring.mm_space = opdata->mmap_base;
+	memset(opdata->rx_ring.rd, 0, opdata->rx_ring.rd_len);
+	for (i = 0; i < opdata->rx_ring.rd_num; ++i) {
+		opdata->rx_ring.rd[i].iov_base =
+			opdata->rx_ring.mm_space
+			+ (i * opdata->rx_ring.flen);
+		opdata->rx_ring.rd[i].iov_len = opdata->rx_ring.flen;
 	}
 
-	pkt_sock->tx_ring.mm_space =
-		pkt_sock->mmap_base + pkt_sock->rx_ring.mm_len;
-	memset(pkt_sock->tx_ring.rd, 0, pkt_sock->tx_ring.rd_len);
-	for (i = 0; i < pkt_sock->tx_ring.rd_num; ++i) {
-		pkt_sock->tx_ring.rd[i].iov_base =
-			pkt_sock->tx_ring.mm_space
-			+ (i * pkt_sock->tx_ring.flen);
-		pkt_sock->tx_ring.rd[i].iov_len = pkt_sock->tx_ring.flen;
+	opdata->tx_ring.mm_space =
+		opdata->mmap_base + opdata->rx_ring.mm_len;
+	memset(opdata->tx_ring.rd, 0, opdata->tx_ring.rd_len);
+	for (i = 0; i < opdata->tx_ring.rd_num; ++i) {
+		opdata->tx_ring.rd[i].iov_base =
+			opdata->tx_ring.mm_space
+			+ (i * opdata->tx_ring.flen);
+		opdata->tx_ring.rd[i].iov_len = opdata->tx_ring.flen;
 	}
 
 	return 0;
 }
 
-static int mmap_unmap_sock(pkt_sock_mmap_t *pkt_sock)
+static int mmap_unmap_sock(pktio_ops_socket_mmap_data_t *opdata)
 {
-	free(pkt_sock->rx_ring.rd);
-	free(pkt_sock->tx_ring.rd);
-	return munmap(pkt_sock->mmap_base, pkt_sock->mmap_len);
+	free(opdata->rx_ring.rd);
+	free(opdata->tx_ring.rd);
+	return munmap(opdata->mmap_base, opdata->mmap_len);
 }
 
-static int mmap_bind_sock(pkt_sock_mmap_t *pkt_sock, const char *netdev)
+static int mmap_bind_sock(pktio_ops_socket_mmap_data_t *opdata,
+			  const char *netdev)
 {
 	int ret;
 
-	pkt_sock->ll.sll_family = PF_PACKET;
-	pkt_sock->ll.sll_protocol = htons(ETH_P_ALL);
-	pkt_sock->ll.sll_ifindex = if_nametoindex(netdev);
-	pkt_sock->ll.sll_hatype = 0;
-	pkt_sock->ll.sll_pkttype = 0;
-	pkt_sock->ll.sll_halen = 0;
+	opdata->ll.sll_family = PF_PACKET;
+	opdata->ll.sll_protocol = htons(ETH_P_ALL);
+	opdata->ll.sll_ifindex = if_nametoindex(netdev);
+	opdata->ll.sll_hatype = 0;
+	opdata->ll.sll_pkttype = 0;
+	opdata->ll.sll_halen = 0;
 
-	ret = bind(pkt_sock->sockfd, (struct sockaddr *)&pkt_sock->ll,
-		   sizeof(pkt_sock->ll));
+	ret = bind(opdata->sockfd, (struct sockaddr *)&opdata->ll,
+		   sizeof(opdata->ll));
 	if (ret == -1) {
 		__odp_errno = errno;
 		ODP_ERR("bind(to IF): %s\n", strerror(errno));
@@ -486,16 +486,17 @@ static int mmap_bind_sock(pkt_sock_mmap_t *pkt_sock, const char *netdev)
 
 static int sock_mmap_close(pktio_entry_t *entry)
 {
-	pkt_sock_mmap_t *const pkt_sock = &entry->s.pkt_sock_mmap;
+	pktio_ops_socket_mmap_data_t *const
+		opdata = &entry->ops_data(_mmap);
 	int ret;
 
-	ret = mmap_unmap_sock(pkt_sock);
+	ret = mmap_unmap_sock(opdata);
 	if (ret != 0) {
 		ODP_ERR("mmap_unmap_sock() %s\n", strerror(errno));
 		return -1;
 	}
 
-	if (pkt_sock->sockfd != -1 && close(pkt_sock->sockfd) != 0) {
+	if (opdata->sockfd != -1 && close(opdata->sockfd) != 0) {
 		__odp_errno = errno;
 		ODP_ERR("close(sockfd): %s\n", strerror(errno));
 		return -1;
@@ -515,44 +516,45 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
 	if (disable_pktio)
 		return -1;
 
-	pkt_sock_mmap_t *const pkt_sock = &pktio_entry->s.pkt_sock_mmap;
+	pktio_ops_socket_mmap_data_t *const
+		opdata = &pktio_entry->ops_data(_mmap);
 	int fanout = 1;
 
 	/* Init pktio entry */
-	memset(pkt_sock, 0, sizeof(*pkt_sock));
+	memset(opdata, 0, sizeof(*opdata));
 	/* set sockfd to -1, because a valid socked might be initialized to 0 */
-	pkt_sock->sockfd = -1;
+	opdata->sockfd = -1;
 
 	if (pool == ODP_POOL_INVALID)
 		return -1;
 
 	/* Store eth buffer offset for pkt buffers from this pool */
-	pkt_sock->frame_offset = 0;
+	opdata->frame_offset = 0;
 
-	pkt_sock->pool = pool;
-	pkt_sock->sockfd = mmap_pkt_socket();
-	if (pkt_sock->sockfd == -1)
+	opdata->pool = pool;
+	opdata->sockfd = mmap_pkt_socket();
+	if (opdata->sockfd == -1)
 		goto error;
 
-	ret = mmap_bind_sock(pkt_sock, netdev);
+	ret = mmap_bind_sock(opdata, netdev);
 	if (ret != 0)
 		goto error;
 
-	ret = mmap_setup_ring(pkt_sock->sockfd, &pkt_sock->tx_ring,
+	ret = mmap_setup_ring(opdata->sockfd, &opdata->tx_ring,
 			      PACKET_TX_RING, pool, fanout);
 	if (ret != 0)
 		goto error;
 
-	ret = mmap_setup_ring(pkt_sock->sockfd, &pkt_sock->rx_ring,
+	ret = mmap_setup_ring(opdata->sockfd, &opdata->rx_ring,
 			      PACKET_RX_RING, pool, fanout);
 	if (ret != 0)
 		goto error;
 
-	ret = mmap_sock(pkt_sock);
+	ret = mmap_sock(opdata);
 	if (ret != 0)
 		goto error;
 
-	ret = mac_addr_get_fd(pkt_sock->sockfd, netdev, pkt_sock->if_mac);
+	ret = mac_addr_get_fd(opdata->sockfd, netdev, opdata->if_mac);
 	if (ret != 0)
 		goto error;
 
@@ -563,14 +565,14 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
 		goto error;
 	}
 
-	pkt_sock->fanout = fanout;
+	opdata->fanout = fanout;
 	if (fanout) {
-		ret = set_pkt_sock_fanout_mmap(pkt_sock, if_idx);
+		ret = set_fanout_mmap(opdata, if_idx);
 		if (ret != 0)
 			goto error;
 	}
 
-	ret = ethtool_stats_get_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
+	ret = ethtool_stats_get_fd(pktio_entry->ops_data(_mmap).sockfd,
 				   pktio_entry->s.name,
 				   &cur_stats);
 	if (ret != 0) {
@@ -587,7 +589,7 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
 	}
 
 	ret = sock_stats_reset_fd(pktio_entry,
-				  pktio_entry->s.pkt_sock_mmap.sockfd);
+				  pktio_entry->ops_data(_mmap).sockfd);
 	if (ret != 0)
 		goto error;
 
@@ -601,12 +603,13 @@ static int sock_mmap_open(odp_pktio_t id ODP_UNUSED,
 static int sock_mmap_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 			  odp_packet_t pkt_table[], int len)
 {
-	pkt_sock_mmap_t *const pkt_sock = &pktio_entry->s.pkt_sock_mmap;
+	pktio_ops_socket_mmap_data_t *const
+		opdata = &pktio_entry->ops_data(_mmap);
 	int ret;
 
 	odp_ticketlock_lock(&pktio_entry->s.rxl);
-	ret = pkt_mmap_v2_rx(pktio_entry, pkt_sock, pkt_table, len,
-			     pkt_sock->if_mac);
+	ret = pkt_mmap_v2_rx(pktio_entry, opdata, pkt_table, len,
+			     opdata->if_mac);
 	odp_ticketlock_unlock(&pktio_entry->s.rxl);
 
 	return ret;
@@ -616,10 +619,11 @@ static int sock_mmap_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 			  const odp_packet_t pkt_table[], int len)
 {
 	int ret;
-	pkt_sock_mmap_t *const pkt_sock = &pktio_entry->s.pkt_sock_mmap;
+	pktio_ops_socket_mmap_data_t *const
+		opdata = &pktio_entry->ops_data(_mmap);
 
 	odp_ticketlock_lock(&pktio_entry->s.txl);
-	ret = pkt_mmap_v2_tx(pkt_sock->tx_ring.sock, &pkt_sock->tx_ring,
+	ret = pkt_mmap_v2_tx(opdata->tx_ring.sock, &opdata->tx_ring,
 			     pkt_table, len);
 	odp_ticketlock_unlock(&pktio_entry->s.txl);
 
@@ -628,32 +632,32 @@ static int sock_mmap_send(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 
 static uint32_t sock_mmap_mtu_get(pktio_entry_t *pktio_entry)
 {
-	return mtu_get_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
+	return mtu_get_fd(pktio_entry->ops_data(_mmap).sockfd,
 			  pktio_entry->s.name);
 }
 
 static int sock_mmap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
 {
-	memcpy(mac_addr, pktio_entry->s.pkt_sock_mmap.if_mac, ETH_ALEN);
+	memcpy(mac_addr, pktio_entry->ops_data(_mmap).if_mac, ETH_ALEN);
 	return ETH_ALEN;
 }
 
 static int sock_mmap_promisc_mode_set(pktio_entry_t *pktio_entry,
 				      odp_bool_t enable)
 {
-	return promisc_mode_set_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
+	return promisc_mode_set_fd(pktio_entry->ops_data(_mmap).sockfd,
 				   pktio_entry->s.name, enable);
 }
 
 static int sock_mmap_promisc_mode_get(pktio_entry_t *pktio_entry)
 {
-	return promisc_mode_get_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
+	return promisc_mode_get_fd(pktio_entry->ops_data(_mmap).sockfd,
 				   pktio_entry->s.name);
 }
 
 static int sock_mmap_link_status(pktio_entry_t *pktio_entry)
 {
-	return link_status_fd(pktio_entry->s.pkt_sock_mmap.sockfd,
+	return link_status_fd(pktio_entry->ops_data(_mmap).sockfd,
 			      pktio_entry->s.name);
 }
 
@@ -682,7 +686,7 @@ static int sock_mmap_stats(pktio_entry_t *pktio_entry,
 
 	return sock_stats_fd(pktio_entry,
 			     stats,
-			     pktio_entry->s.pkt_sock_mmap.sockfd);
+			     pktio_entry->ops_data(_mmap).sockfd);
 }
 
 static int sock_mmap_stats_reset(pktio_entry_t *pktio_entry)
@@ -694,7 +698,7 @@ static int sock_mmap_stats_reset(pktio_entry_t *pktio_entry)
 	}
 
 	return sock_stats_reset_fd(pktio_entry,
-				   pktio_entry->s.pkt_sock_mmap.sockfd);
+				   pktio_entry->ops_data(_mmap).sockfd);
 }
 
 static int sock_mmap_init_global(void)
@@ -710,18 +714,20 @@ static int sock_mmap_init_global(void)
 	return 0;
 }
 
-const pktio_if_ops_t sock_mmap_pktio_ops = {
-	.name = "socket_mmap",
-	.print = NULL,
-	.init_global = sock_mmap_init_global,
+pktio_ops_module_t socket_mmap_pktio_ops = {
+	.name = "socket mmap",
 	.init_local = NULL,
-	.term = NULL,
+	.term_local = NULL,
+	.init_global = sock_mmap_init_global,
+	.term_global = NULL,
 	.open = sock_mmap_open,
 	.close = sock_mmap_close,
 	.start = NULL,
 	.stop = NULL,
 	.stats = sock_mmap_stats,
 	.stats_reset = sock_mmap_stats_reset,
+	.pktin_ts_res = NULL,
+	.pktin_ts_from_ns = NULL,
 	.recv = sock_mmap_recv,
 	.send = sock_mmap_send,
 	.mtu_get = sock_mmap_mtu_get,
@@ -730,9 +736,15 @@ const pktio_if_ops_t sock_mmap_pktio_ops = {
 	.mac_get = sock_mmap_mac_addr_get,
 	.link_status = sock_mmap_link_status,
 	.capability = sock_mmap_capability,
-	.pktin_ts_res = NULL,
-	.pktin_ts_from_ns = NULL,
 	.config = NULL,
 	.input_queues_config = NULL,
 	.output_queues_config = NULL,
+	.print = NULL,
 };
+
+MODULE_CONSTRUCTOR(socket_mmap_pktio_ops)
+{
+        INIT_LIST_HEAD(&socket_mmap_pktio_ops.list);
+
+        subsystem_register_module(pktio_ops, &socket_mmap_pktio_ops);
+}
diff --git a/platform/linux-generic/pktio/tap.c b/platform/linux-generic/pktio/tap.c
index 650c12a7..88884f07 100644
--- a/platform/linux-generic/pktio/tap.c
+++ b/platform/linux-generic/pktio/tap.c
@@ -40,7 +40,6 @@
 #include <linux/if_tun.h>
 
 #include <odp_api.h>
-#include <odp_packet_socket.h>
 #include <odp_packet_internal.h>
 #include <odp_packet_io_internal.h>
 #include <odp_classification_internal.h>
@@ -64,15 +63,15 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
 	int fd, skfd, flags;
 	uint32_t mtu;
 	struct ifreq ifr;
-	pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+	pktio_ops_tap_data_t *opdata = &pktio_entry->ops_data(tap);
 
 	if (strncmp(devname, "tap:", 4) != 0)
 		return -1;
 
 	/* Init pktio entry */
-	memset(tap, 0, sizeof(*tap));
-	tap->fd = -1;
-	tap->skfd = -1;
+	memset(opdata, 0, sizeof(*opdata));
+	opdata->fd = -1;
+	opdata->skfd = -1;
 
 	if (pool == ODP_POOL_INVALID)
 		return -1;
@@ -114,7 +113,7 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
 		goto tap_err;
 	}
 
-	if (gen_random_mac(tap->if_mac) < 0)
+	if (gen_random_mac(opdata->if_mac) < 0)
 		goto tap_err;
 
 	/* Create AF_INET socket for network interface related operations. */
@@ -148,10 +147,10 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
 		goto sock_err;
 	}
 
-	tap->fd = fd;
-	tap->skfd = skfd;
-	tap->mtu = mtu;
-	tap->pool = pool;
+	opdata->fd = fd;
+	opdata->skfd = skfd;
+	opdata->mtu = mtu;
+	opdata->pool = pool;
 	return 0;
 sock_err:
 	close(skfd);
@@ -164,15 +163,15 @@ static int tap_pktio_open(odp_pktio_t id ODP_UNUSED,
 static int tap_pktio_close(pktio_entry_t *pktio_entry)
 {
 	int ret = 0;
-	pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+	pktio_ops_tap_data_t *opdata = &pktio_entry->ops_data(tap);
 
-	if (tap->fd != -1 && close(tap->fd) != 0) {
+	if (opdata->fd != -1 && close(opdata->fd) != 0) {
 		__odp_errno = errno;
 		ODP_ERR("close(tap->fd): %s\n", strerror(errno));
 		ret = -1;
 	}
 
-	if (tap->skfd != -1 && close(tap->skfd) != 0) {
+	if (opdata->skfd != -1 && close(opdata->skfd) != 0) {
 		__odp_errno = errno;
 		ODP_ERR("close(tap->skfd): %s\n", strerror(errno));
 		ret = -1;
@@ -191,13 +190,13 @@ static odp_packet_t pack_odp_pkt(pktio_entry_t *pktio_entry, const void *data,
 
 	if (pktio_cls_enabled(pktio_entry)) {
 		if (cls_classify_packet(pktio_entry, data, len, len,
-					&pktio_entry->s.pkt_tap.pool,
+					&pktio_entry->ops_data(tap).pool,
 					&parsed_hdr)) {
 			return ODP_PACKET_INVALID;
 		}
 	}
 
-	num = packet_alloc_multi(pktio_entry->s.pkt_tap.pool, len, &pkt, 1);
+	num = packet_alloc_multi(pktio_entry->ops_data(tap).pool, len, &pkt, 1);
 
 	if (num != 1)
 		return ODP_PACKET_INVALID;
@@ -228,7 +227,7 @@ static int tap_pktio_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 	ssize_t retval;
 	int i;
 	uint8_t buf[BUF_SIZE];
-	pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+	pktio_ops_tap_data_t *opdata = &pktio_entry->ops_data(tap);
 	odp_time_t ts_val;
 	odp_time_t *ts = NULL;
 
@@ -240,7 +239,7 @@ static int tap_pktio_recv(pktio_entry_t *pktio_entry, int index ODP_UNUSED,
 
 	for (i = 0; i < len; i++) {
 		do {
-			retval = read(tap->fd, buf, BUF_SIZE);
+			retval = read(opdata->fd, buf, BUF_SIZE);
 		} while (retval < 0 && errno == EINTR);
 
 		if (ts != NULL)
@@ -268,12 +267,12 @@ static int tap_pktio_send_lockless(pktio_entry_t *pktio_entry,
 	int i, n;
 	uint32_t pkt_len;
 	uint8_t buf[BUF_SIZE];
-	pkt_tap_t *tap = &pktio_entry->s.pkt_tap;
+	pktio_ops_tap_data_t *opdata = &pktio_entry->ops_data(tap);
 
 	for (i = 0; i < len; i++) {
 		pkt_len = odp_packet_len(pkts[i]);
 
-		if (pkt_len > tap->mtu) {
+		if (pkt_len > opdata->mtu) {
 			if (i == 0) {
 				__odp_errno = EMSGSIZE;
 				return -1;
@@ -287,7 +286,7 @@ static int tap_pktio_send_lockless(pktio_entry_t *pktio_entry,
 		}
 
 		do {
-			retval = write(tap->fd, buf, pkt_len);
+			retval = write(opdata->fd, buf, pkt_len);
 		} while (retval < 0 && errno == EINTR);
 
 		if (retval < 0) {
@@ -331,10 +330,10 @@ static uint32_t tap_mtu_get(pktio_entry_t *pktio_entry)
 {
 	uint32_t ret;
 
-	ret =  mtu_get_fd(pktio_entry->s.pkt_tap.skfd,
+	ret =  mtu_get_fd(pktio_entry->ops_data(tap).skfd,
 			  pktio_entry->s.name + 4);
 	if (ret > 0)
-		pktio_entry->s.pkt_tap.mtu = ret;
+		pktio_entry->ops_data(tap).mtu = ret;
 
 	return ret;
 }
@@ -342,19 +341,19 @@ static uint32_t tap_mtu_get(pktio_entry_t *pktio_entry)
 static int tap_promisc_mode_set(pktio_entry_t *pktio_entry,
 				odp_bool_t enable)
 {
-	return promisc_mode_set_fd(pktio_entry->s.pkt_tap.skfd,
+	return promisc_mode_set_fd(pktio_entry->ops_data(tap).skfd,
 				   pktio_entry->s.name + 4, enable);
 }
 
 static int tap_promisc_mode_get(pktio_entry_t *pktio_entry)
 {
-	return promisc_mode_get_fd(pktio_entry->s.pkt_tap.skfd,
+	return promisc_mode_get_fd(pktio_entry->ops_data(tap).skfd,
 				   pktio_entry->s.name + 4);
 }
 
 static int tap_mac_addr_get(pktio_entry_t *pktio_entry, void *mac_addr)
 {
-	memcpy(mac_addr, pktio_entry->s.pkt_tap.if_mac, ETH_ALEN);
+	memcpy(mac_addr, pktio_entry->ops_data(tap).if_mac, ETH_ALEN);
 	return ETH_ALEN;
 }
 
@@ -373,24 +372,37 @@ static int tap_capability(pktio_entry_t *pktio_entry ODP_UNUSED,
 	return 0;
 }
 
-const pktio_if_ops_t tap_pktio_ops = {
+pktio_ops_module_t tap_pktio_ops = {
 	.name = "tap",
-	.print = NULL,
-	.init_global = NULL,
 	.init_local = NULL,
-	.term = NULL,
+	.term_local = NULL,
+	.init_global = NULL,
+	.term_global = NULL,
 	.open = tap_pktio_open,
 	.close = tap_pktio_close,
 	.start = NULL,
 	.stop = NULL,
+	.stats = NULL,
+	.stats_reset = NULL,
+	.pktin_ts_res = NULL,
+	.pktin_ts_from_ns = NULL,
 	.recv = tap_pktio_recv,
 	.send = tap_pktio_send,
 	.mtu_get = tap_mtu_get,
 	.promisc_mode_set = tap_promisc_mode_set,
 	.promisc_mode_get = tap_promisc_mode_get,
 	.mac_get = tap_mac_addr_get,
+	.link_status = NULL,
 	.capability = tap_capability,
-	.pktin_ts_res = NULL,
-	.pktin_ts_from_ns = NULL,
-	.config = NULL
+	.config = NULL,
+	.input_queues_config = NULL,
+	.output_queues_config = NULL,
+	.print = NULL,
 };
+
+MODULE_CONSTRUCTOR(tap_pktio_ops)
+{
+        INIT_LIST_HEAD(&tap_pktio_ops.list);
+
+        subsystem_register_module(pktio_ops, &tap_pktio_ops);
+}
