Browse Source

[LibOS] Allow poll/ppoll/select/pselect on regular and dev files

Previously, Graphene incorrectly handled poll/ppoll/select/pselect
of regular and dev files like `/dev/urandom`: it tried to perform
an actual host-OS poll on these files. However, poll of such files
must be emulated completely inside LibOS. This commit adds this
special case to poll/ppoll/select/pselect. A test case is supplied.
Dmitrii Kuvaiskii 4 years ago
parent
commit
3def3a6ba6

+ 21 - 0
LibOS/shim/src/sys/shim_poll.c

@@ -117,6 +117,27 @@ int shim_do_poll(struct pollfd* fds, nfds_t nfds, int timeout_ms) {
             continue;
         }
 
+        if (hdl->type == TYPE_FILE || hdl->type == TYPE_DEV) {
+            /* Files and devs are special cases: their poll is emulated at LibOS level; do not
+             * include them in handles-to-poll array but instead use handle-specific callback. */
+            int shim_events = 0;
+            if ((fds[i].events & (POLLIN | POLLRDNORM)) && (hdl->acc_mode & MAY_READ))
+                shim_events |= FS_POLL_RD;
+            if ((fds[i].events & (POLLOUT | POLLWRNORM)) && (hdl->acc_mode & MAY_WRITE))
+                shim_events |= FS_POLL_WR;
+
+            int shim_revents = hdl->fs->fs_ops->poll(hdl, shim_events);
+
+            fds[i].revents = 0;
+            if (shim_revents & FS_POLL_RD)
+                fds[i].revents |= fds[i].events & (POLLIN | POLLRDNORM);
+            if (shim_revents & FS_POLL_WR)
+                fds[i].revents |= fds[i].events & (POLLOUT | POLLWRNORM);
+
+            nrevents++;
+            continue;
+        }
+
         PAL_FLG allowed_events = 0;
         if ((fds[i].events & (POLLIN | POLLRDNORM)) && (hdl->acc_mode & MAY_READ))
             allowed_events |= PAL_WAIT_READ;

+ 55 - 0
LibOS/shim/test/regression/poll_many_types.c

@@ -0,0 +1,55 @@
+#include <fcntl.h>
+#include <poll.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+int main(int argc, char** argv) {
+    int ret;
+    char string[] = "Hello, world!\n";
+
+    /* type 1: pipe */
+    int pipefd[2];
+    ret = pipe(pipefd);
+    if (ret < 0) {
+        perror("pipe creation failed");
+        return 1;
+    }
+    /* write something into write end of pipe so read end becomes pollable */
+    ret = write(pipefd[1], string, (strlen(string) + 1));
+    if (ret < 0) {
+        perror("pipe write failed");
+        return 1;
+    }
+
+    /* type 2: regular file */
+    int filefd = open(argv[0], O_RDONLY);
+    if (filefd < 0) {
+        perror("file open failed");
+        return 1;
+    }
+
+    /* type 3: dev file */
+    int devfd = open("/dev/urandom", O_RDONLY);
+    if (devfd < 0) {
+        perror("dev/urandom open failed");
+        return 1;
+    }
+
+    struct pollfd infds[] = {
+        {.fd = pipefd[0], .events = POLLIN},
+        {.fd = filefd,    .events = POLLIN},
+        {.fd = devfd,     .events = POLLIN},
+    };
+
+    ret = poll(infds, 3, -1);
+    if (ret <= 0) {
+        perror("poll with POLLIN failed");
+        return 1;
+    }
+    printf("poll(POLLIN) returned %d file descriptors\n", ret);
+
+    return 0;
+}

+ 4 - 0
LibOS/shim/test/regression/test_libos.py

@@ -429,6 +429,10 @@ class TC_80_Socket(RegressionTestCase):
         self.assertIn('poll(POLLOUT) returned 1 file descriptors', stdout)
         self.assertIn('poll(POLLIN) returned 1 file descriptors', stdout)
 
+    def test_021_poll_many_types(self):
+        stdout, _ = self.run_binary(['poll_many_types'])
+        self.assertIn('poll(POLLIN) returned 3 file descriptors', stdout)
+
     def test_030_ppoll(self):
         stdout, _ = self.run_binary(['ppoll'])
         self.assertIn('ppoll(POLLOUT) returned 1 file descriptors', stdout)