mirror of
https://github.com/Motorhead1991/qemu.git
synced 2025-08-06 17:23:56 -06:00
some s390 patches:
- Enable irqfds on s390 via the new adapter interrupt routing type. As a prereq, fix the kvm enable_cap helpers for some compilers and split the s390 flic into kvm and non-kvm parts. - Enable software and hardware debugging support on s390. This needs a kernel headers update. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJTezzuAAoJEN7Pa5PG8C+vujQQAIYfi5sdGIomu6sneNW/BFDY 9mik3q6gqLIOWbyVpPP2UyY6LdIhIFlurjHgaM86MQjNLpP1SU8hqoHBYf1dKi8K 59krakj3RlI6S3kbJc/kljUJZ3PM/GChPWS2SA8G7qY0zyK91flhHBlB/hvE/ar6 XwcL5YOJV3XTglVgS3UKVwuOpZwnP8LzUKOytZ141O/u1Eigc/yag/sWZmY90XxY FUVGbf7pWoOQiiYCEUfAVC3oaCa0wlXLy8jPF3/jeSTr40rjCOeBcV+f+QpLHHSj 2KLktaGWl6COqAzvH2mNNy4SdLLCvFi9TfCjKNTMRnIwoEaZT2MjVDe+47aqTMNs p54dbRLOBdFsJ5Z32TWBlFPMKlgsqlVubhhmMVa+zfOGI5JiSX62rimpSESJ3G/H 5NN3WoO6qTrC6JBfu1AZ/dhxehwnPjrQ2dFHtevH1BOj6avn0vi3l2mbHJtdRxnD Txsbi5v5S1K7HKhsqScQabFlAn0ZwhF/rs+6Ssr1lMegYpJMWR2V1AtTFSi1cuGw NY5tRNB+slufIn5GOBnJemyriigg0B/G0QggyqBuehRZ7WjKnKVdfBZhiOePl8ff 2+2ixsGIBNIgsLKvw7zVaWWZH5OHH7+ZXuFmV1O3fqMqwHPysCQE/kAYrbyMzxlF Sb0gKUcqMrYMa1EjDmpf =hrU2 -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20140520' into staging some s390 patches: - Enable irqfds on s390 via the new adapter interrupt routing type. As a prereq, fix the kvm enable_cap helpers for some compilers and split the s390 flic into kvm and non-kvm parts. - Enable software and hardware debugging support on s390. This needs a kernel headers update. # gpg: Signature made Tue 20 May 2014 12:30:54 BST using RSA key ID C6F02FAF # gpg: Can't check signature: public key not found * remotes/cohuck/tags/s390x-20140520: s390x/kvm: hw debugging support via guest PER facility s390x/kvm: software breakpoint support s390x: remove duplicate definitions of DIAG 501 linux-headers: update s390x/virtio-ccw: wire up irq routing and irqfds s390x/virtio-ccw: reference-counted indicators s390x: add I/O adapter registration s390x: split flic into kvm and non-kvm parts kvm: Fix enable_cap helpers on older gcc Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
45e66b7beb
19 changed files with 1115 additions and 336 deletions
|
@ -26,3 +26,4 @@ obj-$(CONFIG_XICS) += xics.o
|
|||
obj-$(CONFIG_XICS_KVM) += xics_kvm.o
|
||||
obj-$(CONFIG_ALLWINNER_A10_PIC) += allwinner-a10-pic.o
|
||||
obj-$(CONFIG_S390_FLIC) += s390_flic.o
|
||||
obj-$(CONFIG_S390_FLIC_KVM) += s390_flic_kvm.o
|
||||
|
|
|
@ -1,322 +1,103 @@
|
|||
/*
|
||||
* QEMU S390x KVM floating interrupt controller (flic)
|
||||
* QEMU S390x floating interrupt controller (flic)
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||
* your option) any later version. See the COPYING file in the top-level
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "migration/qemu-file.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define FLIC_SAVE_INITIAL_SIZE getpagesize()
|
||||
#define FLIC_FAILED (-1UL)
|
||||
#define FLIC_SAVEVM_VERSION 1
|
||||
S390FLICState *s390_get_flic(void)
|
||||
{
|
||||
S390FLICState *fs;
|
||||
|
||||
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_KVM_S390_FLIC, NULL));
|
||||
if (!fs) {
|
||||
fs = S390_FLIC_COMMON(object_resolve_path(TYPE_QEMU_S390_FLIC, NULL));
|
||||
}
|
||||
return fs;
|
||||
}
|
||||
|
||||
void s390_flic_init(void)
|
||||
{
|
||||
DeviceState *dev;
|
||||
int r;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
dev = qdev_create(NULL, "s390-flic");
|
||||
object_property_add_child(qdev_get_machine(), "s390-flic",
|
||||
OBJECT(dev), NULL);
|
||||
r = qdev_init(dev);
|
||||
if (r) {
|
||||
error_report("flic: couldn't create qdev");
|
||||
}
|
||||
dev = s390_flic_kvm_create();
|
||||
if (!dev) {
|
||||
dev = qdev_create(NULL, TYPE_QEMU_S390_FLIC);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_QEMU_S390_FLIC,
|
||||
OBJECT(dev), NULL);
|
||||
}
|
||||
r = qdev_init(dev);
|
||||
if (r) {
|
||||
error_report("flic: couldn't create qdev");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* flic_get_all_irqs - store all pending irqs in buffer
|
||||
* @buf: pointer to buffer which is passed to kernel
|
||||
* @len: length of buffer
|
||||
* @flic: pointer to flic device state
|
||||
*
|
||||
* Returns: -ENOMEM if buffer is too small,
|
||||
* -EINVAL if attr.group is invalid,
|
||||
* -EFAULT if copying to userspace failed,
|
||||
* on success return number of stored interrupts
|
||||
*/
|
||||
static int flic_get_all_irqs(KVMS390FLICState *flic,
|
||||
void *buf, int len)
|
||||
static int qemu_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
|
||||
uint8_t isc, bool swap,
|
||||
bool is_maskable)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_GET_ALL_IRQS,
|
||||
.addr = (uint64_t) buf,
|
||||
.attr = len,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
|
||||
|
||||
return rc == -1 ? -errno : rc;
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flic_enable_pfault(KVMS390FLICState *flic)
|
||||
static int qemu_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
|
||||
uint64_t map_addr, bool do_map)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_APF_ENABLE,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
if (rc) {
|
||||
fprintf(stderr, "flic: couldn't enable pfault\n");
|
||||
}
|
||||
/* nothing to do */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void flic_disable_wait_pfault(KVMS390FLICState *flic)
|
||||
static int qemu_s390_add_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
if (rc) {
|
||||
fprintf(stderr, "flic: couldn't disable pfault\n");
|
||||
}
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
/** flic_enqueue_irqs - returns 0 on success
|
||||
* @buf: pointer to buffer which is passed to kernel
|
||||
* @len: length of buffer
|
||||
* @flic: pointer to flic device state
|
||||
*
|
||||
* Returns: -EINVAL if attr.group is unknown
|
||||
*/
|
||||
static int flic_enqueue_irqs(void *buf, uint64_t len,
|
||||
KVMS390FLICState *flic)
|
||||
static void qemu_s390_release_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
int rc;
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_ENQUEUE,
|
||||
.addr = (uint64_t) buf,
|
||||
.attr = len,
|
||||
};
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
return rc ? -errno : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __get_all_irqs - store all pending irqs in buffer
|
||||
* @flic: pointer to flic device state
|
||||
* @buf: pointer to pointer to a buffer
|
||||
* @len: length of buffer
|
||||
*
|
||||
* Returns: return value of flic_get_all_irqs
|
||||
* Note: Retry and increase buffer size until flic_get_all_irqs
|
||||
* either returns a value >= 0 or a negative error code.
|
||||
* -ENOMEM is an exception, which means the buffer is too small
|
||||
* and we should try again. Other negative error codes can be
|
||||
* -EFAULT and -EINVAL which we ignore at this point
|
||||
*/
|
||||
static int __get_all_irqs(KVMS390FLICState *flic,
|
||||
void **buf, int len)
|
||||
static void qemu_s390_flic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
int r;
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
|
||||
|
||||
do {
|
||||
/* returns -ENOMEM if buffer is too small and number
|
||||
* of queued interrupts on success */
|
||||
r = flic_get_all_irqs(flic, *buf, len);
|
||||
if (r >= 0) {
|
||||
break;
|
||||
}
|
||||
len *= 2;
|
||||
*buf = g_try_realloc(*buf, len);
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
} while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
|
||||
|
||||
return r;
|
||||
fsc->register_io_adapter = qemu_s390_register_io_adapter;
|
||||
fsc->io_adapter_map = qemu_s390_io_adapter_map;
|
||||
fsc->add_adapter_routes = qemu_s390_add_adapter_routes;
|
||||
fsc->release_adapter_routes = qemu_s390_release_adapter_routes;
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_flic_save - Save pending floating interrupts
|
||||
* @f: QEMUFile containing migration state
|
||||
* @opaque: pointer to flic device state
|
||||
*
|
||||
* Note: Pass buf and len to kernel. Start with one page and
|
||||
* increase until buffer is sufficient or maxium size is
|
||||
* reached
|
||||
*/
|
||||
static void kvm_flic_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
KVMS390FLICState *flic = opaque;
|
||||
int len = FLIC_SAVE_INITIAL_SIZE;
|
||||
void *buf;
|
||||
int count;
|
||||
|
||||
flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
|
||||
|
||||
buf = g_try_malloc0(len);
|
||||
if (!buf) {
|
||||
/* Storing FLIC_FAILED into the count field here will cause the
|
||||
* target system to fail when attempting to load irqs from the
|
||||
* migration state */
|
||||
error_report("flic: couldn't allocate memory");
|
||||
qemu_put_be64(f, FLIC_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
count = __get_all_irqs(flic, &buf, len);
|
||||
if (count < 0) {
|
||||
error_report("flic: couldn't retrieve irqs from kernel, rc %d",
|
||||
count);
|
||||
/* Storing FLIC_FAILED into the count field here will cause the
|
||||
* target system to fail when attempting to load irqs from the
|
||||
* migration state */
|
||||
qemu_put_be64(f, FLIC_FAILED);
|
||||
} else {
|
||||
qemu_put_be64(f, count);
|
||||
qemu_put_buffer(f, (uint8_t *) buf,
|
||||
count * sizeof(struct kvm_s390_irq));
|
||||
}
|
||||
g_free(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_flic_load - Load pending floating interrupts
|
||||
* @f: QEMUFile containing migration state
|
||||
* @opaque: pointer to flic device state
|
||||
* @version_id: version id for migration
|
||||
*
|
||||
* Returns: value of flic_enqueue_irqs, -EINVAL on error
|
||||
* Note: Do nothing when no interrupts where stored
|
||||
* in QEMUFile
|
||||
*/
|
||||
static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
uint64_t len = 0;
|
||||
uint64_t count = 0;
|
||||
void *buf = NULL;
|
||||
int r = 0;
|
||||
|
||||
if (version_id != FLIC_SAVEVM_VERSION) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flic_enable_pfault((struct KVMS390FLICState *) opaque);
|
||||
|
||||
count = qemu_get_be64(f);
|
||||
len = count * sizeof(struct kvm_s390_irq);
|
||||
if (count == FLIC_FAILED) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (count == 0) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
buf = g_try_malloc0(len);
|
||||
if (!buf) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
|
||||
r = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
|
||||
|
||||
out_free:
|
||||
g_free(buf);
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||
struct kvm_create_device cd = {0};
|
||||
int ret;
|
||||
|
||||
flic_state->fd = -1;
|
||||
if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
|
||||
trace_flic_no_device_api(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
cd.type = KVM_DEV_TYPE_FLIC;
|
||||
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
|
||||
if (ret < 0) {
|
||||
trace_flic_create_device(errno);
|
||||
return;
|
||||
}
|
||||
flic_state->fd = cd.fd;
|
||||
|
||||
/* Register savevm handler for floating interrupts */
|
||||
register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
|
||||
kvm_flic_load, (void *) flic_state);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||
|
||||
unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_reset(DeviceState *dev)
|
||||
{
|
||||
KVMS390FLICState *flic = KVM_S390_FLIC(dev);
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_CLEAR_IRQS,
|
||||
};
|
||||
int rc = 0;
|
||||
|
||||
if (flic->fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
flic_disable_wait_pfault(flic);
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
if (rc) {
|
||||
trace_flic_reset_failed(errno);
|
||||
}
|
||||
|
||||
flic_enable_pfault(flic);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
|
||||
dc->realize = kvm_s390_flic_realize;
|
||||
dc->unrealize = kvm_s390_flic_unrealize;
|
||||
dc->reset = kvm_s390_flic_reset;
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_s390_flic_info = {
|
||||
.name = TYPE_KVM_S390_FLIC,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(KVMS390FLICState),
|
||||
.class_init = kvm_s390_flic_class_init,
|
||||
static const TypeInfo qemu_s390_flic_info = {
|
||||
.name = TYPE_QEMU_S390_FLIC,
|
||||
.parent = TYPE_S390_FLIC_COMMON,
|
||||
.instance_size = sizeof(QEMUS390FLICState),
|
||||
.class_init = qemu_s390_flic_class_init,
|
||||
};
|
||||
|
||||
static void kvm_s390_flic_register_types(void)
|
||||
static const TypeInfo s390_flic_common_info = {
|
||||
.name = TYPE_S390_FLIC_COMMON,
|
||||
.parent = TYPE_SYS_BUS_DEVICE,
|
||||
.instance_size = sizeof(S390FLICState),
|
||||
.class_size = sizeof(S390FLICStateClass),
|
||||
};
|
||||
|
||||
static void qemu_s390_flic_register_types(void)
|
||||
{
|
||||
type_register_static(&kvm_s390_flic_info);
|
||||
type_register_static(&s390_flic_common_info);
|
||||
type_register_static(&qemu_s390_flic_info);
|
||||
}
|
||||
|
||||
type_init(kvm_s390_flic_register_types)
|
||||
type_init(qemu_s390_flic_register_types)
|
||||
|
|
420
hw/intc/s390_flic_kvm.c
Normal file
420
hw/intc/s390_flic_kvm.c
Normal file
|
@ -0,0 +1,420 @@
|
|||
/*
|
||||
* QEMU S390x KVM floating interrupt controller (flic)
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Jens Freimann <jfrei@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||
* your option) any later version. See the COPYING file in the top-level
|
||||
* directory.
|
||||
*/
|
||||
|
||||
#include <sys/ioctl.h>
|
||||
#include "qemu/error-report.h"
|
||||
#include "hw/sysbus.h"
|
||||
#include "sysemu/kvm.h"
|
||||
#include "migration/qemu-file.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
#include "hw/s390x/adapter.h"
|
||||
#include "trace.h"
|
||||
|
||||
#define FLIC_SAVE_INITIAL_SIZE getpagesize()
|
||||
#define FLIC_FAILED (-1UL)
|
||||
#define FLIC_SAVEVM_VERSION 1
|
||||
|
||||
typedef struct KVMS390FLICState {
|
||||
S390FLICState parent_obj;
|
||||
|
||||
uint32_t fd;
|
||||
} KVMS390FLICState;
|
||||
|
||||
DeviceState *s390_flic_kvm_create(void)
|
||||
{
|
||||
DeviceState *dev = NULL;
|
||||
|
||||
if (kvm_enabled()) {
|
||||
dev = qdev_create(NULL, TYPE_KVM_S390_FLIC);
|
||||
object_property_add_child(qdev_get_machine(), TYPE_KVM_S390_FLIC,
|
||||
OBJECT(dev), NULL);
|
||||
}
|
||||
return dev;
|
||||
}
|
||||
|
||||
/**
|
||||
* flic_get_all_irqs - store all pending irqs in buffer
|
||||
* @buf: pointer to buffer which is passed to kernel
|
||||
* @len: length of buffer
|
||||
* @flic: pointer to flic device state
|
||||
*
|
||||
* Returns: -ENOMEM if buffer is too small,
|
||||
* -EINVAL if attr.group is invalid,
|
||||
* -EFAULT if copying to userspace failed,
|
||||
* on success return number of stored interrupts
|
||||
*/
|
||||
static int flic_get_all_irqs(KVMS390FLICState *flic,
|
||||
void *buf, int len)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_GET_ALL_IRQS,
|
||||
.addr = (uint64_t) buf,
|
||||
.attr = len,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_GET_DEVICE_ATTR, &attr);
|
||||
|
||||
return rc == -1 ? -errno : rc;
|
||||
}
|
||||
|
||||
static void flic_enable_pfault(KVMS390FLICState *flic)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_APF_ENABLE,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
if (rc) {
|
||||
fprintf(stderr, "flic: couldn't enable pfault\n");
|
||||
}
|
||||
}
|
||||
|
||||
static void flic_disable_wait_pfault(KVMS390FLICState *flic)
|
||||
{
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_APF_DISABLE_WAIT,
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
if (rc) {
|
||||
fprintf(stderr, "flic: couldn't disable pfault\n");
|
||||
}
|
||||
}
|
||||
|
||||
/** flic_enqueue_irqs - returns 0 on success
|
||||
* @buf: pointer to buffer which is passed to kernel
|
||||
* @len: length of buffer
|
||||
* @flic: pointer to flic device state
|
||||
*
|
||||
* Returns: -EINVAL if attr.group is unknown
|
||||
*/
|
||||
static int flic_enqueue_irqs(void *buf, uint64_t len,
|
||||
KVMS390FLICState *flic)
|
||||
{
|
||||
int rc;
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_ENQUEUE,
|
||||
.addr = (uint64_t) buf,
|
||||
.attr = len,
|
||||
};
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
return rc ? -errno : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* __get_all_irqs - store all pending irqs in buffer
|
||||
* @flic: pointer to flic device state
|
||||
* @buf: pointer to pointer to a buffer
|
||||
* @len: length of buffer
|
||||
*
|
||||
* Returns: return value of flic_get_all_irqs
|
||||
* Note: Retry and increase buffer size until flic_get_all_irqs
|
||||
* either returns a value >= 0 or a negative error code.
|
||||
* -ENOMEM is an exception, which means the buffer is too small
|
||||
* and we should try again. Other negative error codes can be
|
||||
* -EFAULT and -EINVAL which we ignore at this point
|
||||
*/
|
||||
static int __get_all_irqs(KVMS390FLICState *flic,
|
||||
void **buf, int len)
|
||||
{
|
||||
int r;
|
||||
|
||||
do {
|
||||
/* returns -ENOMEM if buffer is too small and number
|
||||
* of queued interrupts on success */
|
||||
r = flic_get_all_irqs(flic, *buf, len);
|
||||
if (r >= 0) {
|
||||
break;
|
||||
}
|
||||
len *= 2;
|
||||
*buf = g_try_realloc(*buf, len);
|
||||
if (!buf) {
|
||||
return -ENOMEM;
|
||||
}
|
||||
} while (r == -ENOMEM && len <= KVM_S390_FLIC_MAX_BUFFER);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static int kvm_s390_register_io_adapter(S390FLICState *fs, uint32_t id,
|
||||
uint8_t isc, bool swap,
|
||||
bool is_maskable)
|
||||
{
|
||||
struct kvm_s390_io_adapter adapter = {
|
||||
.id = id,
|
||||
.isc = isc,
|
||||
.maskable = is_maskable,
|
||||
.swap = swap,
|
||||
};
|
||||
KVMS390FLICState *flic = KVM_S390_FLIC(fs);
|
||||
int r, ret;
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_ADAPTER_REGISTER,
|
||||
.addr = (uint64_t)&adapter,
|
||||
};
|
||||
|
||||
if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
|
||||
ret = r ? -errno : 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int kvm_s390_io_adapter_map(S390FLICState *fs, uint32_t id,
|
||||
uint64_t map_addr, bool do_map)
|
||||
{
|
||||
struct kvm_s390_io_adapter_req req = {
|
||||
.id = id,
|
||||
.type = do_map ? KVM_S390_IO_ADAPTER_MAP : KVM_S390_IO_ADAPTER_UNMAP,
|
||||
.addr = map_addr,
|
||||
};
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_ADAPTER_MODIFY,
|
||||
.addr = (uint64_t)&req,
|
||||
};
|
||||
KVMS390FLICState *flic = KVM_S390_FLIC(fs);
|
||||
int r;
|
||||
|
||||
if (!kvm_check_extension(kvm_state, KVM_CAP_IRQ_ROUTING)) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
r = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
return r ? -errno : 0;
|
||||
}
|
||||
|
||||
static int kvm_s390_add_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
int ret, i;
|
||||
uint64_t ind_offset = routes->adapter.ind_offset;
|
||||
|
||||
for (i = 0; i < routes->num_routes; i++) {
|
||||
ret = kvm_irqchip_add_adapter_route(kvm_state, &routes->adapter);
|
||||
if (ret < 0) {
|
||||
goto out_undo;
|
||||
}
|
||||
routes->gsi[i] = ret;
|
||||
routes->adapter.ind_offset++;
|
||||
}
|
||||
/* Restore passed-in structure to original state. */
|
||||
routes->adapter.ind_offset = ind_offset;
|
||||
return 0;
|
||||
out_undo:
|
||||
while (--i >= 0) {
|
||||
kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
|
||||
routes->gsi[i] = -1;
|
||||
}
|
||||
routes->adapter.ind_offset = ind_offset;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void kvm_s390_release_adapter_routes(S390FLICState *fs,
|
||||
AdapterRoutes *routes)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < routes->num_routes; i++) {
|
||||
if (routes->gsi[i] >= 0) {
|
||||
kvm_irqchip_release_virq(kvm_state, routes->gsi[i]);
|
||||
routes->gsi[i] = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_flic_save - Save pending floating interrupts
|
||||
* @f: QEMUFile containing migration state
|
||||
* @opaque: pointer to flic device state
|
||||
*
|
||||
* Note: Pass buf and len to kernel. Start with one page and
|
||||
* increase until buffer is sufficient or maxium size is
|
||||
* reached
|
||||
*/
|
||||
static void kvm_flic_save(QEMUFile *f, void *opaque)
|
||||
{
|
||||
KVMS390FLICState *flic = opaque;
|
||||
int len = FLIC_SAVE_INITIAL_SIZE;
|
||||
void *buf;
|
||||
int count;
|
||||
|
||||
flic_disable_wait_pfault((struct KVMS390FLICState *) opaque);
|
||||
|
||||
buf = g_try_malloc0(len);
|
||||
if (!buf) {
|
||||
/* Storing FLIC_FAILED into the count field here will cause the
|
||||
* target system to fail when attempting to load irqs from the
|
||||
* migration state */
|
||||
error_report("flic: couldn't allocate memory");
|
||||
qemu_put_be64(f, FLIC_FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
count = __get_all_irqs(flic, &buf, len);
|
||||
if (count < 0) {
|
||||
error_report("flic: couldn't retrieve irqs from kernel, rc %d",
|
||||
count);
|
||||
/* Storing FLIC_FAILED into the count field here will cause the
|
||||
* target system to fail when attempting to load irqs from the
|
||||
* migration state */
|
||||
qemu_put_be64(f, FLIC_FAILED);
|
||||
} else {
|
||||
qemu_put_be64(f, count);
|
||||
qemu_put_buffer(f, (uint8_t *) buf,
|
||||
count * sizeof(struct kvm_s390_irq));
|
||||
}
|
||||
g_free(buf);
|
||||
}
|
||||
|
||||
/**
|
||||
* kvm_flic_load - Load pending floating interrupts
|
||||
* @f: QEMUFile containing migration state
|
||||
* @opaque: pointer to flic device state
|
||||
* @version_id: version id for migration
|
||||
*
|
||||
* Returns: value of flic_enqueue_irqs, -EINVAL on error
|
||||
* Note: Do nothing when no interrupts where stored
|
||||
* in QEMUFile
|
||||
*/
|
||||
static int kvm_flic_load(QEMUFile *f, void *opaque, int version_id)
|
||||
{
|
||||
uint64_t len = 0;
|
||||
uint64_t count = 0;
|
||||
void *buf = NULL;
|
||||
int r = 0;
|
||||
|
||||
if (version_id != FLIC_SAVEVM_VERSION) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flic_enable_pfault((struct KVMS390FLICState *) opaque);
|
||||
|
||||
count = qemu_get_be64(f);
|
||||
len = count * sizeof(struct kvm_s390_irq);
|
||||
if (count == FLIC_FAILED) {
|
||||
r = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
if (count == 0) {
|
||||
r = 0;
|
||||
goto out;
|
||||
}
|
||||
buf = g_try_malloc0(len);
|
||||
if (!buf) {
|
||||
r = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (qemu_get_buffer(f, (uint8_t *) buf, len) != len) {
|
||||
r = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
r = flic_enqueue_irqs(buf, len, (struct KVMS390FLICState *) opaque);
|
||||
|
||||
out_free:
|
||||
g_free(buf);
|
||||
out:
|
||||
return r;
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_realize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||
struct kvm_create_device cd = {0};
|
||||
int ret;
|
||||
|
||||
flic_state->fd = -1;
|
||||
if (!kvm_check_extension(kvm_state, KVM_CAP_DEVICE_CTRL)) {
|
||||
trace_flic_no_device_api(errno);
|
||||
return;
|
||||
}
|
||||
|
||||
cd.type = KVM_DEV_TYPE_FLIC;
|
||||
ret = kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd);
|
||||
if (ret < 0) {
|
||||
trace_flic_create_device(errno);
|
||||
return;
|
||||
}
|
||||
flic_state->fd = cd.fd;
|
||||
|
||||
/* Register savevm handler for floating interrupts */
|
||||
register_savevm(NULL, "s390-flic", 0, 1, kvm_flic_save,
|
||||
kvm_flic_load, (void *) flic_state);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_unrealize(DeviceState *dev, Error **errp)
|
||||
{
|
||||
KVMS390FLICState *flic_state = KVM_S390_FLIC(dev);
|
||||
|
||||
unregister_savevm(DEVICE(flic_state), "s390-flic", flic_state);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_reset(DeviceState *dev)
|
||||
{
|
||||
KVMS390FLICState *flic = KVM_S390_FLIC(dev);
|
||||
struct kvm_device_attr attr = {
|
||||
.group = KVM_DEV_FLIC_CLEAR_IRQS,
|
||||
};
|
||||
int rc = 0;
|
||||
|
||||
if (flic->fd == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
flic_disable_wait_pfault(flic);
|
||||
|
||||
rc = ioctl(flic->fd, KVM_SET_DEVICE_ATTR, &attr);
|
||||
if (rc) {
|
||||
trace_flic_reset_failed(errno);
|
||||
}
|
||||
|
||||
flic_enable_pfault(flic);
|
||||
}
|
||||
|
||||
static void kvm_s390_flic_class_init(ObjectClass *oc, void *data)
|
||||
{
|
||||
DeviceClass *dc = DEVICE_CLASS(oc);
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc);
|
||||
|
||||
dc->realize = kvm_s390_flic_realize;
|
||||
dc->unrealize = kvm_s390_flic_unrealize;
|
||||
dc->reset = kvm_s390_flic_reset;
|
||||
fsc->register_io_adapter = kvm_s390_register_io_adapter;
|
||||
fsc->io_adapter_map = kvm_s390_io_adapter_map;
|
||||
fsc->add_adapter_routes = kvm_s390_add_adapter_routes;
|
||||
fsc->release_adapter_routes = kvm_s390_release_adapter_routes;
|
||||
}
|
||||
|
||||
static const TypeInfo kvm_s390_flic_info = {
|
||||
.name = TYPE_KVM_S390_FLIC,
|
||||
.parent = TYPE_S390_FLIC_COMMON,
|
||||
.instance_size = sizeof(KVMS390FLICState),
|
||||
.class_init = kvm_s390_flic_class_init,
|
||||
};
|
||||
|
||||
static void kvm_s390_flic_register_types(void)
|
||||
{
|
||||
type_register_static(&kvm_s390_flic_info);
|
||||
}
|
||||
|
||||
type_init(kvm_s390_flic_register_types)
|
|
@ -16,6 +16,7 @@
|
|||
#include "ioinst.h"
|
||||
#include "css.h"
|
||||
#include "trace.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
|
||||
typedef struct CrwContainer {
|
||||
CRW crw;
|
||||
|
@ -39,6 +40,13 @@ typedef struct CssImage {
|
|||
ChpInfo chpids[MAX_CHPID + 1];
|
||||
} CssImage;
|
||||
|
||||
typedef struct IoAdapter {
|
||||
uint32_t id;
|
||||
uint8_t type;
|
||||
uint8_t isc;
|
||||
QTAILQ_ENTRY(IoAdapter) sibling;
|
||||
} IoAdapter;
|
||||
|
||||
typedef struct ChannelSubSys {
|
||||
QTAILQ_HEAD(, CrwContainer) pending_crws;
|
||||
bool do_crw_mchk;
|
||||
|
@ -49,6 +57,7 @@ typedef struct ChannelSubSys {
|
|||
uint64_t chnmon_area;
|
||||
CssImage *css[MAX_CSSID + 1];
|
||||
uint8_t default_cssid;
|
||||
QTAILQ_HEAD(, IoAdapter) io_adapters;
|
||||
} ChannelSubSys;
|
||||
|
||||
static ChannelSubSys *channel_subsys;
|
||||
|
@ -69,6 +78,46 @@ int css_create_css_image(uint8_t cssid, bool default_image)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
|
||||
bool maskable, uint32_t *id)
|
||||
{
|
||||
IoAdapter *adapter;
|
||||
bool found = false;
|
||||
int ret;
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
*id = 0;
|
||||
QTAILQ_FOREACH(adapter, &channel_subsys->io_adapters, sibling) {
|
||||
if ((adapter->type == type) && (adapter->isc == isc)) {
|
||||
*id = adapter->id;
|
||||
found = true;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
if (adapter->id >= *id) {
|
||||
*id = adapter->id + 1;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
goto out;
|
||||
}
|
||||
adapter = g_new0(IoAdapter, 1);
|
||||
ret = fsc->register_io_adapter(fs, *id, isc, swap, maskable);
|
||||
if (ret == 0) {
|
||||
adapter->id = *id;
|
||||
adapter->isc = isc;
|
||||
adapter->type = type;
|
||||
QTAILQ_INSERT_TAIL(&channel_subsys->io_adapters, adapter, sibling);
|
||||
} else {
|
||||
g_free(adapter);
|
||||
fprintf(stderr, "Unexpected error %d when registering adapter %d\n",
|
||||
ret, *id);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint16_t css_build_subchannel_id(SubchDev *sch)
|
||||
{
|
||||
if (channel_subsys->max_cssid > 0) {
|
||||
|
@ -1235,6 +1284,7 @@ static void css_init(void)
|
|||
channel_subsys->do_crw_mchk = true;
|
||||
channel_subsys->crws_lost = false;
|
||||
channel_subsys->chnmon_active = false;
|
||||
QTAILQ_INIT(&channel_subsys->io_adapters);
|
||||
}
|
||||
machine_init(css_init);
|
||||
|
||||
|
|
|
@ -98,4 +98,8 @@ void css_generate_sch_crws(uint8_t cssid, uint8_t ssid, uint16_t schid,
|
|||
int hotplugged, int add);
|
||||
void css_generate_chp_crws(uint8_t cssid, uint8_t chpid);
|
||||
void css_adapter_interrupt(uint8_t isc);
|
||||
|
||||
#define CSS_IO_ADAPTER_VIRTIO 1
|
||||
int css_register_io_adapter(uint8_t type, uint8_t isc, bool swap,
|
||||
bool maskable, uint32_t *id);
|
||||
#endif
|
||||
|
|
|
@ -21,12 +21,77 @@
|
|||
#include "hw/sysbus.h"
|
||||
#include "qemu/bitops.h"
|
||||
#include "hw/virtio/virtio-bus.h"
|
||||
#include "hw/s390x/adapter.h"
|
||||
#include "hw/s390x/s390_flic.h"
|
||||
|
||||
#include "ioinst.h"
|
||||
#include "css.h"
|
||||
#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 int s390_io_adapter_map(AdapterInfo *adapter, uint64_t map_addr,
|
||||
bool do_map)
|
||||
{
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
return fsc->io_adapter_map(fs, adapter->adapter_id, map_addr, do_map);
|
||||
}
|
||||
|
||||
static void release_indicator(AdapterInfo *adapter, IndAddr *indicator)
|
||||
{
|
||||
assert(indicator->refcnt > 0);
|
||||
indicator->refcnt--;
|
||||
if (indicator->refcnt > 0) {
|
||||
return;
|
||||
}
|
||||
QTAILQ_REMOVE(&indicator_addresses, indicator, sibling);
|
||||
if (indicator->map) {
|
||||
s390_io_adapter_map(adapter, indicator->map, false);
|
||||
}
|
||||
g_free(indicator);
|
||||
}
|
||||
|
||||
static int map_indicator(AdapterInfo *adapter, IndAddr *indicator)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (indicator->map) {
|
||||
return 0; /* already mapped is not an error */
|
||||
}
|
||||
indicator->map = indicator->addr;
|
||||
ret = s390_io_adapter_map(adapter, indicator->map, true);
|
||||
if ((ret != 0) && (ret != -ENOSYS)) {
|
||||
goto out_err;
|
||||
}
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
indicator->map = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size,
|
||||
VirtioCcwDevice *dev);
|
||||
|
||||
|
@ -445,7 +510,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 +530,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,13 +582,20 @@ 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;
|
||||
dev->routes.adapter.ind_offset = thinint->ind_bit;
|
||||
dev->routes.adapter.summary_offset = 7;
|
||||
cpu_physical_memory_unmap(thinint, hw_len, 0, hw_len);
|
||||
sch->thinint_active = ((dev->indicators != 0) &&
|
||||
(dev->summary_indicator != 0));
|
||||
ret = css_register_io_adapter(CSS_IO_ADAPTER_VIRTIO,
|
||||
dev->thinint_isc, true, false,
|
||||
&dev->routes.adapter.adapter_id);
|
||||
assert(ret == 0);
|
||||
sch->thinint_active = ((dev->indicators != NULL) &&
|
||||
(dev->summary_indicator != NULL));
|
||||
sch->curr_status.scsw.count = ccw.count - len;
|
||||
ret = 0;
|
||||
}
|
||||
|
@ -554,7 +626,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;
|
||||
|
@ -693,7 +765,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->routes.adapter, dev->indicators);
|
||||
dev->indicators = NULL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -950,17 +1025,19 @@ 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 +
|
||||
(dev->ind_bit + vector) / 8,
|
||||
0x80 >> ((dev->ind_bit + vector) % 8));
|
||||
if (!virtio_set_ind_atomic(sch, dev->summary_indicator,
|
||||
uint64_t ind_bit = dev->routes.adapter.ind_offset;
|
||||
|
||||
virtio_set_ind_atomic(sch, dev->indicators->addr +
|
||||
(ind_bit + vector) / 8,
|
||||
0x80 >> ((ind_bit + vector) % 8));
|
||||
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 {
|
||||
|
@ -968,9 +1045,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);
|
||||
}
|
||||
}
|
||||
|
@ -991,9 +1068,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->routes.adapter, dev->indicators);
|
||||
dev->indicators = NULL;
|
||||
}
|
||||
if (dev->indicators2) {
|
||||
release_indicator(&dev->routes.adapter, dev->indicators2);
|
||||
dev->indicators2 = NULL;
|
||||
}
|
||||
if (dev->summary_indicator) {
|
||||
release_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||
dev->summary_indicator = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void virtio_ccw_vmstate_change(DeviceState *d, bool running)
|
||||
|
@ -1027,6 +1113,79 @@ static int virtio_ccw_set_host_notifier(DeviceState *d, int n, bool assign)
|
|||
return virtio_ccw_set_guest2host_notifier(dev, n, assign, false);
|
||||
}
|
||||
|
||||
static int virtio_ccw_get_mappings(VirtioCcwDevice *dev)
|
||||
{
|
||||
int r;
|
||||
|
||||
if (!dev->sch->thinint_active) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
r = map_indicator(&dev->routes.adapter, dev->summary_indicator);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
r = map_indicator(&dev->routes.adapter, dev->indicators);
|
||||
if (r) {
|
||||
return r;
|
||||
}
|
||||
dev->routes.adapter.summary_addr = dev->summary_indicator->map;
|
||||
dev->routes.adapter.ind_addr = dev->indicators->map;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int virtio_ccw_setup_irqroutes(VirtioCcwDevice *dev, int nvqs)
|
||||
{
|
||||
int i;
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
int ret;
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
ret = virtio_ccw_get_mappings(dev);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
for (i = 0; i < nvqs; i++) {
|
||||
if (!virtio_queue_get_num(vdev, i)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dev->routes.num_routes = i;
|
||||
return fsc->add_adapter_routes(fs, &dev->routes);
|
||||
}
|
||||
|
||||
static void virtio_ccw_release_irqroutes(VirtioCcwDevice *dev, int nvqs)
|
||||
{
|
||||
S390FLICState *fs = s390_get_flic();
|
||||
S390FLICStateClass *fsc = S390_FLIC_COMMON_GET_CLASS(fs);
|
||||
|
||||
fsc->release_adapter_routes(fs, &dev->routes);
|
||||
}
|
||||
|
||||
static int virtio_ccw_add_irqfd(VirtioCcwDevice *dev, int n)
|
||||
{
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||
|
||||
return kvm_irqchip_add_irqfd_notifier(kvm_state, notifier, NULL,
|
||||
dev->routes.gsi[n]);
|
||||
}
|
||||
|
||||
static void virtio_ccw_remove_irqfd(VirtioCcwDevice *dev, int n)
|
||||
{
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
VirtQueue *vq = virtio_get_queue(vdev, n);
|
||||
EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
|
||||
int ret;
|
||||
|
||||
ret = kvm_irqchip_remove_irqfd_notifier(kvm_state, notifier,
|
||||
dev->routes.gsi[n]);
|
||||
assert(ret == 0);
|
||||
}
|
||||
|
||||
static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
||||
bool assign, bool with_irqfd)
|
||||
{
|
||||
|
@ -1042,11 +1201,17 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
|||
return r;
|
||||
}
|
||||
virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
|
||||
/* We do not support irqfd for classic I/O interrupts, because the
|
||||
* classic interrupts are intermixed with the subchannel status, that
|
||||
* is queried with test subchannel. We want to use vhost, though.
|
||||
* Lets make sure to have vhost running and wire up the irq fd to
|
||||
* land in qemu (and only the irq fd) in this code.
|
||||
if (with_irqfd) {
|
||||
r = virtio_ccw_add_irqfd(dev, n);
|
||||
if (r) {
|
||||
virtio_queue_set_guest_notifier_fd_handler(vq, false,
|
||||
with_irqfd);
|
||||
return r;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* We do not support individual masking for channel devices, so we
|
||||
* need to manually trigger any guest masking callbacks here.
|
||||
*/
|
||||
if (k->guest_notifier_mask) {
|
||||
k->guest_notifier_mask(vdev, n, false);
|
||||
|
@ -1060,6 +1225,9 @@ static int virtio_ccw_set_guest_notifier(VirtioCcwDevice *dev, int n,
|
|||
if (k->guest_notifier_mask) {
|
||||
k->guest_notifier_mask(vdev, n, true);
|
||||
}
|
||||
if (with_irqfd) {
|
||||
virtio_ccw_remove_irqfd(dev, n);
|
||||
}
|
||||
virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
|
||||
event_notifier_cleanup(notifier);
|
||||
}
|
||||
|
@ -1071,24 +1239,39 @@ static int virtio_ccw_set_guest_notifiers(DeviceState *d, int nvqs,
|
|||
{
|
||||
VirtioCcwDevice *dev = VIRTIO_CCW_DEVICE(d);
|
||||
VirtIODevice *vdev = virtio_bus_get_device(&dev->bus);
|
||||
bool with_irqfd = dev->sch->thinint_active && kvm_irqfds_enabled();
|
||||
int r, n;
|
||||
|
||||
if (with_irqfd && assigned) {
|
||||
/* irq routes need to be set up before assigning irqfds */
|
||||
r = virtio_ccw_setup_irqroutes(dev, nvqs);
|
||||
if (r < 0) {
|
||||
goto irqroute_error;
|
||||
}
|
||||
}
|
||||
for (n = 0; n < nvqs; n++) {
|
||||
if (!virtio_queue_get_num(vdev, n)) {
|
||||
break;
|
||||
}
|
||||
/* false -> true, as soon as irqfd works */
|
||||
r = virtio_ccw_set_guest_notifier(dev, n, assigned, false);
|
||||
r = virtio_ccw_set_guest_notifier(dev, n, assigned, with_irqfd);
|
||||
if (r < 0) {
|
||||
goto assign_error;
|
||||
}
|
||||
}
|
||||
if (with_irqfd && !assigned) {
|
||||
/* release irq routes after irqfds have been released */
|
||||
virtio_ccw_release_irqroutes(dev, nvqs);
|
||||
}
|
||||
return 0;
|
||||
|
||||
assign_error:
|
||||
while (--n >= 0) {
|
||||
virtio_ccw_set_guest_notifier(dev, n, !assigned, false);
|
||||
}
|
||||
irqroute_error:
|
||||
if (with_irqfd && assigned) {
|
||||
virtio_ccw_release_irqroutes(dev, nvqs);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include <hw/virtio/virtio-balloon.h>
|
||||
#include <hw/virtio/virtio-rng.h>
|
||||
#include <hw/virtio/virtio-bus.h>
|
||||
#include <hw/s390x/s390_flic.h>
|
||||
|
||||
#define VIRTUAL_CSSID 0xfe
|
||||
|
||||
|
@ -75,6 +76,14 @@ 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;
|
||||
uint64_t map;
|
||||
unsigned long refcnt;
|
||||
int len;
|
||||
QTAILQ_ENTRY(IndAddr) sibling;
|
||||
} IndAddr;
|
||||
|
||||
struct VirtioCcwDevice {
|
||||
DeviceState parent_obj;
|
||||
SubchDev *sch;
|
||||
|
@ -85,10 +94,11 @@ struct VirtioCcwDevice {
|
|||
bool ioeventfd_disabled;
|
||||
uint32_t flags;
|
||||
uint8_t thinint_isc;
|
||||
AdapterRoutes routes;
|
||||
/* Guest provided values: */
|
||||
hwaddr indicators;
|
||||
hwaddr indicators2;
|
||||
hwaddr summary_indicator;
|
||||
IndAddr *indicators;
|
||||
IndAddr *indicators2;
|
||||
IndAddr *summary_indicator;
|
||||
uint64_t ind_bit;
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue