Merge branches 'work.misc' and 'work.dcache' of git://git.kernel.org/pub/scm/linux...
[sfrench/cifs-2.6.git] / arch / s390 / kvm / priv.c
index eb0eb60c7be6a26677f8ed20509aba88df6da337..cfc5a62329f607d6971bc1a18b23132fddbc6ca0 100644 (file)
@@ -246,9 +246,10 @@ static int try_handle_skey(struct kvm_vcpu *vcpu)
 
 static int handle_iske(struct kvm_vcpu *vcpu)
 {
-       unsigned long addr;
+       unsigned long gaddr, vmaddr;
        unsigned char key;
        int reg1, reg2;
+       bool unlocked;
        int rc;
 
        vcpu->stat.instruction_iske++;
@@ -262,18 +263,28 @@ static int handle_iske(struct kvm_vcpu *vcpu)
 
        kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
 
-       addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
-       addr = kvm_s390_logical_to_effective(vcpu, addr);
-       addr = kvm_s390_real_to_abs(vcpu, addr);
-       addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr));
-       if (kvm_is_error_hva(addr))
+       gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
+       gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
+       gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
+       vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
+       if (kvm_is_error_hva(vmaddr))
                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-
+retry:
+       unlocked = false;
        down_read(&current->mm->mmap_sem);
-       rc = get_guest_storage_key(current->mm, addr, &key);
-       up_read(&current->mm->mmap_sem);
+       rc = get_guest_storage_key(current->mm, vmaddr, &key);
+
+       if (rc) {
+               rc = fixup_user_fault(current, current->mm, vmaddr,
+                                     FAULT_FLAG_WRITE, &unlocked);
+               if (!rc) {
+                       up_read(&current->mm->mmap_sem);
+                       goto retry;
+               }
+       }
        if (rc)
                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
+       up_read(&current->mm->mmap_sem);
        vcpu->run->s.regs.gprs[reg1] &= ~0xff;
        vcpu->run->s.regs.gprs[reg1] |= key;
        return 0;
@@ -281,8 +292,9 @@ static int handle_iske(struct kvm_vcpu *vcpu)
 
 static int handle_rrbe(struct kvm_vcpu *vcpu)
 {
-       unsigned long addr;
+       unsigned long vmaddr, gaddr;
        int reg1, reg2;
+       bool unlocked;
        int rc;
 
        vcpu->stat.instruction_rrbe++;
@@ -296,19 +308,27 @@ static int handle_rrbe(struct kvm_vcpu *vcpu)
 
        kvm_s390_get_regs_rre(vcpu, &reg1, &reg2);
 
-       addr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
-       addr = kvm_s390_logical_to_effective(vcpu, addr);
-       addr = kvm_s390_real_to_abs(vcpu, addr);
-       addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(addr));
-       if (kvm_is_error_hva(addr))
+       gaddr = vcpu->run->s.regs.gprs[reg2] & PAGE_MASK;
+       gaddr = kvm_s390_logical_to_effective(vcpu, gaddr);
+       gaddr = kvm_s390_real_to_abs(vcpu, gaddr);
+       vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(gaddr));
+       if (kvm_is_error_hva(vmaddr))
                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-
+retry:
+       unlocked = false;
        down_read(&current->mm->mmap_sem);
-       rc = reset_guest_reference_bit(current->mm, addr);
-       up_read(&current->mm->mmap_sem);
+       rc = reset_guest_reference_bit(current->mm, vmaddr);
+       if (rc < 0) {
+               rc = fixup_user_fault(current, current->mm, vmaddr,
+                                     FAULT_FLAG_WRITE, &unlocked);
+               if (!rc) {
+                       up_read(&current->mm->mmap_sem);
+                       goto retry;
+               }
+       }
        if (rc < 0)
                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-
+       up_read(&current->mm->mmap_sem);
        kvm_s390_set_psw_cc(vcpu, rc);
        return 0;
 }
@@ -323,6 +343,7 @@ static int handle_sske(struct kvm_vcpu *vcpu)
        unsigned long start, end;
        unsigned char key, oldkey;
        int reg1, reg2;
+       bool unlocked;
        int rc;
 
        vcpu->stat.instruction_sske++;
@@ -355,19 +376,28 @@ static int handle_sske(struct kvm_vcpu *vcpu)
        }
 
        while (start != end) {
-               unsigned long addr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
+               unsigned long vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
+               unlocked = false;
 
-               if (kvm_is_error_hva(addr))
+               if (kvm_is_error_hva(vmaddr))
                        return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
                down_read(&current->mm->mmap_sem);
-               rc = cond_set_guest_storage_key(current->mm, addr, key, &oldkey,
+               rc = cond_set_guest_storage_key(current->mm, vmaddr, key, &oldkey,
                                                m3 & SSKE_NQ, m3 & SSKE_MR,
                                                m3 & SSKE_MC);
-               up_read(&current->mm->mmap_sem);
-               if (rc < 0)
+
+               if (rc < 0) {
+                       rc = fixup_user_fault(current, current->mm, vmaddr,
+                                             FAULT_FLAG_WRITE, &unlocked);
+                       rc = !rc ? -EAGAIN : rc;
+               }
+               if (rc == -EFAULT)
                        return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-               start += PAGE_SIZE;
+
+               up_read(&current->mm->mmap_sem);
+               if (rc >= 0)
+                       start += PAGE_SIZE;
        }
 
        if (m3 & (SSKE_MC | SSKE_MR)) {
@@ -948,15 +978,16 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
        }
 
        while (start != end) {
-               unsigned long useraddr;
+               unsigned long vmaddr;
+               bool unlocked = false;
 
                /* Translate guest address to host address */
-               useraddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
-               if (kvm_is_error_hva(useraddr))
+               vmaddr = gfn_to_hva(vcpu->kvm, gpa_to_gfn(start));
+               if (kvm_is_error_hva(vmaddr))
                        return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
 
                if (vcpu->run->s.regs.gprs[reg1] & PFMF_CF) {
-                       if (clear_user((void __user *)useraddr, PAGE_SIZE))
+                       if (clear_user((void __user *)vmaddr, PAGE_SIZE))
                                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
                }
 
@@ -966,14 +997,20 @@ static int handle_pfmf(struct kvm_vcpu *vcpu)
                        if (rc)
                                return rc;
                        down_read(&current->mm->mmap_sem);
-                       rc = cond_set_guest_storage_key(current->mm, useraddr,
+                       rc = cond_set_guest_storage_key(current->mm, vmaddr,
                                                        key, NULL, nq, mr, mc);
-                       up_read(&current->mm->mmap_sem);
-                       if (rc < 0)
+                       if (rc < 0) {
+                               rc = fixup_user_fault(current, current->mm, vmaddr,
+                                                     FAULT_FLAG_WRITE, &unlocked);
+                               rc = !rc ? -EAGAIN : rc;
+                       }
+                       if (rc == -EFAULT)
                                return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING);
-               }
 
-               start += PAGE_SIZE;
+                       up_read(&current->mm->mmap_sem);
+                       if (rc >= 0)
+                               start += PAGE_SIZE;
+               }
        }
        if (vcpu->run->s.regs.gprs[reg1] & PFMF_FSC) {
                if (psw_bits(vcpu->arch.sie_block->gpsw).eaba == PSW_BITS_AMODE_64BIT) {