mirror of
https://github.com/torvalds/linux.git
synced 2026-10-28 01:26:02 +01:00
KVM: s390: Misc fixes
A bunch of fixes to bugs found in the past weeks. -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEoWuZBM6M3lCBSfTnuARItAMU6BMFAmqaXq0ACgkQuARItAMU 6BN9RBAAoHqwerRDYTXTRaab4loBOvwEu8SaJ4n1gyKqlhAQ3jum19R1f2+UGYyb YOSRDs9/M7Dwj4W74bIyCHUTCSnpFxxt2i+wOODRIjFzfw1hiVN2i7OZbGB+1NkO ZXHH2/1IYl20aT+LtsLJJAtkmG1s73Awg71+TxvQKrEZKiIDeTOKAUc6qXXRj+2a I0iRAWwIaBC0sKovzOCWF2uOctqXTLk8BzO8wZKhYNrC06+SW4EdoRxgtPsJcyph KfmzIdrK+wV3ZBAi1GAs2wvZd7gWNvdD7OoxiBJvBIW0FxhRrc6fiicn/nkAX2TG YB3heKLDeRjc3wrUZJt5NraHBDGsuXp59JAKHU8zwO2lFRctw/jjqKzqkfl9OdFN fYZkg1CHGY0nwdT4fFSTNTXJrzBc1NxtU6AA+pvf95w8sGXR/jgA8plIeVdaaj3P 6TwgxIP54JBSJibzpAzbl8cnWNVcU6KTmtQRAek7Dw+N6DShq7woES+Cixq/LgQG 4XWYPoBuF6PYVfPeTVIBhzRw5nxvNSJyHaoSaXAiSLZOrSj2dKMpK0ypGtnZAggf /Ae1eZJgYyv28LVwXEe4OCynmIqQWB/tiVwHUjQuyh9nwTj/wGxjetsH2IeMB7uz sP7rm7T9m7FoZBs9i8/wUhXJu13W/giesxXOZDxvtnmgEdYpm0g= =ZSeQ -----END PGP SIGNATURE----- Merge tag 'kvm-s390-master-7.3-1' of https://git.kernel.org/pub/scm/linux/kernel/git/kvms390/linux into HEAD KVM: s390: Misc fixes A bunch of fixes to bugs found in the past weeks.
This commit is contained in:
commit
d599822bdb
|
|
@ -825,7 +825,7 @@ static int find_secret_in_page(const u8 secret_id[UV_SECRET_ID_LEN],
|
|||
{
|
||||
u16 i;
|
||||
|
||||
for (i = 0; i < list->total_num_secrets; i++) {
|
||||
for (i = 0; i < list->num_secr_stored; i++) {
|
||||
if (memcmp(secret_id, list->secrets[i].id, UV_SECRET_ID_LEN) == 0) {
|
||||
*secret = list->secrets[i].hdr;
|
||||
return 0;
|
||||
|
|
@ -846,11 +846,14 @@ int uv_find_secret(const u8 secret_id[UV_SECRET_ID_LEN],
|
|||
struct uv_secret_list *list,
|
||||
struct uv_secret_list_item_hdr *secret)
|
||||
{
|
||||
u16 start_idx = 0;
|
||||
u16 start_idx;
|
||||
u16 list_rc;
|
||||
int ret;
|
||||
|
||||
list->next_secret_idx = 0;
|
||||
|
||||
do {
|
||||
start_idx = list->next_secret_idx;
|
||||
uv_list_secrets(list, start_idx, &list_rc, NULL);
|
||||
if (list_rc != UVC_RC_EXECUTED && list_rc != UVC_RC_MORE_DATA) {
|
||||
if (list_rc == UVC_RC_INV_CMD)
|
||||
|
|
@ -861,7 +864,6 @@ int uv_find_secret(const u8 secret_id[UV_SECRET_ID_LEN],
|
|||
ret = find_secret_in_page(secret_id, list, secret);
|
||||
if (ret == 0)
|
||||
return ret;
|
||||
start_idx = list->next_secret_idx;
|
||||
} while (list_rc == UVC_RC_MORE_DATA && start_idx < list->next_secret_idx);
|
||||
|
||||
return -ENOENT;
|
||||
|
|
|
|||
|
|
@ -621,17 +621,20 @@ int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey)
|
|||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
again:
|
||||
skey->skey = 0;
|
||||
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep) {
|
||||
union crste crste;
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
crste = READ_ONCE(*crstep);
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
if (!crste_leaf(crste) && !crste.h.i)
|
||||
goto again;
|
||||
if (!crste.s.fc1.pr)
|
||||
return 0;
|
||||
|
||||
skey->skey = page_get_storage_key(large_crste_to_phys(crste, gfn));
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -662,13 +665,20 @@ int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gf
|
|||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
again:
|
||||
rc = dat_entry_walk(mc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
|
||||
&crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep) {
|
||||
page_set_storage_key(large_crste_to_phys(*crstep, gfn), skey.skey, !nq);
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
/* A large page has been split concurrently, try again */
|
||||
if (!crste_leaf(crste))
|
||||
goto again;
|
||||
|
||||
page_set_storage_key(large_crste_to_phys(crste, gfn), skey.skey, !nq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -718,15 +728,22 @@ int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gf
|
|||
union pte *ptep;
|
||||
int rc;
|
||||
|
||||
again:
|
||||
rc = dat_entry_walk(mmc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
|
||||
&crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!ptep) {
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
/* A large page has been split concurrently, try again */
|
||||
if (!crste_leaf(crste))
|
||||
goto again;
|
||||
if (!oldkey)
|
||||
oldkey = &prev;
|
||||
return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey,
|
||||
|
||||
return page_cond_set_storage_key(large_crste_to_phys(crste, gfn), skey, oldkey,
|
||||
nq, mr, mc);
|
||||
}
|
||||
|
||||
|
|
@ -768,7 +785,7 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
|
|||
int rc;
|
||||
|
||||
skey->skey = 0;
|
||||
|
||||
again:
|
||||
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
|
@ -776,9 +793,12 @@ int dat_reset_reference_bit(union asce asce, gfn_t gfn, union skey *skey)
|
|||
if (!ptep) {
|
||||
union crste crste = READ_ONCE(*crstep);
|
||||
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
/* A large page has been split concurrently, try again */
|
||||
if (!crste_leaf(crste) && !crste.h.i)
|
||||
goto again;
|
||||
if (!crste.s.fc1.pr)
|
||||
return 0;
|
||||
skey->skey = page_reset_referenced(large_crste_to_phys(*crstep, gfn)) << 1;
|
||||
skey->skey = page_reset_referenced(large_crste_to_phys(crste, gfn)) << 1;
|
||||
return 0;
|
||||
}
|
||||
old = pgste_get_lock(ptep);
|
||||
|
|
@ -846,19 +866,12 @@ long dat_reset_skeys(union asce asce, gfn_t start)
|
|||
}
|
||||
#endif /* KVM_S390_MANAGES_S390_GUEST */
|
||||
|
||||
struct slot_priv {
|
||||
unsigned long token;
|
||||
struct kvm_s390_mmu_cache *mc;
|
||||
};
|
||||
|
||||
static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
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);
|
||||
new_pte = walk->priv ? _PTE_EMPTY : _PTE_TOK(_DAT_TOKEN_PIC, PGM_ADDRESSING);
|
||||
|
||||
/* Table entry already in the desired state. */
|
||||
if (pte.val == new_pte.val)
|
||||
|
|
@ -875,10 +888,9 @@ static long _dat_slot_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_wal
|
|||
static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
union crste new_crste, crste = READ_ONCE(*crstep);
|
||||
struct slot_priv *p = walk->priv;
|
||||
struct kvm_s390_mmu_cache *mc = walk->priv;
|
||||
|
||||
new_crste.val = p->token;
|
||||
new_crste.h.tt = crste.h.tt;
|
||||
new_crste = mc ? _CRSTE_EMPTY(crste.h.tt) : _CRSTE_HOLE(crste.h.tt);
|
||||
|
||||
/* Table entry already in the desired state. */
|
||||
if (crste.val == new_crste.val)
|
||||
|
|
@ -902,7 +914,10 @@ static long _dat_slot_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct d
|
|||
if (!crste.h.fc && !crste.h.i)
|
||||
return 0;
|
||||
/* Split (install a lower level table), and handle things there. */
|
||||
return dat_split_crste(p->mc, crstep, gfn, walk->asce, false);
|
||||
if (mc)
|
||||
return dat_split_crste(mc, crstep, gfn, walk->asce, false);
|
||||
/* A large page should never cross memslots boundaries */
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static const struct dat_walk_ops dat_slot_ops = {
|
||||
|
|
@ -910,16 +925,10 @@ static const struct dat_walk_ops dat_slot_ops = {
|
|||
.crste_ops = { _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, _dat_slot_crste, },
|
||||
};
|
||||
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
|
||||
u16 type, u16 param)
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end)
|
||||
{
|
||||
struct slot_priv priv = {
|
||||
.token = _CRSTE_TOK(0, type, param).val,
|
||||
.mc = mc,
|
||||
};
|
||||
|
||||
return _dat_walk_gfn_range(start, end, asce, &dat_slot_ops,
|
||||
DAT_WALK_IGN_HOLES | DAT_WALK_ANY, &priv);
|
||||
DAT_WALK_IGN_HOLES | DAT_WALK_ANY, mc);
|
||||
}
|
||||
|
||||
static void pgste_set_unlock_multiple(union pte *first, int n, union pgste *pgstes)
|
||||
|
|
|
|||
|
|
@ -547,8 +547,7 @@ long dat_reset_skeys(union asce asce, gfn_t start);
|
|||
unsigned long dat_get_ptval(struct page_table *table, struct ptval_param param);
|
||||
void dat_set_ptval(struct page_table *table, struct ptval_param param, unsigned long val);
|
||||
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end,
|
||||
u16 type, u16 param);
|
||||
int dat_set_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start, gfn_t end);
|
||||
|
||||
#if KVM_S390_MANAGES_S390_GUEST
|
||||
int dat_set_prefix_notif_bit(union asce asce, gfn_t gfn);
|
||||
|
|
@ -973,16 +972,15 @@ static inline int get_level(union crste *crstep, union pte *ptep)
|
|||
return ptep ? TABLE_TYPE_PAGE_TABLE : crstep->h.tt;
|
||||
}
|
||||
|
||||
static inline int dat_delete_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start,
|
||||
unsigned long npages)
|
||||
static inline int dat_delete_slot(union asce asce, gfn_t start, unsigned long npages)
|
||||
{
|
||||
return dat_set_slot(mc, asce, start, start + npages, _DAT_TOKEN_PIC, PGM_ADDRESSING);
|
||||
return dat_set_slot(NULL, asce, start, start + npages);
|
||||
}
|
||||
|
||||
static inline int dat_create_slot(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t start,
|
||||
unsigned long npages)
|
||||
{
|
||||
return dat_set_slot(mc, asce, start, start + npages, _DAT_TOKEN_NONE, 0);
|
||||
return dat_set_slot(mc, asce, start, start + npages);
|
||||
}
|
||||
|
||||
static inline bool crste_is_ucas(union crste crste)
|
||||
|
|
|
|||
|
|
@ -994,11 +994,13 @@ static long _destroy_pages_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct da
|
|||
static long _destroy_pages_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
|
||||
{
|
||||
phys_addr_t origin, cur, end;
|
||||
union crste crste;
|
||||
|
||||
if (!crstep->h.fc || !crstep->s.fc1.pr)
|
||||
crste = READ_ONCE(*crstep);
|
||||
if (!crste.h.fc || !crste.s.fc1.pr)
|
||||
return 0;
|
||||
|
||||
origin = crste_origin_large(*crstep);
|
||||
origin = crste_origin_large(crste);
|
||||
cur = ((max(gfn, walk->start) - gfn) << PAGE_SHIFT) + origin;
|
||||
end = ((min(next, walk->end) - gfn) << PAGE_SHIFT) + origin;
|
||||
for ( ; cur < end; cur += PAGE_SIZE)
|
||||
|
|
|
|||
|
|
@ -47,6 +47,9 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change)
|
||||
{
|
||||
struct kvm_s390_mmu_cache *mc __free(kvm_s390_mmu_cache) = NULL;
|
||||
int rc = 0;
|
||||
|
||||
if (kvm_is_ucontrol(kvm) && new && new->id < KVM_USER_MEM_SLOTS)
|
||||
return -EINVAL;
|
||||
|
||||
|
|
@ -61,6 +64,10 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
* and munmap() stuff in this slot after doing this call at any
|
||||
* time.
|
||||
*/
|
||||
if (change != KVM_MR_MOVE && change != KVM_MR_CREATE) {
|
||||
WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (new->userspace_addr & ~PAGE_MASK)
|
||||
return -EINVAL;
|
||||
if ((new->base_gfn + new->npages) * PAGE_SIZE > kvm->arch.mem_limit)
|
||||
|
|
@ -69,65 +76,41 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!kvm_s390_is_migration_mode(kvm))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Turn off migration mode when:
|
||||
* - userspace creates a new memslot with dirty logging off,
|
||||
* - userspace modifies an existing memslot (MOVE or FLAGS_ONLY) and
|
||||
* dirty logging is turned off.
|
||||
* Migration mode expects dirty page logging being enabled to store
|
||||
* its dirty bitmap.
|
||||
*/
|
||||
if (change != KVM_MR_DELETE &&
|
||||
!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
|
||||
WARN(kvm_s390_vm_stop_migration(kvm),
|
||||
"Failed to stop migration mode");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void s390_kvm_mmu_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 __free(kvm_s390_mmu_cache) = NULL;
|
||||
int rc = 0;
|
||||
|
||||
guard(mutex)(&kvm->slots_arch_lock);
|
||||
if (kvm->arch.migration_mode) {
|
||||
/*
|
||||
* Turn off migration mode when:
|
||||
* - userspace creates a new memslot with dirty logging off,
|
||||
* - userspace modifies an existing memslot (MOVE or FLAGS_ONLY)
|
||||
* and dirty logging is turned off.
|
||||
* Migration mode expects dirty page logging being enabled to
|
||||
* store its dirty bitmap.
|
||||
*/
|
||||
if (change != KVM_MR_DELETE &&
|
||||
!(new->flags & KVM_MEM_LOG_DIRTY_PAGES))
|
||||
WARN(kvm_s390_vm_stop_migration(kvm),
|
||||
"Failed to stop migration mode");
|
||||
}
|
||||
|
||||
if (change == KVM_MR_FLAGS_ONLY)
|
||||
return;
|
||||
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc) {
|
||||
rc = -ENOMEM;
|
||||
goto out;
|
||||
return 0;
|
||||
if (change != KVM_MR_DELETE) {
|
||||
/* Enough capacity to add a new memslot */
|
||||
mc = kvm_s390_new_mmu_cache();
|
||||
if (!mc)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
scoped_guard(write_lock, &kvm->mmu_lock) {
|
||||
kvm_s390_update_cmma_dirty(kvm, old);
|
||||
switch (change) {
|
||||
case KVM_MR_DELETE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
break;
|
||||
case KVM_MR_MOVE:
|
||||
rc = dat_delete_slot(mc, kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
if (rc)
|
||||
break;
|
||||
fallthrough;
|
||||
case KVM_MR_CREATE:
|
||||
if (change == KVM_MR_DELETE || change == KVM_MR_MOVE)
|
||||
rc = dat_delete_slot(kvm->arch.gmap->asce, old->base_gfn, old->npages);
|
||||
if (!rc && (change == KVM_MR_MOVE || change == KVM_MR_CREATE))
|
||||
rc = dat_create_slot(mc, kvm->arch.gmap->asce, new->base_gfn, new->npages);
|
||||
break;
|
||||
case KVM_MR_FLAGS_ONLY:
|
||||
break;
|
||||
default:
|
||||
WARN(1, "Unknown KVM MR CHANGE: %d\n", change);
|
||||
}
|
||||
}
|
||||
out:
|
||||
if (rc)
|
||||
pr_warn("failed to commit memory region\n");
|
||||
/*
|
||||
* Can only be triggered if dat_{create,delete}_slot() found an
|
||||
* internal inconsistency or if the mmu cache ran out of memory;
|
||||
* both should be impossible.
|
||||
*/
|
||||
KVM_BUG_ON(rc, kvm);
|
||||
return rc;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,9 +10,5 @@ int s390_kvm_mmu_prepare_memory_region(struct kvm *kvm,
|
|||
const struct kvm_memory_slot *old,
|
||||
struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change);
|
||||
void s390_kvm_mmu_commit_memory_region(struct kvm *kvm,
|
||||
struct kvm_memory_slot *old,
|
||||
const struct kvm_memory_slot *new,
|
||||
enum kvm_mr_change change);
|
||||
|
||||
#endif /* ARCH_KVM_GMAP_KVM_MMU_H */
|
||||
|
|
|
|||
|
|
@ -1589,12 +1589,25 @@ static inline int ___gaccess_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg
|
|||
parent = READ_ONCE(sg->parent);
|
||||
if (!parent)
|
||||
return -EAGAIN;
|
||||
retry:
|
||||
scoped_guard(spinlock, &parent->children_lock) {
|
||||
if (READ_ONCE(sg->parent) != parent)
|
||||
return -EAGAIN;
|
||||
sg->invalidated = false;
|
||||
rc = _gaccess_do_shadow(vcpu->arch.mc, sg, saddr, walk);
|
||||
}
|
||||
if (rc == -ENOENT) {
|
||||
struct kvm_memory_slot *slot;
|
||||
struct guest_fault *entries;
|
||||
|
||||
entries = get_entries(walk);
|
||||
slot = kvm_vcpu_gfn_to_memslot(vcpu, entries[LEVEL_MEM].gfn);
|
||||
if (!slot)
|
||||
return PGM_ADDRESSING;
|
||||
rc = gmap_link(vcpu->arch.mc, parent, entries + LEVEL_MEM, slot);
|
||||
if (!rc)
|
||||
goto retry;
|
||||
}
|
||||
if (!rc)
|
||||
kvm_s390_release_faultin_array(vcpu->kvm, walk->raw_entries, false);
|
||||
return rc;
|
||||
|
|
|
|||
|
|
@ -2984,61 +2984,58 @@ static int adapter_indicators_set(struct kvm *kvm,
|
|||
struct s390_io_adapter *adapter,
|
||||
struct kvm_s390_adapter_int *adapter_int)
|
||||
{
|
||||
unsigned long bit;
|
||||
int summary_set, idx;
|
||||
struct s390_map_info *ind_info, *summary_info;
|
||||
void *map;
|
||||
struct page *ind_page, *summary_page;
|
||||
unsigned long flags;
|
||||
unsigned long bit;
|
||||
int summary_set;
|
||||
void *map;
|
||||
|
||||
ind_page = NULL;
|
||||
|
||||
spin_lock_irqsave(&adapter->maps_lock, flags);
|
||||
ind_info = get_map_info(adapter, adapter_int->ind_addr);
|
||||
scoped_guard(spinlock_irqsave, &adapter->maps_lock) {
|
||||
ind_info = get_map_info(adapter, adapter_int->ind_addr);
|
||||
if (ind_info) {
|
||||
map = page_address(ind_info->page);
|
||||
bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
|
||||
set_bit(bit, map);
|
||||
}
|
||||
}
|
||||
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);
|
||||
}
|
||||
scoped_guard(srcu, &kvm->srcu)
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->ind_gaddr));
|
||||
|
||||
spin_lock_irqsave(&adapter->maps_lock, flags);
|
||||
summary_info = get_map_info(adapter, adapter_int->summary_addr);
|
||||
scoped_guard(spinlock_irqsave, &adapter->maps_lock) {
|
||||
summary_info = get_map_info(adapter, adapter_int->summary_addr);
|
||||
if (summary_info) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
if (!summary_info) {
|
||||
spin_unlock_irqrestore(&adapter->maps_lock, flags);
|
||||
summary_page = pin_map_page(kvm, adapter_int->summary_addr, 0);
|
||||
if (!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);
|
||||
}
|
||||
scoped_guard(srcu, &kvm->srcu)
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->summary_gaddr));
|
||||
|
||||
return summary_set ? 0 : 1;
|
||||
}
|
||||
|
|
@ -3048,26 +3045,29 @@ static int adapter_indicators_set_fast(struct kvm *kvm,
|
|||
struct kvm_s390_adapter_int *adapter_int,
|
||||
int setbit)
|
||||
{
|
||||
struct s390_map_info *ind_info, *summary_info;
|
||||
unsigned long bit;
|
||||
int summary_set;
|
||||
struct s390_map_info *ind_info, *summary_info;
|
||||
void *map;
|
||||
|
||||
spin_lock(&adapter->maps_lock);
|
||||
guard(srcu)(&kvm->srcu);
|
||||
guard(spinlock)(&adapter->maps_lock);
|
||||
|
||||
ind_info = get_map_info(adapter, adapter_int->ind_addr);
|
||||
if (!ind_info) {
|
||||
spin_unlock(&adapter->maps_lock);
|
||||
if (!ind_info)
|
||||
return -EWOULDBLOCK;
|
||||
}
|
||||
|
||||
map = page_address(ind_info->page);
|
||||
bit = get_ind_bit(ind_info->addr, adapter_int->ind_offset, adapter->swap);
|
||||
if (setbit)
|
||||
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;
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->ind_gaddr));
|
||||
}
|
||||
|
||||
summary_info = get_map_info(adapter, adapter_int->summary_addr);
|
||||
if (!summary_info)
|
||||
return -EWOULDBLOCK;
|
||||
|
||||
map = page_address(summary_info->page);
|
||||
bit = get_ind_bit(summary_info->addr, adapter_int->summary_offset,
|
||||
adapter->swap);
|
||||
|
|
@ -3077,7 +3077,8 @@ static int adapter_indicators_set_fast(struct kvm *kvm,
|
|||
summary_set = test_and_set_bit(bit, map);
|
||||
else
|
||||
summary_set = test_and_clear_bit(bit, map);
|
||||
spin_unlock(&adapter->maps_lock);
|
||||
mark_page_dirty(kvm, gpa_to_gfn(adapter_int->summary_gaddr));
|
||||
|
||||
return summary_set ? 0 : 1;
|
||||
}
|
||||
|
||||
|
|
@ -3228,9 +3229,9 @@ int kvm_s390_set_irq_state(struct kvm_vcpu *vcpu, void __user *irqstate, int len
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (storestatus) {
|
||||
n = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
scoped_guard(srcu, &vcpu->kvm->srcu)
|
||||
n = kvm_s390_store_status_unloaded(vcpu, KVM_S390_STORE_STATUS_NOADDR);
|
||||
return r ? r : n;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5766,7 +5766,7 @@ static long cmma_d_count_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_
|
|||
return 0;
|
||||
}
|
||||
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, struct kvm_memory_slot *old)
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, const struct kvm_memory_slot *old)
|
||||
{
|
||||
const struct dat_walk_ops ops = { .pte_entry = cmma_d_count_pte, };
|
||||
|
||||
|
|
@ -5781,7 +5781,6 @@ 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)
|
||||
{
|
||||
s390_kvm_mmu_commit_memory_region(kvm, old, new, change);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -472,7 +472,7 @@ int __kvm_s390_mprotect_many(struct gmap *gmap, gpa_t gpa, u8 npages, unsigned i
|
|||
unsigned long bits);
|
||||
|
||||
bool kvm_arch_setup_async_pf(struct kvm_vcpu *vcpu);
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, struct kvm_memory_slot *old);
|
||||
void kvm_s390_update_cmma_dirty(struct kvm *kvm, const struct kvm_memory_slot *old);
|
||||
int kvm_s390_vm_stop_migration(struct kvm *kvm);
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -277,9 +277,9 @@ static void vfio_ap_free_aqic_resources(struct vfio_ap_queue *q)
|
|||
{
|
||||
if (!q)
|
||||
return;
|
||||
if (q->saved_isc != VFIO_AP_ISC_INVALID &&
|
||||
!WARN_ON(!(q->matrix_mdev && q->matrix_mdev->kvm))) {
|
||||
kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc);
|
||||
if (q->saved_isc != VFIO_AP_ISC_INVALID) {
|
||||
if (!WARN_ON(!q->matrix_mdev) && q->matrix_mdev->kvm)
|
||||
kvm_s390_gisc_unregister(q->matrix_mdev->kvm, q->saved_isc);
|
||||
q->saved_isc = VFIO_AP_ISC_INVALID;
|
||||
}
|
||||
if (q->saved_iova && !WARN_ON(!q->matrix_mdev)) {
|
||||
|
|
@ -1935,6 +1935,8 @@ static int apq_status_check(int apqn, struct ap_queue_status *status)
|
|||
* a value indicating a reset needs to be performed again.
|
||||
*/
|
||||
return -EAGAIN;
|
||||
case AP_RESPONSE_Q_NOT_AVAIL:
|
||||
return -ENODEV;
|
||||
default:
|
||||
WARN(true,
|
||||
"failed to verify reset of queue %02x.%04x: TAPQ rc=%u\n",
|
||||
|
|
@ -1961,6 +1963,10 @@ static void apq_reset_check(struct work_struct *reset_work)
|
|||
ret = apq_status_check(q->apqn, &status);
|
||||
if (ret == -EIO)
|
||||
return;
|
||||
if (ret == -ENODEV) {
|
||||
vfio_ap_free_aqic_resources(q);
|
||||
return;
|
||||
}
|
||||
if (ret == -EBUSY) {
|
||||
pr_notice_ratelimited(WAIT_MSG, elapsed,
|
||||
AP_QID_CARD(q->apqn),
|
||||
|
|
@ -2004,6 +2010,7 @@ static void vfio_ap_mdev_reset_queue(struct vfio_ap_queue *q)
|
|||
break;
|
||||
case AP_RESPONSE_DECONFIGURED:
|
||||
case AP_RESPONSE_CHECKSTOPPED:
|
||||
case AP_RESPONSE_Q_NOT_AVAIL:
|
||||
vfio_ap_free_aqic_resources(q);
|
||||
break;
|
||||
default:
|
||||
|
|
@ -2528,12 +2535,15 @@ void vfio_ap_mdev_remove_queue(struct ap_device *apdev)
|
|||
/*
|
||||
* If the queue is not in the host's AP configuration, then resetting
|
||||
* it will fail with response code 01, (APQN not valid); so, let's make
|
||||
* sure it is in the host's config.
|
||||
* sure it is in the host's config. If it is not, free the KVM GISC
|
||||
* resources.
|
||||
*/
|
||||
if (test_bit_inv(apid, (unsigned long *)matrix_dev->info.apm) &&
|
||||
test_bit_inv(apqi, (unsigned long *)matrix_dev->info.aqm)) {
|
||||
vfio_ap_mdev_reset_queue(q);
|
||||
flush_work(&q->reset_work);
|
||||
} else {
|
||||
vfio_ap_free_aqic_resources(q);
|
||||
}
|
||||
|
||||
done:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user