diff --git a/LibOS/shim/include/shim_fs.h b/LibOS/shim/include/shim_fs.h
index 1e1cf12c..23006d0d 100644
--- a/LibOS/shim/include/shim_fs.h
+++ b/LibOS/shim/include/shim_fs.h
@@ -569,6 +569,7 @@ extern struct shim_mount chroot_builtin_fs;
extern struct shim_mount pipe_builtin_fs;
extern struct shim_mount socket_builtin_fs;
extern struct shim_mount epoll_builtin_fs;
+extern struct shim_mount eventfd_builtin_fs;
/* proc file system */
struct proc_nm_ops {
diff --git a/LibOS/shim/include/shim_handle.h b/LibOS/shim/include/shim_handle.h
index b573f4ba..993d09e0 100644
--- a/LibOS/shim/include/shim_handle.h
+++ b/LibOS/shim/include/shim_handle.h
@@ -50,6 +50,7 @@ enum shim_handle_type {
TYPE_FUTEX,
TYPE_STR,
TYPE_EPOLL,
+ TYPE_EVENTFD
};
struct shim_handle;
diff --git a/LibOS/shim/include/shim_table.h b/LibOS/shim/include/shim_table.h
index 3dde77cc..44f69004 100644
--- a/LibOS/shim/include/shim_table.h
+++ b/LibOS/shim/include/shim_table.h
@@ -480,6 +480,8 @@ ssize_t shim_do_recvmmsg(int sockfd, struct mmsghdr* msg, size_t vlen, int flags
int shim_do_prlimit64(pid_t pid, int resource, const struct __kernel_rlimit64* new_rlim,
struct __kernel_rlimit64* old_rlim);
ssize_t shim_do_sendmmsg(int sockfd, struct mmsghdr* msg, size_t vlen, int flags);
+int shim_do_eventfd2(unsigned int count, int flags);
+int shim_do_eventfd(unsigned int count);
/* libos call implementation */
int shim_do_msgpersist(int msqid, int cmd);
@@ -781,14 +783,14 @@ int shim_epoll_pwait(int epfd, struct __kernel_epoll_event* events, int maxevent
const __sigset_t* sigmask, size_t sigsetsize);
int shim_signalfd(int ufd, __sigset_t* user_mask, size_t sizemask);
int shim_timerfd_create(int clockid, int flags);
-int shim_eventfd(int count);
+int shim_eventfd(unsigned int count);
int shim_fallocate(int fd, int mode, loff_t offset, loff_t len);
int shim_timerfd_settime(int ufd, int flags, const struct __kernel_itimerspec* utmr,
struct __kernel_itimerspec* otmr);
int shim_timerfd_gettime(int ufd, struct __kernel_itimerspec* otmr);
int shim_accept4(int sockfd, struct sockaddr* addr, socklen_t* addrlen, int flags);
int shim_signalfd4(int ufd, __sigset_t* user_mask, size_t sizemask, int flags);
-int shim_eventfd2(int count, int flags);
+int shim_eventfd2(unsigned int count, int flags);
int shim_epoll_create1(int flags);
int shim_dup3(int oldfd, int newfd, int flags);
int shim_pipe2(int* fildes, int flags);
diff --git a/LibOS/shim/src/Makefile b/LibOS/shim/src/Makefile
index ee04fdc9..bd9429f4 100644
--- a/LibOS/shim/src/Makefile
+++ b/LibOS/shim/src/Makefile
@@ -39,7 +39,7 @@ files_to_install = $(addprefix $(RUNTIME_DIR)/,$(files_to_build))
defs = -DIN_SHIM
CFLAGS += $(defs)
ASFLAGS += $(defs)
-fs = chroot str pipe socket proc dev
+fs = chroot str pipe socket proc dev eventfd
ipcns = pid sysv
objs = $(addprefix bookkeep/shim_,handle vma thread signal) \
$(patsubst %.c,%,$(wildcard utils/*.c)) \
diff --git a/LibOS/shim/src/fs/eventfd/fs.c b/LibOS/shim/src/fs/eventfd/fs.c
new file mode 100644
index 00000000..2a5bad98
--- /dev/null
+++ b/LibOS/shim/src/fs/eventfd/fs.c
@@ -0,0 +1,110 @@
+/* Copyright (C) 2019 Intel Corporation
+ This file is part of Graphene Library OS.
+
+ Graphene Library OS is free software: you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ Graphene Library OS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see . */
+
+/*
+ * fs.c
+ *
+ * This file contains codes for implementation of 'eventfd' filesystem.
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static ssize_t eventfd_read(struct shim_handle* hdl, void* buf, size_t count) {
+ if (!count)
+ return 0;
+
+ if (count < sizeof(uint64_t))
+ return -EINVAL;
+
+ PAL_NUM bytes = DkStreamRead(hdl->pal_handle, 0, count, buf, NULL, 0);
+
+ if (bytes <= 0)
+ return -PAL_ERRNO;
+
+ assert((ssize_t ) bytes == sizeof(uint64_t));
+
+ return (ssize_t) bytes;
+}
+
+static ssize_t eventfd_write(struct shim_handle* hdl, const void* buf, size_t count) {
+ if (!count)
+ return 0;
+
+ if (count < sizeof(uint64_t))
+ return -EINVAL;
+
+ PAL_NUM bytes = DkStreamWrite(hdl->pal_handle, 0, count, (void *) buf, NULL);
+
+ if (bytes <= 0)
+ return -PAL_ERRNO;
+
+ assert((ssize_t ) bytes == sizeof(uint64_t));
+
+ return (ssize_t) bytes;
+}
+
+static off_t eventfd_poll(struct shim_handle* hdl, int poll_type) {
+ off_t ret = 0;
+
+ lock(&hdl->lock);
+
+ if (!hdl->pal_handle) {
+ ret = -EBADF;
+ goto out;
+ }
+
+ PAL_STREAM_ATTR attr;
+ if (!DkStreamAttributesQueryByHandle(hdl->pal_handle, &attr)) {
+ ret = -PAL_ERRNO;
+ goto out;
+ }
+
+ if (poll_type == FS_POLL_SZ) {
+ ret = attr.pending_size;
+ goto out;
+ }
+
+ ret = 0;
+ if (attr.disconnected)
+ ret |= FS_POLL_ER;
+ if ((poll_type & FS_POLL_RD) && attr.readable)
+ ret |= FS_POLL_RD;
+ if ((poll_type & FS_POLL_WR) && attr.writable)
+ ret |= FS_POLL_WR;
+
+out:
+ unlock(&hdl->lock);
+ return ret;
+}
+
+struct shim_fs_ops eventfd_fs_ops = {
+ .read = &eventfd_read,
+ .write = &eventfd_write,
+ .poll = &eventfd_poll,
+};
+
+struct shim_mount eventfd_builtin_fs = {
+ .type = "eventfd",
+ .fs_ops = &eventfd_fs_ops,
+};
diff --git a/LibOS/shim/src/fs/shim_fs.c b/LibOS/shim/src/fs/shim_fs.c
index b83e7cb5..a9623277 100644
--- a/LibOS/shim/src/fs/shim_fs.c
+++ b/LibOS/shim/src/fs/shim_fs.c
@@ -46,13 +46,14 @@ struct shim_fs mountable_fs [NUM_MOUNTABLE_FS] = {
{ .name = "dev", .fs_ops = &dev_fs_ops, .d_ops = &dev_d_ops, },
};
-#define NUM_BUILTIN_FS 4
+#define NUM_BUILTIN_FS 5
struct shim_mount * builtin_fs [NUM_BUILTIN_FS] = {
&chroot_builtin_fs,
&pipe_builtin_fs,
&socket_builtin_fs,
&epoll_builtin_fs,
+ &eventfd_builtin_fs,
};
static struct shim_lock mount_mgr_lock;
diff --git a/LibOS/shim/src/shim_syscalls.c b/LibOS/shim/src/shim_syscalls.c
index 9cd12730..378b3df8 100644
--- a/LibOS/shim/src/shim_syscalls.c
+++ b/LibOS/shim/src/shim_syscalls.c
@@ -963,8 +963,6 @@ SHIM_SYSCALL_PASSTHROUGH(signalfd, 3, int, int, ufd, __sigset_t*, user_mask, siz
SHIM_SYSCALL_PASSTHROUGH(timerfd_create, 2, int, int, clockid, int, flags)
-SHIM_SYSCALL_PASSTHROUGH(eventfd, 1, int, int, count)
-
SHIM_SYSCALL_PASSTHROUGH(fallocate, 4, int, int, fd, int, mode, loff_t, offset, loff_t, len)
SHIM_SYSCALL_PASSTHROUGH(timerfd_settime, 4, int, int, ufd, int, flags,
@@ -979,7 +977,9 @@ DEFINE_SHIM_SYSCALL(accept4, 4, shim_do_accept4, int, int, sockfd, struct sockad
SHIM_SYSCALL_PASSTHROUGH(signalfd4, 4, int, int, ufd, __sigset_t*, user_mask, size_t, sizemask, int,
flags)
-SHIM_SYSCALL_PASSTHROUGH(eventfd2, 2, int, int, count, int, flags)
+DEFINE_SHIM_SYSCALL(eventfd, 1, shim_do_eventfd, int, unsigned int, count)
+
+DEFINE_SHIM_SYSCALL (eventfd2, 2, shim_do_eventfd2, int, unsigned int, count, int, flags)
/* epoll_create1: sys/shim_epoll.c */
DEFINE_SHIM_SYSCALL(epoll_create1, 1, shim_do_epoll_create1, int, int, flags)
diff --git a/LibOS/shim/src/sys/shim_eventfd.c b/LibOS/shim/src/sys/shim_eventfd.c
new file mode 100644
index 00000000..1eff8615
--- /dev/null
+++ b/LibOS/shim/src/sys/shim_eventfd.c
@@ -0,0 +1,89 @@
+/* Copyright (C) 2019 Intel Corporation
+ This file is part of Graphene Library OS.
+
+ Graphene Library OS is free software: you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ Graphene Library OS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see . */
+
+/*
+ * shim_eventfd.c
+ *
+ * Implementation of system calls "eventfd" and "eventfd2".
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+static int create_eventfd(PAL_HANDLE* efd, unsigned count, int flags) {
+ PAL_HANDLE hdl = NULL;
+ int pal_flags = 0;
+
+ pal_flags |= flags & EFD_NONBLOCK ? PAL_OPTION_NONBLOCK : 0;
+ pal_flags |= flags & EFD_CLOEXEC ? PAL_OPTION_CLOEXEC : 0;
+ pal_flags |= flags & EFD_SEMAPHORE ? PAL_OPTION_EFD_SEMAPHORE : 0;
+
+ /* eventfd() requires count (aka initval) but PAL's DkStreamOpen() doesn't have such an argument.
+ * Using create arg as a work-around (note: initval is uint32 but create is int32).*/
+ if (!(hdl = DkStreamOpen("eventfd:", 0, 0, count, pal_flags))) {
+ debug("eventfd open failure\n");
+ return -PAL_ERRNO;
+ }
+
+ *efd = hdl;
+ return 0;
+
+}
+
+int shim_do_eventfd2(unsigned int count, int flags) {
+ int ret = 0;
+ struct shim_handle* hdl = get_new_handle();
+
+ if (!hdl) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ hdl->type = TYPE_EVENTFD;
+ set_handle_fs(hdl, &eventfd_builtin_fs);
+ hdl->flags = O_RDWR;
+ hdl->acc_mode = MAY_READ | MAY_WRITE;
+
+ if ((ret = create_eventfd(&hdl->pal_handle, count, flags)) < 0)
+ goto out;
+
+ flags = flags & EFD_CLOEXEC ? FD_CLOEXEC : 0;
+
+ /* get_new_handle() above increments hdl's refcount.
+ * Followed by another increment inside set_new_fd_handle.
+ * So we need to put_handle() afterwards. */
+ int vfd = set_new_fd_handle(hdl, flags, NULL);
+
+ ret = vfd;
+
+out:
+ if (hdl)
+ put_handle(hdl);
+
+ return ret;
+
+}
+
+int shim_do_eventfd(unsigned int count) {
+ return shim_do_eventfd2(count, 0);
+}
diff --git a/LibOS/shim/test/regression/Makefile b/LibOS/shim/test/regression/Makefile
index d8ff6b84..fa6fd72c 100644
--- a/LibOS/shim/test/regression/Makefile
+++ b/LibOS/shim/test/regression/Makefile
@@ -22,6 +22,7 @@ CFLAGS-openmp = -fopenmp
CFLAGS-multi_pthread = -pthread
CFLAGS-exit_group = -pthread
CFLAGS-abort_multithread = -pthread
+CFLAGS-eventfd = -pthread
%: %.c
$(call cmd,csingle)
diff --git a/LibOS/shim/test/regression/eventfd.c b/LibOS/shim/test/regression/eventfd.c
new file mode 100644
index 00000000..d3a8f691
--- /dev/null
+++ b/LibOS/shim/test/regression/eventfd.c
@@ -0,0 +1,163 @@
+#include
+
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define MAX_EFDS 3
+
+int efds[MAX_EFDS] = { 0 };
+
+void* write_eventfd_thread(void* arg) {
+ uint64_t count = 10;
+
+ int* efds = (int*) arg;
+
+ if (!arg) {
+ printf("arg is NULL\n");
+ return NULL;
+ }
+
+ printf("%s:got here\n", __func__);
+
+ for (int i = 0; i < MAX_EFDS; i++) {
+ printf("%s: efd = %d\n", __func__, efds[i]);
+ }
+
+ for (int i = 0; i < MAX_EFDS; i++) {
+ sleep(1);
+ write(efds[i], &count, sizeof(count));
+ count += 1;
+ }
+
+ return NULL;
+}
+
+/* This function used to test polling on a group of eventfd descriptors.
+ * To support regression testing, positive value returned for error case. */
+int eventfd_using_poll() {
+ int ret = 0;
+ struct pollfd pollfds[MAX_EFDS];
+ pthread_t tid = 0;
+ uint64_t count = 0;
+ int poll_ret = 0;
+ int nread_events = 0;
+
+ for (int i = 0; i < MAX_EFDS; i++) {
+ efds[i] = eventfd(0, 0);
+
+ if (efds[i] < 0) {
+ perror("eventfd failed");
+ return 1;
+ }
+
+ printf("efd = %d\n", efds[i]);
+
+ pollfds[i].fd = efds[i];
+ pollfds[i].events = POLLIN;
+ }
+
+ ret = pthread_create(&tid, NULL, write_eventfd_thread, efds);
+
+ if (ret != 0) {
+ perror("error in thread creation\n");
+ return 1;
+ }
+
+ while (1) {
+ poll_ret = poll(pollfds, MAX_EFDS, 5000);
+
+ if (poll_ret == 0) {
+ printf("Poll timed out. Exiting.\n");
+ break;
+ }
+
+ if (poll_ret < 0) {
+ perror("error from poll");
+ ret = 1;
+ break;
+ }
+
+ for (int i = 0; i < MAX_EFDS; i++) {
+ if (pollfds[i].revents & POLLIN) {
+ pollfds[i].revents = 0;
+ errno = 0;
+ read(pollfds[i].fd, &count, sizeof(count));
+ printf("fd set=%d\n", pollfds[i].fd);
+ printf("efd = %d, count: %lu, errno=%d\n", pollfds[i].fd,
+ count, errno);
+ nread_events++;
+ }
+ }
+ }
+
+ if (nread_events == MAX_EFDS) {
+ printf("%s completed successfully\n", __func__);
+ } else
+ printf("%s: nread_events=%d, MAX_EFDS=%d\n", __func__, nread_events, MAX_EFDS);
+
+ pthread_join(tid, NULL);
+ return ret;
+}
+
+/* This function used to test various flags supported while creating eventfd descriptors.
+ * Note: EFD_SEMAPHORE has not been tested.
+ * To support regression testing, positive value returned for error case. */
+int eventfd_using_various_flags() {
+ uint64_t count = 0;
+ int efd = 0;
+ int eventfd_flags[] = { 0, EFD_NONBLOCK, EFD_CLOEXEC, EFD_NONBLOCK | EFD_CLOEXEC };
+
+ for (int i = 0; i < sizeof(eventfd_flags) / sizeof(int); i++) {
+ printf("iteration #-%d, flags=%d\n", i, eventfd_flags[i]);
+
+ efd = eventfd(0, eventfd_flags[i]);
+
+ if (efd < 0) {
+ perror("eventfd failed");
+ printf("eventfd error for iteration #-%d, flags-%d\n", i, eventfd_flags[i]);
+ return 1;
+ }
+
+ count = 5;
+ eventfd_write(efd, count);
+ eventfd_write(efd, count);
+ count = 0;
+ errno = 0;
+ eventfd_read(efd, &count);
+ printf("efd = %d, count: %lu, errno=%d\n", efd, count, errno);
+
+ /* calling the second read would block if flags doesn't have EFD_NONBLOCK */
+ if (eventfd_flags[i] & EFD_NONBLOCK) {
+ count = 0;
+ errno = 0;
+ eventfd_read(efd, &count);
+ printf("efd = %d, count: %lu, errno=%d\n", efd, count, errno);
+ }
+
+ close(efd);
+ }
+
+ printf("%s completed successfully\n", __func__);
+
+ return 0;
+}
+
+int main(int argc, char* argv[]) {
+ int ret = 0;
+
+ ret = eventfd_using_poll();
+ ret += eventfd_using_various_flags();
+
+ return ret;
+}
diff --git a/LibOS/shim/test/regression/test_libos.py b/LibOS/shim/test/regression/test_libos.py
index cf175b62..d197b09a 100644
--- a/LibOS/shim/test/regression/test_libos.py
+++ b/LibOS/shim/test/regression/test_libos.py
@@ -311,6 +311,13 @@ class TC_30_Syscall(RegressionTestCase):
self.assertIn('OK on sigaltstack in main thread', stdout)
self.assertIn('done exiting', stdout)
+ def test_070_eventfd(self):
+ stdout, stderr = self.run_binary(['eventfd'])
+
+ # Eventfd Test
+ self.assertIn('eventfd_using_poll completed successfully', stdout)
+ self.assertIn('eventfd_using_various_flags completed successfully', stdout)
+
@unittest.skipUnless(HAS_SGX,
'This test is only meaningful on SGX PAL because only SGX catches raw '
'syscalls and redirects to Graphene\'s LibOS. If we will add seccomp to '
diff --git a/Pal/src/db_streams.c b/Pal/src/db_streams.c
index 35a44ea0..39be6735 100644
--- a/Pal/src/db_streams.c
+++ b/Pal/src/db_streams.c
@@ -45,6 +45,7 @@ extern struct handle_ops mutex_ops;
extern struct handle_ops event_ops;
extern struct handle_ops gipc_ops;
extern struct handle_ops mcast_ops;
+extern struct handle_ops eventfd_ops;
const struct handle_ops* pal_handle_ops[PAL_HANDLE_TYPE_BOUND] = {
[pal_type_file] = &file_ops,
@@ -64,6 +65,7 @@ const struct handle_ops* pal_handle_ops[PAL_HANDLE_TYPE_BOUND] = {
[pal_type_mutex] = &mutex_ops,
[pal_type_event] = &event_ops,
[pal_type_gipc] = &gipc_ops,
+ [pal_type_eventfd] = &eventfd_ops,
};
/* parse_stream_uri scan the uri, seperate prefix and search for
@@ -106,6 +108,8 @@ static int parse_stream_uri(const char** uri, char** prefix, struct handle_ops**
hops = &tcp_ops;
else if (strstartswith_static(u, "udp.srv"))
hops = &udp_ops;
+ else if (strstartswith_static(u, "eventfd"))
+ hops = &eventfd_ops;
break;
case 8:
diff --git a/Pal/src/host/FreeBSD/pal_host.h b/Pal/src/host/FreeBSD/pal_host.h
index ed4689b8..c254f599 100644
--- a/Pal/src/host/FreeBSD/pal_host.h
+++ b/Pal/src/host/FreeBSD/pal_host.h
@@ -47,7 +47,7 @@ typedef struct mutex_handle {
#define _DkInternalLock _DkMutexLock
#define _DkInternalUnlock _DkMutexUnlock
-#define MAX_FDS 3
+
typedef union pal_handle
{
/* TSAI: Here we define the internal types of PAL_HANDLE
diff --git a/Pal/src/host/Linux-SGX/Makefile b/Pal/src/host/Linux-SGX/Makefile
index 9bfcbafd..66d1e01e 100644
--- a/Pal/src/host/Linux-SGX/Makefile
+++ b/Pal/src/host/Linux-SGX/Makefile
@@ -12,7 +12,7 @@ host_files = libpal-Linux-SGX.a pal-sgx debugger/sgx_gdb.so pal.map generated_of
defs = -DIN_PAL -DPAL_DIR=$(PAL_DIR) -DRUNTIME_DIR=$(RUNTIME_DIR)
CFLAGS += $(defs)
ASFLAGS += $(defs)
-enclave-objs = $(addprefix db_,files devices pipes sockets streams memory \
+enclave-objs = $(addprefix db_,files devices pipes eventfd sockets streams memory \
threading mutex events process object main rtld \
exception misc ipc spinlock) \
$(addprefix enclave_,ocalls ecalls framework platform pages untrusted)
diff --git a/Pal/src/host/Linux-SGX/db_eventfd.c b/Pal/src/host/Linux-SGX/db_eventfd.c
new file mode 100644
index 00000000..a7ababe2
--- /dev/null
+++ b/Pal/src/host/Linux-SGX/db_eventfd.c
@@ -0,0 +1,187 @@
+/* Copyright (C) 2019 Intel Corporation
+ This file is part of Graphene Library OS.
+
+ Graphene Library OS is free software: you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ Graphene Library OS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see . */
+
+/*
+ * db_eventfd.c
+ *
+ * This file contains operations to handle streams with URIs that have
+ * "eventfd:".
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "api.h"
+#include "pal_defs.h"
+#include "pal_linux_defs.h"
+#include "pal.h"
+#include "pal_internal.h"
+#include "pal_linux.h"
+#include "pal_linux_error.h"
+#include "pal_error.h"
+#include "pal_security.h"
+#include "pal_debug.h"
+
+static inline int eventfd_type(int options) {
+ int type = 0;
+ if (options & PAL_OPTION_NONBLOCK)
+ type |= EFD_NONBLOCK;
+
+ if (options & PAL_OPTION_CLOEXEC)
+ type |= EFD_CLOEXEC;
+
+ if (options & PAL_OPTION_EFD_SEMAPHORE)
+ type |= EFD_SEMAPHORE;
+
+ return type;
+}
+
+/* `type` must be eventfd, `uri` & `access` & `share` are unused,
+ * `create` holds eventfd's initval, `options` holds eventfd's flags */
+static int eventfd_pal_open(PAL_HANDLE* handle, const char* type, const char* uri, int access,
+ int share, int create, int options) {
+ int ret;
+ __UNUSED(access);
+ __UNUSED(share);
+
+ if ((strcmp_static(type, "eventfd") != 0) || (*uri != '\0')) {
+ return -PAL_ERROR_INVAL;
+ }
+
+ /* Using create arg as a work-around (note: initval is uint32 but create is int32).*/
+ ret = ocall_eventfd(create, eventfd_type(options));
+
+ if (IS_ERR(ret))
+ return unix_to_pal_error(ERRNO(ret));
+
+ PAL_HANDLE hdl = malloc(HANDLE_SIZE(eventfd));
+ SET_HANDLE_TYPE(hdl, eventfd);
+
+ /* Note: using index 0, given that there is only 1 eventfd FD per pal-handle. */
+ HANDLE_HDR(hdl)->flags = RFD(0) | WFD(0) | WRITABLE(0);
+
+ hdl->eventfd.fd = ret;
+ hdl->eventfd.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
+ *handle = hdl;
+
+ return 0;
+
+}
+
+static int64_t eventfd_pal_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) {
+ if (offset)
+ return -PAL_ERROR_INVAL;
+
+ if (!IS_HANDLE_TYPE(handle, eventfd))
+ return -PAL_ERROR_NOTCONNECTION;
+
+ if (len < sizeof(uint64_t))
+ return -PAL_ERROR_INVAL;
+
+ /* TODO: verify that the value returned in buffer is somehow meaningful
+ * (to prevent Iago attacks) */
+ int bytes = ocall_read(handle->eventfd.fd, buffer, len);
+
+ if (IS_ERR(bytes))
+ return unix_to_pal_error(ERRNO(bytes));
+
+ if (!bytes)
+ return -PAL_ERROR_ENDOFSTREAM;
+
+ return bytes;
+}
+
+static int64_t eventfd_pal_write(PAL_HANDLE handle, uint64_t offset, uint64_t len,
+ const void* buffer) {
+ if (offset)
+ return -PAL_ERROR_INVAL;
+
+ if (!IS_HANDLE_TYPE(handle, eventfd))
+ return -PAL_ERROR_NOTCONNECTION;
+
+ if (len < sizeof(uint64_t))
+ return -PAL_ERROR_INVAL;
+
+ int bytes = ocall_write(handle->eventfd.fd, buffer, len);
+ PAL_FLG writable = WRITABLE(0);
+
+ if (IS_ERR(bytes)) {
+ if (ERRNO(bytes) == EAGAIN)
+ HANDLE_HDR(handle)->flags &= ~writable;
+ return unix_to_pal_error(ERRNO(bytes));
+ }
+
+ /* whether fd is writable or not, gets updated here,
+ * to optimize polling logic in _DkObjectsWaitAny */
+ if ((uint64_t) bytes == sizeof(uint64_t))
+ HANDLE_HDR(handle)->flags |= writable;
+ else
+ HANDLE_HDR(handle)->flags &= ~writable;
+
+ return bytes;
+}
+
+/* invoked during poll operation on eventfd from LibOS. */
+static int eventfd_pal_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
+ if (handle->generic.fds[0] == PAL_IDX_POISON)
+ return -PAL_ERROR_BADHANDLE;
+
+ attr->handle_type = PAL_GET_TYPE(handle);
+
+ int efd = handle->eventfd.fd;
+ int flags = HANDLE_HDR(handle)->flags;
+
+ struct pollfd pfd = { .fd = efd, .events = POLLIN, .revents = 0 };
+ int ret = ocall_poll(&pfd, 1, 0);
+
+ if (IS_ERR(ret))
+ return unix_to_pal_error(ERRNO(ret));
+
+ attr->readable = (ret == 1 && pfd.revents == POLLIN);
+ attr->disconnected = flags & ERROR(0);
+ attr->nonblocking = handle->eventfd.nonblocking;
+
+ /* For future use, so that Linux host kernel can send notifications to user-space apps.
+ * App receives virtual FD from LibOS, but the Linux-host eventfd is memorized
+ * here, such that this Linux-host eventfd can be retrieved (by LibOS) during app's ioctl(). */
+ attr->no_of_fds = 1;
+ attr->fds[0] = efd;
+
+ return 0;
+}
+
+static int eventfd_pal_close(PAL_HANDLE handle) {
+ if (IS_HANDLE_TYPE(handle, eventfd)) {
+ if (handle->eventfd.fd != PAL_IDX_POISON) {
+ ocall_close(handle->eventfd.fd);
+ handle->eventfd.fd = PAL_IDX_POISON;
+ }
+ return 0;
+ }
+
+ return 0;
+}
+
+struct handle_ops eventfd_ops = {
+ .open = &eventfd_pal_open,
+ .read = &eventfd_pal_read,
+ .write = &eventfd_pal_write,
+ .close = &eventfd_pal_close,
+ .attrquerybyhdl = &eventfd_pal_attrquerybyhdl,
+};
diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.c b/Pal/src/host/Linux-SGX/enclave_ocalls.c
index ddd0666e..6e85b8ec 100644
--- a/Pal/src/host/Linux-SGX/enclave_ocalls.c
+++ b/Pal/src/host/Linux-SGX/enclave_ocalls.c
@@ -1243,3 +1243,23 @@ reset:
out:
return retval;
}
+
+int ocall_eventfd (unsigned int initval, int flags)
+{
+ int retval = 0;
+ ms_ocall_eventfd_t * ms;
+
+ ms = sgx_alloc_on_ustack(sizeof(*ms));
+ if (!ms) {
+ sgx_reset_ustack();
+ return -EPERM;
+ }
+
+ ms->ms_initval = initval;
+ ms->ms_flags = flags;
+
+ retval = sgx_ocall(OCALL_EVENTFD, ms);
+
+ sgx_reset_ustack();
+ return retval;
+}
diff --git a/Pal/src/host/Linux-SGX/enclave_ocalls.h b/Pal/src/host/Linux-SGX/enclave_ocalls.h
index 7729e765..67df43ca 100644
--- a/Pal/src/host/Linux-SGX/enclave_ocalls.h
+++ b/Pal/src/host/Linux-SGX/enclave_ocalls.h
@@ -104,3 +104,5 @@ int ocall_load_debug (const char * command);
int ocall_get_attestation(const sgx_spid_t* spid, const char* subkey, bool linkable,
const sgx_report_t* report, const sgx_quote_nonce_t* nonce,
sgx_attestation_t* attestation);
+int ocall_eventfd (unsigned int initval, int flags);
+
diff --git a/Pal/src/host/Linux-SGX/ocall_types.h b/Pal/src/host/Linux-SGX/ocall_types.h
index 88cbfa4f..c1b62c86 100644
--- a/Pal/src/host/Linux-SGX/ocall_types.h
+++ b/Pal/src/host/Linux-SGX/ocall_types.h
@@ -59,6 +59,7 @@ enum {
OCALL_DELETE,
OCALL_LOAD_DEBUG,
OCALL_GET_ATTESTATION,
+ OCALL_EVENTFD,
OCALL_NR,
};
@@ -286,4 +287,9 @@ typedef struct {
sgx_attestation_t ms_attestation;
} ms_ocall_get_attestation_t;
+typedef struct {
+ unsigned int ms_initval;
+ int ms_flags;
+} ms_ocall_eventfd_t;
+
#pragma pack(pop)
diff --git a/Pal/src/host/Linux-SGX/pal_host.h b/Pal/src/host/Linux-SGX/pal_host.h
index 2bcb708a..e14139e7 100644
--- a/Pal/src/host/Linux-SGX/pal_host.h
+++ b/Pal/src/host/Linux-SGX/pal_host.h
@@ -40,7 +40,6 @@ int _DkSpinUnlock (struct spinlock * lock);
#define LOCK_INIT { .value = { 0 } }
#define _DkInternalLock _DkSpinLock
#define _DkInternalUnlock _DkSpinUnlock
-#define MAX_FDS 3
void * malloc_untrusted (int size);
void free_untrusted (void * mem);
@@ -113,6 +112,12 @@ typedef struct pal_handle
PAL_BOL nonblocking;
} pipeprv;
+ struct {
+ PAL_IDX fd;
+ /* TODO: add other flags in future, if needed (e.g., semaphore) */
+ PAL_BOL nonblocking;
+ } eventfd;
+
struct {
PAL_IDX fd_in, fd_out;
PAL_IDX dev_type;
diff --git a/Pal/src/host/Linux-SGX/sgx_enclave.c b/Pal/src/host/Linux-SGX/sgx_enclave.c
index e2b2b3c9..fa98b029 100644
--- a/Pal/src/host/Linux-SGX/sgx_enclave.c
+++ b/Pal/src/host/Linux-SGX/sgx_enclave.c
@@ -670,6 +670,17 @@ static int sgx_ocall_delete(void * pms)
return ret;
}
+static int sgx_ocall_eventfd (void * pms)
+{
+ ms_ocall_eventfd_t * ms = (ms_ocall_eventfd_t *) pms;
+ int ret;
+ ODEBUG(OCALL_EVENTFD, ms);
+
+ ret = INLINE_SYSCALL(eventfd2, 2, ms->ms_initval, ms->ms_flags);
+
+ return ret;
+}
+
void load_gdb_command (const char * command);
static int sgx_ocall_load_debug(void * pms)
@@ -727,7 +738,8 @@ sgx_ocall_fn_t ocall_table[OCALL_NR] = {
[OCALL_DELETE] = sgx_ocall_delete,
[OCALL_LOAD_DEBUG] = sgx_ocall_load_debug,
[OCALL_GET_ATTESTATION] = sgx_ocall_get_attestation,
- };
+ [OCALL_EVENTFD] = sgx_ocall_eventfd,
+};
#define EDEBUG(code, ms) do {} while (0)
diff --git a/Pal/src/host/Linux/Makefile b/Pal/src/host/Linux/Makefile
index 9cf7ec84..1e6e73b7 100644
--- a/Pal/src/host/Linux/Makefile
+++ b/Pal/src/host/Linux/Makefile
@@ -15,7 +15,7 @@ host_files = libpal-Linux.a pal.map
defs = -DIN_PAL -DPAL_DIR=$(PAL_DIR) -DRUNTIME_DIR=$(RUNTIME_DIR)
CFLAGS += $(defs)
ASFLAGS += $(defs)
-objs = $(addprefix db_,files devices pipes sockets streams memory threading \
+objs = $(addprefix db_,files devices pipes eventfd sockets streams memory threading \
mutex events process object main rtld misc ipc \
exception) clone-x86_64
graphene_lib = .lib/graphene-lib.a
diff --git a/Pal/src/host/Linux/db_eventfd.c b/Pal/src/host/Linux/db_eventfd.c
new file mode 100644
index 00000000..314578a5
--- /dev/null
+++ b/Pal/src/host/Linux/db_eventfd.c
@@ -0,0 +1,186 @@
+/* Copyright (C) 2019 Intel Corporation
+ This file is part of Graphene Library OS.
+
+ Graphene Library OS is free software: you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ Graphene Library OS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see . */
+
+/*
+ * db_eventfd.c
+ *
+ * This file contains operations to handle streams with URIs that have
+ * "eventfd:".
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "api.h"
+#include "pal_defs.h"
+#include "pal_linux_defs.h"
+#include "pal.h"
+#include "pal_internal.h"
+#include "pal_linux.h"
+#include "pal_linux_error.h"
+#include "pal_error.h"
+#include "pal_security.h"
+#include "pal_debug.h"
+
+static inline int eventfd_type(int options) {
+ int type = 0;
+ if (options & PAL_OPTION_NONBLOCK)
+ type |= EFD_NONBLOCK;
+
+ if (options & PAL_OPTION_CLOEXEC)
+ type |= EFD_CLOEXEC;
+
+ if (options & PAL_OPTION_EFD_SEMAPHORE)
+ type |= EFD_SEMAPHORE;
+
+ return type;
+}
+
+/* `type` must be eventfd, `uri` & `access` & `share` are unused,
+ * `create` holds eventfd's initval, `options` holds eventfd's flags */
+static int eventfd_pal_open(PAL_HANDLE* handle, const char* type, const char* uri, int access,
+ int share, int create, int options) {
+ int ret;
+
+ __UNUSED(access);
+ __UNUSED(share);
+
+ if ((strcmp_static(type, "eventfd") != 0) || (*uri != '\0')) {
+ return -PAL_ERROR_INVAL;
+ }
+
+ /* Using create arg as a work-around (note: initval is uint32 but create is int32).*/
+ ret = INLINE_SYSCALL(eventfd2, 2, create, eventfd_type(options));
+
+ if (IS_ERR(ret))
+ return unix_to_pal_error(ERRNO(ret));
+
+ PAL_HANDLE hdl = malloc(HANDLE_SIZE(eventfd));
+ SET_HANDLE_TYPE(hdl, eventfd);
+
+ /* Note: using index 0, given that there is only 1 eventfd FD per pal-handle. */
+ HANDLE_HDR(hdl)->flags = RFD(0) | WFD(0) | WRITABLE(0);
+
+ hdl->eventfd.fd = ret;
+ hdl->eventfd.nonblocking = (options & PAL_OPTION_NONBLOCK) ? PAL_TRUE : PAL_FALSE;
+ *handle = hdl;
+
+ return 0;
+
+}
+
+static int64_t eventfd_pal_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) {
+ if (offset)
+ return -PAL_ERROR_INVAL;
+
+ if (!IS_HANDLE_TYPE(handle, eventfd))
+ return -PAL_ERROR_NOTCONNECTION;
+
+ if (len < sizeof(uint64_t))
+ return -PAL_ERROR_INVAL;
+
+ int bytes = INLINE_SYSCALL(read, 3, handle->eventfd.fd, buffer, len);
+
+ if (IS_ERR(bytes))
+ return unix_to_pal_error(ERRNO(bytes));
+
+ if (!bytes)
+ return -PAL_ERROR_ENDOFSTREAM;
+
+ return bytes;
+}
+
+static int64_t eventfd_pal_write(PAL_HANDLE handle, uint64_t offset, uint64_t len,
+ const void* buffer) {
+ if (offset)
+ return -PAL_ERROR_INVAL;
+
+ if (!IS_HANDLE_TYPE(handle, eventfd))
+ return -PAL_ERROR_NOTCONNECTION;
+
+ if (len < sizeof(uint64_t))
+ return -PAL_ERROR_INVAL;
+
+ int bytes = INLINE_SYSCALL(write, 3, handle->eventfd.fd, buffer, len);
+ PAL_FLG writable = WRITABLE(0);
+
+ if (IS_ERR(bytes)) {
+ if (ERRNO(bytes) == EAGAIN)
+ HANDLE_HDR(handle)->flags &= ~writable;
+ return unix_to_pal_error(ERRNO(bytes));
+ }
+
+ /* whether fd is writable or not, gets updated here,
+ * to optimize polling logic in _DkObjectsWaitAny */
+ if ((uint64_t) bytes == sizeof(uint64_t))
+ HANDLE_HDR(handle)->flags |= writable;
+ else
+ HANDLE_HDR(handle)->flags &= ~writable;
+
+ return bytes;
+}
+
+/* invoked during poll operation on eventfd from LibOS. */
+static int eventfd_pal_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
+ if (handle->generic.fds[0] == PAL_IDX_POISON)
+ return -PAL_ERROR_BADHANDLE;
+
+ attr->handle_type = PAL_GET_TYPE(handle);
+ int efd = handle->eventfd.fd;
+ int flags = HANDLE_HDR(handle)->flags;
+
+ struct pollfd pfd = { .fd = efd, .events = POLLIN, .revents = 0 };
+ struct timespec tp = {0, 0};
+ int ret = INLINE_SYSCALL(ppoll, 5, &pfd, 1, &tp, NULL, 0);
+
+ if (IS_ERR(ret))
+ return unix_to_pal_error(ERRNO(ret));
+
+ attr->readable = (ret == 1 && pfd.revents == POLLIN);
+ attr->disconnected = flags & ERROR(0);
+ attr->nonblocking = handle->eventfd.nonblocking;
+
+ /* For future use, so that Linux host kernel can send notifications to user-space apps.
+ * App receives virtual FD from LibOS, but the Linux-host eventfd is memorized
+ * here, such that this Linux-host eventfd can be retrieved (by LibOS) during app's ioctl(). */
+ attr->no_of_fds = 1;
+ attr->fds[0] = efd;
+
+ return 0;
+}
+
+static int eventfd_pal_close(PAL_HANDLE handle) {
+ if (IS_HANDLE_TYPE(handle, eventfd)) {
+ if (handle->eventfd.fd != PAL_IDX_POISON) {
+ INLINE_SYSCALL(close, 1, handle->eventfd.fd);
+ handle->eventfd.fd = PAL_IDX_POISON;
+ }
+ }
+
+ return 0;
+}
+
+struct handle_ops eventfd_ops = {
+ .open = &eventfd_pal_open,
+ .read = &eventfd_pal_read,
+ .write = &eventfd_pal_write,
+ .close = &eventfd_pal_close,
+ .attrquerybyhdl = &eventfd_pal_attrquerybyhdl,
+};
diff --git a/Pal/src/host/Linux/pal_host.h b/Pal/src/host/Linux/pal_host.h
index 093b873d..d594fd95 100644
--- a/Pal/src/host/Linux/pal_host.h
+++ b/Pal/src/host/Linux/pal_host.h
@@ -67,8 +67,6 @@ typedef struct {
#endif
} PAL_RESERVED_HDR;
-#define MAX_FDS 3
-
typedef struct pal_handle
{
/* TSAI: Here we define the internal types of PAL_HANDLE
@@ -106,6 +104,12 @@ typedef struct pal_handle
PAL_BOL nonblocking;
} pipeprv;
+ struct {
+ PAL_IDX fd;
+ /* TODO: add other flags in future, if needed (e.g., semaphore) */
+ PAL_BOL nonblocking;
+ } eventfd;
+
struct {
PAL_IDX fd_in, fd_out;
PAL_IDX dev_type;
diff --git a/Pal/src/host/Skeleton/Makefile b/Pal/src/host/Skeleton/Makefile
index c453fae6..6d07a269 100644
--- a/Pal/src/host/Skeleton/Makefile
+++ b/Pal/src/host/Skeleton/Makefile
@@ -9,7 +9,7 @@ host_files = libpal-Skeleton.a pal.map
defs = -DIN_PAL
CFLAGS += $(defs)
ASFLAGS += $(defs)
-objs = $(addprefix db_,files devices pipes sockets streams memory threading \
+objs = $(addprefix db_,files devices pipes eventfd sockets streams memory threading \
mutex events process object main rtld misc ipc exception)
headers = $(wildcard *.h) $(wildcard ../../*.h) $(wildcard ../../../lib/*.h)
diff --git a/Pal/src/host/Skeleton/db_eventfd.c b/Pal/src/host/Skeleton/db_eventfd.c
new file mode 100644
index 00000000..cf9bac7b
--- /dev/null
+++ b/Pal/src/host/Skeleton/db_eventfd.c
@@ -0,0 +1,63 @@
+/* Copyright (C) 2019 Intel Corporation
+ This file is part of Graphene Library OS.
+
+ Graphene Library OS is free software: you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public License
+ as published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ Graphene Library OS is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see . */
+
+/*
+ * db_eventfd.c
+ *
+ * This file contains operations to handle streams with URIs that have
+ * "eventfd:".
+ */
+
+#include "pal_defs.h"
+#include "pal.h"
+#include "pal_internal.h"
+#include "pal_error.h"
+#include "api.h"
+
+/* `type` must be eventfd, `uri` & `access` & `share` are unused,
+ * `create` holds eventfd's initval, `options` holds eventfd's flags */
+static int eventfd_pal_open(PAL_HANDLE* handle, const char* type, const char* uri, int access,
+ int share, int create, int options) {
+ return -PAL_ERROR_NOTIMPLEMENTED;
+}
+
+/* offset does not apply here. */
+static int64_t eventfd_pal_read(PAL_HANDLE handle, uint64_t offset, uint64_t len, void* buffer) {
+ return -PAL_ERROR_NOTIMPLEMENTED;
+}
+
+/* offset does not apply here. */
+static int64_t eventfd_pal_write(PAL_HANDLE handle, uint64_t offset, uint64_t len,
+ const void* buffer) {
+ return -PAL_ERROR_NOTIMPLEMENTED;
+}
+
+/* gets used for polling(query) on eventfd from LibOS. */
+static int eventfd_pal_attrquerybyhdl(PAL_HANDLE handle, PAL_STREAM_ATTR* attr) {
+ return -PAL_ERROR_NOTIMPLEMENTED;
+}
+
+static int eventfd_pal_close(PAL_HANDLE handle) {
+ return -PAL_ERROR_NOTIMPLEMENTED;
+}
+
+struct handle_ops eventfd_ops = {
+ .open = &eventfd_pal_open,
+ .read = &eventfd_pal_read,
+ .write = &eventfd_pal_write,
+ .close = &eventfd_pal_close,
+ .attrquerybyhdl = &eventfd_pal_attrquerybyhdl,
+};
diff --git a/Pal/src/host/Skeleton/pal_host.h b/Pal/src/host/Skeleton/pal_host.h
index cebacd4f..c9efa2fb 100644
--- a/Pal/src/host/Skeleton/pal_host.h
+++ b/Pal/src/host/Skeleton/pal_host.h
@@ -34,8 +34,6 @@ typedef struct mutex_handle {
#define LOCK_INIT {}
#define INIT_LOCK(lock) do {} while (0)
-#define MAX_FDS 3
-
typedef struct pal_handle
{
/* TSAI: Here we define the internal types of PAL_HANDLE
@@ -66,6 +64,10 @@ typedef struct pal_handle
PAL_IDX fd;
} pipeprv;
+ struct {
+ PAL_IDX unused;
+ } eventfd;
+
struct {
PAL_IDX fd;
PAL_IDX dev_type;
diff --git a/Pal/src/pal.h b/Pal/src/pal.h
index 529d3f37..1dff9bac 100644
--- a/Pal/src/pal.h
+++ b/Pal/src/pal.h
@@ -35,6 +35,10 @@ typedef uint32_t PAL_FLG;
typedef uint32_t PAL_IDX;
typedef bool PAL_BOL;
+/* Moved MAX_FDS from /pal_host.h to here,
+ * since it is 3, across all host kernels. */
+#define MAX_FDS 3
+
#ifdef IN_PAL
#include
typedef struct atomic_int PAL_REF;
@@ -142,10 +146,10 @@ enum {
pal_type_mutex,
pal_type_event,
pal_type_gipc,
+ pal_type_eventfd,
PAL_HANDLE_TYPE_BOUND,
};
-
#define PAL_IDX_POISON ((PAL_IDX)-1) /* PAL identifier poison value */
#define PAL_GET_TYPE(h) (HANDLE_HDR(h)->type)
#define PAL_CHECK_TYPE(h, t) (PAL_GET_TYPE(h) == pal_type_##t)
@@ -324,6 +328,11 @@ DkProcessExit (PAL_NUM exitCode);
#define PAL_OPTION_NONBLOCK 04000
#define PAL_OPTION_MASK 04000
+/* CLOEXEC is generic for any stream.
+ * SEMAPHORE is specific to eventfd syscall. */
+#define PAL_OPTION_CLOEXEC 01000
+#define PAL_OPTION_EFD_SEMAPHORE 02000
+
#define WITHIN_MASK(val, mask) (((val)|(mask)) == (mask))
PAL_HANDLE
@@ -377,6 +386,8 @@ typedef struct {
PAL_BOL readable, writable, runnable;
PAL_FLG share_flags;
PAL_NUM pending_size;
+ PAL_IDX no_of_fds;
+ PAL_IDX fds[MAX_FDS];
union {
struct {
PAL_NUM linger;