Commit 9669c975 authored by Peter Maydell's avatar Peter Maydell
Browse files

Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20190205' into staging



Fixes and improvements in tcg and the zPCI code.

# gpg: Signature made Tue 05 Feb 2019 16:36:09 GMT
# gpg:                using RSA key C3D0D66DC3624FF6A8C018CEDECF6B93C6F02FAF
# gpg:                issuer "cohuck@redhat.com"
# gpg: Good signature from "Cornelia Huck <conny@cornelia-huck.de>" [unknown]
# gpg:                 aka "Cornelia Huck <huckc@linux.vnet.ibm.com>" [full]
# gpg:                 aka "Cornelia Huck <cornelia.huck@de.ibm.com>" [full]
# gpg:                 aka "Cornelia Huck <cohuck@kernel.org>" [unknown]
# gpg:                 aka "Cornelia Huck <cohuck@redhat.com>" [unknown]
# Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0  18CE DECF 6B93 C6F0 2FAF

* remotes/cohuck/tags/s390x-20190205:
  s390x/pci: Unplug remaining requested devices on pcihost reset
  s390x/pci: Warn when adding PCI devices without the 'zpci' feature
  s390x/pci: Fix hotplugging of PCI bridges
  s390x/pci: Fix primary bus number for PCI bridges
  s390x/tcg: Don't model FP registers as globals
  s390x/pci: mark zpci devices as unmigratable
  s390x/pci: Drop release timer and replace it with a flag
  s390x/pci: Introduce unplug requests and split unplug handler
  s390x: remove direct reference to mem_path global from s390x code
  target/s390x: define TCG_GUEST_DEFAULT_MO for MTTCG

Signed-off-by: default avatarPeter Maydell <peter.maydell@linaro.org>
parents 68df0c30 2313a88f
Loading
Loading
Loading
Loading
+161 −83
Original line number Diff line number Diff line
@@ -148,6 +148,22 @@ out:
    psccb->header.response_code = cpu_to_be16(rc);
}

static void s390_pci_perform_unplug(S390PCIBusDevice *pbdev)
{
    HotplugHandler *hotplug_ctrl;

    /* Unplug the PCI device */
    if (pbdev->pdev) {
        hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev->pdev));
        hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev->pdev),
                               &error_abort);
    }

    /* Unplug the zPCI device */
    hotplug_ctrl = qdev_get_hotplug_handler(DEVICE(pbdev));
    hotplug_handler_unplug(hotplug_ctrl, DEVICE(pbdev), &error_abort);
}

void s390_pci_sclp_deconfigure(SCCB *sccb)
{
    IoaCfgSccb *psccb = (IoaCfgSccb *)sccb;
@@ -178,8 +194,8 @@ void s390_pci_sclp_deconfigure(SCCB *sccb)
        pbdev->state = ZPCI_FS_STANDBY;
        rc = SCLP_RC_NORMAL_COMPLETION;

        if (pbdev->release_timer) {
            qdev_unplug(DEVICE(pbdev->pdev), NULL);
        if (pbdev->unplug_requested) {
            s390_pci_perform_unplug(pbdev);
        }
    }
out:
@@ -217,6 +233,24 @@ S390PCIBusDevice *s390_pci_find_dev_by_target(S390pciState *s,
    return NULL;
}

static S390PCIBusDevice *s390_pci_find_dev_by_pci(S390pciState *s,
                                                  PCIDevice *pci_dev)
{
    S390PCIBusDevice *pbdev;

    if (!pci_dev) {
        return NULL;
    }

    QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
        if (pbdev->pdev == pci_dev) {
            return pbdev;
        }
    }

    return NULL;
}

S390PCIBusDevice *s390_pci_find_dev_by_idx(S390pciState *s, uint32_t idx)
{
    return g_hash_table_lookup(s->zpci_table, &idx);
@@ -826,6 +860,12 @@ static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
{
    S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);

    if (!s390_has_feat(S390_FEAT_ZPCI)) {
        warn_report("Plugging a PCI/zPCI device without the 'zpci' CPU "
                    "feature enabled; the guest will not be able to see/use "
                    "this device");
    }

    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
        PCIDevice *pdev = PCI_DEVICE(dev);

@@ -843,6 +883,21 @@ static void s390_pcihost_pre_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
    }
}

