KVM: s390: New features for 7.2

New features for 7.2 for KVM/s390:
 * KVM_PRE_FAULT_MEMORY support
 * Support for 2G hugepages
 * Support for the ASTFLEIE 2 facility
 * kvm_arch_set_irq_inatomic Fast Inject
 * Fix potential leak of uninitialized bytes
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEoWuZBM6M3lCBSfTnuARItAMU6BMFAmov7sIACgkQuARItAMU
 6BNlChAArkYNfy+aHUKDV+fTbYobSaCLPTTEXtDwkykPIC4CQa55kc/rcXWPxtw8
 C9YupDZ/LFsfbP/bUKXssPcU5fOt2K4BMJOiB/3J4MrfCdEM50KEOGb20f7TRf2Y
 CYVOFEjHum2nA3N7p13VLbUGj+yCzisMCejeVXDQY5nStn+4pcE1S1CZKuZ2k7Vz
 a72CGg6CUs5Ee4yRlsqoUzpqtEIGbOWVbJKVjf7pRSqBgYQQrrpJ0ImleujrAsSA
 eKWnAKUJ/JxpnRRZtudl663v+YXZs2q4cHsKACZ0eugSQIKEU1RYaobz6/e2D7g6
 AOxKurd0YPCrtEulZzwQT+uY3KJXVt8J6KBHEeJiTLeG2Dmm7/dL6t4Y9CNqpGK/
 nx/U05tGqcJ2cvWGAB3NXVj3n1ocrgy9+OUCbLw9YaxeqEAdPKTeRqE4nl6+mla6
 F0t1dEEqJNbm44rcH9sHLzJiOZh4ziKnLT0VwJFGwUDMcNsl+uNqZtl4tjWfD3+t
 15nwUoOiVWPGKqSnXVl8QRCQvAmQ3wK7ThIR2MOCYM4y2sfdF+c5QjX1uznQHc5j
 1o4oSjN1hDNxvjMzGOM2Xn3swtCPhCAzQZCr3JtZkB5je5jtV047uRJTYjqiXgKD
 FnQ7VQzDFTEDhhLWgJJNgjUrKScKfMSrQpapgu0LWSjqisjToHg=
 =GOQC
 -----END PGP SIGNATURE-----

Merge tag 'kvm-s390-next-7.2-1' of https://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD

KVM: s390: New features for 7.2

New features for 7.2 for KVM/s390:
* KVM_PRE_FAULT_MEMORY support
* Support for 2G hugepages
* Support for the ASTFLEIE 2 facility
* kvm_arch_set_irq_inatomic Fast Inject
* Fix potential leak of uninitialized bytes
This commit is contained in:
Paolo Bonzini 2026-06-15 15:36:43 +02:00
commit 4dffb0a5d1
17 changed files with 729 additions and 181 deletions

View File

@ -6483,7 +6483,8 @@ Errors:
========== ===============================================================
EINVAL The specified `gpa` and `size` were invalid (e.g. not
page aligned, causes an overflow, or size is zero).
page aligned, causes an overflow, or size is zero), or the VM
is UCONTROL (s390).
ENOENT The specified `gpa` is outside defined memslots.
EINTR An unmasked signal is pending and no page was processed.
EFAULT The parameter address was invalid.
@ -6506,7 +6507,7 @@ Errors:
KVM_PRE_FAULT_MEMORY populates KVM's stage-2 page tables used to map memory
for the current vCPU state. KVM maps memory as if the vCPU generated a
stage-2 read page fault, e.g. faults in memory as needed, but doesn't break
CoW. However, KVM does not mark any newly created stage-2 PTE as Accessed.
CoW. On x86, KVM does not mark any newly created stage-2 PTE as Accessed.
In the case of confidential VM types where there is an initial set up of
private guest memory before the guest is 'finalized'/measured, this ioctl
@ -8933,6 +8934,21 @@ helpful if user space wants to emulate instructions which are not
This capability can be enabled dynamically even if VCPUs were already
created and are running.
7.47 KVM_CAP_S390_HPAGE_2G
--------------------------
:Architectures: s390
:Parameters: none
:Returns: 0 on success; -EINVAL if hpage_2g module parameter was not set,
cmma is enabled, or the VM has the KVM_VM_S390_UCONTROL
flag set; -EBUSY if vCPUs were already created for the VM.
With this capability the KVM support for memory backing with 2g pages
through hugetlbfs can be enabled for a VM. After the capability is
enabled, cmma can't be enabled anymore and pfmfi and the storage key
interpretation are disabled. If cmma has already been enabled or the
hpage_2g module parameter is not set to 1, -EINVAL is returned.
8. Other capabilities.
======================

View File

