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:
Peter Maydell 2014-05-22 16:14:01 +01:00
commit 45e66b7beb
19 changed files with 1115 additions and 336 deletions

View file

@ -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

View file

@ -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
View 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)

View file

@ -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);

View file

@ -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

View file

@ -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;
}

View file

@ -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;
};