Fix oops on 2.6.25 guest (Rusty Russell)

I believe this is behind the following:
https://bugs.edge.launchpad.net/ubuntu/jaunty/+source/linux/+bug/331128

virtio_pci in 2.6.25 didn't do feature negotiation correctly: it acked every
bit.  Fortunately, we can detect this.

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
Signed-off-by: Anthony Liguori <aliguori@us.ibm.com>


git-svn-id: svn://svn.savannah.nongnu.org/qemu/branches/stable_0_10@6985 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
aliguori
2009-04-05 18:15:54 +00:00
parent 65297bc408
commit c61ad61830
3 changed files with 27 additions and 1 deletions

View File

@ -113,6 +113,21 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev)
return features; return features;
} }
static uint32_t virtio_net_bad_features(VirtIODevice *vdev)
{
uint32_t features = 0;
/* Linux kernel 2.6.25. It understood MAC (as everyone must),
* but also these: */
features |= (1 << VIRTIO_NET_F_MAC);
features |= (1 << VIRTIO_NET_F_GUEST_CSUM);
features |= (1 << VIRTIO_NET_F_GUEST_TSO4);
features |= (1 << VIRTIO_NET_F_GUEST_TSO6);
features |= (1 << VIRTIO_NET_F_GUEST_ECN);
return features & virtio_net_get_features(vdev);
}
static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features)
{ {
VirtIONet *n = to_virtio_net(vdev); VirtIONet *n = to_virtio_net(vdev);
@ -580,6 +595,7 @@ PCIDevice *virtio_net_init(PCIBus *bus, NICInfo *nd, int devfn)
n->vdev.set_config = virtio_net_set_config; n->vdev.set_config = virtio_net_set_config;
n->vdev.get_features = virtio_net_get_features; n->vdev.get_features = virtio_net_get_features;
n->vdev.set_features = virtio_net_set_features; n->vdev.set_features = virtio_net_set_features;
n->vdev.bad_features = virtio_net_bad_features;
n->vdev.reset = virtio_net_reset; n->vdev.reset = virtio_net_reset;
n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx); n->rx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_rx);
n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx); n->tx_vq = virtio_add_queue(&n->vdev, 256, virtio_net_handle_tx);

View File

@ -516,6 +516,13 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val)
switch (addr) { switch (addr) {
case VIRTIO_PCI_GUEST_FEATURES: case VIRTIO_PCI_GUEST_FEATURES:
/* Guest does not negotiate properly? We have to assume nothing. */
if (val & (1 << VIRTIO_F_BAD_FEATURE)) {
if (vdev->bad_features)
val = vdev->bad_features(vdev);
else
val = 0;
}
if (vdev->set_features) if (vdev->set_features)
vdev->set_features(vdev, val); vdev->set_features(vdev, val);
vdev->features = val; vdev->features = val;
@ -555,7 +562,7 @@ static uint32_t virtio_ioport_read(void *opaque, uint32_t addr)
switch (addr) { switch (addr) {
case VIRTIO_PCI_HOST_FEATURES: case VIRTIO_PCI_HOST_FEATURES:
ret = vdev->get_features(vdev); ret = vdev->get_features(vdev);
ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY); ret |= (1 << VIRTIO_F_NOTIFY_ON_EMPTY) | (1 << VIRTIO_F_BAD_FEATURE);
break; break;
case VIRTIO_PCI_GUEST_FEATURES: case VIRTIO_PCI_GUEST_FEATURES:
ret = vdev->features; ret = vdev->features;

View File

@ -32,6 +32,8 @@
/* We notify when the ring is completely used, even if the guest is supressing /* We notify when the ring is completely used, even if the guest is supressing
* callbacks */ * callbacks */
#define VIRTIO_F_NOTIFY_ON_EMPTY 24 #define VIRTIO_F_NOTIFY_ON_EMPTY 24
/* A guest should never accept this. It implies negotiation is broken. */
#define VIRTIO_F_BAD_FEATURE 30
/* from Linux's linux/virtio_ring.h */ /* from Linux's linux/virtio_ring.h */
@ -82,6 +84,7 @@ struct VirtIODevice
size_t config_len; size_t config_len;
void *config; void *config;
uint32_t (*get_features)(VirtIODevice *vdev); uint32_t (*get_features)(VirtIODevice *vdev);
uint32_t (*bad_features)(VirtIODevice *vdev);
void (*set_features)(VirtIODevice *vdev, uint32_t val); void (*set_features)(VirtIODevice *vdev, uint32_t val);
void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*get_config)(VirtIODevice *vdev, uint8_t *config);
void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config);