mirror of
https://github.com/torvalds/linux.git
synced 2026-09-24 06:24:02 +02:00
Merge remote-tracking branch 'kvms390/master' into 'kvms390/next'
This commit is contained in:
commit
d873515be0
|
|
@ -112,9 +112,20 @@ Groups:
|
|||
mask or unmask the adapter, as specified in mask
|
||||
|
||||
KVM_S390_IO_ADAPTER_MAP
|
||||
This is now a no-op. The mapping is purely done by the irq route.
|
||||
Map an adapter indicator or summary page for long-term pinning so that
|
||||
interrupt injection can be performed in atomic context. If long-term
|
||||
pinning is not possible (e.g. file-backed memory), the page is verified
|
||||
via a short-term pin and the ioctl returns success; interrupt injection
|
||||
will use the non-atomic irqfd path with short-term pinning on each
|
||||
interrupt. In Secure Execution mode this is a no-op and the ioctl
|
||||
returns success.
|
||||
|
||||
KVM_S390_IO_ADAPTER_UNMAP
|
||||
This is now a no-op. The mapping is purely done by the irq route.
|
||||
Unmap a previously mapped adapter indicator or summary page and release
|
||||
the long-term pin. If the page was not long-term pinned (e.g. file-backed
|
||||
memory), the map entry is removed and success is returned; if no prior
|
||||
map entry exists, -ENOENT is returned. In Secure Execution mode this is
|
||||
a no-op and the ioctl returns success.
|
||||
|
||||
KVM_DEV_FLIC_AISM
|
||||
modify the adapter-interruption-suppression mode for a given isc if the
|
||||
|
|
|
|||
|
|
@ -440,6 +440,7 @@ struct kvm_vcpu_arch {
|
|||
bool skey_enabled;
|
||||
/* Indicator if the access registers have been loaded from guest */
|
||||
bool acrs_loaded;
|
||||
bool initialized;
|
||||
struct kvm_s390_pv_vcpu pv;
|
||||
union diag318_info diag318_info;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
|
|
@ -476,6 +477,11 @@ struct s390_map_info {
|
|||
__u64 guest_addr;
|
||||
__u64 addr;
|
||||
struct page *page;
|
||||
/*
|
||||
* True if the page is long-term pinned. False if long-term pinning
|
||||
* failed and this entry exists only to preserve MAP/UNMAP symmetry.
|
||||
*/
|
||||
bool pinned;
|
||||
};
|
||||
|
||||
struct s390_io_adapter {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@
|
|||
#define MCCK_CODE_SYSTEM_DAMAGE BIT(63)
|
||||
#define MCCK_CODE_EXT_DAMAGE BIT(63 - 5)
|
||||
#define MCCK_CODE_CP BIT(63 - 9)
|
||||
#define MCCK_CODE_CK BIT(63 - 11)
|
||||
#define MCCK_CODE_STG_ERROR BIT(63 - 16)
|
||||
#define MCCK_CODE_STG_KEY_ERROR BIT(63 - 18)
|
||||
#define MCCK_CODE_STG_DEGRAD BIT(63 - 19)
|
||||
|
|
@ -33,6 +34,8 @@
|
|||
#define MCCK_CODE_FC_VALID BIT(63 - 43)
|
||||
#define MCCK_CODE_CPU_TIMER_VALID BIT(63 - 46)
|
||||
|
||||
#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE | MCCK_CODE_CK)
|
||||
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
union mci {
|
||||
|
|
|
|||
|
|
@ -344,8 +344,7 @@ static void notrace s390_backup_mcck_info(struct pt_regs *regs)
|
|||
|
||||
sie_page = container_of(sie_block, struct sie_page, sie_block);
|
||||
mcck_backup = &sie_page->mcck_info;
|
||||
mcck_backup->mcic = get_lowcore()->mcck_interruption_code &
|
||||
~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE);
|
||||
mcck_backup->mcic = get_lowcore()->mcck_interruption_code & ~MCCK_CODE_NO_GUEST;
|
||||
mcck_backup->ext_damage_code = get_lowcore()->external_damage_code;
|
||||
mcck_backup->failing_storage_address = get_lowcore()->failing_storage_address;
|
||||
}
|
||||
|
|
@ -357,8 +356,6 @@ NOKPROBE_SYMBOL(s390_backup_mcck_info);
|
|||
#define ED_STP_ISLAND 6 /* External damage STP island check */
|
||||
#define ED_STP_SYNC 7 /* External damage STP sync check */
|
||||
|
||||
#define MCCK_CODE_NO_GUEST (MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE)
|
||||
|
||||
/*
|
||||
* machine check handler.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -570,6 +570,8 @@ static long dat_crste_walk_range(gfn_t start, gfn_t end, struct crst_table *tabl
|
|||
else if (walk->ops->pte_entry)
|
||||
rc = dat_pte_walk_range(max(start, cur), min(end, next),
|
||||
dereference_pmd(crste.pmd), walk);
|
||||
if (rc)
|
||||
break;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
|
@ -720,9 +722,12 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep)
|
||||
if (!ptep) {
|
||||
if (!oldkey)
|
||||
oldkey = &prev;
|
||||
return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey,
|
||||
nq, mr, mc);
|
||||
}
|
||||
|
||||
old = pgste_get_lock(ptep);
|
||||
pgste = old;
|
||||
|
|
@ -732,6 +737,7 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
pgste.fp = skey.fp;
|
||||
pgste.gc = skey.c;
|
||||
pgste.gr = skey.r;
|
||||
prev.skey = 0;
|
||||
|
||||
if (!ptep->h.i) {
|
||||
rc = page_cond_set_storage_key(pte_origin(*ptep), skey, &prev, nq, mr, mc);
|
||||
|
|
@ -753,13 +759,15 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
return rc;
|
||||
}
|
||||
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn)
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
|
||||
{
|
||||
union pgste pgste, old;
|
||||
union crste *crstep;
|
||||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
skey->skey = 0;
|
||||
|
||||
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
@ -769,21 +777,23 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn)
|
|||
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
return 0;
|
||||
return page_reset_referenced(large_crste_to_phys(*crstep, gfn));
|
||||
skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1;
|
||||
return 0;
|
||||
}
|
||||
old = pgste_get_lock(ptep);
|
||||
pgste = old;
|
||||
|
||||
if (!ptep->h.i) {
|
||||
rc = page_reset_referenced(pte_origin(*ptep));
|
||||
pgste.hr = rc >> 1;
|
||||
skey->skey = page_reset_referenced(pte_origin(*ptep)) << 1;
|
||||
pgste.hr = skey->r;
|
||||
}
|
||||
rc |= (pgste.gr << 1) | pgste.gc;
|
||||
skey->r |= pgste.gr;
|
||||
skey->c |= pgste.gc;
|
||||
pgste.gr = 0;
|
||||
|
||||
dat_update_ptep_sd(old, pgste, ptep);
|
||||
pgste_set_unlock(ptep, pgste);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
|
|
@ -844,6 +854,7 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
|
|||
struct slot_priv *p = walk->priv;
|
||||
union crste dummy = { .val = p->token };
|
||||
union pte new_pte, pte = READ_ONCE(*ptep);
|
||||
union pgste pgste;
|
||||
|
||||
new_pte = _PTE_TOK(dummy.tok.type, dummy.tok.par);
|
||||
|
||||
|
|
@ -851,7 +862,11 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
|
|||
if (pte.val == new_pte.val)
|
||||
return 0;
|
||||
|
||||
dat_ptep_xchg(ptep, new_pte, gfn, walk->asce, false);
|
||||
pgste = pgste_get_lock(ptep);
|
||||
pgste = __dat_ptep_xchg(ptep, pgste, new_pte, gfn, walk->asce, false);
|
||||
pgste.cmma_d = 0;
|
||||
pgste_set_unlock(ptep, pgste);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -909,11 +924,8 @@ static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgst
|
|||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < n; i++) {
|
||||
if (!pgstes[i].pcl)
|
||||
break;
|
||||
for (i = 0; i < n; i++)
|
||||
pgste_set_unlock(first + i, pgstes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgstes)
|
||||
|
|
@ -926,7 +938,7 @@ static bool pgste_get_trylock_multiple(union pte *first, int n, union pgste *pgs
|
|||
}
|
||||
if (i == n)
|
||||
return true;
|
||||
pgste_set_unlock_multiple(first, n, pgstes);
|
||||
pgste_set_unlock_multiple(first, i, pgstes);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -537,7 +537,7 @@ int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gf
|
|||
union skey skey, bool nq);
|
||||
int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,
|
||||
union skey skey, union skey *oldkey, bool nq, bool mr, bool mc);
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn);
|
||||
int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey);
|
||||
long dat_reset_skeys(union asce asce, gfn_t start);
|
||||
|
||||
unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param);
|
||||
|
|
|
|||
|
|
@ -91,9 +91,9 @@ int kvm_s390_faultin_gfn(struct kvm_vcpu *vcpu, struct kvm *kvm, struct guest_fa
|
|||
/* Access outside memory, addressing exception. */
|
||||
if (is_noslot_pfn(f->pfn))
|
||||
return PGM_ADDRESSING;
|
||||
/* Signal pending: try again. */
|
||||
if (f->pfn == KVM_PFN_ERR_SIGPENDING)
|
||||
return -EAGAIN;
|
||||
/* Fatal signal pending: bail out. */
|
||||
if (is_sigpending_pfn(f->pfn))
|
||||
return -EINTR;
|
||||
/* Check if it's read-only memory; don't try to actually handle that case. */
|
||||
if (f->pfn == KVM_PFN_ERR_RO_FAULT)
|
||||
return -EOPNOTSUPP;
|
||||
|
|
|
|||
|
|
@ -1374,8 +1374,13 @@ struct gmap *gmap_create_shadow(struct kvm_s390_mmu_cache *mc, struct gmap *pare
|
|||
/* Only allow one real-space gmap shadow. */
|
||||
list_for_each_entry(sg, &parent->children, list) {
|
||||
if (sg->guest_asce.r) {
|
||||
scoped_guard(write_lock, &parent->kvm->mmu_lock)
|
||||
if (write_trylock(&parent->kvm->mmu_lock)) {
|
||||
gmap_unshadow(sg);
|
||||
write_unlock(&parent->kvm->mmu_lock);
|
||||
} else {
|
||||
gmap_put(new);
|
||||
return ERR_PTR(-EAGAIN);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,7 +184,7 @@ static int __import_wp_info(struct kvm_vcpu *vcpu,
|
|||
if (wp_info->len < 0 || wp_info->len > MAX_WP_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
wp_info->old_data = kmalloc(bp_data->len, GFP_KERNEL_ACCOUNT);
|
||||
wp_info->old_data = kmalloc(wp_info->len, GFP_KERNEL_ACCOUNT);
|
||||
if (!wp_info->old_data)
|
||||
return -ENOMEM;
|
||||
/* try to backup the original value */
|
||||
|
|
@ -252,7 +252,7 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
|
|||
ret = __import_wp_info(vcpu, &bp_data[i],
|
||||
&wp_info[nr_wp]);
|
||||
if (ret)
|
||||
goto error;
|
||||
goto error_wp;
|
||||
nr_wp++;
|
||||
break;
|
||||
case KVM_HW_BP:
|
||||
|
|
@ -267,7 +267,12 @@ int kvm_s390_import_bp_data(struct kvm_vcpu *vcpu,
|
|||
vcpu->arch.guestdbg.hw_bp_info = bp_info;
|
||||
vcpu->arch.guestdbg.nr_hw_wp = nr_wp;
|
||||
vcpu->arch.guestdbg.hw_wp_info = wp_info;
|
||||
kfree(bp_data);
|
||||
return 0;
|
||||
|
||||
error_wp:
|
||||
while (nr_wp--)
|
||||
kfree(wp_info[nr_wp].old_data);
|
||||
error:
|
||||
kfree(bp_data);
|
||||
kfree(wp_info);
|
||||
|
|
|
|||
|
|
@ -45,13 +45,16 @@ static struct kvm_s390_gib *gib;
|
|||
static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
|
||||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
union esca_sigp_ctrl sigp_ctrl;
|
||||
|
||||
if (!kvm_s390_test_cpuflags(vcpu, CPUSTAT_ECALL_PEND))
|
||||
return 0;
|
||||
if (kvm_is_ucontrol(vcpu->kvm))
|
||||
return 0;
|
||||
|
||||
BUG_ON(!kvm_s390_use_sca_entries());
|
||||
|
||||
sigp_ctrl = sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
if (src_id)
|
||||
*src_id = sigp_ctrl.scn;
|
||||
|
||||
|
|
@ -60,13 +63,16 @@ static int sca_ext_call_pending(struct kvm_vcpu *vcpu, int *src_id)
|
|||
|
||||
static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
|
||||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
union esca_sigp_ctrl old_val, new_val = {.scn = src_id, .c = 1};
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl *sigp_ctrl;
|
||||
int expect, rc;
|
||||
|
||||
BUG_ON(!kvm_s390_use_sca_entries());
|
||||
if (kvm_is_ucontrol(vcpu->kvm))
|
||||
return -EINVAL;
|
||||
|
||||
sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
old_val = READ_ONCE(*sigp_ctrl);
|
||||
old_val.c = 0;
|
||||
|
||||
|
|
@ -84,10 +90,13 @@ static int sca_inject_ext_call(struct kvm_vcpu *vcpu, int src_id)
|
|||
static void sca_clear_ext_call(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
union esca_sigp_ctrl *sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
union esca_sigp_ctrl *sigp_ctrl;
|
||||
|
||||
if (!kvm_s390_use_sca_entries())
|
||||
if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized || kvm_is_ucontrol(vcpu->kvm))
|
||||
return;
|
||||
|
||||
/* Initialize after the above check, to prevent going out of bounds */
|
||||
sigp_ctrl = &sca->cpu[vcpu->vcpu_id].sigp_ctrl;
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_ECALL_PEND);
|
||||
|
||||
WRITE_ONCE(sigp_ctrl->value, 0);
|
||||
|
|
@ -264,6 +273,11 @@ static inline int gisa_tac_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
|
|||
return test_and_clear_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *) gisa);
|
||||
}
|
||||
|
||||
static inline int gisa_test_ipm_gisc(struct kvm_s390_gisa *gisa, u32 gisc)
|
||||
{
|
||||
return test_bit_inv(IPM_BIT_OFFSET + gisc, (unsigned long *)gisa);
|
||||
}
|
||||
|
||||
static inline unsigned long pending_irqs_no_gisa(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long pending = vcpu->kvm->arch.float_int.pending_irqs |
|
||||
|
|
@ -1541,23 +1555,21 @@ static int __inject_set_prefix(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
|||
}
|
||||
|
||||
#define KVM_S390_STOP_SUPP_FLAGS (KVM_S390_STOP_FLAG_STORE_STATUS)
|
||||
static int __inject_sigp_stop(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
static int __inject_sigp_stop(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq, bool *storestatus)
|
||||
{
|
||||
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
||||
struct kvm_s390_stop_info *stop = &li->irq.stop;
|
||||
int rc = 0;
|
||||
|
||||
vcpu->stat.inject_stop_signal++;
|
||||
trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_SIGP_STOP, 0, 0);
|
||||
|
||||
if (irq->u.stop.flags & ~KVM_S390_STOP_SUPP_FLAGS)
|
||||
return -EINVAL;
|
||||
|
||||
if (is_vcpu_stopped(vcpu)) {
|
||||
if (irq->u.stop.flags & KVM_S390_STOP_FLAG_STORE_STATUS)
|
||||
rc = kvm_s390_store_status_unloaded(vcpu,
|
||||
KVM_S390_STORE_STATUS_NOADDR);
|
||||
return rc;
|
||||
if (!(irq->u.stop.flags & KVM_S390_STOP_FLAG_STORE_STATUS))
|
||||
return 0;
|
||||
*storestatus = true;
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
if (test_and_set_bit(IRQ_PEND_SIGP_STOP, &li->pending_irqs))
|
||||
|
|
@ -2093,7 +2105,7 @@ void kvm_s390_clear_stop_irq(struct kvm_vcpu *vcpu)
|
|||
spin_unlock(&li->lock);
|
||||
}
|
||||
|
||||
static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq, bool *storestatus)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
|
@ -2105,7 +2117,7 @@ static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
|||
rc = __inject_set_prefix(vcpu, irq);
|
||||
break;
|
||||
case KVM_S390_SIGP_STOP:
|
||||
rc = __inject_sigp_stop(vcpu, irq);
|
||||
rc = __inject_sigp_stop(vcpu, irq, storestatus);
|
||||
break;
|
||||
case KVM_S390_RESTART:
|
||||
rc = __inject_sigp_restart(vcpu);
|
||||
|
|
@ -2141,11 +2153,16 @@ static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
|||
int kvm_s390_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq)
|
||||
{
|
||||
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
||||
bool storestatus = false;
|
||||
int rc;
|
||||
|
||||
spin_lock(&li->lock);
|
||||
rc = do_inject_vcpu(vcpu, irq);
|
||||
rc = do_inject_vcpu(vcpu, irq, &storestatus);
|
||||
spin_unlock(&li->lock);
|
||||
|
||||
if (rc == -EWOULDBLOCK && storestatus)
|
||||
rc = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
|
||||
if (!rc)
|
||||
kvm_s390_vcpu_wakeup(vcpu);
|
||||
return rc;
|
||||
|
|
@ -2233,7 +2250,7 @@ static int get_all_floating_irqs(struct kvm *kvm, u8 __user *usrbuf, u64 len)
|
|||
ret = -ENOMEM;
|
||||
goto out_nolock;
|
||||
}
|
||||
if (gisa_tac_ipm_gisc(gi->origin, i)) {
|
||||
if (gisa_test_ipm_gisc(gi->origin, i)) {
|
||||
irq = (struct kvm_s390_irq *) &buf[n];
|
||||
irq->type = KVM_S390_INT_IO(1, 0, 0, 0);
|
||||
irq->u.io.io_int_word = isc_to_int_word(i);
|
||||
|
|
@ -2520,8 +2537,22 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
map->addr = host_addr;
|
||||
map->page = pin_map_page(kvm, host_addr, FOLL_LONGTERM);
|
||||
if (!map->page) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
/*
|
||||
* Long-term pinning may fail for memory types such as file-backed
|
||||
* memory. Verify that short-term pinning succeeds so that the
|
||||
* non-atomic irqfd path can handle interrupt injection.
|
||||
*/
|
||||
map->page = pin_map_page(kvm, host_addr, 0);
|
||||
if (!map->page) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
unpin_user_page(map->page);
|
||||
map->page = NULL;
|
||||
map->pinned = false;
|
||||
/* Add an entry to preserve MAP/UNMAP symmetry. */
|
||||
} else {
|
||||
map->pinned = true;
|
||||
}
|
||||
spin_lock_irqsave(&adapter->maps_lock, flags);
|
||||
if (adapter->nr_maps < MAX_S390_ADAPTER_MAPS) {
|
||||
|
|
@ -2532,7 +2563,7 @@ static int kvm_s390_adapter_map(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
ret = -EINVAL;
|
||||
}
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
if (ret)
|
||||
if (ret && map->page)
|
||||
unpin_user_page(map->page);
|
||||
out:
|
||||
if (ret)
|
||||
|
|
@ -2546,6 +2577,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
struct s390_map_info *map, *tmp, *map_to_free;
|
||||
struct page *map_page_to_put = NULL;
|
||||
u64 map_addr_to_mark = 0;
|
||||
bool map_pinned = false;
|
||||
unsigned long flags;
|
||||
int found = 0, idx;
|
||||
|
||||
|
|
@ -2560,6 +2592,7 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
list_del(&map->list);
|
||||
map_page_to_put = map->page;
|
||||
map_addr_to_mark = map->guest_addr;
|
||||
map_pinned = map->pinned;
|
||||
map_to_free = map;
|
||||
break;
|
||||
}
|
||||
|
|
@ -2568,11 +2601,18 @@ static int kvm_s390_adapter_unmap(struct kvm *kvm, unsigned int id, __u64 addr)
|
|||
|
||||
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);
|
||||
if (map_pinned) {
|
||||
/*
|
||||
* Only long-term pinned pages need to be marked dirty
|
||||
* and released. Fallback entries exist only for
|
||||
* MAP/UNMAP symmetry.
|
||||
*/
|
||||
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;
|
||||
|
|
@ -2598,11 +2638,13 @@ void kvm_s390_unmap_all_adapters(struct kvm *kvm)
|
|||
|
||||
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);
|
||||
if (map->pinned) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -2929,8 +2971,11 @@ static struct s390_map_info *get_map_info(struct s390_io_adapter *adapter,
|
|||
return NULL;
|
||||
|
||||
list_for_each_entry(map, &adapter->maps, list) {
|
||||
if (map->addr == addr)
|
||||
if (map->addr == addr) {
|
||||
if (!map->pinned)
|
||||
return NULL;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
@ -2976,7 +3021,7 @@ static int adapter_indicators_set(struct kvm *kvm,
|
|||
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))
|
||||
if (!summary_page)
|
||||
return -1;
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
map = page_address(summary_page);
|
||||
|
|
@ -3071,9 +3116,7 @@ static int set_adapter_int(struct kvm_kernel_irq_routing_entry *e,
|
|||
void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu,
|
||||
struct mcck_volatile_info *mcck_info)
|
||||
{
|
||||
struct kvm_s390_interrupt_info inti;
|
||||
struct kvm_s390_irq irq;
|
||||
struct kvm_s390_mchk_info *mchk;
|
||||
struct kvm_s390_irq irq = {};
|
||||
union mci mci;
|
||||
__u64 cr14 = 0; /* upper bits are not used */
|
||||
int rc;
|
||||
|
|
@ -3092,20 +3135,14 @@ void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu,
|
|||
if (mci.w)
|
||||
cr14 |= CR14_WARNING_SUBMASK;
|
||||
|
||||
mchk = mci.ck ? &inti.mchk : &irq.u.mchk;
|
||||
mchk->cr14 = cr14;
|
||||
mchk->mcic = mcck_info->mcic;
|
||||
mchk->ext_damage_code = mcck_info->ext_damage_code;
|
||||
mchk->failing_storage_address = mcck_info->failing_storage_address;
|
||||
if (mci.ck) {
|
||||
/* Inject the floating machine check */
|
||||
inti.type = KVM_S390_MCHK;
|
||||
rc = __inject_vm(vcpu->kvm, &inti);
|
||||
} else {
|
||||
/* Inject the machine check to specified vcpu */
|
||||
irq.type = KVM_S390_MCHK;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &irq);
|
||||
}
|
||||
irq.u.mchk.cr14 = cr14;
|
||||
irq.u.mchk.mcic = mcck_info->mcic;
|
||||
irq.u.mchk.ext_damage_code = mcck_info->ext_damage_code;
|
||||
irq.u.mchk.failing_storage_address = mcck_info->failing_storage_address;
|
||||
|
||||
/* Inject the machine check to specified vcpu */
|
||||
irq.type = KVM_S390_MCHK;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &irq);
|
||||
WARN_ON_ONCE(rc);
|
||||
}
|
||||
|
||||
|
|
@ -3160,7 +3197,8 @@ int kvm_set_msi(struct kvm_kernel_irq_routing_entry *e, struct kvm *kvm,
|
|||
int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, void __user *irqstate, int len)
|
||||
{
|
||||
struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int;
|
||||
struct kvm_s390_irq *buf;
|
||||
struct kvm_s390_irq *buf __free(kvfree) = NULL;
|
||||
bool tmp, storestatus = false;
|
||||
int r = 0;
|
||||
int n;
|
||||
|
||||
|
|
@ -3168,32 +3206,34 @@ int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, void __user *irqstate, int len
|
|||
if (!buf)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user((void *) buf, irqstate, len)) {
|
||||
r = -EFAULT;
|
||||
goto out_free;
|
||||
if (copy_from_user((void *)buf, irqstate, len))
|
||||
return -EFAULT;
|
||||
|
||||
scoped_guard(spinlock, &li->lock) {
|
||||
/*
|
||||
* Don't allow setting the interrupt state
|
||||
* when there are already interrupts pending
|
||||
*/
|
||||
if (li->pending_irqs)
|
||||
return -EBUSY;
|
||||
|
||||
for (n = 0; n < len / sizeof(*buf); n++) {
|
||||
tmp = false;
|
||||
r = do_inject_vcpu(vcpu, &buf[n], &tmp);
|
||||
if (r == -EWOULDBLOCK && tmp) {
|
||||
storestatus = true;
|
||||
r = 0;
|
||||
}
|
||||
if (r)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow setting the interrupt state
|
||||
* when there are already interrupts pending
|
||||
*/
|
||||
spin_lock(&li->lock);
|
||||
if (li->pending_irqs) {
|
||||
r = -EBUSY;
|
||||
goto out_unlock;
|
||||
if (storestatus) {
|
||||
n = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
return r ? r : n;
|
||||
}
|
||||
|
||||
for (n = 0; n < len / sizeof(*buf); n++) {
|
||||
r = do_inject_vcpu(vcpu, &buf[n]);
|
||||
if (r)
|
||||
break;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&li->lock);
|
||||
out_free:
|
||||
vfree(buf);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -561,16 +561,17 @@ static void __kvm_s390_exit(void)
|
|||
static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op,
|
||||
unsigned long addr, union skey skey)
|
||||
{
|
||||
union asce asce = kvm->arch.gmap->asce;
|
||||
gfn_t gfn = gpa_to_gfn(addr);
|
||||
union asce asce;
|
||||
int r;
|
||||
|
||||
guard(read_lock)(&kvm->mmu_lock);
|
||||
asce = kvm->arch.gmap->asce;
|
||||
|
||||
switch (op) {
|
||||
case KVM_S390_KEYOP_SSKE:
|
||||
r = dat_cond_set_storage_key(mc, asce, gfn, skey, &skey, 0, 0, 0);
|
||||
if (r >= 0)
|
||||
if (r == 0 || r == 1)
|
||||
return skey.skey;
|
||||
break;
|
||||
case KVM_S390_KEYOP_ISKE:
|
||||
|
|
@ -579,14 +580,14 @@ static int kvm_s390_keyop(struct kvm_s390_mmu_cache *mc, struct kvm *kvm, int op
|
|||
return skey.skey;
|
||||
break;
|
||||
case KVM_S390_KEYOP_RRBE:
|
||||
r = dat_reset_reference_bit(asce, gfn);
|
||||
if (r > 0)
|
||||
return r << 1;
|
||||
r = dat_reset_reference_bit(asce, gfn, &skey);
|
||||
if (!r)
|
||||
return skey.skey;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return r;
|
||||
return r > 0 ? -EFAULT : r;
|
||||
}
|
||||
|
||||
/* Section: device related */
|
||||
|
|
@ -1218,8 +1219,8 @@ static void kvm_s390_sync_request_broadcast(struct kvm *kvm, int req)
|
|||
|
||||
/*
|
||||
* Must be called with kvm->srcu held to avoid races on memslots, and with
|
||||
* kvm->slots_lock to avoid races with ourselves, kvm_s390_vm_stop_migration(),
|
||||
* and kvm_s390_get_cmma_bits().
|
||||
* kvm->slots_arch_lock to avoid races with ourselves,
|
||||
* kvm_s390_vm_stop_migration(), and kvm_s390_get_cmma_bits().
|
||||
*/
|
||||
static int kvm_s390_vm_start_migration(struct kvm *kvm)
|
||||
{
|
||||
|
|
@ -1264,7 +1265,7 @@ static int kvm_s390_vm_start_migration(struct kvm *kvm)
|
|||
}
|
||||
|
||||
/*
|
||||
* Must be called with kvm->slots_lock to avoid races with ourselves,
|
||||
* Must be called with kvm->slots_arch_lock to avoid races with ourselves,
|
||||
* kvm_s390_vm_start_migration() and kvm_s390_get_cmma_bits().
|
||||
*/
|
||||
static int kvm_s390_vm_stop_migration(struct kvm *kvm)
|
||||
|
|
@ -1279,8 +1280,10 @@ static int kvm_s390_vm_stop_migration(struct kvm *kvm)
|
|||
* PGSTEs might have cmma_d set.
|
||||
*/
|
||||
WRITE_ONCE(kvm->arch.migration_mode, 0);
|
||||
if (kvm->arch.use_cmma)
|
||||
kvm_s390_sync_request_broadcast(kvm, KVM_REQ_STOP_MIGRATION);
|
||||
if (!kvm->arch.use_cmma)
|
||||
return 0;
|
||||
|
||||
kvm_s390_sync_request_broadcast(kvm, KVM_REQ_STOP_MIGRATION);
|
||||
/* Clear cmma_d on all existing PGSTEs and set cmma_dirty_pages to 0. */
|
||||
gmap_set_cmma_all_clean(kvm->arch.gmap);
|
||||
atomic64_set(&kvm->arch.cmma_dirty_pages, 0);
|
||||
|
|
@ -1297,7 +1300,9 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,
|
|||
{
|
||||
int res = -ENXIO;
|
||||
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
guard(srcu)(&kvm->srcu);
|
||||
guard(mutex)(&kvm->slots_arch_lock);
|
||||
|
||||
switch (attr->attr) {
|
||||
case KVM_S390_VM_MIGRATION_START:
|
||||
res = kvm_s390_vm_start_migration(kvm);
|
||||
|
|
@ -1308,7 +1313,6 @@ static int kvm_s390_vm_set_migration(struct kvm *kvm,
|
|||
default:
|
||||
break;
|
||||
}
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -2211,7 +2215,7 @@ static int kvm_s390_get_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
|
|||
}
|
||||
|
||||
kvfree(keys);
|
||||
return r;
|
||||
return r <= 0 ? r : -EFAULT;
|
||||
}
|
||||
|
||||
static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
|
||||
|
|
@ -2273,7 +2277,7 @@ static int kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args)
|
|||
kvm_s390_free_mmu_cache(mc);
|
||||
out:
|
||||
kvfree(keys);
|
||||
return r;
|
||||
return r <= 0 ? r : -EFAULT;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -2383,7 +2387,7 @@ static int kvm_s390_set_cmma_bits(struct kvm *kvm,
|
|||
|
||||
set_bit(GMAP_FLAG_USES_CMM, &kvm->arch.gmap->flags);
|
||||
|
||||
return r;
|
||||
return r <= 0 ? r : -EFAULT;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -2931,6 +2935,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|||
case KVM_S390_INTERRUPT: {
|
||||
struct kvm_s390_interrupt s390int;
|
||||
|
||||
r = -EINVAL;
|
||||
if (kvm_is_ucontrol(kvm))
|
||||
break;
|
||||
r = -EFAULT;
|
||||
if (copy_from_user(&s390int, argp, sizeof(s390int)))
|
||||
break;
|
||||
|
|
@ -2995,9 +3002,8 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|||
r = -EFAULT;
|
||||
if (copy_from_user(&args, argp, sizeof(args)))
|
||||
break;
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
r = kvm_s390_get_cmma_bits(kvm, &args);
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
scoped_guard(mutex, &kvm->slots_arch_lock)
|
||||
r = kvm_s390_get_cmma_bits(kvm, &args);
|
||||
if (!r) {
|
||||
r = copy_to_user(argp, &args, sizeof(args));
|
||||
if (r)
|
||||
|
|
@ -3011,9 +3017,9 @@ int kvm_arch_vm_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg)
|
|||
r = -EFAULT;
|
||||
if (copy_from_user(&args, argp, sizeof(args)))
|
||||
break;
|
||||
mutex_lock(&kvm->slots_lock);
|
||||
mutex_lock(&kvm->slots_arch_lock);
|
||||
r = kvm_s390_set_cmma_bits(kvm, &args);
|
||||
mutex_unlock(&kvm->slots_lock);
|
||||
mutex_unlock(&kvm->slots_arch_lock);
|
||||
break;
|
||||
}
|
||||
case KVM_S390_PV_COMMAND: {
|
||||
|
|
@ -3244,7 +3250,8 @@ static void kvm_s390_crypto_init(struct kvm *kvm)
|
|||
|
||||
static void sca_dispose(struct kvm *kvm)
|
||||
{
|
||||
free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca));
|
||||
if (kvm->arch.sca)
|
||||
free_pages_exact(kvm->arch.sca, sizeof(*kvm->arch.sca));
|
||||
kvm->arch.sca = NULL;
|
||||
}
|
||||
|
||||
|
|
@ -3403,6 +3410,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu)
|
|||
trace_kvm_s390_destroy_vcpu(vcpu->vcpu_id);
|
||||
kvm_s390_clear_local_irqs(vcpu);
|
||||
kvm_clear_async_pf_completion_queue(vcpu);
|
||||
kvm_s390_clear_bp_data(vcpu);
|
||||
if (!kvm_is_ucontrol(vcpu->kvm))
|
||||
sca_del_vcpu(vcpu);
|
||||
kvm_s390_update_topology_change_report(vcpu->kvm, 1);
|
||||
|
|
@ -3458,7 +3466,7 @@ static void sca_del_vcpu(struct kvm_vcpu *vcpu)
|
|||
{
|
||||
struct esca_block *sca = vcpu->kvm->arch.sca;
|
||||
|
||||
if (!kvm_s390_use_sca_entries())
|
||||
if (!kvm_s390_use_sca_entries() || !vcpu->arch.initialized)
|
||||
return;
|
||||
|
||||
clear_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn);
|
||||
|
|
@ -3478,8 +3486,8 @@ static void sca_add_vcpu(struct kvm_vcpu *vcpu)
|
|||
if (!kvm_s390_use_sca_entries())
|
||||
return;
|
||||
|
||||
WRITE_ONCE(sca->cpu[vcpu->vcpu_id].sda, virt_to_phys(vcpu->arch.sie_block));
|
||||
set_bit_inv(vcpu->vcpu_id, (unsigned long *)sca->mcn);
|
||||
sca->cpu[vcpu->vcpu_id].sda = virt_to_phys(vcpu->arch.sie_block);
|
||||
}
|
||||
|
||||
static int sca_can_add_vcpu(struct kvm *kvm, unsigned int id)
|
||||
|
|
@ -3610,6 +3618,9 @@ void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu)
|
|||
if (test_kvm_facility(vcpu->kvm, 74) || vcpu->kvm->arch.user_instr0 ||
|
||||
vcpu->kvm->arch.user_operexec)
|
||||
vcpu->arch.sie_block->ictl |= ICTL_OPEREXC;
|
||||
|
||||
/* Pairs with smp_load_acquire() in kvm_arch_vcpu_ioctl_run() and kvm_arch_vcpu_ioctl() */
|
||||
smp_store_release(&vcpu->arch.initialized, true);
|
||||
}
|
||||
|
||||
static bool kvm_has_pckmo_subfunc(struct kvm *kvm, unsigned long nr)
|
||||
|
|
@ -3671,7 +3682,8 @@ static void kvm_s390_vcpu_crypto_setup(struct kvm_vcpu *vcpu)
|
|||
|
||||
void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
|
||||
if (vcpu->arch.sie_block->cbrlo)
|
||||
free_page((unsigned long)phys_to_virt(vcpu->arch.sie_block->cbrlo));
|
||||
vcpu->arch.sie_block->cbrlo = 0;
|
||||
}
|
||||
|
||||
|
|
@ -3789,21 +3801,21 @@ int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id)
|
|||
return 0;
|
||||
}
|
||||
|
||||
DEFINE_FREE(sie_page, struct sie_page *, if (_T) free_page((unsigned long)(_T)))
|
||||
|
||||
int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
struct sie_page *sie_page;
|
||||
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
|
||||
struct sie_page *sie_page __free(sie_page) = NULL;
|
||||
int rc;
|
||||
|
||||
BUILD_BUG_ON(sizeof(struct sie_page) != 4096);
|
||||
vcpu->arch.mc = kvm_s390_new_mmu_cache();
|
||||
if (!vcpu->arch.mc)
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
sie_page = (struct sie_page *) get_zeroed_page(GFP_KERNEL_ACCOUNT);
|
||||
if (!sie_page) {
|
||||
kvm_s390_free_mmu_cache(vcpu->arch.mc);
|
||||
vcpu->arch.mc = NULL;
|
||||
if (!sie_page)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
vcpu->arch.sie_block = &sie_page->sie_block;
|
||||
vcpu->arch.sie_block->itdba = virt_to_phys(&sie_page->itdb);
|
||||
|
|
@ -3845,10 +3857,9 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||
vcpu->run->kvm_valid_regs |= KVM_SYNC_FPRS;
|
||||
|
||||
if (kvm_is_ucontrol(vcpu->kvm)) {
|
||||
rc = -ENOMEM;
|
||||
vcpu->arch.gmap = gmap_new_child(vcpu->kvm->arch.gmap, -1UL);
|
||||
if (!vcpu->arch.gmap)
|
||||
goto out_free_sie_block;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
VM_EVENT(vcpu->kvm, 3, "create cpu %d at 0x%p, sie block at 0x%p",
|
||||
|
|
@ -3856,20 +3867,19 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu)
|
|||
trace_kvm_s390_create_vcpu(vcpu->vcpu_id, vcpu, vcpu->arch.sie_block);
|
||||
|
||||
rc = kvm_s390_vcpu_setup(vcpu);
|
||||
if (rc)
|
||||
goto out_ucontrol_uninit;
|
||||
if (rc) {
|
||||
if (kvm_is_ucontrol(vcpu->kvm)) {
|
||||
scoped_guard(spinlock, &vcpu->kvm->arch.gmap->children_lock)
|
||||
gmap_remove_child(vcpu->arch.gmap);
|
||||
vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
vcpu->arch.mc = no_free_ptr(mc);
|
||||
sie_page = NULL;
|
||||
kvm_s390_update_topology_change_report(vcpu->kvm, 1);
|
||||
return 0;
|
||||
|
||||
out_ucontrol_uninit:
|
||||
if (kvm_is_ucontrol(vcpu->kvm)) {
|
||||
gmap_remove_child(vcpu->arch.gmap);
|
||||
vcpu->arch.gmap = gmap_put(vcpu->arch.gmap);
|
||||
}
|
||||
out_free_sie_block:
|
||||
free_page((unsigned long)(vcpu->arch.sie_block));
|
||||
return rc;
|
||||
}
|
||||
|
||||
int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu)
|
||||
|
|
@ -4239,8 +4249,10 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu,
|
|||
/* enforce guest PER */
|
||||
kvm_s390_set_cpuflags(vcpu, CPUSTAT_P);
|
||||
|
||||
if (dbg->control & KVM_GUESTDBG_USE_HW_BP)
|
||||
rc = kvm_s390_import_bp_data(vcpu, dbg);
|
||||
if (dbg->control & KVM_GUESTDBG_USE_HW_BP) {
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
rc = kvm_s390_import_bp_data(vcpu, dbg);
|
||||
}
|
||||
} else {
|
||||
kvm_s390_clear_cpuflags(vcpu, CPUSTAT_P);
|
||||
vcpu->arch.guestdbg.last_bp = 0;
|
||||
|
|
@ -4465,8 +4477,8 @@ 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)
|
||||
{
|
||||
struct kvm_s390_interrupt inti;
|
||||
struct kvm_s390_irq irq;
|
||||
struct kvm_s390_interrupt inti = {};
|
||||
struct kvm_s390_irq irq = {};
|
||||
struct kvm_s390_interrupt_info *inti_mem = NULL;
|
||||
int ret = 0;
|
||||
|
||||
|
|
@ -5036,6 +5048,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
kvm_run->kvm_dirty_regs & ~KVM_SYNC_S390_VALID_FIELDS)
|
||||
return -EINVAL;
|
||||
|
||||
/* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */
|
||||
if (!smp_load_acquire(&vcpu->arch.initialized))
|
||||
return -EINVAL;
|
||||
|
||||
vcpu_load(vcpu);
|
||||
|
||||
if (guestdbg_exit_pending(vcpu)) {
|
||||
|
|
@ -5056,7 +5072,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
pr_err_ratelimited("can't run stopped vcpu %d\n",
|
||||
vcpu->vcpu_id);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
goto out_sigset;
|
||||
}
|
||||
|
||||
kernel_fpu_begin(&fpu, KERNEL_FPC | KERNEL_VXR);
|
||||
|
|
@ -5086,9 +5102,11 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu)
|
|||
store_regs(vcpu);
|
||||
kernel_fpu_end(&fpu, KERNEL_FPC | KERNEL_VXR);
|
||||
|
||||
vcpu->stat.exit_userspace++;
|
||||
|
||||
out_sigset:
|
||||
kvm_sigset_deactivate(vcpu);
|
||||
|
||||
vcpu->stat.exit_userspace++;
|
||||
out:
|
||||
vcpu_put(vcpu);
|
||||
return rc;
|
||||
|
|
@ -5437,18 +5455,22 @@ long kvm_arch_vcpu_unlocked_ioctl(struct file *filp, unsigned int ioctl,
|
|||
|
||||
if (copy_from_user(&s390irq, argp, sizeof(s390irq)))
|
||||
return -EFAULT;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
break;
|
||||
}
|
||||
case KVM_S390_INTERRUPT: {
|
||||
struct kvm_s390_interrupt s390int;
|
||||
struct kvm_s390_irq s390irq = {};
|
||||
|
||||
if (kvm_is_ucontrol(vcpu->kvm))
|
||||
return -EINVAL;
|
||||
if (copy_from_user(&s390int, argp, sizeof(s390int)))
|
||||
return -EFAULT;
|
||||
if (s390int_to_s390irq(&s390int, &s390irq))
|
||||
return -EINVAL;
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
rc = kvm_s390_inject_vcpu(vcpu, &s390irq);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
|
@ -5520,6 +5542,10 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
|||
long r;
|
||||
u16 rc, rrc;
|
||||
|
||||
/* Pairs with smp_store_release() in kvm_arch_vcpu_postcreate() */
|
||||
if (!smp_load_acquire(&vcpu->arch.initialized))
|
||||
return -EINVAL;
|
||||
|
||||
vcpu_load(vcpu);
|
||||
|
||||
switch (ioctl) {
|
||||
|
|
@ -5713,7 +5739,7 @@ long kvm_arch_vcpu_ioctl(struct file *filp,
|
|||
r = kvm_s390_handle_pv_vcpu_dump(vcpu, &cmd);
|
||||
|
||||
/* Always copy over UV rc / rrc data */
|
||||
if (copy_to_user((__u8 __user *)argp, &cmd.rc,
|
||||
if (copy_to_user(argp + offsetof(struct kvm_pv_cmd, rc), &cmd.rc,
|
||||
sizeof(cmd.rc) + sizeof(cmd.rrc)))
|
||||
r = -EFAULT;
|
||||
break;
|
||||
|
|
@ -5791,14 +5817,30 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
union pgste pgste;
|
||||
|
||||
pgste = pgste_get_lock(ptep);
|
||||
if (pgste.cmma_d) {
|
||||
pgste.cmma_d = 0;
|
||||
atomic64_dec(walk->priv);
|
||||
}
|
||||
pgste_set_unlock(ptep, pgste);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void kvm_arch_commit_memory_region(struct kvm *kvm,
|
||||
struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
struct kvm_s390_mmu_cache *mc = NULL;
|
||||
const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
|
||||
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
|
||||
int rc = 0;
|
||||
|
||||
guard(mutex)(&kvm->slots_arch_lock);
|
||||
|
||||
if (change == KVM_MR_FLAGS_ONLY)
|
||||
return;
|
||||
|
||||
|
|
@ -5809,6 +5851,12 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
|
|||
}
|
||||
|
||||
scoped_guard(write_lock, &kvm->mmu_lock) {
|
||||
if (kvm->arch.migration_mode && kvm->arch.use_cmma && old) {
|
||||
_dat_walk_gfn_range(old->base_gfn, old->base_gfn + old->npages,
|
||||
kvm->arch.gmap->asce, &ops, DAT_WALK_IGN_HOLES,
|
||||
&kvm->arch.cmma_dirty_pages);
|
||||
}
|
||||
|
||||
switch (change) {
|
||||
case KVM_MR_DELETE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
|
|
@ -5830,7 +5878,6 @@ void kvm_arch_commit_memory_region(struct kvm *kvm,
|
|||
out:
|
||||
if (rc)
|
||||
pr_warn("failed to commit memory region\n");
|
||||
kvm_s390_free_mmu_cache(mc);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -190,40 +190,61 @@ static int kvm_zpci_clear_airq(struct zpci_dev *zdev)
|
|||
return cc ? -EIO : 0;
|
||||
}
|
||||
|
||||
static inline void unaccount_mem(unsigned long nr_pages)
|
||||
static inline void unaccount_mem(struct kvm_zdev *kzdev, unsigned long nr_pages)
|
||||
{
|
||||
struct user_struct *user = get_uid(current_user());
|
||||
struct user_struct *user = kzdev->user_account;
|
||||
struct mm_struct *mm_account = kzdev->mm_account;
|
||||
|
||||
if (user)
|
||||
if (user) {
|
||||
atomic_long_sub(nr_pages, &user->locked_vm);
|
||||
if (current->mm)
|
||||
atomic64_sub(nr_pages, ¤t->mm->pinned_vm);
|
||||
free_uid(user);
|
||||
kzdev->user_account = NULL;
|
||||
}
|
||||
|
||||
if (mm_account) {
|
||||
atomic64_sub(nr_pages, &mm_account->pinned_vm);
|
||||
mmdrop(mm_account);
|
||||
kzdev->mm_account = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static inline int account_mem(unsigned long nr_pages)
|
||||
static inline int account_mem(struct kvm_zdev *kzdev, unsigned long nr_pages)
|
||||
{
|
||||
struct user_struct *user = get_uid(current_user());
|
||||
unsigned long page_limit, cur_pages, new_pages;
|
||||
int rc = 0;
|
||||
|
||||
page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
|
||||
|
||||
cur_pages = atomic_long_read(&user->locked_vm);
|
||||
do {
|
||||
new_pages = cur_pages + nr_pages;
|
||||
if (new_pages > page_limit)
|
||||
return -ENOMEM;
|
||||
if (new_pages > page_limit) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
} while (!atomic_long_try_cmpxchg(&user->locked_vm, &cur_pages, new_pages));
|
||||
|
||||
atomic64_add(nr_pages, ¤t->mm->pinned_vm);
|
||||
if (current->mm) {
|
||||
mmgrab(current->mm);
|
||||
atomic64_add(nr_pages, ¤t->mm->pinned_vm);
|
||||
}
|
||||
|
||||
kzdev->user_account = user;
|
||||
kzdev->mm_account = current->mm;
|
||||
|
||||
return 0;
|
||||
|
||||
out:
|
||||
free_uid(user);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
||||
bool assist)
|
||||
{
|
||||
struct page *pages[1], *aibv_page, *aisb_page = NULL;
|
||||
unsigned int msi_vecs, idx;
|
||||
unsigned int msi_vecs, idx, size;
|
||||
struct zpci_gaite *gaite;
|
||||
unsigned long hva, bit;
|
||||
struct kvm *kvm;
|
||||
|
|
@ -237,6 +258,10 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
if (zdev->gisa == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* AIF already enabled for the device */
|
||||
if (zdev->kzdev->fib.fmt0.aibv != 0)
|
||||
return -EINVAL;
|
||||
|
||||
kvm = zdev->kzdev->kvm;
|
||||
msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi);
|
||||
|
||||
|
|
@ -246,6 +271,14 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
return gisc;
|
||||
|
||||
/* Replace AIBV address */
|
||||
size = BITS_TO_LONGS(msi_vecs + fib->fmt0.aibvo) * sizeof(unsigned long);
|
||||
npages = DIV_ROUND_UP((fib->fmt0.aibv & ~PAGE_MASK) + size, PAGE_SIZE);
|
||||
/* AIBV cannot span more than 1 page */
|
||||
if (npages > 1) {
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv));
|
||||
npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages);
|
||||
|
|
@ -261,6 +294,12 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
|
||||
/* Pin the guest AISB if one was specified */
|
||||
if (fib->fmt0.sum == 1) {
|
||||
/* AISB must be dword aligned */
|
||||
if (fib->fmt0.aisb & 0x7) {
|
||||
rc = -EINVAL;
|
||||
goto unpin1;
|
||||
}
|
||||
|
||||
idx = srcu_read_lock(&kvm->srcu);
|
||||
hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb));
|
||||
npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM,
|
||||
|
|
@ -275,20 +314,28 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
}
|
||||
|
||||
/* Account for pinned pages, roll back on failure */
|
||||
if (account_mem(pcount))
|
||||
rc = account_mem(zdev->kzdev, pcount);
|
||||
if (rc)
|
||||
goto unpin2;
|
||||
|
||||
/* AISB must be allocated before we can fill in GAITE */
|
||||
mutex_lock(&aift->aift_lock);
|
||||
bit = airq_iv_alloc_bit(aift->sbv);
|
||||
if (bit == -1UL)
|
||||
if (bit == -1UL) {
|
||||
rc = -ENOMEM;
|
||||
goto unlock;
|
||||
}
|
||||
zdev->aisb = bit; /* store the summary bit number */
|
||||
zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA |
|
||||
AIRQ_IV_BITLOCK |
|
||||
AIRQ_IV_GUESTVEC,
|
||||
phys_to_virt(fib->fmt0.aibv));
|
||||
|
||||
if (!zdev->aibv) {
|
||||
rc = -ENOMEM;
|
||||
goto free_aisb;
|
||||
}
|
||||
|
||||
spin_lock_irq(&aift->gait_lock);
|
||||
gaite = aift->gait + zdev->aisb;
|
||||
|
||||
|
|
@ -300,27 +347,50 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
|
||||
gaite->gisc = fib->fmt0.isc;
|
||||
gaite->count++;
|
||||
gaite->aisbo = fib->fmt0.aisbo;
|
||||
gaite->aisb = virt_to_phys(page_address(aisb_page) + (fib->fmt0.aisb &
|
||||
~PAGE_MASK));
|
||||
if (fib->fmt0.sum == 1) {
|
||||
gaite->aisbo = fib->fmt0.aisbo;
|
||||
gaite->aisb = virt_to_phys(page_address(aisb_page) +
|
||||
(fib->fmt0.aisb & ~PAGE_MASK));
|
||||
} else {
|
||||
gaite->aisbo = 0;
|
||||
gaite->aisb = 0;
|
||||
}
|
||||
aift->kzdev[zdev->aisb] = zdev->kzdev;
|
||||
spin_unlock_irq(&aift->gait_lock);
|
||||
|
||||
/* Update guest FIB for re-issue */
|
||||
fib->fmt0.aisbo = zdev->aisb & 63;
|
||||
fib->fmt0.aisb = virt_to_phys(aift->sbv->vector) + (zdev->aisb / 64) * 8;
|
||||
fib->fmt0.isc = gisc;
|
||||
|
||||
/* Save some guest fib values in the host for later use */
|
||||
zdev->kzdev->fib.fmt0.isc = fib->fmt0.isc;
|
||||
zdev->kzdev->fib.fmt0.isc = gisc;
|
||||
zdev->kzdev->fib.fmt0.aibv = fib->fmt0.aibv;
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
|
||||
/* Issue the clp to setup the irq now */
|
||||
rc = kvm_zpci_set_airq(zdev);
|
||||
return rc;
|
||||
if (!rc) {
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* Start cleanup */
|
||||
zdev->kzdev->fib.fmt0.isc = 0;
|
||||
zdev->kzdev->fib.fmt0.aibv = 0;
|
||||
|
||||
spin_lock_irq(&aift->gait_lock);
|
||||
gaite->count--;
|
||||
gaite->aisb = 0;
|
||||
gaite->gisc = 0;
|
||||
gaite->aisbo = 0;
|
||||
gaite->gisa = 0;
|
||||
aift->kzdev[zdev->aisb] = NULL;
|
||||
spin_unlock_irq(&aift->gait_lock);
|
||||
|
||||
airq_iv_release(zdev->aibv);
|
||||
zdev->aibv = NULL;
|
||||
|
||||
free_aisb:
|
||||
airq_iv_free_bit(aift->sbv, zdev->aisb);
|
||||
zdev->aisb = 0;
|
||||
unlock:
|
||||
if (pcount > 0)
|
||||
unaccount_mem(zdev->kzdev, pcount);
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
unpin2:
|
||||
if (fib->fmt0.sum == 1)
|
||||
|
|
@ -328,6 +398,7 @@ static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib,
|
|||
unpin1:
|
||||
unpin_user_page(aibv_page);
|
||||
out:
|
||||
kvm_s390_gisc_unregister(kvm, fib->fmt0.isc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
@ -390,7 +461,7 @@ static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force)
|
|||
pcount++;
|
||||
}
|
||||
if (pcount > 0)
|
||||
unaccount_mem(pcount);
|
||||
unaccount_mem(kzdev, pcount);
|
||||
out:
|
||||
mutex_unlock(&aift->aift_lock);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,6 +22,8 @@ struct kvm_zdev {
|
|||
struct kvm *kvm;
|
||||
struct zpci_fib fib;
|
||||
struct list_head entry;
|
||||
struct user_struct *user_account;
|
||||
struct mm_struct *mm_account;
|
||||
};
|
||||
|
||||
struct zpci_gaite {
|
||||
|
|
|
|||
|
|
@ -289,6 +289,7 @@ static int handle_iske(struct kvm_vcpu *vcpu)
|
|||
static int handle_rrbe(struct kvm_vcpu *vcpu)
|
||||
{
|
||||
unsigned long gaddr;
|
||||
union skey skey;
|
||||
int reg1, reg2;
|
||||
int rc;
|
||||
|
||||
|
|
@ -307,12 +308,12 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
|
|||
gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
|
||||
gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
|
||||
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
|
||||
rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr));
|
||||
rc = dat_reset_reference_bit(vcpu->arch.gmap->asce, gpa_to_gfn(gaddr), &skey);
|
||||
if (rc > 0)
|
||||
return kvm_s390_inject_program_int(vcpu, rc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
kvm_s390_set_psw_cc(vcpu, rc);
|
||||
kvm_s390_set_psw_cc(vcpu, (skey.skey >> 1) & 3);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -1260,8 +1261,9 @@ static int handle_essa(struct kvm_vcpu *vcpu)
|
|||
/* Retry the ESSA instruction */
|
||||
kvm_s390_retry_instr(vcpu);
|
||||
} else {
|
||||
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
|
||||
i = __do_essa(vcpu, orc);
|
||||
scoped_guard(mutex, &vcpu->kvm->slots_arch_lock)
|
||||
scoped_guard(read_lock, &vcpu->kvm->mmu_lock)
|
||||
i = __do_essa(vcpu, orc);
|
||||
if (i < 0)
|
||||
return i;
|
||||
/* Account for the possible extra cbrl entry */
|
||||
|
|
|
|||
|
|
@ -242,6 +242,28 @@ static void kvm_s390_clear_pv_state(struct kvm *kvm)
|
|||
kvm->arch.pv.guest_len = 0;
|
||||
kvm->arch.pv.stor_base = 0;
|
||||
kvm->arch.pv.stor_var = NULL;
|
||||
if (kvm->arch.pv.dumping) {
|
||||
kvm_s390_vcpu_unblock_all(kvm);
|
||||
kvm->arch.pv.dumping = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void kvm_s390_pv_dispose_cpu(struct kvm_vcpu *vcpu, bool free_stor_base)
|
||||
{
|
||||
if (free_stor_base)
|
||||
free_pages(vcpu->arch.pv.stor_base, get_order(uv_info.guest_cpu_stor_len));
|
||||
free_page((unsigned long)sida_addr(vcpu->arch.sie_block));
|
||||
vcpu->arch.sie_block->pv_handle_cpu = 0;
|
||||
vcpu->arch.sie_block->pv_handle_config = 0;
|
||||
memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
|
||||
vcpu->arch.sie_block->sdf = 0;
|
||||
/*
|
||||
* The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).
|
||||
* Use the reset value of gbea to avoid leaking the kernel pointer of
|
||||
* the just freed sida.
|
||||
*/
|
||||
vcpu->arch.sie_block->gbea = 1;
|
||||
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
|
||||
}
|
||||
|
||||
int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
||||
|
|
@ -258,24 +280,9 @@ int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
|||
WARN_ONCE(cc, "protvirt destroy cpu failed rc %x rrc %x", *rc, *rrc);
|
||||
|
||||
/* Intended memory leak for something that should never happen. */
|
||||
if (!cc)
|
||||
free_pages(vcpu->arch.pv.stor_base,
|
||||
get_order(uv_info.guest_cpu_stor_len));
|
||||
kvm_s390_pv_dispose_cpu(vcpu, !cc);
|
||||
|
||||
free_page((unsigned long)sida_addr(vcpu->arch.sie_block));
|
||||
vcpu->arch.sie_block->pv_handle_cpu = 0;
|
||||
vcpu->arch.sie_block->pv_handle_config = 0;
|
||||
memset(&vcpu->arch.pv, 0, sizeof(vcpu->arch.pv));
|
||||
vcpu->arch.sie_block->sdf = 0;
|
||||
/*
|
||||
* The sidad field (for sdf == 2) is now the gbea field (for sdf == 0).
|
||||
* Use the reset value of gbea to avoid leaking the kernel pointer of
|
||||
* the just freed sida.
|
||||
*/
|
||||
vcpu->arch.sie_block->gbea = 1;
|
||||
kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu);
|
||||
|
||||
return cc ? EIO : 0;
|
||||
return cc ? -EIO : 0;
|
||||
}
|
||||
|
||||
int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
||||
|
|
@ -319,9 +326,7 @@ int kvm_s390_pv_create_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc)
|
|||
uvcb.header.rrc);
|
||||
|
||||
if (cc) {
|
||||
u16 dummy;
|
||||
|
||||
kvm_s390_pv_destroy_cpu(vcpu, &dummy, &dummy);
|
||||
kvm_s390_pv_dispose_cpu(vcpu, true);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
|
@ -809,7 +814,7 @@ static int unpack_one(struct kvm *kvm, unsigned long addr, u64 tweak,
|
|||
return -EAGAIN;
|
||||
}
|
||||
|
||||
if (ret && ret != -EAGAIN)
|
||||
if (ret && ret != -EAGAIN && ret != -EINTR)
|
||||
KVM_UV_EVENT(kvm, 3, "PROTVIRT VM UNPACK: failed addr %llx with rc %x rrc %x",
|
||||
uvcb.gaddr, *rc, *rrc);
|
||||
return ret;
|
||||
|
|
|
|||
|
|
@ -172,6 +172,7 @@ static int setup_apcb10(struct kvm_vcpu *vcpu, struct kvm_s390_apcb1 *apcb_s,
|
|||
sizeof(struct kvm_s390_apcb0)))
|
||||
return -EFAULT;
|
||||
|
||||
memset(apcb_s, 0, sizeof(*apcb_s));
|
||||
apcb_s->apm[0] = apcb_h->apm[0] & tmp.apm[0];
|
||||
apcb_s->aqm[0] = apcb_h->aqm[0] & tmp.aqm[0] & 0xffff000000000000UL;
|
||||
apcb_s->adm[0] = apcb_h->adm[0] & tmp.adm[0] & 0xffff000000000000UL;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <linux/nospec.h>
|
||||
|
||||
#include "vfio_ccw_private.h"
|
||||
|
||||
|
|
@ -24,11 +25,20 @@ static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private,
|
|||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
if (copy_to_user(buf, (void *)region + pos, count))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = count;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
|
@ -48,6 +58,12 @@ static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private,
|
|||
if (!mutex_trylock(&private->io_mutex))
|
||||
return -EAGAIN;
|
||||
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
if (copy_from_user((void *)region + pos, buf, count)) {
|
||||
ret = -EFAULT;
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/vfio.h>
|
||||
#include "vfio_ccw_private.h"
|
||||
|
||||
|
|
@ -26,6 +27,13 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private,
|
|||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
|
||||
if (cio_update_schib(sch)) {
|
||||
|
|
@ -85,19 +93,30 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
|
|||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_crw_region *region;
|
||||
struct vfio_ccw_crw *crw;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (pos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
crw = list_first_entry_or_null(&private->crw,
|
||||
struct vfio_ccw_crw, next);
|
||||
|
||||
if (crw)
|
||||
list_del(&crw->next);
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
region = private->region[i].data;
|
||||
/* Drop CRW lock while copying to userspace */
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
if (crw)
|
||||
memcpy(®ion->crw, &crw->crw, sizeof(region->crw));
|
||||
|
|
@ -108,14 +127,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
|
|||
ret = count;
|
||||
|
||||
region->crw = 0;
|
||||
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
kfree(crw);
|
||||
|
||||
/* Notify the guest if more CRWs are on our queue */
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
if (!list_empty(&private->crw) && private->crw_trigger)
|
||||
eventfd_signal(private->crw_trigger);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
out:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,7 @@ static void convert_ccw0_to_ccw1(struct ccw1 *source, unsigned long len)
|
|||
}
|
||||
|
||||
#define idal_is_2k(_cp) (!(_cp)->orb.cmd.c64 || (_cp)->orb.cmd.i2k)
|
||||
#define get_idaw_size(_cp) ((_cp)->orb.cmd.c64 ? sizeof(u64) : sizeof(u32))
|
||||
|
||||
/*
|
||||
* Helpers to operate ccwchain.
|
||||
|
|
@ -332,6 +333,7 @@ static struct ccwchain *ccwchain_alloc(struct channel_program *cp, int len)
|
|||
goto out_err;
|
||||
|
||||
list_add_tail(&chain->next, &cp->ccwchain_list);
|
||||
cp->ccwchain_count++;
|
||||
|
||||
return chain;
|
||||
|
||||
|
|
@ -376,11 +378,9 @@ static void ccwchain_cda_free(struct ccwchain *chain, int idx)
|
|||
static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
|
||||
{
|
||||
struct ccw1 *ccw = cp->guest_cp;
|
||||
int cnt = 0;
|
||||
|
||||
do {
|
||||
cnt++;
|
||||
int cnt;
|
||||
|
||||
for (cnt = 1; cnt <= CCWCHAIN_LEN_MAX; cnt++, ccw++) {
|
||||
/*
|
||||
* We want to keep counting if the current CCW has the
|
||||
* command-chaining flag enabled, or if it is a TIC CCW
|
||||
|
|
@ -390,15 +390,10 @@ static int ccwchain_calc_length(u64 iova, struct channel_program *cp)
|
|||
* after the TIC, depending on the results of its operation.
|
||||
*/
|
||||
if (!ccw_is_chain(ccw) && !is_tic_within_range(ccw, iova, cnt))
|
||||
break;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
ccw++;
|
||||
} while (cnt < CCWCHAIN_LEN_MAX + 1);
|
||||
|
||||
if (cnt == CCWCHAIN_LEN_MAX + 1)
|
||||
cnt = -EINVAL;
|
||||
|
||||
return cnt;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int tic_target_chain_exists(struct ccw1 *tic, struct channel_program *cp)
|
||||
|
|
@ -441,6 +436,10 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp)
|
|||
if (len < 0)
|
||||
return len;
|
||||
|
||||
/* Limit number of chains in a single channel program */
|
||||
if (cp->ccwchain_count >= CCWCHAIN_COUNT_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
/* Need alloc a new chain for this one. */
|
||||
chain = ccwchain_alloc(cp, len);
|
||||
if (!chain)
|
||||
|
|
@ -455,9 +454,6 @@ static int ccwchain_handle_ccw(dma32_t cda, struct channel_program *cp)
|
|||
/* Loop for tics on this new chain. */
|
||||
ret = ccwchain_loop_tic(chain, cp);
|
||||
|
||||
if (ret)
|
||||
ccwchain_free(chain);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -486,6 +482,23 @@ static int ccwchain_loop_tic(struct ccwchain *chain, struct channel_program *cp)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ccwchain_build_ccws(dma32_t cda, struct channel_program *cp)
|
||||
{
|
||||
struct ccwchain *chain, *temp;
|
||||
int ret;
|
||||
|
||||
ret = ccwchain_handle_ccw(cda, cp);
|
||||
|
||||
if (ret) {
|
||||
/* Cleanup if an error occurred */
|
||||
list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) {
|
||||
ccwchain_free(chain);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int ccwchain_fetch_tic(struct ccw1 *ccw,
|
||||
struct channel_program *cp)
|
||||
{
|
||||
|
|
@ -511,7 +524,8 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int
|
|||
&container_of(cp, struct vfio_ccw_private, cp)->vdev;
|
||||
dma64_t *idaws;
|
||||
dma32_t *idaws_f1;
|
||||
int idal_len = idaw_nr * sizeof(*idaws);
|
||||
u64 first_idaw;
|
||||
int idal_len = idaw_nr * get_idaw_size(cp);
|
||||
int idaw_size = idal_is_2k(cp) ? PAGE_SIZE / 2 : PAGE_SIZE;
|
||||
int idaw_mask = ~(idaw_size - 1);
|
||||
int i, ret;
|
||||
|
|
@ -527,6 +541,18 @@ static dma64_t *get_guest_idal(struct ccw1 *ccw, struct channel_program *cp, int
|
|||
kfree(idaws);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
idaws_f1 = (dma32_t *)idaws;
|
||||
if (cp->orb.cmd.c64)
|
||||
first_idaw = dma64_to_u64(idaws[0]);
|
||||
else
|
||||
first_idaw = dma32_to_u32(idaws_f1[0]);
|
||||
|
||||
/* Unexpected mismatch from earlier read */
|
||||
if (first_idaw != cp->guest_iova) {
|
||||
kfree(idaws);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
} else {
|
||||
/* Fabricate an IDAL based off CCW data address */
|
||||
if (cp->orb.cmd.c64) {
|
||||
|
|
@ -568,7 +594,7 @@ static int ccw_count_idaws(struct ccw1 *ccw,
|
|||
struct vfio_device *vdev =
|
||||
&container_of(cp, struct vfio_ccw_private, cp)->vdev;
|
||||
u64 iova;
|
||||
int size = cp->orb.cmd.c64 ? sizeof(u64) : sizeof(u32);
|
||||
int size = get_idaw_size(cp);
|
||||
int ret;
|
||||
int bytes = 1;
|
||||
|
||||
|
|
@ -592,6 +618,9 @@ static int ccw_count_idaws(struct ccw1 *ccw,
|
|||
iova = dma32_to_u32(ccw->cda);
|
||||
}
|
||||
|
||||
/* Save the read address for later */
|
||||
cp->guest_iova = iova;
|
||||
|
||||
/* Format-1 IDAWs operate on 2K each */
|
||||
if (!cp->orb.cmd.c64)
|
||||
return idal_2k_nr_words((void *)iova, bytes);
|
||||
|
|
@ -731,11 +760,12 @@ int cp_init(struct channel_program *cp, union orb *orb)
|
|||
vdev->dev,
|
||||
"Prefetching channel program even though prefetch not specified in ORB");
|
||||
|
||||
cp->ccwchain_count = 0;
|
||||
INIT_LIST_HEAD(&cp->ccwchain_list);
|
||||
memcpy(&cp->orb, orb, sizeof(*orb));
|
||||
|
||||
/* Build a ccwchain for the first CCW segment */
|
||||
ret = ccwchain_handle_ccw(orb->cmd.cpa, cp);
|
||||
ret = ccwchain_build_ccws(orb->cmd.cpa, cp);
|
||||
|
||||
if (!ret)
|
||||
cp->initialized = true;
|
||||
|
|
@ -947,17 +977,23 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw)
|
|||
*/
|
||||
bool cp_iova_pinned(struct channel_program *cp, u64 iova, u64 length)
|
||||
{
|
||||
struct vfio_ccw_private *private =
|
||||
container_of(cp, struct vfio_ccw_private, cp);
|
||||
struct ccwchain *chain;
|
||||
int i;
|
||||
|
||||
if (!cp->initialized)
|
||||
return false;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
list_for_each_entry(chain, &cp->ccwchain_list, next) {
|
||||
for (i = 0; i < chain->ch_len; i++)
|
||||
if (page_array_iova_pinned(&chain->ch_pa[i], iova, length))
|
||||
if (page_array_iova_pinned(&chain->ch_pa[i], iova, length)) {
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,11 +23,19 @@
|
|||
*/
|
||||
#define CCWCHAIN_LEN_MAX 256
|
||||
|
||||
/*
|
||||
* Maximum number of chains
|
||||
*/
|
||||
#define CCWCHAIN_COUNT_MAX 16
|
||||
|
||||
/**
|
||||
* struct channel_program - manage information for channel program
|
||||
* @ccwchain_list: list head of ccwchains
|
||||
* @orb: orb for the currently processed ssch request
|
||||
* @initialized: whether this instance is actually initialized
|
||||
* @guest_cp: copy of guest channel program
|
||||
* @ccwchain_count: number of channel program segments (linked by TIC)
|
||||
* @guest_iova: first data address of a guest channel program
|
||||
*
|
||||
* @ccwchain_list is the head of a ccwchain list, that contents the
|
||||
* translated result of the guest channel program that pointed out by
|
||||
|
|
@ -38,6 +46,8 @@ struct channel_program {
|
|||
union orb orb;
|
||||
bool initialized;
|
||||
struct ccw1 *guest_cp;
|
||||
unsigned int ccwchain_count;
|
||||
u64 guest_iova;
|
||||
};
|
||||
|
||||
int cp_init(struct channel_program *cp, union orb *orb);
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
|
||||
is_final = !(scsw_actl(&irb->scsw) &
|
||||
(SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT));
|
||||
mutex_lock(&private->io_mutex);
|
||||
if (scsw_is_solicited(&irb->scsw)) {
|
||||
cp_update_scsw(&private->cp, &irb->scsw);
|
||||
if (is_final && private->state == VFIO_CCW_STATE_CP_PENDING) {
|
||||
|
|
@ -99,9 +100,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
cp_is_finished = true;
|
||||
}
|
||||
}
|
||||
mutex_lock(&private->io_mutex);
|
||||
memcpy(private->io_region->irb_area, irb, sizeof(*irb));
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
/*
|
||||
* Reset to IDLE only if processing of a channel program
|
||||
|
|
@ -111,6 +110,7 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
*/
|
||||
if (cp_is_finished)
|
||||
private->state = VFIO_CCW_STATE_IDLE;
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
if (private->io_trigger)
|
||||
eventfd_signal(private->io_trigger);
|
||||
|
|
@ -119,11 +119,25 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
void vfio_ccw_crw_todo(struct work_struct *work)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
unsigned long flags;
|
||||
|
||||
private = container_of(work, struct vfio_ccw_private, crw_work);
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
if (!list_empty(&private->crw) && private->crw_trigger)
|
||||
eventfd_signal(private->crw_trigger);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
}
|
||||
|
||||
void vfio_ccw_notoper_todo(struct work_struct *work)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
|
||||
private = container_of(work, struct vfio_ccw_private, notoper_work);
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
cp_free(&private->cp);
|
||||
mutex_unlock(&private->io_mutex);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -276,6 +290,7 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
|
|||
unsigned int rsid)
|
||||
{
|
||||
struct vfio_ccw_crw *crw;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* If unable to allocate a CRW, just drop the event and
|
||||
|
|
@ -293,7 +308,9 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
|
|||
crw->crw.erc = erc;
|
||||
crw->crw.rsid = rsid;
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
list_add_tail(&crw->next, &private->crw);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
queue_work(vfio_ccw_work_q, &private->crw_work);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -170,8 +170,8 @@ static void fsm_notoper(struct vfio_ccw_private *private,
|
|||
css_sched_sch_todo(sch, SCH_TODO_UNREG);
|
||||
private->state = VFIO_CCW_STATE_NOT_OPER;
|
||||
|
||||
/* This is usually handled during CLOSE event */
|
||||
cp_free(&private->cp);
|
||||
/* This routine could be called from IRQ context, so defer */
|
||||
queue_work(vfio_ccw_work_q, &private->notoper_work);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -410,7 +410,11 @@ static void fsm_close(struct vfio_ccw_private *private,
|
|||
|
||||
private->state = VFIO_CCW_STATE_STANDBY;
|
||||
spin_unlock_irq(&sch->lock);
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
cp_free(&private->cp);
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
return;
|
||||
|
||||
err_unlock:
|
||||
|
|
|
|||
|
|
@ -54,6 +54,8 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev)
|
|||
INIT_LIST_HEAD(&private->crw);
|
||||
INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
|
||||
INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
|
||||
INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo);
|
||||
spin_lock_init(&private->crw_lock);
|
||||
|
||||
private->cp.guest_cp = kzalloc_objs(struct ccw1, CCWCHAIN_LEN_MAX);
|
||||
if (!private->cp.guest_cp)
|
||||
|
|
@ -130,11 +132,28 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
|
|||
struct vfio_ccw_private *private =
|
||||
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||
struct vfio_ccw_crw *crw, *temp;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Ensure these work items are fully drained, so none can
|
||||
* fire after being released.
|
||||
*
|
||||
* notoper_work should have nothing to do here, because only
|
||||
* open devices could have channel_program resources in use
|
||||
* and those would be released during close. Nevertheless,
|
||||
* call flush here as well to be certain anything that was
|
||||
* allocated is freed.
|
||||
*/
|
||||
cancel_work_sync(&private->io_work);
|
||||
cancel_work_sync(&private->crw_work);
|
||||
flush_work(&private->notoper_work);
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
list_for_each_entry_safe(crw, temp, &private->crw, next) {
|
||||
list_del(&crw->next);
|
||||
kfree(crw);
|
||||
}
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
|
||||
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
|
||||
|
|
@ -202,6 +221,19 @@ static void vfio_ccw_mdev_close_device(struct vfio_device *vdev)
|
|||
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
|
||||
|
||||
/*
|
||||
* Ensure these work items are drained, in the event the
|
||||
* device is re-opened instead of released.
|
||||
*
|
||||
* notoper_work needs to be given a chance to run if it
|
||||
* is queued, so any memory associated with the channel
|
||||
* program can be returned.
|
||||
*/
|
||||
cancel_work_sync(&private->io_work);
|
||||
cancel_work_sync(&private->crw_work);
|
||||
flush_work(&private->notoper_work);
|
||||
|
||||
vfio_ccw_unregister_dev_regions(private);
|
||||
}
|
||||
|
||||
|
|
@ -243,6 +275,7 @@ static ssize_t vfio_ccw_mdev_read(struct vfio_device *vdev,
|
|||
return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
|
||||
default:
|
||||
index -= VFIO_CCW_NUM_REGIONS;
|
||||
index = array_index_nospec(index, private->num_regions);
|
||||
return private->region[index].ops->read(private, buf, count,
|
||||
ppos);
|
||||
}
|
||||
|
|
@ -295,6 +328,7 @@ static ssize_t vfio_ccw_mdev_write(struct vfio_device *vdev,
|
|||
return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
|
||||
default:
|
||||
index -= VFIO_CCW_NUM_REGIONS;
|
||||
index = array_index_nospec(index, private->num_regions);
|
||||
return private->region[index].ops->write(private, buf, count,
|
||||
ppos);
|
||||
}
|
||||
|
|
@ -338,11 +372,8 @@ static int vfio_ccw_mdev_ioctl_get_region_info(struct vfio_device *vdev,
|
|||
VFIO_CCW_NUM_REGIONS + private->num_regions)
|
||||
return -EINVAL;
|
||||
|
||||
info->index = array_index_nospec(info->index,
|
||||
VFIO_CCW_NUM_REGIONS +
|
||||
private->num_regions);
|
||||
|
||||
i = info->index - VFIO_CCW_NUM_REGIONS;
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
|
||||
info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
|
||||
info->size = private->region[i].size;
|
||||
|
|
|
|||
|
|
@ -88,7 +88,8 @@ struct vfio_ccw_parent {
|
|||
* @state: internal state of the device
|
||||
* @completion: synchronization helper of the I/O completion
|
||||
* @io_region: MMIO region to input/output I/O arguments/results
|
||||
* @io_mutex: protect against concurrent update of I/O regions
|
||||
* @io_mutex: protect against concurrent update of I/O resources
|
||||
* and @cp lifecycle
|
||||
* @region: additional regions for other subchannel operations
|
||||
* @cmd_region: MMIO region for asynchronous I/O commands other than START
|
||||
* @schib_region: MMIO region for SCHIB information
|
||||
|
|
@ -97,11 +98,14 @@ struct vfio_ccw_parent {
|
|||
* @cp: channel program for the current I/O operation
|
||||
* @irb: irb info received from interrupt
|
||||
* @scsw: scsw info
|
||||
* @crw_lock: serialization of CRW list information
|
||||
* @crw: list of Channel Report Word elements
|
||||
* @io_trigger: eventfd ctx for signaling userspace I/O results
|
||||
* @crw_trigger: eventfd ctx for signaling userspace CRW information
|
||||
* @req_trigger: eventfd ctx for signaling userspace to return device
|
||||
* @io_work: work for deferral process of I/O handling
|
||||
* @crw_work: work for deferral process of CRW handling
|
||||
* @notoper_work: work for deferred processing in not-operational state
|
||||
*/
|
||||
struct vfio_ccw_private {
|
||||
struct vfio_device vdev;
|
||||
|
|
@ -118,6 +122,8 @@ struct vfio_ccw_private {
|
|||
struct channel_program cp;
|
||||
struct irb irb;
|
||||
union scsw scsw;
|
||||
|
||||
spinlock_t crw_lock;
|
||||
struct list_head crw;
|
||||
|
||||
struct eventfd_ctx *io_trigger;
|
||||
|
|
@ -125,12 +131,14 @@ struct vfio_ccw_private {
|
|||
struct eventfd_ctx *req_trigger;
|
||||
struct work_struct io_work;
|
||||
struct work_struct crw_work;
|
||||
struct work_struct notoper_work;
|
||||
} __aligned(8);
|
||||
|
||||
int vfio_ccw_sch_quiesce(struct subchannel *sch)
|
||||
__must_hold(&sch->lock);
|
||||
void vfio_ccw_sch_io_todo(struct work_struct *work);
|
||||
void vfio_ccw_crw_todo(struct work_struct *work);
|
||||
void vfio_ccw_notoper_todo(struct work_struct *work);
|
||||
|
||||
extern struct mdev_driver vfio_ccw_mdev_driver;
|
||||
|
||||
|
|
|
|||
|
|
@ -48,15 +48,19 @@ static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q);
|
|||
* 1. matrix_dev->guests_lock: required to use the KVM pointer to update a KVM
|
||||
* guest's APCB.
|
||||
* 2. kvm->lock: required to update a guest's APCB
|
||||
* 3. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev
|
||||
* 3. kvm->arch.crypto.pqap_hook_rwsem: required to update pqap_hook and
|
||||
* serialize against PQAP intercepts
|
||||
* 4. matrix_dev->mdevs_lock: required to access data stored in a matrix_mdev
|
||||
*
|
||||
* Note: If @kvm is NULL, the KVM lock will not be taken.
|
||||
* Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be taken.
|
||||
*/
|
||||
static inline void get_update_locks_for_kvm(struct kvm *kvm)
|
||||
{
|
||||
mutex_lock(&matrix_dev->guests_lock);
|
||||
if (kvm)
|
||||
if (kvm) {
|
||||
mutex_lock(&kvm->lock);
|
||||
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
}
|
||||
mutex_lock(&matrix_dev->mdevs_lock);
|
||||
}
|
||||
|
||||
|
|
@ -68,16 +72,19 @@ static inline void get_update_locks_for_kvm(struct kvm *kvm)
|
|||
*
|
||||
* The proper unlocking order is:
|
||||
* 1. matrix_dev->mdevs_lock
|
||||
* 2. kvm->lock
|
||||
* 3. matrix_dev->guests_lock
|
||||
* 2. kvm->arch.crypto.pqap_hook_rwsem
|
||||
* 3. kvm->lock
|
||||
* 4. matrix_dev->guests_lock
|
||||
*
|
||||
* Note: If @kvm is NULL, the KVM lock will not be released.
|
||||
* Note: If @kvm is NULL, the KVM lock and pqap_hook_rwsem will not be released.
|
||||
*/
|
||||
static inline void release_update_locks_for_kvm(struct kvm *kvm)
|
||||
{
|
||||
mutex_unlock(&matrix_dev->mdevs_lock);
|
||||
if (kvm)
|
||||
if (kvm) {
|
||||
up_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
mutex_unlock(&kvm->lock);
|
||||
}
|
||||
mutex_unlock(&matrix_dev->guests_lock);
|
||||
}
|
||||
|
||||
|
|
@ -1821,26 +1828,17 @@ static const struct attribute_group *vfio_ap_mdev_attr_groups[] = {
|
|||
static int vfio_ap_mdev_set_kvm(struct ap_matrix_mdev *matrix_mdev,
|
||||
struct kvm *kvm)
|
||||
{
|
||||
struct ap_matrix_mdev *m;
|
||||
|
||||
if (kvm->arch.crypto.crycbd) {
|
||||
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
|
||||
up_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
|
||||
get_update_locks_for_kvm(kvm);
|
||||
|
||||
list_for_each_entry(m, &matrix_dev->mdev_list, node) {
|
||||
if (m != matrix_mdev && m->kvm == kvm) {
|
||||
release_update_locks_for_kvm(kvm);
|
||||
return -EPERM;
|
||||
}
|
||||
if (kvm->arch.crypto.pqap_hook) {
|
||||
release_update_locks_for_kvm(kvm);
|
||||
return -EPERM;
|
||||
}
|
||||
kvm->arch.crypto.pqap_hook = &matrix_mdev->pqap_hook;
|
||||
|
||||
kvm_get_kvm(kvm);
|
||||
matrix_mdev->kvm = kvm;
|
||||
vfio_ap_mdev_update_guest_apcb(matrix_mdev);
|
||||
|
||||
release_update_locks_for_kvm(kvm);
|
||||
}
|
||||
|
||||
|
|
@ -1883,18 +1881,15 @@ static void vfio_ap_mdev_unset_kvm(struct ap_matrix_mdev *matrix_mdev)
|
|||
struct kvm *kvm = matrix_mdev->kvm;
|
||||
|
||||
if (kvm && kvm->arch.crypto.crycbd) {
|
||||
down_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
kvm->arch.crypto.pqap_hook = NULL;
|
||||
up_write(&kvm->arch.crypto.pqap_hook_rwsem);
|
||||
|
||||
get_update_locks_for_kvm(kvm);
|
||||
kvm->arch.crypto.pqap_hook = NULL;
|
||||
|
||||
kvm_arch_crypto_clear_masks(kvm);
|
||||
vfio_ap_mdev_reset_queues(matrix_mdev);
|
||||
kvm_put_kvm(kvm);
|
||||
matrix_mdev->kvm = NULL;
|
||||
|
||||
release_update_locks_for_kvm(kvm);
|
||||
kvm_put_kvm(kvm);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user