Commit fe51fa16 authored by Andrew Morton's avatar Andrew Morton Committed by Linus Torvalds

[PATCH] dirty bit clearing on s390.

From: Martin Schwidefsky <schwidefsky@de.ibm.com>

The S390 hardware has the interesting characteristic that the storage key
hardware which records a page's pte-dirtiness is also marked dirty when a
page is modified by the disk hardware.

Consequently, reading data from disk makes the page appear to be dirty and
things which were read from disk need to be written back.

Up to now s390 uses a special bit in the pte that is set in mk_pte for the
first user of a page and makes set_pte to clear the storage key.  The problem
is that this is a race condition if two processes want to access the same
page simultaneously.  Then the page count is already > 1 in mk_pte and nobody
will clear the storage key.  It doesn't lead to any data loss because what
happens is that a clean page is considered dirty and is written back to the
disk.  The worst scenario is a read only disk where this results in i/o
errors (but no data loss).

Martin's fix to this is to clear the page's dirty flag in SetPageUptodate().
Which seems a bit kludgey, but I cannot think of a case in which this in
incorrect, inadequate or inappropriate.  So...
parent 9174121c
...@@ -212,8 +212,7 @@ extern char empty_zero_page[PAGE_SIZE]; ...@@ -212,8 +212,7 @@ extern char empty_zero_page[PAGE_SIZE];
#define _PAGE_INVALID 0x400 /* HW invalid */ #define _PAGE_INVALID 0x400 /* HW invalid */
/* Software bits in the page table entry */ /* Software bits in the page table entry */
#define _PAGE_MKCLEAN 0x002 #define _PAGE_ISCLEAN 0x002
#define _PAGE_ISCLEAN 0x004
/* Mask and four different kinds of invalid pages. */ /* Mask and four different kinds of invalid pages. */
#define _PAGE_INVALID_MASK 0x601 #define _PAGE_INVALID_MASK 0x601
...@@ -320,15 +319,6 @@ extern char empty_zero_page[PAGE_SIZE]; ...@@ -320,15 +319,6 @@ extern char empty_zero_page[PAGE_SIZE];
*/ */
extern inline void set_pte(pte_t *pteptr, pte_t pteval) extern inline void set_pte(pte_t *pteptr, pte_t pteval)
{ {
if ((pte_val(pteval) & (_PAGE_MKCLEAN|_PAGE_INVALID))
== _PAGE_MKCLEAN)
{
pte_val(pteval) &= ~_PAGE_MKCLEAN;
asm volatile ("sske %0,%1"
: : "d" (0), "a" (pte_val(pteval)));
}
*pteptr = pteval; *pteptr = pteval;
} }
...@@ -501,7 +491,7 @@ extern inline pte_t pte_mkdirty(pte_t pte) ...@@ -501,7 +491,7 @@ extern inline pte_t pte_mkdirty(pte_t pte)
* sske instruction is slow. It is faster to let the * sske instruction is slow. It is faster to let the
* next instruction set the dirty bit. * next instruction set the dirty bit.
*/ */
pte_val(pte) &= ~(_PAGE_MKCLEAN | _PAGE_ISCLEAN); pte_val(pte) &= ~ _PAGE_ISCLEAN;
return pte; return pte;
} }
...@@ -582,30 +572,23 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot) ...@@ -582,30 +572,23 @@ static inline pte_t mk_pte_phys(unsigned long physpage, pgprot_t pgprot)
pgprot_t __pgprot = (pgprot); \ pgprot_t __pgprot = (pgprot); \
unsigned long __physpage = __pa((__page-mem_map) << PAGE_SHIFT); \ unsigned long __physpage = __pa((__page-mem_map) << PAGE_SHIFT); \
pte_t __pte = mk_pte_phys(__physpage, __pgprot); \ pte_t __pte = mk_pte_phys(__physpage, __pgprot); \
\
if (!(pgprot_val(__pgprot) & _PAGE_ISCLEAN)) { \
int __users = !!PagePrivate(__page) + !!__page->mapping; \
if (__users + page_count(__page) == 1) \
pte_val(__pte) |= _PAGE_MKCLEAN; \
} \
__pte; \ __pte; \
}) })
#define pfn_pte(pfn, pgprot) \ #define pfn_pte(pfn, pgprot) \
({ \ ({ \
struct page *__page = mem_map+(pfn); \
pgprot_t __pgprot = (pgprot); \ pgprot_t __pgprot = (pgprot); \
unsigned long __physpage = __pa((pfn) << PAGE_SHIFT); \ unsigned long __physpage = __pa((pfn) << PAGE_SHIFT); \
pte_t __pte = mk_pte_phys(__physpage, __pgprot); \ pte_t __pte = mk_pte_phys(__physpage, __pgprot); \
\
if (!(pgprot_val(__pgprot) & _PAGE_ISCLEAN)) { \
int __users = !!PagePrivate(__page) + !!__page->mapping; \
if (__users + page_count(__page) == 1) \
pte_val(__pte) |= _PAGE_MKCLEAN; \
} \
__pte; \ __pte; \
}) })
#define arch_set_page_uptodate(__page) \
do { \
asm volatile ("sske %0,%1" : : "d" (0), \
"a" (__pa((__page-mem_map) << PAGE_SHIFT)));\
} while (0)
#ifdef __s390x__ #ifdef __s390x__
#define pfn_pmd(pfn, pgprot) \ #define pfn_pmd(pfn, pgprot) \
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <linux/percpu.h> #include <linux/percpu.h>
#include <linux/cache.h> #include <linux/cache.h>
#include <asm/pgtable.h>
/* /*
* Various page->flags bits: * Various page->flags bits:
...@@ -158,8 +159,16 @@ extern void get_full_page_state(struct page_state *ret); ...@@ -158,8 +159,16 @@ extern void get_full_page_state(struct page_state *ret);
#define ClearPageReferenced(page) clear_bit(PG_referenced, &(page)->flags) #define ClearPageReferenced(page) clear_bit(PG_referenced, &(page)->flags)
#define TestClearPageReferenced(page) test_and_clear_bit(PG_referenced, &(page)->flags) #define TestClearPageReferenced(page) test_and_clear_bit(PG_referenced, &(page)->flags)
#ifndef arch_set_page_uptodate
#define arch_set_page_uptodate(page) do { } while (0)
#endif
#define PageUptodate(page) test_bit(PG_uptodate, &(page)->flags) #define PageUptodate(page) test_bit(PG_uptodate, &(page)->flags)
#define SetPageUptodate(page) set_bit(PG_uptodate, &(page)->flags) #define SetPageUptodate(page) \
do { \
arch_set_page_uptodate(page); \
set_bit(PG_uptodate, &(page)->flags); \
} while (0)
#define ClearPageUptodate(page) clear_bit(PG_uptodate, &(page)->flags) #define ClearPageUptodate(page) clear_bit(PG_uptodate, &(page)->flags)
#define PageDirty(page) test_bit(PG_dirty, &(page)->flags) #define PageDirty(page) test_bit(PG_dirty, &(page)->flags)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment