Commit db1c875e authored by Matthew Rosato's avatar Matthew Rosato Committed by Christian Borntraeger
Browse files

KVM: s390: add KVM_S390_ZPCI_OP to manage guest zPCI devices



The KVM_S390_ZPCI_OP ioctl provides a mechanism for managing
hardware-assisted virtualization features for s390x zPCI passthrough.
Add the first 2 operations, which can be used to enable/disable
the specified device for Adapter Event Notification interpretation.

Signed-off-by: default avatarMatthew Rosato <mjrosato@linux.ibm.com>
Acked-by: default avatarPierre Morel <pmorel@linux.ibm.com>
Reviewed-by: default avatarThomas Huth <thuth@redhat.com>
Link: https://lore.kernel.org/r/20220606203325.110625-21-mjrosato@linux.ibm.com


Signed-off-by: default avatarChristian Borntraeger <borntraeger@linux.ibm.com>
parent ba6090ff
Loading
Loading
Loading
Loading
+47 −0
Original line number Diff line number Diff line
@@ -5802,6 +5802,53 @@ of CPUID leaf 0xD on the host.

This ioctl injects an event channel interrupt directly to the guest vCPU.

4.137 KVM_S390_ZPCI_OP
--------------------

:Capability: KVM_CAP_S390_ZPCI_OP
:Architectures: s390
:Type: vm ioctl
:Parameters: struct kvm_s390_zpci_op (in)
:Returns: 0 on success, <0 on error

Used to manage hardware-assisted virtualization features for zPCI devices.

Parameters are specified via the following structure::

  struct kvm_s390_zpci_op {
	/* in */
	__u32 fh;		/* target device */
	__u8  op;		/* operation to perform */
	__u8  pad[3];
	union {
		/* for KVM_S390_ZPCIOP_REG_AEN */
		struct {
			__u64 ibv;	/* Guest addr of interrupt bit vector */
			__u64 sb;	/* Guest addr of summary bit */
			__u32 flags;
			__u32 noi;	/* Number of interrupts */
			__u8 isc;	/* Guest interrupt subclass */
			__u8 sbo;	/* Offset of guest summary bit vector */
			__u16 pad;
		} reg_aen;
		__u64 reserved[8];
	} u;
  };

The type of operation is specified in the "op" field.
KVM_S390_ZPCIOP_REG_AEN is used to register the VM for adapter event
notification interpretation, which will allow firmware delivery of adapter
events directly to the vm, with KVM providing a backup delivery mechanism;
KVM_S390_ZPCIOP_DEREG_AEN is used to subsequently disable interpretation of
adapter event notifications.

The target zPCI function must also be specified via the "fh" field.  For the
KVM_S390_ZPCIOP_REG_AEN operation, additional information to establish firmware
delivery must be provided via the "reg_aen" struct.

The "pad" and "reserved" fields may be used for future extensions and should be
set to 0s by userspace.

5. The kvm_run structure
========================

+16 −0
Original line number Diff line number Diff line
@@ -618,6 +618,9 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
	case KVM_CAP_S390_PROTECTED:
		r = is_prot_virt_host();
		break;
	case KVM_CAP_S390_ZPCI_OP:
		r = kvm_s390_pci_interp_allowed();
		break;
	default:
		r = 0;
	}
@@ -2629,6 +2632,19 @@ long kvm_arch_vm_ioctl(struct file *filp,
			r = -EFAULT;
		break;
	}
	case KVM_S390_ZPCI_OP: {
		struct kvm_s390_zpci_op args;

		r = -EINVAL;
		if (!IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM))
			break;
		if (copy_from_user(&args, argp, sizeof(args))) {
			r = -EFAULT;
			break;
		}
		r = kvm_s390_pci_zpci_op(kvm, &args);
		break;
	}
	default:
		r = -ENOTTY;
	}
+85 −0
Original line number Diff line number Diff line
@@ -585,6 +585,91 @@ void kvm_s390_pci_clear_list(struct kvm *kvm)
	spin_unlock(&kvm->arch.kzdev_list_lock);
}

static struct zpci_dev *get_zdev_from_kvm_by_fh(struct kvm *kvm, u32 fh)
{
	struct zpci_dev *zdev = NULL;
	struct kvm_zdev *kzdev;

	spin_lock(&kvm->arch.kzdev_list_lock);
	list_for_each_entry(kzdev, &kvm->arch.kzdev_list, entry) {
		if (kzdev->zdev->fh == fh) {
			zdev = kzdev->zdev;
			break;
		}
	}
	spin_unlock(&kvm->arch.kzdev_list_lock);

	return zdev;
}

static int kvm_s390_pci_zpci_reg_aen(struct zpci_dev *zdev,
				     struct kvm_s390_zpci_op *args)
{
	struct zpci_fib fib = {};
	bool hostflag;

