Index: sys/dev/virtio/mmio/virtio_mmio.c =================================================================== --- sys/dev/virtio/mmio/virtio_mmio.c +++ sys/dev/virtio/mmio/virtio_mmio.c @@ -88,6 +88,7 @@ static void vtmmio_set_status(device_t, uint8_t); static void vtmmio_read_dev_config(device_t, bus_size_t, void *, int); static void vtmmio_write_dev_config(device_t, bus_size_t, void *, int); +static uint32_t vtmmio_virtqueue_size(device_t dev, int idx); static void vtmmio_describe_features(struct vtmmio_softc *, const char *, uint64_t); static void vtmmio_probe_and_attach_child(struct vtmmio_softc *); @@ -161,6 +162,7 @@ DEVMETHOD(virtio_bus_notify_vq, vtmmio_notify_virtqueue), DEVMETHOD(virtio_bus_read_device_config, vtmmio_read_dev_config), DEVMETHOD(virtio_bus_write_device_config, vtmmio_write_dev_config), + DEVMETHOD(virtio_bus_virtqueue_size, vtmmio_virtqueue_size), DEVMETHOD_END }; @@ -404,6 +406,19 @@ return ((sc->vtmmio_features & feature) != 0); } +static uint32_t +vtmmio_virtqueue_size(device_t dev, int idx) +{ + struct vtmmio_softc *sc; + uint32_t size; + + sc = device_get_softc(dev); + vtmmio_select_virtqueue(sc, idx); + size = vtmmio_read_config_4(sc, VIRTIO_MMIO_QUEUE_NUM_MAX); + + return (size); +} + static int vtmmio_alloc_virtqueues(device_t dev, int flags, int nvqs, struct vq_alloc_info *vq_info) Index: sys/dev/virtio/pci/virtio_pci.c =================================================================== --- sys/dev/virtio/pci/virtio_pci.c +++ sys/dev/virtio/pci/virtio_pci.c @@ -113,6 +113,7 @@ static uint64_t vtpci_negotiate_features(device_t, uint64_t); static int vtpci_with_feature(device_t, uint64_t); +static uint32_t vtpci_virtqueue_size(device_t dev, int idx); static int vtpci_alloc_virtqueues(device_t, int, int, struct vq_alloc_info *); static int vtpci_setup_intr(device_t, enum intr_type); @@ -215,6 +216,7 @@ DEVMETHOD(virtio_bus_notify_vq, vtpci_notify_virtqueue), DEVMETHOD(virtio_bus_read_device_config, vtpci_read_dev_config), DEVMETHOD(virtio_bus_write_device_config, vtpci_write_dev_config), + DEVMETHOD(virtio_bus_virtqueue_size, vtpci_virtqueue_size), DEVMETHOD_END }; @@ -474,6 +476,19 @@ return ((sc->vtpci_features & feature) != 0); } +static uint32_t +vtpci_virtqueue_size(device_t dev, int idx) +{ + struct vtpci_softc *sc; + uint32_t size; + + sc = device_get_softc(dev); + vtpci_select_virtqueue(sc, idx); + size = vtpci_read_config_2(sc, VIRTIO_PCI_QUEUE_NUM); + + return (size); +} + static int vtpci_alloc_virtqueues(device_t dev, int flags, int nvqs, struct vq_alloc_info *vq_info) Index: sys/dev/virtio/scsi/virtio_scsi.c =================================================================== --- sys/dev/virtio/scsi/virtio_scsi.c +++ sys/dev/virtio/scsi/virtio_scsi.c @@ -68,6 +68,8 @@ #include "virtio_if.h" +#define VTSCSI_FIRST_REQUEST_QUEUE 2 + static int vtscsi_modevent(module_t, int, void *); static int vtscsi_probe(device_t); @@ -79,7 +81,8 @@ static void vtscsi_negotiate_features(struct vtscsi_softc *); static void vtscsi_read_config(struct vtscsi_softc *, struct virtio_scsi_config *); -static int vtscsi_maximum_segments(struct vtscsi_softc *, int); +static uint32_t vtscsi_minimum_request_queue_size(struct vtscsi_softc *, int); +static int vtscsi_maximum_segments(struct vtscsi_softc *, int, int); static int vtscsi_alloc_virtqueues(struct vtscsi_softc *); static void vtscsi_write_device_config(struct vtscsi_softc *); static int vtscsi_reinit(struct vtscsi_softc *); @@ -297,7 +300,8 @@ vtscsi_write_device_config(sc); - sc->vtscsi_max_nsegs = vtscsi_maximum_segments(sc, scsicfg.seg_max); + sc->vtscsi_max_nsegs = vtscsi_maximum_segments(sc, scsicfg.seg_max, + scsicfg.num_queues); sc->vtscsi_sglist = sglist_alloc(sc->vtscsi_max_nsegs, M_NOWAIT); if (sc->vtscsi_sglist == NULL) { error = ENOMEM; @@ -437,8 +441,27 @@ #undef VTSCSI_GET_CONFIG +static uint32_t +vtscsi_minimum_request_queue_size(struct vtscsi_softc *sc, int num_queues) +{ + device_t dev; + uint32_t size, s; + int i; + + dev = sc->vtscsi_dev; + size = 0; + for (i = 0; i < num_queues; ++i) { + s = virtio_virtqueue_size(dev, i + VTSCSI_FIRST_REQUEST_QUEUE); + if (s != 0 && (size == 0 || s < size)) { + size = s; + } + } + + return (size); +} + static int -vtscsi_maximum_segments(struct vtscsi_softc *sc, int seg_max) +vtscsi_maximum_segments(struct vtscsi_softc *sc, int seg_max, int num_queues) { int nsegs; @@ -448,6 +471,12 @@ nsegs += MIN(seg_max, MAXPHYS / PAGE_SIZE + 1); if (sc->vtscsi_flags & VTSCSI_FLAG_INDIRECT) nsegs = MIN(nsegs, VIRTIO_MAX_INDIRECT); + /* + * Ensure the assertions in virtqueue_enqueue(), + * even if the hypervisor reports a bad seg_max. + */ + nsegs = MIN(nsegs, + vtscsi_minimum_request_queue_size(sc, num_queues)); } else nsegs += 1; Index: sys/dev/virtio/virtio.h =================================================================== --- sys/dev/virtio/virtio.h +++ sys/dev/virtio/virtio.h @@ -96,6 +96,7 @@ int virtio_config_generation(device_t dev); int virtio_reinit(device_t dev, uint64_t features); void virtio_reinit_complete(device_t dev); +uint32_t virtio_virtqueue_size(device_t dev, int idx); int virtio_child_pnpinfo_str(device_t busdev, device_t child, char *buf, size_t buflen); Index: sys/dev/virtio/virtio.c =================================================================== --- sys/dev/virtio/virtio.c +++ sys/dev/virtio/virtio.c @@ -287,6 +287,13 @@ return (0); } +uint32_t +virtio_virtqueue_size(device_t dev, int idx) +{ + + return (VIRTIO_BUS_VIRTQUEUE_SIZE(device_get_parent(dev), idx)); +} + static int virtio_modevent(module_t mod, int type, void *unused) { Index: sys/dev/virtio/virtio_bus_if.m =================================================================== --- sys/dev/virtio/virtio_bus_if.m +++ sys/dev/virtio/virtio_bus_if.m @@ -104,3 +104,7 @@ device_t dev; }; +METHOD uint32_t virtqueue_size { + device_t dev; + int idx; +};