[S390] introduce dirty bit for kvm live migration
Florian Funke [Fri, 10 Oct 2008 19:33:26 +0000 (21:33 +0200)]
This patch defines a dirty bit in the PGSTE that can be used to implement
dirty pages logging for KVM's live migration. The bit is set in the
ptep_rcp_copy function, which is called to save dirty and referenced information
from the storage key in the PGSTE. The bit can be tested and reset by KVM using
the kvm_s390_test_and_clear_page_dirty function that is introduced by this patch.

Acked-by: Carsten Otte <cotte@de.ibm.com>
Signed-off-by: Florian Funke <ffunke@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>

arch/s390/include/asm/pgtable.h

index 0bdb704..1a928f8 100644 (file)
@@ -281,6 +281,9 @@ extern char empty_zero_page[PAGE_SIZE];
 #define RCP_GR_BIT     50
 #define RCP_GC_BIT     49
 
+/* User dirty bit for KVM's migration feature */
+#define KVM_UD_BIT     47
+
 #ifndef __s390x__
 
 /* Bits in the segment table address-space-control-element */
@@ -575,12 +578,16 @@ static inline void ptep_rcp_copy(pte_t *ptep)
        unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE);
 
        skey = page_get_storage_key(page_to_phys(page));
-       if (skey & _PAGE_CHANGED)
+       if (skey & _PAGE_CHANGED) {
                set_bit_simple(RCP_GC_BIT, pgste);
+               set_bit_simple(KVM_UD_BIT, pgste);
+       }
        if (skey & _PAGE_REFERENCED)
                set_bit_simple(RCP_GR_BIT, pgste);
-       if (test_and_clear_bit_simple(RCP_HC_BIT, pgste))
+       if (test_and_clear_bit_simple(RCP_HC_BIT, pgste)) {
                SetPageDirty(page);
+               set_bit_simple(KVM_UD_BIT, pgste);
+       }
        if (test_and_clear_bit_simple(RCP_HR_BIT, pgste))
                SetPageReferenced(page);
 #endif
@@ -744,6 +751,40 @@ static inline pte_t pte_mkspecial(pte_t pte)
        return pte;
 }
 
+#ifdef CONFIG_PGSTE
+/*
+ * Get (and clear) the user dirty bit for a PTE.
+ */
+static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm,
+                                                    pte_t *ptep)
+{
+       int dirty;
+       unsigned long *pgste;
+       struct page *page;
+       unsigned int skey;
+
+       if (!mm->context.pgstes)
+               return -EINVAL;
+       rcp_lock(ptep);
+       pgste = (unsigned long *) (ptep + PTRS_PER_PTE);
+       page = virt_to_page(pte_val(*ptep));
+       skey = page_get_storage_key(page_to_phys(page));
+       if (skey & _PAGE_CHANGED) {
+               set_bit_simple(RCP_GC_BIT, pgste);
+               set_bit_simple(KVM_UD_BIT, pgste);
+       }
+       if (test_and_clear_bit_simple(RCP_HC_BIT, pgste)) {
+               SetPageDirty(page);
+               set_bit_simple(KVM_UD_BIT, pgste);
+       }
+       dirty = test_and_clear_bit_simple(KVM_UD_BIT, pgste);
+       if (skey & _PAGE_CHANGED)
+               page_clear_dirty(page);
+       rcp_unlock(ptep);
+       return dirty;
+}
+#endif
+
 #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
 static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
                                            unsigned long addr, pte_t *ptep)