static void s390_pci_update_subordinate(PCIDevice *dev, uint32_t nr)
{
    uint32_t old_nr;

    pci_default_write_config(dev, PCI_SUBORDINATE_BUS, nr, 1);
    while (!pci_bus_is_root(pci_get_bus(dev))) {
        dev = pci_get_bus(dev)->parent_dev;

        old_nr = pci_default_read_config(dev, PCI_SUBORDINATE_BUS, 1);
        if (old_nr < nr) {
            pci_default_write_config(dev, PCI_SUBORDINATE_BUS, nr, 1);
        }
    }
}

static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
                              Error **errp)
{
@@ -851,25 +906,21 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
    S390PCIBusDevice *pbdev = NULL;

    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
        BusState *bus;
        PCIBridge *pb = PCI_BRIDGE(dev);
        PCIDevice *pdev = PCI_DEVICE(dev);

        pdev = PCI_DEVICE(dev);
        pci_bridge_map_irq(pb, dev->id, s390_pci_map_irq);
        pci_setup_iommu(&pb->sec_bus, s390_pci_dma_iommu, s);

        bus = BUS(&pb->sec_bus);
        qbus_set_hotplug_handler(bus, DEVICE(s), errp);
        qbus_set_hotplug_handler(BUS(&pb->sec_bus), DEVICE(s), errp);

        if (dev->hotplugged) {
            pci_default_write_config(pdev, PCI_PRIMARY_BUS, s->bus_no, 1);
            pci_default_write_config(pdev, PCI_PRIMARY_BUS,
                                     pci_dev_bus_num(pdev), 1);
            s->bus_no += 1;
            pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1);
            do {
                pdev = pci_get_bus(pdev)->parent_dev;
                pci_default_write_config(pdev, PCI_SUBORDINATE_BUS,
                                         s->bus_no, 1);
            } while (pci_get_bus(pdev) && pci_dev_bus_num(pdev));

            s390_pci_update_subordinate(pdev, s->bus_no);
        }
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
        pdev = PCI_DEVICE(dev);
@@ -925,99 +976,96 @@ static void s390_pcihost_plug(HotplugHandler *hotplug_dev, DeviceState *dev,
    }
}

static void s390_pcihost_timer_cb(void *opaque)
static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
                                Error **errp)
{
    S390PCIBusDevice *pbdev = opaque;
    S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
    S390PCIBusDevice *pbdev = NULL;

    if (pbdev->summary_ind) {
        pci_dereg_irqs(pbdev);
    }
    if (pbdev->iommu->enabled) {
        pci_dereg_ioat(pbdev->iommu);
    }
    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
        PCIDevice *pci_dev = PCI_DEVICE(dev);
        PCIBus *bus;
        int32_t devfn;

    pbdev->state = ZPCI_FS_STANDBY;
    s390_pci_generate_plug_event(HP_EVENT_CONFIGURED_TO_STBRES,
        pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
        g_assert(pbdev);

        s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
                                     pbdev->fh, pbdev->fid);
    qdev_unplug(DEVICE(pbdev), NULL);
        bus = pci_get_bus(pci_dev);
        devfn = pci_dev->devfn;
        object_unparent(OBJECT(pci_dev));

        s390_pci_msix_free(pbdev);
        s390_pci_iommu_free(s, bus, devfn);
        pbdev->pdev = NULL;
        pbdev->state = ZPCI_FS_RESERVED;
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
        pbdev = S390_PCI_DEVICE(dev);
        pbdev->fid = 0;
        QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
        g_hash_table_remove(s->zpci_table, &pbdev->idx);
        object_unparent(OBJECT(pbdev));
    }
}

static void s390_pcihost_unplug(HotplugHandler *hotplug_dev, DeviceState *dev,
static void s390_pcihost_unplug_request(HotplugHandler *hotplug_dev,
                                        DeviceState *dev,
                                        Error **errp)
{
    S390pciState *s = S390_PCI_HOST_BRIDGE(hotplug_dev);
    PCIDevice *pci_dev = NULL;
    PCIBus *bus;
    int32_t devfn;
    S390PCIBusDevice *pbdev = NULL;
    S390PCIBusDevice *pbdev;

    if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_BRIDGE)) {
        error_setg(errp, "PCI bridge hot unplug currently not supported");
        return;
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) {
        pci_dev = PCI_DEVICE(dev);

        QTAILQ_FOREACH(pbdev, &s->zpci_devs, link) {
            if (pbdev->pdev == pci_dev) {
                break;
            }
        }
        assert(pbdev != NULL);
        /*
         * Redirect the unplug request to the zPCI device and remember that
         * we've checked the PCI device already (to prevent endless recursion).
         */
        pbdev = s390_pci_find_dev_by_pci(s, PCI_DEVICE(dev));
        g_assert(pbdev);
        pbdev->pci_unplug_request_processed = true;
        qdev_unplug(DEVICE(pbdev), errp);
    } else if (object_dynamic_cast(OBJECT(dev), TYPE_S390_PCI_DEVICE)) {
        pbdev = S390_PCI_DEVICE(dev);
        pci_dev = pbdev->pdev;
    } else {
        g_assert_not_reached();

        /*
         * If unplug was initially requested for the zPCI device, we
         * first have to redirect to the PCI device, which will in return
         * redirect back to us after performing its checks (if the request
         * is not blocked, e.g. because it's a PCI bridge).
         */
        if (pbdev->pdev && !pbdev->pci_unplug_request_processed) {
            qdev_unplug(DEVICE(pbdev->pdev), errp);
            return;
        }
        pbdev->pci_unplug_request_processed = false;

        switch (pbdev->state) {
    case ZPCI_FS_RESERVED:
        goto out;
        case ZPCI_FS_STANDBY:
        case ZPCI_FS_RESERVED:
            s390_pci_perform_unplug(pbdev);
            break;
        default:
        if (pbdev->release_timer) {
            return;
        }
            /*
             * Allow to send multiple requests, e.g. if the guest crashed
             * before releasing the device, we would not be able to send
             * another request to the same VM (e.g. fresh OS).
             */
            pbdev->unplug_requested = true;
            s390_pci_generate_plug_event(HP_EVENT_DECONFIGURE_REQUEST,
                                         pbdev->fh, pbdev->fid);
        pbdev->release_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL,
                                            s390_pcihost_timer_cb,
                                            pbdev);
        timer_mod(pbdev->release_timer,
                  qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + HOT_UNPLUG_TIMEOUT);
        return;
        }

    if (pbdev->release_timer) {
        timer_del(pbdev->release_timer);
        timer_free(pbdev->release_timer);
        pbdev->release_timer = NULL;
    } else {
        g_assert_not_reached();
    }

    s390_pci_generate_plug_event(HP_EVENT_STANDBY_TO_RESERVED,
                                 pbdev->fh, pbdev->fid);
    bus = pci_get_bus(pci_dev);
    devfn = pci_dev->devfn;
    object_unparent(OBJECT(pci_dev));
    fmb_timer_free(pbdev);
    s390_pci_msix_free(pbdev);
    s390_pci_iommu_free(s, bus, devfn);
    pbdev->pdev = NULL;
    pbdev->state = ZPCI_FS_RESERVED;
out:
    pbdev->fid = 0;
    QTAILQ_REMOVE(&s->zpci_devs, pbdev, link);
    g_hash_table_remove(s->zpci_table, &pbdev->idx);
    object_unparent(OBJECT(pbdev));
}

static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev,
                                      void *opaque)
{
    S390pciState *s = opaque;
    unsigned int primary = s->bus_no;
    unsigned int subordinate = 0xff;
    PCIBus *sec_bus = NULL;

    if ((pci_default_read_config(pdev, PCI_HEADER_TYPE, 1) !=
@@ -1026,7 +1074,7 @@ static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev,
    }

    (s->bus_no)++;
    pci_default_write_config(pdev, PCI_PRIMARY_BUS, primary, 1);
    pci_default_write_config(pdev, PCI_PRIMARY_BUS, pci_dev_bus_num(pdev), 1);
    pci_default_write_config(pdev, PCI_SECONDARY_BUS, s->bus_no, 1);
    pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1);

@@ -1035,7 +1083,7 @@ static void s390_pci_enumerate_bridge(PCIBus *bus, PCIDevice *pdev,
        return;
    }

    pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, subordinate, 1);
    /* Assign numbers to all child bridges. The last is the highest number. */
    pci_for_each_device(sec_bus, pci_bus_num(sec_bus),
                        s390_pci_enumerate_bridge, s);
    pci_default_write_config(pdev, PCI_SUBORDINATE_BUS, s->bus_no, 1);
@@ -1045,7 +1093,26 @@ static void s390_pcihost_reset(DeviceState *dev)
{
    S390pciState *s = S390_PCI_HOST_BRIDGE(dev);
    PCIBus *bus = s->parent_obj.bus;
    S390PCIBusDevice *pbdev, *next;

    /* Process all pending unplug requests */
    QTAILQ_FOREACH_SAFE(pbdev, &s->zpci_devs, link, next) {
        if (pbdev->unplug_requested) {
            if (pbdev->summary_ind) {
                pci_dereg_irqs(pbdev);
            }
            if (pbdev->iommu->enabled) {
                pci_dereg_ioat(pbdev->iommu);
            }
            pbdev->state = ZPCI_FS_STANDBY;
            s390_pci_perform_unplug(pbdev);
        }
    }

    /*
     * When resetting a PCI bridge, the assigned numbers are set to 0. So
     * on every system reset, we also have to reassign numbers.
     */
    s->bus_no = 0;
    pci_for_each_device(bus, pci_bus_num(bus), s390_pci_enumerate_bridge, s);
}
@@ -1059,6 +1126,7 @@ static void s390_pcihost_class_init(ObjectClass *klass, void *data)
    dc->realize = s390_pcihost_realize;
    hc->pre_plug = s390_pcihost_pre_plug;
    hc->plug = s390_pcihost_plug;
    hc->unplug_request = s390_pcihost_unplug_request;
    hc->unplug = s390_pcihost_unplug;
    msi_nonbroken = true;
}
@@ -1219,6 +1287,15 @@ static Property s390_pci_device_properties[] = {
    DEFINE_PROP_END_OF_LIST(),
};

static const VMStateDescription s390_pci_device_vmstate = {
    .name = TYPE_S390_PCI_DEVICE,
    /*
     * TODO: add state handling here, so migration works at least with
     * emulated pci devices on s390x
     */
    .unmigratable = 1,
};

static void s390_pci_device_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);
@@ -1229,6 +1306,7 @@ static void s390_pci_device_class_init(ObjectClass *klass, void *data)
    dc->bus_type = TYPE_S390_PCI_BUS;
    dc->realize = s390_pci_device_realize;
    dc->props = s390_pci_device_properties;
    dc->vmsd = &s390_pci_device_vmstate;
}

static const TypeInfo s390_pci_device_info = {
+2 −2
Original line number Diff line number Diff line
@@ -35,7 +35,6 @@
#define ZPCI_MAX_UID 0xffff
#define UID_UNDEFINED 0
#define UID_CHECKING_ENABLED 0x01
#define HOT_UNPLUG_TIMEOUT (NANOSECONDS_PER_SECOND * 60 * 5)

#define S390_PCI_HOST_BRIDGE(obj) \
    OBJECT_CHECK(S390pciState, (obj), TYPE_S390_PCI_HOST_BRIDGE)
@@ -335,7 +334,8 @@ struct S390PCIBusDevice {
    MemoryRegion msix_notify_mr;
    IndAddr *summary_ind;
    IndAddr *indicator;
    QEMUTimer *release_timer;
    bool pci_unplug_request_processed;
    bool unplug_requested;
    QTAILQ_ENTRY(S390PCIBusDevice) link;
};

+4 −0
Original line number Diff line number Diff line
@@ -35,6 +35,10 @@
#define CPUArchState struct CPUS390XState

#include "exec/cpu-defs.h"

/* The z/Architecture has a strong memory model with some store-after-load re-ordering */
#define TCG_GUEST_DEFAULT_MO      (TCG_MO_ALL & ~TCG_MO_ST_LD)

#define TARGET_PAGE_BITS 12

#define TARGET_PHYS_ADDR_SPACE_BITS 64
+75 −75

File changed.

Preview size limit exceeded, changes collapsed.

+3 −2
Original line number Diff line number Diff line
@@ -42,6 +42,7 @@
#include "hw/hw.h"
#include "sysemu/device_tree.h"
#include "exec/gdbstub.h"
#include "exec/ram_addr.h"
#include "trace.h"
#include "hw/s390x/s390-pci-inst.h"
#include "hw/s390x/s390-pci-bus.h"
@@ -287,7 +288,7 @@ void kvm_s390_crypto_reset(void)

static int kvm_s390_configure_mempath_backing(KVMState *s)
{
    size_t path_psize = qemu_mempath_getpagesize(mem_path);
    size_t path_psize = qemu_getrampagesize();

    if (path_psize == 4 * KiB) {
        return 0;
@@ -319,7 +320,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s)
{
    MachineClass *mc = MACHINE_GET_CLASS(ms);

    if (mem_path && kvm_s390_configure_mempath_backing(s)) {
    if (kvm_s390_configure_mempath_backing(s)) {
        return -EINVAL;
    }

Loading