Merge remote-tracking branch 'mst/for_anthony' into staging

This commit is contained in:
Anthony Liguori 2011-08-29 08:48:15 -05:00
commit c783924136
6 changed files with 125 additions and 26 deletions

View File

@ -1811,6 +1811,25 @@ static uint8_t pci_find_capability_list(PCIDevice *pdev, uint8_t cap_id,
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.
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)
@ -1952,11 +1971,30 @@ int pci_add_capability(PCIDevice *pdev, uint8_t cap_id,
uint8_t offset, uint8_t size)
{
uint8_t *config;
int i, overlapping_cap;
if (!offset) {
offset = pci_find_space(pdev, size);
if (!offset) {
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;

View File

@ -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
* 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;
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)) {
return;
}

View File

@ -415,7 +415,7 @@ static void pcie_aer_update_log(PCIDevice *dev, const PCIEAERErr *err)
int i;
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(first_bit);
@ -495,7 +495,7 @@ static int pcie_aer_record_error(PCIDevice *dev,
int fep = PCI_ERR_CAP_FEP(errcap);
assert(err->status);
assert(err->status & (err->status - 1));
assert(!(err->status & (err->status - 1)));
if (errcap & PCI_ERR_CAP_MHRE &&
(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)) {
char *e = NULL;
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') {
monitor_printf(mon, "invalid error status value. \"%s\"",
error_name);
return -EINVAL;
}
}
err.status = error_status;
err.source_id = (pci_bus_num(dev->bus) << 8) | dev->devfn;
err.flags = 0;
if (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;
}
if (qdict_haskey(qdict, "header0")) {

View File

@ -515,11 +515,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
};
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);
r = ioctl(dev->control, VHOST_SET_VRING_NUM, &state);
if (r) {
@ -567,12 +562,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
r = -errno;
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));
r = ioctl(dev->control, VHOST_SET_VRING_KICK, &file);
if (r) {
@ -591,8 +580,6 @@ static int vhost_virtqueue_init(struct vhost_dev *dev,
fail_call:
fail_kick:
vdev->binding->set_host_notifier(vdev->binding_opaque, idx, false);
fail_host_notifier:
fail_alloc:
cpu_physical_memory_unmap(vq->ring, virtio_queue_get_ring_size(vdev, idx),
0, 0);
@ -618,12 +605,6 @@ static void vhost_virtqueue_cleanup(struct vhost_dev *dev,
.index = idx,
};
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);
if (r < 0) {
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;
}
/* 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 i, r;
@ -762,6 +797,7 @@ fail:
return r;
}
/* Host notifiers must be enabled at this point. */
void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev)
{
int i, r;

View File

@ -46,5 +46,7 @@ void vhost_dev_cleanup(struct vhost_dev *hdev);
bool vhost_dev_query(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);
int vhost_dev_enable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev);
void vhost_dev_disable_notifiers(struct vhost_dev *hdev, VirtIODevice *vdev);
#endif

View File

@ -139,16 +139,22 @@ int vhost_net_start(struct vhost_net *net,
{
struct vhost_vring_file file = { };
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)) {
tap_set_vnet_hdr_len(net->vc,
sizeof(struct virtio_net_hdr_mrg_rxbuf));
}
net->dev.nvqs = 2;
net->dev.vqs = net->vqs;
r = vhost_dev_start(&net->dev, dev);
if (r < 0) {
return r;
goto fail_start;
}
net->vc->info->poll(net->vc, false);
@ -173,6 +179,9 @@ fail:
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
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;
}
@ -190,6 +199,7 @@ void vhost_net_stop(struct vhost_net *net,
if (net->dev.acked_features & (1 << VIRTIO_NET_F_MRG_RXBUF)) {
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)