@ -359,7 +359,7 @@ struct kvm_s390_float_interrupt {
struct kvm_s390_mchk_info mchk;
struct kvm_s390_ext_info srv_signal;
int last_sleep_cpu;
struct mutex ais_lock;
spinlock_t ais_lock;
u8 simm;
u8 nimm;
};
@ -448,6 +448,12 @@ struct kvm_vcpu_arch {
struct kvm_vm_stat {
struct kvm_vm_stat_generic generic;
u64 inject_io;
u64 io_390_adapter_map;
u64 io_390_adapter_unmap;
u64 io_390_inatomic;
u64 io_flic_inject_airq;
u64 io_set_adapter_int;
u64 io_390_inatomic_no_inject;
u64 inject_float_mchk;
u64 inject_pfault_done;
u64 inject_service_signal;
@ -479,6 +485,9 @@ struct s390_io_adapter {
bool masked;
bool swap;
bool suppressible;
spinlock_t maps_lock;
struct list_head maps;
unsigned int nr_maps;
};
#define MAX_S390_IO_ADAPTERS ((MAX_ISC + 1) * 8)
@ -504,6 +513,18 @@ struct kvm_s390_cpu_model {
struct kvm_s390_vm_cpu_uv_feat uv_feat_guest;
};
#define S390_ARCH_FAC_FORMAT_2 2
struct kvm_s390_flcb2 {
union {
struct {
u8 reserved0[7];
u8 length;
};
u64 header_val;
};
u64 facilities[S390_ARCH_FAC_LIST_SIZE_U64];
};
typedef int (*crypto_hook)(struct kvm_vcpu *vcpu);
struct kvm_s390_crypto {

View File

@ -104,6 +104,7 @@ struct sclp_info {
unsigned char has_aisii : 1;
unsigned char has_aeni : 1;
unsigned char has_aisi : 1;
unsigned char has_astfleie2 : 1;
unsigned int ibc;
unsigned int mtid;
unsigned int mtid_cp;

View File

@ -444,6 +444,7 @@ struct kvm_s390_vm_cpu_machine {
#define KVM_S390_VM_CPU_FEAT_PFMFI 11
#define KVM_S390_VM_CPU_FEAT_SIGPIF 12
#define KVM_S390_VM_CPU_FEAT_KSS 13
#define KVM_S390_VM_CPU_FEAT_ASTFLEIE2 14
struct kvm_s390_vm_cpu_feat {
__u64 feat[16];
};

View File

@ -30,6 +30,7 @@ config KVM
select KVM_VFIO
select VIRT_XFER_TO_GUEST_WORK
select KVM_MMU_LOCKLESS_AGING
select KVM_GENERIC_PRE_FAULT_MEMORY
help
Support hosting paravirtualized guest machines using the SIE
virtualization capability on the mainframe. This should work

View File

@ -501,6 +501,7 @@ struct guest_fault {
bool write_attempt; /* Write access attempted */
bool attempt_pfault; /* Attempt a pfault first */
bool valid; /* This entry contains valid data */
bool crste_region3; /* Whether crstep refers to a region3 entry */
void (*callback)(struct guest_fault *f);
void *priv;
};

View File

@ -105,6 +105,11 @@ static void gmap_add_child(struct gmap *parent, struct gmap *child)
else
clear_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &child->flags);
if (test_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &parent->flags))
set_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &child->flags);
else
clear_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &child->flags);
if (kvm_is_ucontrol(parent->kvm))
clear_bit(GMAP_FLAG_OWNS_PAGETABLES, &child->flags);
list_add(&child->list, &parent->children);
@ -543,6 +548,7 @@ static int gmap_handle_minor_crste_fault(struct gmap *gmap, struct guest_fault *
f->pfn = PHYS_PFN(large_crste_to_phys(oldcrste, f->gfn));
f->writable = oldcrste.s.fc1.w;
f->crste_region3 = is_pud(oldcrste);
/* Appropriate permissions already (race with another handler), nothing to do. */
if (!oldcrste.h.i && !(f->write_attempt && oldcrste.h.p))
return 0;
@ -630,10 +636,27 @@ int gmap_try_fixup_minor(struct gmap *gmap, struct guest_fault *fault)
return rc;
}
/**
* gmap_2g_allowed() - Check whether a 2G hugepage is allowed.
* @gmap: The gmap of the guest.
* @f: Describes the fault that is being resolved.
* @slot: The memslot the faulting address belongs to.
*
* The function checks whether the GMAP_FLAG_ALLOW_HPAGE_2G flag is set for
* @gmap, whether the offset of the address in the 2G virtual frame is the
* same as the offset in the physical 2G frame, and finally whether the whole
* 2G page would fit in the given memslot.
*
* Return: true if a 2G hugepage is allowed to back the faulting address, false
* otherwise.
*/
static inline bool gmap_2g_allowed(struct gmap *gmap, struct guest_fault *f,
struct kvm_memory_slot *slot)
{
return false;
return test_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &gmap->flags) &&
!((f->gfn ^ f->pfn) & ~_REGION3_FR_MASK) &&
slot->base_gfn <= ALIGN_DOWN(f->gfn, _PAGES_PER_REGION3) &&
slot->base_gfn + slot->npages >= ALIGN(f->gfn + 1, _PAGES_PER_REGION3);
}
/**
@ -702,6 +725,7 @@ static int _gmap_link(struct kvm_s390_mmu_cache *mc, struct gmap *gmap, int leve
if (oldval.val != _CRSTE_EMPTY(oldval.h.tt).val &&
crste_origin_large(oldval) != crste_origin_large(newval))
return -EAGAIN;
f->crste_region3 = is_pud(newval);
} while (!gmap_crstep_xchg_atomic(gmap, f->crstep, oldval, newval, f->gfn));
if (f->callback)
f->callback(f);

View File

@ -517,8 +517,9 @@ static int handle_pv_spx(struct kvm_vcpu *vcpu)
static int handle_pv_sclp(struct kvm_vcpu *vcpu)
{
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
unsigned long flags;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
/*
* 2 cases:
* a: an sccb answering interrupt was already pending or in flight.
@ -534,7 +535,7 @@ static int handle_pv_sclp(struct kvm_vcpu *vcpu)
fi->srv_signal.ext_params |= 0x43000;
set_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs);
clear_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return 0;
}

View File

@ -624,8 +624,9 @@ static int __must_check __deliver_machine_check(struct kvm_vcpu *vcpu)
struct kvm_s390_mchk_info mchk = {};
int deliver = 0;
int rc = 0;
unsigned long flags;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
spin_lock(&li->lock);
if (test_bit(IRQ_PEND_MCHK_EX, &li->pending_irqs) ||
test_bit(IRQ_PEND_MCHK_REP, &li->pending_irqs)) {
@ -654,7 +655,7 @@ static int __must_check __deliver_machine_check(struct kvm_vcpu *vcpu)
deliver = 1;
}
spin_unlock(&li->lock);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
if (deliver) {
VCPU_EVENT(vcpu, 3, "deliver: machine check mcic 0x%llx",
@ -941,11 +942,12 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu)
{
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_ext_info ext;
unsigned long flags;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
if (test_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs) ||
!(test_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs))) {
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return 0;
}
ext = fi->srv_signal;
@ -954,7 +956,7 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu)
clear_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs);
if (kvm_s390_pv_cpu_is_protected(vcpu))
set_bit(IRQ_PEND_EXT_SERVICE, &fi->masked_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
if (!ext.ext_params)
return 0;
@ -972,17 +974,18 @@ static int __must_check __deliver_service_ev(struct kvm_vcpu *vcpu)
{
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_ext_info ext;
unsigned long flags;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
if (!(test_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs))) {
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return 0;
}
ext = fi->srv_signal;
/* only clear the event bits */
fi->srv_signal.ext_params &= ~SCCB_EVENT_PENDING;
clear_bit(IRQ_PEND_EXT_SERVICE_EV, &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
VCPU_EVENT(vcpu, 4, "%s", "deliver: sclp parameter event");
vcpu->stat.deliver_service_signal++;
@ -997,8 +1000,9 @@ static int __must_check __deliver_pfault_done(struct kvm_vcpu *vcpu)
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_interrupt_info *inti;
int rc = 0;
unsigned long flags;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
inti = list_first_entry_or_null(&fi->lists[FIRQ_LIST_PFAULT],
struct kvm_s390_interrupt_info,
list);
@ -1008,7 +1012,7 @@ static int __must_check __deliver_pfault_done(struct kvm_vcpu *vcpu)
}
if (list_empty(&fi->lists[FIRQ_LIST_PFAULT]))
clear_bit(IRQ_PEND_PFAULT_DONE, &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
if (inti) {
trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id,
@ -1039,8 +1043,9 @@ static int __must_check __deliver_virtio(struct kvm_vcpu *vcpu)
struct kvm_s390_float_interrupt *fi = &vcpu->kvm->arch.float_int;
struct kvm_s390_interrupt_info *inti;
int rc = 0;
unsigned long flags;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
inti = list_first_entry_or_null(&fi->lists[FIRQ_LIST_VIRTIO],
struct kvm_s390_interrupt_info,
list);
@ -1058,7 +1063,7 @@ static int __must_check __deliver_virtio(struct kvm_vcpu *vcpu)
}
if (list_empty(&fi->lists[FIRQ_LIST_VIRTIO]))
clear_bit(IRQ_PEND_VIRTIO, &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
if (inti) {
rc = put_guest_lc(vcpu, EXT_IRQ_CP_SERVICE,
@ -1116,10 +1121,11 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu,
struct kvm_s390_io_info io;
u32 isc;
int rc = 0;
unsigned long flags;
fi = &vcpu->kvm->arch.float_int;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
isc = irq_type_to_isc(irq_type);
isc_list = &fi->lists[isc];
inti = list_first_entry_or_null(isc_list,
@ -1146,7 +1152,7 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu,
}
if (list_empty(isc_list))
clear_bit(irq_type, &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
if (inti) {
rc = __do_deliver_io(vcpu, &(inti->io));
@ -1662,8 +1668,9 @@ static struct kvm_s390_interrupt_info *get_io_int(struct kvm *kvm,
struct kvm_s390_interrupt_info *iter;
u16 id = (schid & 0xffff0000U) >> 16;
u16 nr = schid & 0x0000ffffU;
unsigned long flags;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
list_for_each_entry(iter, isc_list, list) {
if (schid && (id != iter->io.subchannel_id ||
nr != iter->io.subchannel_nr))
@ -1673,10 +1680,10 @@ static struct kvm_s390_interrupt_info *get_io_int(struct kvm *kvm,
fi->counters[FIRQ_CNTR_IO] -= 1;
if (list_empty(isc_list))
clear_bit(isc_to_irq_type(isc), &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return iter;
}
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return NULL;
}
@ -1769,9 +1776,10 @@ static int __inject_service(struct kvm *kvm,
struct kvm_s390_interrupt_info *inti)
{
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
unsigned long flags;
kvm->stat.inject_service_signal++;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
fi->srv_signal.ext_params |= inti->ext.ext_params & SCCB_EVENT_PENDING;
/* We always allow events, track them separately from the sccb ints */
@ -1791,7 +1799,7 @@ static int __inject_service(struct kvm *kvm,
fi->srv_signal.ext_params |= inti->ext.ext_params & SCCB_MASK;
set_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs);
out:
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
kfree(inti);
return 0;
}
@ -1800,17 +1808,18 @@ static int __inject_virtio(struct kvm *kvm,
struct kvm_s390_interrupt_info *inti)
{
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
unsigned long flags;
kvm->stat.inject_virtio++;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
if (fi->counters[FIRQ_CNTR_VIRTIO] >= KVM_S390_MAX_VIRTIO_IRQS) {
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return -EBUSY;
}
fi->counters[FIRQ_CNTR_VIRTIO] += 1;
list_add_tail(&inti->list, &fi->lists[FIRQ_LIST_VIRTIO]);
set_bit(IRQ_PEND_VIRTIO, &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return 0;
}
@ -1818,18 +1827,19 @@ static int __inject_pfault_done(struct kvm *kvm,
struct kvm_s390_interrupt_info *inti)
{
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
unsigned long flags;
kvm->stat.inject_pfault_done++;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
if (fi->counters[FIRQ_CNTR_PFAULT] >=
(ASYNC_PF_PER_VCPU * KVM_MAX_VCPUS)) {
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return -EBUSY;
}
fi->counters[FIRQ_CNTR_PFAULT] += 1;
list_add_tail(&inti->list, &fi->lists[FIRQ_LIST_PFAULT]);
set_bit(IRQ_PEND_PFAULT_DONE, &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return 0;
}
@ -1838,13 +1848,14 @@ static int __inject_float_mchk(struct kvm *kvm,
struct kvm_s390_interrupt_info *inti)
{
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
unsigned long flags;
kvm->stat.inject_float_mchk++;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
fi->mchk.cr14 |= inti->mchk.cr14 & (1UL << CR_PENDING_SUBCLASS);
fi->mchk.mcic |= inti->mchk.mcic;
set_bit(IRQ_PEND_MCHK_REP, &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
kfree(inti);
return 0;
}
@ -1855,6 +1866,7 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
struct kvm_s390_float_interrupt *fi;
struct list_head *list;
int isc;
unsigned long flags;
kvm->stat.inject_io++;
isc = int_word_to_isc(inti->io.io_int_word);
@ -1873,9 +1885,9 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
}
fi = &kvm->arch.float_int;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
if (fi->counters[FIRQ_CNTR_IO] >= KVM_S390_MAX_FLOAT_IRQS) {
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return -EBUSY;
}
fi->counters[FIRQ_CNTR_IO] += 1;
@ -1890,7 +1902,7 @@ static int __inject_io(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
list = &fi->lists[FIRQ_LIST_IO_ISC_0 + isc];
list_add_tail(&inti->list, list);
set_bit(isc_to_irq_type(isc), &fi->pending_irqs);
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
return 0;
}
@ -1966,15 +1978,10 @@ static int __inject_vm(struct kvm *kvm, struct kvm_s390_interrupt_info *inti)
}
int kvm_s390_inject_vm(struct kvm *kvm,
struct kvm_s390_interrupt *s390int)
struct kvm_s390_interrupt *s390int, struct kvm_s390_interrupt_info *inti)
{
struct kvm_s390_interrupt_info *inti;
int rc;
inti = kzalloc_obj(*inti, GFP_KERNEL_ACCOUNT);
if (!inti)
return -ENOMEM;
inti->type = s390int->type;
switch (inti->type) {
case KVM_S390_INT_VIRTIO:
@ -2003,15 +2010,13 @@ int kvm_s390_inject_vm(struct kvm *kvm,
inti->io.io_int_word = s390int->parm64 & 0x00000000ffffffffull;
break;
default:
kfree(inti);
return -EINVAL;
}
trace_kvm_s390_inject_vm(s390int->type, s390int->parm, s390int->parm64,
2);
rc = __inject_vm(kvm, inti);
if (rc)
kfree(inti);
return rc;
}
@ -2176,12 +2181,13 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm)
{
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
int i;
unsigned long flags;
mutex_lock(&kvm->lock);
if (!kvm_s390_pv_is_protected(kvm))
fi->masked_irqs = 0;
mutex_unlock(&kvm->lock);
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
fi->pending_irqs = 0;
memset(&fi->srv_signal, 0, sizeof(fi->srv_signal));
memset(&fi->mchk, 0, sizeof(fi->mchk));
@ -2189,7 +2195,7 @@ void kvm_s390_clear_float_irqs(struct kvm *kvm)
clear_irq_list(&fi->lists[i]);
for (i = 0; i < FIRQ_MAX_COUNT; i++)
fi->counters[i] = 0;
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
kvm_s390_gisa_clear(kvm);
};
@ -2204,6 +2210,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
int ret = 0;
int n = 0;
int i;
unsigned long flags;
if (len > KVM_S390_FLIC_MAX_BUFFER || len == 0)
return -EINVAL;
@ -2235,7 +2242,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
}
}
fi = &kvm->arch.float_int;
spin_lock(&fi->lock);
spin_lock_irqsave(&fi->lock, flags);
for (i = 0; i < FIRQ_LIST_COUNT; i++) {
list_for_each_entry(inti, &fi->lists[i], list) {
if (n == max_irqs) {
@ -2272,7 +2279,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
}
out:
spin_unlock(&fi->lock);
spin_unlock_irqrestore(&fi->lock, flags);
out_nolock:
if (!ret && n > 0) {
if (copy_to_user(usrbuf, buf, sizeof(struct kvm_s390_irq) * n))
@ -2287,6 +2294,7 @@ static int flic_ais_mode_get_all(struct kvm *kvm, struct kvm_device_attr *attr)
{
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
struct kvm_s390_ais_all ais;
unsigned long flags;
if (attr->attr < sizeof(ais))
return -EINVAL;
@ -2294,10 +2302,10 @@ static int flic_ais_mode_get_all(struct kvm *kvm, struct kvm_device_attr *attr)
if (!test_kvm_facility(kvm, 72))
return -EOPNOTSUPP;
mutex_lock(&fi->ais_lock);
spin_lock_irqsave(&fi->ais_lock, flags);
ais.simm = fi->simm;
ais.nimm = fi->nimm;
mutex_unlock(&fi->ais_lock);
spin_unlock_irqrestore(&fi->ais_lock, flags);
if (copy_to_user((void __user *)attr->addr, &ais, sizeof(ais)))
return -EFAULT;
@ -2411,34 +2419,46 @@ static int register_io_adapter(struct kvm_device *dev,
{
struct s390_io_adapter *adapter;
struct kvm_s390_io_adapter adapter_info;
int rc = 0;
mutex_lock(&dev->kvm->lock);
if (copy_from_user(&adapter_info,
(void __user *)attr->addr, sizeof(adapter_info)))
return -EFAULT;
if (adapter_info.id >= MAX_S390_IO_ADAPTERS)
return -EINVAL;
(void __user *)attr->addr, sizeof(adapter_info))) {
rc = -EFAULT;
goto out;
}
if (adapter_info.id >= MAX_S390_IO_ADAPTERS) {
rc = -EINVAL;
goto out;
}
adapter_info.id = array_index_nospec(adapter_info.id,
MAX_S390_IO_ADAPTERS);
if (dev->kvm->arch.adapters[adapter_info.id] != NULL)
return -EINVAL;
if (dev->kvm->arch.adapters[adapter_info.id] != NULL) {
rc = -EINVAL;
goto out;
}
adapter = kzalloc_obj(*adapter, GFP_KERNEL_ACCOUNT);
if (!adapter)
return -ENOMEM;
if (!adapter) {
rc = -ENOMEM;
goto out;
}
INIT_LIST_HEAD(&adapter->maps);
spin_lock_init(&adapter->maps_lock);
adapter->nr_maps = 0;
adapter->id = adapter_info.id;
adapter->isc = adapter_info.isc;
adapter->maskable = adapter_info.maskable;
adapter->masked = false;
adapter->swap = adapter_info.swap;
adapter->suppressible = (adapter_info.flags) &
adapter->suppressible = adapter_info.flags &
KVM_S390_ADAPTER_SUPPRESSIBLE;
dev->kvm->arch.adapters[adapter->id] = adapter;
return 0;
out:
mutex_unlock(&dev->kvm->lock);
return rc;
}
int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked)
@ -2453,12 +2473,151 @@ int kvm_s390_mask_adapter(struct kvm *kvm, unsigned int id, bool masked)
return ret;
}
static struct page *pin_map_page(struct kvm *kvm, u64 uaddr,
unsigned int gup_flags)
{
struct mm_struct *mm = kvm->mm;
struct page *page = NULL;
int locked = 1;
if (mmget_not_zero(mm)) {
mmap_read_lock(mm);
pin_user_pages_remote(mm, uaddr, 1, FOLL_WRITE | gup_flags,
&page, &locked);
if (locked)
mmap_read_unlock(mm);
mmput(mm);
}
return page;
}
static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
{
struct s390_io_adapter *adapter = get_io_adapter(kvm, id);
struct s390_map_info *map;
unsigned long flags;
__u64 host_addr;
int ret, idx;
if (!adapter || !addr)
return -EINVAL;
map = kzalloc_obj(*map, GFP_KERNEL_ACCOUNT);
if (!map)
return -ENOMEM;
INIT_LIST_HEAD(&map->list);
idx = srcu_read_lock(&kvm->srcu);
host_addr = gpa_to_hva(kvm, addr);
if (kvm_is_error_hva(host_addr)) {
srcu_read_unlock(&kvm->srcu, idx);
ret = -EFAULT;
goto out;
}
srcu_read_unlock(&kvm->srcu, idx);
map->guest_addr = addr;
map->addr = host_addr;
map->page = pin_map_page(kvm, host_addr, FOLL_LONGTERM);
if (!map->page) {
ret = -EINVAL;
goto out;
}
spin_lock_irqsave(&adapter->maps_lock, flags);
if (adapter->nr_maps < MAX_S390_ADAPTER_MAPS) {
list_add_tail(&map->list, &adapter->maps);
adapter->nr_maps++;
ret = 0;
} else {
ret = -EINVAL;
}
spin_unlock_irqrestore(&adapter->maps_lock, flags);
if (ret)
unpin_user_page(map->page);
out:
if (ret)
kfree(map);
return ret;
}
static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
{
struct s390_io_adapter *adapter = get_io_adapter(kvm, id);
struct s390_map_info *map, *tmp, *map_to_free;
struct page *map_page_to_put = NULL;
u64 map_addr_to_mark = 0;
unsigned long flags;
int found = 0, idx;
if (!adapter || !addr)
return -EINVAL;
spin_lock_irqsave(&adapter->maps_lock, flags);
list_for_each_entry_safe(map, tmp, &adapter->maps, list) {
if (map->guest_addr == addr) {
found = 1;
adapter->nr_maps--;
list_del(&map->list);
map_page_to_put = map->page;
map_addr_to_mark = map->guest_addr;
map_to_free = map;
break;
}
}
spin_unlock_irqrestore(&adapter->maps_lock, flags);
if (found) {
kfree(map_to_free);
idx = srcu_read_lock(&kvm->srcu);
mark_page_dirty(kvm, map_addr_to_mark >> PAGE_SHIFT);
set_page_dirty_lock(map_page_to_put);
srcu_read_unlock(&kvm->srcu, idx);
unpin_user_page(map_page_to_put);
}
return found ? 0 : -ENOENT;
}
void kvm_s390_unmap_all_adapters(struct kvm *kvm)
{
struct s390_map_info *map, *tmp;
unsigned long flags;
int i, idx;
for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) {
struct s390_io_adapter *adapter = kvm->arch.adapters[i];
LIST_HEAD(local_list);
if (!adapter)
continue;
spin_lock_irqsave(&adapter->maps_lock, flags);
list_splice_init(&adapter->maps, &local_list);
adapter->nr_maps = 0;
spin_unlock_irqrestore(&adapter->maps_lock, flags);
list_for_each_entry_safe(map, tmp, &local_list, list) {
list_del(&map->list);
idx = srcu_read_lock(&kvm->srcu);
mark_page_dirty(kvm, map->guest_addr >> PAGE_SHIFT);
set_page_dirty_lock(map->page);
srcu_read_unlock(&kvm->srcu, idx);
unpin_user_page(map->page);
kfree(map);
}
}
}
void kvm_s390_destroy_adapters(struct kvm *kvm)
{
int i;
for (i = 0; i < MAX_S390_IO_ADAPTERS; i++)
kvm_s390_unmap_all_adapters(kvm);
for (i = 0; i < MAX_S390_IO_ADAPTERS; i++) {
kfree(kvm->arch.adapters[i]);
kvm->arch.adapters[i] = NULL;
}
}
static int modify_io_adapter(struct kvm_device *dev,
@ -2480,14 +2639,22 @@ static int modify_io_adapter(struct kvm_device *dev,
if (ret > 0)
ret = 0;
break;
/*
* The following operations are no longer needed and therefore no-ops.
* The gpa to hva translation is done when an IRQ route is set up. The
* set_irq code uses get_user_pages_remote() to do the actual write.
*/
case KVM_S390_IO_ADAPTER_MAP:
case KVM_S390_IO_ADAPTER_UNMAP:
ret = 0;
/* If in Secure Execution mode do not long term pin. */
mutex_lock(&dev->kvm->lock);
if (kvm_s390_pv_is_protected(dev->kvm)) {
mutex_unlock(&dev->kvm->lock);
return 0;
}
if (req.type == KVM_S390_IO_ADAPTER_MAP) {
dev->kvm->stat.io_390_adapter_map++;
ret = kvm_s390_adapter_map(dev->kvm, req.id, req.addr);
} else {
dev->kvm->stat.io_390_adapter_unmap++;
ret = kvm_s390_adapter_unmap(dev->kvm, req.id, req.addr);
}
mutex_unlock(&dev->kvm->lock);
break;
default:
ret = -EINVAL;
@ -2524,6 +2691,7 @@ static int modify_ais_mode(struct kvm *kvm, struct kvm_device_attr *attr)
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
struct kvm_s390_ais_req req;
int ret = 0;
unsigned long flags;
if (!test_kvm_facility(kvm, 72))
return -EOPNOTSUPP;
@ -2540,7 +2708,7 @@ static int modify_ais_mode(struct kvm *kvm, struct kvm_device_attr *attr)
2 : KVM_S390_AIS_MODE_SINGLE :
KVM_S390_AIS_MODE_ALL, req.mode);
mutex_lock(&fi->ais_lock);
spin_lock_irqsave(&fi->ais_lock, flags);
switch (req.mode) {
case KVM_S390_AIS_MODE_ALL:
fi->simm &= ~AIS_MODE_MASK(req.isc);
@ -2553,7 +2721,7 @@ static int modify_ais_mode(struct kvm *kvm, struct kvm_device_attr *attr)
default:
ret = -EINVAL;
}
mutex_unlock(&fi->ais_lock);
spin_unlock_irqrestore(&fi->ais_lock, flags);
return ret;
}
@ -2567,25 +2735,41 @@ static int kvm_s390_inject_airq(struct kvm *kvm,
.parm = 0,
.parm64 = isc_to_int_word(adapter->isc),
};
struct kvm_s390_interrupt_info *inti;
unsigned long flags;
int ret = 0;
if (!test_kvm_facility(kvm, 72) || !adapter->suppressible)
return kvm_s390_inject_vm(kvm, &s390int);
inti = kzalloc_obj(*inti, GFP_KERNEL_ACCOUNT);
if (!inti)
return -ENOMEM;
mutex_lock(&fi->ais_lock);
if (fi->nimm & AIS_MODE_MASK(adapter->isc)) {
trace_kvm_s390_airq_suppressed(adapter->id, adapter->isc);
goto out;
if (!test_kvm_facility(kvm, 72) || !adapter->suppressible) {
ret = kvm_s390_inject_vm(kvm, &s390int, inti);
if (ret)
kfree(inti);
return ret;
}
ret = kvm_s390_inject_vm(kvm, &s390int);
spin_lock_irqsave(&fi->ais_lock, flags);
if (fi->nimm & AIS_MODE_MASK(adapter->isc)) {
trace_kvm_s390_airq_suppressed(adapter->id, adapter->isc);
spin_unlock_irqrestore(&fi->ais_lock, flags);
kfree(inti);
return ret;
}
ret = kvm_s390_inject_vm(kvm, &s390int, inti);
if (!ret && (fi->simm & AIS_MODE_MASK(adapter->isc))) {
fi->nimm |= AIS_MODE_MASK(adapter->isc);
trace_kvm_s390_modify_ais_mode(adapter->isc,
KVM_S390_AIS_MODE_SINGLE, 2);
}
out:
mutex_unlock(&fi->ais_lock);
spin_unlock_irqrestore(&fi->ais_lock, flags);
if (ret)
kfree(inti);
return ret;
}
@ -2594,6 +2778,8 @@ static int flic_inject_airq(struct kvm *kvm, struct kvm_device_attr *attr)
unsigned int id = attr->attr;
struct s390_io_adapter *adapter = get_io_adapter(kvm, id);
kvm->stat.io_flic_inject_airq++;
if (!adapter)
return -EINVAL;
@ -2604,6 +2790,7 @@ static int flic_ais_mode_set_all(struct kvm *kvm, struct kvm_device_attr *attr)
{
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
struct kvm_s390_ais_all ais;
unsigned long flags;
if (!test_kvm_facility(kvm, 72))
return -EOPNOTSUPP;
@ -2611,10 +2798,10 @@ static int flic_ais_mode_set_all(struct kvm *kvm, struct kvm_device_attr *attr)
if (copy_from_user(&ais, (void __user *)attr->addr, sizeof(ais)))
return -EFAULT;
mutex_lock(&fi->ais_lock);
spin_lock_irqsave(&fi->ais_lock, flags);
fi->simm = ais.simm;
fi->nimm = ais.nimm;
mutex_unlock(&fi->ais_lock);
spin_unlock_irqrestore(&fi->ais_lock, flags);
return 0;
}
@ -2733,22 +2920,19 @@ static unsigned long get_ind_bit(__u64 addr, unsigned long bit_nr, bool swap)
return swap ? (bit ^ (BITS_PER_LONG - 1)) : bit;
}
static struct page *get_map_page(struct kvm *kvm, u64 uaddr)
static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter,
u64 addr)
{
struct mm_struct *mm = kvm->mm;
struct page *page = NULL;
int locked = 1;
struct s390_map_info *map;
if (mmget_not_zero(mm)) {
mmap_read_lock(mm);
get_user_pages_remote(mm, uaddr, 1, FOLL_WRITE,
&page, &locked);
if (locked)
mmap_read_unlock(mm);
mmput(mm);
if (!adapter)
return NULL;
list_for_each_entry(map, &adapter->maps, list) {
if (map->addr == addr)
return map;
}
return page;
return NULL;
}
static int adapter_indicators_set(struct kvm *kvm,
@ -2757,35 +2941,98 @@ static int adapter_indicators_set(struct kvm *kvm,
{
unsigned long bit;
int summary_set, idx;
struct page *ind_page, *summary_page;
struct s390_map_info *ind_info, *summary_info;
void *map;
struct page *ind_page, *summary_page;
unsigned long flags;
ind_page = get_map_page(kvm, adapter_int->ind_addr);
if (!ind_page)
return -1;
summary_page = get_map_page(kvm, adapter_int->summary_addr);
if (!summary_page) {
put_page(ind_page);
return -1;
ind_page = NULL;
spin_lock_irqsave(&adapter->maps_lock, flags);
ind_info = get_map_info(adapter, adapter_int->ind_addr);
if (!ind_info) {
spin_unlock_irqrestore(&adapter->maps_lock, flags);
ind_page = pin_map_page(kvm, adapter_int->ind_addr, 0);
if (!ind_page)
return -1;
idx = srcu_read_lock(&kvm->srcu);
map = page_address(ind_page);
bit = get_ind_bit(adapter_int->ind_addr,
adapter_int->ind_offset, adapter->swap);
set_bit(bit, map);
mark_page_dirty(kvm, adapter_int->ind_gaddr >> PAGE_SHIFT);
set_page_dirty_lock(ind_page);
srcu_read_unlock(&kvm->srcu, idx);
unpin_user_page(ind_page);
} else {
map = page_address(ind_info->page);
bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
set_bit(bit, map);
spin_unlock_irqrestore(&adapter->maps_lock, flags);
}
idx = srcu_read_lock(&kvm->srcu);
map = page_address(ind_page);
bit = get_ind_bit(adapter_int->ind_addr,
adapter_int->ind_offset, adapter->swap);
set_bit(bit, map);
mark_page_dirty(kvm, adapter_int->ind_gaddr >> PAGE_SHIFT);
set_page_dirty_lock(ind_page);
map = page_address(summary_page);
bit = get_ind_bit(adapter_int->summary_addr,
adapter_int->summary_offset, adapter->swap);
summary_set = test_and_set_bit(bit, map);
mark_page_dirty(kvm, adapter_int->summary_gaddr >> PAGE_SHIFT);
set_page_dirty_lock(summary_page);
srcu_read_unlock(&kvm->srcu, idx);
spin_lock_irqsave(&adapter->maps_lock, flags);
summary_info = get_map_info(adapter, adapter_int->summary_addr);
if (!summary_info) {
spin_unlock_irqrestore(&adapter->maps_lock, flags);
summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0);
if (WARN_ON_ONCE(!summary_page))
return -1;
idx = srcu_read_lock(&kvm->srcu);
map = page_address(summary_page);
bit = get_ind_bit(adapter_int->summary_addr,
adapter_int->summary_offset, adapter->swap);
summary_set = test_and_set_bit(bit, map);
mark_page_dirty(kvm, adapter_int->summary_gaddr >> PAGE_SHIFT);
set_page_dirty_lock(summary_page);
srcu_read_unlock(&kvm->srcu, idx);
unpin_user_page(summary_page);
} else {
map = page_address(summary_info->page);
bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset,
adapter->swap);
summary_set = test_and_set_bit(bit, map);
spin_unlock_irqrestore(&adapter->maps_lock, flags);
}
put_page(ind_page);
put_page(summary_page);
return summary_set ? 0 : 1;
}
static int adapter_indicators_set_fast(struct kvm *kvm,
struct s390_io_adapter *adapter,
struct kvm_s390_adapter_int *adapter_int,
int setbit)
{
unsigned long bit;
int summary_set;
struct s390_map_info *ind_info, *summary_info;
void *map;
spin_lock(&adapter->maps_lock);
ind_info = get_map_info(adapter, adapter_int->ind_addr);
if (!ind_info) {
spin_unlock(&adapter->maps_lock);
return -EWOULDBLOCK;
}
map = page_address(ind_info->page);
bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
if (setbit)
set_bit(bit, map);
summary_info = get_map_info(adapter, adapter_int->summary_addr);
if (!summary_info) {
spin_unlock(&adapter->maps_lock);
return -EWOULDBLOCK;
}
map = page_address(summary_info->page);
bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset,
adapter->swap);
/* If setbit then set summary bit. Else if falling back to the slow path */
/* with setbit==0 then clear the summary bit so the slow path re-injects */
if (setbit)
summary_set = test_and_set_bit(bit, map);
else
summary_set = test_and_clear_bit(bit, map);
spin_unlock(&adapter->maps_lock);
return summary_set ? 0 : 1;
}
@ -2801,6 +3048,8 @@ static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e,
int ret;
struct s390_io_adapter *adapter;
kvm->stat.io_set_adapter_int++;
/* We're only interested in the 0->1 transition. */
if (!level)
return 0;
@ -2869,7 +3118,6 @@ int kvm_set_routing_entry(struct kvm *kvm,
int idx;
switch (ue->type) {
/* we store the userspace addresses instead of the guest addresses */
case KVM_IRQ_ROUTING_S390_ADAPTER:
if (kvm_is_ucontrol(kvm))
return -EINVAL;
@ -3459,3 +3707,86 @@ int __init kvm_s390_gib_init(u8 nisc)
out:
return rc;
}
/*
* kvm_arch_set_irq_inatomic: fast-path for irqfd injection
*/
int kvm_arch_set_irq_inatomic(struct kvm_kernel_irq_routing_entry *e,
struct kvm *kvm, int irq_source_id, int level,
bool line_status)
{
int ret, setbit;
struct s390_io_adapter *adapter;
struct kvm_s390_float_interrupt *fi = &kvm->arch.float_int;
struct kvm_s390_interrupt_info *inti;
struct kvm_s390_interrupt s390int = {
.type = KVM_S390_INT_IO(1, 0, 0, 0),
.parm = 0,
};
kvm->stat.io_390_inatomic++;
/* We're only interested in the 0->1 transition. */
if (!level)
return 0;
if (e->type != KVM_IRQ_ROUTING_S390_ADAPTER)
return -EWOULDBLOCK;
adapter = get_io_adapter(kvm, e->adapter.adapter_id);
if (!adapter)
return -EWOULDBLOCK;
s390int.parm64 = isc_to_int_word(adapter->isc);
setbit = 1;
ret = adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit);
if (ret < 0)
return -EWOULDBLOCK;
if (!ret || adapter->masked) {
kvm->stat.io_390_inatomic_no_inject++;
return 0;
}
inti = kzalloc_obj(*inti, GFP_ATOMIC);
if (!inti) {
setbit = 0;
adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit);
return -EWOULDBLOCK;
}
if (!test_kvm_facility(kvm, 72) || !adapter->suppressible) {
ret = kvm_s390_inject_vm(kvm, &s390int, inti);
if (ret == 0) {
return ret;
} else {
setbit = 0;
adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit);
kfree(inti);
return -EWOULDBLOCK;
}
}
spin_lock(&fi->ais_lock);
if (fi->nimm & AIS_MODE_MASK(adapter->isc)) {
trace_kvm_s390_airq_suppressed(adapter->id, adapter->isc);
spin_unlock(&fi->ais_lock);
kfree(inti);
kvm->stat.io_390_inatomic_no_inject++;
return 0;
}
ret = kvm_s390_inject_vm(kvm, &s390int, inti);
if (!ret && (fi->simm & AIS_MODE_MASK(adapter->isc))) {
fi->nimm |= AIS_MODE_MASK(adapter->isc);
trace_kvm_s390_modify_ais_mode(adapter->isc,
KVM_S390_AIS_MODE_SINGLE, 2);
} else if (ret) {
spin_unlock(&fi->ais_lock);
setbit = 0;
adapter_indicators_set_fast(kvm, adapter, &e->adapter, setbit);
kfree(inti);
return -EWOULDBLOCK;
}
spin_unlock(&fi->ais_lock);
return 0;
}

View File

@ -68,6 +68,12 @@
const struct kvm_stats_desc kvm_vm_stats_desc[] = {
KVM_GENERIC_VM_STATS(),
STATS_DESC_COUNTER(VM, inject_io),
STATS_DESC_COUNTER(VM, io_390_adapter_map),
STATS_DESC_COUNTER(VM, io_390_adapter_unmap),
STATS_DESC_COUNTER(VM, io_390_inatomic),
STATS_DESC_COUNTER(VM, io_flic_inject_airq),
STATS_DESC_COUNTER(VM, io_set_adapter_int),
STATS_DESC_COUNTER(VM, io_390_inatomic_no_inject),
STATS_DESC_COUNTER(VM, inject_float_mchk),
STATS_DESC_COUNTER(VM, inject_pfault_done),
STATS_DESC_COUNTER(VM, inject_service_signal),
@ -204,11 +210,16 @@ static int nested;
module_param(nested, int, S_IRUGO);
MODULE_PARM_DESC(nested, "Nested virtualization support");
/* allow 1m huge page guest backing, if !nested */
/* allow 1m huge page guest backing */
static int hpage;
module_param(hpage, int, 0444);
MODULE_PARM_DESC(hpage, "1m huge page backing support");
/* allow 2g huge page guest backing */
static int hpage_2g;
module_param(hpage_2g, int, 0444);
MODULE_PARM_DESC(hpage_2g, "2g huge page backing support");
/* maximum percentage of steal time for polling. >100 is treated like 100 */
static u8 halt_poll_max_steal = 10;
module_param(halt_poll_max_steal, byte, 0644);
@ -232,33 +243,25 @@ static int async_destroy = 1;
module_param(async_destroy, int, 0444);
MODULE_PARM_DESC(async_destroy, "Asynchronous destroy for protected guests");
/*
* For now we handle at most 16 double words as this is what the s390 base
* kernel handles and stores in the prefix page. If we ever need to go beyond
* this, this requires changes to code, but the external uapi can stay.
*/
#define SIZE_INTERNAL 16
#define HMFAI_DWORDS 16
/*
* Base feature mask that defines default mask for facilities. Consists of the
* defines in FACILITIES_KVM and the non-hypervisor managed bits.
*/
static unsigned long kvm_s390_fac_base[SIZE_INTERNAL] = { FACILITIES_KVM };
static unsigned long kvm_s390_fac_base[HMFAI_DWORDS] = { FACILITIES_KVM };
static_assert(ARRAY_SIZE(((long[]){ FACILITIES_KVM })) <= HMFAI_DWORDS);
static_assert(ARRAY_SIZE(kvm_s390_fac_base) <= S390_ARCH_FAC_MASK_SIZE_U64);
static_assert(ARRAY_SIZE(kvm_s390_fac_base) <= S390_ARCH_FAC_LIST_SIZE_U64);
static_assert(ARRAY_SIZE(kvm_s390_fac_base) <= ARRAY_SIZE(stfle_fac_list));
/*
* Extended feature mask. Consists of the defines in FACILITIES_KVM_CPUMODEL
* and defines the facilities that can be enabled via a cpu model.
*/
static unsigned long kvm_s390_fac_ext[SIZE_INTERNAL] = { FACILITIES_KVM_CPUMODEL };
static unsigned long kvm_s390_fac_size(void)
{
BUILD_BUG_ON(SIZE_INTERNAL > S390_ARCH_FAC_MASK_SIZE_U64);
BUILD_BUG_ON(SIZE_INTERNAL > S390_ARCH_FAC_LIST_SIZE_U64);
BUILD_BUG_ON(SIZE_INTERNAL * sizeof(unsigned long) >
sizeof(stfle_fac_list));
return SIZE_INTERNAL;
}
static const unsigned long kvm_s390_fac_ext[] = { FACILITIES_KVM_CPUMODEL };
static_assert(ARRAY_SIZE(kvm_s390_fac_ext) <= S390_ARCH_FAC_MASK_SIZE_U64);
static_assert(ARRAY_SIZE(kvm_s390_fac_ext) <= S390_ARCH_FAC_LIST_SIZE_U64);
static_assert(ARRAY_SIZE(kvm_s390_fac_ext) <= ARRAY_SIZE(stfle_fac_list));
/* available cpu features supported by kvm */
static DECLARE_BITMAP(kvm_s390_available_cpu_feat, KVM_S390_VM_CPU_FEAT_NR_BITS);
@ -468,6 +471,8 @@ static void __init kvm_s390_cpu_feat_init(void)
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_IBS);
if (sclp.has_kss)
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_KSS);
if (sclp.has_astfleie2)
allow_cpu_feat(KVM_S390_VM_CPU_FEAT_ASTFLEIE2);
/*
* KVM_S390_VM_CPU_FEAT_SKEY: Wrong shadow of PTE.I bits will make
* all skey handling functions read/set the skey from the PGSTE
@ -630,6 +635,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_S390_USER_OPEREXEC:
case KVM_CAP_S390_KEYOP:
case KVM_CAP_S390_VSIE_ESAMODE:
case KVM_CAP_PRE_FAULT_MEMORY:
r = 1;
break;
case KVM_CAP_SET_GUEST_DEBUG2:
@ -640,6 +646,11 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
if (hpage && !(kvm && kvm_is_ucontrol(kvm)))
r = 1;
break;
case KVM_CAP_S390_HPAGE_2G:
r = 0;
if (hpage_2g && !(kvm && kvm_is_ucontrol(kvm)))
r = 1;
break;
case KVM_CAP_S390_MEM_OP:
r = MEM_OP_MAX_SIZE;
break;
@ -896,6 +907,27 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap)
VM_EVENT(kvm, 3, "ENABLE: CAP_S390_HPAGE %s",
r ? "(not available)" : "(success)");
break;
case KVM_CAP_S390_HPAGE_2G:
mutex_lock(&kvm->lock);
if (kvm->created_vcpus) {
r = -EBUSY;
} else if (!hpage_2g || kvm->arch.use_cmma || kvm_is_ucontrol(kvm)) {
r = -EINVAL;
} else {
r = 0;
set_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &kvm->arch.gmap->flags);
/*
* We might have to create fake 4k page
* tables. To avoid that the hardware works on
* stale PGSTEs, we emulate these instructions.
*/
kvm->arch.use_skf = 0;
kvm->arch.use_pfmfi = 0;
}
mutex_unlock(&kvm->lock);
VM_EVENT(kvm, 3, "ENABLE: CAP_S390_HPAGE_2G %s",
r ? "(not available)" : "(success)");
break;
case KVM_CAP_S390_USER_STSI:
VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_STSI");
kvm->arch.user_stsi = 1;
@ -2252,7 +2284,7 @@ static int kvm_s390_get_cmma_bits(struct kvm *kvm,
return 0;
}
values = vmalloc(args->count);
values = vzalloc(args->count);
if (!values)
return -ENOMEM;
@ -2534,6 +2566,7 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd)
if (kvm_s390_pv_is_protected(kvm))
break;
kvm_s390_unmap_all_adapters(kvm);
mmap_write_lock(kvm->mm);
/*
* Disable creation of new THPs. Existing THPs can stay, they
@ -2869,6 +2902,7 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
void __user *argp = (void __user *)arg;
struct kvm_device_attr attr;
int r;
struct kvm_s390_interrupt_info *inti;
switch (ioctl) {
case KVM_S390_INTERRUPT: {
@ -2877,7 +2911,12 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
r = -EFAULT;
if (copy_from_user(&s390int, argp, sizeof(s390int)))
break;
r = kvm_s390_inject_vm(kvm, &s390int);
inti = kzalloc_obj(*inti, GFP_KERNEL_ACCOUNT);
if (!inti)
return -ENOMEM;
r = kvm_s390_inject_vm(kvm, &s390int, inti);
if (r)
kfree(inti);
break;
}
case KVM_CREATE_IRQCHIP: {
@ -3238,13 +3277,16 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
kvm->arch.sie_page2->kvm = kvm;
kvm->arch.model.fac_list = kvm->arch.sie_page2->fac_list;
for (i = 0; i < kvm_s390_fac_size(); i++) {
for (i = 0; i < ARRAY_SIZE(kvm_s390_fac_base); i++) {
kvm->arch.model.fac_mask[i] = stfle_fac_list[i] &
(kvm_s390_fac_base[i] |
kvm_s390_fac_ext[i]);
kvm_s390_fac_base[i];
kvm->arch.model.fac_list[i] = stfle_fac_list[i] &
kvm_s390_fac_base[i];
}
for (i = 0; i < ARRAY_SIZE(kvm_s390_fac_ext); i++) {
kvm->arch.model.fac_mask[i] |= stfle_fac_list[i] &
kvm_s390_fac_ext[i];
}
kvm->arch.model.subfuncs = kvm_s390_available_subfunc;
/* we are always in czam mode - even on pre z14 machines */
@ -3275,7 +3317,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type)
mutex_unlock(&kvm->lock);
}
mutex_init(&kvm->arch.float_int.ais_lock);
spin_lock_init(&kvm->arch.float_int.ais_lock);
spin_lock_init(&kvm->arch.float_int.lock);
for (i = 0; i < FIRQ_LIST_COUNT; i++)
INIT_LIST_HEAD(&kvm->arch.float_int.lists[i]);
@ -4397,19 +4439,28 @@ int kvm_s390_try_set_tod_clock(struct kvm *kvm, const struct kvm_s390_vm_tod_clo
}
static void __kvm_inject_pfault_token(struct kvm_vcpu *vcpu, bool start_token,
unsigned long token)
unsigned long token)
{
struct kvm_s390_interrupt inti;
struct kvm_s390_irq irq;
struct kvm_s390_interrupt_info *inti_mem = NULL;
int ret = 0;
if (start_token) {
irq.u.ext.ext_params2 = token;
irq.type = KVM_S390_INT_PFAULT_INIT;
WARN_ON_ONCE(kvm_s390_inject_vcpu(vcpu, &irq));
} else {
inti_mem = kzalloc_obj(*inti_mem, GFP_KERNEL_ACCOUNT);
if (WARN_ON_ONCE(!inti_mem))
return;
inti.type = KVM_S390_INT_PFAULT_DONE;
inti.parm64 = token;
WARN_ON_ONCE(kvm_s390_inject_vm(vcpu->kvm, &inti));
ret = kvm_s390_inject_vm(vcpu->kvm, &inti, inti_mem);
if (ret)
kfree(inti_mem);
WARN_ON_ONCE(ret);
}
}
@ -5759,6 +5810,50 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
return;
}
/**
* kvm_arch_vcpu_pre_fault_memory() -- pre-fault and link gmap dat tables
* @vcpu: the vcpu that shall appear to have generated the fault-in.
* @range: the range that needs to be faulted in.
*
* The first page of the given range is faulted in and the corresponding gmap
* page tables are created, as if the given vCPU had performed a read
* operation.
* If the range starts outside any memslots, an error is returned. An error is
* also returned for UCONTROL VMs, which should instead use the
* KVM_S390_VCPU_FAULT ioctl.
*
* Return:
* * %-ENOENT if the range lies outside of a memslot.
* * %-EINVAL in case of invalid state (for example if the VM is UCONTROL).
* * %-EIO if errors happen while faulting-in the page (will trigger a warning
* in the caller).
* * other error codes < 0 in case of other errors.
* * otherwise a number > 0 of bytes that have been faulted in successfully.
*/
long kvm_arch_vcpu_pre_fault_memory(struct kvm_vcpu *vcpu, struct kvm_pre_fault_memory *range)
{
struct guest_fault f = { .gfn = gpa_to_gfn(range->gpa), };
gpa_t end;
int rc;
if (kvm_is_ucontrol(vcpu->kvm))
return -EINVAL;
rc = kvm_s390_faultin_gfn(vcpu, NULL, &f);
if (rc == PGM_ADDRESSING)
return -ENOENT;
if (rc > 0)
return -EIO;
if (rc < 0)
return rc;
if (f.ptep)
return PAGE_SIZE;
end = ALIGN(range->gpa + PAGE_SIZE, f.crste_region3 ? _REGION3_SIZE : HPAGE_SIZE);
return min(range->size, end - range->gpa);
}
/**
* kvm_test_age_gfn() - test young
* @kvm: the kvm instance
@ -5820,9 +5915,13 @@ static int __init kvm_s390_init(void)
return -ENODEV;
}
for (i = 0; i < 16; i++)
kvm_s390_fac_base[i] |=
stfle_fac_list[i] & nonhyp_mask(i);
if (hpage_2g && !hpage) {
hpage_2g = 0;
pr_info("Disabling 2G hugepage support, since 1M hugepage support is not enabled.\n");
}
for (i = 0; i < HMFAI_DWORDS; i++)
kvm_s390_fac_base[i] |= nonhyp_mask(i);
r = __kvm_s390_init();
if (r)

View File

@ -376,7 +376,8 @@ int __must_check kvm_s390_deliver_pending_interrupts(struct kvm_vcpu *vcpu);
void kvm_s390_clear_local_irqs(struct kvm_vcpu *vcpu);
void kvm_s390_clear_float_irqs(struct kvm *kvm);
int __must_check kvm_s390_inject_vm(struct kvm *kvm,
struct kvm_s390_interrupt *s390int);
struct kvm_s390_interrupt *s390int,
struct kvm_s390_interrupt_info *inti);
int __must_check kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu,
struct kvm_s390_irq *irq);
static inline int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu,
@ -560,6 +561,8 @@ void kvm_s390_gisa_disable(struct kvm *kvm);
void kvm_s390_gisa_enable(struct kvm *kvm);
int __init kvm_s390_gib_init(u8 nisc);
void kvm_s390_gib_destroy(void);
void kvm_s390_unmap_all_adapters(struct kvm *kvm);
/* implemented in guestdbg.c */
void kvm_s390_backup_guest_per_regs(struct kvm_vcpu *vcpu);