	fib.fmt0.aibv = args->u.reg_aen.ibv;
	fib.fmt0.isc = args->u.reg_aen.isc;
	fib.fmt0.noi = args->u.reg_aen.noi;
	if (args->u.reg_aen.sb != 0) {
		fib.fmt0.aisb = args->u.reg_aen.sb;
		fib.fmt0.aisbo = args->u.reg_aen.sbo;
		fib.fmt0.sum = 1;
	} else {
		fib.fmt0.aisb = 0;
		fib.fmt0.aisbo = 0;
		fib.fmt0.sum = 0;
	}

	hostflag = !(args->u.reg_aen.flags & KVM_S390_ZPCIOP_REGAEN_HOST);
	return kvm_s390_pci_aif_enable(zdev, &fib, hostflag);
}

int kvm_s390_pci_zpci_op(struct kvm *kvm, struct kvm_s390_zpci_op *args)
{
	struct kvm_zdev *kzdev;
	struct zpci_dev *zdev;
	int r;

	zdev = get_zdev_from_kvm_by_fh(kvm, args->fh);
	if (!zdev)
		return -ENODEV;

	mutex_lock(&zdev->kzdev_lock);
	mutex_lock(&kvm->lock);

	kzdev = zdev->kzdev;
	if (!kzdev) {
		r = -ENODEV;
		goto out;
	}
	if (kzdev->kvm != kvm) {
		r = -EPERM;
		goto out;
	}

	switch (args->op) {
	case KVM_S390_ZPCIOP_REG_AEN:
		/* Fail on unknown flags */
		if (args->u.reg_aen.flags & ~KVM_S390_ZPCIOP_REGAEN_HOST) {
			r = -EINVAL;
			break;
		}
		r = kvm_s390_pci_zpci_reg_aen(zdev, args);
		break;
	case KVM_S390_ZPCIOP_DEREG_AEN:
		r = kvm_s390_pci_aif_disable(zdev, false);
		break;
	default:
		r = -EINVAL;
	}

out:
	mutex_unlock(&kvm->lock);
	mutex_unlock(&zdev->kzdev_lock);
	return r;
}

int kvm_s390_pci_init(void)
{
	aift = kzalloc(sizeof(struct zpci_aift), GFP_KERNEL);
+2 −0
Original line number Diff line number Diff line
@@ -59,6 +59,8 @@ void kvm_s390_pci_aen_exit(void);
void kvm_s390_pci_init_list(struct kvm *kvm);
void kvm_s390_pci_clear_list(struct kvm *kvm);

int kvm_s390_pci_zpci_op(struct kvm *kvm, struct kvm_s390_zpci_op *args);

int kvm_s390_pci_init(void);
void kvm_s390_pci_exit(void);

+31 −0
Original line number Diff line number Diff line
@@ -1157,6 +1157,7 @@ struct kvm_ppc_resize_hpt {
#define KVM_CAP_VM_TSC_CONTROL 214
#define KVM_CAP_SYSTEM_EVENT_DATA 215
#define KVM_CAP_ARM_SYSTEM_SUSPEND 216
#define KVM_CAP_S390_ZPCI_OP 221

#ifdef KVM_CAP_IRQ_ROUTING

@@ -2118,4 +2119,34 @@ struct kvm_stats_desc {
/* Available with KVM_CAP_XSAVE2 */
#define KVM_GET_XSAVE2		  _IOR(KVMIO,  0xcf, struct kvm_xsave)

/* Available with KVM_CAP_S390_ZPCI_OP */
#define KVM_S390_ZPCI_OP         _IOW(KVMIO,  0xd1, struct kvm_s390_zpci_op)

struct kvm_s390_zpci_op {
	/* in */
	__u32 fh;               /* target device */
	__u8  op;               /* operation to perform */
	__u8  pad[3];
	union {
		/* for KVM_S390_ZPCIOP_REG_AEN */
		struct {
			__u64 ibv;      /* Guest addr of interrupt bit vector */
			__u64 sb;       /* Guest addr of summary bit */
			__u32 flags;
			__u32 noi;      /* Number of interrupts */
			__u8 isc;       /* Guest interrupt subclass */
			__u8 sbo;       /* Offset of guest summary bit vector */
			__u16 pad;
		} reg_aen;
		__u64 reserved[8];
	} u;
};

/* types for kvm_s390_zpci_op->op */
#define KVM_S390_ZPCIOP_REG_AEN                0
#define KVM_S390_ZPCIOP_DEREG_AEN      1

/* flags for kvm_s390_zpci_op->u.reg_aen.flags */
#define KVM_S390_ZPCIOP_REGAEN_HOST    (1 << 0)

#endif /* __LINUX_KVM_H */