Commit 0d66cba1 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6

* 'for-linus' of git://git390.marist.edu/pub/scm/linux-2.6: (29 commits)
  [S390] cpu hotplug: fix external interrupt subclass mask handling
  [S390] oprofile: dont access lowcore
  [S390] oprofile: add missing irq stats counter
  [S390] Ignore sendmmsg system call note wired up warning
  [S390] s390,oprofile: fix compile error for !CONFIG_SMP
  [S390] s390,oprofile: fix alert counter increment
  [S390] Remove unused includes in process.c
  [S390] get CPC image name
  [S390] sclp: event buffer dissection
  [S390] chsc: process channel-path-availability information
  [S390] refactor page table functions for better pgste support
  [S390] merge page_test_dirty and page_clear_dirty
  [S390] qdio: prevent compile warning
  [S390] sclp: remove unnecessary sendmask check
  [S390] convert old cpumask API into new one
  [S390] pfault: cleanup code
  [S390] pfault: cpu hotplug vs missing completion interrupts
  [S390] smp: add __noreturn attribute to cpu_die()
  [S390] percpu: implement arch specific irqsafe_cpu_ops
  [S390] vdso: disable gcov profiling
  ...
parents 4637f40f 5bd41878
...@@ -230,17 +230,6 @@ config SYSVIPC_COMPAT ...@@ -230,17 +230,6 @@ config SYSVIPC_COMPAT
config AUDIT_ARCH config AUDIT_ARCH
def_bool y def_bool y
config S390_EXEC_PROTECT
def_bool y
prompt "Data execute protection"
help
This option allows to enable a buffer overflow protection for user
space programs and it also selects the addressing mode option above.
The kernel parameter noexec=on will enable this feature and also
switch the addressing modes, default is disabled. Enabling this (via
kernel parameter) on machines earlier than IBM System z9 this will
reduce system performance.
comment "Code generation options" comment "Code generation options"
choice choice
......
...@@ -130,9 +130,7 @@ static void appldata_work_fn(struct work_struct *work) ...@@ -130,9 +130,7 @@ static void appldata_work_fn(struct work_struct *work)
{ {
struct list_head *lh; struct list_head *lh;
struct appldata_ops *ops; struct appldata_ops *ops;
int i;
i = 0;
get_online_cpus(); get_online_cpus();
mutex_lock(&appldata_ops_mutex); mutex_lock(&appldata_ops_mutex);
list_for_each(lh, &appldata_ops_list) { list_for_each(lh, &appldata_ops_list) {
......
...@@ -167,7 +167,6 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old, ...@@ -167,7 +167,6 @@ static inline unsigned long __cmpxchg(void *ptr, unsigned long old,
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
#define cmpxchg64(ptr, o, n) \ #define cmpxchg64(ptr, o, n) \
({ \ ({ \
BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
cmpxchg((ptr), (o), (n)); \ cmpxchg((ptr), (o), (n)); \
}) })
#else /* CONFIG_64BIT */ #else /* CONFIG_64BIT */
......
...@@ -196,18 +196,6 @@ do { \ ...@@ -196,18 +196,6 @@ do { \
} while (0) } while (0)
#endif /* __s390x__ */ #endif /* __s390x__ */
/*
* An executable for which elf_read_implies_exec() returns TRUE will
* have the READ_IMPLIES_EXEC personality flag set automatically.
*/
#define elf_read_implies_exec(ex, executable_stack) \
({ \
if (current->mm->context.noexec && \
executable_stack != EXSTACK_DISABLE_X) \
disable_noexec(current->mm, current); \
current->mm->context.noexec == 0; \
})
#define STACK_RND_MASK 0x7ffUL #define STACK_RND_MASK 0x7ffUL
#define ARCH_DLINFO \ #define ARCH_DLINFO \
......
...@@ -111,21 +111,10 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm, ...@@ -111,21 +111,10 @@ static inline void huge_ptep_invalidate(struct mm_struct *mm,
{ {
pmd_t *pmdp = (pmd_t *) ptep; pmd_t *pmdp = (pmd_t *) ptep;
if (!MACHINE_HAS_IDTE) { if (MACHINE_HAS_IDTE)
__pmd_csp(pmdp);
if (mm->context.noexec) {
pmdp = get_shadow_table(pmdp);
__pmd_csp(pmdp);
}
return;
}
__pmd_idte(address, pmdp);
if (mm->context.noexec) {
pmdp = get_shadow_table(pmdp);
__pmd_idte(address, pmdp); __pmd_idte(address, pmdp);
} else
return; __pmd_csp(pmdp);
} }
#define huge_ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \ #define huge_ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \
......
...@@ -15,6 +15,7 @@ enum interruption_class { ...@@ -15,6 +15,7 @@ enum interruption_class {
EXTINT_VRT, EXTINT_VRT,
EXTINT_SCP, EXTINT_SCP,
EXTINT_IUC, EXTINT_IUC,
EXTINT_CPM,
IOINT_QAI, IOINT_QAI,
IOINT_QDI, IOINT_QDI,
IOINT_DAS, IOINT_DAS,
......
...@@ -124,7 +124,7 @@ struct _lowcore { ...@@ -124,7 +124,7 @@ struct _lowcore {
/* Address space pointer. */ /* Address space pointer. */
__u32 kernel_asce; /* 0x02ac */ __u32 kernel_asce; /* 0x02ac */
__u32 user_asce; /* 0x02b0 */ __u32 user_asce; /* 0x02b0 */
__u32 user_exec_asce; /* 0x02b4 */ __u32 current_pid; /* 0x02b4 */
/* SMP info area */ /* SMP info area */
__u32 cpu_nr; /* 0x02b8 */ __u32 cpu_nr; /* 0x02b8 */
...@@ -255,7 +255,7 @@ struct _lowcore { ...@@ -255,7 +255,7 @@ struct _lowcore {
/* Address space pointer. */ /* Address space pointer. */
__u64 kernel_asce; /* 0x0310 */ __u64 kernel_asce; /* 0x0310 */
__u64 user_asce; /* 0x0318 */ __u64 user_asce; /* 0x0318 */
__u64 user_exec_asce; /* 0x0320 */ __u64 current_pid; /* 0x0320 */
/* SMP info area */ /* SMP info area */
__u32 cpu_nr; /* 0x0328 */ __u32 cpu_nr; /* 0x0328 */
......
...@@ -5,19 +5,18 @@ typedef struct { ...@@ -5,19 +5,18 @@ typedef struct {
atomic_t attach_count; atomic_t attach_count;
unsigned int flush_mm; unsigned int flush_mm;
spinlock_t list_lock; spinlock_t list_lock;
struct list_head crst_list;
struct list_head pgtable_list; struct list_head pgtable_list;
unsigned long asce_bits; unsigned long asce_bits;
unsigned long asce_limit; unsigned long asce_limit;
unsigned long vdso_base; unsigned long vdso_base;
int noexec; /* Cloned contexts will be created with extended page tables. */
int has_pgste; /* The mmu context has extended page tables */ unsigned int alloc_pgste:1;
int alloc_pgste; /* cloned contexts will have extended page tables */ /* The mmu context has extended page tables. */
unsigned int has_pgste:1;
} mm_context_t; } mm_context_t;
#define INIT_MM_CONTEXT(name) \ #define INIT_MM_CONTEXT(name) \
.context.list_lock = __SPIN_LOCK_UNLOCKED(name.context.list_lock), \ .context.list_lock = __SPIN_LOCK_UNLOCKED(name.context.list_lock), \
.context.crst_list = LIST_HEAD_INIT(name.context.crst_list), \
.context.pgtable_list = LIST_HEAD_INIT(name.context.pgtable_list), .context.pgtable_list = LIST_HEAD_INIT(name.context.pgtable_list),
#endif #endif
...@@ -35,11 +35,9 @@ static inline int init_new_context(struct task_struct *tsk, ...@@ -35,11 +35,9 @@ static inline int init_new_context(struct task_struct *tsk,
* and if has_pgste is set, it will create extended page * and if has_pgste is set, it will create extended page
* tables. * tables.
*/ */
mm->context.noexec = 0;
mm->context.has_pgste = 1; mm->context.has_pgste = 1;
mm->context.alloc_pgste = 1; mm->context.alloc_pgste = 1;
} else { } else {
mm->context.noexec = (user_mode == SECONDARY_SPACE_MODE);
mm->context.has_pgste = 0; mm->context.has_pgste = 0;
mm->context.alloc_pgste = 0; mm->context.alloc_pgste = 0;
} }
...@@ -63,10 +61,8 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk) ...@@ -63,10 +61,8 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd); S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd);
if (user_mode != HOME_SPACE_MODE) { if (user_mode != HOME_SPACE_MODE) {
/* Load primary space page table origin. */ /* Load primary space page table origin. */
pgd = mm->context.noexec ? get_shadow_table(pgd) : pgd;
S390_lowcore.user_exec_asce = mm->context.asce_bits | __pa(pgd);
asm volatile(LCTL_OPCODE" 1,1,%0\n" asm volatile(LCTL_OPCODE" 1,1,%0\n"
: : "m" (S390_lowcore.user_exec_asce) ); : : "m" (S390_lowcore.user_asce) );
} else } else
/* Load home space page table origin. */ /* Load home space page table origin. */
asm volatile(LCTL_OPCODE" 13,13,%0" asm volatile(LCTL_OPCODE" 13,13,%0"
......
...@@ -90,6 +90,7 @@ static inline void copy_page(void *to, void *from) ...@@ -90,6 +90,7 @@ static inline void copy_page(void *to, void *from)
*/ */
typedef struct { unsigned long pgprot; } pgprot_t; typedef struct { unsigned long pgprot; } pgprot_t;
typedef struct { unsigned long pgste; } pgste_t;
typedef struct { unsigned long pte; } pte_t; typedef struct { unsigned long pte; } pte_t;
typedef struct { unsigned long pmd; } pmd_t; typedef struct { unsigned long pmd; } pmd_t;
typedef struct { unsigned long pud; } pud_t; typedef struct { unsigned long pud; } pud_t;
...@@ -97,18 +98,21 @@ typedef struct { unsigned long pgd; } pgd_t; ...@@ -97,18 +98,21 @@ typedef struct { unsigned long pgd; } pgd_t;
typedef pte_t *pgtable_t; typedef pte_t *pgtable_t;
#define pgprot_val(x) ((x).pgprot) #define pgprot_val(x) ((x).pgprot)
#define pgste_val(x) ((x).pgste)
#define pte_val(x) ((x).pte) #define pte_val(x) ((x).pte)
#define pmd_val(x) ((x).pmd) #define pmd_val(x) ((x).pmd)
#define pud_val(x) ((x).pud) #define pud_val(x) ((x).pud)
#define pgd_val(x) ((x).pgd) #define pgd_val(x) ((x).pgd)
#define __pgste(x) ((pgste_t) { (x) } )
#define __pte(x) ((pte_t) { (x) } ) #define __pte(x) ((pte_t) { (x) } )
#define __pmd(x) ((pmd_t) { (x) } ) #define __pmd(x) ((pmd_t) { (x) } )
#define __pud(x) ((pud_t) { (x) } )
#define __pgd(x) ((pgd_t) { (x) } ) #define __pgd(x) ((pgd_t) { (x) } )
#define __pgprot(x) ((pgprot_t) { (x) } ) #define __pgprot(x) ((pgprot_t) { (x) } )
static inline void static inline void page_set_storage_key(unsigned long addr,
page_set_storage_key(unsigned long addr, unsigned int skey, int mapped) unsigned char skey, int mapped)
{ {
if (!mapped) if (!mapped)
asm volatile(".insn rrf,0xb22b0000,%0,%1,8,0" asm volatile(".insn rrf,0xb22b0000,%0,%1,8,0"
...@@ -117,15 +121,59 @@ page_set_storage_key(unsigned long addr, unsigned int skey, int mapped) ...@@ -117,15 +121,59 @@ page_set_storage_key(unsigned long addr, unsigned int skey, int mapped)
asm volatile("sske %0,%1" : : "d" (skey), "a" (addr)); asm volatile("sske %0,%1" : : "d" (skey), "a" (addr));
} }
static inline unsigned int static inline unsigned char page_get_storage_key(unsigned long addr)
page_get_storage_key(unsigned long addr)
{ {
unsigned int skey; unsigned char skey;
asm volatile("iske %0,%1" : "=d" (skey) : "a" (addr), "0" (0)); asm volatile("iske %0,%1" : "=d" (skey) : "a" (addr));
return skey; return skey;
} }
static inline int page_reset_referenced(unsigned long addr)
{
unsigned int ipm;
asm volatile(
" rrbe 0,%1\n"
" ipm %0\n"
: "=d" (ipm) : "a" (addr) : "cc");
return !!(ipm & 0x20000000);
}
/* Bits int the storage key */
#define _PAGE_CHANGED 0x02 /* HW changed bit */
#define _PAGE_REFERENCED 0x04 /* HW referenced bit */
#define _PAGE_FP_BIT 0x08 /* HW fetch protection bit */
#define _PAGE_ACC_BITS 0xf0 /* HW access control bits */
/*
* Test and clear dirty bit in storage key.
* We can't clear the changed bit atomically. This is a potential
* race against modification of the referenced bit. This function
* should therefore only be called if it is not mapped in any
* address space.
*/
#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY
static inline int page_test_and_clear_dirty(unsigned long pfn, int mapped)
{
unsigned char skey;
skey = page_get_storage_key(pfn << PAGE_SHIFT);
if (!(skey & _PAGE_CHANGED))
return 0;
page_set_storage_key(pfn << PAGE_SHIFT, skey & ~_PAGE_CHANGED, mapped);
return 1;
}
/*
* Test and clear referenced bit in storage key.
*/
#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
static inline int page_test_and_clear_young(unsigned long pfn)
{
return page_reset_referenced(pfn << PAGE_SHIFT);
}
struct page; struct page;
void arch_free_page(struct page *page, int order); void arch_free_page(struct page *page, int order);
void arch_alloc_page(struct page *page, int order); void arch_alloc_page(struct page *page, int order);
......
#ifndef __ARCH_S390_PERCPU__ #ifndef __ARCH_S390_PERCPU__
#define __ARCH_S390_PERCPU__ #define __ARCH_S390_PERCPU__
#include <linux/preempt.h>
#include <asm/cmpxchg.h>
/* /*
* s390 uses its own implementation for per cpu data, the offset of * s390 uses its own implementation for per cpu data, the offset of
* the cpu local data area is cached in the cpu's lowcore memory. * the cpu local data area is cached in the cpu's lowcore memory.
...@@ -16,6 +19,71 @@ ...@@ -16,6 +19,71 @@
#define ARCH_NEEDS_WEAK_PER_CPU #define ARCH_NEEDS_WEAK_PER_CPU
#endif #endif
#define arch_irqsafe_cpu_to_op(pcp, val, op) \
do { \
typedef typeof(pcp) pcp_op_T__; \
pcp_op_T__ old__, new__, prev__; \
pcp_op_T__ *ptr__; \
preempt_disable(); \
ptr__ = __this_cpu_ptr(&(pcp)); \
prev__ = *ptr__; \
do { \
old__ = prev__; \
new__ = old__ op (val); \
switch (sizeof(*ptr__)) { \
case 8: \
prev__ = cmpxchg64(ptr__, old__, new__); \
break; \
default: \
prev__ = cmpxchg(ptr__, old__, new__); \
} \
} while (prev__ != old__); \
preempt_enable(); \
} while (0)
#define irqsafe_cpu_add_1(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, +)
#define irqsafe_cpu_add_2(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, +)
#define irqsafe_cpu_add_4(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, +)
#define irqsafe_cpu_add_8(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, +)
#define irqsafe_cpu_and_1(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, &)
#define irqsafe_cpu_and_2(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, &)
#define irqsafe_cpu_and_4(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, &)
#define irqsafe_cpu_and_8(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, &)
#define irqsafe_cpu_or_1(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, |)
#define irqsafe_cpu_or_2(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, |)
#define irqsafe_cpu_or_4(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, |)
#define irqsafe_cpu_or_8(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, |)
#define irqsafe_cpu_xor_1(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, ^)
#define irqsafe_cpu_xor_2(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, ^)
#define irqsafe_cpu_xor_4(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, ^)
#define irqsafe_cpu_xor_8(pcp, val) arch_irqsafe_cpu_to_op(pcp, val, ^)
#define arch_irqsafe_cpu_cmpxchg(pcp, oval, nval) \
({ \
typedef typeof(pcp) pcp_op_T__; \
pcp_op_T__ ret__; \
pcp_op_T__ *ptr__; \
preempt_disable(); \
ptr__ = __this_cpu_ptr(&(pcp)); \
switch (sizeof(*ptr__)) { \
case 8: \
ret__ = cmpxchg64(ptr__, oval, nval); \
break; \
default: \
ret__ = cmpxchg(ptr__, oval, nval); \
} \
preempt_enable(); \
ret__; \
})
#define irqsafe_cpu_cmpxchg_1(pcp, oval, nval) arch_irqsafe_cpu_cmpxchg(pcp, oval, nval)
#define irqsafe_cpu_cmpxchg_2(pcp, oval, nval) arch_irqsafe_cpu_cmpxchg(pcp, oval, nval)
#define irqsafe_cpu_cmpxchg_4(pcp, oval, nval) arch_irqsafe_cpu_cmpxchg(pcp, oval, nval)
#define irqsafe_cpu_cmpxchg_8(pcp, oval, nval) arch_irqsafe_cpu_cmpxchg(pcp, oval, nval)
#include <asm-generic/percpu.h> #include <asm-generic/percpu.h>
#endif /* __ARCH_S390_PERCPU__ */ #endif /* __ARCH_S390_PERCPU__ */
...@@ -19,14 +19,13 @@ ...@@ -19,14 +19,13 @@
#define check_pgt_cache() do {} while (0) #define check_pgt_cache() do {} while (0)
unsigned long *crst_table_alloc(struct mm_struct *, int); unsigned long *crst_table_alloc(struct mm_struct *);
void crst_table_free(struct mm_struct *, unsigned long *); void crst_table_free(struct mm_struct *, unsigned long *);
void crst_table_free_rcu(struct mm_struct *, unsigned long *); void crst_table_free_rcu(struct mm_struct *, unsigned long *);
unsigned long *page_table_alloc(struct mm_struct *); unsigned long *page_table_alloc(struct mm_struct *);
void page_table_free(struct mm_struct *, unsigned long *); void page_table_free(struct mm_struct *, unsigned long *);
void page_table_free_rcu(struct mm_struct *, unsigned long *); void page_table_free_rcu(struct mm_struct *, unsigned long *);
void disable_noexec(struct mm_struct *, struct task_struct *);
static inline void clear_table(unsigned long *s, unsigned long val, size_t n) static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
{ {
...@@ -50,9 +49,6 @@ static inline void clear_table(unsigned long *s, unsigned long val, size_t n) ...@@ -50,9 +49,6 @@ static inline void clear_table(unsigned long *s, unsigned long val, size_t n)
static inline void crst_table_init(unsigned long *crst, unsigned long entry) static inline void crst_table_init(unsigned long *crst, unsigned long entry)
{ {
clear_table(crst, entry, sizeof(unsigned long)*2048); clear_table(crst, entry, sizeof(unsigned long)*2048);
crst = get_shadow_table(crst);
if (crst)
clear_table(crst, entry, sizeof(unsigned long)*2048);
} }
#ifndef __s390x__ #ifndef __s390x__
...@@ -69,10 +65,7 @@ static inline unsigned long pgd_entry_type(struct mm_struct *mm) ...@@ -69,10 +65,7 @@ static inline unsigned long pgd_entry_type(struct mm_struct *mm)
#define pmd_free(mm, x) do { } while (0) #define pmd_free(mm, x) do { } while (0)
#define pgd_populate(mm, pgd, pud) BUG() #define pgd_populate(mm, pgd, pud) BUG()
#define pgd_populate_kernel(mm, pgd, pud) BUG()
#define pud_populate(mm, pud, pmd) BUG() #define pud_populate(mm, pud, pmd) BUG()
#define pud_populate_kernel(mm, pud, pmd) BUG()
#else /* __s390x__ */ #else /* __s390x__ */
...@@ -90,7 +83,7 @@ void crst_table_downgrade(struct mm_struct *, unsigned long limit); ...@@ -90,7 +83,7 @@ void crst_table_downgrade(struct mm_struct *, unsigned long limit);
static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address) static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
{ {
unsigned long *table = crst_table_alloc(mm, mm->context.noexec); unsigned long *table = crst_table_alloc(mm);
if (table) if (table)
crst_table_init(table, _REGION3_ENTRY_EMPTY); crst_table_init(table, _REGION3_ENTRY_EMPTY);
return (pud_t *) table; return (pud_t *) table;
...@@ -99,43 +92,21 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address) ...@@ -99,43 +92,21 @@ static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr) static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long vmaddr)
{ {
unsigned long *table = crst_table_alloc(mm, mm->context.noexec); unsigned long *table = crst_table_alloc(mm);
if (table) if (table)
crst_table_init(table, _SEGMENT_ENTRY_EMPTY); crst_table_init(table, _SEGMENT_ENTRY_EMPTY);
return (pmd_t *) table; return (pmd_t *) table;
} }
#define pmd_free(mm, pmd) crst_table_free(mm, (unsigned long *) pmd) #define pmd_free(mm, pmd) crst_table_free(mm, (unsigned long *) pmd)
static inline void pgd_populate_kernel(struct mm_struct *mm,
pgd_t *pgd, pud_t *pud)
{
pgd_val(*pgd) = _REGION2_ENTRY | __pa(pud);
}
static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud) static inline void pgd_populate(struct mm_struct *mm, pgd_t *pgd, pud_t *pud)
{ {
pgd_populate_kernel(mm, pgd, pud); pgd_val(*pgd) = _REGION2_ENTRY | __pa(pud);
if (mm->context.noexec) {
pgd = get_shadow_table(pgd);
pud = get_shadow_table(pud);
pgd_populate_kernel(mm, pgd, pud);
}
}
static inline void pud_populate_kernel(struct mm_struct *mm,
pud_t *pud, pmd_t *pmd)
{
pud_val(*pud) = _REGION3_ENTRY | __pa(pmd);
} }
static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
{ {
pud_populate_kernel(mm, pud, pmd); pud_val(*pud) = _REGION3_ENTRY | __pa(pmd);
if (mm->context.noexec) {
pud = get_shadow_table(pud);
pmd = get_shadow_table(pmd);
pud_populate_kernel(mm, pud, pmd);
}
} }
#endif /* __s390x__ */ #endif /* __s390x__ */
...@@ -143,29 +114,19 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd) ...@@ -143,29 +114,19 @@ static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
static inline pgd_t *pgd_alloc(struct mm_struct *mm) static inline pgd_t *pgd_alloc(struct mm_struct *mm)
{ {
spin_lock_init(&mm->context.list_lock); spin_lock_init(&mm->context.list_lock);
INIT_LIST_HEAD(&mm->context.crst_list);
INIT_LIST_HEAD(&mm->context.pgtable_list); INIT_LIST_HEAD(&mm->context.pgtable_list);
return (pgd_t *) return (pgd_t *) crst_table_alloc(mm);
crst_table_alloc(mm, user_mode == SECONDARY_SPACE_MODE);
} }
#define pgd_free(mm, pgd) crst_table_free(mm, (unsigned long *) pgd) #define pgd_free(mm, pgd) crst_table_free(mm, (unsigned long *) pgd)
static inline void pmd_populate_kernel(struct mm_struct *mm,
pmd_t *pmd, pte_t *pte)
{
pmd_val(*pmd) = _SEGMENT_ENTRY + __pa(pte);
}
static inline void pmd_populate(struct mm_struct *mm, static inline void pmd_populate(struct mm_struct *mm,
pmd_t *pmd, pgtable_t pte) pmd_t *pmd, pgtable_t pte)
{ {
pmd_populate_kernel(mm, pmd, pte); pmd_val(*pmd) = _SEGMENT_ENTRY + __pa(pte);
if (mm->context.noexec) {
pmd = get_shadow_table(pmd);
pmd_populate_kernel(mm, pmd, pte + PTRS_PER_PTE);
}
} }
#define pmd_populate_kernel(mm, pmd, pte) pmd_populate(mm, pmd, pte)
#define pmd_pgtable(pmd) \ #define pmd_pgtable(pmd) \
(pgtable_t)(pmd_val(pmd) & -sizeof(pte_t)*PTRS_PER_PTE) (pgtable_t)(pmd_val(pmd) & -sizeof(pte_t)*PTRS_PER_PTE)
......
...@@ -31,9 +31,8 @@ ...@@ -31,9 +31,8 @@
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/mm_types.h> #include <linux/mm_types.h>
#include <asm/bitops.h>
#include <asm/bug.h> #include <asm/bug.h>
#include <asm/processor.h> #include <asm/page.h>
extern pgd_t swapper_pg_dir[] __attribute__ ((aligned (4096))); extern pgd_t swapper_pg_dir[] __attribute__ ((aligned (4096)));
extern void paging_init(void); extern void paging_init(void);
...@@ -243,11 +242,13 @@ extern unsigned long VMALLOC_START; ...@@ -243,11 +242,13 @@ extern unsigned long VMALLOC_START;
/* Software bits in the page table entry */ /* Software bits in the page table entry */
#define _PAGE_SWT 0x001 /* SW pte type bit t */ #define _PAGE_SWT 0x001 /* SW pte type bit t */
#define _PAGE_SWX 0x002 /* SW pte type bit x */ #define _PAGE_SWX 0x002 /* SW pte type bit x */
#define _PAGE_SPECIAL 0x004 /* SW associated with special page */ #define _PAGE_SWC 0x004 /* SW pte changed bit (for KVM) */
#define _PAGE_SWR 0x008 /* SW pte referenced bit (for KVM) */
#define _PAGE_SPECIAL 0x010 /* SW associated with special page */
#define __HAVE_ARCH_PTE_SPECIAL #define __HAVE_ARCH_PTE_SPECIAL
/* Set of bits not changed in pte_modify */ /* Set of bits not changed in pte_modify */
#define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL) #define _PAGE_CHG_MASK (PAGE_MASK | _PAGE_SPECIAL | _PAGE_SWC | _PAGE_SWR)
/* Six different types of pages. */ /* Six different types of pages. */
#define _PAGE_TYPE_EMPTY 0x400 #define _PAGE_TYPE_EMPTY 0x400
...@@ -256,8 +257,6 @@ extern unsigned long VMALLOC_START; ...@@ -256,8 +257,6 @@ extern unsigned long VMALLOC_START;
#define _PAGE_TYPE_FILE 0x601 /* bit 0x002 is used for offset !! */ #define _PAGE_TYPE_FILE 0x601 /* bit 0x002 is used for offset !! */
#define _PAGE_TYPE_RO 0x200 #define _PAGE_TYPE_RO 0x200
#define _PAGE_TYPE_RW 0x000 #define _PAGE_TYPE_RW 0x000
#define _PAGE_TYPE_EX_RO 0x202
#define _PAGE_TYPE_EX_RW 0x002
/* /*
* Only four types for huge pages, using the invalid bit and protection bit * Only four types for huge pages, using the invalid bit and protection bit
...@@ -287,8 +286,6 @@ extern unsigned long VMALLOC_START; ...@@ -287,8 +286,6 @@ extern unsigned long VMALLOC_START;
* _PAGE_TYPE_FILE 11?1 -> 11?1 * _PAGE_TYPE_FILE 11?1 -> 11?1
* _PAGE_TYPE_RO 0100 -> 1100 * _PAGE_TYPE_RO 0100 -> 1100
* _PAGE_TYPE_RW 0000 -> 1000 * _PAGE_TYPE_RW 0000 -> 1000
* _PAGE_TYPE_EX_RO 0110 -> 1110
* _PAGE_TYPE_EX_RW 0010 -> 1010
* *
* pte_none is true for bits combinations 1000, 1010, 1100, 1110 * pte_none is true for bits combinations 1000, 1010, 1100, 1110
* pte_present is true for bits combinations 0000, 0010, 0100, 0110, 1001 * pte_present is true for bits combinations 0000, 0010, 0100, 0110, 1001
...@@ -297,14 +294,17 @@ extern unsigned long VMALLOC_START; ...@@ -297,14 +294,17 @@ extern unsigned long VMALLOC_START;
*/ */
/* Page status table bits for virtualization */ /* Page status table bits for virtualization */
#define RCP_PCL_BIT 55 #define RCP_ACC_BITS 0xf000000000000000UL
#define RCP_HR_BIT 54 #define RCP_FP_BIT 0x0800000000000000UL
#define RCP_HC_BIT 53 #define RCP_PCL_BIT 0x0080000000000000UL
#define RCP_GR_BIT 50 #define RCP_HR_BIT 0x0040000000000000UL
#define RCP_GC_BIT 49 #define RCP_HC_BIT 0x0020000000000000UL
#define RCP_GR_BIT 0x0004000000000000UL
/* User dirty bit for KVM's migration feature */ #define RCP_GC_BIT 0x0002000000000000UL
#define KVM_UD_BIT 47
/* User dirty / referenced bit for KVM's migration feature */
#define KVM_UR_BIT 0x0000800000000000UL
#define KVM_UC_BIT 0x0000400000000000UL
#ifndef __s390x__ #ifndef __s390x__
...@@ -377,85 +377,54 @@ extern unsigned long VMALLOC_START; ...@@ -377,85 +377,54 @@ extern unsigned long VMALLOC_START;
#define _ASCE_USER_BITS (_ASCE_SPACE_SWITCH | _ASCE_PRIVATE_SPACE | \ #define _ASCE_USER_BITS (_ASCE_SPACE_SWITCH | _ASCE_PRIVATE_SPACE | \
_ASCE_ALT_EVENT) _ASCE_ALT_EVENT)
/* Bits int the storage key */
#define _PAGE_CHANGED 0x02 /* HW changed bit */
#define _PAGE_REFERENCED 0x04 /* HW referenced bit */
/* /*
* Page protection definitions. * Page protection definitions.
*/ */
#define PAGE_NONE __pgprot(_PAGE_TYPE_NONE) #define PAGE_NONE __pgprot(_PAGE_TYPE_NONE)
#define PAGE_RO __pgprot(_PAGE_TYPE_RO) #define PAGE_RO __pgprot(_PAGE_TYPE_RO)
#define PAGE_RW __pgprot(_PAGE_TYPE_RW) #define PAGE_RW __pgprot(_PAGE_TYPE_RW)
#define PAGE_EX_RO __pgprot(_PAGE_TYPE_EX_RO)
#define PAGE_EX_RW __pgprot(_PAGE_TYPE_EX_RW)
#define PAGE_KERNEL PAGE_RW #define PAGE_KERNEL PAGE_RW
#define PAGE_COPY PAGE_RO #define PAGE_COPY PAGE_RO
/* /*
* Dependent on the EXEC_PROTECT option s390 can do execute protection. * On s390 the page table entry has an invalid bit and a read-only bit.
* Write permission always implies read permission. In theory with a * Read permission implies execute permission and write permission
* primary/secondary page table execute only can be implemented but * implies read permission.
* it would cost an additional bit in the pte to distinguish all the
* different pte types. To avoid that execute permission currently
* implies read permission as well.
*/ */
/*xwr*/ /*xwr*/
#define __P000 PAGE_NONE #define __P000 PAGE_NONE
#define __P001 PAGE_RO #define __P001 PAGE_RO
#define __P010 PAGE_RO #define __P010 PAGE_RO
#define __P011 PAGE_RO #define __P011 PAGE_RO
#define __P100 PAGE_EX_RO #define __P100 PAGE_RO
#define __P101 PAGE_EX_RO #define __P101 PAGE_RO
#define __P110 PAGE_EX_RO #define __P110 PAGE_RO
#define __P111 PAGE_EX_RO #define __P111 PAGE_RO
#define __S000 PAGE_NONE #define __S000 PAGE_NONE
#define __S001 PAGE_RO #define __S001 PAGE_RO
#define __S010 PAGE_RW #define __S010 PAGE_RW
#define __S011 PAGE_RW #define __S011 PAGE_RW
#define __S100 PAGE_EX_RO #define __S100 PAGE_RO
#define __S101 PAGE_EX_RO #define __S101 PAGE_RO
#define __S110 PAGE_EX_RW #define __S110 PAGE_RW
#define __S111 PAGE_EX_RW #define __S111 PAGE_RW
#ifndef __s390x__
# define PxD_SHADOW_SHIFT 1
#else /* __s390x__ */
# define PxD_SHADOW_SHIFT 2
#endif /* __s390x__ */
static inline void *get_shadow_table(void *table) static inline int mm_exclusive(struct mm_struct *mm)
{ {
unsigned long addr, offset; return likely(mm == current->active_mm &&
struct page *page; atomic_read(&mm->context.attach_count) <= 1);
addr = (unsigned long) table;
offset = addr & ((PAGE_SIZE << PxD_SHADOW_SHIFT) - 1);
page = virt_to_page((void *)(addr ^ offset));
return (void *)(addr_t)(page->index ? (page->index | offset) : 0UL);
} }
/* static inline int mm_has_pgste(struct mm_struct *mm)
* Certain architectures need to do special things when PTEs
* within a page table are directly modified. Thus, the following
* hook is made available.
*/
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t entry)
{ {
*ptep = entry; #ifdef CONFIG_PGSTE
if (mm->context.noexec) { if (unlikely(mm->context.has_pgste))
if (!(pte_val(entry) & _PAGE_INVALID) && return 1;
(pte_val(entry) & _PAGE_SWX)) #endif
pte_val(entry) |= _PAGE_RO; return 0;
else
pte_val(entry) = _PAGE_TYPE_EMPTY;
ptep[PTRS_PER_PTE] = entry;
}
} }
/* /*
* pgd/pmd/pte query functions * pgd/pmd/pte query functions
*/ */
...@@ -568,52 +537,127 @@ static inline int pte_special(pte_t pte) ...@@ -568,52 +537,127 @@ static inline int pte_special(pte_t pte)
} }
#define __HAVE_ARCH_PTE_SAME #define __HAVE_ARCH_PTE_SAME
#define pte_same(a,b) (pte_val(a) == pte_val(b)) static inline int pte_same(pte_t a, pte_t b)
{
return pte_val(a) == pte_val(b);
}
static inline void rcp_lock(pte_t *ptep) static inline pgste_t pgste_get_lock(pte_t *ptep)
{ {
unsigned long new = 0;
#ifdef CONFIG_PGSTE #ifdef CONFIG_PGSTE
unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE); unsigned long old;
preempt_disable(); preempt_disable();
while (test_and_set_bit(RCP_PCL_BIT, pgste)) asm(
; " lg %0,%2\n"
"0: lgr %1,%0\n"
" nihh %0,0xff7f\n" /* clear RCP_PCL_BIT in old */
" oihh %1,0x0080\n" /* set RCP_PCL_BIT in new */
" csg %0,%1,%2\n"
" jl 0b\n"
: "=&d" (old), "=&d" (new), "=Q" (ptep[PTRS_PER_PTE])
: "Q" (ptep[PTRS_PER_PTE]) : "cc");
#endif #endif
return __pgste(new);
} }
static inline void rcp_unlock(pte_t *ptep) static inline void pgste_set_unlock(pte_t *ptep, pgste_t pgste)
{ {
#ifdef CONFIG_PGSTE #ifdef CONFIG_PGSTE
unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE); asm(
clear_bit(RCP_PCL_BIT, pgste); " nihh %1,0xff7f\n" /* clear RCP_PCL_BIT */
" stg %1,%0\n"
: "=Q" (ptep[PTRS_PER_PTE])
: "d" (pgste_val(pgste)), "Q" (ptep[PTRS_PER_PTE]) : "cc");
preempt_enable(); preempt_enable();
#endif #endif
} }
/* forward declaration for SetPageUptodate in page-flags.h*/ static inline pgste_t pgste_update_all(pte_t *ptep, pgste_t pgste)
static inline void page_clear_dirty(struct page *page, int mapped);
#include <linux/page-flags.h>
static inline void ptep_rcp_copy(pte_t *ptep)
{ {
#ifdef CONFIG_PGSTE #ifdef CONFIG_PGSTE
struct page *page = virt_to_page(pte_val(*ptep)); unsigned long pfn, bits;
unsigned int skey; unsigned char skey;
unsigned long *pgste = (unsigned long *) (ptep + PTRS_PER_PTE);
pfn = pte_val(*ptep) >> PAGE_SHIFT;
skey = page_get_storage_key(page_to_phys(page)); skey = page_get_storage_key(pfn);
if (skey & _PAGE_CHANGED) { bits = skey & (_PAGE_CHANGED | _PAGE_REFERENCED);
set_bit_simple(RCP_GC_BIT, pgste); /* Clear page changed & referenced bit in the storage key */
set_bit_simple(KVM_UD_BIT, pgste); if (bits) {
} skey ^= bits;
if (skey & _PAGE_REFERENCED) page_set_storage_key(pfn, skey, 1);
set_bit_simple(RCP_GR_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)) /* Transfer page changed & referenced bit to guest bits in pgste */
SetPageReferenced(page); pgste_val(pgste) |= bits << 48; /* RCP_GR_BIT & RCP_GC_BIT */
/* Get host changed & referenced bits from pgste */
bits |= (pgste_val(pgste) & (RCP_HR_BIT | RCP_HC_BIT)) >> 52;
/* Clear host bits in pgste. */
pgste_val(pgste) &= ~(RCP_HR_BIT | RCP_HC_BIT);
pgste_val(pgste) &= ~(RCP_ACC_BITS | RCP_FP_BIT);
/* Copy page access key and fetch protection bit to pgste */
pgste_val(pgste) |=
(unsigned long) (skey & (_PAGE_ACC_BITS | _PAGE_FP_BIT)) << 56;
/* Transfer changed and referenced to kvm user bits */
pgste_val(pgste) |= bits << 45; /* KVM_UR_BIT & KVM_UC_BIT */
/* Transfer changed & referenced to pte sofware bits */
pte_val(*ptep) |= bits << 1; /* _PAGE_SWR & _PAGE_SWC */
#endif
return pgste;
}
static inline pgste_t pgste_update_young(pte_t *ptep, pgste_t pgste)
{
#ifdef CONFIG_PGSTE
int young;
young = page_reset_referenced(pte_val(*ptep) & PAGE_MASK);
/* Transfer page referenced bit to pte software bit (host view) */
if (young || (pgste_val(pgste) & RCP_HR_BIT))
pte_val(*ptep) |= _PAGE_SWR;
/* Clear host referenced bit in pgste. */
pgste_val(pgste) &= ~RCP_HR_BIT;
/* Transfer page referenced bit to guest bit in pgste */
pgste_val(pgste) |= (unsigned long) young << 50; /* set RCP_GR_BIT */
#endif #endif
return pgste;
}
static inline void pgste_set_pte(pte_t *ptep, pgste_t pgste)
{
#ifdef CONFIG_PGSTE
unsigned long pfn;
unsigned long okey, nkey;
pfn = pte_val(*ptep) >> PAGE_SHIFT;
okey = nkey = page_get_storage_key(pfn);
nkey &= ~(_PAGE_ACC_BITS | _PAGE_FP_BIT);
/* Set page access key and fetch protection bit from pgste */
nkey |= (pgste_val(pgste) & (RCP_ACC_BITS | RCP_FP_BIT)) >> 56;
if (okey != nkey)
page_set_storage_key(pfn, nkey, 1);
#endif
}
/*
* Certain architectures need to do special things when PTEs
* within a page table are directly modified. Thus, the following
* hook is made available.
*/
static inline void set_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, pte_t entry)
{
pgste_t pgste;
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste_set_pte(ptep, pgste);
*ptep = entry;
pgste_set_unlock(ptep, pgste);
} else
*ptep = entry;
} }
/* /*
...@@ -627,19 +671,19 @@ static inline int pte_write(pte_t pte) ...@@ -627,19 +671,19 @@ static inline int pte_write(pte_t pte)
static inline int pte_dirty(pte_t pte) static inline int pte_dirty(pte_t pte)
{ {
/* A pte is neither clean nor dirty on s/390. The dirty bit #ifdef CONFIG_PGSTE
* is in the storage key. See page_test_and_clear_dirty for if (pte_val(pte) & _PAGE_SWC)
* details. return 1;
*/ #endif
return 0; return 0;
} }
static inline int pte_young(pte_t pte) static inline int pte_young(pte_t pte)
{ {
/* A pte is neither young nor old on s/390. The young bit #ifdef CONFIG_PGSTE
* is in the storage key. See page_test_and_clear_young for if (pte_val(pte) & _PAGE_SWR)
* details. return 1;
*/ #endif
return 0; return 0;
} }
...@@ -647,64 +691,30 @@ static inline int pte_young(pte_t pte) ...@@ -647,64 +691,30 @@ static inline int pte_young(pte_t pte)
* pgd/pmd/pte modification functions * pgd/pmd/pte modification functions
*/ */
#ifndef __s390x__ static inline void pgd_clear(pgd_t *pgd)
#define pgd_clear(pgd) do { } while (0)
#define pud_clear(pud) do { } while (0)
#else /* __s390x__ */
static inline void pgd_clear_kernel(pgd_t * pgd)
{ {
#ifdef __s390x__
if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2) if ((pgd_val(*pgd) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R2)
pgd_val(*pgd) = _REGION2_ENTRY_EMPTY; pgd_val(*pgd) = _REGION2_ENTRY_EMPTY;
#endif
} }
static inline void pgd_clear(pgd_t * pgd) static inline void pud_clear(pud_t *pud)
{
pgd_t *shadow = get_shadow_table(pgd);
pgd_clear_kernel(pgd);
if (shadow)
pgd_clear_kernel(shadow);
}
static inline void pud_clear_kernel(pud_t *pud)
{ {
#ifdef __s390x__
if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) if ((pud_val(*pud) & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3)
pud_val(*pud) = _REGION3_ENTRY_EMPTY; pud_val(*pud) = _REGION3_ENTRY_EMPTY;
#endif
} }
static inline void pud_clear(pud_t *pud) static inline void pmd_clear(pmd_t *pmdp)
{
pud_t *shadow = get_shadow_table(pud);
pud_clear_kernel(pud);
if (shadow)
pud_clear_kernel(shadow);
}
#endif /* __s390x__ */
static inline void pmd_clear_kernel(pmd_t * pmdp)
{ {
pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY; pmd_val(*pmdp) = _SEGMENT_ENTRY_EMPTY;
} }
static inline void pmd_clear(pmd_t *pmd)
{
pmd_t *shadow = get_shadow_table(pmd);
pmd_clear_kernel(pmd);
if (shadow)
pmd_clear_kernel(shadow);
}
static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep) static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{ {
pte_val(*ptep) = _PAGE_TYPE_EMPTY; pte_val(*ptep) = _PAGE_TYPE_EMPTY;
if (mm->context.noexec)
pte_val(ptep[PTRS_PER_PTE]) = _PAGE_TYPE_EMPTY;
} }
/* /*
...@@ -734,35 +744,27 @@ static inline pte_t pte_mkwrite(pte_t pte) ...@@ -734,35 +744,27 @@ static inline pte_t pte_mkwrite(pte_t pte)
static inline pte_t pte_mkclean(pte_t pte) static inline pte_t pte_mkclean(pte_t pte)
{ {
/* The only user of pte_mkclean is the fork() code. #ifdef CONFIG_PGSTE
We must *not* clear the *physical* page dirty bit pte_val(pte) &= ~_PAGE_SWC;
just because fork() wants to clear the dirty bit in #endif
*one* of the page's mappings. So we just do nothing. */
return pte; return pte;
} }
static inline pte_t pte_mkdirty(pte_t pte) static inline pte_t pte_mkdirty(pte_t pte)
{ {
/* We do not explicitly set the dirty bit because the
* sske instruction is slow. It is faster to let the
* next instruction set the dirty bit.
*/
return pte; return pte;
} }
static inline pte_t pte_mkold(pte_t pte) static inline pte_t pte_mkold(pte_t pte)
{ {
/* S/390 doesn't keep its dirty/referenced bit in the pte. #ifdef CONFIG_PGSTE
* There is no point in clearing the real referenced bit. pte_val(pte) &= ~_PAGE_SWR;
*/ #endif
return pte; return pte;
} }
static inline pte_t pte_mkyoung(pte_t pte) static inline pte_t pte_mkyoung(pte_t pte)
{ {
/* S/390 doesn't keep its dirty/referenced bit in the pte.
* There is no point in setting the real referenced bit.
*/
return pte; return pte;
} }
...@@ -800,62 +802,60 @@ static inline pte_t pte_mkhuge(pte_t pte) ...@@ -800,62 +802,60 @@ static inline pte_t pte_mkhuge(pte_t pte)
} }
#endif #endif
#ifdef CONFIG_PGSTE
/* /*
* Get (and clear) the user dirty bit for a PTE. * Get (and clear) the user dirty bit for a pte.
*/ */
static inline int kvm_s390_test_and_clear_page_dirty(struct mm_struct *mm, static inline int ptep_test_and_clear_user_dirty(struct mm_struct *mm,
pte_t *ptep) pte_t *ptep)
{ {
int dirty; pgste_t pgste;
unsigned long *pgste; int dirty = 0;
struct page *page;
unsigned int skey; if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
if (!mm->context.has_pgste) pgste = pgste_update_all(ptep, pgste);
return -EINVAL; dirty = !!(pgste_val(pgste) & KVM_UC_BIT);
rcp_lock(ptep); pgste_val(pgste) &= ~KVM_UC_BIT;
pgste = (unsigned long *) (ptep + PTRS_PER_PTE); pgste_set_unlock(ptep, pgste);
page = virt_to_page(pte_val(*ptep)); return dirty;
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, 1);
rcp_unlock(ptep);
return dirty; return dirty;
} }
#endif
/*
* Get (and clear) the user referenced bit for a pte.
*/
static inline int ptep_test_and_clear_user_young(struct mm_struct *mm,
pte_t *ptep)
{
pgste_t pgste;
int young = 0;
if (mm_has_pgste(mm)) {
pgste = pgste_get_lock(ptep);
pgste = pgste_update_young(ptep, pgste);
young = !!(pgste_val(pgste) & KVM_UR_BIT);
pgste_val(pgste) &= ~KVM_UR_BIT;
pgste_set_unlock(ptep, pgste);
}
return young;
}
#define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG #define __HAVE_ARCH_PTEP_TEST_AND_CLEAR_YOUNG
static inline int ptep_test_and_clear_young(struct vm_area_struct *vma, static inline int ptep_test_and_clear_young(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep) unsigned long addr, pte_t *ptep)
{ {
#ifdef CONFIG_PGSTE pgste_t pgste;
unsigned long physpage; pte_t pte;
int young;
unsigned long *pgste;
if (!vma->vm_mm->context.has_pgste) if (mm_has_pgste(vma->vm_mm)) {
return 0; pgste = pgste_get_lock(ptep);
physpage = pte_val(*ptep) & PAGE_MASK; pgste = pgste_update_young(ptep, pgste);
pgste = (unsigned long *) (ptep + PTRS_PER_PTE); pte = *ptep;
*ptep = pte_mkold(pte);
young = ((page_get_storage_key(physpage) & _PAGE_REFERENCED) != 0); pgste_set_unlock(ptep, pgste);
rcp_lock(ptep); return pte_young(pte);
if (young) }
set_bit_simple(RCP_GR_BIT, pgste);
young |= test_and_clear_bit_simple(RCP_HR_BIT, pgste);
rcp_unlock(ptep);
return young;
#endif
return 0; return 0;
} }
...@@ -867,10 +867,7 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma, ...@@ -867,10 +867,7 @@ static inline int ptep_clear_flush_young(struct vm_area_struct *vma,
* On s390 reference bits are in storage key and never in TLB * On s390 reference bits are in storage key and never in TLB
* With virtualization we handle the reference bit, without we * With virtualization we handle the reference bit, without we
* we can simply return */ * we can simply return */
#ifdef CONFIG_PGSTE
return ptep_test_and_clear_young(vma, address, ptep); return ptep_test_and_clear_young(vma, address, ptep);
#endif
return 0;
} }
static inline void __ptep_ipte(unsigned long address, pte_t *ptep) static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
...@@ -890,25 +887,6 @@ static inline void __ptep_ipte(unsigned long address, pte_t *ptep) ...@@ -890,25 +887,6 @@ static inline void __ptep_ipte(unsigned long address, pte_t *ptep)
} }
} }
static inline void ptep_invalidate(struct mm_struct *mm,
unsigned long address, pte_t *ptep)
{
if (mm->context.has_pgste) {
rcp_lock(ptep);
__ptep_ipte(address, ptep);
ptep_rcp_copy(ptep);
pte_val(*ptep) = _PAGE_TYPE_EMPTY;
rcp_unlock(ptep);
return;
}
__ptep_ipte(address, ptep);
pte_val(*ptep) = _PAGE_TYPE_EMPTY;
if (mm->context.noexec) {
__ptep_ipte(address, ptep + PTRS_PER_PTE);
pte_val(*(ptep + PTRS_PER_PTE)) = _PAGE_TYPE_EMPTY;
}
}
/* /*
* This is hard to understand. ptep_get_and_clear and ptep_clear_flush * This is hard to understand. ptep_get_and_clear and ptep_clear_flush
* both clear the TLB for the unmapped pte. The reason is that * both clear the TLB for the unmapped pte. The reason is that
...@@ -923,24 +901,72 @@ static inline void ptep_invalidate(struct mm_struct *mm, ...@@ -923,24 +901,72 @@ static inline void ptep_invalidate(struct mm_struct *mm,
* is a nop. * is a nop.
*/ */
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR #define __HAVE_ARCH_PTEP_GET_AND_CLEAR
#define ptep_get_and_clear(__mm, __address, __ptep) \ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
({ \ unsigned long address, pte_t *ptep)
pte_t __pte = *(__ptep); \ {
(__mm)->context.flush_mm = 1; \ pgste_t pgste;
if (atomic_read(&(__mm)->context.attach_count) > 1 || \ pte_t pte;
(__mm) != current->active_mm) \
ptep_invalidate(__mm, __address, __ptep); \ mm->context.flush_mm = 1;
else \ if (mm_has_pgste(mm))
pte_clear((__mm), (__address), (__ptep)); \ pgste = pgste_get_lock(ptep);
__pte; \
}) pte = *ptep;
if (!mm_exclusive(mm))
__ptep_ipte(address, ptep);
pte_val(*ptep) = _PAGE_TYPE_EMPTY;
if (mm_has_pgste(mm)) {
pgste = pgste_update_all(&pte, pgste);
pgste_set_unlock(ptep, pgste);
}
return pte;
}
#define __HAVE_ARCH_PTEP_MODIFY_PROT_TRANSACTION
static inline pte_t ptep_modify_prot_start(struct mm_struct *mm,
unsigned long address,
pte_t *ptep)
{
pte_t pte;
mm->context.flush_mm = 1;
if (mm_has_pgste(mm))
pgste_get_lock(ptep);
pte = *ptep;
if (!mm_exclusive(mm))
__ptep_ipte(address, ptep);
return pte;
}
static inline void ptep_modify_prot_commit(struct mm_struct *mm,
unsigned long address,
pte_t *ptep, pte_t pte)
{
*ptep = pte;
if (mm_has_pgste(mm))
pgste_set_unlock(ptep, *(pgste_t *)(ptep + PTRS_PER_PTE));
}
#define __HAVE_ARCH_PTEP_CLEAR_FLUSH #define __HAVE_ARCH_PTEP_CLEAR_FLUSH
static inline pte_t ptep_clear_flush(struct vm_area_struct *vma, static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep) unsigned long address, pte_t *ptep)
{ {
pte_t pte = *ptep; pgste_t pgste;
ptep_invalidate(vma->vm_mm, address, ptep); pte_t pte;
if (mm_has_pgste(vma->vm_mm))
pgste = pgste_get_lock(ptep);
pte = *ptep;
__ptep_ipte(address, ptep);
pte_val(*ptep) = _PAGE_TYPE_EMPTY;
if (mm_has_pgste(vma->vm_mm)) {
pgste = pgste_update_all(&pte, pgste);
pgste_set_unlock(ptep, pgste);
}
return pte; return pte;
} }
...@@ -953,76 +979,67 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma, ...@@ -953,76 +979,67 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
*/ */
#define __HAVE_ARCH_PTEP_GET_AND_CLEAR_FULL #define __HAVE_ARCH_PTEP_GET_AND_CLEAR_FULL
static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
unsigned long addr, unsigned long address,
pte_t *ptep, int full) pte_t *ptep, int full)
{ {
pte_t pte = *ptep; pgste_t pgste;
pte_t pte;
if (full) if (mm_has_pgste(mm))
pte_clear(mm, addr, ptep); pgste = pgste_get_lock(ptep);
else
ptep_invalidate(mm, addr, ptep); pte = *ptep;
if (!full)
__ptep_ipte(address, ptep);
pte_val(*ptep) = _PAGE_TYPE_EMPTY;
if (mm_has_pgste(mm)) {
pgste = pgste_update_all(&pte, pgste);
pgste_set_unlock(ptep, pgste);
}
return pte; return pte;
} }
#define __HAVE_ARCH_PTEP_SET_WRPROTECT #define __HAVE_ARCH_PTEP_SET_WRPROTECT
#define ptep_set_wrprotect(__mm, __addr, __ptep) \ static inline pte_t ptep_set_wrprotect(struct mm_struct *mm,
({ \ unsigned long address, pte_t *ptep)
pte_t __pte = *(__ptep); \ {
if (pte_write(__pte)) { \ pgste_t pgste;
(__mm)->context.flush_mm = 1; \ pte_t pte = *ptep;
if (atomic_read(&(__mm)->context.attach_count) > 1 || \
(__mm) != current->active_mm) \
ptep_invalidate(__mm, __addr, __ptep); \
set_pte_at(__mm, __addr, __ptep, pte_wrprotect(__pte)); \
} \
})
#define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS if (pte_write(pte)) {
#define ptep_set_access_flags(__vma, __addr, __ptep, __entry, __dirty) \ mm->context.flush_mm = 1;
({ \ if (mm_has_pgste(mm))
int __changed = !pte_same(*(__ptep), __entry); \ pgste = pgste_get_lock(ptep);
if (__changed) { \
ptep_invalidate((__vma)->vm_mm, __addr, __ptep); \
set_pte_at((__vma)->vm_mm, __addr, __ptep, __entry); \
} \
__changed; \
})
/* if (!mm_exclusive(mm))
* Test and clear dirty bit in storage key. __ptep_ipte(address, ptep);
* We can't clear the changed bit atomically. This is a potential *ptep = pte_wrprotect(pte);
* race against modification of the referenced bit. This function
* should therefore only be called if it is not mapped in any
* address space.
*/
#define __HAVE_ARCH_PAGE_TEST_DIRTY
static inline int page_test_dirty(struct page *page)
{
return (page_get_storage_key(page_to_phys(page)) & _PAGE_CHANGED) != 0;
}
#define __HAVE_ARCH_PAGE_CLEAR_DIRTY if (mm_has_pgste(mm))
static inline void page_clear_dirty(struct page *page, int mapped) pgste_set_unlock(ptep, pgste);
{ }
page_set_storage_key(page_to_phys(page), PAGE_DEFAULT_KEY, mapped); return pte;
} }
/* #define __HAVE_ARCH_PTEP_SET_ACCESS_FLAGS
* Test and clear referenced bit in storage key. static inline int ptep_set_access_flags(struct vm_area_struct *vma,
*/ unsigned long address, pte_t *ptep,
#define __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG pte_t entry, int dirty)
static inline int page_test_and_clear_young(struct page *page)
{ {
unsigned long physpage = page_to_phys(page); pgste_t pgste;
int ccode;
asm volatile( if (pte_same(*ptep, entry))
" rrbe 0,%1\n" return 0;
" ipm %0\n" if (mm_has_pgste(vma->vm_mm))
" srl %0,28\n" pgste = pgste_get_lock(ptep);
: "=d" (ccode) : "a" (physpage) : "cc" );
return ccode & 2; __ptep_ipte(address, ptep);
*ptep = entry;
if (mm_has_pgste(vma->vm_mm))
pgste_set_unlock(ptep, pgste);
return 1;
} }
/* /*
......
...@@ -84,6 +84,7 @@ struct thread_struct { ...@@ -84,6 +84,7 @@ struct thread_struct {
struct per_event per_event; /* Cause of the last PER trap */ struct per_event per_event; /* Cause of the last PER trap */
/* pfault_wait is used to block the process on a pfault event */ /* pfault_wait is used to block the process on a pfault event */
unsigned long pfault_wait; unsigned long pfault_wait;
struct list_head list;
}; };
typedef struct thread_struct thread_struct; typedef struct thread_struct thread_struct;
......
...@@ -50,7 +50,7 @@ static inline void __tlb_flush_full(struct mm_struct *mm) ...@@ -50,7 +50,7 @@ static inline void __tlb_flush_full(struct mm_struct *mm)
/* /*
* If the process only ran on the local cpu, do a local flush. * If the process only ran on the local cpu, do a local flush.
*/ */
local_cpumask = cpumask_of_cpu(smp_processor_id()); cpumask_copy(&local_cpumask, cpumask_of(smp_processor_id()));
if (cpumask_equal(mm_cpumask(mm), &local_cpumask)) if (cpumask_equal(mm_cpumask(mm), &local_cpumask))
__tlb_flush_local(); __tlb_flush_local();
else else
...@@ -80,15 +80,10 @@ static inline void __tlb_flush_mm(struct mm_struct * mm) ...@@ -80,15 +80,10 @@ static inline void __tlb_flush_mm(struct mm_struct * mm)
* on all cpus instead of doing a local flush if the mm * on all cpus instead of doing a local flush if the mm
* only ran on the local cpu. * only ran on the local cpu.
*/ */
if (MACHINE_HAS_IDTE) { if (MACHINE_HAS_IDTE)
if (mm->context.noexec)
__tlb_flush_idte((unsigned long)
get_shadow_table(mm->pgd) |
mm->context.asce_bits);
__tlb_flush_idte((unsigned long) mm->pgd | __tlb_flush_idte((unsigned long) mm->pgd |
mm->context.asce_bits); mm->context.asce_bits);
return; else
}
__tlb_flush_full(mm); __tlb_flush_full(mm);
} }
......
...@@ -385,6 +385,7 @@ ...@@ -385,6 +385,7 @@
/* Ignore system calls that are also reachable via sys_socket */ /* Ignore system calls that are also reachable via sys_socket */
#define __IGNORE_recvmmsg #define __IGNORE_recvmmsg
#define __IGNORE_sendmmsg
#define __ARCH_WANT_IPC_PARSE_VERSION #define __ARCH_WANT_IPC_PARSE_VERSION
#define __ARCH_WANT_OLD_READDIR #define __ARCH_WANT_OLD_READDIR
......
...@@ -124,13 +124,11 @@ int main(void) ...@@ -124,13 +124,11 @@ int main(void)
DEFINE(__LC_LAST_UPDATE_TIMER, offsetof(struct _lowcore, last_update_timer)); DEFINE(__LC_LAST_UPDATE_TIMER, offsetof(struct _lowcore, last_update_timer));
DEFINE(__LC_LAST_UPDATE_CLOCK, offsetof(struct _lowcore, last_update_clock)); DEFINE(__LC_LAST_UPDATE_CLOCK, offsetof(struct _lowcore, last_update_clock));
DEFINE(__LC_CURRENT, offsetof(struct _lowcore, current_task)); DEFINE(__LC_CURRENT, offsetof(struct _lowcore, current_task));
DEFINE(__LC_CURRENT_PID, offsetof(struct _lowcore, current_pid));
DEFINE(__LC_THREAD_INFO, offsetof(struct _lowcore, thread_info)); DEFINE(__LC_THREAD_INFO, offsetof(struct _lowcore, thread_info));
DEFINE(__LC_KERNEL_STACK, offsetof(struct _lowcore, kernel_stack)); DEFINE(__LC_KERNEL_STACK, offsetof(struct _lowcore, kernel_stack));
DEFINE(__LC_ASYNC_STACK, offsetof(struct _lowcore, async_stack)); DEFINE(__LC_ASYNC_STACK, offsetof(struct _lowcore, async_stack));
DEFINE(__LC_PANIC_STACK, offsetof(struct _lowcore, panic_stack)); DEFINE(__LC_PANIC_STACK, offsetof(struct _lowcore, panic_stack));
DEFINE(__LC_KERNEL_ASCE, offsetof(struct _lowcore, kernel_asce));
DEFINE(__LC_USER_ASCE, offsetof(struct _lowcore, user_asce));
DEFINE(__LC_USER_EXEC_ASCE, offsetof(struct _lowcore, user_exec_asce));
DEFINE(__LC_INT_CLOCK, offsetof(struct _lowcore, int_clock)); DEFINE(__LC_INT_CLOCK, offsetof(struct _lowcore, int_clock));
DEFINE(__LC_MCCK_CLOCK, offsetof(struct _lowcore, mcck_clock)); DEFINE(__LC_MCCK_CLOCK, offsetof(struct _lowcore, mcck_clock));
DEFINE(__LC_MACHINE_FLAGS, offsetof(struct _lowcore, machine_flags)); DEFINE(__LC_MACHINE_FLAGS, offsetof(struct _lowcore, machine_flags));
......
...@@ -212,6 +212,7 @@ __switch_to: ...@@ -212,6 +212,7 @@ __switch_to:
lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4 lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4
lm %r6,%r15,__SF_GPRS(%r15) # load gprs of next task lm %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
st %r3,__LC_CURRENT # store task struct of next st %r3,__LC_CURRENT # store task struct of next
mvc __LC_CURRENT_PID(4,%r0),__TASK_pid(%r3) # store pid of next
st %r5,__LC_THREAD_INFO # store thread info of next st %r5,__LC_THREAD_INFO # store thread info of next
ahi %r5,STACK_SIZE # end of kernel stack of next ahi %r5,STACK_SIZE # end of kernel stack of next
st %r5,__LC_KERNEL_STACK # store end of kernel stack st %r5,__LC_KERNEL_STACK # store end of kernel stack
......
...@@ -220,6 +220,7 @@ __switch_to: ...@@ -220,6 +220,7 @@ __switch_to:
lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4 lctl %c4,%c4,__TASK_pid(%r3) # load pid to control reg. 4
lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task lmg %r6,%r15,__SF_GPRS(%r15) # load gprs of next task
stg %r3,__LC_CURRENT # store task struct of next stg %r3,__LC_CURRENT # store task struct of next
mvc __LC_CURRENT_PID+4(4,%r0),__TASK_pid(%r3) # store pid of next
stg %r5,__LC_THREAD_INFO # store thread info of next stg %r5,__LC_THREAD_INFO # store thread info of next
aghi %r5,STACK_SIZE # end of kernel stack of next aghi %r5,STACK_SIZE # end of kernel stack of next
stg %r5,__LC_KERNEL_STACK # store end of kernel stack stg %r5,__LC_KERNEL_STACK # store end of kernel stack
......
...@@ -32,6 +32,7 @@ static const struct irq_class intrclass_names[] = { ...@@ -32,6 +32,7 @@ static const struct irq_class intrclass_names[] = {
{.name = "VRT", .desc = "[EXT] Virtio" }, {.name = "VRT", .desc = "[EXT] Virtio" },
{.name = "SCP", .desc = "[EXT] Service Call" }, {.name = "SCP", .desc = "[EXT] Service Call" },
{.name = "IUC", .desc = "[EXT] IUCV" }, {.name = "IUC", .desc = "[EXT] IUCV" },
{.name = "CPM", .desc = "[EXT] CPU Measurement" },
{.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" }, {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" },
{.name = "QDI", .desc = "[I/O] QDIO Interrupt" }, {.name = "QDI", .desc = "[I/O] QDIO Interrupt" },
{.name = "DAS", .desc = "[I/O] DASD" }, {.name = "DAS", .desc = "[I/O] DASD" },
......
...@@ -9,41 +9,26 @@ ...@@ -9,41 +9,26 @@
#include <linux/compiler.h> #include <linux/compiler.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/errno.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/fs.h>
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/unistd.h>
#include <linux/ptrace.h>
#include <linux/vmalloc.h>
#include <linux/user.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/reboot.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/tick.h> #include <linux/tick.h>
#include <linux/elfcore.h>
#include <linux/kernel_stat.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/kprobes.h> #include <linux/kprobes.h>
#include <linux/random.h> #include <linux/random.h>
#include <asm/compat.h> #include <linux/module.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/irq.h> #include <asm/irq.h>
#include <asm/timer.h> #include <asm/timer.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/compat.h>
#include <asm/smp.h> #include <asm/smp.h>
#include "entry.h" #include "entry.h"
......
...@@ -305,7 +305,6 @@ static int set_amode_and_uaccess(unsigned long user_amode, ...@@ -305,7 +305,6 @@ static int set_amode_and_uaccess(unsigned long user_amode,
*/ */
static int __init early_parse_switch_amode(char *p) static int __init early_parse_switch_amode(char *p)
{ {
if (user_mode != SECONDARY_SPACE_MODE)
user_mode = PRIMARY_SPACE_MODE; user_mode = PRIMARY_SPACE_MODE;
return 0; return 0;
} }
...@@ -315,10 +314,6 @@ static int __init early_parse_user_mode(char *p) ...@@ -315,10 +314,6 @@ static int __init early_parse_user_mode(char *p)
{ {
if (p && strcmp(p, "primary") == 0) if (p && strcmp(p, "primary") == 0)
user_mode = PRIMARY_SPACE_MODE; user_mode = PRIMARY_SPACE_MODE;
#ifdef CONFIG_S390_EXEC_PROTECT
else if (p && strcmp(p, "secondary") == 0)
user_mode = SECONDARY_SPACE_MODE;
#endif
else if (!p || strcmp(p, "home") == 0) else if (!p || strcmp(p, "home") == 0)
user_mode = HOME_SPACE_MODE; user_mode = HOME_SPACE_MODE;
else else
...@@ -327,31 +322,9 @@ static int __init early_parse_user_mode(char *p) ...@@ -327,31 +322,9 @@ static int __init early_parse_user_mode(char *p)
} }
early_param("user_mode", early_parse_user_mode); early_param("user_mode", early_parse_user_mode);
#ifdef CONFIG_S390_EXEC_PROTECT
/*
* Enable execute protection?
*/
static int __init early_parse_noexec(char *p)
{
if (!strncmp(p, "off", 3))
return 0;
user_mode = SECONDARY_SPACE_MODE;
return 0;
}
early_param("noexec", early_parse_noexec);
#endif /* CONFIG_S390_EXEC_PROTECT */
static void setup_addressing_mode(void) static void setup_addressing_mode(void)
{ {
if (user_mode == SECONDARY_SPACE_MODE) { if (user_mode == PRIMARY_SPACE_MODE) {
if (set_amode_and_uaccess(PSW_ASC_SECONDARY,
PSW32_ASC_SECONDARY))
pr_info("Execute protection active, "
"mvcos available\n");
else
pr_info("Execute protection active, "
"mvcos not available\n");
} else if (user_mode == PRIMARY_SPACE_MODE) {
if (set_amode_and_uaccess(PSW_ASC_PRIMARY, PSW32_ASC_PRIMARY)) if (set_amode_and_uaccess(PSW_ASC_PRIMARY, PSW32_ASC_PRIMARY))
pr_info("Address spaces switched, " pr_info("Address spaces switched, "
"mvcos available\n"); "mvcos available\n");
......
...@@ -335,7 +335,7 @@ static int smp_rescan_cpus_sigp(cpumask_t avail) ...@@ -335,7 +335,7 @@ static int smp_rescan_cpus_sigp(cpumask_t avail)
smp_cpu_polarization[logical_cpu] = POLARIZATION_UNKNWN; smp_cpu_polarization[logical_cpu] = POLARIZATION_UNKNWN;
if (!cpu_stopped(logical_cpu)) if (!cpu_stopped(logical_cpu))
continue; continue;
cpu_set(logical_cpu, cpu_present_map); set_cpu_present(logical_cpu, true);
smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED; smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED;
logical_cpu = cpumask_next(logical_cpu, &avail); logical_cpu = cpumask_next(logical_cpu, &avail);
if (logical_cpu >= nr_cpu_ids) if (logical_cpu >= nr_cpu_ids)
...@@ -367,7 +367,7 @@ static int smp_rescan_cpus_sclp(cpumask_t avail) ...@@ -367,7 +367,7 @@ static int smp_rescan_cpus_sclp(cpumask_t avail)
continue; continue;
__cpu_logical_map[logical_cpu] = cpu_id; __cpu_logical_map[logical_cpu] = cpu_id;
smp_cpu_polarization[logical_cpu] = POLARIZATION_UNKNWN; smp_cpu_polarization[logical_cpu] = POLARIZATION_UNKNWN;
cpu_set(logical_cpu, cpu_present_map); set_cpu_present(logical_cpu, true);
if (cpu >= info->configured) if (cpu >= info->configured)
smp_cpu_state[logical_cpu] = CPU_STATE_STANDBY; smp_cpu_state[logical_cpu] = CPU_STATE_STANDBY;
else else
...@@ -385,7 +385,7 @@ static int __smp_rescan_cpus(void) ...@@ -385,7 +385,7 @@ static int __smp_rescan_cpus(void)
{ {
cpumask_t avail; cpumask_t avail;
cpus_xor(avail, cpu_possible_map, cpu_present_map); cpumask_xor(&avail, cpu_possible_mask, cpu_present_mask);
if (smp_use_sigp_detection) if (smp_use_sigp_detection)
return smp_rescan_cpus_sigp(avail); return smp_rescan_cpus_sigp(avail);
else else
...@@ -467,7 +467,7 @@ int __cpuinit start_secondary(void *cpuvoid) ...@@ -467,7 +467,7 @@ int __cpuinit start_secondary(void *cpuvoid)
notify_cpu_starting(smp_processor_id()); notify_cpu_starting(smp_processor_id());
/* Mark this cpu as online */ /* Mark this cpu as online */
ipi_call_lock(); ipi_call_lock();
cpu_set(smp_processor_id(), cpu_online_map); set_cpu_online(smp_processor_id(), true);
ipi_call_unlock(); ipi_call_unlock();
/* Switch on interrupts */ /* Switch on interrupts */
local_irq_enable(); local_irq_enable();
...@@ -644,7 +644,7 @@ int __cpu_disable(void) ...@@ -644,7 +644,7 @@ int __cpu_disable(void)
struct ec_creg_mask_parms cr_parms; struct ec_creg_mask_parms cr_parms;
int cpu = smp_processor_id(); int cpu = smp_processor_id();
cpu_clear(cpu, cpu_online_map); set_cpu_online(cpu, false);
/* Disable pfault pseudo page faults on this cpu. */ /* Disable pfault pseudo page faults on this cpu. */
pfault_fini(); pfault_fini();
...@@ -654,8 +654,8 @@ int __cpu_disable(void) ...@@ -654,8 +654,8 @@ int __cpu_disable(void)
/* disable all external interrupts */ /* disable all external interrupts */
cr_parms.orvals[0] = 0; cr_parms.orvals[0] = 0;
cr_parms.andvals[0] = ~(1 << 15 | 1 << 14 | 1 << 13 | 1 << 12 | cr_parms.andvals[0] = ~(1 << 15 | 1 << 14 | 1 << 13 | 1 << 11 |
1 << 11 | 1 << 10 | 1 << 6 | 1 << 4); 1 << 10 | 1 << 9 | 1 << 6 | 1 << 4);
/* disable all I/O interrupts */ /* disable all I/O interrupts */
cr_parms.orvals[6] = 0; cr_parms.orvals[6] = 0;
cr_parms.andvals[6] = ~(1 << 31 | 1 << 30 | 1 << 29 | 1 << 28 | cr_parms.andvals[6] = ~(1 << 31 | 1 << 30 | 1 << 29 | 1 << 28 |
...@@ -681,7 +681,7 @@ void __cpu_die(unsigned int cpu) ...@@ -681,7 +681,7 @@ void __cpu_die(unsigned int cpu)
atomic_dec(&init_mm.context.attach_count); atomic_dec(&init_mm.context.attach_count);
} }
void cpu_die(void) void __noreturn cpu_die(void)
{ {
idle_task_exit(); idle_task_exit();
while (sigp(smp_processor_id(), sigp_stop) == sigp_busy) while (sigp(smp_processor_id(), sigp_stop) == sigp_busy)
...@@ -738,8 +738,8 @@ void __init smp_prepare_boot_cpu(void) ...@@ -738,8 +738,8 @@ void __init smp_prepare_boot_cpu(void)
BUG_ON(smp_processor_id() != 0); BUG_ON(smp_processor_id() != 0);
current_thread_info()->cpu = 0; current_thread_info()->cpu = 0;
cpu_set(0, cpu_present_map); set_cpu_present(0, true);
cpu_set(0, cpu_online_map); set_cpu_online(0, true);
S390_lowcore.percpu_offset = __per_cpu_offset[0]; S390_lowcore.percpu_offset = __per_cpu_offset[0];
current_set[0] = current; current_set[0] = current;
smp_cpu_state[0] = CPU_STATE_CONFIGURED; smp_cpu_state[0] = CPU_STATE_CONFIGURED;
...@@ -1016,21 +1016,21 @@ int __ref smp_rescan_cpus(void) ...@@ -1016,21 +1016,21 @@ int __ref smp_rescan_cpus(void)
get_online_cpus(); get_online_cpus();
mutex_lock(&smp_cpu_state_mutex); mutex_lock(&smp_cpu_state_mutex);
newcpus = cpu_present_map; cpumask_copy(&newcpus, cpu_present_mask);
rc = __smp_rescan_cpus(); rc = __smp_rescan_cpus();
if (rc) if (rc)
goto out; goto out;
cpus_andnot(newcpus, cpu_present_map, newcpus); cpumask_andnot(&newcpus, cpu_present_mask, &newcpus);
for_each_cpu_mask(cpu, newcpus) { for_each_cpu(cpu, &newcpus) {
rc = smp_add_present_cpu(cpu); rc = smp_add_present_cpu(cpu);
if (rc) if (rc)
cpu_clear(cpu, cpu_present_map); set_cpu_present(cpu, false);
} }
rc = 0; rc = 0;
out: out:
mutex_unlock(&smp_cpu_state_mutex); mutex_unlock(&smp_cpu_state_mutex);
put_online_cpus(); put_online_cpus();
if (!cpus_empty(newcpus)) if (!cpumask_empty(&newcpus))
topology_schedule_update(); topology_schedule_update();
return rc; return rc;
} }
......
...@@ -810,7 +810,7 @@ static int etr_sync_clock_stop(struct etr_aib *aib, int port) ...@@ -810,7 +810,7 @@ static int etr_sync_clock_stop(struct etr_aib *aib, int port)
etr_sync.etr_port = port; etr_sync.etr_port = port;
get_online_cpus(); get_online_cpus();
atomic_set(&etr_sync.cpus, num_online_cpus() - 1); atomic_set(&etr_sync.cpus, num_online_cpus() - 1);
rc = stop_machine(etr_sync_clock, &etr_sync, &cpu_online_map); rc = stop_machine(etr_sync_clock, &etr_sync, cpu_online_mask);
put_online_cpus(); put_online_cpus();
return rc; return rc;
} }
...@@ -1579,7 +1579,7 @@ static void stp_work_fn(struct work_struct *work) ...@@ -1579,7 +1579,7 @@ static void stp_work_fn(struct work_struct *work)
memset(&stp_sync, 0, sizeof(stp_sync)); memset(&stp_sync, 0, sizeof(stp_sync));
get_online_cpus(); get_online_cpus();
atomic_set(&stp_sync.cpus, num_online_cpus() - 1); atomic_set(&stp_sync.cpus, num_online_cpus() - 1);
stop_machine(stp_sync_clock, &stp_sync, &cpu_online_map); stop_machine(stp_sync_clock, &stp_sync, cpu_online_mask);
put_online_cpus(); put_online_cpus();
if (!check_sync_clock()) if (!check_sync_clock())
......
...@@ -52,20 +52,20 @@ static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu) ...@@ -52,20 +52,20 @@ static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
{ {
cpumask_t mask; cpumask_t mask;
cpus_clear(mask); cpumask_clear(&mask);
if (!topology_enabled || !MACHINE_HAS_TOPOLOGY) { if (!topology_enabled || !MACHINE_HAS_TOPOLOGY) {
cpumask_copy(&mask, cpumask_of(cpu)); cpumask_copy(&mask, cpumask_of(cpu));
return mask; return mask;
} }
while (info) { while (info) {
if (cpu_isset(cpu, info->mask)) { if (cpumask_test_cpu(cpu, &info->mask)) {
mask = info->mask; mask = info->mask;
break; break;
} }
info = info->next; info = info->next;
} }
if (cpus_empty(mask)) if (cpumask_empty(&mask))
mask = cpumask_of_cpu(cpu); cpumask_copy(&mask, cpumask_of(cpu));
return mask; return mask;
} }
...@@ -85,10 +85,10 @@ static void add_cpus_to_mask(struct topology_cpu *tl_cpu, ...@@ -85,10 +85,10 @@ static void add_cpus_to_mask(struct topology_cpu *tl_cpu,
if (cpu_logical_map(lcpu) != rcpu) if (cpu_logical_map(lcpu) != rcpu)
continue; continue;
#ifdef CONFIG_SCHED_BOOK #ifdef CONFIG_SCHED_BOOK
cpu_set(lcpu, book->mask); cpumask_set_cpu(lcpu, &book->mask);
cpu_book_id[lcpu] = book->id; cpu_book_id[lcpu] = book->id;
#endif #endif
cpu_set(lcpu, core->mask); cpumask_set_cpu(lcpu, &core->mask);
cpu_core_id[lcpu] = core->id; cpu_core_id[lcpu] = core->id;
smp_cpu_polarization[lcpu] = tl_cpu->pp; smp_cpu_polarization[lcpu] = tl_cpu->pp;
} }
...@@ -101,13 +101,13 @@ static void clear_masks(void) ...@@ -101,13 +101,13 @@ static void clear_masks(void)
info = &core_info; info = &core_info;
while (info) { while (info) {
cpus_clear(info->mask); cpumask_clear(&info->mask);
info = info->next; info = info->next;
} }
#ifdef CONFIG_SCHED_BOOK #ifdef CONFIG_SCHED_BOOK
info = &book_info; info = &book_info;
while (info) { while (info) {
cpus_clear(info->mask); cpumask_clear(&info->mask);
info = info->next; info = info->next;
} }
#endif #endif
......
...@@ -22,6 +22,9 @@ obj-y += vdso32_wrapper.o ...@@ -22,6 +22,9 @@ obj-y += vdso32_wrapper.o
extra-y += vdso32.lds extra-y += vdso32.lds
CPPFLAGS_vdso32.lds += -P -C -U$(ARCH) CPPFLAGS_vdso32.lds += -P -C -U$(ARCH)
# Disable gcov profiling for VDSO code
GCOV_PROFILE := n
# Force dependency (incbin is bad) # Force dependency (incbin is bad)
$(obj)/vdso32_wrapper.o : $(obj)/vdso32.so $(obj)/vdso32_wrapper.o : $(obj)/vdso32.so
......
...@@ -22,6 +22,9 @@ obj-y += vdso64_wrapper.o ...@@ -22,6 +22,9 @@ obj-y += vdso64_wrapper.o
extra-y += vdso64.lds extra-y += vdso64.lds
CPPFLAGS_vdso64.lds += -P -C -U$(ARCH) CPPFLAGS_vdso64.lds += -P -C -U$(ARCH)
# Disable gcov profiling for VDSO code
GCOV_PROFILE := n
# Force dependency (incbin is bad) # Force dependency (incbin is bad)
$(obj)/vdso64_wrapper.o : $(obj)/vdso64.so $(obj)/vdso64_wrapper.o : $(obj)/vdso64.so
......
...@@ -412,6 +412,7 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long ...@@ -412,6 +412,7 @@ __segment_load (char *name, int do_nonshared, unsigned long *addr, unsigned long
struct dcss_segment *seg; struct dcss_segment *seg;
int rc, diag_cc; int rc, diag_cc;
start_addr = end_addr = 0;
seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA); seg = kmalloc(sizeof(*seg), GFP_KERNEL | GFP_DMA);
if (seg == NULL) { if (seg == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
...@@ -573,6 +574,7 @@ segment_modify_shared (char *name, int do_nonshared) ...@@ -573,6 +574,7 @@ segment_modify_shared (char *name, int do_nonshared)
unsigned long start_addr, end_addr, dummy; unsigned long start_addr, end_addr, dummy;
int rc, diag_cc; int rc, diag_cc;
start_addr = end_addr = 0;
mutex_lock(&dcss_lock); mutex_lock(&dcss_lock);
seg = segment_by_name (name); seg = segment_by_name (name);
if (seg == NULL) { if (seg == NULL) {
...@@ -681,8 +683,6 @@ void ...@@ -681,8 +683,6 @@ void
segment_save(char *name) segment_save(char *name)
{ {
struct dcss_segment *seg; struct dcss_segment *seg;
int startpfn = 0;
int endpfn = 0;
char cmd1[160]; char cmd1[160];
char cmd2[80]; char cmd2[80];
int i, response; int i, response;
...@@ -698,8 +698,6 @@ segment_save(char *name) ...@@ -698,8 +698,6 @@ segment_save(char *name)
goto out; goto out;
} }
startpfn = seg->start_addr >> PAGE_SHIFT;
endpfn = (seg->end) >> PAGE_SHIFT;
sprintf(cmd1, "DEFSEG %s", name); sprintf(cmd1, "DEFSEG %s", name);
for (i=0; i<seg->segcnt; i++) { for (i=0; i<seg->segcnt; i++) {
sprintf(cmd1+strlen(cmd1), " %lX-%lX %s", sprintf(cmd1+strlen(cmd1), " %lX-%lX %s",
......
...@@ -225,33 +225,6 @@ static noinline void do_sigbus(struct pt_regs *regs, long int_code, ...@@ -225,33 +225,6 @@ static noinline void do_sigbus(struct pt_regs *regs, long int_code,
force_sig_info(SIGBUS, &si, tsk); force_sig_info(SIGBUS, &si, tsk);
} }
#ifdef CONFIG_S390_EXEC_PROTECT
static noinline int signal_return(struct pt_regs *regs, long int_code,
unsigned long trans_exc_code)
{
u16 instruction;
int rc;
rc = __get_user(instruction, (u16 __user *) regs->psw.addr);
if (!rc && instruction == 0x0a77) {
clear_tsk_thread_flag(current, TIF_PER_TRAP);
if (is_compat_task())
sys32_sigreturn();
else
sys_sigreturn();
} else if (!rc && instruction == 0x0aad) {
clear_tsk_thread_flag(current, TIF_PER_TRAP);
if (is_compat_task())
sys32_rt_sigreturn();
else
sys_rt_sigreturn();
} else
do_sigsegv(regs, int_code, SEGV_MAPERR, trans_exc_code);
return 0;
}
#endif /* CONFIG_S390_EXEC_PROTECT */
static noinline void do_fault_error(struct pt_regs *regs, long int_code, static noinline void do_fault_error(struct pt_regs *regs, long int_code,
unsigned long trans_exc_code, int fault) unsigned long trans_exc_code, int fault)
{ {
...@@ -259,13 +232,6 @@ static noinline void do_fault_error(struct pt_regs *regs, long int_code, ...@@ -259,13 +232,6 @@ static noinline void do_fault_error(struct pt_regs *regs, long int_code,
switch (fault) { switch (fault) {
case VM_FAULT_BADACCESS: case VM_FAULT_BADACCESS:
#ifdef CONFIG_S390_EXEC_PROTECT
if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_SECONDARY &&
(trans_exc_code & 3) == 0) {
signal_return(regs, int_code, trans_exc_code);
break;
}
#endif /* CONFIG_S390_EXEC_PROTECT */
case VM_FAULT_BADMAP: case VM_FAULT_BADMAP:
/* Bad memory access. Check if it is kernel or user space. */ /* Bad memory access. Check if it is kernel or user space. */
if (regs->psw.mask & PSW_MASK_PSTATE) { if (regs->psw.mask & PSW_MASK_PSTATE) {
...@@ -414,11 +380,6 @@ void __kprobes do_dat_exception(struct pt_regs *regs, long pgm_int_code, ...@@ -414,11 +380,6 @@ void __kprobes do_dat_exception(struct pt_regs *regs, long pgm_int_code,
int access, fault; int access, fault;
access = VM_READ | VM_EXEC | VM_WRITE; access = VM_READ | VM_EXEC | VM_WRITE;
#ifdef CONFIG_S390_EXEC_PROTECT
if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_SECONDARY &&
(trans_exc_code & 3) == 0)
access = VM_EXEC;
#endif
fault = do_exception(regs, access, trans_exc_code); fault = do_exception(regs, access, trans_exc_code);
if (unlikely(fault)) if (unlikely(fault))
do_fault_error(regs, pgm_int_code & 255, trans_exc_code, fault); do_fault_error(regs, pgm_int_code & 255, trans_exc_code, fault);
...@@ -491,22 +452,28 @@ static int __init nopfault(char *str) ...@@ -491,22 +452,28 @@ static int __init nopfault(char *str)
__setup("nopfault", nopfault); __setup("nopfault", nopfault);
typedef struct { struct pfault_refbk {
__u16 refdiagc; u16 refdiagc;
__u16 reffcode; u16 reffcode;
__u16 refdwlen; u16 refdwlen;
__u16 refversn; u16 refversn;
__u64 refgaddr; u64 refgaddr;
__u64 refselmk; u64 refselmk;
__u64 refcmpmk; u64 refcmpmk;
__u64 reserved; u64 reserved;
} __attribute__ ((packed, aligned(8))) pfault_refbk_t; } __attribute__ ((packed, aligned(8)));
int pfault_init(void) int pfault_init(void)
{ {
pfault_refbk_t refbk = struct pfault_refbk refbk = {
{ 0x258, 0, 5, 2, __LC_CURRENT, 1ULL << 48, 1ULL << 48, .refdiagc = 0x258,
__PF_RES_FIELD }; .reffcode = 0,
.refdwlen = 5,
.refversn = 2,
.refgaddr = __LC_CURRENT_PID,
.refselmk = 1ULL << 48,
.refcmpmk = 1ULL << 48,
.reserved = __PF_RES_FIELD };
int rc; int rc;
if (!MACHINE_IS_VM || pfault_disable) if (!MACHINE_IS_VM || pfault_disable)
...@@ -524,8 +491,12 @@ int pfault_init(void) ...@@ -524,8 +491,12 @@ int pfault_init(void)
void pfault_fini(void) void pfault_fini(void)
{ {
pfault_refbk_t refbk = struct pfault_refbk refbk = {
{ 0x258, 1, 5, 2, 0ULL, 0ULL, 0ULL, 0ULL }; .refdiagc = 0x258,
.reffcode = 1,
.refdwlen = 5,
.refversn = 2,
};
if (!MACHINE_IS_VM || pfault_disable) if (!MACHINE_IS_VM || pfault_disable)
return; return;
...@@ -537,11 +508,15 @@ void pfault_fini(void) ...@@ -537,11 +508,15 @@ void pfault_fini(void)
: : "a" (&refbk), "m" (refbk) : "cc"); : : "a" (&refbk), "m" (refbk) : "cc");
} }
static DEFINE_SPINLOCK(pfault_lock);
static LIST_HEAD(pfault_list);
static void pfault_interrupt(unsigned int ext_int_code, static void pfault_interrupt(unsigned int ext_int_code,
unsigned int param32, unsigned long param64) unsigned int param32, unsigned long param64)
{ {
struct task_struct *tsk; struct task_struct *tsk;
__u16 subcode; __u16 subcode;
pid_t pid;
/* /*
* Get the external interruption subcode & pfault * Get the external interruption subcode & pfault
...@@ -553,44 +528,79 @@ static void pfault_interrupt(unsigned int ext_int_code, ...@@ -553,44 +528,79 @@ static void pfault_interrupt(unsigned int ext_int_code,
if ((subcode & 0xff00) != __SUBCODE_MASK) if ((subcode & 0xff00) != __SUBCODE_MASK)
return; return;
kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++; kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++;
if (subcode & 0x0080) {
/* /* Get the token (= pid of the affected task). */
* Get the token (= address of the task structure of the affected task). pid = sizeof(void *) == 4 ? param32 : param64;
*/ rcu_read_lock();
#ifdef CONFIG_64BIT tsk = find_task_by_pid_ns(pid, &init_pid_ns);
tsk = (struct task_struct *) param64; if (tsk)
#else get_task_struct(tsk);
tsk = (struct task_struct *) param32; rcu_read_unlock();
#endif if (!tsk)
return;
} else {
tsk = current;
}
spin_lock(&pfault_lock);
if (subcode & 0x0080) { if (subcode & 0x0080) {
/* signal bit is set -> a page has been swapped in by VM */ /* signal bit is set -> a page has been swapped in by VM */
if (xchg(&tsk->thread.pfault_wait, -1) != 0) { if (tsk->thread.pfault_wait == 1) {
/* Initial interrupt was faster than the completion /* Initial interrupt was faster than the completion
* interrupt. pfault_wait is valid. Set pfault_wait * interrupt. pfault_wait is valid. Set pfault_wait
* back to zero and wake up the process. This can * back to zero and wake up the process. This can
* safely be done because the task is still sleeping * safely be done because the task is still sleeping
* and can't produce new pfaults. */ * and can't produce new pfaults. */
tsk->thread.pfault_wait = 0; tsk->thread.pfault_wait = 0;
list_del(&tsk->thread.list);
wake_up_process(tsk); wake_up_process(tsk);
put_task_struct(tsk); } else {
/* Completion interrupt was faster than initial
* interrupt. Set pfault_wait to -1 so the initial
* interrupt doesn't put the task to sleep. */
tsk->thread.pfault_wait = -1;
} }
put_task_struct(tsk);
} else { } else {
/* signal bit not set -> a real page is missing. */ /* signal bit not set -> a real page is missing. */
get_task_struct(tsk); if (tsk->thread.pfault_wait == -1) {
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
if (xchg(&tsk->thread.pfault_wait, 1) != 0) {
/* Completion interrupt was faster than the initial /* Completion interrupt was faster than the initial
* interrupt (swapped in a -1 for pfault_wait). Set * interrupt (pfault_wait == -1). Set pfault_wait
* pfault_wait back to zero and exit. This can be * back to zero and exit. */
* done safely because tsk is running in kernel
* mode and can't produce new pfaults. */
tsk->thread.pfault_wait = 0; tsk->thread.pfault_wait = 0;
set_task_state(tsk, TASK_RUNNING); } else {
put_task_struct(tsk); /* Initial interrupt arrived before completion
} else * interrupt. Let the task sleep. */
tsk->thread.pfault_wait = 1;
list_add(&tsk->thread.list, &pfault_list);
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
set_tsk_need_resched(tsk); set_tsk_need_resched(tsk);
} }
}
spin_unlock(&pfault_lock);
}
static int __cpuinit pfault_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
struct thread_struct *thread, *next;
struct task_struct *tsk;
switch (action) {
case CPU_DEAD:
case CPU_DEAD_FROZEN:
spin_lock_irq(&pfault_lock);
list_for_each_entry_safe(thread, next, &pfault_list, list) {
thread->pfault_wait = 0;
list_del(&thread->list);
tsk = container_of(thread, struct task_struct, thread);
wake_up_process(tsk);
}
spin_unlock_irq(&pfault_lock);
break;
default:
break;
}
return NOTIFY_OK;
} }
static int __init pfault_irq_init(void) static int __init pfault_irq_init(void)
...@@ -599,22 +609,21 @@ static int __init pfault_irq_init(void) ...@@ -599,22 +609,21 @@ static int __init pfault_irq_init(void)
if (!MACHINE_IS_VM) if (!MACHINE_IS_VM)
return 0; return 0;
/*
* Try to get pfault pseudo page faults going.
*/
rc = register_external_interrupt(0x2603, pfault_interrupt); rc = register_external_interrupt(0x2603, pfault_interrupt);
if (rc) { if (rc)
pfault_disable = 1; goto out_extint;
return rc; rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP;
} if (rc)
if (pfault_init() == 0) goto out_pfault;
hotcpu_notifier(pfault_cpu_notify, 0);
return 0; return 0;
/* Tough luck, no pfault. */ out_pfault:
pfault_disable = 1;
unregister_external_interrupt(0x2603, pfault_interrupt); unregister_external_interrupt(0x2603, pfault_interrupt);
return 0; out_extint:
pfault_disable = 1;
return rc;
} }
early_initcall(pfault_irq_init); early_initcall(pfault_irq_init);
#endif #endif /* CONFIG_PFAULT */
...@@ -13,7 +13,6 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, ...@@ -13,7 +13,6 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
pte_t *pteptr, pte_t pteval) pte_t *pteptr, pte_t pteval)
{ {
pmd_t *pmdp = (pmd_t *) pteptr; pmd_t *pmdp = (pmd_t *) pteptr;
pte_t shadow_pteval = pteval;
unsigned long mask; unsigned long mask;
if (!MACHINE_HAS_HPAGE) { if (!MACHINE_HAS_HPAGE) {
...@@ -21,18 +20,9 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr, ...@@ -21,18 +20,9 @@ void set_huge_pte_at(struct mm_struct *mm, unsigned long addr,
mask = pte_val(pteval) & mask = pte_val(pteval) &
(_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO); (_SEGMENT_ENTRY_INV | _SEGMENT_ENTRY_RO);
pte_val(pteval) = (_SEGMENT_ENTRY + __pa(pteptr)) | mask; pte_val(pteval) = (_SEGMENT_ENTRY + __pa(pteptr)) | mask;
if (mm->context.noexec) {
pteptr += PTRS_PER_PTE;
pte_val(shadow_pteval) =
(_SEGMENT_ENTRY + __pa(pteptr)) | mask;
}
} }
pmd_val(*pmdp) = pte_val(pteval); pmd_val(*pmdp) = pte_val(pteval);
if (mm->context.noexec) {
pmdp = get_shadow_table(pmdp);
pmd_val(*pmdp) = pte_val(shadow_pteval);
}
} }
int arch_prepare_hugepage(struct page *page) int arch_prepare_hugepage(struct page *page)
......
...@@ -175,7 +175,8 @@ void kernel_map_pages(struct page *page, int numpages, int enable) ...@@ -175,7 +175,8 @@ void kernel_map_pages(struct page *page, int numpages, int enable)
pmd = pmd_offset(pud, address); pmd = pmd_offset(pud, address);
pte = pte_offset_kernel(pmd, address); pte = pte_offset_kernel(pmd, address);
if (!enable) { if (!enable) {
ptep_invalidate(&init_mm, address, pte); __ptep_ipte(address, pte);
pte_val(*pte) = _PAGE_TYPE_EMPTY;
continue; continue;
} }
*pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW)); *pte = mk_pte_phys(address, __pgprot(_PAGE_TYPE_RW));
......
...@@ -28,7 +28,7 @@ static void change_page_attr(unsigned long addr, int numpages, ...@@ -28,7 +28,7 @@ static void change_page_attr(unsigned long addr, int numpages,
pte = *ptep; pte = *ptep;
pte = set(pte); pte = set(pte);
ptep_invalidate(&init_mm, addr, ptep); __ptep_ipte(addr, ptep);
*ptep = pte; *ptep = pte;
addr += PAGE_SIZE; addr += PAGE_SIZE;
} }
......
...@@ -40,7 +40,6 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); ...@@ -40,7 +40,6 @@ DEFINE_PER_CPU(struct mmu_gather, mmu_gathers);
static DEFINE_PER_CPU(struct rcu_table_freelist *, rcu_table_freelist); static DEFINE_PER_CPU(struct rcu_table_freelist *, rcu_table_freelist);
static void __page_table_free(struct mm_struct *mm, unsigned long *table); static void __page_table_free(struct mm_struct *mm, unsigned long *table);
static void __crst_table_free(struct mm_struct *mm, unsigned long *table);
static struct rcu_table_freelist *rcu_table_freelist_get(struct mm_struct *mm) static struct rcu_table_freelist *rcu_table_freelist_get(struct mm_struct *mm)
{ {
...@@ -67,7 +66,7 @@ static void rcu_table_freelist_callback(struct rcu_head *head) ...@@ -67,7 +66,7 @@ static void rcu_table_freelist_callback(struct rcu_head *head)
while (batch->pgt_index > 0) while (batch->pgt_index > 0)
__page_table_free(batch->mm, batch->table[--batch->pgt_index]); __page_table_free(batch->mm, batch->table[--batch->pgt_index]);
while (batch->crst_index < RCU_FREELIST_SIZE) while (batch->crst_index < RCU_FREELIST_SIZE)
__crst_table_free(batch->mm, batch->table[batch->crst_index++]); crst_table_free(batch->mm, batch->table[batch->crst_index++]);
free_page((unsigned long) batch); free_page((unsigned long) batch);
} }
...@@ -125,63 +124,33 @@ static int __init parse_vmalloc(char *arg) ...@@ -125,63 +124,33 @@ static int __init parse_vmalloc(char *arg)
} }
early_param("vmalloc", parse_vmalloc); early_param("vmalloc", parse_vmalloc);
unsigned long *crst_table_alloc(struct mm_struct *mm, int noexec) unsigned long *crst_table_alloc(struct mm_struct *mm)
{ {
struct page *page = alloc_pages(GFP_KERNEL, ALLOC_ORDER); struct page *page = alloc_pages(GFP_KERNEL, ALLOC_ORDER);
if (!page) if (!page)
return NULL; return NULL;
page->index = 0;
if (noexec) {
struct page *shadow = alloc_pages(GFP_KERNEL, ALLOC_ORDER);
if (!shadow) {
__free_pages(page, ALLOC_ORDER);
return NULL;
}
page->index = page_to_phys(shadow);
}
spin_lock_bh(&mm->context.list_lock);
list_add(&page->lru, &mm->context.crst_list);
spin_unlock_bh(&mm->context.list_lock);
return (unsigned long *) page_to_phys(page); return (unsigned long *) page_to_phys(page);
} }
static void __crst_table_free(struct mm_struct *mm, unsigned long *table)
{
unsigned long *shadow = get_shadow_table(table);
if (shadow)
free_pages((unsigned long) shadow, ALLOC_ORDER);
free_pages((unsigned long) table, ALLOC_ORDER);
}
void crst_table_free(struct mm_struct *mm, unsigned long *table) void crst_table_free(struct mm_struct *mm, unsigned long *table)
{ {
struct page *page = virt_to_page(table); free_pages((unsigned long) table, ALLOC_ORDER);
spin_lock_bh(&mm->context.list_lock);
list_del(&page->lru);
spin_unlock_bh(&mm->context.list_lock);
__crst_table_free(mm, table);
} }
void crst_table_free_rcu(struct mm_struct *mm, unsigned long *table) void crst_table_free_rcu(struct mm_struct *mm, unsigned long *table)
{ {
struct rcu_table_freelist *batch; struct rcu_table_freelist *batch;
struct page *page = virt_to_page(table);
spin_lock_bh(&mm->context.list_lock);
list_del(&page->lru);
spin_unlock_bh(&mm->context.list_lock);
if (atomic_read(&mm->mm_users) < 2 && if (atomic_read(&mm->mm_users) < 2 &&
cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) { cpumask_equal(mm_cpumask(mm), cpumask_of(smp_processor_id()))) {
__crst_table_free(mm, table); crst_table_free(mm, table);
return; return;
} }
batch = rcu_table_freelist_get(mm); batch = rcu_table_freelist_get(mm);
if (!batch) { if (!batch) {
smp_call_function(smp_sync, NULL, 1); smp_call_function(smp_sync, NULL, 1);
__crst_table_free(mm, table); crst_table_free(mm, table);
return; return;
} }
batch->table[--batch->crst_index] = table; batch->table[--batch->crst_index] = table;
...@@ -197,7 +166,7 @@ int crst_table_upgrade(struct mm_struct *mm, unsigned long limit) ...@@ -197,7 +166,7 @@ int crst_table_upgrade(struct mm_struct *mm, unsigned long limit)
BUG_ON(limit > (1UL << 53)); BUG_ON(limit > (1UL << 53));
repeat: repeat:
table = crst_table_alloc(mm, mm->context.noexec); table = crst_table_alloc(mm);
if (!table) if (!table)
return -ENOMEM; return -ENOMEM;
spin_lock_bh(&mm->page_table_lock); spin_lock_bh(&mm->page_table_lock);
...@@ -273,7 +242,7 @@ unsigned long *page_table_alloc(struct mm_struct *mm) ...@@ -273,7 +242,7 @@ unsigned long *page_table_alloc(struct mm_struct *mm)
unsigned long *table; unsigned long *table;
unsigned long bits; unsigned long bits;
bits = (mm->context.noexec || mm->context.has_pgste) ? 3UL : 1UL; bits = (mm->context.has_pgste) ? 3UL : 1UL;
spin_lock_bh(&mm->context.list_lock); spin_lock_bh(&mm->context.list_lock);
page = NULL; page = NULL;
if (!list_empty(&mm->context.pgtable_list)) { if (!list_empty(&mm->context.pgtable_list)) {
...@@ -329,7 +298,7 @@ void page_table_free(struct mm_struct *mm, unsigned long *table) ...@@ -329,7 +298,7 @@ void page_table_free(struct mm_struct *mm, unsigned long *table)
struct page *page; struct page *page;
unsigned long bits; unsigned long bits;
bits = (mm->context.noexec || mm->context.has_pgste) ? 3UL : 1UL; bits = (mm->context.has_pgste) ? 3UL : 1UL;
bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long); bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long);
page = pfn_to_page(__pa(table) >> PAGE_SHIFT); page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
spin_lock_bh(&mm->context.list_lock); spin_lock_bh(&mm->context.list_lock);
...@@ -366,7 +335,7 @@ void page_table_free_rcu(struct mm_struct *mm, unsigned long *table) ...@@ -366,7 +335,7 @@ void page_table_free_rcu(struct mm_struct *mm, unsigned long *table)
page_table_free(mm, table); page_table_free(mm, table);
return; return;
} }
bits = (mm->context.noexec || mm->context.has_pgste) ? 3UL : 1UL; bits = (mm->context.has_pgste) ? 3UL : 1UL;
bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long); bits <<= (__pa(table) & (PAGE_SIZE - 1)) / 256 / sizeof(unsigned long);
page = pfn_to_page(__pa(table) >> PAGE_SHIFT); page = pfn_to_page(__pa(table) >> PAGE_SHIFT);
spin_lock_bh(&mm->context.list_lock); spin_lock_bh(&mm->context.list_lock);
...@@ -379,25 +348,6 @@ void page_table_free_rcu(struct mm_struct *mm, unsigned long *table) ...@@ -379,25 +348,6 @@ void page_table_free_rcu(struct mm_struct *mm, unsigned long *table)
rcu_table_freelist_finish(); rcu_table_freelist_finish();
} }
void disable_noexec(struct mm_struct *mm, struct task_struct *tsk)
{
struct page *page;
spin_lock_bh(&mm->context.list_lock);
/* Free shadow region and segment tables. */
list_for_each_entry(page, &mm->context.crst_list, lru)
if (page->index) {
free_pages((unsigned long) page->index, ALLOC_ORDER);
page->index = 0;
}
/* "Free" second halves of page tables. */
list_for_each_entry(page, &mm->context.pgtable_list, lru)
page->flags &= ~SECOND_HALVES;
spin_unlock_bh(&mm->context.list_lock);
mm->context.noexec = 0;
update_mm(mm, tsk);
}
/* /*
* switch on pgstes for its userspace process (for kvm) * switch on pgstes for its userspace process (for kvm)
*/ */
......
...@@ -95,7 +95,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) ...@@ -95,7 +95,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
pu_dir = vmem_pud_alloc(); pu_dir = vmem_pud_alloc();
if (!pu_dir) if (!pu_dir)
goto out; goto out;
pgd_populate_kernel(&init_mm, pg_dir, pu_dir); pgd_populate(&init_mm, pg_dir, pu_dir);
} }
pu_dir = pud_offset(pg_dir, address); pu_dir = pud_offset(pg_dir, address);
...@@ -103,7 +103,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) ...@@ -103,7 +103,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
pm_dir = vmem_pmd_alloc(); pm_dir = vmem_pmd_alloc();
if (!pm_dir) if (!pm_dir)
goto out; goto out;
pud_populate_kernel(&init_mm, pu_dir, pm_dir); pud_populate(&init_mm, pu_dir, pm_dir);
} }
pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0)); pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
...@@ -123,7 +123,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) ...@@ -123,7 +123,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
pt_dir = vmem_pte_alloc(); pt_dir = vmem_pte_alloc();
if (!pt_dir) if (!pt_dir)
goto out; goto out;
pmd_populate_kernel(&init_mm, pm_dir, pt_dir); pmd_populate(&init_mm, pm_dir, pt_dir);
} }
pt_dir = pte_offset_kernel(pm_dir, address); pt_dir = pte_offset_kernel(pm_dir, address);
...@@ -159,7 +159,7 @@ static void vmem_remove_range(unsigned long start, unsigned long size) ...@@ -159,7 +159,7 @@ static void vmem_remove_range(unsigned long start, unsigned long size)
continue; continue;
if (pmd_huge(*pm_dir)) { if (pmd_huge(*pm_dir)) {
pmd_clear_kernel(pm_dir); pmd_clear(pm_dir);
address += HPAGE_SIZE - PAGE_SIZE; address += HPAGE_SIZE - PAGE_SIZE;
continue; continue;
} }
...@@ -192,7 +192,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) ...@@ -192,7 +192,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
pu_dir = vmem_pud_alloc(); pu_dir = vmem_pud_alloc();
if (!pu_dir) if (!pu_dir)
goto out; goto out;
pgd_populate_kernel(&init_mm, pg_dir, pu_dir); pgd_populate(&init_mm, pg_dir, pu_dir);
} }
pu_dir = pud_offset(pg_dir, address); pu_dir = pud_offset(pg_dir, address);
...@@ -200,7 +200,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) ...@@ -200,7 +200,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
pm_dir = vmem_pmd_alloc(); pm_dir = vmem_pmd_alloc();
if (!pm_dir) if (!pm_dir)
goto out; goto out;
pud_populate_kernel(&init_mm, pu_dir, pm_dir); pud_populate(&init_mm, pu_dir, pm_dir);
} }
pm_dir = pmd_offset(pu_dir, address); pm_dir = pmd_offset(pu_dir, address);
...@@ -208,7 +208,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node) ...@@ -208,7 +208,7 @@ int __meminit vmemmap_populate(struct page *start, unsigned long nr, int node)
pt_dir = vmem_pte_alloc(); pt_dir = vmem_pte_alloc();
if (!pt_dir) if (!pt_dir)
goto out; goto out;
pmd_populate_kernel(&init_mm, pm_dir, pt_dir); pmd_populate(&init_mm, pm_dir, pt_dir);
} }
pt_dir = pte_offset_kernel(pm_dir, address); pt_dir = pte_offset_kernel(pm_dir, address);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* Author: Heinz Graalfs <graalfs@de.ibm.com> * Author: Heinz Graalfs <graalfs@de.ibm.com>
*/ */
#include <linux/kernel_stat.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/smp.h> #include <linux/smp.h>
...@@ -674,17 +675,11 @@ int hwsampler_activate(unsigned int cpu) ...@@ -674,17 +675,11 @@ int hwsampler_activate(unsigned int cpu)
static void hws_ext_handler(unsigned int ext_int_code, static void hws_ext_handler(unsigned int ext_int_code,
unsigned int param32, unsigned long param64) unsigned int param32, unsigned long param64)
{ {
int cpu;
struct hws_cpu_buffer *cb; struct hws_cpu_buffer *cb;
cpu = smp_processor_id(); kstat_cpu(smp_processor_id()).irqs[EXTINT_CPM]++;
cb = &per_cpu(sampler_cpu_buffer, cpu); cb = &__get_cpu_var(sampler_cpu_buffer);
atomic_xchg(&cb->ext_params, atomic_read(&cb->ext_params) | param32);
atomic_xchg(
&cb->ext_params,
atomic_read(&cb->ext_params)
| S390_lowcore.ext_params);
if (hws_wq) if (hws_wq)
queue_work(hws_wq, &cb->worker); queue_work(hws_wq, &cb->worker);
} }
...@@ -764,7 +759,7 @@ static int worker_check_error(unsigned int cpu, int ext_params) ...@@ -764,7 +759,7 @@ static int worker_check_error(unsigned int cpu, int ext_params)
if (!sdbt || !*sdbt) if (!sdbt || !*sdbt)
return -EINVAL; return -EINVAL;
if (ext_params & EI_IEA) if (ext_params & EI_PRA)
cb->req_alert++; cb->req_alert++;
if (ext_params & EI_LSDA) if (ext_params & EI_LSDA)
...@@ -1009,7 +1004,7 @@ int hwsampler_deallocate() ...@@ -1009,7 +1004,7 @@ int hwsampler_deallocate()
if (hws_state != HWS_STOPPED) if (hws_state != HWS_STOPPED)
goto deallocate_exit; goto deallocate_exit;
smp_ctl_clear_bit(0, 5); /* set bit 58 CR0 off */ ctl_clear_bit(0, 5); /* set bit 58 CR0 off */
deallocate_sdbt(); deallocate_sdbt();
hws_state = HWS_DEALLOCATED; hws_state = HWS_DEALLOCATED;
...@@ -1123,7 +1118,7 @@ int hwsampler_shutdown() ...@@ -1123,7 +1118,7 @@ int hwsampler_shutdown()
mutex_lock(&hws_sem); mutex_lock(&hws_sem);
if (hws_state == HWS_STOPPED) { if (hws_state == HWS_STOPPED) {
smp_ctl_clear_bit(0, 5); /* set bit 58 CR0 off */ ctl_clear_bit(0, 5); /* set bit 58 CR0 off */
deallocate_sdbt(); deallocate_sdbt();
} }
if (hws_wq) { if (hws_wq) {
...@@ -1198,7 +1193,7 @@ int hwsampler_start_all(unsigned long rate) ...@@ -1198,7 +1193,7 @@ int hwsampler_start_all(unsigned long rate)
hws_oom = 1; hws_oom = 1;
hws_flush_all = 0; hws_flush_all = 0;
/* now let them in, 1407 CPUMF external interrupts */ /* now let them in, 1407 CPUMF external interrupts */
smp_ctl_set_bit(0, 5); /* set CR0 bit 58 */ ctl_set_bit(0, 5); /* set CR0 bit 58 */
return 0; return 0;
} }
......
...@@ -74,6 +74,8 @@ config ZCRYPT ...@@ -74,6 +74,8 @@ config ZCRYPT
+ PCI-X Cryptographic Coprocessor (PCIXCC) + PCI-X Cryptographic Coprocessor (PCIXCC)
+ Crypto Express2 Coprocessor (CEX2C) + Crypto Express2 Coprocessor (CEX2C)
+ Crypto Express2 Accelerator (CEX2A) + Crypto Express2 Accelerator (CEX2A)
+ Crypto Express3 Coprocessor (CEX3C)
+ Crypto Express3 Accelerator (CEX3A)
config ZCRYPT_MONOLITHIC config ZCRYPT_MONOLITHIC
bool "Monolithic zcrypt module" bool "Monolithic zcrypt module"
......
...@@ -253,13 +253,11 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) ...@@ -253,13 +253,11 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
*/ */
void dasd_alias_lcu_setup_complete(struct dasd_device *device) void dasd_alias_lcu_setup_complete(struct dasd_device *device)
{ {
struct dasd_eckd_private *private;
unsigned long flags; unsigned long flags;
struct alias_server *server; struct alias_server *server;
struct alias_lcu *lcu; struct alias_lcu *lcu;
struct dasd_uid uid; struct dasd_uid uid;
private = (struct dasd_eckd_private *) device->private;
device->discipline->get_uid(device, &uid); device->discipline->get_uid(device, &uid);
lcu = NULL; lcu = NULL;
spin_lock_irqsave(&aliastree.lock, flags); spin_lock_irqsave(&aliastree.lock, flags);
...@@ -279,13 +277,11 @@ void dasd_alias_lcu_setup_complete(struct dasd_device *device) ...@@ -279,13 +277,11 @@ void dasd_alias_lcu_setup_complete(struct dasd_device *device)
void dasd_alias_wait_for_lcu_setup(struct dasd_device *device) void dasd_alias_wait_for_lcu_setup(struct dasd_device *device)
{ {
struct dasd_eckd_private *private;
unsigned long flags; unsigned long flags;
struct alias_server *server; struct alias_server *server;
struct alias_lcu *lcu; struct alias_lcu *lcu;
struct dasd_uid uid; struct dasd_uid uid;
private = (struct dasd_eckd_private *) device->private;
device->discipline->get_uid(device, &uid); device->discipline->get_uid(device, &uid);
lcu = NULL; lcu = NULL;
spin_lock_irqsave(&aliastree.lock, flags); spin_lock_irqsave(&aliastree.lock, flags);
......
...@@ -1611,10 +1611,8 @@ static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, ...@@ -1611,10 +1611,8 @@ static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
static int dasd_eckd_start_analysis(struct dasd_block *block) static int dasd_eckd_start_analysis(struct dasd_block *block)
{ {
struct dasd_eckd_private *private;
struct dasd_ccw_req *init_cqr; struct dasd_ccw_req *init_cqr;
private = (struct dasd_eckd_private *) block->base->private;
init_cqr = dasd_eckd_analysis_ccw(block->base); init_cqr = dasd_eckd_analysis_ccw(block->base);
if (IS_ERR(init_cqr)) if (IS_ERR(init_cqr))
return PTR_ERR(init_cqr); return PTR_ERR(init_cqr);
...@@ -2264,7 +2262,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( ...@@ -2264,7 +2262,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
unsigned int blk_per_trk, unsigned int blk_per_trk,
unsigned int blksize) unsigned int blksize)
{ {
struct dasd_eckd_private *private;
unsigned long *idaws; unsigned long *idaws;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct ccw1 *ccw; struct ccw1 *ccw;
...@@ -2283,7 +2280,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( ...@@ -2283,7 +2280,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
unsigned int recoffs; unsigned int recoffs;
basedev = block->base; basedev = block->base;
private = (struct dasd_eckd_private *) basedev->private;
if (rq_data_dir(req) == READ) if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_TRACK_DATA; cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
else if (rq_data_dir(req) == WRITE) else if (rq_data_dir(req) == WRITE)
...@@ -2556,8 +2552,7 @@ static int prepare_itcw(struct itcw *itcw, ...@@ -2556,8 +2552,7 @@ static int prepare_itcw(struct itcw *itcw,
dcw = itcw_add_dcw(itcw, pfx_cmd, 0, dcw = itcw_add_dcw(itcw, pfx_cmd, 0,
&pfxdata, sizeof(pfxdata), total_data_size); &pfxdata, sizeof(pfxdata), total_data_size);
return IS_ERR(dcw) ? PTR_ERR(dcw) : 0;
return rc;
} }
static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
...@@ -2573,7 +2568,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( ...@@ -2573,7 +2568,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
unsigned int blk_per_trk, unsigned int blk_per_trk,
unsigned int blksize) unsigned int blksize)
{ {
struct dasd_eckd_private *private;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
struct req_iterator iter; struct req_iterator iter;
struct bio_vec *bv; struct bio_vec *bv;
...@@ -2594,7 +2588,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( ...@@ -2594,7 +2588,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
unsigned int count, count_to_trk_end; unsigned int count, count_to_trk_end;
basedev = block->base; basedev = block->base;
private = (struct dasd_eckd_private *) basedev->private;
if (rq_data_dir(req) == READ) { if (rq_data_dir(req) == READ) {
cmd = DASD_ECKD_CCW_READ_TRACK_DATA; cmd = DASD_ECKD_CCW_READ_TRACK_DATA;
itcw_op = ITCW_OP_READ; itcw_op = ITCW_OP_READ;
...@@ -2801,7 +2794,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, ...@@ -2801,7 +2794,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
struct dasd_block *block, struct dasd_block *block,
struct request *req) struct request *req)
{ {
struct dasd_eckd_private *private;
unsigned long *idaws; unsigned long *idaws;
struct dasd_device *basedev; struct dasd_device *basedev;
struct dasd_ccw_req *cqr; struct dasd_ccw_req *cqr;
...@@ -2836,7 +2828,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, ...@@ -2836,7 +2828,6 @@ static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev,
trkcount = last_trk - first_trk + 1; trkcount = last_trk - first_trk + 1;
first_offs = 0; first_offs = 0;
basedev = block->base; basedev = block->base;
private = (struct dasd_eckd_private *) basedev->private;
if (rq_data_dir(req) == READ) if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_TRACK; cmd = DASD_ECKD_CCW_READ_TRACK;
......
...@@ -119,18 +119,6 @@ config S390_TAPE ...@@ -119,18 +119,6 @@ config S390_TAPE
comment "S/390 tape interface support" comment "S/390 tape interface support"
depends on S390_TAPE depends on S390_TAPE
config S390_TAPE_BLOCK
def_bool y
prompt "Support for tape block devices"
depends on S390_TAPE && BLOCK
help
Select this option if you want to access your channel-attached tape
devices using the block device interface. This interface is similar
to CD-ROM devices on other platforms. The tapes can only be
accessed read-only when using this interface. Have a look at
<file:Documentation/s390/TAPE> for further information about creating
volumes for and using this interface. It is safe to say "Y" here.
comment "S/390 tape hardware support" comment "S/390 tape hardware support"
depends on S390_TAPE depends on S390_TAPE
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# #
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \ obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_cmd.o sclp_config.o sclp_cpi_sys.o sclp_ocf.o
obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270) += raw3270.o
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
...@@ -22,7 +22,6 @@ obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o ...@@ -22,7 +22,6 @@ obj-$(CONFIG_ZVM_WATCHDOG) += vmwatchdog.o
obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o obj-$(CONFIG_VMLOGRDR) += vmlogrdr.o
obj-$(CONFIG_VMCP) += vmcp.o obj-$(CONFIG_VMCP) += vmcp.o
tape-$(CONFIG_S390_TAPE_BLOCK) += tape_block.o
tape-$(CONFIG_PROC_FS) += tape_proc.o tape-$(CONFIG_PROC_FS) += tape_proc.o
tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y) tape-objs := tape_core.o tape_std.o tape_char.o $(tape-y)
obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o obj-$(CONFIG_S390_TAPE) += tape.o tape_class.o
......
...@@ -97,7 +97,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv) ...@@ -97,7 +97,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv)
{ {
struct monwrite_hdr *monhdr = &monpriv->hdr; struct monwrite_hdr *monhdr = &monpriv->hdr;
struct mon_buf *monbuf; struct mon_buf *monbuf;
int rc; int rc = 0;
if (monhdr->datalen > MONWRITE_MAX_DATALEN || if (monhdr->datalen > MONWRITE_MAX_DATALEN ||
monhdr->mon_function > MONWRITE_START_CONFIG || monhdr->mon_function > MONWRITE_START_CONFIG ||
...@@ -135,7 +135,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv) ...@@ -135,7 +135,7 @@ static int monwrite_new_hdr(struct mon_private *monpriv)
mon_buf_count++; mon_buf_count++;
} }
monpriv->current_buf = monbuf; monpriv->current_buf = monbuf;
return 0; return rc;
} }
static int monwrite_new_data(struct mon_private *monpriv) static int monwrite_new_data(struct mon_private *monpriv)
......
...@@ -598,7 +598,6 @@ __raw3270_size_device(struct raw3270 *rp) ...@@ -598,7 +598,6 @@ __raw3270_size_device(struct raw3270 *rp)
static const unsigned char wbuf[] = static const unsigned char wbuf[] =
{ 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 }; { 0x00, 0x07, 0x01, 0xff, 0x03, 0x00, 0x81 };
struct raw3270_ua *uap; struct raw3270_ua *uap;
unsigned short count;
int rc; int rc;
/* /*
...@@ -653,7 +652,6 @@ __raw3270_size_device(struct raw3270 *rp) ...@@ -653,7 +652,6 @@ __raw3270_size_device(struct raw3270 *rp)
if (rc) if (rc)
return rc; return rc;
/* Got a Query Reply */ /* Got a Query Reply */
count = sizeof(rp->init_data) - rp->init_request.rescnt;
uap = (struct raw3270_ua *) (rp->init_data + 1); uap = (struct raw3270_ua *) (rp->init_data + 1);
/* Paranoia check. */ /* Paranoia check. */
if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81) if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81)
......
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#define EVTYP_CONFMGMDATA 0x04 #define EVTYP_CONFMGMDATA 0x04
#define EVTYP_SDIAS 0x1C #define EVTYP_SDIAS 0x1C
#define EVTYP_ASYNC 0x0A #define EVTYP_ASYNC 0x0A
#define EVTYP_OCF 0x1E
#define EVTYP_OPCMD_MASK 0x80000000 #define EVTYP_OPCMD_MASK 0x80000000
#define EVTYP_MSG_MASK 0x40000000 #define EVTYP_MSG_MASK 0x40000000
...@@ -40,6 +41,7 @@ ...@@ -40,6 +41,7 @@
#define EVTYP_CONFMGMDATA_MASK 0x10000000 #define EVTYP_CONFMGMDATA_MASK 0x10000000
#define EVTYP_SDIAS_MASK 0x00000010 #define EVTYP_SDIAS_MASK 0x00000010
#define EVTYP_ASYNC_MASK 0x00400000 #define EVTYP_ASYNC_MASK 0x00400000
#define EVTYP_OCF_MASK 0x00000004
#define GNRLMSGFLGS_DOM 0x8000 #define GNRLMSGFLGS_DOM 0x8000
#define GNRLMSGFLGS_SNDALRM 0x4000 #define GNRLMSGFLGS_SNDALRM 0x4000
...@@ -186,4 +188,26 @@ sclp_ascebc_str(unsigned char *str, int nr) ...@@ -186,4 +188,26 @@ sclp_ascebc_str(unsigned char *str, int nr)
(MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr); (MACHINE_IS_VM) ? ASCEBC(str, nr) : ASCEBC_500(str, nr);
} }
static inline struct gds_vector *
sclp_find_gds_vector(void *start, void *end, u16 id)
{
struct gds_vector *v;
for (v = start; (void *) v < end; v = (void *) v + v->length)
if (v->gds_id == id)
return v;
return NULL;
}
static inline struct gds_subvector *
sclp_find_gds_subvector(void *start, void *end, u8 key)
{
struct gds_subvector *sv;
for (sv = start; (void *) sv < end; sv = (void *) sv + sv->length)
if (sv->key == key)
return sv;
return NULL;
}
#endif /* __SCLP_H__ */ #endif /* __SCLP_H__ */
...@@ -71,21 +71,9 @@ static struct sclp_register sclp_conf_register = ...@@ -71,21 +71,9 @@ static struct sclp_register sclp_conf_register =
static int __init sclp_conf_init(void) static int __init sclp_conf_init(void)
{ {
int rc;
INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify); INIT_WORK(&sclp_cpu_capability_work, sclp_cpu_capability_notify);
INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify); INIT_WORK(&sclp_cpu_change_work, sclp_cpu_change_notify);
return sclp_register(&sclp_conf_register);
rc = sclp_register(&sclp_conf_register);
if (rc)
return rc;
if (!(sclp_conf_register.sclp_send_mask & EVTYP_CONFMGMDATA_MASK)) {
pr_warning("no configuration management.\n");
sclp_unregister(&sclp_conf_register);
rc = -ENOSYS;
}
return rc;
} }
__initcall(sclp_conf_init); __initcall(sclp_conf_init);
/*
* drivers/s390/char/sclp_ocf.c
* SCLP OCF communication parameters sysfs interface
*
* Copyright IBM Corp. 2011
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
*/
#define KMSG_COMPONENT "sclp_ocf"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/stat.h>
#include <linux/device.h>
#include <linux/string.h>
#include <linux/ctype.h>
#include <linux/kmod.h>
#include <linux/timer.h>
#include <linux/err.h>
#include <asm/ebcdic.h>
#include <asm/sclp.h>
#include "sclp.h"
#define OCF_LENGTH_HMC_NETWORK 8UL
#define OCF_LENGTH_CPC_NAME 8UL
static char hmc_network[OCF_LENGTH_HMC_NETWORK + 1];
static char cpc_name[OCF_LENGTH_CPC_NAME + 1];
static DEFINE_SPINLOCK(sclp_ocf_lock);
static struct work_struct sclp_ocf_change_work;
static struct kset *ocf_kset;
static void sclp_ocf_change_notify(struct work_struct *work)
{
kobject_uevent(&ocf_kset->kobj, KOBJ_CHANGE);
}
/* Handler for OCF event. Look for the CPC image name. */
static void sclp_ocf_handler(struct evbuf_header *evbuf)
{
struct gds_vector *v;
struct gds_subvector *sv, *netid, *cpc;
size_t size;
/* Find the 0x9f00 block. */
v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
0x9f00);
if (!v)
return;
/* Find the 0x9f22 block inside the 0x9f00 block. */
v = sclp_find_gds_vector(v + 1, (void *) v + v->length, 0x9f22);
if (!v)
return;
/* Find the 0x81 block inside the 0x9f22 block. */
sv = sclp_find_gds_subvector(v + 1, (void *) v + v->length, 0x81);
if (!sv)
return;
/* Find the 0x01 block inside the 0x81 block. */
netid = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 1);
/* Find the 0x02 block inside the 0x81 block. */
cpc = sclp_find_gds_subvector(sv + 1, (void *) sv + sv->length, 2);
/* Copy network name and cpc name. */
spin_lock(&sclp_ocf_lock);
if (netid) {
size = min(OCF_LENGTH_HMC_NETWORK, (size_t) netid->length);
memcpy(hmc_network, netid + 1, size);
EBCASC(hmc_network, size);
hmc_network[size] = 0;
}
if (cpc) {
size = min(OCF_LENGTH_CPC_NAME, (size_t) cpc->length);
memcpy(cpc_name, cpc + 1, size);
EBCASC(cpc_name, size);
cpc_name[size] = 0;
}
spin_unlock(&sclp_ocf_lock);
schedule_work(&sclp_ocf_change_work);
}
static struct sclp_register sclp_ocf_event = {
.receive_mask = EVTYP_OCF_MASK,
.receiver_fn = sclp_ocf_handler,
};
static ssize_t cpc_name_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
int rc;
spin_lock_irq(&sclp_ocf_lock);
rc = snprintf(page, PAGE_SIZE, "%s\n", cpc_name);
spin_unlock_irq(&sclp_ocf_lock);
return rc;
}
static struct kobj_attribute cpc_name_attr =
__ATTR(cpc_name, 0444, cpc_name_show, NULL);
static ssize_t hmc_network_show(struct kobject *kobj,
struct kobj_attribute *attr, char *page)
{
int rc;
spin_lock_irq(&sclp_ocf_lock);
rc = snprintf(page, PAGE_SIZE, "%s\n", hmc_network);
spin_unlock_irq(&sclp_ocf_lock);
return rc;
}
static struct kobj_attribute hmc_network_attr =
__ATTR(hmc_network, 0444, hmc_network_show, NULL);
static struct attribute *ocf_attrs[] = {
&cpc_name_attr.attr,
&hmc_network_attr.attr,
NULL,
};
static struct attribute_group ocf_attr_group = {
.attrs = ocf_attrs,
};
static int __init ocf_init(void)
{
int rc;
INIT_WORK(&sclp_ocf_change_work, sclp_ocf_change_notify);
ocf_kset = kset_create_and_add("ocf", NULL, firmware_kobj);
if (!ocf_kset)
return -ENOMEM;
rc = sysfs_create_group(&ocf_kset->kobj, &ocf_attr_group);
if (rc) {
kset_unregister(ocf_kset);
return rc;
}
return sclp_register(&sclp_ocf_event);
}
device_initcall(ocf_init);
...@@ -69,9 +69,6 @@ static DEFINE_MUTEX(sdias_mutex); ...@@ -69,9 +69,6 @@ static DEFINE_MUTEX(sdias_mutex);
static void sdias_callback(struct sclp_req *request, void *data) static void sdias_callback(struct sclp_req *request, void *data)
{ {
struct sdias_sccb *cbsccb;
cbsccb = (struct sdias_sccb *) request->sccb;
sclp_req_done = 1; sclp_req_done = 1;
wake_up(&sdias_wq); /* Inform caller, that request is complete */ wake_up(&sdias_wq); /* Inform caller, that request is complete */
TRACE("callback done\n"); TRACE("callback done\n");
......
...@@ -408,118 +408,72 @@ static int sclp_switch_cases(unsigned char *buf, int count) ...@@ -408,118 +408,72 @@ static int sclp_switch_cases(unsigned char *buf, int count)
return op - buf; return op - buf;
} }
static void static void sclp_get_input(struct gds_subvector *sv)
sclp_get_input(unsigned char *start, unsigned char *end)
{ {
unsigned char *str;
int count; int count;
count = end - start; str = (unsigned char *) (sv + 1);
count = sv->length - sizeof(*sv);
if (sclp_tty_tolower) if (sclp_tty_tolower)
EBC_TOLOWER(start, count); EBC_TOLOWER(str, count);
count = sclp_switch_cases(start, count); count = sclp_switch_cases(str, count);
/* convert EBCDIC to ASCII (modify original input in SCCB) */ /* convert EBCDIC to ASCII (modify original input in SCCB) */
sclp_ebcasc_str(start, count); sclp_ebcasc_str(str, count);
/* transfer input to high level driver */ /* transfer input to high level driver */
sclp_tty_input(start, count); sclp_tty_input(str, count);
}
static inline struct gds_vector *
find_gds_vector(struct gds_vector *start, struct gds_vector *end, u16 id)
{
struct gds_vector *vec;
for (vec = start; vec < end; vec = (void *) vec + vec->length)
if (vec->gds_id == id)
return vec;
return NULL;
} }
static inline struct gds_subvector * static inline void sclp_eval_selfdeftextmsg(struct gds_subvector *sv)
find_gds_subvector(struct gds_subvector *start,
struct gds_subvector *end, u8 key)
{ {
struct gds_subvector *subvec; void *end;
for (subvec = start; subvec < end; end = (void *) sv + sv->length;
subvec = (void *) subvec + subvec->length) for (sv = sv + 1; (void *) sv < end; sv = (void *) sv + sv->length)
if (subvec->key == key) if (sv->key == 0x30)
return subvec; sclp_get_input(sv);
return NULL;
} }
static inline void static inline void sclp_eval_textcmd(struct gds_vector *v)
sclp_eval_selfdeftextmsg(struct gds_subvector *start,
struct gds_subvector *end)
{ {
struct gds_subvector *subvec; struct gds_subvector *sv;
void *end;
subvec = start;
while (subvec < end) {
subvec = find_gds_subvector(subvec, end, 0x30);
if (!subvec)
break;
sclp_get_input((unsigned char *)(subvec + 1),
(unsigned char *) subvec + subvec->length);
subvec = (void *) subvec + subvec->length;
}
}
static inline void end = (void *) v + v->length;
sclp_eval_textcmd(struct gds_subvector *start, for (sv = (struct gds_subvector *) (v + 1);
struct gds_subvector *end) (void *) sv < end; sv = (void *) sv + sv->length)
{ if (sv->key == GDS_KEY_SELFDEFTEXTMSG)
struct gds_subvector *subvec; sclp_eval_selfdeftextmsg(sv);
subvec = start;
while (subvec < end) {
subvec = find_gds_subvector(subvec, end,
GDS_KEY_SELFDEFTEXTMSG);
if (!subvec)
break;
sclp_eval_selfdeftextmsg((struct gds_subvector *)(subvec + 1),
(void *)subvec + subvec->length);
subvec = (void *) subvec + subvec->length;
}
} }
static inline void static inline void sclp_eval_cpmsu(struct gds_vector *v)
sclp_eval_cpmsu(struct gds_vector *start, struct gds_vector *end)
{ {
struct gds_vector *vec; void *end;
vec = start; end = (void *) v + v->length;
while (vec < end) { for (v = v + 1; (void *) v < end; v = (void *) v + v->length)
vec = find_gds_vector(vec, end, GDS_ID_TEXTCMD); if (v->gds_id == GDS_ID_TEXTCMD)
if (!vec) sclp_eval_textcmd(v);
break;
sclp_eval_textcmd((struct gds_subvector *)(vec + 1),
(void *) vec + vec->length);
vec = (void *) vec + vec->length;
}
} }
static inline void static inline void sclp_eval_mdsmu(struct gds_vector *v)
sclp_eval_mdsmu(struct gds_vector *start, void *end)
{ {
struct gds_vector *vec; v = sclp_find_gds_vector(v + 1, (void *) v + v->length, GDS_ID_CPMSU);
if (v)
vec = find_gds_vector(start, end, GDS_ID_CPMSU); sclp_eval_cpmsu(v);
if (vec)
sclp_eval_cpmsu(vec + 1, (void *) vec + vec->length);
} }
static void static void sclp_tty_receiver(struct evbuf_header *evbuf)
sclp_tty_receiver(struct evbuf_header *evbuf)
{ {
struct gds_vector *start, *end, *vec; struct gds_vector *v;
start = (struct gds_vector *)(evbuf + 1); v = sclp_find_gds_vector(evbuf + 1, (void *) evbuf + evbuf->length,
end = (void *) evbuf + evbuf->length; GDS_ID_MDSMU);
vec = find_gds_vector(start, end, GDS_ID_MDSMU); if (v)
if (vec) sclp_eval_mdsmu(v);
sclp_eval_mdsmu(vec + 1, (void *) vec + vec->length);
} }
static void static void
......
...@@ -796,10 +796,8 @@ static void tape_3590_med_state_set(struct tape_device *device, ...@@ -796,10 +796,8 @@ static void tape_3590_med_state_set(struct tape_device *device,
static int static int
tape_3590_done(struct tape_device *device, struct tape_request *request) tape_3590_done(struct tape_device *device, struct tape_request *request)
{ {
struct tape_3590_disc_data *disc_data;
DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]); DBF_EVENT(6, "%s done\n", tape_op_verbose[request->op]);
disc_data = device->discdata;
switch (request->op) { switch (request->op) {
case TO_BSB: case TO_BSB:
...@@ -1394,17 +1392,12 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb) ...@@ -1394,17 +1392,12 @@ tape_3590_print_era_msg(struct tape_device *device, struct irb *irb)
static int tape_3590_crypt_error(struct tape_device *device, static int tape_3590_crypt_error(struct tape_device *device,
struct tape_request *request, struct irb *irb) struct tape_request *request, struct irb *irb)
{ {
u8 cu_rc, ekm_rc1; u8 cu_rc;
u16 ekm_rc2; u16 ekm_rc2;
u32 drv_rc;
const char *bus_id;
char *sense; char *sense;
sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data; sense = ((struct tape_3590_sense *) irb->ecw)->fmt.data;
bus_id = dev_name(&device->cdev->dev);
cu_rc = sense[0]; cu_rc = sense[0];
drv_rc = *((u32*) &sense[5]) & 0xffffff;
ekm_rc1 = sense[9];
ekm_rc2 = *((u16*) &sense[10]); ekm_rc2 = *((u16*) &sense[10]);
if ((cu_rc == 0) && (ekm_rc2 == 0xee31)) if ((cu_rc == 0) && (ekm_rc2 == 0xee31))
/* key not defined on EKM */ /* key not defined on EKM */
...@@ -1429,7 +1422,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request, ...@@ -1429,7 +1422,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
struct irb *irb) struct irb *irb)
{ {
struct tape_3590_sense *sense; struct tape_3590_sense *sense;
int rc;
#ifdef CONFIG_S390_TAPE_BLOCK #ifdef CONFIG_S390_TAPE_BLOCK
if (request->op == TO_BLOCK) { if (request->op == TO_BLOCK) {
...@@ -1454,7 +1446,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request, ...@@ -1454,7 +1446,6 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
* - "break": basic error recovery is done * - "break": basic error recovery is done
* - "goto out:": just print error message if available * - "goto out:": just print error message if available
*/ */
rc = -EIO;
switch (sense->rc_rqc) { switch (sense->rc_rqc) {
case 0x1110: case 0x1110:
......
/*
* drivers/s390/char/tape_block.c
* block device frontend for tape device driver
*
* S390 and zSeries version
* Copyright (C) 2001,2003 IBM Deutschland Entwicklung GmbH, IBM Corporation
* Author(s): Carsten Otte <cotte@de.ibm.com>
* Tuan Ngo-Anh <ngoanh@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Stefan Bader <shbader@de.ibm.com>
*/
#define KMSG_COMPONENT "tape"
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/blkdev.h>
#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/buffer_head.h>
#include <linux/kernel.h>
#include <asm/debug.h>
#define TAPE_DBF_AREA tape_core_dbf
#include "tape.h"
#define TAPEBLOCK_MAX_SEC 100
#define TAPEBLOCK_MIN_REQUEUE 3
/*
* 2003/11/25 Stefan Bader <shbader@de.ibm.com>
*
* In 2.5/2.6 the block device request function is very likely to be called
* with disabled interrupts (e.g. generic_unplug_device). So the driver can't
* just call any function that tries to allocate CCW requests from that con-
* text since it might sleep. There are two choices to work around this:
* a) do not allocate with kmalloc but use its own memory pool
* b) take requests from the queue outside that context, knowing that
* allocation might sleep
*/
/*
* file operation structure for tape block frontend
*/
static DEFINE_MUTEX(tape_block_mutex);
static int tapeblock_open(struct block_device *, fmode_t);
static int tapeblock_release(struct gendisk *, fmode_t);
static unsigned int tapeblock_check_events(struct gendisk *, unsigned int);
static int tapeblock_revalidate_disk(struct gendisk *);
static const struct block_device_operations tapeblock_fops = {
.owner = THIS_MODULE,
.open = tapeblock_open,
.release = tapeblock_release,
.check_events = tapeblock_check_events,
.revalidate_disk = tapeblock_revalidate_disk,
};
static int tapeblock_major = 0;
static void
tapeblock_trigger_requeue(struct tape_device *device)
{
/* Protect against rescheduling. */
if (atomic_cmpxchg(&device->blk_data.requeue_scheduled, 0, 1) != 0)
return;
schedule_work(&device->blk_data.requeue_task);
}
/*
* Post finished request.
*/
static void
__tapeblock_end_request(struct tape_request *ccw_req, void *data)
{
struct tape_device *device;
struct request *req;
DBF_LH(6, "__tapeblock_end_request()\n");
device = ccw_req->device;
req = (struct request *) data;
blk_end_request_all(req, (ccw_req->rc == 0) ? 0 : -EIO);
if (ccw_req->rc == 0)
/* Update position. */
device->blk_data.block_position =
(blk_rq_pos(req) + blk_rq_sectors(req)) >> TAPEBLOCK_HSEC_S2B;
else
/* We lost the position information due to an error. */
device->blk_data.block_position = -1;
device->discipline->free_bread(ccw_req);
if (!list_empty(&device->req_queue) ||
blk_peek_request(device->blk_data.request_queue))
tapeblock_trigger_requeue(device);
}
/*
* Feed the tape device CCW queue with requests supplied in a list.
*/
static int
tapeblock_start_request(struct tape_device *device, struct request *req)
{
struct tape_request * ccw_req;
int rc;
DBF_LH(6, "tapeblock_start_request(%p, %p)\n", device, req);
ccw_req = device->discipline->bread(device, req);
if (IS_ERR(ccw_req)) {
DBF_EVENT(1, "TBLOCK: bread failed\n");
blk_end_request_all(req, -EIO);
return PTR_ERR(ccw_req);
}
ccw_req->callback = __tapeblock_end_request;
ccw_req->callback_data = (void *) req;
ccw_req->retries = TAPEBLOCK_RETRIES;
rc = tape_do_io_async(device, ccw_req);
if (rc) {
/*
* Start/enqueueing failed. No retries in
* this case.
*/
blk_end_request_all(req, -EIO);
device->discipline->free_bread(ccw_req);
}
return rc;
}
/*
* Move requests from the block device request queue to the tape device ccw
* queue.
*/
static void
tapeblock_requeue(struct work_struct *work) {
struct tape_blk_data * blkdat;
struct tape_device * device;
struct request_queue * queue;
int nr_queued;
struct request * req;
struct list_head * l;
int rc;
blkdat = container_of(work, struct tape_blk_data, requeue_task);
device = blkdat->device;
if (!device)
return;
spin_lock_irq(get_ccwdev_lock(device->cdev));
queue = device->blk_data.request_queue;
/* Count number of requests on ccw queue. */
nr_queued = 0;
list_for_each(l, &device->req_queue)
nr_queued++;
spin_unlock(get_ccwdev_lock(device->cdev));
spin_lock_irq(&device->blk_data.request_queue_lock);
while (
blk_peek_request(queue) &&
nr_queued < TAPEBLOCK_MIN_REQUEUE
) {
req = blk_fetch_request(queue);
if (rq_data_dir(req) == WRITE) {
DBF_EVENT(1, "TBLOCK: Rejecting write request\n");
spin_unlock_irq(&device->blk_data.request_queue_lock);
blk_end_request_all(req, -EIO);
spin_lock_irq(&device->blk_data.request_queue_lock);
continue;
}
nr_queued++;
spin_unlock_irq(&device->blk_data.request_queue_lock);
rc = tapeblock_start_request(device, req);
spin_lock_irq(&device->blk_data.request_queue_lock);
}
spin_unlock_irq(&device->blk_data.request_queue_lock);
atomic_set(&device->blk_data.requeue_scheduled, 0);
}
/*
* Tape request queue function. Called from ll_rw_blk.c
*/
static void
tapeblock_request_fn(struct request_queue *queue)
{
struct tape_device *device;
device = (struct tape_device *) queue->queuedata;
DBF_LH(6, "tapeblock_request_fn(device=%p)\n", device);
BUG_ON(device == NULL);
tapeblock_trigger_requeue(device);
}
/*
* This function is called for every new tapedevice
*/
int
tapeblock_setup_device(struct tape_device * device)
{
struct tape_blk_data * blkdat;
struct gendisk * disk;
int rc;
blkdat = &device->blk_data;
blkdat->device = device;
spin_lock_init(&blkdat->request_queue_lock);
atomic_set(&blkdat->requeue_scheduled, 0);
blkdat->request_queue = blk_init_queue(
tapeblock_request_fn,
&blkdat->request_queue_lock
);
if (!blkdat->request_queue)
return -ENOMEM;
rc = elevator_change(blkdat->request_queue, "noop");
if (rc)
goto cleanup_queue;
blk_queue_logical_block_size(blkdat->request_queue, TAPEBLOCK_HSEC_SIZE);
blk_queue_max_hw_sectors(blkdat->request_queue, TAPEBLOCK_MAX_SEC);
blk_queue_max_segments(blkdat->request_queue, -1L);
blk_queue_max_segment_size(blkdat->request_queue, -1L);
blk_queue_segment_boundary(blkdat->request_queue, -1L);
disk = alloc_disk(1);
if (!disk) {
rc = -ENOMEM;
goto cleanup_queue;
}
disk->major = tapeblock_major;
disk->first_minor = device->first_minor;
disk->fops = &tapeblock_fops;
disk->private_data = tape_get_device(device);
disk->queue = blkdat->request_queue;
set_capacity(disk, 0);
sprintf(disk->disk_name, "btibm%d",
device->first_minor / TAPE_MINORS_PER_DEV);
blkdat->disk = disk;
blkdat->medium_changed = 1;
blkdat->request_queue->queuedata = tape_get_device(device);
add_disk(disk);
tape_get_device(device);
INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
return 0;
cleanup_queue:
blk_cleanup_queue(blkdat->request_queue);
blkdat->request_queue = NULL;
return rc;
}
void
tapeblock_cleanup_device(struct tape_device *device)
{
flush_work_sync(&device->blk_data.requeue_task);
tape_put_device(device);
if (!device->blk_data.disk) {
goto cleanup_queue;
}
del_gendisk(device->blk_data.disk);
device->blk_data.disk->private_data = NULL;
tape_put_device(device);
put_disk(device->blk_data.disk);
device->blk_data.disk = NULL;
cleanup_queue:
device->blk_data.request_queue->queuedata = NULL;
tape_put_device(device);
blk_cleanup_queue(device->blk_data.request_queue);
device->blk_data.request_queue = NULL;
}
/*
* Detect number of blocks of the tape.
* FIXME: can we extent this to detect the blocks size as well ?
*/
static int
tapeblock_revalidate_disk(struct gendisk *disk)
{
struct tape_device * device;
unsigned int nr_of_blks;
int rc;
device = (struct tape_device *) disk->private_data;
BUG_ON(!device);
if (!device->blk_data.medium_changed)
return 0;
rc = tape_mtop(device, MTFSFM, 1);
if (rc)
return rc;
rc = tape_mtop(device, MTTELL, 1);
if (rc < 0)
return rc;
pr_info("%s: Determining the size of the recorded area...\n",
dev_name(&device->cdev->dev));
DBF_LH(3, "Image file ends at %d\n", rc);
nr_of_blks = rc;
/* This will fail for the first file. Catch the error by checking the
* position. */
tape_mtop(device, MTBSF, 1);
rc = tape_mtop(device, MTTELL, 1);
if (rc < 0)
return rc;
if (rc > nr_of_blks)
return -EINVAL;
DBF_LH(3, "Image file starts at %d\n", rc);
device->bof = rc;
nr_of_blks -= rc;
pr_info("%s: The size of the recorded area is %i blocks\n",
dev_name(&device->cdev->dev), nr_of_blks);
set_capacity(device->blk_data.disk,
nr_of_blks*(TAPEBLOCK_HSEC_SIZE/512));
device->blk_data.block_position = 0;
device->blk_data.medium_changed = 0;
return 0;
}
static unsigned int
tapeblock_check_events(struct gendisk *disk, unsigned int clearing)
{
struct tape_device *device;
device = (struct tape_device *) disk->private_data;
DBF_LH(6, "tapeblock_medium_changed(%p) = %d\n",
device, device->blk_data.medium_changed);
return device->blk_data.medium_changed ? DISK_EVENT_MEDIA_CHANGE : 0;
}
/*
* Block frontend tape device open function.
*/
static int
tapeblock_open(struct block_device *bdev, fmode_t mode)
{
struct gendisk * disk = bdev->bd_disk;
struct tape_device * device;
int rc;
mutex_lock(&tape_block_mutex);
device = tape_get_device(disk->private_data);
if (device->required_tapemarks) {
DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
pr_warning("%s: Opening the tape failed because of missing "
"end-of-file marks\n", dev_name(&device->cdev->dev));
rc = -EPERM;
goto put_device;
}
rc = tape_open(device);
if (rc)
goto put_device;
rc = tapeblock_revalidate_disk(disk);
if (rc)
goto release;
/*
* Note: The reference to <device> is hold until the release function
* is called.
*/
tape_state_set(device, TS_BLKUSE);
mutex_unlock(&tape_block_mutex);
return 0;
release:
tape_release(device);
put_device:
tape_put_device(device);
mutex_unlock(&tape_block_mutex);
return rc;
}
/*
* Block frontend tape device release function.
*
* Note: One reference to the tape device was made by the open function. So
* we just get the pointer here and release the reference.
*/
static int
tapeblock_release(struct gendisk *disk, fmode_t mode)
{
struct tape_device *device = disk->private_data;
mutex_lock(&tape_block_mutex);
tape_state_set(device, TS_IN_USE);
tape_release(device);
tape_put_device(device);
mutex_unlock(&tape_block_mutex);
return 0;
}
/*
* Initialize block device frontend.
*/
int
tapeblock_init(void)
{
int rc;
/* Register the tape major number to the kernel */
rc = register_blkdev(tapeblock_major, "tBLK");
if (rc < 0)
return rc;
if (tapeblock_major == 0)
tapeblock_major = rc;
return 0;
}
/*
* Deregister major for block device frontend
*/
void
tapeblock_exit(void)
{
unregister_blkdev(tapeblock_major, "tBLK");
}
...@@ -564,7 +564,6 @@ int ...@@ -564,7 +564,6 @@ int
tape_std_mtreten(struct tape_device *device, int mt_count) tape_std_mtreten(struct tape_device *device, int mt_count)
{ {
struct tape_request *request; struct tape_request *request;
int rc;
request = tape_alloc_request(4, 0); request = tape_alloc_request(4, 0);
if (IS_ERR(request)) if (IS_ERR(request))
...@@ -576,7 +575,7 @@ tape_std_mtreten(struct tape_device *device, int mt_count) ...@@ -576,7 +575,7 @@ tape_std_mtreten(struct tape_device *device, int mt_count)
tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL); tape_ccw_cc(request->cpaddr + 2, NOP, 0, NULL);
tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr); tape_ccw_end(request->cpaddr + 3, CCW_CMD_TIC, 0, request->cpaddr);
/* execute it, MTRETEN rc gets ignored */ /* execute it, MTRETEN rc gets ignored */
rc = tape_do_io_interruptible(device, request); tape_do_io_interruptible(device, request);
tape_free_request(request); tape_free_request(request);
return tape_mtop(device, MTREW, 1); return tape_mtop(device, MTREW, 1);
} }
......
...@@ -326,6 +326,36 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area) ...@@ -326,6 +326,36 @@ static void chsc_process_sei_res_acc(struct chsc_sei_area *sei_area)
s390_process_res_acc(&link); s390_process_res_acc(&link);
} }
static void chsc_process_sei_chp_avail(struct chsc_sei_area *sei_area)
{
struct channel_path *chp;
struct chp_id chpid;
u8 *data;
int num;
CIO_CRW_EVENT(4, "chsc: channel path availability information\n");
if (sei_area->rs != 0)
return;
data = sei_area->ccdf;
chp_id_init(&chpid);
for (num = 0; num <= __MAX_CHPID; num++) {
if (!chp_test_bit(data, num))
continue;
chpid.id = num;
CIO_CRW_EVENT(4, "Update information for channel path "
"%x.%02x\n", chpid.cssid, chpid.id);
chp = chpid_to_chp(chpid);
if (!chp) {
chp_new(chpid);
continue;
}
mutex_lock(&chp->lock);
chsc_determine_base_channel_path_desc(chpid, &chp->desc);
mutex_unlock(&chp->lock);
}
}
struct chp_config_data { struct chp_config_data {
u8 map[32]; u8 map[32];
u8 op; u8 op;
...@@ -376,9 +406,12 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area) ...@@ -376,9 +406,12 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
case 1: /* link incident*/ case 1: /* link incident*/
chsc_process_sei_link_incident(sei_area); chsc_process_sei_link_incident(sei_area);
break; break;
case 2: /* i/o resource accessibiliy */ case 2: /* i/o resource accessibility */
chsc_process_sei_res_acc(sei_area); chsc_process_sei_res_acc(sei_area);
break; break;
case 7: /* channel-path-availability information */
chsc_process_sei_chp_avail(sei_area);
break;
case 8: /* channel-path-configuration notification */ case 8: /* channel-path-configuration notification */
chsc_process_sei_chp_config(sei_area); chsc_process_sei_chp_config(sei_area);
break; break;
......
...@@ -408,9 +408,10 @@ ccw_device_done(struct ccw_device *cdev, int state) ...@@ -408,9 +408,10 @@ ccw_device_done(struct ccw_device *cdev, int state)
CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel " CIO_MSG_EVENT(0, "Disconnected device %04x on subchannel "
"%04x\n", cdev->private->dev_id.devno, "%04x\n", cdev->private->dev_id.devno,
sch->schid.sch_no); sch->schid.sch_no);
if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK) if (ccw_device_notify(cdev, CIO_NO_PATH) != NOTIFY_OK) {
cdev->private->state = DEV_STATE_NOT_OPER;
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG); ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
else } else
ccw_device_set_disconnected(cdev); ccw_device_set_disconnected(cdev);
cdev->private->flags.donotify = 0; cdev->private->flags.donotify = 0;
break; break;
...@@ -840,9 +841,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event) ...@@ -840,9 +841,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
static void static void
ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event) ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
{ {
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
ccw_device_set_timeout(cdev, 0); ccw_device_set_timeout(cdev, 0);
/* Start delayed path verification. */ /* Start delayed path verification. */
ccw_device_online_verify(cdev, 0); ccw_device_online_verify(cdev, 0);
......
...@@ -418,12 +418,9 @@ int ccw_device_resume(struct ccw_device *cdev) ...@@ -418,12 +418,9 @@ int ccw_device_resume(struct ccw_device *cdev)
int int
ccw_device_call_handler(struct ccw_device *cdev) ccw_device_call_handler(struct ccw_device *cdev)
{ {
struct subchannel *sch;
unsigned int stctl; unsigned int stctl;
int ending_status; int ending_status;
sch = to_subchannel(cdev->dev.parent);
/* /*
* we allow for the device action handler if . * we allow for the device action handler if .
* - we received ending status * - we received ending status
......
...@@ -1446,7 +1446,7 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags, ...@@ -1446,7 +1446,7 @@ static int handle_inbound(struct qdio_q *q, unsigned int callflags,
static int handle_outbound(struct qdio_q *q, unsigned int callflags, static int handle_outbound(struct qdio_q *q, unsigned int callflags,
int bufnr, int count) int bufnr, int count)
{ {
unsigned char state; unsigned char state = 0;
int used, rc = 0; int used, rc = 0;
qperf_inc(q, outbound_call); qperf_inc(q, outbound_call);
......
...@@ -1183,8 +1183,12 @@ static void ap_scan_bus(struct work_struct *unused) ...@@ -1183,8 +1183,12 @@ static void ap_scan_bus(struct work_struct *unused)
INIT_LIST_HEAD(&ap_dev->list); INIT_LIST_HEAD(&ap_dev->list);
setup_timer(&ap_dev->timeout, ap_request_timeout, setup_timer(&ap_dev->timeout, ap_request_timeout,
(unsigned long) ap_dev); (unsigned long) ap_dev);
if (device_type == 0) if (device_type == 0) {
ap_probe_device_type(ap_dev); if (ap_probe_device_type(ap_dev)) {
kfree(ap_dev);
continue;
}
}
else else
ap_dev->device_type = device_type; ap_dev->device_type = device_type;
......
...@@ -184,22 +184,18 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b) ...@@ -184,22 +184,18 @@ static inline int pmd_same(pmd_t pmd_a, pmd_t pmd_b)
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */ #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#endif #endif
#ifndef __HAVE_ARCH_PAGE_TEST_DIRTY #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY
#define page_test_dirty(page) (0) #define page_test_and_clear_dirty(pfn, mapped) (0)
#endif #endif
#ifndef __HAVE_ARCH_PAGE_CLEAR_DIRTY #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_DIRTY
#define page_clear_dirty(page, mapped) do { } while (0)
#endif
#ifndef __HAVE_ARCH_PAGE_TEST_DIRTY
#define pte_maybe_dirty(pte) pte_dirty(pte) #define pte_maybe_dirty(pte) pte_dirty(pte)
#else #else
#define pte_maybe_dirty(pte) (1) #define pte_maybe_dirty(pte) (1)
#endif #endif
#ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG #ifndef __HAVE_ARCH_PAGE_TEST_AND_CLEAR_YOUNG
#define page_test_and_clear_young(page) (0) #define page_test_and_clear_young(pfn) (0)
#endif #endif
#ifndef __HAVE_ARCH_PGD_OFFSET_GATE #ifndef __HAVE_ARCH_PGD_OFFSET_GATE
......
...@@ -308,7 +308,7 @@ static inline void SetPageUptodate(struct page *page) ...@@ -308,7 +308,7 @@ static inline void SetPageUptodate(struct page *page)
{ {
#ifdef CONFIG_S390 #ifdef CONFIG_S390
if (!test_and_set_bit(PG_uptodate, &page->flags)) if (!test_and_set_bit(PG_uptodate, &page->flags))
page_clear_dirty(page, 0); page_set_storage_key(page_to_pfn(page), PAGE_DEFAULT_KEY, 0);
#else #else
/* /*
* Memory barrier must be issued before setting the PG_uptodate bit, * Memory barrier must be issued before setting the PG_uptodate bit,
......
...@@ -719,7 +719,7 @@ int page_referenced(struct page *page, ...@@ -719,7 +719,7 @@ int page_referenced(struct page *page,
unlock_page(page); unlock_page(page);
} }
out: out:
if (page_test_and_clear_young(page)) if (page_test_and_clear_young(page_to_pfn(page)))
referenced++; referenced++;
return referenced; return referenced;
...@@ -785,12 +785,10 @@ int page_mkclean(struct page *page) ...@@ -785,12 +785,10 @@ int page_mkclean(struct page *page)
struct address_space *mapping = page_mapping(page); struct address_space *mapping = page_mapping(page);
if (mapping) { if (mapping) {
ret = page_mkclean_file(mapping, page); ret = page_mkclean_file(mapping, page);
if (page_test_dirty(page)) { if (page_test_and_clear_dirty(page_to_pfn(page), 1))
page_clear_dirty(page, 1);
ret = 1; ret = 1;
} }
} }
}
return ret; return ret;
} }
...@@ -981,10 +979,9 @@ void page_remove_rmap(struct page *page) ...@@ -981,10 +979,9 @@ void page_remove_rmap(struct page *page)
* not if it's in swapcache - there might be another pte slot * not if it's in swapcache - there might be another pte slot
* containing the swap entry, but page not yet written to swap. * containing the swap entry, but page not yet written to swap.
*/ */
if ((!PageAnon(page) || PageSwapCache(page)) && page_test_dirty(page)) { if ((!PageAnon(page) || PageSwapCache(page)) &&
page_clear_dirty(page, 1); page_test_and_clear_dirty(page_to_pfn(page), 1))
set_page_dirty(page); set_page_dirty(page);
}
/* /*
* Hugepages are not counted in NR_ANON_PAGES nor NR_FILE_MAPPED * Hugepages are not counted in NR_ANON_PAGES nor NR_FILE_MAPPED
* and not charged by memcg for now. * and not charged by memcg for now.
......
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