Skip to content

Commit

Permalink
Example launcher handle guests not being ready for input
Browse files Browse the repository at this point in the history
We currently discard console and network input when the guest has no
input buffers.  This patch changes that, so that we simply stop
listening to that fd until the guest refills its input buffers.

This is particularly important because hvc_console without interrupts
does backoff polling and so often lose characters if we discard.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
  • Loading branch information
Rusty Russell committed Oct 23, 2007
1 parent 17cbca2 commit 56ae43d
Showing 1 changed file with 57 additions and 50 deletions.
107 changes: 57 additions & 50 deletions Documentation/lguest/lguest.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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
Expand All @@ -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.
*
Expand Down Expand Up @@ -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?");

Expand All @@ -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],
Expand All @@ -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)
{
Expand Down Expand Up @@ -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));
}
}
}
Expand Down Expand Up @@ -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++);
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 56ae43d

Please sign in to comment.