View File

@ -740,7 +740,10 @@ int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc)
uvcb.flags.ap_allow_instr = kvm->arch.model.uv_feat_guest.ap;
uvcb.flags.ap_instr_intr = kvm->arch.model.uv_feat_guest.ap_intr;
clear_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &kvm->arch.gmap->flags);
scoped_guard(write_lock, &kvm->mmu_lock) {
clear_bit(GMAP_FLAG_ALLOW_HPAGE_1M, &kvm->arch.gmap->flags);
clear_bit(GMAP_FLAG_ALLOW_HPAGE_2G, &kvm->arch.gmap->flags);
}
gmap_split_huge_pages(kvm->arch.gmap);
cc = uv_call_sched(0, (u64)&uvcb);

View File

@ -6,12 +6,15 @@
*
* Author(s): David Hildenbrand <dahi@linux.vnet.ibm.com>
*/
#include <linux/align.h>
#include <linux/vmalloc.h>
#include <linux/kvm_host.h>
#include <linux/bug.h>
#include <linux/compiler.h>
#include <linux/list.h>
#include <linux/bitmap.h>
#include <linux/sched/signal.h>
#include <linux/stddef.h>
#include <linux/io.h>
#include <linux/mman.h>
@ -62,9 +65,9 @@ struct vsie_page {
gpa_t scb_gpa; /* 0x0258 */
/* the shadow gmap in use by the vsie_page */
struct gmap_cache gmap_cache; /* 0x0260 */
__u8 reserved[0x0700 - 0x0278]; /* 0x0278 */
struct kvm_s390_crypto_cb crycb; /* 0x0700 */
__u8 fac[S390_ARCH_FAC_LIST_SIZE_BYTE]; /* 0x0800 */
__u8 reserved[0x06f8 - 0x0278]; /* 0x0278 */
struct kvm_s390_crypto_cb crycb; /* 0x06f8 */
__u8 fac[8 + S390_ARCH_FAC_LIST_SIZE_BYTE];/* 0x07f8 */
};
static_assert(sizeof(struct vsie_page) == PAGE_SIZE);
@ -1000,6 +1003,45 @@ static void retry_vsie_icpt(struct vsie_page *vsie_page)
clear_vsie_icpt(vsie_page);
}
static int handle_stfle_0(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page,
u32 fac_list_origin)
{
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
/*
* format-0 -> size of nested guest's facility list == guest's size
* guest's size == host's size, since STFLE is interpretatively executed
* using a format-0 for the guest, too.
*/
if (read_guest_real(vcpu, fac_list_origin, &vsie_page->fac,
stfle_size() * sizeof(u64)))
return set_validity_icpt(scb_s, 0x1090U);
scb_s->fac = (u32)virt_to_phys(&vsie_page->fac);
return 0;
}
static int handle_stfle_2(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page, u32 fac_list_origin)
{
struct kvm_s390_flcb2 *flcb_s = (struct kvm_s390_flcb2 *)vsie_page->fac;
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
u64 len;
if (read_guest_real(vcpu, fac_list_origin, &len, sizeof(len)))
return set_validity_icpt(scb_s, 0x1090U);
/* discard reserved bits */
len = (len & U8_MAX);
flcb_s->header_val = len;
len += 1;
if (read_guest_real(vcpu, fac_list_origin + offsetof(struct kvm_s390_flcb2, facilities),
&flcb_s->facilities, len * sizeof(u64)))
return set_validity_icpt(scb_s, 0x1090U);
scb_s->fac = (u32)virt_to_phys(&vsie_page->fac) | S390_ARCH_FAC_FORMAT_2;
return 0;
}
/*
* Try to shadow + enable the guest 2 provided facility list.
* Retry instruction execution if enabled for and provided by guest 2.
@ -1009,29 +1051,34 @@ static void retry_vsie_icpt(struct vsie_page *vsie_page)
*/
static int handle_stfle(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
{
struct kvm_s390_sie_block *scb_s = &vsie_page->scb_s;
__u32 fac = READ_ONCE(vsie_page->scb_o->fac);
bool has_astfleie2 = test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_ASTFLEIE2);
u32 fac = READ_ONCE(vsie_page->scb_o->fac);
int format_mask, format;
u32 origin;
/* assert no overflow with maximum len */
BUILD_BUG_ON(sizeof(vsie_page->fac) < ((S390_ARCH_FAC_LIST_SIZE_U64 + 1) * sizeof(u64)));
BUILD_BUG_ON(!IS_ALIGNED(offsetof(struct vsie_page, fac), 8));
/*
* Alternate-STFLE-Interpretive-Execution facilities are not supported
* -> format-0 flcb
*/
if (fac && test_kvm_facility(vcpu->kvm, 7)) {
retry_vsie_icpt(vsie_page);
/*
* The facility list origin (FLO) is in bits 1 - 28 of the FLD
* so we need to mask here before reading.
*/
fac = fac & 0x7ffffff8U;
/*
* format-0 -> size of nested guest's facility list == guest's size
* guest's size == host's size, since STFLE is interpretatively executed
* using a format-0 for the guest, too.
*/
if (read_guest_real(vcpu, fac, &vsie_page->fac,
stfle_size() * sizeof(u64)))
return set_validity_icpt(scb_s, 0x1090U);
scb_s->fac = (u32)virt_to_phys(&vsie_page->fac);
origin = fac & 0x7ffffff8U;
format_mask = has_astfleie2 ? 3 : 0;
format = fac & format_mask;
switch (format) {
case 0:
return handle_stfle_0(vcpu, vsie_page, origin);
case 1:
return set_validity_icpt(&vsie_page->scb_s, 0x1330U);
case 2:
return handle_stfle_2(vcpu, vsie_page, origin);
case 3:
return set_validity_icpt(&vsie_page->scb_s, 0x1330U);
}
}
return 0;
}

