@@ -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+ }
0 commit comments