Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

s390/kvm: Provide function for setting the guest storage key

From time to time we need to set the guest storage key. Lets
provide a helper function that handles the changes with all the
right locking and checking.

Signed-off-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

authored by

Christian Borntraeger and committed by
Martin Schwidefsky
24d5dd02 25b41a7b

+51
+3
arch/s390/include/asm/pgalloc.h
··· 22 22 void page_table_free(struct mm_struct *, unsigned long *); 23 23 void page_table_free_rcu(struct mmu_gather *, unsigned long *); 24 24 25 + int set_guest_storage_key(struct mm_struct *mm, unsigned long addr, 26 + unsigned long key, bool nq); 27 + 25 28 static inline void clear_table(unsigned long *s, unsigned long val, size_t n) 26 29 { 27 30 typedef struct { char _[n]; } addrtype;
+48
arch/s390/mm/pgtable.c
··· 771 771 __free_page(page); 772 772 } 773 773 774 + int set_guest_storage_key(struct mm_struct *mm, unsigned long addr, 775 + unsigned long key, bool nq) 776 + { 777 + spinlock_t *ptl; 778 + pgste_t old, new; 779 + pte_t *ptep; 780 + 781 + down_read(&mm->mmap_sem); 782 + ptep = get_locked_pte(current->mm, addr, &ptl); 783 + if (unlikely(!ptep)) { 784 + up_read(&mm->mmap_sem); 785 + return -EFAULT; 786 + } 787 + 788 + new = old = pgste_get_lock(ptep); 789 + pgste_val(new) &= ~(PGSTE_GR_BIT | PGSTE_GC_BIT | 790 + PGSTE_ACC_BITS | PGSTE_FP_BIT); 791 + pgste_val(new) |= (key & (_PAGE_CHANGED | _PAGE_REFERENCED)) << 48; 792 + pgste_val(new) |= (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56; 793 + if (!(pte_val(*ptep) & _PAGE_INVALID)) { 794 + unsigned long address, bits; 795 + unsigned char skey; 796 + 797 + address = pte_val(*ptep) & PAGE_MASK; 798 + skey = page_get_storage_key(address); 799 + bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED); 800 + /* Set storage key ACC and FP */ 801 + page_set_storage_key(address, 802 + (key & (_PAGE_ACC_BITS | _PAGE_FP_BIT)), 803 + !nq); 804 + 805 + /* Merge host changed & referenced into pgste */ 806 + pgste_val(new) |= bits << 52; 807 + /* Transfer skey changed & referenced bit to kvm user bits */ 808 + pgste_val(new) |= bits << 45; /* PGSTE_UR_BIT & PGSTE_UC_BIT */ 809 + } 810 + /* changing the guest storage key is considered a change of the page */ 811 + if ((pgste_val(new) ^ pgste_val(old)) & 812 + (PGSTE_ACC_BITS | PGSTE_FP_BIT | PGSTE_GR_BIT | PGSTE_GC_BIT)) 813 + pgste_val(new) |= PGSTE_UC_BIT; 814 + 815 + pgste_set_unlock(ptep, new); 816 + pte_unmap_unlock(*ptep, ptl); 817 + up_read(&mm->mmap_sem); 818 + return 0; 819 + } 820 + EXPORT_SYMBOL(set_guest_storage_key); 821 + 774 822 #else /* CONFIG_PGSTE */ 775 823 776 824 static inline unsigned long *page_table_alloc_pgste(struct mm_struct *mm,