|
@@ -598,15 +598,17 @@ static void wake_parent(int pipefd, int lguest_fd)
|
|
|
select(devices.max_infd+1, &rfds, NULL, NULL, NULL);
|
|
|
/* Is it a message from the Launcher? */
|
|
|
if (FD_ISSET(pipefd, &rfds)) {
|
|
|
- int ignorefd;
|
|
|
+ int fd;
|
|
|
/* If read() returns 0, it means the Launcher has
|
|
|
* exited. We silently follow. */
|
|
|
- if (read(pipefd, &ignorefd, sizeof(ignorefd)) == 0)
|
|
|
+ if (read(pipefd, &fd, sizeof(fd)) == 0)
|
|
|
exit(0);
|
|
|
- /* Otherwise it's telling us there's a problem with one
|
|
|
- * of the devices, and we should ignore that file
|
|
|
- * descriptor from now on. */
|
|
|
- FD_CLR(ignorefd, &devices.infds);
|
|
|
+ /* Otherwise it's telling us to change what file
|
|
|
+ * descriptors we're to listen to. */
|
|
|
+ if (fd >= 0)
|
|
|
+ FD_SET(fd, &devices.infds);
|
|
|
+ else
|
|
|
+ FD_CLR(-fd - 1, &devices.infds);
|
|
|
} else /* Send LHREQ_BREAK command. */
|
|
|
write(lguest_fd, args, sizeof(args));
|
|
|
}
|
|
@@ -658,18 +660,6 @@ static void *_check_pointer(unsigned long addr, unsigned int size,
|
|
|
/* A macro which transparently hands the line number to the real function. */
|
|
|
#define check_pointer(addr,size) _check_pointer(addr, size, __LINE__)
|
|
|
|
|
|
-/* This simply sets up an iovec array where we can put data to be discarded.
|
|
|
- * This happens when the Guest doesn't want or can't handle the input: we have
|
|
|
- * to get rid of it somewhere, and if we bury it in the ceiling space it will
|
|
|
- * start to smell after a week. */
|
|
|
-static void discard_iovec(struct iovec *iov, unsigned int *num)
|
|
|
-{
|
|
|
- static char discard_buf[1024];
|
|
|
- *num = 1;
|
|
|
- iov->iov_base = discard_buf;
|
|
|
- iov->iov_len = sizeof(discard_buf);
|
|
|
-}
|
|
|
-
|
|
|
/* This function returns the next descriptor in the chain, or vq->vring.num. */
|
|
|
static unsigned next_desc(struct virtqueue *vq, unsigned int i)
|
|
|
{
|
|
@@ -812,12 +802,13 @@ static bool handle_console_input(int fd, struct device *dev)
|
|
|
|
|
|
/* First we need a console buffer from the Guests's input virtqueue. */
|
|
|
head = get_vq_desc(dev->vq, iov, &out_num, &in_num);
|
|
|
- if (head == dev->vq->vring.num) {
|
|
|
- /* If they're not ready for input, we warn and set up to
|
|
|
- * discard. */
|
|
|
- warnx("console: no dma buffer!");
|
|
|
- discard_iovec(iov, &in_num);
|
|
|
- } else if (out_num)
|
|
|
+
|
|
|
+ /* If they're not ready for input, stop listening to this file
|
|
|
+ * descriptor. We'll start again once they add an input buffer. */
|
|
|
+ if (head == dev->vq->vring.num)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (out_num)
|
|
|
errx(1, "Output buffers in console in queue?");
|
|
|
|
|
|
/* This is why we convert to iovecs: the readv() call uses them, and so
|
|
@@ -827,15 +818,16 @@ static bool handle_console_input(int fd, struct device *dev)
|
|
|
/* This implies that the console is closed, is /dev/null, or
|
|
|
* something went terribly wrong. */
|
|
|
warnx("Failed to get console input, ignoring console.");
|
|
|
- /* Put the input terminal back and return failure (meaning,
|
|
|
- * don't call us again). */
|
|
|
+ /* Put the input terminal back. */
|
|
|
restore_term();
|
|
|
+ /* Remove callback from input vq, so it doesn't restart us. */
|
|
|
+ dev->vq->handle_output = NULL;
|
|
|
+ /* Stop listening to this fd: don't call us again. */
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- /* If we actually read the data into the Guest, tell them about it. */
|
|
|
- if (head != dev->vq->vring.num)
|
|
|
- add_used_and_trigger(fd, dev->vq, head, len);
|
|
|
+ /* Tell the Guest about the new input. */
|
|
|
+ add_used_and_trigger(fd, dev->vq, head, len);
|
|
|
|
|
|
/* Three ^C within one second? Exit.
|
|
|
*
|
|
@@ -924,7 +916,8 @@ static bool handle_tun_input(int fd, struct device *dev)
|
|
|
/* FIXME: Actually want DRIVER_ACTIVE here. */
|
|
|
if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK)
|
|
|
warn("network: no dma buffer!");
|
|
|
- discard_iovec(iov, &in_num);
|
|
|
+ /* We'll turn this back on if input buffers are registered. */
|
|
|
+ return false;
|
|
|
} else if (out_num)
|
|
|
errx(1, "Output buffers in network recv queue?");
|
|
|
|
|
@@ -938,9 +931,8 @@ static bool handle_tun_input(int fd, struct device *dev)
|
|
|
if (len <= 0)
|
|
|
err(1, "reading network");
|
|
|
|
|
|
- /* If we actually read the data into the Guest, tell them about it. */
|
|
|
- if (head != dev->vq->vring.num)
|
|
|
- add_used_and_trigger(fd, dev->vq, head, sizeof(*hdr) + len);
|
|
|
+ /* Tell the Guest about the new packet. */
|
|
|
+ add_used_and_trigger(fd, dev->vq, head, sizeof(*hdr) + len);
|
|
|
|
|
|
verbose("tun input packet len %i [%02x %02x] (%s)\n", len,
|
|
|
((u8 *)iov[1].iov_base)[0], ((u8 *)iov[1].iov_base)[1],
|
|
@@ -950,6 +942,15 @@ static bool handle_tun_input(int fd, struct device *dev)
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
+/* This callback ensures we try again, in case we stopped console or net
|
|
|
+ * delivery because Guest didn't have any buffers. */
|
|
|
+static void enable_fd(int fd, struct virtqueue *vq)
|
|
|
+{
|
|
|
+ add_device_fd(vq->dev->fd);
|
|
|
+ /* Tell waker to listen to it again */
|
|
|
+ write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd));
|
|
|
+}
|
|
|
+
|
|
|
/* This is the generic routine we call when the Guest uses LHCALL_NOTIFY. */
|
|
|
static void handle_output(int fd, unsigned long addr)
|
|
|
{
|
|
@@ -996,17 +997,22 @@ static void handle_input(int fd)
|
|
|
* file descriptors and a method of handling them. */
|
|
|
for (i = devices.dev; i; i = i->next) {
|
|
|
if (i->handle_input && FD_ISSET(i->fd, &fds)) {
|
|
|
+ int dev_fd;
|
|
|
+ if (i->handle_input(fd, i))
|
|
|
+ continue;
|
|
|
+
|
|
|
/* If handle_input() returns false, it means we
|
|
|
- * should no longer service it.
|
|
|
- * handle_console_input() does this. */
|
|
|
- if (!i->handle_input(fd, i)) {
|
|
|
- /* Clear it from the set of input file
|
|
|
- * descriptors kept at the head of the
|
|
|
- * device list. */
|
|
|
- FD_CLR(i->fd, &devices.infds);
|
|
|
- /* Tell waker to ignore it too... */
|
|
|
- write(waker_fd, &i->fd, sizeof(i->fd));
|
|
|
- }
|
|
|
+ * should no longer service it. Networking and
|
|
|
+ * console do this when there's no input
|
|
|
+ * buffers to deliver into. Console also uses
|
|
|
+ * it when it discovers that stdin is
|
|
|
+ * closed. */
|
|
|
+ FD_CLR(i->fd, &devices.infds);
|
|
|
+ /* Tell waker to ignore it too, by sending a
|
|
|
+ * negative fd number (-1, since 0 is a valid
|
|
|
+ * FD number). */
|
|
|
+ dev_fd = -i->fd - 1;
|
|
|
+ write(waker_fd, &dev_fd, sizeof(dev_fd));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1154,11 +1160,11 @@ static void setup_console(void)
|
|
|
dev->priv = malloc(sizeof(struct console_abort));
|
|
|
((struct console_abort *)dev->priv)->count = 0;
|
|
|
|
|
|
- /* The console needs two virtqueues: the input then the output. We
|
|
|
- * don't care when they refill the input queue, since we don't hold
|
|
|
- * data waiting for them. That's why the input queue's callback is
|
|
|
- * NULL. */
|
|
|
- add_virtqueue(dev, VIRTQUEUE_NUM, NULL);
|
|
|
+ /* The console needs two virtqueues: the input then the output. When
|
|
|
+ * they put something the input queue, we make sure we're listening to
|
|
|
+ * stdin. When they put something in the output queue, we write it to
|
|
|
+ * stdout. */
|
|
|
+ add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd);
|
|
|
add_virtqueue(dev, VIRTQUEUE_NUM, handle_console_output);
|
|
|
|
|
|
verbose("device %u: console\n", devices.device_num++);
|
|
@@ -1270,8 +1276,9 @@ static void setup_tun_net(const char *arg)
|
|
|
/* First we create a new network device. */
|
|
|
dev = new_device("net", VIRTIO_ID_NET, netfd, handle_tun_input);
|
|
|
|
|
|
- /* Network devices need a receive and a send queue. */
|
|
|
- add_virtqueue(dev, VIRTQUEUE_NUM, NULL);
|
|
|
+ /* Network devices need a receive and a send queue, just like
|
|
|
+ * console. */
|
|
|
+ add_virtqueue(dev, VIRTQUEUE_NUM, enable_fd);
|
|
|
add_virtqueue(dev, VIRTQUEUE_NUM, handle_net_output);
|
|
|
|
|
|
/* We need a socket to perform the magic network ioctls to bring up the
|