View File

@ -61,8 +61,10 @@ static void __init sclp_early_facilities_detect(void)
sclp.has_sipl = !!(sccb->cbl & 0x4000);
sclp.has_sipl_eckd = !!(sccb->cbl & 0x2000);
}
if (sccb->cpuoff > 139)
if (sccb->cpuoff > 139) {
sclp.has_diag324 = !!(sccb->byte_139 & 0x80);
sclp.has_astfleie2 = !!(sccb->byte_139 & 0x40);
}
sclp.rnmax = sccb->rnmax ? sccb->rnmax : sccb->rnmax2;
sclp.rzm = sccb->rnsize ? sccb->rnsize : sccb->rnsize2;
sclp.rzm <<= 20;

View File

@ -996,6 +996,7 @@ struct kvm_enable_cap {
#define KVM_CAP_S390_USER_OPEREXEC 246
#define KVM_CAP_S390_KEYOP 247
#define KVM_CAP_S390_VSIE_ESAMODE 248
#define KVM_CAP_S390_HPAGE_2G 249
struct kvm_irq_routing_irqchip {
__u32 irqchip;

View File

@ -213,6 +213,7 @@ TEST_GEN_PROGS_s390 += s390/keyop
TEST_GEN_PROGS_s390 += rseq_test
TEST_GEN_PROGS_s390 += s390/irq_routing
TEST_GEN_PROGS_s390 += mmu_stress_test
TEST_GEN_PROGS_s390 += pre_fault_memory_test
TEST_GEN_PROGS_riscv = $(TEST_GEN_PROGS_COMMON)
TEST_GEN_PROGS_riscv += riscv/sbi_pmu_test

View File

@ -11,6 +11,7 @@
#include <kvm_util.h>
#include <processor.h>
#include <pthread.h>
#include <ucall_common.h>
/* Arbitrarily chosen values */
#define TEST_SIZE (SZ_2M + PAGE_SIZE)
@ -167,7 +168,6 @@ static void __test_pre_fault_memory(unsigned long vm_type, bool private)
.type = vm_type,
};
struct kvm_vcpu *vcpu;
struct kvm_run *run;
struct kvm_vm *vm;
struct ucall uc;
@ -193,11 +193,6 @@ static void __test_pre_fault_memory(unsigned long vm_type, bool private)
vcpu_args_set(vcpu, 1, gva);
vcpu_run(vcpu);
run = vcpu->run;
TEST_ASSERT(run->exit_reason == KVM_EXIT_IO,
"Wanted KVM_EXIT_IO, got exit reason: %u (%s)",
run->exit_reason, exit_reason_str(run->exit_reason));
switch (get_ucall(vcpu, &uc)) {
case UCALL_ABORT:
REPORT_GUEST_ASSERT(uc);