Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

lguest: Simplify device initialization.

We used to notify the Host every time we updated a device's status. However,
it only really needs to know when we're resetting the device, or failed to
initialize it, or when we've finished our feature negotiation.

In particular, we used to wait for VIRTIO_CONFIG_S_DRIVER_OK in the
status byte before starting the device service threads. But this
corresponds to the successful finish of device initialization, which
might (like virtio_blk's partition scanning) use the device. So we
had a hack, if they used the device before we expected we started the
threads anyway.

Now we hook into the finalize_features hook in the Guest: at that
point we tell the Launcher that it can rely on the features we have
acked. On the Launcher side, we look at the status at that point, and
start servicing the device.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>

+28 -34
+6 -19
Documentation/virtual/lguest/lguest.c
··· 1095 1095 warnx("Device %s configuration FAILED", dev->name); 1096 1096 if (dev->running) 1097 1097 reset_device(dev); 1098 - } else if (dev->desc->status & VIRTIO_CONFIG_S_DRIVER_OK) { 1099 - if (!dev->running) 1100 - start_device(dev); 1098 + } else { 1099 + if (dev->running) 1100 + err(1, "Device %s features finalized twice", dev->name); 1101 + start_device(dev); 1101 1102 } 1102 1103 } 1103 1104 ··· 1123 1122 return; 1124 1123 } 1125 1124 1126 - /* 1127 - * Devices *can* be used before status is set to DRIVER_OK. 1128 - * The original plan was that they would never do this: they 1129 - * would always finish setting up their status bits before 1130 - * actually touching the virtqueues. In practice, we allowed 1131 - * them to, and they do (eg. the disk probes for partition 1132 - * tables as part of initialization). 1133 - * 1134 - * If we see this, we start the device: once it's running, we 1135 - * expect the device to catch all the notifications. 1136 - */ 1125 + /* Devices should not be used before features are finalized. */ 1137 1126 for (vq = i->vq; vq; vq = vq->next) { 1138 1127 if (addr != vq->config.pfn*getpagesize()) 1139 1128 continue; 1140 - if (i->running) 1141 - errx(1, "Notification on running %s", i->name); 1142 - /* This just calls create_thread() for each virtqueue */ 1143 - start_device(i); 1144 - return; 1129 + errx(1, "Notification on %s before setup!", i->name); 1145 1130 } 1146 1131 } 1147 1132
+22 -15
drivers/lguest/lguest_device.c
··· 109 109 } 110 110 111 111 /* 112 + * To notify on reset or feature finalization, we (ab)use the NOTIFY 113 + * hypercall, with the descriptor address of the device. 114 + */ 115 + static void status_notify(struct virtio_device *vdev) 116 + { 117 + unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; 118 + 119 + hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0); 120 + } 121 + 122 + /* 112 123 * The virtio core takes the features the Host offers, and copies the ones 113 124 * supported by the driver into the vdev->features array. Once that's all 114 125 * sorted out, this routine is called so we can tell the Host which features we ··· 146 135 if (test_bit(i, vdev->features)) 147 136 out_features[i / 8] |= (1 << (i % 8)); 148 137 } 138 + 139 + /* Tell Host we've finished with this device's feature negotiation */ 140 + status_notify(vdev); 149 141 } 150 142 151 143 /* Once they've found a field, getting a copy of it is easy. */ ··· 182 168 return to_lgdev(vdev)->desc->status; 183 169 } 184 170 185 - /* 186 - * To notify on status updates, we (ab)use the NOTIFY hypercall, with the 187 - * descriptor address of the device. A zero status means "reset". 188 - */ 189 - static void set_status(struct virtio_device *vdev, u8 status) 190 - { 191 - unsigned long offset = (void *)to_lgdev(vdev)->desc - lguest_devices; 192 - 193 - /* We set the status. */ 194 - to_lgdev(vdev)->desc->status = status; 195 - hcall(LHCALL_NOTIFY, (max_pfn << PAGE_SHIFT) + offset, 0, 0, 0); 196 - } 197 - 198 171 static void lg_set_status(struct virtio_device *vdev, u8 status) 199 172 { 200 173 BUG_ON(!status); 201 - set_status(vdev, status); 174 + to_lgdev(vdev)->desc->status = status; 175 + 176 + /* Tell Host immediately if we failed. */ 177 + if (status & VIRTIO_CONFIG_S_FAILED) 178 + status_notify(vdev); 202 179 } 203 180 204 181 static void lg_reset(struct virtio_device *vdev) 205 182 { 206 - set_status(vdev, 0); 183 + /* 0 status means "reset" */ 184 + to_lgdev(vdev)->desc->status = 0; 185 + status_notify(vdev); 207 186 } 208 187 209 188 /*