This patchset aims to get us some way to implement virtio-1 compliant and transitional devices in qemu. Branch available at git://github.com/cohuck/qemu virtio-1 I've mainly focused on: - endianness handling - extended feature bits - virtio-ccw new/changed commands Thanks go to Thomas for some preliminary work in this area. I've been able to start guests both with and without the virtio-1 patches in the linux guest patchset, with virtio-net and virtio-blk devices (with and without dataplane). virtio-ccw only :) vhost, migration and loads of other things have been ignored for now. I'd like to know whether I walk into the right direction, so let's consider this as a starting point. Cornelia Huck (8): virtio: cull virtio_bus_set_vdev_features virtio: support more feature bits s390x/virtio-ccw: fix check for WRITE_FEAT virtio: introduce legacy virtio devices virtio: allow virtio-1 queue layout dataplane: allow virtio-1 devices s390x/virtio-ccw: support virtio-1 set_vq format s390x/virtio-ccw: enable virtio 1.0 Thomas Huth (3): linux-headers/virtio_config: Update with VIRTIO_F_VERSION_1 s390x/css: Add a callback for when subchannel gets disabled s390x/virtio-ccw: add virtio set-revision call hw/9pfs/virtio-9p-device.c | 7 +- hw/block/dataplane/virtio-blk.c | 3 +- hw/block/virtio-blk.c | 9 +- hw/char/virtio-serial-bus.c | 9 +- hw/net/virtio-net.c | 38 ++++--- hw/s390x/css.c | 12 +++ hw/s390x/css.h | 1 + hw/s390x/s390-virtio-bus.c | 9 +- hw/s390x/virtio-ccw.c | 188 +++++++++++++++++++++++++++-------- hw/s390x/virtio-ccw.h | 7 +- hw/scsi/vhost-scsi.c | 7 +- hw/scsi/virtio-scsi-dataplane.c | 2 +- hw/scsi/virtio-scsi.c | 8 +- hw/virtio/Makefile.objs | 2 +- hw/virtio/dataplane/Makefile.objs | 2 +- hw/virtio/dataplane/vring.c | 95 ++++++++++-------- hw/virtio/virtio-balloon.c | 8 +- hw/virtio/virtio-bus.c | 23 +---- hw/virtio/virtio-mmio.c | 9 +- hw/virtio/virtio-pci.c | 13 +-- hw/virtio/virtio-rng.c | 2 +- hw/virtio/virtio.c | 51 +++++++--- include/hw/virtio/dataplane/vring.h | 64 +++++++++++- include/hw/virtio/virtio-access.h | 4 + include/hw/virtio/virtio-bus.h | 10 +- include/hw/virtio/virtio.h | 34 +++++-- linux-headers/linux/virtio_config.h | 3 + 27 files changed, 442 insertions(+), 178 deletions(-) -- 1.7.9.5
Cornelia Huck
2014-Oct-07 14:39 UTC
[PATCH RFC 01/11] linux-headers/virtio_config: Update with VIRTIO_F_VERSION_1
From: Thomas Huth <thuth at linux.vnet.ibm.com> Add the new VIRTIO_F_VERSION_1 definition to the virtio_config.h linux header. Signed-off-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- linux-headers/linux/virtio_config.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-headers/linux/virtio_config.h b/linux-headers/linux/virtio_config.h index 75dc20b..16aa289 100644 --- a/linux-headers/linux/virtio_config.h +++ b/linux-headers/linux/virtio_config.h @@ -54,4 +54,7 @@ /* Can the device handle any descriptor layout? */ #define VIRTIO_F_ANY_LAYOUT 27 +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 + #endif /* _LINUX_VIRTIO_CONFIG_H */ -- 1.7.9.5
Cornelia Huck
2014-Oct-07 14:39 UTC
[PATCH RFC 02/11] virtio: cull virtio_bus_set_vdev_features
The only user of this function was virtio-ccw, and it should use virtio_set_features() like everybody else: We need to make sure that bad features are masked out properly, which this function did not do. Reviewed-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/s390x/virtio-ccw.c | 3 +-- hw/virtio/virtio-bus.c | 14 -------------- include/hw/virtio/virtio-bus.h | 3 --- 3 files changed, 1 insertion(+), 19 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e7d3ea1..7833dd2 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -400,8 +400,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ccw.cda + sizeof(features.features)); features.features = ldl_le_phys(&address_space_memory, ccw.cda); if (features.index < ARRAY_SIZE(dev->host_features)) { - virtio_bus_set_vdev_features(&dev->bus, features.features); - vdev->guest_features = features.features; + virtio_set_features(vdev, features.features); } else { /* * If the guest supports more feature bits, assert that it diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index eb77019..a8ffa07 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -109,20 +109,6 @@ uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, return k->get_features(vdev, requested_features); } -/* Set the features of the plugged device. */ -void virtio_bus_set_vdev_features(VirtioBusState *bus, - uint32_t requested_features) -{ - VirtIODevice *vdev = virtio_bus_get_device(bus); - VirtioDeviceClass *k; - - assert(vdev != NULL); - k = VIRTIO_DEVICE_GET_CLASS(vdev); - if (k->set_features != NULL) { - k->set_features(vdev, requested_features); - } -} - /* Get bad features of the plugged device. */ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) { diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 0756545..0d2e7b4 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -84,9 +84,6 @@ size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus); /* Get the features of the plugged device. */ uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, uint32_t requested_features); -/* Set the features of the plugged device. */ -void virtio_bus_set_vdev_features(VirtioBusState *bus, - uint32_t requested_features); /* Get bad features of the plugged device. */ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus); /* Get config of the plugged device. */ -- 1.7.9.5
With virtio-1, we support more than 32 feature bits. Let's make vdev->guest_features depend on the number of supported feature bits, allowing us to grow the feature bits automatically. We also need to enhance the internal functions dealing with getting and setting features with an additional index field, so that all feature bits may be accessed (in chunks of 32 bits). vhost and migration have been ignored for now. Reviewed-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/9pfs/virtio-9p-device.c | 7 ++++++- hw/block/virtio-blk.c | 9 +++++++-- hw/char/virtio-serial-bus.c | 9 +++++++-- hw/net/virtio-net.c | 38 ++++++++++++++++++++++++++------------ hw/s390x/s390-virtio-bus.c | 9 +++++---- hw/s390x/virtio-ccw.c | 17 ++++++++++------- hw/scsi/vhost-scsi.c | 7 +++++-- hw/scsi/virtio-scsi.c | 8 ++++---- hw/virtio/dataplane/vring.c | 10 +++++----- hw/virtio/virtio-balloon.c | 8 ++++++-- hw/virtio/virtio-bus.c | 9 +++++---- hw/virtio/virtio-mmio.c | 9 +++++---- hw/virtio/virtio-pci.c | 13 +++++++------ hw/virtio/virtio-rng.c | 2 +- hw/virtio/virtio.c | 29 +++++++++++++++++------------ include/hw/virtio/virtio-bus.h | 7 ++++--- include/hw/virtio/virtio.h | 19 ++++++++++++++----- 17 files changed, 134 insertions(+), 76 deletions(-) diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index 2572747..c29c8c8 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -21,8 +21,13 @@ #include "virtio-9p-coth.h" #include "hw/virtio/virtio-access.h" -static uint32_t virtio_9p_get_features(VirtIODevice *vdev, uint32_t features) +static uint32_t virtio_9p_get_features(VirtIODevice *vdev, unsigned int index, + uint32_t features) { + if (index > 0) { + return features; + } + features |= 1 << VIRTIO_9P_MOUNT_TAG; return features; } diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 45e0c8f..5abc327 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -561,10 +561,15 @@ static void virtio_blk_set_config(VirtIODevice *vdev, const uint8_t *config) aio_context_release(bdrv_get_aio_context(s->bs)); } -static uint32_t virtio_blk_get_features(VirtIODevice *vdev, uint32_t features) +static uint32_t virtio_blk_get_features(VirtIODevice *vdev, unsigned int index, + uint32_t features) { VirtIOBlock *s = VIRTIO_BLK(vdev); + if (index > 0) { + return features; + } + features |= (1 << VIRTIO_BLK_F_SEG_MAX); features |= (1 << VIRTIO_BLK_F_GEOMETRY); features |= (1 << VIRTIO_BLK_F_TOPOLOGY); @@ -597,7 +602,7 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) return; } - features = vdev->guest_features; + features = vdev->guest_features[0]; /* A guest that supports VIRTIO_BLK_F_CONFIG_WCE must be able to send * cache flushes. Thus, the "auto writethrough" behavior is never diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 3931085..0d843fe 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -75,7 +75,7 @@ static VirtIOSerialPort *find_port_by_name(char *name) static bool use_multiport(VirtIOSerial *vser) { VirtIODevice *vdev = VIRTIO_DEVICE(vser); - return vdev->guest_features & (1 << VIRTIO_CONSOLE_F_MULTIPORT); + return vdev->guest_features[0] & (1 << VIRTIO_CONSOLE_F_MULTIPORT); } static size_t write_to_port(VirtIOSerialPort *port, @@ -467,10 +467,15 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq) { } -static uint32_t get_features(VirtIODevice *vdev, uint32_t features) +static uint32_t get_features(VirtIODevice *vdev, unsigned int index, + uint32_t features) { VirtIOSerial *vser; + if (index > 0) { + return features; + } + vser = VIRTIO_SERIAL(vdev); if (vser->bus.max_nr_ports > 1) { diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 2040eac..67f91c0 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -86,7 +86,7 @@ static void virtio_net_set_config(VirtIODevice *vdev, const uint8_t *config) memcpy(&netcfg, config, n->config_size); - if (!(vdev->guest_features >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) && + if (!(vdev->guest_features[0] >> VIRTIO_NET_F_CTRL_MAC_ADDR & 1) && memcmp(netcfg.mac, n->mac, ETH_ALEN)) { memcpy(n->mac, netcfg.mac, ETH_ALEN); qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); @@ -305,7 +305,7 @@ static RxFilterInfo *virtio_net_query_rxfilter(NetClientState *nc) info->multicast_table = str_list; info->vlan_table = get_vlan_table(n); - if (!((1 << VIRTIO_NET_F_CTRL_VLAN) & vdev->guest_features)) { + if (!((1 << VIRTIO_NET_F_CTRL_VLAN) & vdev->guest_features[0])) { info->vlan = RX_STATE_ALL; } else if (!info->vlan_table) { info->vlan = RX_STATE_NONE; @@ -441,11 +441,16 @@ static void virtio_net_set_queues(VirtIONet *n) static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue); -static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) +static uint32_t virtio_net_get_features(VirtIODevice *vdev, unsigned int index, + uint32_t features) { VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_queue(n->nic); + if (index > 0) { + return features; + } + features |= (1 << VIRTIO_NET_F_MAC); if (!peer_has_vnet_hdr(n)) { @@ -471,10 +476,14 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, uint32_t features) return vhost_net_get_features(get_vhost_net(nc->peer), features); } -static uint32_t virtio_net_bad_features(VirtIODevice *vdev) +static uint32_t virtio_net_bad_features(VirtIODevice *vdev, unsigned int index) { uint32_t features = 0; + if (index > 0) { + return 0; + } + /* Linux kernel 2.6.25. It understood MAC (as everyone must), * but also these: */ features |= (1 << VIRTIO_NET_F_MAC); @@ -511,14 +520,19 @@ static uint64_t virtio_net_guest_offloads_by_features(uint32_t features) static inline uint64_t virtio_net_supported_guest_offloads(VirtIONet *n) { VirtIODevice *vdev = VIRTIO_DEVICE(n); - return virtio_net_guest_offloads_by_features(vdev->guest_features); + return virtio_net_guest_offloads_by_features(vdev->guest_features[0]); } -static void virtio_net_set_features(VirtIODevice *vdev, uint32_t features) +static void virtio_net_set_features(VirtIODevice *vdev, unsigned int index, + uint32_t features) { VirtIONet *n = VIRTIO_NET(vdev); int i; + if (index > 0) { + return; + } + virtio_net_set_multiqueue(n, !!(features & (1 << VIRTIO_NET_F_MQ))); virtio_net_set_mrg_rx_bufs(n, !!(features & (1 << VIRTIO_NET_F_MRG_RXBUF))); @@ -585,7 +599,7 @@ static int virtio_net_handle_offloads(VirtIONet *n, uint8_t cmd, uint64_t offloads; size_t s; - if (!((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features)) { + if (!((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features[0])) { return VIRTIO_NET_ERR; } @@ -1034,7 +1048,7 @@ static ssize_t virtio_net_receive(NetClientState *nc, const uint8_t *buf, size_t "i %zd mergeable %d offset %zd, size %zd, " "guest hdr len %zd, host hdr len %zd guest features 0x%x", i, n->mergeable_rx_bufs, offset, size, - n->guest_hdr_len, n->host_hdr_len, vdev->guest_features); + n->guest_hdr_len, n->host_hdr_len, vdev->guest_features[0]); exit(1); } @@ -1377,7 +1391,7 @@ static void virtio_net_save_device(VirtIODevice *vdev, QEMUFile *f) } } - if ((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features) { + if ((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features[0]) { qemu_put_be64(f, n->curr_guest_offloads); } } @@ -1485,7 +1499,7 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, } } - if ((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features) { + if ((1 << VIRTIO_NET_F_CTRL_GUEST_OFFLOADS) & vdev->guest_features[0]) { n->curr_guest_offloads = qemu_get_be64(f); } else { n->curr_guest_offloads = virtio_net_supported_guest_offloads(n); @@ -1512,8 +1526,8 @@ static int virtio_net_load_device(VirtIODevice *vdev, QEMUFile *f, qemu_get_subqueue(n->nic, i)->link_down = link_down; } - if (vdev->guest_features & (0x1 << VIRTIO_NET_F_GUEST_ANNOUNCE) && - vdev->guest_features & (0x1 << VIRTIO_NET_F_CTRL_VQ)) { + if (vdev->guest_features[0] & (0x1 << VIRTIO_NET_F_GUEST_ANNOUNCE) && + vdev->guest_features[0] & (0x1 << VIRTIO_NET_F_CTRL_VQ)) { n->announce_counter = SELF_ANNOUNCE_ROUNDS; timer_mod(n->announce_timer, qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL)); } diff --git a/hw/s390x/s390-virtio-bus.c b/hw/s390x/s390-virtio-bus.c index f451ca1..1ddf133 100644 --- a/hw/s390x/s390-virtio-bus.c +++ b/hw/s390x/s390-virtio-bus.c @@ -128,7 +128,7 @@ static int s390_virtio_device_init(VirtIOS390Device *dev, VirtIODevice *vdev) bus->dev_offs += dev_len; - dev->host_features = virtio_bus_get_vdev_features(&dev->bus, + dev->host_features = virtio_bus_get_vdev_features(&dev->bus, 0, dev->host_features); s390_virtio_device_sync(dev); s390_virtio_reset_idx(dev); @@ -417,7 +417,7 @@ void s390_virtio_device_update_status(VirtIOS390Device *dev) /* Update guest supported feature bitmap */ features = bswap32(ldl_be_phys(&address_space_memory, dev->feat_offs)); - virtio_set_features(vdev, features); + virtio_set_features(vdev, 0, features); } VirtIOS390Device *s390_virtio_bus_console(VirtIOS390Bus *bus) @@ -488,10 +488,11 @@ static void virtio_s390_notify(DeviceState *d, uint16_t vector) s390_virtio_irq(0, token); } -static unsigned virtio_s390_get_features(DeviceState *d) +static unsigned virtio_s390_get_features(DeviceState *d, unsigned int index) { VirtIOS390Device *dev = to_virtio_s390_device(d); - return dev->host_features; + + return index == 0 ? dev->host_features : 0; } /**************** S390 Virtio Bus Device Descriptions *******************/ diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 7833dd2..8aa79a7 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -400,7 +400,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ccw.cda + sizeof(features.features)); features.features = ldl_le_phys(&address_space_memory, ccw.cda); if (features.index < ARRAY_SIZE(dev->host_features)) { - virtio_set_features(vdev, features.features); + virtio_set_features(vdev, features.index, features.features); } else { /* * If the guest supports more feature bits, assert that it @@ -619,6 +619,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) int ret; int num; DeviceState *parent = DEVICE(dev); + int i; sch = g_malloc0(sizeof(SubchDev)); @@ -739,9 +740,11 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->id.cu_type = VIRTIO_CCW_CU_TYPE; sch->id.cu_model = vdev->device_id; - /* Only the first 32 feature bits are used. */ - dev->host_features[0] = virtio_bus_get_vdev_features(&dev->bus, - dev->host_features[0]); + /* Set default feature bits that are offered by the host. */ + for (i = 0; i < ARRAY_SIZE(dev->host_features); i++) { + dev->host_features[i] + virtio_bus_get_vdev_features(&dev->bus, i, dev->host_features[i]); + } dev->host_features[0] |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; @@ -1059,12 +1062,12 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector) } } -static unsigned virtio_ccw_get_features(DeviceState *d) +static unsigned virtio_ccw_get_features(DeviceState *d, unsigned int index) { VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d); - /* Only the first 32 feature bits are used. */ - return dev->host_features[0]; + return index < ARRAY_SIZE(dev->host_features) ? + dev->host_features[index] : 0; } static void virtio_ccw_reset(DeviceState *d) diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 308b393..8e1afa0 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -92,7 +92,7 @@ static int vhost_scsi_start(VHostSCSI *s) return ret; } - s->dev.acked_features = vdev->guest_features; + s->dev.acked_features = vdev->guest_features[0]; ret = vhost_dev_start(&s->dev, vdev); if (ret < 0) { error_report("Error start vhost dev"); @@ -150,11 +150,14 @@ static void vhost_scsi_stop(VHostSCSI *s) vhost_dev_disable_notifiers(&s->dev, vdev); } -static uint32_t vhost_scsi_get_features(VirtIODevice *vdev, +static uint32_t vhost_scsi_get_features(VirtIODevice *vdev, unsigned int index, uint32_t features) { VHostSCSI *s = VHOST_SCSI(vdev); + if (index > 0) { + return features; + } return vhost_get_features(&s->dev, kernel_feature_bits, features); } diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 203e624..088e688 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -608,7 +608,7 @@ static void virtio_scsi_set_config(VirtIODevice *vdev, vs->cdb_size = virtio_ldl_p(vdev, &scsiconf->cdb_size); } -static uint32_t virtio_scsi_get_features(VirtIODevice *vdev, +static uint32_t virtio_scsi_get_features(VirtIODevice *vdev, unsigned int index, uint32_t requested_features) { return requested_features; @@ -729,7 +729,7 @@ static void virtio_scsi_change(SCSIBus *bus, SCSIDevice *dev, SCSISense sense) VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIODevice *vdev = VIRTIO_DEVICE(s); - if (((vdev->guest_features >> VIRTIO_SCSI_F_CHANGE) & 1) && + if (((vdev->guest_features[0] >> VIRTIO_SCSI_F_CHANGE) & 1) && dev->type != TYPE_ROM) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_PARAM_CHANGE, sense.asc | (sense.ascq << 8)); @@ -741,7 +741,7 @@ static void virtio_scsi_hotplug(SCSIBus *bus, SCSIDevice *dev) VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIODevice *vdev = VIRTIO_DEVICE(s); - if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { + if ((vdev->guest_features[0] >> VIRTIO_SCSI_F_HOTPLUG) & 1) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_RESCAN); } @@ -752,7 +752,7 @@ static void virtio_scsi_hot_unplug(SCSIBus *bus, SCSIDevice *dev) VirtIOSCSI *s = container_of(bus, VirtIOSCSI, bus); VirtIODevice *vdev = VIRTIO_DEVICE(s); - if ((vdev->guest_features >> VIRTIO_SCSI_F_HOTPLUG) & 1) { + if ((vdev->guest_features[0] >> VIRTIO_SCSI_F_HOTPLUG) & 1) { virtio_scsi_push_event(s, dev, VIRTIO_SCSI_T_TRANSPORT_RESET, VIRTIO_SCSI_EVT_RESET_REMOVED); } diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index 372706a..b84957f 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -103,7 +103,7 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n) /* Disable guest->host notifies */ void vring_disable_notification(VirtIODevice *vdev, Vring *vring) { - if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + if (!(vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX))) { vring->vr.used->flags |= VRING_USED_F_NO_NOTIFY; } } @@ -114,7 +114,7 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring) */ bool vring_enable_notification(VirtIODevice *vdev, Vring *vring) { - if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + if (vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(&vring->vr) = vring->vr.avail->idx; } else { vring->vr.used->flags &= ~VRING_USED_F_NO_NOTIFY; @@ -133,12 +133,12 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring) * interrupts. */ smp_mb(); - if ((vdev->guest_features & VIRTIO_F_NOTIFY_ON_EMPTY) && + if ((vdev->guest_features[0] & VIRTIO_F_NOTIFY_ON_EMPTY) && unlikely(vring->vr.avail->idx == vring->last_avail_idx)) { return true; } - if (!(vdev->guest_features & VIRTIO_RING_F_EVENT_IDX)) { + if (!(vdev->guest_features[0] & VIRTIO_RING_F_EVENT_IDX)) { return !(vring->vr.avail->flags & VRING_AVAIL_F_NO_INTERRUPT); } old = vring->signalled_used; @@ -352,7 +352,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, goto out; } - if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + if (vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(&vring->vr) = vring->vr.avail->idx; } diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index b5cf7ca..5af17e2 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -69,7 +69,7 @@ static inline void reset_stats(VirtIOBalloon *dev) static bool balloon_stats_supported(const VirtIOBalloon *s) { VirtIODevice *vdev = VIRTIO_DEVICE(s); - return vdev->guest_features & (1 << VIRTIO_BALLOON_F_STATS_VQ); + return vdev->guest_features[0] & (1 << VIRTIO_BALLOON_F_STATS_VQ); } static bool balloon_stats_enabled(const VirtIOBalloon *s) @@ -303,8 +303,12 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, } } -static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, uint32_t f) +static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, + unsigned int index, uint32_t f) { + if (index > 0) { + return f; + } f |= (1 << VIRTIO_BALLOON_F_STATS_VQ); return f; } diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index a8ffa07..503114d 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -97,7 +97,7 @@ size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus) } /* Get the features of the plugged device. */ -uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, +uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, unsigned int index, uint32_t requested_features) { VirtIODevice *vdev = virtio_bus_get_device(bus); @@ -106,11 +106,12 @@ uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, assert(vdev != NULL); k = VIRTIO_DEVICE_GET_CLASS(vdev); assert(k->get_features != NULL); - return k->get_features(vdev, requested_features); + return k->get_features(vdev, index, requested_features); } /* Get bad features of the plugged device. */ -uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) +uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus, + unsigned int index) { VirtIODevice *vdev = virtio_bus_get_device(bus); VirtioDeviceClass *k; @@ -118,7 +119,7 @@ uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus) assert(vdev != NULL); k = VIRTIO_DEVICE_GET_CLASS(vdev); if (k->bad_features != NULL) { - return k->bad_features(vdev); + return k->bad_features(vdev, index); } else { return 0; } diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 18c6e5b..b4282c4 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -225,7 +225,7 @@ static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value, break; case VIRTIO_MMIO_GUESTFEATURES: if (!proxy->guest_features_sel) { - virtio_set_features(vdev, value); + virtio_set_features(vdev, 0, value); } break; case VIRTIO_MMIO_GUESTFEATURESSEL: @@ -309,11 +309,12 @@ static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector) qemu_set_irq(proxy->irq, level); } -static unsigned int virtio_mmio_get_features(DeviceState *opaque) +static unsigned int virtio_mmio_get_features(DeviceState *opaque, + unsigned int index) { VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); - return proxy->host_features; + return index == 0 ? proxy->host_features : 0; } static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f) @@ -353,7 +354,7 @@ static void virtio_mmio_device_plugged(DeviceState *opaque) VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque); proxy->host_features |= (0x1 << VIRTIO_F_NOTIFY_ON_EMPTY); - proxy->host_features = virtio_bus_get_vdev_features(&proxy->bus, + proxy->host_features = virtio_bus_get_vdev_features(&proxy->bus, 0, proxy->host_features); } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 390f824..b22fc64 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -275,9 +275,9 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) case VIRTIO_PCI_GUEST_FEATURES: /* Guest does not negotiate properly? We have to assume nothing. */ if (val & (1 << VIRTIO_F_BAD_FEATURE)) { - val = virtio_bus_get_vdev_bad_features(&proxy->bus); + val = virtio_bus_get_vdev_bad_features(&proxy->bus, 0); } - virtio_set_features(vdev, val); + virtio_set_features(vdev, 0, val); break; case VIRTIO_PCI_QUEUE_PFN: pa = (hwaddr)val << VIRTIO_PCI_QUEUE_ADDR_SHIFT; @@ -364,7 +364,7 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr) ret = proxy->host_features; break; case VIRTIO_PCI_GUEST_FEATURES: - ret = vdev->guest_features; + ret = vdev->guest_features[0]; break; case VIRTIO_PCI_QUEUE_PFN: ret = virtio_queue_get_addr(vdev, vdev->queue_sel) @@ -490,10 +490,11 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, } } -static unsigned virtio_pci_get_features(DeviceState *d) +static unsigned virtio_pci_get_features(DeviceState *d, unsigned int index) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - return proxy->host_features; + + return index == 0 ? proxy->host_features : 0; } static int kvm_virtio_pci_vq_vector_use(VirtIOPCIProxy *proxy, @@ -1006,7 +1007,7 @@ static void virtio_pci_device_plugged(DeviceState *d) proxy->host_features |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; proxy->host_features |= 0x1 << VIRTIO_F_BAD_FEATURE; - proxy->host_features = virtio_bus_get_vdev_features(bus, + proxy->host_features = virtio_bus_get_vdev_features(bus, 0, proxy->host_features); } diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index e85a979..2814017 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -99,7 +99,7 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq) virtio_rng_process(vrng); } -static uint32_t get_features(VirtIODevice *vdev, uint32_t f) +static uint32_t get_features(VirtIODevice *vdev, unsigned int index, uint32_t f) { return f; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2c236bf..7aaa953 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -217,7 +217,7 @@ static inline void vring_avail_event(VirtQueue *vq, uint16_t val) void virtio_queue_set_notification(VirtQueue *vq, int enable) { vq->notification = enable; - if (vq->vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + if (vq->vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(vq, vring_avail_idx(vq)); } else if (enable) { vring_used_flags_unset_bit(vq, VRING_USED_F_NO_NOTIFY); @@ -468,7 +468,7 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) max = vq->vring.num; i = head = virtqueue_get_head(vq, vq->last_avail_idx++); - if (vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX)) { + if (vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(vq, vring_avail_idx(vq)); } @@ -592,7 +592,10 @@ void virtio_reset(void *opaque) k->reset(vdev); } - vdev->guest_features = 0; + for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) { + vdev->guest_features[i] = 0; + } + vdev->queue_sel = 0; vdev->status = 0; vdev->isr = 0; @@ -839,12 +842,12 @@ static bool vring_notify(VirtIODevice *vdev, VirtQueue *vq) /* We need to expose used array entries before checking used event. */ smp_mb(); /* Always notify when queue is empty (when feature acknowledge) */ - if (((vdev->guest_features & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) && + if (((vdev->guest_features[0] & (1 << VIRTIO_F_NOTIFY_ON_EMPTY)) && !vq->inuse && vring_avail_idx(vq) == vq->last_avail_idx)) { return true; } - if (!(vdev->guest_features & (1 << VIRTIO_RING_F_EVENT_IDX))) { + if (!(vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX))) { return !(vring_avail_flags(vq) & VRING_AVAIL_F_NO_INTERRUPT); } @@ -924,7 +927,8 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) qemu_put_8s(f, &vdev->status); qemu_put_8s(f, &vdev->isr); qemu_put_be16s(f, &vdev->queue_sel); - qemu_put_be32s(f, &vdev->guest_features); + /* XXX features >= 32 */ + qemu_put_be32s(f, &vdev->guest_features[0]); qemu_put_be32(f, vdev->config_len); qemu_put_buffer(f, vdev->config, vdev->config_len); @@ -958,19 +962,19 @@ void virtio_save(VirtIODevice *vdev, QEMUFile *f) vmstate_save_state(f, &vmstate_virtio, vdev); } -int virtio_set_features(VirtIODevice *vdev, uint32_t val) +int virtio_set_features(VirtIODevice *vdev, unsigned int index, uint32_t val) { BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); VirtioBusClass *vbusk = VIRTIO_BUS_GET_CLASS(qbus); VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - uint32_t supported_features = vbusk->get_features(qbus->parent); + uint32_t supported_features = vbusk->get_features(qbus->parent, index); bool bad = (val & ~supported_features) != 0; val &= supported_features; if (k->set_features) { - k->set_features(vdev, val); + k->set_features(vdev, index, val); } - vdev->guest_features = val; + vdev->guest_features[index] = val; return bad ? -1 : 0; } @@ -1005,8 +1009,9 @@ int virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) } qemu_get_be32s(f, &features); - if (virtio_set_features(vdev, features) < 0) { - supported_features = k->get_features(qbus->parent); + /* XXX features >= 32 */ + if (virtio_set_features(vdev, 0, features) < 0) { + supported_features = k->get_features(qbus->parent, 0); error_report("Features 0x%x unsupported. Allowed features: 0x%x", features, supported_features); return -1; diff --git a/include/hw/virtio/virtio-bus.h b/include/hw/virtio/virtio-bus.h index 0d2e7b4..03f4432 100644 --- a/include/hw/virtio/virtio-bus.h +++ b/include/hw/virtio/virtio-bus.h @@ -47,7 +47,7 @@ typedef struct VirtioBusClass { int (*load_config)(DeviceState *d, QEMUFile *f); int (*load_queue)(DeviceState *d, int n, QEMUFile *f); int (*load_done)(DeviceState *d, QEMUFile *f); - unsigned (*get_features)(DeviceState *d); + unsigned (*get_features)(DeviceState *d, unsigned int index); bool (*query_guest_notifiers)(DeviceState *d); int (*set_guest_notifiers)(DeviceState *d, int nvqs, bool assign); int (*set_host_notifier)(DeviceState *d, int n, bool assigned); @@ -82,10 +82,11 @@ uint16_t virtio_bus_get_vdev_id(VirtioBusState *bus); /* Get the config_len field of the plugged device. */ size_t virtio_bus_get_vdev_config_len(VirtioBusState *bus); /* Get the features of the plugged device. */ -uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, +uint32_t virtio_bus_get_vdev_features(VirtioBusState *bus, unsigned int index, uint32_t requested_features); /* Get bad features of the plugged device. */ -uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus); +uint32_t virtio_bus_get_vdev_bad_features(VirtioBusState *bus, + unsigned int index); /* Get config of the plugged device. */ void virtio_bus_get_vdev_config(VirtioBusState *bus, uint8_t *config); /* Set config of the plugged device. */ diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 0726d76..b408166 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -55,6 +55,12 @@ /* A guest should never accept this. It implies negotiation is broken. */ #define VIRTIO_F_BAD_FEATURE 30 +/* v1.0 compliant. */ +#define VIRTIO_F_VERSION_1 32 + +/* The highest feature bit that we support */ +#define VIRTIO_HIGHEST_FEATURE_BIT 32 + /* from Linux's linux/virtio_ring.h */ /* This marks a buffer as continuing via the next field. */ @@ -110,6 +116,8 @@ enum virtio_device_endian { VIRTIO_DEVICE_ENDIAN_BIG, }; +#define NR_VIRTIO_FEATURE_WORDS ((VIRTIO_HIGHEST_FEATURE_BIT + 32) / 32) + struct VirtIODevice { DeviceState parent_obj; @@ -117,7 +125,7 @@ struct VirtIODevice uint8_t status; uint8_t isr; uint16_t queue_sel; - uint32_t guest_features; + uint32_t guest_features[NR_VIRTIO_FEATURE_WORDS]; size_t config_len; void *config; uint16_t config_vector; @@ -138,9 +146,10 @@ typedef struct VirtioDeviceClass { /* This is what a VirtioDevice must implement */ DeviceRealize realize; DeviceUnrealize unrealize; - uint32_t (*get_features)(VirtIODevice *vdev, uint32_t requested_features); - uint32_t (*bad_features)(VirtIODevice *vdev); - void (*set_features)(VirtIODevice *vdev, uint32_t val); + uint32_t (*get_features)(VirtIODevice *vdev, unsigned int index, + uint32_t requested_features); + uint32_t (*bad_features)(VirtIODevice *vdev, unsigned int index); + void (*set_features)(VirtIODevice *vdev, unsigned int index, uint32_t val); void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); @@ -225,7 +234,7 @@ void virtio_queue_set_vector(VirtIODevice *vdev, int n, uint16_t vector); void virtio_set_status(VirtIODevice *vdev, uint8_t val); void virtio_reset(void *opaque); void virtio_update_irq(VirtIODevice *vdev); -int virtio_set_features(VirtIODevice *vdev, uint32_t val); +int virtio_set_features(VirtIODevice *vdev, unsigned int index, uint32_t val); /* Base devices. */ typedef struct VirtIOBlkConf VirtIOBlkConf; -- 1.7.9.5
Cornelia Huck
2014-Oct-07 14:40 UTC
[PATCH RFC 04/11] s390x/virtio-ccw: fix check for WRITE_FEAT
We need to check guest feature size, not host feature size to find out whether we should call virtio_set_features(). This check is possible now that vdev->guest_features is an array. Reviewed-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/s390x/virtio-ccw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 8aa79a7..69add47 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -399,7 +399,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) features.index = ldub_phys(&address_space_memory, ccw.cda + sizeof(features.features)); features.features = ldl_le_phys(&address_space_memory, ccw.cda); - if (features.index < ARRAY_SIZE(dev->host_features)) { + if (features.index < ARRAY_SIZE(vdev->guest_features)) { virtio_set_features(vdev, features.index, features.features); } else { /* -- 1.7.9.5
Cornelia Huck
2014-Oct-07 14:40 UTC
[PATCH RFC 05/11] virtio: introduce legacy virtio devices
Introduce a helper function to indicate whether a virtio device is operating in legacy or virtio standard mode. It may be used to make decisions about the endianess of virtio accesses and other virtio-1 specific changes, enabling us to support transitional devices. Reviewed-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/virtio/virtio.c | 6 +++++- include/hw/virtio/virtio-access.h | 4 ++++ include/hw/virtio/virtio.h | 13 +++++++++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 7aaa953..e6ae3a0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -883,7 +883,11 @@ static bool virtio_device_endian_needed(void *opaque) VirtIODevice *vdev = opaque; assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); - return vdev->device_endian != virtio_default_endian(); + if (virtio_device_is_legacy(vdev)) { + return vdev->device_endian != virtio_default_endian(); + } + /* Devices conforming to VIRTIO 1.0 or later are always LE. */ + return vdev->device_endian != VIRTIO_DEVICE_ENDIAN_LITTLE; } static const VMStateDescription vmstate_virtio_device_endian = { diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h index 46456fd..c123ee0 100644 --- a/include/hw/virtio/virtio-access.h +++ b/include/hw/virtio/virtio-access.h @@ -19,6 +19,10 @@ static inline bool virtio_access_is_big_endian(VirtIODevice *vdev) { + if (!virtio_device_is_legacy(vdev)) { + /* Devices conforming to VIRTIO 1.0 or later are always LE. */ + return false; + } #if defined(TARGET_IS_BIENDIAN) return virtio_is_big_endian(vdev); #elif defined(TARGET_WORDS_BIGENDIAN) diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index b408166..40e567c 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -275,9 +275,18 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, void virtio_queue_notify_vq(VirtQueue *vq); void virtio_irq(VirtQueue *vq); +static inline bool virtio_device_is_legacy(VirtIODevice *vdev) +{ + return !(vdev->guest_features[1] & (1 << (VIRTIO_F_VERSION_1 - 32))); +} + static inline bool virtio_is_big_endian(VirtIODevice *vdev) { - assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); - return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; + if (virtio_device_is_legacy(vdev)) { + assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); + return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; + } + /* Devices conforming to VIRTIO 1.0 or later are always LE. */ + return false; } #endif -- 1.7.9.5
For virtio-1 devices, we allow a more complex queue layout that doesn't require descriptor table and rings on a physically-contigous memory area: add virtio_queue_set_rings() to allow transports to set this up. Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/virtio/virtio.c | 16 ++++++++++++++++ include/hw/virtio/virtio.h | 2 ++ 2 files changed, 18 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index e6ae3a0..147d227 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -96,6 +96,13 @@ static void virtqueue_init(VirtQueue *vq) { hwaddr pa = vq->pa; + if (pa == -1ULL) { + /* + * This is a virtio-1 style vq that has already been setup + * in virtio_queue_set. + */ + return; + } vq->vring.desc = pa; vq->vring.avail = pa + vq->vring.num * sizeof(VRingDesc); vq->vring.used = vring_align(vq->vring.avail + @@ -719,6 +726,15 @@ hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n) return vdev->vq[n].pa; } +void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, + hwaddr avail, hwaddr used) +{ + vdev->vq[n].pa = -1ULL; + vdev->vq[n].vring.desc = desc; + vdev->vq[n].vring.avail = avail; + vdev->vq[n].vring.used = used; +} + void virtio_queue_set_num(VirtIODevice *vdev, int n, int num) { /* Don't allow guest to flip queue between existent and diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 40e567c..f840320 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -227,6 +227,8 @@ void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr); hwaddr virtio_queue_get_addr(VirtIODevice *vdev, int n); void virtio_queue_set_num(VirtIODevice *vdev, int n, int num); int virtio_queue_get_num(VirtIODevice *vdev, int n); +void virtio_queue_set_rings(VirtIODevice *vdev, int n, hwaddr desc, + hwaddr avail, hwaddr used); void virtio_queue_set_align(VirtIODevice *vdev, int n, int align); void virtio_queue_notify(VirtIODevice *vdev, int n); uint16_t virtio_queue_vector(VirtIODevice *vdev, int n); -- 1.7.9.5
Handle endianness conversion for virtio-1 virtqueues correctly. Note that dataplane now needs to be built per-target. Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/block/dataplane/virtio-blk.c | 3 +- hw/scsi/virtio-scsi-dataplane.c | 2 +- hw/virtio/Makefile.objs | 2 +- hw/virtio/dataplane/Makefile.objs | 2 +- hw/virtio/dataplane/vring.c | 85 +++++++++++++++++++---------------- include/hw/virtio/dataplane/vring.h | 64 ++++++++++++++++++++++++-- 6 files changed, 113 insertions(+), 45 deletions(-) diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c index 5458f9d..eb45a3d 100644 --- a/hw/block/dataplane/virtio-blk.c +++ b/hw/block/dataplane/virtio-blk.c @@ -16,6 +16,7 @@ #include "qemu/iov.h" #include "qemu/thread.h" #include "qemu/error-report.h" +#include "hw/virtio/virtio-access.h" #include "hw/virtio/dataplane/vring.h" #include "block/block.h" #include "hw/virtio/virtio-blk.h" @@ -75,7 +76,7 @@ static void complete_request_vring(VirtIOBlockReq *req, unsigned char status) VirtIOBlockDataPlane *s = req->dev->dataplane; stb_p(&req->in->status, status); - vring_push(&req->dev->dataplane->vring, &req->elem, + vring_push(s->vdev, &req->dev->dataplane->vring, &req->elem, req->qiov.size + sizeof(*req->in)); /* Suppress notification to guest by BH and its scheduled diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c index b778e05..3e2b706 100644 --- a/hw/scsi/virtio-scsi-dataplane.c +++ b/hw/scsi/virtio-scsi-dataplane.c @@ -81,7 +81,7 @@ VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) { - vring_push(&req->vring->vring, &req->elem, + vring_push((VirtIODevice *)req->dev, &req->vring->vring, &req->elem, req->qsgl.size + req->resp_iov.size); event_notifier_set(&req->vring->guest_notifier); } diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs index d21c397..19b224a 100644 --- a/hw/virtio/Makefile.objs +++ b/hw/virtio/Makefile.objs @@ -2,7 +2,7 @@ common-obj-y += virtio-rng.o common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o common-obj-y += virtio-bus.o common-obj-y += virtio-mmio.o -common-obj-$(CONFIG_VIRTIO) += dataplane/ +obj-$(CONFIG_VIRTIO) += dataplane/ obj-y += virtio.o virtio-balloon.o obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o diff --git a/hw/virtio/dataplane/Makefile.objs b/hw/virtio/dataplane/Makefile.objs index 9a8cfc0..753a9ca 100644 --- a/hw/virtio/dataplane/Makefile.objs +++ b/hw/virtio/dataplane/Makefile.objs @@ -1 +1 @@ -common-obj-y += vring.o +obj-y += vring.o diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c index b84957f..4624521 100644 --- a/hw/virtio/dataplane/vring.c +++ b/hw/virtio/dataplane/vring.c @@ -18,6 +18,7 @@ #include "hw/hw.h" #include "exec/memory.h" #include "exec/address-spaces.h" +#include "hw/virtio/virtio-access.h" #include "hw/virtio/dataplane/vring.h" #include "qemu/error-report.h" @@ -83,7 +84,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int n) vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096); vring->last_avail_idx = virtio_queue_get_last_avail_idx(vdev, n); - vring->last_used_idx = vring->vr.used->idx; + vring->last_used_idx = vring_get_used_idx(vdev, vring); vring->signalled_used = 0; vring->signalled_used_valid = false; @@ -104,7 +105,7 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n) void vring_disable_notification(VirtIODevice *vdev, Vring *vring) { if (!(vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX))) { - vring->vr.used->flags |= VRING_USED_F_NO_NOTIFY; + vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); } } @@ -117,10 +118,10 @@ bool vring_enable_notification(VirtIODevice *vdev, Vring *vring) if (vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX)) { vring_avail_event(&vring->vr) = vring->vr.avail->idx; } else { - vring->vr.used->flags &= ~VRING_USED_F_NO_NOTIFY; + vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); } smp_mb(); /* ensure update is seen before reading avail_idx */ - return !vring_more_avail(vring); + return !vring_more_avail(vdev, vring); } /* This is stolen from linux/drivers/vhost/vhost.c:vhost_notify() */ @@ -134,12 +135,13 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring) smp_mb(); if ((vdev->guest_features[0] & VIRTIO_F_NOTIFY_ON_EMPTY) && - unlikely(vring->vr.avail->idx == vring->last_avail_idx)) { + unlikely(!vring_more_avail(vdev, vring))) { return true; } if (!(vdev->guest_features[0] & VIRTIO_RING_F_EVENT_IDX)) { - return !(vring->vr.avail->flags & VRING_AVAIL_F_NO_INTERRUPT); + return !(vring_get_avail_flags(vdev, vring) & + VRING_AVAIL_F_NO_INTERRUPT); } old = vring->signalled_used; v = vring->signalled_used_valid; @@ -154,15 +156,18 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring) } -static int get_desc(Vring *vring, VirtQueueElement *elem, +static int get_desc(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, struct vring_desc *desc) { unsigned *num; struct iovec *iov; hwaddr *addr; MemoryRegion *mr; + int is_write = virtio_tswap16(vdev, desc->flags) & VRING_DESC_F_WRITE; + uint32_t len = virtio_tswap32(vdev, desc->len); + uint64_t desc_addr = virtio_tswap64(vdev, desc->addr); - if (desc->flags & VRING_DESC_F_WRITE) { + if (is_write) { num = &elem->in_num; iov = &elem->in_sg[*num]; addr = &elem->in_addr[*num]; @@ -186,44 +191,45 @@ static int get_desc(Vring *vring, VirtQueueElement *elem, } /* TODO handle non-contiguous memory across region boundaries */ - iov->iov_base = vring_map(&mr, desc->addr, desc->len, - desc->flags & VRING_DESC_F_WRITE); + iov->iov_base = vring_map(&mr, desc_addr, len, is_write); if (!iov->iov_base) { error_report("Failed to map descriptor addr %#" PRIx64 " len %u", - (uint64_t)desc->addr, desc->len); + (uint64_t)desc_addr, len); return -EFAULT; } /* The MemoryRegion is looked up again and unref'ed later, leave the * ref in place. */ - iov->iov_len = desc->len; - *addr = desc->addr; + iov->iov_len = len; + *addr = desc_addr; *num += 1; return 0; } /* This is stolen from linux/drivers/vhost/vhost.c. */ -static int get_indirect(Vring *vring, VirtQueueElement *elem, - struct vring_desc *indirect) +static int get_indirect(VirtIODevice *vdev, Vring *vring, + VirtQueueElement *elem, struct vring_desc *indirect) { struct vring_desc desc; unsigned int i = 0, count, found = 0; int ret; + uint32_t len = virtio_tswap32(vdev, indirect->len); + uint64_t addr = virtio_tswap64(vdev, indirect->addr); /* Sanity check */ - if (unlikely(indirect->len % sizeof(desc))) { + if (unlikely(len % sizeof(desc))) { error_report("Invalid length in indirect descriptor: " "len %#x not multiple of %#zx", - indirect->len, sizeof(desc)); + len, sizeof(desc)); vring->broken = true; return -EFAULT; } - count = indirect->len / sizeof(desc); + count = len / sizeof(desc); /* Buffers are chained via a 16 bit next field, so * we can have at most 2^16 of these. */ if (unlikely(count > USHRT_MAX + 1)) { - error_report("Indirect buffer length too big: %d", indirect->len); + error_report("Indirect buffer length too big: %d", len); vring->broken = true; return -EFAULT; } @@ -234,12 +240,12 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem, /* Translate indirect descriptor */ desc_ptr = vring_map(&mr, - indirect->addr + found * sizeof(desc), + addr + found * sizeof(desc), sizeof(desc), false); if (!desc_ptr) { error_report("Failed to map indirect descriptor " "addr %#" PRIx64 " len %zu", - (uint64_t)indirect->addr + found * sizeof(desc), + (uint64_t)addr + found * sizeof(desc), sizeof(desc)); vring->broken = true; return -EFAULT; @@ -257,19 +263,20 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem, return -EFAULT; } - if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) { + if (unlikely(virtio_tswap16(vdev, desc.flags) + & VRING_DESC_F_INDIRECT)) { error_report("Nested indirect descriptor"); vring->broken = true; return -EFAULT; } - ret = get_desc(vring, elem, &desc); + ret = get_desc(vdev, vring, elem, &desc); if (ret < 0) { vring->broken |= (ret == -EFAULT); return ret; } - i = desc.next; - } while (desc.flags & VRING_DESC_F_NEXT); + i = virtio_tswap16(vdev, desc.next); + } while (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_NEXT); return 0; } @@ -320,7 +327,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* Check it isn't doing very strange things with descriptor numbers. */ last_avail_idx = vring->last_avail_idx; - avail_idx = vring->vr.avail->idx; + avail_idx = vring_get_avail_idx(vdev, vring); barrier(); /* load indices now and not again later */ if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) { @@ -341,7 +348,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* Grab the next descriptor number they're advertising, and increment * the index we've seen. */ - head = vring->vr.avail->ring[last_avail_idx % num]; + head = vring_get_avail_ring(vdev, vring, last_avail_idx % num); elem->index = head; @@ -374,21 +381,21 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, /* Ensure descriptor is loaded before accessing fields */ barrier(); - if (desc.flags & VRING_DESC_F_INDIRECT) { - ret = get_indirect(vring, elem, &desc); + if (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_INDIRECT) { + ret = get_indirect(vdev, vring, elem, &desc); if (ret < 0) { goto out; } continue; } - ret = get_desc(vring, elem, &desc); + ret = get_desc(vdev, vring, elem, &desc); if (ret < 0) { goto out; } - i = desc.next; - } while (desc.flags & VRING_DESC_F_NEXT); + i = virtio_tswap16(vdev, desc.next); + } while (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_NEXT); /* On success, increment avail index. */ vring->last_avail_idx++; @@ -407,9 +414,9 @@ out: * * Stolen from linux/drivers/vhost/vhost.c. */ -void vring_push(Vring *vring, VirtQueueElement *elem, int len) +void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, + int len) { - struct vring_used_elem *used; unsigned int head = elem->index; uint16_t new; @@ -422,14 +429,16 @@ void vring_push(Vring *vring, VirtQueueElement *elem, int len) /* The virtqueue contains a ring of used buffers. Get a pointer to the * next entry in that used ring. */ - used = &vring->vr.used->ring[vring->last_used_idx % vring->vr.num]; - used->id = head; - used->len = len; + vring_set_used_ring_id(vdev, vring, vring->last_used_idx % vring->vr.num, + head); + vring_set_used_ring_len(vdev, vring, vring->last_used_idx % vring->vr.num, + len); /* Make sure buffer is written before we update index. */ smp_wmb(); - new = vring->vr.used->idx = ++vring->last_used_idx; + new = ++vring->last_used_idx; + vring_set_used_idx(vdev, vring, new); if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) { vring->signalled_used_valid = false; } diff --git a/include/hw/virtio/dataplane/vring.h b/include/hw/virtio/dataplane/vring.h index d3e086a..fde15f3 100644 --- a/include/hw/virtio/dataplane/vring.h +++ b/include/hw/virtio/dataplane/vring.h @@ -20,6 +20,7 @@ #include "qemu-common.h" #include "hw/virtio/virtio_ring.h" #include "hw/virtio/virtio.h" +#include "hw/virtio/virtio-access.h" typedef struct { MemoryRegion *mr; /* memory region containing the vring */ @@ -31,15 +32,71 @@ typedef struct { bool broken; /* was there a fatal error? */ } Vring; +static inline uint16_t vring_get_used_idx(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.used->idx); +} + +static inline void vring_set_used_idx(VirtIODevice *vdev, Vring *vring, + uint16_t idx) +{ + vring->vr.used->idx = virtio_tswap16(vdev, idx); +} + +static inline uint16_t vring_get_avail_idx(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.avail->idx); +} + +static inline uint16_t vring_get_avail_ring(VirtIODevice *vdev, Vring *vring, + int i) +{ + return virtio_tswap16(vdev, vring->vr.avail->ring[i]); +} + +static inline void vring_set_used_ring_id(VirtIODevice *vdev, Vring *vring, + int i, uint32_t id) +{ + vring->vr.used->ring[i].id = virtio_tswap32(vdev, id); +} + +static inline void vring_set_used_ring_len(VirtIODevice *vdev, Vring *vring, + int i, uint32_t len) +{ + vring->vr.used->ring[i].len = virtio_tswap32(vdev, len); +} + +static inline uint16_t vring_get_used_flags(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.used->flags); +} + +static inline uint16_t vring_get_avail_flags(VirtIODevice *vdev, Vring *vring) +{ + return virtio_tswap16(vdev, vring->vr.avail->flags); +} + +static inline void vring_set_used_flags(VirtIODevice *vdev, Vring *vring, + uint16_t flags) +{ + vring->vr.used->flags |= virtio_tswap16(vdev, flags); +} + +static inline void vring_clear_used_flags(VirtIODevice *vdev, Vring *vring, + uint16_t flags) +{ + vring->vr.used->flags &= virtio_tswap16(vdev, ~flags); +} + static inline unsigned int vring_get_num(Vring *vring) { return vring->vr.num; } /* Are there more descriptors available? */ -static inline bool vring_more_avail(Vring *vring) +static inline bool vring_more_avail(VirtIODevice *vdev, Vring *vring) { - return vring->vr.avail->idx != vring->last_avail_idx; + return vring_get_avail_idx(vdev, vring) != vring->last_avail_idx; } /* Fail future vring_pop() and vring_push() calls until reset */ @@ -54,6 +111,7 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring); bool vring_enable_notification(VirtIODevice *vdev, Vring *vring); bool vring_should_notify(VirtIODevice *vdev, Vring *vring); int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem); -void vring_push(Vring *vring, VirtQueueElement *elem, int len); +void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, + int len); #endif /* VRING_H */ -- 1.7.9.5
Cornelia Huck
2014-Oct-07 14:40 UTC
[PATCH RFC 08/11] s390x/css: Add a callback for when subchannel gets disabled
From: Thomas Huth <thuth at linux.vnet.ibm.com> We need a possibility to run code when a subchannel gets disabled. This patch adds the necessary infrastructure. Signed-off-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/s390x/css.c | 12 ++++++++++++ hw/s390x/css.h | 1 + 2 files changed, 13 insertions(+) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index b67c039..735ec55 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -588,6 +588,7 @@ int css_do_msch(SubchDev *sch, SCHIB *orig_schib) { SCSW *s = &sch->curr_status.scsw; PMCW *p = &sch->curr_status.pmcw; + uint16_t oldflags; int ret; SCHIB schib; @@ -610,6 +611,7 @@ int css_do_msch(SubchDev *sch, SCHIB *orig_schib) copy_schib_from_guest(&schib, orig_schib); /* Only update the program-modifiable fields. */ p->intparm = schib.pmcw.intparm; + oldflags = p->flags; p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | PMCW_FLAGS_MASK_MP); @@ -625,6 +627,12 @@ int css_do_msch(SubchDev *sch, SCHIB *orig_schib) (PMCW_CHARS_MASK_MBFC | PMCW_CHARS_MASK_CSENSE); sch->curr_status.mba = schib.mba; + /* Has the channel been disabled? */ + if (sch->disable_cb && (oldflags & PMCW_FLAGS_MASK_ENA) != 0 + && (p->flags & PMCW_FLAGS_MASK_ENA) == 0) { + sch->disable_cb(sch); + } + ret = 0; out: @@ -1443,6 +1451,10 @@ void css_reset_sch(SubchDev *sch) { PMCW *p = &sch->curr_status.pmcw; + if ((p->flags & PMCW_FLAGS_MASK_ENA) != 0 && sch->disable_cb) { + sch->disable_cb(sch); + } + p->intparm = 0; p->flags &= ~(PMCW_FLAGS_MASK_ISC | PMCW_FLAGS_MASK_ENA | PMCW_FLAGS_MASK_LM | PMCW_FLAGS_MASK_MME | diff --git a/hw/s390x/css.h b/hw/s390x/css.h index 33104ac..7fa807b 100644 --- a/hw/s390x/css.h +++ b/hw/s390x/css.h @@ -81,6 +81,7 @@ struct SubchDev { uint8_t ccw_no_data_cnt; /* transport-provided data: */ int (*ccw_cb) (SubchDev *, CCW1); + void (*disable_cb)(SubchDev *); SenseId id; void *driver_data; }; -- 1.7.9.5
Cornelia Huck
2014-Oct-07 14:40 UTC
[PATCH RFC 09/11] s390x/virtio-ccw: add virtio set-revision call
From: Thomas Huth <thuth at linux.vnet.ibm.com> Handle the virtio-ccw revision according to what the guest sets. When revision 1 is selected, we have a virtio-1 standard device with byteswapping for the virtio rings. When a channel gets disabled, we have to revert to the legacy behavior in case the next user of the device does not negotiate the revision 1 anymore (e.g. the boot firmware uses revision 1, but the operating system only uses the legacy mode). Note that revisions > 0 are still disabled; but we still extend the feature bit size to be able to handle the VERSION_1 bit. Signed-off-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/s390x/virtio-ccw.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++ hw/s390x/virtio-ccw.h | 7 ++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 69add47..0d414f6 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -20,9 +20,11 @@ #include "hw/virtio/virtio-net.h" #include "hw/sysbus.h" #include "qemu/bitops.h" +#include "hw/virtio/virtio-access.h" #include "hw/virtio/virtio-bus.h" #include "hw/s390x/adapter.h" #include "hw/s390x/s390_flic.h" +#include "linux/virtio_config.h" #include "ioinst.h" #include "css.h" @@ -260,6 +262,12 @@ typedef struct VirtioThinintInfo { uint8_t isc; } QEMU_PACKED VirtioThinintInfo; +typedef struct VirtioRevInfo { + uint16_t revision; + uint16_t length; + uint8_t data[0]; +} QEMU_PACKED VirtioRevInfo; + /* Specify where the virtqueues for the subchannel are in guest memory. */ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, uint16_t index, uint16_t num) @@ -299,6 +307,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) { int ret; VqInfoBlock info; + VirtioRevInfo revinfo; uint8_t status; VirtioFeatDesc features; void *config; @@ -373,6 +382,13 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ccw.cda + sizeof(features.features)); if (features.index < ARRAY_SIZE(dev->host_features)) { features.features = dev->host_features[features.index]; + /* + * Don't offer version 1 to the guest if it did not + * negotiate at least revision 1. + */ + if (features.index == 1 && dev->revision <= 0) { + features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } } else { /* Return zeroes if the guest supports more feature bits. */ features.features = 0; @@ -400,6 +416,13 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) ccw.cda + sizeof(features.features)); features.features = ldl_le_phys(&address_space_memory, ccw.cda); if (features.index < ARRAY_SIZE(vdev->guest_features)) { + /* + * The guest should not set version 1 if it didn't + * negotiate a revision >= 1. + */ + if (features.index == 1 && dev->revision <= 0) { + features.features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } virtio_set_features(vdev, features.index, features.features); } else { /* @@ -600,6 +623,25 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) } } break; + case CCW_CMD_SET_VIRTIO_REV: + len = sizeof(revinfo); + if (ccw.count < len || (check_len && ccw.count > len)) { + ret = -EINVAL; + break; + } + if (!ccw.cda) { + ret = -EFAULT; + break; + } + cpu_physical_memory_read(ccw.cda, &revinfo, len); + if (dev->revision >= 0 || + revinfo.revision > VIRTIO_CCW_REV_MAX) { + ret = -ENOSYS; + break; + } + ret = 0; + dev->revision = revinfo.revision; + break; default: ret = -ENOSYS; break; @@ -607,6 +649,13 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) return ret; } +static void virtio_sch_disable_cb(SubchDev *sch) +{ + VirtioCcwDevice *dev = sch->driver_data; + + dev->revision = -1; +} + static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) { unsigned int cssid = 0; @@ -733,6 +782,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) css_sch_build_virtual_schib(sch, 0, VIRTIO_CCW_CHPID_TYPE); sch->ccw_cb = virtio_ccw_cb; + sch->disable_cb = virtio_sch_disable_cb; /* Build senseid data. */ memset(&sch->id, 0, sizeof(SenseId)); @@ -740,6 +790,8 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) sch->id.cu_type = VIRTIO_CCW_CU_TYPE; sch->id.cu_model = vdev->device_id; + dev->revision = -1; + /* Set default feature bits that are offered by the host. */ for (i = 0; i < ARRAY_SIZE(dev->host_features); i++) { dev->host_features[i] @@ -749,6 +801,8 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) dev->host_features[0] |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; + dev->host_features[1] = 1 << (VIRTIO_F_VERSION_1 - 32); + css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, parent->hotplugged, 1); return 0; diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 5a1f16e..03d5955 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -40,6 +40,7 @@ #define CCW_CMD_SET_CONF_IND 0x53 #define CCW_CMD_READ_VQ_CONF 0x32 #define CCW_CMD_SET_IND_ADAPTER 0x73 +#define CCW_CMD_SET_VIRTIO_REV 0x83 #define TYPE_VIRTIO_CCW_DEVICE "virtio-ccw-device" #define VIRTIO_CCW_DEVICE(obj) \ @@ -69,7 +70,10 @@ typedef struct VirtIOCCWDeviceClass { } VirtIOCCWDeviceClass; /* Change here if we want to support more feature bits. */ -#define VIRTIO_CCW_FEATURE_SIZE 1 +#define VIRTIO_CCW_FEATURE_SIZE NR_VIRTIO_FEATURE_WORDS + +/* The maximum virtio revision we support. */ +#define VIRTIO_CCW_REV_MAX 0 /* Performance improves when virtqueue kick processing is decoupled from the * vcpu thread using ioeventfd for some devices. */ @@ -89,6 +93,7 @@ struct VirtioCcwDevice { SubchDev *sch; char *bus_id; uint32_t host_features[VIRTIO_CCW_FEATURE_SIZE]; + int revision; VirtioBusState bus; bool ioeventfd_started; bool ioeventfd_disabled; -- 1.7.9.5
Cornelia Huck
2014-Oct-07 14:40 UTC
[PATCH RFC 10/11] s390x/virtio-ccw: support virtio-1 set_vq format
Support the new CCW_CMD_SET_VQ format for virtio-1 devices. While we're at it, refactor the code a bit and enforce big endian fields (which had always been required, even for legacy). Reviewed-by: Thomas Huth <thuth at linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/s390x/virtio-ccw.c | 114 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 34 deletions(-) diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 0d414f6..80efe88 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -238,11 +238,20 @@ VirtualCssBus *virtual_css_bus_init(void) } /* Communication blocks used by several channel commands. */ -typedef struct VqInfoBlock { +typedef struct VqInfoBlockLegacy { uint64_t queue; uint32_t align; uint16_t index; uint16_t num; +} QEMU_PACKED VqInfoBlockLegacy; + +typedef struct VqInfoBlock { + uint64_t desc; + uint32_t res0; + uint16_t index; + uint16_t num; + uint64_t avail; + uint64_t used; } QEMU_PACKED VqInfoBlock; typedef struct VqConfigBlock { @@ -269,17 +278,20 @@ typedef struct VirtioRevInfo { } QEMU_PACKED VirtioRevInfo; /* Specify where the virtqueues for the subchannel are in guest memory. */ -static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, - uint16_t index, uint16_t num) +static int virtio_ccw_set_vqs(SubchDev *sch, VqInfoBlock *info, + VqInfoBlockLegacy *linfo) { VirtIODevice *vdev = virtio_ccw_get_vdev(sch); + uint16_t index = info ? info->index : linfo->index; + uint16_t num = info ? info->num : linfo->num; + uint64_t desc = info ? info->desc : linfo->queue; if (index > VIRTIO_PCI_QUEUE_MAX) { return -EINVAL; } /* Current code in virtio.c relies on 4K alignment. */ - if (addr && (align != 4096)) { + if (linfo && desc && (linfo->align != 4096)) { return -EINVAL; } @@ -287,8 +299,12 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, return -EINVAL; } - virtio_queue_set_addr(vdev, index, addr); - if (!addr) { + if (info) { + virtio_queue_set_rings(vdev, index, desc, info->avail, info->used); + } else { + virtio_queue_set_addr(vdev, index, desc); + } + if (!desc) { virtio_queue_set_vector(vdev, index, 0); } else { /* Fail if we don't have a big enough queue. */ @@ -303,10 +319,66 @@ static int virtio_ccw_set_vqs(SubchDev *sch, uint64_t addr, uint32_t align, return 0; } -static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +static int virtio_ccw_handle_set_vq(SubchDev *sch, CCW1 ccw, bool check_len, + bool is_legacy) { int ret; VqInfoBlock info; + VqInfoBlockLegacy linfo; + size_t info_len = is_legacy ? sizeof(linfo) : sizeof(info); + + if (check_len) { + if (ccw.count != info_len) { + return -EINVAL; + } + } else if (ccw.count < info_len) { + /* Can't execute command. */ + return -EINVAL; + } + if (!ccw.cda) { + return -EFAULT; + } + if (is_legacy) { + linfo.queue = ldq_be_phys(&address_space_memory, ccw.cda); + linfo.align = ldl_be_phys(&address_space_memory, + ccw.cda + sizeof(linfo.queue)); + linfo.index = lduw_be_phys(&address_space_memory, + ccw.cda + sizeof(linfo.queue) + + sizeof(linfo.align)); + linfo.num = lduw_be_phys(&address_space_memory, + ccw.cda + sizeof(linfo.queue) + + sizeof(linfo.align) + + sizeof(linfo.index)); + ret = virtio_ccw_set_vqs(sch, NULL, &linfo); + } else { + info.desc = ldq_be_phys(&address_space_memory, ccw.cda); + info.index = lduw_be_phys(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0)); + info.num = lduw_be_phys(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index)); + info.avail = ldq_be_phys(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index) + + sizeof(info.num)); + info.used = ldq_be_phys(&address_space_memory, + ccw.cda + sizeof(info.desc) + + sizeof(info.res0) + + sizeof(info.index) + + sizeof(info.num) + + sizeof(info.avail)); + ret = virtio_ccw_set_vqs(sch, &info, NULL); + } + sch->curr_status.scsw.count = 0; + return ret; +} + +static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) +{ + int ret; VirtioRevInfo revinfo; uint8_t status; VirtioFeatDesc features; @@ -331,33 +403,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw) /* Look at the command. */ switch (ccw.cmd_code) { case CCW_CMD_SET_VQ: - if (check_len) { - if (ccw.count != sizeof(info)) { - ret = -EINVAL; - break; - } - } else if (ccw.count < sizeof(info)) { - /* Can't execute command. */ - ret = -EINVAL; - break; - } - if (!ccw.cda) { - ret = -EFAULT; - } else { - info.queue = ldq_phys(&address_space_memory, ccw.cda); - info.align = ldl_phys(&address_space_memory, - ccw.cda + sizeof(info.queue)); - info.index = lduw_phys(&address_space_memory, - ccw.cda + sizeof(info.queue) - + sizeof(info.align)); - info.num = lduw_phys(&address_space_memory, - ccw.cda + sizeof(info.queue) - + sizeof(info.align) - + sizeof(info.index)); - ret = virtio_ccw_set_vqs(sch, info.queue, info.align, info.index, - info.num); - sch->curr_status.scsw.count = 0; - } + ret = virtio_ccw_handle_set_vq(sch, ccw, check_len, dev->revision < 1); break; case CCW_CMD_VDEV_RESET: virtio_ccw_stop_ioeventfd(dev); -- 1.7.9.5
virtio-ccw should now have everything in place to operate virtio 1.0 devices, so let's enable revision 1. Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> --- hw/s390x/virtio-ccw.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/s390x/virtio-ccw.h b/hw/s390x/virtio-ccw.h index 03d5955..08edd8d 100644 --- a/hw/s390x/virtio-ccw.h +++ b/hw/s390x/virtio-ccw.h @@ -73,7 +73,7 @@ typedef struct VirtIOCCWDeviceClass { #define VIRTIO_CCW_FEATURE_SIZE NR_VIRTIO_FEATURE_WORDS /* The maximum virtio revision we support. */ -#define VIRTIO_CCW_REV_MAX 0 +#define VIRTIO_CCW_REV_MAX 1 /* Performance improves when virtqueue kick processing is decoupled from the * vcpu thread using ioeventfd for some devices. */ -- 1.7.9.5
Andy Lutomirski
2014-Oct-08 01:24 UTC
[PATCH RFC 00/11] qemu: towards virtio-1 host support
On 10/07/2014 07:39 AM, Cornelia Huck wrote:> This patchset aims to get us some way to implement virtio-1 compliant > and transitional devices in qemu. Branch available at > > git://github.com/cohuck/qemu virtio-1 > > I've mainly focused on: > - endianness handling > - extended feature bits > - virtio-ccw new/changed commandsAt the risk of some distraction, would it be worth thinking about a solution to the IOMMU bypassing mess as part of this? --Andy
On Tue, 07 Oct 2014 18:24:22 -0700 Andy Lutomirski <luto at amacapital.net> wrote:> On 10/07/2014 07:39 AM, Cornelia Huck wrote: > > This patchset aims to get us some way to implement virtio-1 compliant > > and transitional devices in qemu. Branch available at > > > > git://github.com/cohuck/qemu virtio-1 > > > > I've mainly focused on: > > - endianness handling > > - extended feature bits > > - virtio-ccw new/changed commands > > At the risk of some distraction, would it be worth thinking about a > solution to the IOMMU bypassing mess as part of this?I think that is a whole different issue. virtio-1 is basically done - we just need to implement it - while the IOMMU/DMA stuff certainly needs more discussion. Therefore, I'd like to defer to the other discussion thread here.
Cornelia Huck <cornelia.huck at de.ibm.com> writes:> With virtio-1, we support more than 32 feature bits. Let's make > vdev->guest_features depend on the number of supported feature bits, > allowing us to grow the feature bits automatically.It's a judgement call, but I would say that simply using uint64_t will be sufficient for quite a while. Cheers, Rusty.
Michael S. Tsirkin
2014-Oct-23 21:42 UTC
[PATCH RFC 00/11] qemu: towards virtio-1 host support
On Tue, Oct 07, 2014 at 04:39:56PM +0200, Cornelia Huck wrote:> This patchset aims to get us some way to implement virtio-1 compliant > and transitional devices in qemu. Branch available at > > git://github.com/cohuck/qemu virtio-1 > > I've mainly focused on: > - endianness handling > - extended feature bits > - virtio-ccw new/changed commandsSo issues identified so far: - devices not converted yet should not advertize 1.0 - blk has some features removed under 1.0, specifically CONFIG_WCE. - net header is different for 1.0> Thanks go to Thomas for some preliminary work in this area. > > I've been able to start guests both with and without the virtio-1 patches > in the linux guest patchset, with virtio-net and virtio-blk devices (with > and without dataplane). virtio-ccw only :) vhost, migration and loads of > other things have been ignored for now. > > I'd like to know whether I walk into the right direction, so let's consider > this as a starting point. > > Cornelia Huck (8): > virtio: cull virtio_bus_set_vdev_features > virtio: support more feature bits > s390x/virtio-ccw: fix check for WRITE_FEAT > virtio: introduce legacy virtio devices > virtio: allow virtio-1 queue layout > dataplane: allow virtio-1 devices > s390x/virtio-ccw: support virtio-1 set_vq format > s390x/virtio-ccw: enable virtio 1.0 > > Thomas Huth (3): > linux-headers/virtio_config: Update with VIRTIO_F_VERSION_1 > s390x/css: Add a callback for when subchannel gets disabled > s390x/virtio-ccw: add virtio set-revision call > > hw/9pfs/virtio-9p-device.c | 7 +- > hw/block/dataplane/virtio-blk.c | 3 +- > hw/block/virtio-blk.c | 9 +- > hw/char/virtio-serial-bus.c | 9 +- > hw/net/virtio-net.c | 38 ++++--- > hw/s390x/css.c | 12 +++ > hw/s390x/css.h | 1 + > hw/s390x/s390-virtio-bus.c | 9 +- > hw/s390x/virtio-ccw.c | 188 +++++++++++++++++++++++++++-------- > hw/s390x/virtio-ccw.h | 7 +- > hw/scsi/vhost-scsi.c | 7 +- > hw/scsi/virtio-scsi-dataplane.c | 2 +- > hw/scsi/virtio-scsi.c | 8 +- > hw/virtio/Makefile.objs | 2 +- > hw/virtio/dataplane/Makefile.objs | 2 +- > hw/virtio/dataplane/vring.c | 95 ++++++++++-------- > hw/virtio/virtio-balloon.c | 8 +- > hw/virtio/virtio-bus.c | 23 +---- > hw/virtio/virtio-mmio.c | 9 +- > hw/virtio/virtio-pci.c | 13 +-- > hw/virtio/virtio-rng.c | 2 +- > hw/virtio/virtio.c | 51 +++++++--- > include/hw/virtio/dataplane/vring.h | 64 +++++++++++- > include/hw/virtio/virtio-access.h | 4 + > include/hw/virtio/virtio-bus.h | 10 +- > include/hw/virtio/virtio.h | 34 +++++-- > linux-headers/linux/virtio_config.h | 3 + > 27 files changed, 442 insertions(+), 178 deletions(-) > > -- > 1.7.9.5 > > _______________________________________________ > Virtualization mailing list > Virtualization at lists.linux-foundation.org > https://lists.linuxfoundation.org/mailman/listinfo/virtualization
On Fri, 24 Oct 2014 00:42:20 +0300 "Michael S. Tsirkin" <mst at redhat.com> wrote:> On Tue, Oct 07, 2014 at 04:39:56PM +0200, Cornelia Huck wrote: > > This patchset aims to get us some way to implement virtio-1 compliant > > and transitional devices in qemu. Branch available at > > > > git://github.com/cohuck/qemu virtio-1 > > > > I've mainly focused on: > > - endianness handling > > - extended feature bits > > - virtio-ccw new/changed commands > > So issues identified so far:Thanks for taking a look.> - devices not converted yet should not advertize 1.0Neither should an uncoverted transport. So we either can - have transport set the bit and rely on devices ->get_features callback to mask it out (virtio-ccw has to change the calling order for get_features, btw.) - have device set the bit and the transport mask it out later. Feels a bit weird, as virtio-1 is a transport feature bit. I'm tending towards the first option; smth like this (on top of my branch): diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index c29c8c8..f6501ea 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -24,6 +24,9 @@ static uint32_t virtio_9p_get_features(VirtIODevice *vdev, unsigned int index, uint32_t features) { + if (index == 1) { + features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } if (index > 0) { return features; } diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 0d843fe..07a7a6f 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -472,6 +472,9 @@ static uint32_t get_features(VirtIODevice *vdev, unsigned int index, { VirtIOSerial *vser; + if (index == 1) { + features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } if (index > 0) { return features; } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 67f91c0..4b75105 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -447,6 +447,9 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev, unsigned int index, VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_queue(n->nic); + if (index == 1 && get_vhost_net(nc->peer)) { + features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } if (index > 0) { return features; } diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 80efe88..07fbf40 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -839,16 +839,16 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev) dev->revision = -1; /* Set default feature bits that are offered by the host. */ + dev->host_features[0] = 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; + dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; + + dev->host_features[1] = 1 << (VIRTIO_F_VERSION_1 - 32); + for (i = 0; i < ARRAY_SIZE(dev->host_features); i++) { dev->host_features[i] virtio_bus_get_vdev_features(&dev->bus, i, dev->host_features[i]); } - dev->host_features[0] |= 0x1 << VIRTIO_F_NOTIFY_ON_EMPTY; - dev->host_features[0] |= 0x1 << VIRTIO_F_BAD_FEATURE; - - dev->host_features[1] = 1 << (VIRTIO_F_VERSION_1 - 32); - css_generate_sch_crws(sch->cssid, sch->ssid, sch->schid, parent->hotplugged, 1); return 0; diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 8e1afa0..8a8fdb9 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -155,6 +155,9 @@ static uint32_t vhost_scsi_get_features(VirtIODevice *vdev, unsigned int index, { VHostSCSI *s = VHOST_SCSI(vdev); + if (index == 1) { + features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } if (index > 0) { return features; } diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index 088e688..378783f 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -611,6 +611,9 @@ static void virtio_scsi_set_config(VirtIODevice *vdev, static uint32_t virtio_scsi_get_features(VirtIODevice *vdev, unsigned int index, uint32_t requested_features) { + if (index == 1) { + requested_features &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } return requested_features; } diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 5af17e2..bd52845 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -306,6 +306,9 @@ static void virtio_balloon_set_config(VirtIODevice *vdev, static uint32_t virtio_balloon_get_features(VirtIODevice *vdev, unsigned int index, uint32_t f) { + if (index == 1) { + f &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } if (index > 0) { return f; } diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index 2814017..bf6101d 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -101,6 +101,9 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq) static uint32_t get_features(VirtIODevice *vdev, unsigned int index, uint32_t f) { + if (index == 1) { + f &= ~(1 << (VIRTIO_F_VERSION_1 - 32)); + } return f; }> - blk has some features removed under 1.0, > specifically CONFIG_WCE. > - net header is different for 1.0
Greg Kurz
2014-Oct-28 15:22 UTC
[Qemu-devel] [PATCH RFC 07/11] dataplane: allow virtio-1 devices
On Tue, 7 Oct 2014 16:40:03 +0200 Cornelia Huck <cornelia.huck at de.ibm.com> wrote:> Handle endianness conversion for virtio-1 virtqueues correctly. > > Note that dataplane now needs to be built per-target. >It also affects hw/virtio/virtio-pci.c: In file included from include/hw/virtio/dataplane/vring.h:23:0, from include/hw/virtio/virtio-scsi.h:21, from hw/virtio/virtio-pci.c:24: include/hw/virtio/virtio-access.h: In function ?virtio_access_is_big_endian?: include/hw/virtio/virtio-access.h:28:15: error: attempt to use poisoned "TARGET_WORDS_BIGENDIAN" #elif defined(TARGET_WORDS_BIGENDIAN) ^ FWIW when I added endian ambivalent support to virtio, I remember *some people* getting angry at the idea of turning common code into per-target... :) See comment below.> Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> > --- > hw/block/dataplane/virtio-blk.c | 3 +- > hw/scsi/virtio-scsi-dataplane.c | 2 +- > hw/virtio/Makefile.objs | 2 +- > hw/virtio/dataplane/Makefile.objs | 2 +- > hw/virtio/dataplane/vring.c | 85 +++++++++++++++++++---------------- > include/hw/virtio/dataplane/vring.h | 64 ++++++++++++++++++++++++-- > 6 files changed, 113 insertions(+), 45 deletions(-) > > diff --git a/hw/block/dataplane/virtio-blk.c b/hw/block/dataplane/virtio-blk.c > index 5458f9d..eb45a3d 100644 > --- a/hw/block/dataplane/virtio-blk.c > +++ b/hw/block/dataplane/virtio-blk.c > @@ -16,6 +16,7 @@ > #include "qemu/iov.h" > #include "qemu/thread.h" > #include "qemu/error-report.h" > +#include "hw/virtio/virtio-access.h" > #include "hw/virtio/dataplane/vring.h" > #include "block/block.h" > #include "hw/virtio/virtio-blk.h" > @@ -75,7 +76,7 @@ static void complete_request_vring(VirtIOBlockReq *req, unsigned char status) > VirtIOBlockDataPlane *s = req->dev->dataplane; > stb_p(&req->in->status, status); > > - vring_push(&req->dev->dataplane->vring, &req->elem, > + vring_push(s->vdev, &req->dev->dataplane->vring, &req->elem, > req->qiov.size + sizeof(*req->in)); > > /* Suppress notification to guest by BH and its scheduled > diff --git a/hw/scsi/virtio-scsi-dataplane.c b/hw/scsi/virtio-scsi-dataplane.c > index b778e05..3e2b706 100644 > --- a/hw/scsi/virtio-scsi-dataplane.c > +++ b/hw/scsi/virtio-scsi-dataplane.c > @@ -81,7 +81,7 @@ VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, > > void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) > { > - vring_push(&req->vring->vring, &req->elem, > + vring_push((VirtIODevice *)req->dev, &req->vring->vring, &req->elem, > req->qsgl.size + req->resp_iov.size); > event_notifier_set(&req->vring->guest_notifier); > } > diff --git a/hw/virtio/Makefile.objs b/hw/virtio/Makefile.objs > index d21c397..19b224a 100644 > --- a/hw/virtio/Makefile.objs > +++ b/hw/virtio/Makefile.objs > @@ -2,7 +2,7 @@ common-obj-y += virtio-rng.o > common-obj-$(CONFIG_VIRTIO_PCI) += virtio-pci.o > common-obj-y += virtio-bus.o > common-obj-y += virtio-mmio.o > -common-obj-$(CONFIG_VIRTIO) += dataplane/ > +obj-$(CONFIG_VIRTIO) += dataplane/ > > obj-y += virtio.o virtio-balloon.o > obj-$(CONFIG_LINUX) += vhost.o vhost-backend.o vhost-user.o > diff --git a/hw/virtio/dataplane/Makefile.objs b/hw/virtio/dataplane/Makefile.objs > index 9a8cfc0..753a9ca 100644 > --- a/hw/virtio/dataplane/Makefile.objs > +++ b/hw/virtio/dataplane/Makefile.objs > @@ -1 +1 @@ > -common-obj-y += vring.o > +obj-y += vring.o > diff --git a/hw/virtio/dataplane/vring.c b/hw/virtio/dataplane/vring.c > index b84957f..4624521 100644 > --- a/hw/virtio/dataplane/vring.c > +++ b/hw/virtio/dataplane/vring.c > @@ -18,6 +18,7 @@ > #include "hw/hw.h" > #include "exec/memory.h" > #include "exec/address-spaces.h" > +#include "hw/virtio/virtio-access.h" > #include "hw/virtio/dataplane/vring.h" > #include "qemu/error-report.h" > > @@ -83,7 +84,7 @@ bool vring_setup(Vring *vring, VirtIODevice *vdev, int n) > vring_init(&vring->vr, virtio_queue_get_num(vdev, n), vring_ptr, 4096); > > vring->last_avail_idx = virtio_queue_get_last_avail_idx(vdev, n); > - vring->last_used_idx = vring->vr.used->idx; > + vring->last_used_idx = vring_get_used_idx(vdev, vring); > vring->signalled_used = 0; > vring->signalled_used_valid = false; > > @@ -104,7 +105,7 @@ void vring_teardown(Vring *vring, VirtIODevice *vdev, int n) > void vring_disable_notification(VirtIODevice *vdev, Vring *vring) > { > if (!(vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX))) { > - vring->vr.used->flags |= VRING_USED_F_NO_NOTIFY; > + vring_set_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); > } > } > > @@ -117,10 +118,10 @@ bool vring_enable_notification(VirtIODevice *vdev, Vring *vring) > if (vdev->guest_features[0] & (1 << VIRTIO_RING_F_EVENT_IDX)) { > vring_avail_event(&vring->vr) = vring->vr.avail->idx; > } else { > - vring->vr.used->flags &= ~VRING_USED_F_NO_NOTIFY; > + vring_clear_used_flags(vdev, vring, VRING_USED_F_NO_NOTIFY); > } > smp_mb(); /* ensure update is seen before reading avail_idx */ > - return !vring_more_avail(vring); > + return !vring_more_avail(vdev, vring); > } > > /* This is stolen from linux/drivers/vhost/vhost.c:vhost_notify() */ > @@ -134,12 +135,13 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring) > smp_mb(); > > if ((vdev->guest_features[0] & VIRTIO_F_NOTIFY_ON_EMPTY) && > - unlikely(vring->vr.avail->idx == vring->last_avail_idx)) { > + unlikely(!vring_more_avail(vdev, vring))) { > return true; > } > > if (!(vdev->guest_features[0] & VIRTIO_RING_F_EVENT_IDX)) { > - return !(vring->vr.avail->flags & VRING_AVAIL_F_NO_INTERRUPT); > + return !(vring_get_avail_flags(vdev, vring) & > + VRING_AVAIL_F_NO_INTERRUPT); > } > old = vring->signalled_used; > v = vring->signalled_used_valid; > @@ -154,15 +156,18 @@ bool vring_should_notify(VirtIODevice *vdev, Vring *vring) > } > > > -static int get_desc(Vring *vring, VirtQueueElement *elem, > +static int get_desc(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, > struct vring_desc *desc) > { > unsigned *num; > struct iovec *iov; > hwaddr *addr; > MemoryRegion *mr; > + int is_write = virtio_tswap16(vdev, desc->flags) & VRING_DESC_F_WRITE; > + uint32_t len = virtio_tswap32(vdev, desc->len); > + uint64_t desc_addr = virtio_tswap64(vdev, desc->addr); > > - if (desc->flags & VRING_DESC_F_WRITE) { > + if (is_write) { > num = &elem->in_num; > iov = &elem->in_sg[*num]; > addr = &elem->in_addr[*num]; > @@ -186,44 +191,45 @@ static int get_desc(Vring *vring, VirtQueueElement *elem, > } > > /* TODO handle non-contiguous memory across region boundaries */ > - iov->iov_base = vring_map(&mr, desc->addr, desc->len, > - desc->flags & VRING_DESC_F_WRITE); > + iov->iov_base = vring_map(&mr, desc_addr, len, is_write); > if (!iov->iov_base) { > error_report("Failed to map descriptor addr %#" PRIx64 " len %u", > - (uint64_t)desc->addr, desc->len); > + (uint64_t)desc_addr, len); > return -EFAULT; > } > > /* The MemoryRegion is looked up again and unref'ed later, leave the > * ref in place. */ > - iov->iov_len = desc->len; > - *addr = desc->addr; > + iov->iov_len = len; > + *addr = desc_addr; > *num += 1; > return 0; > } > > /* This is stolen from linux/drivers/vhost/vhost.c. */ > -static int get_indirect(Vring *vring, VirtQueueElement *elem, > - struct vring_desc *indirect) > +static int get_indirect(VirtIODevice *vdev, Vring *vring, > + VirtQueueElement *elem, struct vring_desc *indirect) > { > struct vring_desc desc; > unsigned int i = 0, count, found = 0; > int ret; > + uint32_t len = virtio_tswap32(vdev, indirect->len); > + uint64_t addr = virtio_tswap64(vdev, indirect->addr); > > /* Sanity check */ > - if (unlikely(indirect->len % sizeof(desc))) { > + if (unlikely(len % sizeof(desc))) { > error_report("Invalid length in indirect descriptor: " > "len %#x not multiple of %#zx", > - indirect->len, sizeof(desc)); > + len, sizeof(desc)); > vring->broken = true; > return -EFAULT; > } > > - count = indirect->len / sizeof(desc); > + count = len / sizeof(desc); > /* Buffers are chained via a 16 bit next field, so > * we can have at most 2^16 of these. */ > if (unlikely(count > USHRT_MAX + 1)) { > - error_report("Indirect buffer length too big: %d", indirect->len); > + error_report("Indirect buffer length too big: %d", len); > vring->broken = true; > return -EFAULT; > } > @@ -234,12 +240,12 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem, > > /* Translate indirect descriptor */ > desc_ptr = vring_map(&mr, > - indirect->addr + found * sizeof(desc), > + addr + found * sizeof(desc), > sizeof(desc), false); > if (!desc_ptr) { > error_report("Failed to map indirect descriptor " > "addr %#" PRIx64 " len %zu", > - (uint64_t)indirect->addr + found * sizeof(desc), > + (uint64_t)addr + found * sizeof(desc), > sizeof(desc)); > vring->broken = true; > return -EFAULT; > @@ -257,19 +263,20 @@ static int get_indirect(Vring *vring, VirtQueueElement *elem, > return -EFAULT; > } > > - if (unlikely(desc.flags & VRING_DESC_F_INDIRECT)) { > + if (unlikely(virtio_tswap16(vdev, desc.flags) > + & VRING_DESC_F_INDIRECT)) { > error_report("Nested indirect descriptor"); > vring->broken = true; > return -EFAULT; > } > > - ret = get_desc(vring, elem, &desc); > + ret = get_desc(vdev, vring, elem, &desc); > if (ret < 0) { > vring->broken |= (ret == -EFAULT); > return ret; > } > - i = desc.next; > - } while (desc.flags & VRING_DESC_F_NEXT); > + i = virtio_tswap16(vdev, desc.next); > + } while (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_NEXT); > return 0; > } > > @@ -320,7 +327,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, > > /* Check it isn't doing very strange things with descriptor numbers. */ > last_avail_idx = vring->last_avail_idx; > - avail_idx = vring->vr.avail->idx; > + avail_idx = vring_get_avail_idx(vdev, vring); > barrier(); /* load indices now and not again later */ > > if (unlikely((uint16_t)(avail_idx - last_avail_idx) > num)) { > @@ -341,7 +348,7 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, > > /* Grab the next descriptor number they're advertising, and increment > * the index we've seen. */ > - head = vring->vr.avail->ring[last_avail_idx % num]; > + head = vring_get_avail_ring(vdev, vring, last_avail_idx % num); > > elem->index = head; > > @@ -374,21 +381,21 @@ int vring_pop(VirtIODevice *vdev, Vring *vring, > /* Ensure descriptor is loaded before accessing fields */ > barrier(); > > - if (desc.flags & VRING_DESC_F_INDIRECT) { > - ret = get_indirect(vring, elem, &desc); > + if (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_INDIRECT) { > + ret = get_indirect(vdev, vring, elem, &desc); > if (ret < 0) { > goto out; > } > continue; > } > > - ret = get_desc(vring, elem, &desc); > + ret = get_desc(vdev, vring, elem, &desc); > if (ret < 0) { > goto out; > } > > - i = desc.next; > - } while (desc.flags & VRING_DESC_F_NEXT); > + i = virtio_tswap16(vdev, desc.next); > + } while (virtio_tswap16(vdev, desc.flags) & VRING_DESC_F_NEXT); > > /* On success, increment avail index. */ > vring->last_avail_idx++; > @@ -407,9 +414,9 @@ out: > * > * Stolen from linux/drivers/vhost/vhost.c. > */ > -void vring_push(Vring *vring, VirtQueueElement *elem, int len) > +void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, > + int len) > { > - struct vring_used_elem *used; > unsigned int head = elem->index; > uint16_t new; > > @@ -422,14 +429,16 @@ void vring_push(Vring *vring, VirtQueueElement *elem, int len) > > /* The virtqueue contains a ring of used buffers. Get a pointer to the > * next entry in that used ring. */ > - used = &vring->vr.used->ring[vring->last_used_idx % vring->vr.num]; > - used->id = head; > - used->len = len; > + vring_set_used_ring_id(vdev, vring, vring->last_used_idx % vring->vr.num, > + head); > + vring_set_used_ring_len(vdev, vring, vring->last_used_idx % vring->vr.num, > + len); > > /* Make sure buffer is written before we update index. */ > smp_wmb(); > > - new = vring->vr.used->idx = ++vring->last_used_idx; > + new = ++vring->last_used_idx; > + vring_set_used_idx(vdev, vring, new); > if (unlikely((int16_t)(new - vring->signalled_used) < (uint16_t)1)) { > vring->signalled_used_valid = false; > } > diff --git a/include/hw/virtio/dataplane/vring.h b/include/hw/virtio/dataplane/vring.h > index d3e086a..fde15f3 100644 > --- a/ > +++ b/include/hw/virtio/dataplane/vring.h > @@ -20,6 +20,7 @@ > #include "qemu-common.h" > #include "hw/virtio/virtio_ring.h" > #include "hw/virtio/virtio.h" > +#include "hw/virtio/virtio-access.h" >Since the following commit: commit 244e2898b7a7735b3da114c120abe206af56a167 Author: Fam Zheng <famz at redhat.com> Date: Wed Sep 24 15:21:41 2014 +0800 virtio-scsi: Add VirtIOSCSIVring in VirtIOSCSIReq The include/hw/virtio/dataplane/vring.h header is indirectly included by hw/virtio/virtio-pci.c. Why don't you move all this target dependent helpers to another header ? Cheers. -- Greg> typedef struct { > MemoryRegion *mr; /* memory region containing the vring */ > @@ -31,15 +32,71 @@ typedef struct { > bool broken; /* was there a fatal error? */ > } Vring; > > +static inline uint16_t vring_get_used_idx(VirtIODevice *vdev, Vring *vring) > +{ > + return virtio_tswap16(vdev, vring->vr.used->idx); > +} > + > +static inline void vring_set_used_idx(VirtIODevice *vdev, Vring *vring, > + uint16_t idx) > +{ > + vring->vr.used->idx = virtio_tswap16(vdev, idx); > +} > + > +static inline uint16_t vring_get_avail_idx(VirtIODevice *vdev, Vring *vring) > +{ > + return virtio_tswap16(vdev, vring->vr.avail->idx); > +} > + > +static inline uint16_t vring_get_avail_ring(VirtIODevice *vdev, Vring *vring, > + int i) > +{ > + return virtio_tswap16(vdev, vring->vr.avail->ring[i]); > +} > + > +static inline void vring_set_used_ring_id(VirtIODevice *vdev, Vring *vring, > + int i, uint32_t id) > +{ > + vring->vr.used->ring[i].id = virtio_tswap32(vdev, id); > +} > + > +static inline void vring_set_used_ring_len(VirtIODevice *vdev, Vring *vring, > + int i, uint32_t len) > +{ > + vring->vr.used->ring[i].len = virtio_tswap32(vdev, len); > +} > + > +static inline uint16_t vring_get_used_flags(VirtIODevice *vdev, Vring *vring) > +{ > + return virtio_tswap16(vdev, vring->vr.used->flags); > +} > + > +static inline uint16_t vring_get_avail_flags(VirtIODevice *vdev, Vring *vring) > +{ > + return virtio_tswap16(vdev, vring->vr.avail->flags); > +} > + > +static inline void vring_set_used_flags(VirtIODevice *vdev, Vring *vring, > + uint16_t flags) > +{ > + vring->vr.used->flags |= virtio_tswap16(vdev, flags); > +} > + > +static inline void vring_clear_used_flags(VirtIODevice *vdev, Vring *vring, > + uint16_t flags) > +{ > + vring->vr.used->flags &= virtio_tswap16(vdev, ~flags); > +} > + > static inline unsigned int vring_get_num(Vring *vring) > { > return vring->vr.num; > } > > /* Are there more descriptors available? */ > -static inline bool vring_more_avail(Vring *vring) > +static inline bool vring_more_avail(VirtIODevice *vdev, Vring *vring) > { > - return vring->vr.avail->idx != vring->last_avail_idx; > + return vring_get_avail_idx(vdev, vring) != vring->last_avail_idx; > } > > /* Fail future vring_pop() and vring_push() calls until reset */ > @@ -54,6 +111,7 @@ void vring_disable_notification(VirtIODevice *vdev, Vring *vring); > bool vring_enable_notification(VirtIODevice *vdev, Vring *vring); > bool vring_should_notify(VirtIODevice *vdev, Vring *vring); > int vring_pop(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem); > -void vring_push(Vring *vring, VirtQueueElement *elem, int len); > +void vring_push(VirtIODevice *vdev, Vring *vring, VirtQueueElement *elem, > + int len); > > #endif /* VRING_H */
Greg Kurz
2014-Oct-28 15:40 UTC
[Qemu-devel] [PATCH RFC 05/11] virtio: introduce legacy virtio devices
On Tue, 7 Oct 2014 16:40:01 +0200 Cornelia Huck <cornelia.huck at de.ibm.com> wrote:> Introduce a helper function to indicate whether a virtio device is > operating in legacy or virtio standard mode. > > It may be used to make decisions about the endianess of virtio accesses > and other virtio-1 specific changes, enabling us to support transitional > devices. > > Reviewed-by: Thomas Huth <thuth at linux.vnet.ibm.com> > Signed-off-by: Cornelia Huck <cornelia.huck at de.ibm.com> > --- > hw/virtio/virtio.c | 6 +++++- > include/hw/virtio/virtio-access.h | 4 ++++ > include/hw/virtio/virtio.h | 13 +++++++++++-- > 3 files changed, 20 insertions(+), 3 deletions(-) > > diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c > index 7aaa953..e6ae3a0 100644 > --- a/hw/virtio/virtio.c > +++ b/hw/virtio/virtio.c > @@ -883,7 +883,11 @@ static bool virtio_device_endian_needed(void *opaque) > VirtIODevice *vdev = opaque; > > assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); > - return vdev->device_endian != virtio_default_endian(); > + if (virtio_device_is_legacy(vdev)) { > + return vdev->device_endian != virtio_default_endian(); > + } > + /* Devices conforming to VIRTIO 1.0 or later are always LE. */ > + return vdev->device_endian != VIRTIO_DEVICE_ENDIAN_LITTLE; > } >Shouldn't we have some code doing the following somewhere ? if (!virtio_device_is_legacy(vdev)) { vdev->device_endian = VIRTIO_DEVICE_ENDIAN_LITTLE; } also, since virtio-1 is LE only, do we expect device_endian to be different from VIRTIO_DEVICE_ENDIAN_LITTLE ? Cheers. -- Greg> static const VMStateDescription vmstate_virtio_device_endian = { > diff --git a/include/hw/virtio/virtio-access.h b/include/hw/virtio/virtio-access.h > index 46456fd..c123ee0 100644 > --- a/include/hw/virtio/virtio-access.h > +++ b/include/hw/virtio/virtio-access.h > @@ -19,6 +19,10 @@ > > static inline bool virtio_access_is_big_endian(VirtIODevice *vdev) > { > + if (!virtio_device_is_legacy(vdev)) { > + /* Devices conforming to VIRTIO 1.0 or later are always LE. */ > + return false; > + } > #if defined(TARGET_IS_BIENDIAN) > return virtio_is_big_endian(vdev); > #elif defined(TARGET_WORDS_BIGENDIAN) > diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h > index b408166..40e567c 100644 > --- a/include/hw/virtio/virtio.h > +++ b/include/hw/virtio/virtio.h > @@ -275,9 +275,18 @@ void virtio_queue_set_host_notifier_fd_handler(VirtQueue *vq, bool assign, > void virtio_queue_notify_vq(VirtQueue *vq); > void virtio_irq(VirtQueue *vq); > > +static inline bool virtio_device_is_legacy(VirtIODevice *vdev) > +{ > + return !(vdev->guest_features[1] & (1 << (VIRTIO_F_VERSION_1 - 32))); > +} > + > static inline bool virtio_is_big_endian(VirtIODevice *vdev) > { > - assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); > - return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; > + if (virtio_device_is_legacy(vdev)) { > + assert(vdev->device_endian != VIRTIO_DEVICE_ENDIAN_UNKNOWN); > + return vdev->device_endian == VIRTIO_DEVICE_ENDIAN_BIG; > + } > + /* Devices conforming to VIRTIO 1.0 or later are always LE. */ > + return false; > } > #endif
Seemingly Similar Threads
- [PATCH RFC 00/11] qemu: towards virtio-1 host support
- [PATCH RFC 00/11] qemu: towards virtio-1 host support
- [PATCH RFC 00/11] qemu: towards virtio-1 host support
- [PATCH v4 0/4] virtio: Clean up scatterlists and use the DMA API
- [PATCH v4 0/4] virtio: Clean up scatterlists and use the DMA API