aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/lguest/lguest.c50
-rw-r--r--drivers/lguest/lguest_device.c20
2 files changed, 48 insertions, 22 deletions
diff --git a/Documentation/lguest/lguest.c b/Documentation/lguest/lguest.c
index 5cd705c3d75..3be8ab2a886 100644
--- a/Documentation/lguest/lguest.c
+++ b/Documentation/lguest/lguest.c
@@ -131,6 +131,9 @@ struct device
/* Any queues attached to this device */
struct virtqueue *vq;
+ /* Handle status being finalized (ie. feature bits stable). */
+ void (*ready)(struct device *me);
+
/* Device-specific data. */
void *priv;
};
@@ -925,24 +928,40 @@ static void enable_fd(int fd, struct virtqueue *vq)
write(waker_fd, &vq->dev->fd, sizeof(vq->dev->fd));
}
-/* When the Guest asks us to reset a device, it's is fairly easy. */
-static void reset_device(struct device *dev)
+/* When the Guest tells us they updated the status field, we handle it. */
+static void update_device_status(struct device *dev)
{
struct virtqueue *vq;
- verbose("Resetting device %s\n", dev->name);
- /* Clear the status. */
- dev->desc->status = 0;
+ /* This is a reset. */
+ if (dev->desc->status == 0) {
+ verbose("Resetting device %s\n", dev->name);
- /* Clear any features they've acked. */
- memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
- dev->desc->feature_len);
+ /* Clear any features they've acked. */
+ memset(get_feature_bits(dev) + dev->desc->feature_len, 0,
+ dev->desc->feature_len);
- /* Zero out the virtqueues. */
- for (vq = dev->vq; vq; vq = vq->next) {
- memset(vq->vring.desc, 0,
- vring_size(vq->config.num, getpagesize()));
- vq->last_avail_idx = 0;
+ /* Zero out the virtqueues. */
+ for (vq = dev->vq; vq; vq = vq->next) {
+ memset(vq->vring.desc, 0,
+ vring_size(vq->config.num, getpagesize()));
+ vq->last_avail_idx = 0;
+ }
+ } else if (dev->desc->status & VIRTIO_CONFIG_S_FAILED) {
+ warnx("Device %s configuration FAILED", dev->name);
+ } else if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK) {
+ unsigned int i;
+
+ verbose("Device %s OK: offered", dev->name);
+ for (i = 0; i < dev->desc->feature_len; i++)
+ verbose(" %08x", get_feature_bits(dev)[i]);
+ verbose(", accepted");
+ for (i = 0; i < dev->desc->feature_len; i++)
+ verbose(" %08x", get_feature_bits(dev)
+ [dev->desc->feature_len+i]);
+
+ if (dev->ready)
+ dev->ready(dev);
}
}
@@ -954,9 +973,9 @@ static void handle_output(int fd, unsigned long addr)
/* Check each device and virtqueue. */
for (i = devices.dev; i; i = i->next) {
- /* Notifications to device descriptors reset the device. */
+ /* Notifications to device descriptors update device status. */
if (from_guest_phys(addr) == i->desc) {
- reset_device(i);
+ update_device_status(i);
return;
}
@@ -1170,6 +1189,7 @@ static struct device *new_device(const char *name, u16 type, int fd,
dev->handle_input = handle_input;
dev->name = name;
dev->vq = NULL;
+ dev->ready = NULL;
/* Append to device list. Prepending to a single-linked list is
* easier, but the user expects the devices to be arranged on the bus
diff --git a/drivers/lguest/lguest_device.c b/drivers/lguest/lguest_device.c
index 7a643a6ee9a..8080249957a 100644
--- a/drivers/lguest/lguest_device.c
+++ b/drivers/lguest/lguest_device.c
@@ -144,20 +144,26 @@ static u8 lg_get_status(struct virtio_device *vdev)
return to_lgdev(vdev)->desc->status;
}
+/* To notify on status updates, we (ab)use the NOTIFY hypercall, with the
+ * descriptor address of the device. A zero status means "reset". */
+static void set_status(struct virtio_device *vdev, u8 status)
+{
+ unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
+
+ /* We set the status. */
+ to_lgdev(vdev)->desc->status = status;
+ hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
+}
+
static void lg_set_status(struct virtio_device *vdev, u8 status)
{
BUG_ON(!status);
- to_lgdev(vdev)->desc->status = status;
+ set_status(vdev, status);
}
-/* To reset the device, we (ab)use the NOTIFY hypercall, with the descriptor
- * address of the device. The Host will zero the status and all the
- * features. */
static void lg_reset(struct virtio_device *vdev)
{
- unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices;
-
- hcall(LHCALL_NOTIFY, (max_pfn<<PAGE_SHIFT) + offset, 0, 0);
+ set_status(vdev, 0);
}
/*