summary refs log tree commit diff stats
path: root/hw/s390x/css.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2016-07-11 18:46:38 +0100
committerPeter Maydell <peter.maydell@linaro.org>2016-07-11 18:46:38 +0100
commitf1ef55786691a1bf79db0b74ba1e5347a0d38c1b (patch)
treee42476d052ce6815b18f2a2460c30d64a99585d6 /hw/s390x/css.c
parent7de2cc8f787a9cf8edff616c75ea9e73a86db9ca (diff)
parent93d16d81c8af0fe93a04bee8a4cb3259f480cab6 (diff)
downloadfocaccia-qemu-f1ef55786691a1bf79db0b74ba1e5347a0d38c1b.tar.gz
focaccia-qemu-f1ef55786691a1bf79db0b74ba1e5347a0d38c1b.zip
Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20160711' into staging
Last round of s390x patches for 2.7:
- A large update of the s390x PCI code, bringing it in line with
  the architecture
- Fixes and improvements in the ipl (boot) code
- Refactoring in the css code

# gpg: Signature made Mon 11 Jul 2016 09:04:51 BST
# gpg:                using RSA key 0xDECF6B93C6F02FAF
# gpg: Good signature from "Cornelia Huck <huckc@linux.vnet.ibm.com>"
# gpg:                 aka "Cornelia Huck <cornelia.huck@de.ibm.com>"
# Primary key fingerprint: C3D0 D66D C362 4FF6 A8C0  18CE DECF 6B93 C6F0 2FAF

* remotes/cohuck/tags/s390x-20160711: (25 commits)
  s390x/pci: make hot-unplug handler smoother
  s390x/pci: replace fid with idx in msg data of msix
  s390x/pci: fix stpcifc_service_call
  s390x/pci: refactor list_pci
  s390x/pci: refactor s390_pci_find_dev_by_idx
  s390x/pci: add checkings in CLP_SET_PCI_FN
  s390x/pci: enable zpci hot-plug/hot-unplug
  s390x/pci: enable uid-checking
  s390x/pci: introduce S390PCIBusDevice qdev
  s390x/pci: introduce S390PCIIOMMU
  s390x/pci: introduce S390PCIBus
  s390x/pci: enforce zPCI state checking
  s390x/pci: refactor s390_pci_find_dev_by_fh
  s390x/pci: unify FH_ macros
  s390x/pci: write fid in CLP_QUERY_PCI_FN
  s390x/pci: acceleration for getting S390pciState
  s390x/pci: fix failures of dma map/unmap
  s390x/css: Unplug handler of virtual css bridge
  s390x/css: Factor out virtual css bridge and bus
  s390x/css: use define for "virtual-css-bridge" literal
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/s390x/css.c')
-rw-r--r--hw/s390x/css.c143
1 files changed, 143 insertions, 0 deletions
diff --git a/hw/s390x/css.c b/hw/s390x/css.c
index 76668814da..54991f5d6f 100644
--- a/hw/s390x/css.c
+++ b/hw/s390x/css.c
@@ -1340,6 +1340,116 @@ SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid)
     return channel_subsys.css[real_cssid]->sch_set[ssid]->sch[schid];
 }
 
