Commit 7bca3892 authored by Cornelia Huck's avatar Cornelia Huck
Browse files

s390x/virtio-ccw: reference-counted indicators



Make code using the same indicators point to a single allocated structure
that is freed when the last user goes away.

This will be used by the irqfd code to unmap addresses after the last user
is gone.

Reviewed-by: default avatarThomas Huth <thuth@linux.vnet.ibm.com>
Reviewed-by: default avatarChristian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
parent 03cf077a
Loading
Loading
Loading
Loading
+63 −17
Original line number Diff line number Diff line
@@ -27,6 +27,38 @@
#include "virtio-ccw.h"
#include "trace.h"

static QTAILQ_HEAD(, IndAddr) indicator_addresses =
    QTAILQ_HEAD_INITIALIZER(indicator_addresses);

static IndAddr *get_indicator(hwaddr ind_addr, int len)
{
    IndAddr *indicator;

    QTAILQ_FOREACH(indicator, &indicator_addresses, sibling) {
        if (indicator->addr == ind_addr) {
            indicator->refcnt++;
            return indicator;
        }
    }
    indicator = g_new0(IndAddr, 1);
    indicator->addr = ind_addr;
    indicator->len = len;
    indicator->refcnt = 1;
    QTAILQ_INSERT_TAIL(&indicator_addresses, indicator, sibling);
    return indicator;
}

static void release_indicator(IndAddr *indicator)
{
    assert(indicator->refcnt > 0);
    indicator->refcnt--;
    if (indicator->refcnt > 0) {
        return;
    }
    QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
    g_free(indicator);
}

static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
                               VirtioCcwDevice *dev);

@@ -445,7 +477,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
            ret = -EFAULT;
        } else {
            indicators = ldq_phys(&address_space_memory, ccw.cda);
            dev->indicators = indicators;
            dev->indicators = get_indicator(indicators, sizeof(uint64_t));
            sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
            ret = 0;
        }
@@ -465,7 +497,7 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
            ret = -EFAULT;
        } else {
            indicators = ldq_phys(&address_space_memory, ccw.cda);
            dev->indicators2 = indicators;
            dev->indicators2 = get_indicator(indicators, sizeof(uint64_t));
            sch->curr_status.scsw.count = ccw.count - sizeof(indicators);
            ret = 0;
        }
@@ -517,8 +549,10 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
                ret = -EFAULT;
            } else {
                len = hw_len;
                dev->summary_indicator = thinint->summary_indicator;
                dev->indicators = thinint->device_indicator;
                dev->summary_indicator =
                    get_indicator(thinint->summary_indicator, sizeof(uint8_t));
                dev->indicators = get_indicator(thinint->device_indicator,
                                                thinint->ind_bit / 8 + 1);
                dev->thinint_isc = thinint->isc;
                dev->ind_bit = thinint->ind_bit;
                cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len);
@@ -526,8 +560,8 @@ static int virtio_ccw_cb(SubchDev *sch, CCW1 ccw)
                                              dev->thinint_isc, true, false,
                                              &dev->adapter_id);
                assert(ret == 0);
                sch->thinint_active = ((dev->indicators != 0) &&
                                       (dev->summary_indicator != 0));
                sch->thinint_active = ((dev->indicators != NULL) &&
                                       (dev->summary_indicator != NULL));
                sch->curr_status.scsw.count = ccw.count - len;
                ret = 0;
            }
@@ -558,7 +592,7 @@ static int virtio_ccw_device_init(VirtioCcwDevice *dev, VirtIODevice *vdev)
    sch->driver_data = dev;
    dev->sch = sch;

    dev->indicators = 0;
    dev->indicators = NULL;

    /* Initialize subchannel structure. */
    sch->channel_prog = 0x0;
@@ -697,7 +731,10 @@ static int virtio_ccw_exit(VirtioCcwDevice *dev)
        css_subch_assign(sch->cssid, sch->ssid, sch->schid, sch->devno, NULL);
        g_free(sch);
    }
    dev->indicators = 0;
    if (dev->indicators) {
        release_indicator(dev->indicators);
        dev->indicators = NULL;
    }
    return 0;
}

@@ -954,17 +991,17 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
             * ind_bit indicates the start of the indicators in a big
             * endian notation.
             */
            virtio_set_ind_atomic(sch, dev->indicators +
            virtio_set_ind_atomic(sch, dev->indicators->addr +
                                  (dev->ind_bit + vector) / 8,
                                  0x80 >> ((dev->ind_bit + vector) % 8));
            if (!virtio_set_ind_atomic(sch, dev->summary_indicator,
            if (!virtio_set_ind_atomic(sch, dev->summary_indicator->addr,
                                       0x01)) {
                css_adapter_interrupt(dev->thinint_isc);
            }
        } else {
            indicators = ldq_phys(&address_space_memory, dev->indicators);
            indicators = ldq_phys(&address_space_memory, dev->indicators->addr);
            indicators |= 1ULL << vector;
            stq_phys(&address_space_memory, dev->indicators, indicators);
            stq_phys(&address_space_memory, dev->indicators->addr, indicators);
            css_conditional_io_interrupt(sch);
        }
    } else {
@@ -972,9 +1009,9 @@ static void virtio_ccw_notify(DeviceState *d, uint16_t vector)
            return;
        }
        vector = 0;
        indicators = ldq_phys(&address_space_memory, dev->indicators2);
        indicators = ldq_phys(&address_space_memory, dev->indicators2->addr);
        indicators |= 1ULL << vector;
        stq_phys(&address_space_memory, dev->indicators2, indicators);
        stq_phys(&address_space_memory, dev->indicators2->addr, indicators);
        css_conditional_io_interrupt(sch);
    }
}
@@ -995,9 +1032,18 @@ static void virtio_ccw_reset(DeviceState *d)
    virtio_ccw_stop_ioeventfd(dev);
    virtio_reset(vdev);
    css_reset_sch(dev->sch);
    dev->indicators = 0;
    dev->indicators2 = 0;
    dev->summary_indicator = 0;
    if (dev->indicators) {
        release_indicator(dev->indicators);
        dev->indicators = NULL;
    }
    if (dev->indicators2) {
        release_indicator(dev->indicators2);
        dev->indicators2 = NULL;
    }
    if (dev->summary_indicator) {
        release_indicator(dev->summary_indicator);
        dev->summary_indicator = NULL;
    }
}

static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
+10 −3
Original line number Diff line number Diff line
@@ -75,6 +75,13 @@ typedef struct VirtIOCCWDeviceClass {
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT 1
#define VIRTIO_CCW_FLAG_USE_IOEVENTFD   (1 << VIRTIO_CCW_FLAG_USE_IOEVENTFD_BIT)

typedef struct IndAddr {
    hwaddr addr;
    unsigned long refcnt;
    int len;
    QTAILQ_ENTRY(IndAddr) sibling;
} IndAddr;

struct VirtioCcwDevice {
    DeviceState parent_obj;
    SubchDev *sch;
@@ -87,9 +94,9 @@ struct VirtioCcwDevice {
    uint8_t thinint_isc;
    uint32_t adapter_id;
    /* Guest provided values: */
    hwaddr indicators;
    hwaddr indicators2;
    hwaddr summary_indicator;
    IndAddr *indicators;
    IndAddr *indicators2;
    IndAddr *summary_indicator;
    uint64_t ind_bit;
};