mirror of https://github.com/xqemu/xqemu.git
Merge remote-tracking branch 'mst/for_anthony' into staging
This commit is contained in:
commit
c783924136
38
hw/pci.c
38
hw/pci.c
|
@ -1811,6 +1811,25 @@ static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id,
|
||||||
return next;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t pci_find_capability_at_offset(PCIDevice *pdev, uint8_t offset)
|
||||||
|
{
|
||||||
|
uint8_t next, prev, found = 0;
|
||||||
|
|
||||||
|
if (!(pdev->used[offset])) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(pdev->config[PCI_STATUS] & PCI_STATUS_CAP_LIST);
|
||||||
|
|
||||||
|
for (prev = PCI_CAPABILITY_LIST; (next = pdev->config[prev]);
|
||||||
|
prev = next + PCI_CAP_LIST_NEXT) {
|
||||||
|
if (next <= offset && next > found) {
|
||||||
|
found = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
/* Patch the PCI vendor and device ids in a PCI rom image if necessary.
|
/* Patch the PCI vendor and device ids in a PCI rom image if necessary.
|
||||||
This is needed for an option rom which is used for more than one device. */
|
This is needed for an option rom which is used for more than one device. */
|
||||||
static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size)
|
static void pci_patch_ids(PCIDevice *pdev, uint8_t *ptr, int size)
|
||||||
|
@ -1952,11 +1971,30 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
|
||||||
uint8_t offset, uint8_t size)
|
uint8_t offset, uint8_t size)
|
||||||
{
|
{
|
||||||
uint8_t *config;
|
uint8_t *config;
|
||||||
|
int i, overlapping_cap;
|
||||||
|
|
||||||
if (!offset) {
|
if (!offset) {
|
||||||
offset = pci_find_space(pdev, size);
|
offset = pci_find_space(pdev, size);
|
||||||
if (!offset) {
|
if (!offset) {
|
||||||
return -ENOSPC;
|
return -ENOSPC;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
/* Verify that capabilities don't overlap. Note: device assignment
|
||||||
|
* depends on this check to verify that the device is not broken.
|
||||||
|
* Should never trigger for emulated devices, but it's helpful
|
||||||
|
* for debugging these. */
|
||||||
|
for (i = offset; i < offset + size; i++) {
|
||||||
|
overlapping_cap = pci_find_capability_at_offset(pdev, i);
|
||||||
|
if (overlapping_cap) {
|
||||||
|
fprintf(stderr, "ERROR: %04x:%02x:%02x.%x "
|
||||||
|
"Attempt to add PCI capability %x at offset "
|
||||||
|
"%x overlaps existing capability %x at offset %x\n",
|
||||||
|
pci_find_domain(pdev->bus), pci_bus_num(pdev->bus),
|
||||||
|
PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
|
||||||
|
cap_id, offset, overlapping_cap, i);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
config = pdev->config + offset;
|
config = pdev->config + offset;
|
||||||
|
|
12
hw/pcie.c
12
hw/pcie.c
|
@ -175,6 +175,14 @@ static void hotplug_event_notify(PCIDevice *dev)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void hotplug_event_clear(PCIDevice *dev)
|
||||||
|
{
|
||||||
|
hotplug_event_update_event_status(dev);
|
||||||
|
if (!msix_enabled(dev) && !msi_enabled(dev) && !dev->exp.hpev_notified) {
|
||||||
|
qemu_set_irq(dev->irq[dev->exp.hpev_intx], 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* A PCI Express Hot-Plug Event has occurred, so update slot status register
|
* A PCI Express Hot-Plug Event has occurred, so update slot status register
|
||||||
* and notify OS of the event if necessary.
|
* and notify OS of the event if necessary.
|
||||||
|
@ -320,6 +328,10 @@ void pcie_cap_slot_write_config(PCIDevice *dev,
|
||||||
uint8_t *exp_cap = dev->config + pos;
|
uint8_t *exp_cap = dev->config + pos;
|
||||||
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
|
uint16_t sltsta = pci_get_word(exp_cap + PCI_EXP_SLTSTA);
|
||||||
|
|
||||||
|
if (ranges_overlap(addr, len, pos + PCI_EXP_SLTSTA, 2)) {
|
||||||
|
hotplug_event_clear(dev);
|
||||||
|
}
|
||||||
|
|
||||||
if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) {
|
if (!ranges_overlap(addr, len, pos + PCI_EXP_SLTCTL, 2)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -415,7 +415,7 @@ static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err)
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
assert(err->status);
|
assert(err->status);
|
||||||
assert(err->status & (err->status - 1));
|
assert(!(err->status & (err->status - 1)));
|
||||||
|
|
||||||
errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
|
errcap &= ~(PCI_ERR_CAP_FEP_MASK | PCI_ERR_CAP_TLP);
|
||||||
errcap |= PCI_ERR_CAP_FEP(first_bit);
|
errcap |= PCI_ERR_CAP_FEP(first_bit);
|
||||||
|
@ -495,7 +495,7 @@ static int pcie_aer_record_error(PCIDevice *dev,
|
||||||
int fep = PCI_ERR_CAP_FEP(errcap);
|
int fep = PCI_ERR_CAP_FEP(errcap);
|
||||||
|
|
||||||
assert(err->status);
|
assert(err->status);
|
||||||
assert(err->status & (err->status - 1));
|
assert(!(err->status & (err->status - 1)));
|
||||||
|
|
||||||
if (errcap & PCI_ERR_CAP_MHRE &&
|
if (errcap & PCI_ERR_CAP_MHRE &&
|
||||||
(pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) {
|
(pci_get_long(aer_cap + PCI_ERR_UNCOR_STATUS) & (1U << fep))) {
|
||||||
|
@ -979,20 +979,21 @@ int do_pcie_aer_inejct_error(Monitor *mon,
|
||||||
if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) {
|
if (pcie_aer_parse_error_string(error_name, &error_status, &correctable)) {
|
||||||
char *e = NULL;
|
char *e = NULL;
|
||||||
error_status = strtoul(error_name, &e, 0);
|
error_status = strtoul(error_name, &e, 0);
|
||||||
correctable = !!qdict_get_int(qdict, "correctable");
|
correctable = qdict_get_try_bool(qdict, "correctable", 0);
|
||||||
if (!e || *e != '\0') {
|
if (!e || *e != '\0') {
|
||||||
monitor_printf(mon, "invalid error status value. \"%s\"",
|
monitor_printf(mon, "invalid error status value. \"%s\"",
|
||||||
error_name);
|
error_name);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
err.status = error_status;
|
||||||
err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
|
err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
|
||||||
|
|
||||||
err.flags = 0;
|
err.flags = 0;
|
||||||
if (correctable) {
|
if (correctable) {
|
||||||
err.flags |= PCIE_AER_ERR_IS_CORRECTABLE;
|
err.flags |= PCIE_AER_ERR_IS_CORRECTABLE;
|
||||||
}
|
}
|
||||||
if (qdict_get_int(qdict, "advisory_non_fatal")) {
|
if (qdict_get_try_bool(qdict, "advisory_non_fatal", 0)) {
|
||||||
err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY;
|
err.flags |= PCIE_AER_ERR_MAYBE_ADVISORY;
|
||||||
}
|
}
|
||||||
if (qdict_haskey(qdict, "header0")) {
|
if (qdict_haskey(qdict, "header0")) {
|
||||||
|
|
74
hw/vhost.c
74
hw/vhost.c
|
@ -515,11 +515,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
|
||||||
};
|
};
|
||||||
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
|
struct VirtQueue *vvq = virtio_get_queue(vdev, idx);
|
||||||
|
|
||||||
if (!vdev->binding->set_host_notifier) {
|
|
||||||
fprintf(stderr, "binding does not support host notifiers\n");
|
|
||||||
return -ENOSYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
vq->num = state.num = virtio_queue_get_num(vdev, idx);
|
vq->num = state.num = virtio_queue_get_num(vdev, idx);
|
||||||
r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
|
r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
|
||||||
if (r) {
|
if (r) {
|
||||||
|
@ -567,12 +562,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
|
||||||
r = -errno;
|
r = -errno;
|
||||||
goto fail_alloc;
|
goto fail_alloc;
|
||||||
}
|
}
|
||||||
r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, true);
|
|
||||||
if (r < 0) {
|
|
||||||
fprintf(stderr, "Error binding host notifier: %d\n", -r);
|
|
||||||
goto fail_host_notifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
|
file.fd = event_notifier_get_fd(virtio_queue_get_host_notifier(vvq));
|
||||||
r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
|
r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
|
||||||
if (r) {
|
if (r) {
|
||||||
|
@ -591,8 +580,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
|
||||||
|
|
||||||
fail_call:
|
fail_call:
|
||||||
fail_kick:
|
fail_kick:
|
||||||
vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false);
|
|
||||||
fail_host_notifier:
|
|
||||||
fail_alloc:
|
fail_alloc:
|
||||||
cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
|
cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
|
||||||
0, 0);
|
0, 0);
|
||||||
|
@ -618,12 +605,6 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev,
|
||||||
.index = idx,
|
.index = idx,
|
||||||
};
|
};
|
||||||
int r;
|
int r;
|
||||||
r = vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false);
|
|
||||||
if (r < 0) {
|
|
||||||
fprintf(stderr, "vhost VQ %d host cleanup failed: %d\n", idx, r);
|
|
||||||
fflush(stderr);
|
|
||||||
}
|
|
||||||
assert (r >= 0);
|
|
||||||
r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state);
|
r = ioctl(dev->control, VHOST_GET_VRING_BASE, &state);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r);
|
fprintf(stderr, "vhost VQ %d ring restore failed: %d\n", idx, r);
|
||||||
|
@ -697,6 +678,60 @@ bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||||
hdev->force;
|
hdev->force;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Stop processing guest IO notifications in qemu.
|
||||||
|
* Start processing them in vhost in kernel.
|
||||||
|
*/
|
||||||
|
int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
if (!vdev->binding->set_host_notifier) {
|
||||||
|
fprintf(stderr, "binding does not support host notifiers\n");
|
||||||
|
r = -ENOSYS;
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < hdev->nvqs; ++i) {
|
||||||
|
r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, true);
|
||||||
|
if (r < 0) {
|
||||||
|
fprintf(stderr, "vhost VQ %d notifier binding failed: %d\n", i, -r);
|
||||||
|
goto fail_vq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
fail_vq:
|
||||||
|
while (--i >= 0) {
|
||||||
|
r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false);
|
||||||
|
if (r < 0) {
|
||||||
|
fprintf(stderr, "vhost VQ %d notifier cleanup error: %d\n", i, -r);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
assert (r >= 0);
|
||||||
|
}
|
||||||
|
fail:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stop processing guest IO notifications in vhost.
|
||||||
|
* Start processing them in qemu.
|
||||||
|
* This might actually run the qemu handlers right away,
|
||||||
|
* so virtio in qemu must be completely setup when this is called.
|
||||||
|
*/
|
||||||
|
void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||||
|
{
|
||||||
|
int i, r;
|
||||||
|
|
||||||
|
for (i = 0; i < hdev->nvqs; ++i) {
|
||||||
|
r = vdev->binding->set_host_notifier(vdev->binding_opaque, i, false);
|
||||||
|
if (r < 0) {
|
||||||
|
fprintf(stderr, "vhost VQ %d notifier cleanup failed: %d\n", i, -r);
|
||||||
|
fflush(stderr);
|
||||||
|
}
|
||||||
|
assert (r >= 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Host notifiers must be enabled at this point. */
|
||||||
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
|
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||||
{
|
{
|
||||||
int i, r;
|
int i, r;
|
||||||
|
@ -762,6 +797,7 @@ fail:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Host notifiers must be enabled at this point. */
|
||||||
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
|
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
|
||||||
{
|
{
|
||||||
int i, r;
|
int i, r;
|
||||||
|
|
|
@ -46,5 +46,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev);
|
||||||
bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev);
|
bool vhost_dev_query(struct vhost_dev *hdev, VirtIODevice *vdev);
|
||||||
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev);
|
int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev);
|
||||||
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev);
|
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev);
|
||||||
|
int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev);
|
||||||
|
void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -139,16 +139,22 @@ int vhost_net_start(struct vhost_net *net,
|
||||||
{
|
{
|
||||||
struct vhost_vring_file file = { };
|
struct vhost_vring_file file = { };
|
||||||
int r;
|
int r;
|
||||||
|
|
||||||
|
net->dev.nvqs = 2;
|
||||||
|
net->dev.vqs = net->vqs;
|
||||||
|
|
||||||
|
r = vhost_dev_enable_notifiers(&net->dev, dev);
|
||||||
|
if (r < 0) {
|
||||||
|
goto fail_notifiers;
|
||||||
|
}
|
||||||
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
|
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
|
||||||
tap_set_vnet_hdr_len(net->vc,
|
tap_set_vnet_hdr_len(net->vc,
|
||||||
sizeof(struct virtio_net_hdr_mrg_rxbuf));
|
sizeof(struct virtio_net_hdr_mrg_rxbuf));
|
||||||
}
|
}
|
||||||
|
|
||||||
net->dev.nvqs = 2;
|
|
||||||
net->dev.vqs = net->vqs;
|
|
||||||
r = vhost_dev_start(&net->dev, dev);
|
r = vhost_dev_start(&net->dev, dev);
|
||||||
if (r < 0) {
|
if (r < 0) {
|
||||||
return r;
|
goto fail_start;
|
||||||
}
|
}
|
||||||
|
|
||||||
net->vc->info->poll(net->vc, false);
|
net->vc->info->poll(net->vc, false);
|
||||||
|
@ -173,6 +179,9 @@ fail:
|
||||||
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
|
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
|
||||||
tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
|
tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
|
||||||
}
|
}
|
||||||
|
fail_start:
|
||||||
|
vhost_dev_disable_notifiers(&net->dev, dev);
|
||||||
|
fail_notifiers:
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -190,6 +199,7 @@ void vhost_net_stop(struct vhost_net *net,
|
||||||
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
|
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
|
||||||
tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
|
tap_set_vnet_hdr_len(net->vc, sizeof(struct virtio_net_hdr));
|
||||||
}
|
}
|
||||||
|
vhost_dev_disable_notifiers(&net->dev, dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vhost_net_cleanup(struct vhost_net *net)
|
void vhost_net_cleanup(struct vhost_net *net)
|
||||||
|
|
Loading…
Reference in New Issue