+/**
+ * Return free device number in subchannel set.
+ *
+ * Return index of the first free device number in the subchannel set
+ * identified by @p cssid and @p ssid, beginning the search at @p
+ * start and wrapping around at MAX_DEVNO. Return a value exceeding
+ * MAX_SCHID if there are no free device numbers in the subchannel
+ * set.
+ */
+static uint32_t css_find_free_devno(uint8_t cssid, uint8_t ssid,
+                                    uint16_t start)
+{
+    uint32_t round;
+
+    for (round = 0; round <= MAX_DEVNO; round++) {
+        uint16_t devno = (start + round) % MAX_DEVNO;
+
+        if (!css_devno_used(cssid, ssid, devno)) {
+            return devno;
+        }
+    }
+    return MAX_DEVNO + 1;
+}
+
+/**
+ * Return first free subchannel (id) in subchannel set.
+ *
+ * Return index of the first free subchannel in the subchannel set
+ * identified by @p cssid and @p ssid, if there is any. Return a value
+ * exceeding MAX_SCHID if there are no free subchannels in the
+ * subchannel set.
+ */
+static uint32_t css_find_free_subch(uint8_t cssid, uint8_t ssid)
+{
+    uint32_t schid;
+
+    for (schid = 0; schid <= MAX_SCHID; schid++) {
+        if (!css_find_subch(1, cssid, ssid, schid)) {
+            return schid;
+        }
+    }
+    return MAX_SCHID + 1;
+}
+
+/**
+ * Return first free subchannel (id) in subchannel set for a device number
+ *
+ * Verify the device number @p devno is not used yet in the subchannel
+ * set identified by @p cssid and @p ssid. Set @p schid to the index
+ * of the first free subchannel in the subchannel set, if there is
+ * any. Return true if everything succeeded and false otherwise.
+ */
+static bool css_find_free_subch_for_devno(uint8_t cssid, uint8_t ssid,
+                                          uint16_t devno, uint16_t *schid,
+                                          Error **errp)
+{
+    uint32_t free_schid;
+
+    assert(schid);
+    if (css_devno_used(cssid, ssid, devno)) {
+        error_setg(errp, "Device %x.%x.%04x already exists",
+                   cssid, ssid, devno);
+        return false;
+    }
+    free_schid = css_find_free_subch(cssid, ssid);
+    if (free_schid > MAX_SCHID) {
+        error_setg(errp, "No free subchannel found for %x.%x.%04x",
+                   cssid, ssid, devno);
+        return false;
+    }
+    *schid = free_schid;
+    return true;
+}
+
+/**
+ * Return first free subchannel (id) and device number
+ *
+ * Locate the first free subchannel and first free device number in
+ * any of the subchannel sets of the channel subsystem identified by
+ * @p cssid. Return false if no free subchannel / device number could
+ * be found. Otherwise set @p ssid, @p devno and @p schid to identify
+ * the available subchannel and device number and return true.
+ *
+ * May modify @p ssid, @p devno and / or @p schid even if no free
+ * subchannel / device number could be found.
+ */
+static bool css_find_free_subch_and_devno(uint8_t cssid, uint8_t *ssid,
+                                          uint16_t *devno, uint16_t *schid,
+                                          Error **errp)
+{
+    uint32_t free_schid, free_devno;
+
+    assert(ssid && devno && schid);
+    for (*ssid = 0; *ssid <= MAX_SSID; (*ssid)++) {
+        free_schid = css_find_free_subch(cssid, *ssid);
+        if (free_schid > MAX_SCHID) {
+            continue;
+        }
+        free_devno = css_find_free_devno(cssid, *ssid, free_schid);
+        if (free_devno > MAX_DEVNO) {
+            continue;
+        }
+        *schid = free_schid;
+        *devno = free_devno;
+        return true;
+    }
+    error_setg(errp, "Virtual channel subsystem is full!");
+    return false;
+}
+
 bool css_subch_visible(SubchDev *sch)
 {
     if (sch->ssid > channel_subsys.max_ssid) {
@@ -1762,3 +1872,36 @@ PropertyInfo css_devid_propinfo = {
     .get = get_css_devid,
     .set = set_css_devid,
 };
+
+SubchDev *css_create_virtual_sch(CssDevId bus_id, Error **errp)
+{
+    uint16_t schid = 0;
+    SubchDev *sch;
+
+    if (bus_id.valid) {
+        /* Enforce use of virtual cssid. */
+        if (bus_id.cssid != VIRTUAL_CSSID) {
+            error_setg(errp, "cssid %hhx not valid for virtual devices",
+                       bus_id.cssid);
+            return NULL;
+        }
+        if (!css_find_free_subch_for_devno(bus_id.cssid, bus_id.ssid,
+                                           bus_id.devid, &schid, errp)) {
+            return NULL;
+        }
+    } else {
+        bus_id.cssid = VIRTUAL_CSSID;
+        if (!css_find_free_subch_and_devno(bus_id.cssid, &bus_id.ssid,
+                                           &bus_id.devid, &schid, errp)) {
+            return NULL;
+        }
+    }
+
+    sch = g_malloc0(sizeof(*sch));
+    sch->cssid = bus_id.cssid;
+    sch->ssid = bus_id.ssid;
+    sch->devno = bus_id.devid;
+    sch->schid = schid;
+    css_subch_assign(sch->cssid, sch->ssid, schid, sch->devno, sch);
+    return sch;
+}