diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am
index 271f5b5..cb7c8e9 100644
--- a/platform/linux-generic/Makefile.am
+++ b/platform/linux-generic/Makefile.am
@@ -112,6 +112,7 @@ odpdrvplatinclude_HEADERS = \
 		  $(srcdir)/include/odp/drv/plat/strong_types.h
 
 noinst_HEADERS = \
+		  ${srcdir}/include/_fdserver_internal.h \
 		  ${srcdir}/include/odp_align_internal.h \
 		  ${srcdir}/include/odp_atomic_internal.h \
 		  ${srcdir}/include/odp_buffer_inlines.h \
@@ -154,6 +155,7 @@ noinst_HEADERS = \
 		  ${srcdir}/Makefile.inc
 
 __LIB__libodp_linux_la_SOURCES = \
+			   _fdserver.c \
 			   odp_atomic.c \
 			   odp_barrier.c \
 			   odp_buffer.c \
diff --git a/platform/linux-generic/_fdserver.c b/platform/linux-generic/_fdserver.c
new file mode 100644
index 0000000..bf36eb2
--- /dev/null
+++ b/platform/linux-generic/_fdserver.c
@@ -0,0 +1,655 @@
+/* Copyright (c) 2016, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+/*
+ * This file implements a file descriptor sharing server enabling
+ * sharing of file descriptors between processes, regardless of fork time.
+ *
+ * File descriptors are process scoped, but they can be "sent and converted
+ * on the fly" between processes using special unix domain socket ancillary
+ * data.
+ * The receiving process gets a file descriptor "pointing" to the same thing
+ * as the one sent (but the value of the file descriptor itself may be different
+ * from the one sent).
+ * Because ODP applications are responsible for creating ODP threads (i.e.
+ * pthreads or linux processes), ODP has no control on the order things happen:
+ * Nothing prevent a thread A to fork B and C, and then C creating a pktio
+ * which will be used by A and B to send/receive packets.
+ * Assuming this pktio uses a file descriptor, the latter will need to be
+ * shared between the processes, despite the "non convenient" fork time.
+ * The shared memory allocator is likely to use this as well to be able to
+ * share memory regardless of fork() time.
+ * This server handles a table of {(context,key)<-> fd} pair, and is
+ * interfaced by the following functions:
+ *
+ * _odp_fdserver_register_fd(context, key, fd_to_send);
+ * _odp_fdserver_deregister_fd(context, key);
+ * _odp_fdserver_lookup_fd(context, key);
+ *
+ * which are used to register/deregister or querry for file descriptor based
+ * on a context and key value couple, which has to be unique.
+ *
+ * Note again that the file descriptors stored here are local to this server
+ * process and get converted both when registered or looked up.
+ */
+
+#include <odp_posix_extensions.h>
+#include <odp/api/spinlock.h>
+#include <odp_internal.h>
+#include <odp_debug_internal.h>
+#include <_fdserver_internal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+
+#define FDSERVER_SOCKPATH_MAXLEN 32
+#define FDSERVER_SOCKPATH_FORMAT "/tmp/odp-%d-fdserver"
+#define FDSERVER_BACKLOG 5
+
+#ifndef MAP_ANONYMOUS
+#define MAP_ANONYMOUS MAP_ANON
+#endif
+
+/* when accessing the client functions, clients should be mutexed: */
+odp_spinlock_t *client_lock;
+
+/* define the tables of file descriptors handled by this server: */
+#define FDSERVER_MAX_ENTRIES 256
+typedef struct fdentry_s {
+	fd_server_context_e context;
+	uint64_t key;
+	int  fd;
+} fdentry_t;
+static fdentry_t fd_table[FDSERVER_MAX_ENTRIES];
+static int fd_table_nb_entries;
+
+/*
+ * define the message struct used for communication between client and server
+ * (this single message is used in both direction)
+ * The file descriptors are sent out of band as ancillary data for conversion.
+ */
+typedef struct fd_server_msg {
+	int command;
+	fd_server_context_e context;
+	uint64_t key;
+} fdserver_msg_t;
+/* possible commands are: */
+#define FD_REGISTER_REQ		1  /* client -> server */
+#define FD_REGISTER_ACK		2  /* server -> client */
+#define FD_REGISTER_NACK	3  /* server -> client */
+#define FD_LOOKUP_REQ		4  /* client -> server */
+#define FD_LOOKUP_ACK		5  /* server -> client */
+#define FD_LOOKUP_NACK		6  /* server -> client */
+#define FD_DEREGISTER_REQ	7  /* client -> server */
+#define FD_DEREGISTER_ACK	8  /* server -> client */
+#define FD_DEREGISTER_NACK	9  /* server -> client */
+#define FD_SERVERSTOP_REQ	10 /* client -> server (stops) */
+
+/*
+ * Client and server function:
+ * Send a fdserver_msg, possibly including a file descriptor, on the socket
+ * This function is used both by:
+ * -the client (sending a FD_REGISTER_REQ with a file descriptor to be shared,
+ *  or FD_LOOKUP_REQ/FD_DEREGISTER_REQ without a file descirptor)
+ * -the server (sending FD_REGISTER_ACK/NACK, FD_LOOKUP_NACK,
+ *  FD_DEREGISTER_ACK/NACK... without a fd or a
+ *  FD_LOOKUP_ACK with a fd)
+ * This function make use of the ancillary data (control data) to pass and
+ * convert file descriptors over UNIX sockets
+ * Return -1 on error, 0 on success.
+ */
+static int send_fdserver_msg(int sock, int command,
+			     fd_server_context_e context, uint64_t key,
+			     int fd_to_send)
+{
+	struct msghdr socket_message;
+	struct iovec io_vector[1]; /* one msg frgmt only */
+	struct cmsghdr *control_message = NULL;
+	int *fd_location;
+	fdserver_msg_t msg;
+	int res;
+
+	char ancillary_data[CMSG_SPACE(sizeof(int))];
+
+	/* prepare the register request body (single framgent): */
+	msg.command = command;
+	msg.context = context;
+	msg.key = key;
+	io_vector[0].iov_base = &msg;
+	io_vector[0].iov_len = sizeof(fdserver_msg_t);
+
+	/* initialize socket message */
+	memset(&socket_message, 0, sizeof(struct msghdr));
+	socket_message.msg_iov = io_vector;
+	socket_message.msg_iovlen = 1;
+
+	if (fd_to_send >= 0) {
+		/* provide space for the ancillary data */
+		memset(ancillary_data, 0, CMSG_SPACE(sizeof(int)));
+		socket_message.msg_control = ancillary_data;
+		socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+		/* initialize a single ancillary data element for fd passing */
+		control_message = CMSG_FIRSTHDR(&socket_message);
+		control_message->cmsg_level = SOL_SOCKET;
+		control_message->cmsg_type = SCM_RIGHTS;
+		control_message->cmsg_len = CMSG_LEN(sizeof(int));
+		fd_location = (int *)(void *)CMSG_DATA(control_message);
+		*fd_location = fd_to_send;
+	}
+	res = sendmsg(sock, &socket_message, 0);
+	if (res < 0) {
+		ODP_ERR("send_fdserver_msg: %s\n", strerror(errno));
+		return(-1);
+	}
+
+	return 0;
+}
+
+/*
+ * Client and server function
+ * Receive a fdserver_msg, possibly including a file descriptor, on the
+ * given socket.
+ * This function is used both by:
+ * -the server (receiving a FD_REGISTER_REQ with a file descriptor to be shared,
+ *  or FD_LOOKUP_REQ, FD_DEREGISTER_REQ without a file descirptor)
+ * -the client (receiving FD_REGISTER_ACK...without a fd or a FD_LOOKUP_ACK with
+ * a fd)
+ * This function make use of the ancillary data (control data) to pass and
+ * convert file descriptors over UNIX sockets.
+ * Return -1 on error, 0 on success.
+ */
+static int recv_fdserver_msg(int sock, int *command,
+			     fd_server_context_e *context, uint64_t *key,
+			     int *recvd_fd)
+{
+	struct msghdr socket_message;
+	struct iovec io_vector[1]; /* one msg frgmt only */
+	struct cmsghdr *control_message = NULL;
+	int *fd_location;
+	fdserver_msg_t msg;
+	char ancillary_data[CMSG_SPACE(sizeof(int))];
+
+	memset(&socket_message, 0, sizeof(struct msghdr));
+	memset(ancillary_data, 0, CMSG_SPACE(sizeof(int)));
+
+	/* setup a place to fill in message contents */
+	io_vector[0].iov_base = &msg;
+	io_vector[0].iov_len = sizeof(fdserver_msg_t);
+	socket_message.msg_iov = io_vector;
+	socket_message.msg_iovlen = 1;
+
+	/* provide space for the ancillary data */
+	socket_message.msg_control = ancillary_data;
+	socket_message.msg_controllen = CMSG_SPACE(sizeof(int));
+
+	/* receive the message */
+	if (recvmsg(sock, &socket_message, MSG_CMSG_CLOEXEC) < 0) {
+		ODP_ERR("recv_fdserver_msg: %s\n", strerror(errno));
+		return(-1);
+	}
+
+	*command = msg.command;
+	*context = msg.context;
+	*key = msg.key;
+
+	/* grab the converted file descriptor (if any) */
+	*recvd_fd = -1;
+
+	if ((socket_message.msg_flags & MSG_CTRUNC) == MSG_CTRUNC)
+		return 0;
+
+	/* iterate ancillary elements to find the file descriptor: */
+	for (control_message = CMSG_FIRSTHDR(&socket_message);
+	     control_message != NULL;
+	     control_message = CMSG_NXTHDR(&socket_message, control_message)) {
+		if ((control_message->cmsg_level == SOL_SOCKET) &&
+		    (control_message->cmsg_type == SCM_RIGHTS)) {
+			fd_location = (int *)(void *)CMSG_DATA(control_message);
+			*recvd_fd = *fd_location;
+			break;
+		}
+	}
+
+	return 0;
+}
+
+/* opens and returns a connected socket to the server */
+static int get_socket(void)
+{
+	char sockpath[FDSERVER_SOCKPATH_MAXLEN];
+	int s_sock; /* server socket */
+	struct sockaddr_un remote;
+	int len;
+
+	/* construct the named socket path: */
+	snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCKPATH_FORMAT,
+		 odp_global_data.main_pid);
+
+	s_sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (s_sock == -1) {
+		ODP_ERR("cannot connect to server: %s\n", strerror(errno));
+		return(-1);
+	}
+
+	remote.sun_family = AF_UNIX;
+	strcpy(remote.sun_path, sockpath);
+	len = strlen(remote.sun_path) + sizeof(remote.sun_family);
+	if (connect(s_sock, (struct sockaddr *)&remote, len) == -1) {
+		ODP_ERR("cannot connect to server: %s\n", strerror(errno));
+		close(s_sock);
+		return(-1);
+	}
+
+	return s_sock;
+}
+
+/*
+ * Client function:
+ * Register a file descriptor to the server. Return -1 on error.
+ */
+int _odp_fdserver_register_fd(fd_server_context_e context, uint64_t key,
+			      int fd_to_send)
+{
+	int s_sock; /* server socket */
+	int res;
+	int command;
+	int fd;
+
+	odp_spinlock_lock(client_lock);
+
+	ODP_DBG("FD client register: pid=%d key=%" PRIu64 ", fd=%d\n",
+		getpid(), key, fd_to_send);
+
+	s_sock = get_socket();
+	if (s_sock < 0) {
+		odp_spinlock_unlock(client_lock);
+		return(-1);
+	}
+
+	res =  send_fdserver_msg(s_sock, FD_REGISTER_REQ, context, key,
+				 fd_to_send);
+	if (res < 0) {
+		ODP_ERR("fd registration failure\n");
+		close(s_sock);
+		odp_spinlock_unlock(client_lock);
+		return -1;
+	}
+
+	res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
+
+	if ((res < 0) || (command != FD_REGISTER_ACK)) {
+		ODP_ERR("fd registration failure\n");
+		close(s_sock);
+		odp_spinlock_unlock(client_lock);
+		return -1;
+	}
+
+	close(s_sock);
+
+	odp_spinlock_unlock(client_lock);
+	return 0;
+}
+
+/*
+ * Client function:
+ * Deregister a file descriptor from the server. Return -1 on error.
+ */
+int _odp_fdserver_deregister_fd(fd_server_context_e context, uint64_t key)
+{
+	int s_sock; /* server socket */
+	int res;
+	int command;
+	int fd;
+
+	odp_spinlock_lock(client_lock);
+
+	ODP_DBG("FD client deregister: pid=%d key=%" PRIu64 "\n",
+		getpid(), key);
+
+	s_sock = get_socket();
+	if (s_sock < 0) {
+		odp_spinlock_unlock(client_lock);
+		return(-1);
+	}
+
+	res =  send_fdserver_msg(s_sock, FD_DEREGISTER_REQ, context, key, -1);
+	if (res < 0) {
+		ODP_ERR("fd de-registration failure\n");
+		close(s_sock);
+		odp_spinlock_unlock(client_lock);
+		return -1;
+	}
+
+	res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
+
+	if ((res < 0) || (command != FD_DEREGISTER_ACK)) {
+		ODP_ERR("fd de-registration failure\n");
+		close(s_sock);
+		odp_spinlock_unlock(client_lock);
+		return -1;
+	}
+
+	close(s_sock);
+
+	odp_spinlock_unlock(client_lock);
+	return 0;
+}
+
+/*
+ * client function:
+ * lookup a file descriptor from the server. return -1 on error,
+ * or the file descriptor on success (>=0).
+ */
+int _odp_fdserver_lookup_fd(fd_server_context_e context, uint64_t key)
+{
+	int s_sock; /* server socket */
+	int res;
+	int command;
+	int fd;
+
+	odp_spinlock_lock(client_lock);
+
+	s_sock = get_socket();
+	if (s_sock < 0) {
+		odp_spinlock_unlock(client_lock);
+		return(-1);
+	}
+
+	res =  send_fdserver_msg(s_sock, FD_LOOKUP_REQ, context, key, -1);
+	if (res < 0) {
+		ODP_ERR("fd lookup failure\n");
+		close(s_sock);
+		odp_spinlock_unlock(client_lock);
+		return -1;
+	}
+
+	res = recv_fdserver_msg(s_sock, &command, &context, &key, &fd);
+
+	if ((res < 0) || (command != FD_LOOKUP_ACK)) {
+		ODP_ERR("fd lookup failure\n");
+		close(s_sock);
+		odp_spinlock_unlock(client_lock);
+		return -1;
+	}
+
+	close(s_sock);
+	ODP_DBG("FD client lookup: pid=%d, key=%" PRIu64 ", fd=%d\n",
+		getpid(), key, fd);
+
+	odp_spinlock_unlock(client_lock);
+	return fd;
+}
+
+/*
+ * request server terminaison:
+ */
+static int stop_server(void)
+{
+	int s_sock; /* server socket */
+	int res;
+
+	odp_spinlock_lock(client_lock);
+
+	ODP_DBG("FD sending server stop request\n");
+
+	s_sock = get_socket();
+	if (s_sock < 0) {
+		odp_spinlock_unlock(client_lock);
+		return(-1);
+	}
+
+	res =  send_fdserver_msg(s_sock, FD_SERVERSTOP_REQ, 0, 0, -1);
+	if (res < 0) {
+		ODP_ERR("fd stop request failure\n");
+		close(s_sock);
+		odp_spinlock_unlock(client_lock);
+		return -1;
+	}
+
+	close(s_sock);
+
+	odp_spinlock_unlock(client_lock);
+	return 0;
+}
+
+/*
+ * server function
+ * receive a client request and handle it.
+ * Always returns 0 unless a stop request is received.
+ */
+static int handle_request(int client_sock)
+{
+	int command;
+	fd_server_context_e context;
+	uint64_t key;
+	int fd;
+	int i;
+
+	/* get a client request: */
+	recv_fdserver_msg(client_sock, &command, &context, &key, &fd);
+	switch (command) {
+	case FD_REGISTER_REQ:
+		if ((fd < 0) || (context >= FD_SRV_CTX_END)) {
+			ODP_ERR("Invalid register fd or context\n");
+			send_fdserver_msg(client_sock, FD_REGISTER_NACK,
+					  FD_SRV_CTX_NA, 0, -1);
+			return 0;
+		}
+
+		/* store the file descriptor in table: */
+		if (fd_table_nb_entries < FDSERVER_MAX_ENTRIES) {
+			fd_table[fd_table_nb_entries].context = context;
+			fd_table[fd_table_nb_entries].key     = key;
+			fd_table[fd_table_nb_entries++].fd    = fd;
+			ODP_DBG("storing {ctx=%d, key=%" PRIu64 "}->fd=%d\n",
+				context, key, fd);
+		} else {
+			ODP_ERR("FD table full\n");
+			send_fdserver_msg(client_sock, FD_REGISTER_NACK,
+					  FD_SRV_CTX_NA, 0, -1);
+			return 0;
+		}
+
+		send_fdserver_msg(client_sock, FD_REGISTER_ACK,
+				  FD_SRV_CTX_NA, 0, -1);
+		break;
+
+	case FD_LOOKUP_REQ:
+		if (context >= FD_SRV_CTX_END) {
+			ODP_ERR("invalid lookup context\n");
+			send_fdserver_msg(client_sock, FD_LOOKUP_NACK,
+					  FD_SRV_CTX_NA, 0, -1);
+			return 0;
+		}
+
+		/* search key in table and sent reply: */
+		for (i = 0; i < fd_table_nb_entries; i++) {
+			if ((fd_table[i].context == context) &&
+			    (fd_table[i].key == key)) {
+				fd = fd_table[i].fd;
+				ODP_DBG("lookup {ctx=%d,"
+					" key=%" PRIu64 "}->fd=%d\n",
+					context, key, fd);
+				send_fdserver_msg(client_sock,
+						  FD_LOOKUP_ACK, context, key,
+						  fd);
+				return 0;
+			}
+		}
+
+		/* context+key not found... send nack */
+		send_fdserver_msg(client_sock, FD_LOOKUP_NACK, context, key,
+				  -1);
+		break;
+
+	case FD_DEREGISTER_REQ:
+		if (context >= FD_SRV_CTX_END) {
+			ODP_ERR("invalid deregister context\n");
+			send_fdserver_msg(client_sock, FD_DEREGISTER_NACK,
+					  FD_SRV_CTX_NA, 0, -1);
+			return 0;
+		}
+
+		/* search key in table and remove it if found, and reply: */
+		for (i = 0; i < fd_table_nb_entries; i++) {
+			if ((fd_table[i].context == context) &&
+			    (fd_table[i].key == key)) {
+				ODP_DBG("drop {ctx=%d,"
+					" key=%" PRIu64 "}->fd=%d\n",
+					context, key, fd_table[i].fd);
+				close(fd_table[i].fd);
+				fd_table[i] = fd_table[--fd_table_nb_entries];
+				send_fdserver_msg(client_sock,
+						  FD_DEREGISTER_ACK,
+						  context, key, -1);
+				return 0;
+			}
+		}
+
+		/* key not found... send nack */
+		send_fdserver_msg(client_sock, FD_DEREGISTER_NACK,
+				  context, key, -1);
+		break;
+
+	case FD_SERVERSTOP_REQ:
+		ODP_DBG("Stoping FD server\n");
+		return 1;
+
+	default:
+		ODP_ERR("Unexpected request\n");
+		break;
+	}
+	return 0;
+}
+
+/*
+ * server function
+ * loop forever, handling client requests one by one
+ */
+static void wait_requests(int sock)
+{
+	int c_socket; /* client connection */
+	unsigned int addr_sz;
+	struct sockaddr_un remote;
+
+	for (;;) {
+		addr_sz = sizeof(remote);
+		c_socket = accept(sock, (struct sockaddr *)&remote, &addr_sz);
+		if (c_socket == -1) {
+				ODP_ERR("wait_requests: %s\n", strerror(errno));
+				return;
+		}
+
+		if (handle_request(c_socket))
+			break;
+		close(c_socket);
+	}
+	close(c_socket);
+}
+
+/*
+ * Create a unix domain socket and fork a process to listen to incoming
+ * requests.
+ */
+int _odp_fdserver_init_global(void)
+{
+	char sockpath[FDSERVER_SOCKPATH_MAXLEN];
+	int sock;
+	struct sockaddr_un local;
+	pid_t server_pid;
+	int res;
+
+	/* create the client spinlock that any client can see: */
+	client_lock = mmap(NULL, sizeof(odp_spinlock_t), PROT_READ | PROT_WRITE,
+			   MAP_SHARED | MAP_ANONYMOUS, -1, 0);
+
+	odp_spinlock_init(client_lock);
+
+	/* construct the server named socket path: */
+	snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCKPATH_FORMAT,
+		 odp_global_data.main_pid);
+
+	/* create UNIX domain socket: */
+	sock = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sock == -1) {
+		ODP_ERR("_odp_fdserver_init_global: %s\n", strerror(errno));
+		return(-1);
+	}
+
+	/* remove previous named socket if it already exists: */
+	unlink(sockpath);
+
+	/* bind to new named socket: */
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, sockpath, sizeof(local.sun_path));
+	res = bind(sock, (struct sockaddr *)&local, sizeof(struct sockaddr_un));
+	if (res == -1) {
+		ODP_ERR("_odp_fdserver_init_global: %s\n", strerror(errno));
+		close(sock);
+		return(-1);
+	}
+
+	/* listen for incoming conections: */
+	if (listen(sock, FDSERVER_BACKLOG) == -1) {
+		ODP_ERR("_odp_fdserver_init_global: %s\n", strerror(errno));
+		close(sock);
+		return(-1);
+	}
+
+	/* fork a server process: */
+	server_pid = fork();
+	if (server_pid == -1) {
+		ODP_ERR("Could not fork!\n");
+		close(sock);
+		return(-1);
+	}
+
+	if (server_pid == 0) { /*child */
+		/* TODO: pin the server on appropriate service cpu mask */
+		/* when (if) we can agree on the usage of service mask  */
+
+		wait_requests(sock); /* Returns when server is stopped  */
+		close(sock);
+		exit(0);
+	}
+
+	/* parent */
+	close(sock);
+	return 0;
+}
+
+/*
+ * Terminate the server
+ */
+int _odp_fdserver_term_global(void)
+{
+	int status;
+	char sockpath[FDSERVER_SOCKPATH_MAXLEN];
+
+	/* close the server and wait for child terminaison*/
+	stop_server();
+	wait(&status);
+
+	/* construct the server named socket path: */
+	snprintf(sockpath, FDSERVER_SOCKPATH_MAXLEN, FDSERVER_SOCKPATH_FORMAT,
+		 odp_global_data.main_pid);
+
+	/* delete the UNIX domain socket: */
+	unlink(sockpath);
+
+	return 0;
+}
diff --git a/platform/linux-generic/include/_fdserver_internal.h b/platform/linux-generic/include/_fdserver_internal.h
new file mode 100644
index 0000000..480ac02
--- /dev/null
+++ b/platform/linux-generic/include/_fdserver_internal.h
@@ -0,0 +1,38 @@
+/* Copyright (c) 2016, Linaro Limited
+ * All rights reserved.
+ *
+ * SPDX-License-Identifier:     BSD-3-Clause
+ */
+
+#ifndef _FD_SERVER_INTERNAL_H
+#define _FD_SERVER_INTERNAL_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * the following enum defines the different contextes by which the
+ * FD server may be used: In the FD server, the keys used to store/retrieve
+ * a file descriptor are actually context based:
+ * Both the context and the key are stored at fd registration time,
+ * and both the context and the key are used to retrieve a fd.
+ * In other words a context identifies a FD server usage, so that different
+ * unrelated fd server users do not have to guarantee key unicity between
+ * them.
+ */
+typedef enum fd_server_context {
+	FD_SRV_CTX_NA,  /* Not Applicable   */
+	FD_SRV_CTX_END, /* upper enum limit */
+} fd_server_context_e;
+
+int _odp_fdserver_register_fd(fd_server_context_e context, uint64_t key,
+			      int fd);
+int _odp_fdserver_deregister_fd(fd_server_context_e context, uint64_t key);
+int _odp_fdserver_lookup_fd(fd_server_context_e context, uint64_t key);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h
index 8bad450..fd770b5 100644
--- a/platform/linux-generic/include/odp_internal.h
+++ b/platform/linux-generic/include/odp_internal.h
@@ -54,6 +54,7 @@ enum init_stage {
 	CPUMASK_INIT,
 	TIME_INIT,
 	SYSINFO_INIT,
+	FDSERVER_INIT,
 	SHM_INIT,
 	THREAD_INIT,
 	POOL_INIT,
@@ -119,6 +120,9 @@ int odp_tm_term_global(void);
 int _odp_int_name_tbl_init_global(void);
 int _odp_int_name_tbl_term_global(void);
 
+int _odp_fdserver_init_global(void);
+int _odp_fdserver_term_global(void);
+
 int cpuinfo_parser(FILE *file, system_info_t *sysinfo);
 uint64_t odp_cpu_hz_current(int id);
 
diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c
index f534759..ec84fd0 100644
--- a/platform/linux-generic/odp_init.c
+++ b/platform/linux-generic/odp_init.c
@@ -51,6 +51,12 @@ int odp_init_global(odp_instance_t *instance,
 	}
 	stage = SYSINFO_INIT;
 
+	if (_odp_fdserver_init_global()) {
+		ODP_ERR("ODP fdserver init failed.\n");
+		goto init_failed;
+	}
+	stage = FDSERVER_INIT;
+
 	if (odp_shm_init_global()) {
 		ODP_ERR("ODP shm init failed.\n");
 		goto init_failed;
@@ -218,6 +224,13 @@ int _odp_term_global(enum init_stage stage)
 		}
 		/* Fall through */
 
+	case FDSERVER_INIT:
+		if (_odp_fdserver_term_global()) {
+			ODP_ERR("ODP fdserver term failed.\n");
+			rc = -1;
+		}
+		/* Fall through */
+
 	case SYSINFO_INIT:
 		if (odp_system_info_term()) {
 			ODP_ERR("ODP system info term failed.\n");
