Skip to content

Commit 8e03e83

Browse files
author
Claudio Imbrenda
committed
KVM: s390: KVM page table management functions: storage keys
Add page table management functions to be used for KVM guest (gmap) page tables. This patch adds functions related to storage key handling. Acked-by: Heiko Carstens <hca@linux.ibm.com> Signed-off-by: Claudio Imbrenda <imbrenda@linux.ibm.com>
1 parent 2db149a commit 8e03e83

2 files changed

Lines changed: 230 additions & 0 deletions

File tree

arch/s390/kvm/dat.c

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,3 +602,226 @@ long _dat_walk_gfn_range(gfn_t start, gfn_t end, union asce asce,
602602

603603
return dat_crste_walk_range(start, min(end, asce_end(asce)), table, &walk);
604604
}
605+
606+
int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey)
607+
{
608+
union crste *crstep;
609+
union pgste pgste;
610+
union pte *ptep;
611+
int rc;
612+
613+
skey->skey = 0;
614+
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
615+
if (rc)
616+
return rc;
617+
618+
if (!ptep) {
619+
union crste crste;
620+
621+
crste = READ_ONCE(*crstep);
622+
if (!crste.h.fc || !crste.s.fc1.pr)
623+
return 0;
624+
skey->skey = page_get_storage_key(large_crste_to_phys(crste, gfn));
625+
return 0;
626+
}
627+
pgste = pgste_get_lock(ptep);
628+
if (ptep->h.i) {
629+
skey->acc = pgste.acc;
630+
skey->fp = pgste.fp;
631+
} else {
632+
skey->skey = page_get_storage_key(pte_origin(*ptep));
633+
}
634+
skey->r |= pgste.gr;
635+
skey->c |= pgste.gc;
636+
pgste_set_unlock(ptep, pgste);
637+
return 0;
638+
}
639+
640+
static void dat_update_ptep_sd(union pgste old, union pgste pgste, union pte *ptep)
641+
{
642+
if (pgste.acc != old.acc || pgste.fp != old.fp || pgste.gr != old.gr || pgste.gc != old.gc)
643+
__atomic64_or(_PAGE_SD, &ptep->val);
644+
}
645+
646+
int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
647+
union skey skey, bool nq)
648+
{
649+
union pgste pgste, old;
650+
union crste *crstep;
651+
union pte *ptep;
652+
int rc;
653+
654+
rc = dat_entry_walk(mc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
655+
&crstep, &ptep);
656+
if (rc)
657+
return rc;
658+
659+
if (!ptep) {
660+
page_set_storage_key(large_crste_to_phys(*crstep, gfn), skey.skey, !nq);
661+
return 0;
662+
}
663+
664+
old = pgste_get_lock(ptep);
665+
pgste = old;
666+
667+
pgste.acc = skey.acc;
668+
pgste.fp = skey.fp;
669+
pgste.gc = skey.c;
670+
pgste.gr = skey.r;
671+
672+
if (!ptep->h.i) {
673+
union skey old_skey;
674+
675+
old_skey.skey = page_get_storage_key(pte_origin(*ptep));
676+
pgste.hc |= old_skey.c;
677+
pgste.hr |= old_skey.r;
678+
old_skey.c = old.gc;
679+
old_skey.r = old.gr;
680+
skey.r = 0;
681+
skey.c = 0;
682+
page_set_storage_key(pte_origin(*ptep), skey.skey, !nq);
683+
}
684+
685+
dat_update_ptep_sd(old, pgste, ptep);
686+
pgste_set_unlock(ptep, pgste);
687+
return 0;
688+
}
689+
690+
static bool page_cond_set_storage_key(phys_addr_t paddr, union skey skey, union skey *oldkey,
691+
bool nq, bool mr, bool mc)
692+
{
693+
oldkey->skey = page_get_storage_key(paddr);
694+
if (oldkey->acc == skey.acc && oldkey->fp == skey.fp &&
695+
(oldkey->r == skey.r || mr) && (oldkey->c == skey.c || mc))
696+
return false;
697+
page_set_storage_key(paddr, skey.skey, !nq);
698+
return true;
699+
}
700+
701+
int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,
702+
union skey skey, union skey *oldkey, bool nq, bool mr, bool mc)
703+
{
704+
union pgste pgste, old;
705+
union crste *crstep;
706+
union skey prev;
707+
union pte *ptep;
708+
int rc;
709+
710+
rc = dat_entry_walk(mmc, gfn, asce, DAT_WALK_LEAF_ALLOC, TABLE_TYPE_PAGE_TABLE,
711+
&crstep, &ptep);
712+
if (rc)
713+
return rc;
714+
715+
if (!ptep)
716+
return page_cond_set_storage_key(large_crste_to_phys(*crstep, gfn), skey, oldkey,
717+
nq, mr, mc);
718+
719+
old = pgste_get_lock(ptep);
720+
pgste = old;
721+
722+
rc = 1;
723+
pgste.acc = skey.acc;
724+
pgste.fp = skey.fp;
725+
pgste.gc = skey.c;
726+
pgste.gr = skey.r;
727+
728+
if (!ptep->h.i) {
729+
rc = page_cond_set_storage_key(pte_origin(*ptep), skey, &prev, nq, mr, mc);
730+
pgste.hc |= prev.c;
731+
pgste.hr |= prev.r;
732+
prev.c |= old.gc;
733+
prev.r |= old.gr;
734+
} else {
735+
prev.acc = old.acc;
736+
prev.fp = old.fp;
737+
prev.c = old.gc;
738+
prev.r = old.gr;
739+
}
740+
if (oldkey)
741+
*oldkey = prev;
742+
743+
dat_update_ptep_sd(old, pgste, ptep);
744+
pgste_set_unlock(ptep, pgste);
745+
return rc;
746+
}
747+
748+
int dat_reset_reference_bit(union asce asce, gfn_t gfn)
749+
{
750+
union pgste pgste, old;
751+
union crste *crstep;
752+
union pte *ptep;
753+
int rc;
754+
755+
rc = dat_entry_walk(NULL, gfn, asce, DAT_WALK_ANY, TABLE_TYPE_PAGE_TABLE, &crstep, &ptep);
756+
if (rc)
757+
return rc;
758+
759+
if (!ptep) {
760+
union crste crste = READ_ONCE(*crstep);
761+
762+
if (!crste.h.fc || !crste.s.fc1.pr)
763+
return 0;
764+
return page_reset_referenced(large_crste_to_phys(*crstep, gfn));
765+
}
766+
old = pgste_get_lock(ptep);
767+
pgste = old;
768+
769+
if (!ptep->h.i) {
770+
rc = page_reset_referenced(pte_origin(*ptep));
771+
pgste.hr = rc >> 1;
772+
}
773+
rc |= (pgste.gr << 1) | pgste.gc;
774+
pgste.gr = 0;
775+
776+
dat_update_ptep_sd(old, pgste, ptep);
777+
pgste_set_unlock(ptep, pgste);
778+
return rc;
779+
}
780+
781+
static long dat_reset_skeys_pte(union pte *ptep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
782+
{
783+
union pgste pgste;
784+
785+
pgste = pgste_get_lock(ptep);
786+
pgste.acc = 0;
787+
pgste.fp = 0;
788+
pgste.gr = 0;
789+
pgste.gc = 0;
790+
if (ptep->s.pr)
791+
page_set_storage_key(pte_origin(*ptep), PAGE_DEFAULT_KEY, 1);
792+
pgste_set_unlock(ptep, pgste);
793+
794+
if (need_resched())
795+
return next;
796+
return 0;
797+
}
798+
799+
static long dat_reset_skeys_crste(union crste *crstep, gfn_t gfn, gfn_t next, struct dat_walk *walk)
800+
{
801+
phys_addr_t addr, end, origin = crste_origin_large(*crstep);
802+
803+
if (!crstep->h.fc || !crstep->s.fc1.pr)
804+
return 0;
805+
806+
addr = ((max(gfn, walk->start) - gfn) << PAGE_SHIFT) + origin;
807+
end = ((min(next, walk->end) - gfn) << PAGE_SHIFT) + origin;
808+
while (ALIGN(addr + 1, _SEGMENT_SIZE) <= end)
809+
addr = sske_frame(addr, PAGE_DEFAULT_KEY);
810+
for ( ; addr < end; addr += PAGE_SIZE)
811+
page_set_storage_key(addr, PAGE_DEFAULT_KEY, 1);
812+
813+
if (need_resched())
814+
return next;
815+
return 0;
816+
}
817+
818+
long dat_reset_skeys(union asce asce, gfn_t start)
819+
{
820+
const struct dat_walk_ops ops = {
821+
.pte_entry = dat_reset_skeys_pte,
822+
.pmd_entry = dat_reset_skeys_crste,
823+
.pud_entry = dat_reset_skeys_crste,
824+
};
825+
826+
return _dat_walk_gfn_range(start, asce_end(asce), asce, &ops, DAT_WALK_IGN_HOLES, NULL);
827+
}

arch/s390/kvm/dat.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,13 @@ int dat_entry_walk(struct kvm_s390_mmu_cache *mc, gfn_t gfn, union asce asce, in
472472
int walk_level, union crste **last, union pte **ptepp);
473473
void dat_free_level(struct crst_table *table, bool owns_ptes);
474474
struct crst_table *dat_alloc_crst_sleepable(unsigned long init);
475+
int dat_get_storage_key(union asce asce, gfn_t gfn, union skey *skey);
476+
int dat_set_storage_key(struct kvm_s390_mmu_cache *mc, union asce asce, gfn_t gfn,
477+
union skey skey, bool nq);
478+
int dat_cond_set_storage_key(struct kvm_s390_mmu_cache *mmc, union asce asce, gfn_t gfn,
479+
union skey skey, union skey *oldkey, bool nq, bool mr, bool mc);
480+
int dat_reset_reference_bit(union asce asce, gfn_t gfn);
481+
long dat_reset_skeys(union asce asce, gfn_t start);
475482

476483
int kvm_s390_mmu_cache_topup(struct kvm_s390_mmu_cache *mc);
477484

0 commit comments

Comments
 (0)