Commit 67dd2f5a 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: (72 commits)
  [S390] 3215/3270 console: remove wrong comment
  [S390] dasd: remove BKL from extended error reporting code
  [S390] vmlogrdr: remove BKL
  [S390] vmur: remove BKL
  [S390] zcrypt: remove BKL
  [S390] 3270: remove BKL
  [S390] vmwatchdog: remove lock_kernel() from open() function
  [S390] monwriter: remove lock_kernel() from open() function
  [S390] monreader: remove lock_kernel() from open() function
  [S390] s390: remove unused nfsd #includes
  [S390] ftrace: build ftrace.o when CONFIG_FTRACE_SYSCALLS is set for s390
  [S390] etr/stp: put correct per cpu variable
  [S390] tty3270: move keyboard compat ioctls
  [S390] sclp: improve servicability setting
  [S390] s390: use change recording override for kernel mapping
  [S390] MAINTAINERS: Add s390 drivers block
  [S390] use generic sockios.h header file
  [S390] use generic termbits.h header file
  [S390] smp: remove unused typedef and defines
  [S390] cmm: free pages on hibernate.
  ...
parents 5327b9b8 42d61b9b
...@@ -3139,6 +3139,7 @@ S: Supported ...@@ -3139,6 +3139,7 @@ S: Supported
F: Documentation/s390/kvm.txt F: Documentation/s390/kvm.txt
F: arch/s390/include/asm/kvm* F: arch/s390/include/asm/kvm*
F: arch/s390/kvm/ F: arch/s390/kvm/
F: drivers/s390/kvm/
KEXEC KEXEC
M: Eric Biederman <ebiederm@xmission.com> M: Eric Biederman <ebiederm@xmission.com>
...@@ -4553,6 +4554,7 @@ L: linux-s390@vger.kernel.org ...@@ -4553,6 +4554,7 @@ L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/ W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
F: arch/s390/ F: arch/s390/
F: drivers/s390/
S390 NETWORK DRIVERS S390 NETWORK DRIVERS
M: Ursula Braun <ursula.braun@de.ibm.com> M: Ursula Braun <ursula.braun@de.ibm.com>
...@@ -4568,6 +4570,7 @@ M: Felix Beck <felix.beck@de.ibm.com> ...@@ -4568,6 +4570,7 @@ M: Felix Beck <felix.beck@de.ibm.com>
M: Ralph Wuerthner <ralph.wuerthner@de.ibm.com> M: Ralph Wuerthner <ralph.wuerthner@de.ibm.com>
M: linux390@de.ibm.com M: linux390@de.ibm.com
L: linux-s390@vger.kernel.org L: linux-s390@vger.kernel.org
W: http://www.ibm.com/developerworks/linux/linux390/
S: Supported S: Supported
F: drivers/s390/crypto/ F: drivers/s390/crypto/
......
...@@ -220,23 +220,8 @@ config AUDIT_ARCH ...@@ -220,23 +220,8 @@ config AUDIT_ARCH
bool bool
default y default y
config S390_SWITCH_AMODE
bool "Switch kernel/user addressing modes"
help
This option allows to switch the addressing modes of kernel and user
space. The kernel parameter switch_amode=on will enable this feature,
default is disabled. Enabling this (via kernel parameter) on machines
earlier than IBM System z9-109 EC/BC will reduce system performance.
Note that this option will also be selected by selecting the execute
protection option below. Enabling the execute protection via the
noexec kernel parameter will also switch the addressing modes,
independent of the switch_amode kernel parameter.
config S390_EXEC_PROTECT config S390_EXEC_PROTECT
bool "Data execute protection" bool "Data execute protection"
select S390_SWITCH_AMODE
help help
This option allows to enable a buffer overflow protection for user This option allows to enable a buffer overflow protection for user
space programs and it also selects the addressing mode option above. space programs and it also selects the addressing mode option above.
......
...@@ -185,7 +185,6 @@ CONFIG_HOTPLUG_CPU=y ...@@ -185,7 +185,6 @@ CONFIG_HOTPLUG_CPU=y
CONFIG_COMPAT=y CONFIG_COMPAT=y
CONFIG_SYSVIPC_COMPAT=y CONFIG_SYSVIPC_COMPAT=y
CONFIG_AUDIT_ARCH=y CONFIG_AUDIT_ARCH=y
CONFIG_S390_SWITCH_AMODE=y
CONFIG_S390_EXEC_PROTECT=y CONFIG_S390_EXEC_PROTECT=y
# #
......
...@@ -21,7 +21,7 @@ ...@@ -21,7 +21,7 @@
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2) #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
#define __CS_LOOP(ptr, op_val, op_string) ({ \ #define __CS_LOOP(ptr, op_val, op_string) ({ \
typeof(ptr->counter) old_val, new_val; \ int old_val, new_val; \
asm volatile( \ asm volatile( \
" l %0,%2\n" \ " l %0,%2\n" \
"0: lr %1,%0\n" \ "0: lr %1,%0\n" \
...@@ -38,7 +38,7 @@ ...@@ -38,7 +38,7 @@
#else /* __GNUC__ */ #else /* __GNUC__ */
#define __CS_LOOP(ptr, op_val, op_string) ({ \ #define __CS_LOOP(ptr, op_val, op_string) ({ \
typeof(ptr->counter) old_val, new_val; \ int old_val, new_val; \
asm volatile( \ asm volatile( \
" l %0,0(%3)\n" \ " l %0,0(%3)\n" \
"0: lr %1,%0\n" \ "0: lr %1,%0\n" \
...@@ -143,7 +143,7 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u) ...@@ -143,7 +143,7 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2) #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
#define __CSG_LOOP(ptr, op_val, op_string) ({ \ #define __CSG_LOOP(ptr, op_val, op_string) ({ \
typeof(ptr->counter) old_val, new_val; \ long long old_val, new_val; \
asm volatile( \ asm volatile( \
" lg %0,%2\n" \ " lg %0,%2\n" \
"0: lgr %1,%0\n" \ "0: lgr %1,%0\n" \
...@@ -160,7 +160,7 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u) ...@@ -160,7 +160,7 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
#else /* __GNUC__ */ #else /* __GNUC__ */
#define __CSG_LOOP(ptr, op_val, op_string) ({ \ #define __CSG_LOOP(ptr, op_val, op_string) ({ \
typeof(ptr->counter) old_val, new_val; \ long long old_val, new_val; \
asm volatile( \ asm volatile( \
" lg %0,0(%3)\n" \ " lg %0,0(%3)\n" \
"0: lgr %1,%0\n" \ "0: lgr %1,%0\n" \
......
...@@ -142,6 +142,8 @@ struct ccw1; ...@@ -142,6 +142,8 @@ struct ccw1;
extern int ccw_device_set_options_mask(struct ccw_device *, unsigned long); extern int ccw_device_set_options_mask(struct ccw_device *, unsigned long);
extern int ccw_device_set_options(struct ccw_device *, unsigned long); extern int ccw_device_set_options(struct ccw_device *, unsigned long);
extern void ccw_device_clear_options(struct ccw_device *, unsigned long); extern void ccw_device_clear_options(struct ccw_device *, unsigned long);
int ccw_device_is_pathgroup(struct ccw_device *cdev);
int ccw_device_is_multipath(struct ccw_device *cdev);
/* Allow for i/o completion notification after primary interrupt status. */ /* Allow for i/o completion notification after primary interrupt status. */
#define CCWDEV_EARLY_NOTIFICATION 0x0001 #define CCWDEV_EARLY_NOTIFICATION 0x0001
...@@ -151,6 +153,8 @@ extern void ccw_device_clear_options(struct ccw_device *, unsigned long); ...@@ -151,6 +153,8 @@ extern void ccw_device_clear_options(struct ccw_device *, unsigned long);
#define CCWDEV_DO_PATHGROUP 0x0004 #define CCWDEV_DO_PATHGROUP 0x0004
/* Allow forced onlining of boxed devices. */ /* Allow forced onlining of boxed devices. */
#define CCWDEV_ALLOW_FORCE 0x0008 #define CCWDEV_ALLOW_FORCE 0x0008
/* Try to use multipath mode. */
#define CCWDEV_DO_MULTIPATH 0x0010
extern int ccw_device_start(struct ccw_device *, struct ccw1 *, extern int ccw_device_start(struct ccw_device *, struct ccw1 *,
unsigned long, __u8, unsigned long); unsigned long, __u8, unsigned long);
......
...@@ -36,7 +36,7 @@ static inline int init_new_context(struct task_struct *tsk, ...@@ -36,7 +36,7 @@ static inline int init_new_context(struct task_struct *tsk,
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 = s390_noexec; 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;
} }
...@@ -58,7 +58,7 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk) ...@@ -58,7 +58,7 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
pgd_t *pgd = mm->pgd; pgd_t *pgd = mm->pgd;
S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd); S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd);
if (switch_amode) { 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; pgd = mm->context.noexec ? get_shadow_table(pgd) : pgd;
S390_lowcore.user_exec_asce = mm->context.asce_bits | __pa(pgd); S390_lowcore.user_exec_asce = mm->context.asce_bits | __pa(pgd);
......
...@@ -143,7 +143,8 @@ static inline pgd_t *pgd_alloc(struct mm_struct *mm) ...@@ -143,7 +143,8 @@ 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.crst_list);
INIT_LIST_HEAD(&mm->context.pgtable_list); INIT_LIST_HEAD(&mm->context.pgtable_list);
return (pgd_t *) crst_table_alloc(mm, s390_noexec); return (pgd_t *)
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)
......
...@@ -169,12 +169,13 @@ extern unsigned long VMALLOC_START; ...@@ -169,12 +169,13 @@ extern unsigned long VMALLOC_START;
* STL Segment-Table-Length: Segment-table length (STL+1*16 entries -> up to 2048) * STL Segment-Table-Length: Segment-table length (STL+1*16 entries -> up to 2048)
* *
* A 64 bit pagetable entry of S390 has following format: * A 64 bit pagetable entry of S390 has following format:
* | PFRA |0IP0| OS | * | PFRA |0IPC| OS |
* 0000000000111111111122222222223333333333444444444455555555556666 * 0000000000111111111122222222223333333333444444444455555555556666
* 0123456789012345678901234567890123456789012345678901234567890123 * 0123456789012345678901234567890123456789012345678901234567890123
* *
* I Page-Invalid Bit: Page is not available for address-translation * I Page-Invalid Bit: Page is not available for address-translation
* P Page-Protection Bit: Store access not possible for page * P Page-Protection Bit: Store access not possible for page
* C Change-bit override: HW is not required to set change bit
* *
* A 64 bit segmenttable entry of S390 has following format: * A 64 bit segmenttable entry of S390 has following format:
* | P-table origin | TT * | P-table origin | TT
...@@ -218,6 +219,7 @@ extern unsigned long VMALLOC_START; ...@@ -218,6 +219,7 @@ extern unsigned long VMALLOC_START;
*/ */
/* Hardware bits in the page table entry */ /* Hardware bits in the page table entry */
#define _PAGE_CO 0x100 /* HW Change-bit override */
#define _PAGE_RO 0x200 /* HW read-only bit */ #define _PAGE_RO 0x200 /* HW read-only bit */
#define _PAGE_INVALID 0x400 /* HW invalid bit */ #define _PAGE_INVALID 0x400 /* HW invalid bit */
......
...@@ -49,17 +49,12 @@ extern unsigned long memory_end; ...@@ -49,17 +49,12 @@ extern unsigned long memory_end;
void detect_memory_layout(struct mem_chunk chunk[]); void detect_memory_layout(struct mem_chunk chunk[]);
#ifdef CONFIG_S390_SWITCH_AMODE #define PRIMARY_SPACE_MODE 0
extern unsigned int switch_amode; #define ACCESS_REGISTER_MODE 1
#else #define SECONDARY_SPACE_MODE 2
#define switch_amode (0) #define HOME_SPACE_MODE 3
#endif
extern unsigned int user_mode;
#ifdef CONFIG_S390_EXEC_PROTECT
extern unsigned int s390_noexec;
#else
#define s390_noexec (0)
#endif
/* /*
* Machine features detected in head.S * Machine features detected in head.S
......
/* /*
* include/asm-s390/smp.h * Copyright IBM Corp. 1999,2009
* * Author(s): Denis Joseph Barrow,
* S390 version * Martin Schwidefsky <schwidefsky@de.ibm.com>,
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation * Heiko Carstens <heiko.carstens@de.ibm.com>,
* Author(s): Denis Joseph Barrow (djbarrow@de.ibm.com,barrow_dj@yahoo.com),
* Martin Schwidefsky (schwidefsky@de.ibm.com)
* Heiko Carstens (heiko.carstens@de.ibm.com)
*/ */
#ifndef __ASM_SMP_H #ifndef __ASM_SMP_H
#define __ASM_SMP_H #define __ASM_SMP_H
#include <linux/threads.h> #ifdef CONFIG_SMP
#include <linux/cpumask.h>
#include <linux/bitops.h>
#if defined(__KERNEL__) && defined(CONFIG_SMP) && !defined(__ASSEMBLY__)
#include <asm/lowcore.h>
#include <asm/sigp.h>
#include <asm/ptrace.h>
#include <asm/system.h> #include <asm/system.h>
#include <asm/sigp.h>
/*
s390 specific smp.c headers
*/
typedef struct
{
int intresting;
sigp_ccode ccode;
__u32 status;
__u16 cpu;
} sigp_info;
extern void machine_restart_smp(char *); extern void machine_restart_smp(char *);
extern void machine_halt_smp(void); extern void machine_halt_smp(void);
extern void machine_power_off_smp(void); extern void machine_power_off_smp(void);
#define NO_PROC_ID 0xFF /* No processor magic marker */
/*
* This magic constant controls our willingness to transfer
* a process across CPUs. Such a transfer incurs misses on the L1
* cache, and on a P6 or P5 with multiple L2 caches L2 hits. My
* gut feeling is this will vary by board in value. For a board
* with separate L2 cache it probably depends also on the RSS, and
* for a board with shared L2 cache it ought to decay fast as other
* processes are run.
*/
#define PROC_CHANGE_PENALTY 20 /* Schedule penalty */
#define raw_smp_processor_id() (S390_lowcore.cpu_nr) #define raw_smp_processor_id() (S390_lowcore.cpu_nr)
#define cpu_logical_map(cpu) (cpu)
extern int __cpu_disable (void); extern int __cpu_disable (void);
extern void __cpu_die (unsigned int cpu); extern void __cpu_die (unsigned int cpu);
...@@ -64,7 +29,9 @@ extern int smp_cpu_polarization[]; ...@@ -64,7 +29,9 @@ extern int smp_cpu_polarization[];
extern void arch_send_call_function_single_ipi(int cpu); extern void arch_send_call_function_single_ipi(int cpu);
extern void arch_send_call_function_ipi_mask(const struct cpumask *mask); extern void arch_send_call_function_ipi_mask(const struct cpumask *mask);
#endif extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
#endif /* CONFIG_SMP */
#ifdef CONFIG_HOTPLUG_CPU #ifdef CONFIG_HOTPLUG_CPU
extern int smp_rescan_cpus(void); extern int smp_rescan_cpus(void);
...@@ -72,5 +39,4 @@ extern int smp_rescan_cpus(void); ...@@ -72,5 +39,4 @@ extern int smp_rescan_cpus(void);
static inline int smp_rescan_cpus(void) { return 0; } static inline int smp_rescan_cpus(void) { return 0; }
#endif #endif
extern union save_area *zfcpdump_save_areas[NR_CPUS + 1]; #endif /* __ASM_SMP_H */
#endif
/* #ifndef _ASM_S390_SOCKIOS_H
* include/asm-s390/sockios.h #define _ASM_S390_SOCKIOS_H
*
* S390 version
*
* Derived from "include/asm-i386/sockios.h"
*/
#ifndef __ARCH_S390_SOCKIOS__ #include <asm-generic/sockios.h>
#define __ARCH_S390_SOCKIOS__
/* Socket-level I/O control calls. */
#define FIOSETOWN 0x8901
#define SIOCSPGRP 0x8902
#define FIOGETOWN 0x8903
#define SIOCGPGRP 0x8904
#define SIOCATMARK 0x8905
#define SIOCGSTAMP 0x8906 /* Get stamp (timeval) */
#define SIOCGSTAMPNS 0x8907 /* Get stamp (timespec) */
#endif #endif
/* #ifndef _ASM_S390_TERMBITS_H
* include/asm-s390/termbits.h #define _ASM_S390_TERMBITS_H
*
* S390 version
*
* Derived from "include/asm-i386/termbits.h"
*/
#ifndef __ARCH_S390_TERMBITS_H__ #include <asm-generic/termbits.h>
#define __ARCH_S390_TERMBITS_H__
#include <linux/posix_types.h>
typedef unsigned char cc_t;
typedef unsigned int speed_t;
typedef unsigned int tcflag_t;
#define NCCS 19
struct termios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
};
struct termios2 {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
struct ktermios {
tcflag_t c_iflag; /* input mode flags */
tcflag_t c_oflag; /* output mode flags */
tcflag_t c_cflag; /* control mode flags */
tcflag_t c_lflag; /* local mode flags */
cc_t c_line; /* line discipline */
cc_t c_cc[NCCS]; /* control characters */
speed_t c_ispeed; /* input speed */
speed_t c_ospeed; /* output speed */
};
/* c_cc characters */
#define VINTR 0
#define VQUIT 1
#define VERASE 2
#define VKILL 3
#define VEOF 4
#define VTIME 5
#define VMIN 6
#define VSWTC 7
#define VSTART 8
#define VSTOP 9
#define VSUSP 10
#define VEOL 11
#define VREPRINT 12
#define VDISCARD 13
#define VWERASE 14
#define VLNEXT 15
#define VEOL2 16
/* c_iflag bits */
#define IGNBRK 0000001
#define BRKINT 0000002
#define IGNPAR 0000004
#define PARMRK 0000010
#define INPCK 0000020
#define ISTRIP 0000040
#define INLCR 0000100
#define IGNCR 0000200
#define ICRNL 0000400
#define IUCLC 0001000
#define IXON 0002000
#define IXANY 0004000
#define IXOFF 0010000
#define IMAXBEL 0020000
#define IUTF8 0040000
/* c_oflag bits */
#define OPOST 0000001
#define OLCUC 0000002
#define ONLCR 0000004
#define OCRNL 0000010
#define ONOCR 0000020
#define ONLRET 0000040
#define OFILL 0000100
#define OFDEL 0000200
#define NLDLY 0000400
#define NL0 0000000
#define NL1 0000400
#define CRDLY 0003000
#define CR0 0000000
#define CR1 0001000
#define CR2 0002000
#define CR3 0003000
#define TABDLY 0014000
#define TAB0 0000000
#define TAB1 0004000
#define TAB2 0010000
#define TAB3 0014000
#define XTABS 0014000
#define BSDLY 0020000
#define BS0 0000000
#define BS1 0020000
#define VTDLY 0040000
#define VT0 0000000
#define VT1 0040000
#define FFDLY 0100000
#define FF0 0000000
#define FF1 0100000
/* c_cflag bit meaning */
#define CBAUD 0010017
#define B0 0000000 /* hang up */
#define B50 0000001
#define B75 0000002
#define B110 0000003
#define B134 0000004
#define B150 0000005
#define B200 0000006
#define B300 0000007
#define B600 0000010
#define B1200 0000011
#define B1800 0000012
#define B2400 0000013
#define B4800 0000014
#define B9600 0000015
#define B19200 0000016
#define B38400 0000017
#define EXTA B19200
#define EXTB B38400
#define CSIZE 0000060
#define CS5 0000000
#define CS6 0000020
#define CS7 0000040
#define CS8 0000060
#define CSTOPB 0000100
#define CREAD 0000200
#define PARENB 0000400
#define PARODD 0001000
#define HUPCL 0002000
#define CLOCAL 0004000
#define CBAUDEX 0010000
#define BOTHER 0010000
#define B57600 0010001
#define B115200 0010002
#define B230400 0010003
#define B460800 0010004
#define B500000 0010005
#define B576000 0010006
#define B921600 0010007
#define B1000000 0010010
#define B1152000 0010011
#define B1500000 0010012
#define B2000000 0010013
#define B2500000 0010014
#define B3000000 0010015
#define B3500000 0010016
#define B4000000 0010017
#define CIBAUD 002003600000 /* input baud rate */
#define CMSPAR 010000000000 /* mark or space (stick) parity */
#define CRTSCTS 020000000000 /* flow control */
#define IBSHIFT 16 /* Shift from CBAUD to CIBAUD */
/* c_lflag bits */
#define ISIG 0000001
#define ICANON 0000002
#define XCASE 0000004
#define ECHO 0000010
#define ECHOE 0000020
#define ECHOK 0000040
#define ECHONL 0000100
#define NOFLSH 0000200
#define TOSTOP 0000400
#define ECHOCTL 0001000
#define ECHOPRT 0002000
#define ECHOKE 0004000
#define FLUSHO 0010000
#define PENDIN 0040000
#define IEXTEN 0100000
/* tcflow() and TCXONC use these */
#define TCOOFF 0
#define TCOON 1
#define TCIOFF 2
#define TCION 3
/* tcflush() and TCFLSH use these */
#define TCIFLUSH 0
#define TCOFLUSH 1
#define TCIOFLUSH 2
/* tcsetattr uses these */
#define TCSANOW 0
#define TCSADRAIN 1
#define TCSAFLUSH 2
#endif #endif
/*
* File...........: linux/include/asm/todclk.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
* History of changes (starts July 2000)
*/
#ifndef __ASM_TODCLK_H
#define __ASM_TODCLK_H
#ifdef __KERNEL__
#define TOD_uSEC (0x1000ULL)
#define TOD_mSEC (1000 * TOD_uSEC)
#define TOD_SEC (1000 * TOD_mSEC)
#define TOD_MIN (60 * TOD_SEC)
#define TOD_HOUR (60 * TOD_MIN)
#endif
#endif
...@@ -93,6 +93,8 @@ extern struct uaccess_ops uaccess_mvcos; ...@@ -93,6 +93,8 @@ extern struct uaccess_ops uaccess_mvcos;
extern struct uaccess_ops uaccess_mvcos_switch; extern struct uaccess_ops uaccess_mvcos_switch;
extern struct uaccess_ops uaccess_pt; extern struct uaccess_ops uaccess_pt;
extern int __handle_fault(unsigned long, unsigned long, int);
static inline int __put_user_fn(size_t size, void __user *ptr, void *x) static inline int __put_user_fn(size_t size, void __user *ptr, void *x)
{ {
size = uaccess.copy_to_user_small(size, ptr, x); size = uaccess.copy_to_user_small(size, ptr, x);
......
...@@ -44,6 +44,7 @@ obj-$(CONFIG_KPROBES) += kprobes.o ...@@ -44,6 +44,7 @@ obj-$(CONFIG_KPROBES) += kprobes.o
obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o) obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o)
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
# Kexec part # Kexec part
S390_KEXEC_OBJS := machine_kexec.o crash.o S390_KEXEC_OBJS := machine_kexec.o crash.o
......
...@@ -31,14 +31,8 @@ ...@@ -31,14 +31,8 @@
#include <linux/shm.h> #include <linux/shm.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uio.h> #include <linux/uio.h>
#include <linux/nfs_fs.h>
#include <linux/quota.h> #include <linux/quota.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/cache.h>
#include <linux/nfsd/xdr.h>
#include <linux/nfsd/syscall.h>
#include <linux/poll.h> #include <linux/poll.h>
#include <linux/personality.h> #include <linux/personality.h>
#include <linux/stat.h> #include <linux/stat.h>
......
...@@ -4,10 +4,6 @@ ...@@ -4,10 +4,6 @@
#include <linux/compat.h> #include <linux/compat.h>
#include <linux/socket.h> #include <linux/socket.h>
#include <linux/syscalls.h> #include <linux/syscalls.h>
#include <linux/nfs_fs.h>
#include <linux/sunrpc/svc.h>
#include <linux/nfsd/nfsd.h>
#include <linux/nfsd/export.h>
/* Macro that masks the high order bit of an 32 bit pointer and converts it*/ /* Macro that masks the high order bit of an 32 bit pointer and converts it*/
/* to a 64 bit pointer */ /* to a 64 bit pointer */
......
...@@ -83,6 +83,8 @@ startup_continue: ...@@ -83,6 +83,8 @@ startup_continue:
slr %r0,%r0 # set cpuid to zero slr %r0,%r0 # set cpuid to zero
sigp %r1,%r0,0x12 # switch to esame mode sigp %r1,%r0,0x12 # switch to esame mode
sam64 # switch to 64 bit mode sam64 # switch to 64 bit mode
llgfr %r13,%r13 # clear high-order half of base reg
lmh %r0,%r15,.Lzero64-.LPG1(%r13) # clear high-order half
lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers lctlg %c0,%c15,.Lctl-.LPG1(%r13) # load control registers
lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
# move IPL device to lowcore # move IPL device to lowcore
...@@ -127,6 +129,7 @@ startup_continue: ...@@ -127,6 +129,7 @@ startup_continue:
.L4malign:.quad 0xffffffffffc00000 .L4malign:.quad 0xffffffffffc00000
.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8 .Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
.Lnop: .long 0x07000700 .Lnop: .long 0x07000700
.Lzero64:.fill 16,4,0x0
#ifdef CONFIG_ZFCPDUMP #ifdef CONFIG_ZFCPDUMP
.Lcurrent_cpu: .Lcurrent_cpu:
.long 0x0 .long 0x0
......
...@@ -305,9 +305,8 @@ static int __init early_parse_mem(char *p) ...@@ -305,9 +305,8 @@ static int __init early_parse_mem(char *p)
} }
early_param("mem", early_parse_mem); early_param("mem", early_parse_mem);
#ifdef CONFIG_S390_SWITCH_AMODE unsigned int user_mode = HOME_SPACE_MODE;
unsigned int switch_amode = 0; EXPORT_SYMBOL_GPL(user_mode);
EXPORT_SYMBOL_GPL(switch_amode);
static int set_amode_and_uaccess(unsigned long user_amode, static int set_amode_and_uaccess(unsigned long user_amode,
unsigned long user32_amode) unsigned long user32_amode)
...@@ -340,23 +339,29 @@ static int set_amode_and_uaccess(unsigned long user_amode, ...@@ -340,23 +339,29 @@ 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)
{ {
switch_amode = 1; if (user_mode != SECONDARY_SPACE_MODE)
user_mode = PRIMARY_SPACE_MODE;
return 0; return 0;
} }
early_param("switch_amode", early_parse_switch_amode); early_param("switch_amode", early_parse_switch_amode);
#else /* CONFIG_S390_SWITCH_AMODE */ static int __init early_parse_user_mode(char *p)
static inline int set_amode_and_uaccess(unsigned long user_amode,
unsigned long user32_amode)
{ {
if (p && strcmp(p, "primary") == 0)
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)
user_mode = HOME_SPACE_MODE;
else
return 1;
return 0; return 0;
} }
#endif /* CONFIG_S390_SWITCH_AMODE */ early_param("user_mode", early_parse_user_mode);
#ifdef CONFIG_S390_EXEC_PROTECT #ifdef CONFIG_S390_EXEC_PROTECT
unsigned int s390_noexec = 0;
EXPORT_SYMBOL_GPL(s390_noexec);
/* /*
* Enable execute protection? * Enable execute protection?
*/ */
...@@ -364,8 +369,7 @@ static int __init early_parse_noexec(char *p) ...@@ -364,8 +369,7 @@ static int __init early_parse_noexec(char *p)
{ {
if (!strncmp(p, "off", 3)) if (!strncmp(p, "off", 3))
return 0; return 0;
switch_amode = 1; user_mode = SECONDARY_SPACE_MODE;
s390_noexec = 1;
return 0; return 0;
} }
early_param("noexec", early_parse_noexec); early_param("noexec", early_parse_noexec);
...@@ -373,7 +377,7 @@ early_param("noexec", early_parse_noexec); ...@@ -373,7 +377,7 @@ early_param("noexec", early_parse_noexec);
static void setup_addressing_mode(void) static void setup_addressing_mode(void)
{ {
if (s390_noexec) { if (user_mode == SECONDARY_SPACE_MODE) {
if (set_amode_and_uaccess(PSW_ASC_SECONDARY, if (set_amode_and_uaccess(PSW_ASC_SECONDARY,
PSW32_ASC_SECONDARY)) PSW32_ASC_SECONDARY))
pr_info("Execute protection active, " pr_info("Execute protection active, "
...@@ -381,7 +385,7 @@ static void setup_addressing_mode(void) ...@@ -381,7 +385,7 @@ static void setup_addressing_mode(void)
else else
pr_info("Execute protection active, " pr_info("Execute protection active, "
"mvcos not available\n"); "mvcos not available\n");
} else if (switch_amode) { } 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");
...@@ -411,7 +415,7 @@ setup_lowcore(void) ...@@ -411,7 +415,7 @@ setup_lowcore(void)
lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY; lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
lc->restart_psw.addr = lc->restart_psw.addr =
PSW_ADDR_AMODE | (unsigned long) restart_int_handler; PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
if (switch_amode) if (user_mode != HOME_SPACE_MODE)
lc->restart_psw.mask |= PSW_ASC_HOME; lc->restart_psw.mask |= PSW_ASC_HOME;
lc->external_new_psw.mask = psw_kernel_bits; lc->external_new_psw.mask = psw_kernel_bits;
lc->external_new_psw.addr = lc->external_new_psw.addr =
......
...@@ -335,7 +335,7 @@ int get_sync_clock(unsigned long long *clock) ...@@ -335,7 +335,7 @@ int get_sync_clock(unsigned long long *clock)
sw0 = atomic_read(sw_ptr); sw0 = atomic_read(sw_ptr);
*clock = get_clock(); *clock = get_clock();
sw1 = atomic_read(sw_ptr); sw1 = atomic_read(sw_ptr);
put_cpu_var(clock_sync_sync); put_cpu_var(clock_sync_word);
if (sw0 == sw1 && (sw0 & 0x80000000U)) if (sw0 == sw1 && (sw0 & 0x80000000U))
/* Success: time is in sync. */ /* Success: time is in sync. */
return 0; return 0;
...@@ -385,7 +385,7 @@ static inline int check_sync_clock(void) ...@@ -385,7 +385,7 @@ static inline int check_sync_clock(void)
sw_ptr = &get_cpu_var(clock_sync_word); sw_ptr = &get_cpu_var(clock_sync_word);
rc = (atomic_read(sw_ptr) & 0x80000000U) != 0; rc = (atomic_read(sw_ptr) & 0x80000000U) != 0;
put_cpu_var(clock_sync_sync); put_cpu_var(clock_sync_word);
return rc; return rc;
} }
......
...@@ -86,7 +86,8 @@ static void vdso_init_data(struct vdso_data *vd) ...@@ -86,7 +86,8 @@ static void vdso_init_data(struct vdso_data *vd)
unsigned int facility_list; unsigned int facility_list;
facility_list = stfl(); facility_list = stfl();
vd->ectg_available = switch_amode && (facility_list & 1); vd->ectg_available =
user_mode != HOME_SPACE_MODE && (facility_list & 1);
} }
#ifdef CONFIG_64BIT #ifdef CONFIG_64BIT
...@@ -114,7 +115,7 @@ int vdso_alloc_per_cpu(int cpu, struct _lowcore *lowcore) ...@@ -114,7 +115,7 @@ int vdso_alloc_per_cpu(int cpu, struct _lowcore *lowcore)
lowcore->vdso_per_cpu_data = __LC_PASTE; lowcore->vdso_per_cpu_data = __LC_PASTE;
if (!switch_amode || !vdso_enabled) if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
return 0; return 0;
segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER); segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER);
...@@ -160,7 +161,7 @@ void vdso_free_per_cpu(int cpu, struct _lowcore *lowcore) ...@@ -160,7 +161,7 @@ void vdso_free_per_cpu(int cpu, struct _lowcore *lowcore)
unsigned long segment_table, page_table, page_frame; unsigned long segment_table, page_table, page_frame;
u32 *psal, *aste; u32 *psal, *aste;
if (!switch_amode || !vdso_enabled) if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
return; return;
psal = (u32 *)(addr_t) lowcore->paste[4]; psal = (u32 *)(addr_t) lowcore->paste[4];
...@@ -184,7 +185,7 @@ static void __vdso_init_cr5(void *dummy) ...@@ -184,7 +185,7 @@ static void __vdso_init_cr5(void *dummy)
static void vdso_init_cr5(void) static void vdso_init_cr5(void)
{ {
if (switch_amode && vdso_enabled) if (user_mode != HOME_SPACE_MODE && vdso_enabled)
on_each_cpu(__vdso_init_cr5, NULL, 1); on_each_cpu(__vdso_init_cr5, NULL, 1);
} }
#endif /* CONFIG_64BIT */ #endif /* CONFIG_64BIT */
......
...@@ -20,7 +20,6 @@ config KVM ...@@ -20,7 +20,6 @@ config KVM
depends on HAVE_KVM && EXPERIMENTAL depends on HAVE_KVM && EXPERIMENTAL
select PREEMPT_NOTIFIERS select PREEMPT_NOTIFIERS
select ANON_INODES select ANON_INODES
select S390_SWITCH_AMODE
---help--- ---help---
Support hosting paravirtualized guest machines using the SIE Support hosting paravirtualized guest machines using the SIE
virtualization capability on the mainframe. This should work virtualization capability on the mainframe. This should work
......
...@@ -162,7 +162,6 @@ static size_t clear_user_mvcos(size_t size, void __user *to) ...@@ -162,7 +162,6 @@ static size_t clear_user_mvcos(size_t size, void __user *to)
return size; return size;
} }
#ifdef CONFIG_S390_SWITCH_AMODE
static size_t strnlen_user_mvcos(size_t count, const char __user *src) static size_t strnlen_user_mvcos(size_t count, const char __user *src)
{ {
char buf[256]; char buf[256];
...@@ -200,7 +199,6 @@ static size_t strncpy_from_user_mvcos(size_t count, const char __user *src, ...@@ -200,7 +199,6 @@ static size_t strncpy_from_user_mvcos(size_t count, const char __user *src,
} while ((len_str == len) && (done < count)); } while ((len_str == len) && (done < count));
return done; return done;
} }
#endif /* CONFIG_S390_SWITCH_AMODE */
struct uaccess_ops uaccess_mvcos = { struct uaccess_ops uaccess_mvcos = {
.copy_from_user = copy_from_user_mvcos_check, .copy_from_user = copy_from_user_mvcos_check,
...@@ -215,7 +213,6 @@ struct uaccess_ops uaccess_mvcos = { ...@@ -215,7 +213,6 @@ struct uaccess_ops uaccess_mvcos = {
.futex_atomic_cmpxchg = futex_atomic_cmpxchg_std, .futex_atomic_cmpxchg = futex_atomic_cmpxchg_std,
}; };
#ifdef CONFIG_S390_SWITCH_AMODE
struct uaccess_ops uaccess_mvcos_switch = { struct uaccess_ops uaccess_mvcos_switch = {
.copy_from_user = copy_from_user_mvcos, .copy_from_user = copy_from_user_mvcos,
.copy_from_user_small = copy_from_user_mvcos, .copy_from_user_small = copy_from_user_mvcos,
...@@ -228,4 +225,3 @@ struct uaccess_ops uaccess_mvcos_switch = { ...@@ -228,4 +225,3 @@ struct uaccess_ops uaccess_mvcos_switch = {
.futex_atomic_op = futex_atomic_op_pt, .futex_atomic_op = futex_atomic_op_pt,
.futex_atomic_cmpxchg = futex_atomic_cmpxchg_pt, .futex_atomic_cmpxchg = futex_atomic_cmpxchg_pt,
}; };
#endif
...@@ -23,85 +23,20 @@ static inline pte_t *follow_table(struct mm_struct *mm, unsigned long addr) ...@@ -23,85 +23,20 @@ static inline pte_t *follow_table(struct mm_struct *mm, unsigned long addr)
pgd = pgd_offset(mm, addr); pgd = pgd_offset(mm, addr);
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd))) if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
return NULL; return (pte_t *) 0x3a;
pud = pud_offset(pgd, addr); pud = pud_offset(pgd, addr);
if (pud_none(*pud) || unlikely(pud_bad(*pud))) if (pud_none(*pud) || unlikely(pud_bad(*pud)))
return NULL; return (pte_t *) 0x3b;
pmd = pmd_offset(pud, addr); pmd = pmd_offset(pud, addr);
if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd))) if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
return NULL; return (pte_t *) 0x10;
return pte_offset_map(pmd, addr); return pte_offset_map(pmd, addr);
} }
static int __handle_fault(struct mm_struct *mm, unsigned long address, static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
int write_access)
{
struct vm_area_struct *vma;
int ret = -EFAULT;
int fault;
if (in_atomic())
return ret;
down_read(&mm->mmap_sem);
vma = find_vma(mm, address);
if (unlikely(!vma))
goto out;
if (unlikely(vma->vm_start > address)) {
if (!(vma->vm_flags & VM_GROWSDOWN))
goto out;
if (expand_stack(vma, address))
goto out;
}
if (!write_access) {
/* page not present, check vm flags */
if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
goto out;
} else {
if (!(vma->vm_flags & VM_WRITE))
goto out;
}
survive:
fault = handle_mm_fault(mm, vma, address, write_access ? FAULT_FLAG_WRITE : 0);
if (unlikely(fault & VM_FAULT_ERROR)) {
if (fault & VM_FAULT_OOM)
goto out_of_memory;
else if (fault & VM_FAULT_SIGBUS)
goto out_sigbus;
BUG();
}
if (fault & VM_FAULT_MAJOR)
current->maj_flt++;
else
current->min_flt++;
ret = 0;
out:
up_read(&mm->mmap_sem);
return ret;
out_of_memory:
up_read(&mm->mmap_sem);
if (is_global_init(current)) {
yield();
down_read(&mm->mmap_sem);
goto survive;
}
printk("VM: killing process %s\n", current->comm);
return ret;
out_sigbus:
up_read(&mm->mmap_sem);
current->thread.prot_addr = address;
current->thread.trap_no = 0x11;
force_sig(SIGBUS, current);
return ret;
}
static size_t __user_copy_pt(unsigned long uaddr, void *kptr,
size_t n, int write_user) size_t n, int write_user)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
...@@ -114,12 +49,17 @@ static size_t __user_copy_pt(unsigned long uaddr, void *kptr, ...@@ -114,12 +49,17 @@ static size_t __user_copy_pt(unsigned long uaddr, void *kptr,
spin_lock(&mm->page_table_lock); spin_lock(&mm->page_table_lock);
do { do {
pte = follow_table(mm, uaddr); pte = follow_table(mm, uaddr);
if (!pte || !pte_present(*pte) || if ((unsigned long) pte < 0x1000)
(write_user && !pte_write(*pte))) goto fault;
if (!pte_present(*pte)) {
pte = (pte_t *) 0x11;
goto fault;
} else if (write_user && !pte_write(*pte)) {
pte = (pte_t *) 0x04;
goto fault; goto fault;
}
pfn = pte_pfn(*pte); pfn = pte_pfn(*pte);
offset = uaddr & (PAGE_SIZE - 1); offset = uaddr & (PAGE_SIZE - 1);
size = min(n - done, PAGE_SIZE - offset); size = min(n - done, PAGE_SIZE - offset);
if (write_user) { if (write_user) {
...@@ -137,7 +77,7 @@ static size_t __user_copy_pt(unsigned long uaddr, void *kptr, ...@@ -137,7 +77,7 @@ static size_t __user_copy_pt(unsigned long uaddr, void *kptr,
return n - done; return n - done;
fault: fault:
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
if (__handle_fault(mm, uaddr, write_user)) if (__handle_fault(uaddr, (unsigned long) pte, write_user))
return n - done; return n - done;
goto retry; goto retry;
} }
...@@ -146,30 +86,31 @@ static size_t __user_copy_pt(unsigned long uaddr, void *kptr, ...@@ -146,30 +86,31 @@ static size_t __user_copy_pt(unsigned long uaddr, void *kptr,
* Do DAT for user address by page table walk, return kernel address. * Do DAT for user address by page table walk, return kernel address.
* This function needs to be called with current->mm->page_table_lock held. * This function needs to be called with current->mm->page_table_lock held.
*/ */
static unsigned long __dat_user_addr(unsigned long uaddr) static __always_inline unsigned long __dat_user_addr(unsigned long uaddr)
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
unsigned long pfn, ret; unsigned long pfn;
pte_t *pte; pte_t *pte;
int rc; int rc;
ret = 0;
retry: retry:
pte = follow_table(mm, uaddr); pte = follow_table(mm, uaddr);
if (!pte || !pte_present(*pte)) if ((unsigned long) pte < 0x1000)
goto fault;
if (!pte_present(*pte)) {
pte = (pte_t *) 0x11;
goto fault; goto fault;
}
pfn = pte_pfn(*pte); pfn = pte_pfn(*pte);
ret = (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1)); return (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
out:
return ret;
fault: fault:
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
rc = __handle_fault(mm, uaddr, 0); rc = __handle_fault(uaddr, (unsigned long) pte, 0);
spin_lock(&mm->page_table_lock); spin_lock(&mm->page_table_lock);
if (rc) if (!rc)
goto out;
goto retry; goto retry;
return 0;
} }
size_t copy_from_user_pt(size_t n, const void __user *from, void *to) size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
...@@ -234,8 +175,12 @@ static size_t strnlen_user_pt(size_t count, const char __user *src) ...@@ -234,8 +175,12 @@ static size_t strnlen_user_pt(size_t count, const char __user *src)
spin_lock(&mm->page_table_lock); spin_lock(&mm->page_table_lock);
do { do {
pte = follow_table(mm, uaddr); pte = follow_table(mm, uaddr);
if (!pte || !pte_present(*pte)) if ((unsigned long) pte < 0x1000)
goto fault;
if (!pte_present(*pte)) {
pte = (pte_t *) 0x11;
goto fault; goto fault;
}
pfn = pte_pfn(*pte); pfn = pte_pfn(*pte);
offset = uaddr & (PAGE_SIZE-1); offset = uaddr & (PAGE_SIZE-1);
...@@ -249,9 +194,8 @@ static size_t strnlen_user_pt(size_t count, const char __user *src) ...@@ -249,9 +194,8 @@ static size_t strnlen_user_pt(size_t count, const char __user *src)
return done + 1; return done + 1;
fault: fault:
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
if (__handle_fault(mm, uaddr, 0)) { if (__handle_fault(uaddr, (unsigned long) pte, 0))
return 0; return 0;
}
goto retry; goto retry;
} }
...@@ -284,7 +228,7 @@ static size_t copy_in_user_pt(size_t n, void __user *to, ...@@ -284,7 +228,7 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
{ {
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
unsigned long offset_from, offset_to, offset_max, pfn_from, pfn_to, unsigned long offset_from, offset_to, offset_max, pfn_from, pfn_to,
uaddr, done, size; uaddr, done, size, error_code;
unsigned long uaddr_from = (unsigned long) from; unsigned long uaddr_from = (unsigned long) from;
unsigned long uaddr_to = (unsigned long) to; unsigned long uaddr_to = (unsigned long) to;
pte_t *pte_from, *pte_to; pte_t *pte_from, *pte_to;
...@@ -298,17 +242,28 @@ static size_t copy_in_user_pt(size_t n, void __user *to, ...@@ -298,17 +242,28 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
retry: retry:
spin_lock(&mm->page_table_lock); spin_lock(&mm->page_table_lock);
do { do {
pte_from = follow_table(mm, uaddr_from);
if (!pte_from || !pte_present(*pte_from)) {
uaddr = uaddr_from;
write_user = 0; write_user = 0;
uaddr = uaddr_from;
pte_from = follow_table(mm, uaddr_from);
error_code = (unsigned long) pte_from;
if (error_code < 0x1000)
goto fault;
if (!pte_present(*pte_from)) {
error_code = 0x11;
goto fault; goto fault;
} }
pte_to = follow_table(mm, uaddr_to);
if (!pte_to || !pte_present(*pte_to) || !pte_write(*pte_to)) {
uaddr = uaddr_to;
write_user = 1; write_user = 1;
uaddr = uaddr_to;
pte_to = follow_table(mm, uaddr_to);
error_code = (unsigned long) pte_to;
if (error_code < 0x1000)
goto fault;
if (!pte_present(*pte_to)) {
error_code = 0x11;
goto fault;
} else if (!pte_write(*pte_to)) {
error_code = 0x04;
goto fault; goto fault;
} }
...@@ -329,7 +284,7 @@ static size_t copy_in_user_pt(size_t n, void __user *to, ...@@ -329,7 +284,7 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
return n - done; return n - done;
fault: fault:
spin_unlock(&mm->page_table_lock); spin_unlock(&mm->page_table_lock);
if (__handle_fault(mm, uaddr, write_user)) if (__handle_fault(uaddr, error_code, write_user))
return n - done; return n - done;
goto retry; goto retry;
} }
......
...@@ -18,6 +18,7 @@ ...@@ -18,6 +18,7 @@
#include <linux/swap.h> #include <linux/swap.h>
#include <linux/kthread.h> #include <linux/kthread.h>
#include <linux/oom.h> #include <linux/oom.h>
#include <linux/suspend.h>
#include <asm/pgalloc.h> #include <asm/pgalloc.h>
#include <asm/uaccess.h> #include <asm/uaccess.h>
...@@ -44,6 +45,7 @@ static volatile long cmm_pages_target; ...@@ -44,6 +45,7 @@ static volatile long cmm_pages_target;
static volatile long cmm_timed_pages_target; static volatile long cmm_timed_pages_target;
static long cmm_timeout_pages; static long cmm_timeout_pages;
static long cmm_timeout_seconds; static long cmm_timeout_seconds;
static int cmm_suspended;
static struct cmm_page_array *cmm_page_list; static struct cmm_page_array *cmm_page_list;
static struct cmm_page_array *cmm_timed_page_list; static struct cmm_page_array *cmm_timed_page_list;
...@@ -147,9 +149,9 @@ cmm_thread(void *dummy) ...@@ -147,9 +149,9 @@ cmm_thread(void *dummy)
while (1) { while (1) {
rc = wait_event_interruptible(cmm_thread_wait, rc = wait_event_interruptible(cmm_thread_wait,
(cmm_pages != cmm_pages_target || (!cmm_suspended && (cmm_pages != cmm_pages_target ||
cmm_timed_pages != cmm_timed_pages_target || cmm_timed_pages != cmm_timed_pages_target)) ||
kthread_should_stop())); kthread_should_stop());
if (kthread_should_stop() || rc == -ERESTARTSYS) { if (kthread_should_stop() || rc == -ERESTARTSYS) {
cmm_pages_target = cmm_pages; cmm_pages_target = cmm_pages;
cmm_timed_pages_target = cmm_timed_pages; cmm_timed_pages_target = cmm_timed_pages;
...@@ -410,6 +412,38 @@ cmm_smsg_target(char *from, char *msg) ...@@ -410,6 +412,38 @@ cmm_smsg_target(char *from, char *msg)
static struct ctl_table_header *cmm_sysctl_header; static struct ctl_table_header *cmm_sysctl_header;
static int cmm_suspend(void)
{
cmm_suspended = 1;
cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
return 0;
}
static int cmm_resume(void)
{
cmm_suspended = 0;
cmm_kick_thread();
return 0;
}
static int cmm_power_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
switch (event) {
case PM_POST_HIBERNATION:
return cmm_resume();
case PM_HIBERNATION_PREPARE:
return cmm_suspend();
default:
return NOTIFY_DONE;
}
}
static struct notifier_block cmm_power_notifier = {
.notifier_call = cmm_power_event,
};
static int static int
cmm_init (void) cmm_init (void)
{ {
...@@ -418,7 +452,7 @@ cmm_init (void) ...@@ -418,7 +452,7 @@ cmm_init (void)
#ifdef CONFIG_CMM_PROC #ifdef CONFIG_CMM_PROC
cmm_sysctl_header = register_sysctl_table(cmm_dir_table); cmm_sysctl_header = register_sysctl_table(cmm_dir_table);
if (!cmm_sysctl_header) if (!cmm_sysctl_header)
goto out; goto out_sysctl;
#endif #endif
#ifdef CONFIG_CMM_IUCV #ifdef CONFIG_CMM_IUCV
rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target); rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
...@@ -428,17 +462,21 @@ cmm_init (void) ...@@ -428,17 +462,21 @@ cmm_init (void)
rc = register_oom_notifier(&cmm_oom_nb); rc = register_oom_notifier(&cmm_oom_nb);
if (rc < 0) if (rc < 0)
goto out_oom_notify; goto out_oom_notify;
rc = register_pm_notifier(&cmm_power_notifier);
if (rc)
goto out_pm;
init_waitqueue_head(&cmm_thread_wait); init_waitqueue_head(&cmm_thread_wait);
init_timer(&cmm_timer); init_timer(&cmm_timer);
cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread"); cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0; rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0;
if (!rc) if (rc)
goto out; goto out_kthread;
/* return 0;
* kthread_create failed. undo all the stuff from above again.
*/
unregister_oom_notifier(&cmm_oom_nb);
out_kthread:
unregister_pm_notifier(&cmm_power_notifier);
out_pm:
unregister_oom_notifier(&cmm_oom_nb);
out_oom_notify: out_oom_notify:
#ifdef CONFIG_CMM_IUCV #ifdef CONFIG_CMM_IUCV
smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target); smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
...@@ -446,8 +484,8 @@ cmm_init (void) ...@@ -446,8 +484,8 @@ cmm_init (void)
#endif #endif
#ifdef CONFIG_CMM_PROC #ifdef CONFIG_CMM_PROC
unregister_sysctl_table(cmm_sysctl_header); unregister_sysctl_table(cmm_sysctl_header);
out_sysctl:
#endif #endif
out:
return rc; return rc;
} }
...@@ -455,6 +493,7 @@ static void ...@@ -455,6 +493,7 @@ static void
cmm_exit(void) cmm_exit(void)
{ {
kthread_stop(cmm_thread_ptr); kthread_stop(cmm_thread_ptr);
unregister_pm_notifier(&cmm_power_notifier);
unregister_oom_notifier(&cmm_oom_nb); unregister_oom_notifier(&cmm_oom_nb);
cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list); cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list); cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
......
This diff is collapsed.
...@@ -269,7 +269,7 @@ int s390_enable_sie(void) ...@@ -269,7 +269,7 @@ int s390_enable_sie(void)
struct mm_struct *mm, *old_mm; struct mm_struct *mm, *old_mm;
/* Do we have switched amode? If no, we cannot do sie */ /* Do we have switched amode? If no, we cannot do sie */
if (!switch_amode) if (user_mode == HOME_SPACE_MODE)
return -EINVAL; return -EINVAL;
/* Do we have pgstes? if yes, we are done */ /* Do we have pgstes? if yes, we are done */
......
...@@ -70,6 +70,10 @@ static pte_t __ref *vmem_pte_alloc(void) ...@@ -70,6 +70,10 @@ static pte_t __ref *vmem_pte_alloc(void)
pte = alloc_bootmem(PTRS_PER_PTE * sizeof(pte_t)); pte = alloc_bootmem(PTRS_PER_PTE * sizeof(pte_t));
if (!pte) if (!pte)
return NULL; return NULL;
if (MACHINE_HAS_HPAGE)
clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY | _PAGE_CO,
PTRS_PER_PTE * sizeof(pte_t));
else
clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY, clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY,
PTRS_PER_PTE * sizeof(pte_t)); PTRS_PER_PTE * sizeof(pte_t));
return pte; return pte;
...@@ -112,7 +116,8 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro) ...@@ -112,7 +116,8 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) && if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
(address + HPAGE_SIZE <= start + size) && (address + HPAGE_SIZE <= start + size) &&
(address >= HPAGE_SIZE)) { (address >= HPAGE_SIZE)) {
pte_val(pte) |= _SEGMENT_ENTRY_LARGE; pte_val(pte) |= _SEGMENT_ENTRY_LARGE |
_SEGMENT_ENTRY_CO;
pmd_val(*pm_dir) = pte_val(pte); pmd_val(*pm_dir) = pte_val(pte);
address += HPAGE_SIZE - PAGE_SIZE; address += HPAGE_SIZE - PAGE_SIZE;
continue; continue;
......
This diff is collapsed.
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include <linux/timer.h> #include <linux/timer.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <asm/idals.h> #include <asm/idals.h>
#include <asm/todclk.h>
#define PRINTK_HEADER "dasd_erp(3990): " #define PRINTK_HEADER "dasd_erp(3990): "
...@@ -70,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status) ...@@ -70,8 +69,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
* processing until the started timer has expired or an related * processing until the started timer has expired or an related
* interrupt was received. * interrupt was received.
*/ */
static void static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires)
dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
{ {
struct dasd_device *device = erp->startdev; struct dasd_device *device = erp->startdev;
...@@ -81,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) ...@@ -81,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
"blocking request queue for %is", expires/HZ); "blocking request queue for %is", expires/HZ);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped |= DASD_STOPPED_PENDING; dasd_device_set_stop_bits(device, DASD_STOPPED_PENDING);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
erp->status = DASD_CQR_FILLED; erp->status = DASD_CQR_FILLED;
dasd_block_set_timer(device->block, expires); if (erp->block)
dasd_block_set_timer(erp->block, expires);
else
dasd_device_set_timer(device, expires);
} }
/* /*
...@@ -243,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) ...@@ -243,9 +244,13 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
* DESCRIPTION * DESCRIPTION
* Setup ERP to do the ERP action 1 (see Reference manual). * Setup ERP to do the ERP action 1 (see Reference manual).
* Repeat the operation on a different channel path. * Repeat the operation on a different channel path.
* If all alternate paths have been tried, the request is posted with a * As deviation from the recommended recovery action, we reset the path mask
* permanent error. * after we have tried each path and go through all paths a second time.
* Note: duplex handling is not implemented (yet). * This will cover situations where only one path at a time is actually down,
* but all paths fail and recover just with the same sequence and timing as
* we try to use them (flapping links).
* If all alternate paths have been tried twice, the request is posted with
* a permanent error.
* *
* PARAMETER * PARAMETER
* erp pointer to the current ERP * erp pointer to the current ERP
...@@ -254,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) ...@@ -254,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
* erp pointer to the ERP * erp pointer to the ERP
* *
*/ */
static struct dasd_ccw_req * static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp)
dasd_3990_erp_action_1(struct dasd_ccw_req * erp)
{ {
erp->function = dasd_3990_erp_action_1_sec;
dasd_3990_erp_alternate_path(erp);
return erp;
}
static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
{
erp->function = dasd_3990_erp_action_1; erp->function = dasd_3990_erp_action_1;
dasd_3990_erp_alternate_path(erp); dasd_3990_erp_alternate_path(erp);
if (erp->status == DASD_CQR_FAILED) {
erp->status = DASD_CQR_FILLED;
erp->retries = 10;
erp->lpm = LPM_ANYPATH;
erp->function = dasd_3990_erp_action_1_sec;
}
return erp; return erp;
} /* end dasd_3990_erp_action_1(b) */
} /* end dasd_3990_erp_action_1 */
/* /*
* DASD_3990_ERP_ACTION_4 * DASD_3990_ERP_ACTION_4
...@@ -2295,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) ...@@ -2295,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
return cqr; return cqr;
} }
ccw = cqr->cpaddr;
if (cqr->cpmode == 1) { if (cqr->cpmode == 1) {
/* make a shallow copy of the original tcw but set new tsb */ /* make a shallow copy of the original tcw but set new tsb */
erp->cpmode = 1; erp->cpmode = 1;
...@@ -2303,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr) ...@@ -2303,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
tsb = (struct tsb *) &tcw[1]; tsb = (struct tsb *) &tcw[1];
*tcw = *((struct tcw *)cqr->cpaddr); *tcw = *((struct tcw *)cqr->cpaddr);
tcw->tsb = (long)tsb; tcw->tsb = (long)tsb;
} else if (ccw->cmd_code == DASD_ECKD_CCW_PSF) {
/* PSF cannot be chained from NOOP/TIC */
erp->cpaddr = cqr->cpaddr;
} else { } else {
/* initialize request with default TIC to current ERP/CQR */ /* initialize request with default TIC to current ERP/CQR */
ccw = erp->cpaddr; ccw = erp->cpaddr;
...@@ -2487,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) ...@@ -2487,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
erp = dasd_3990_erp_action_1(erp); erp = dasd_3990_erp_action_1(erp);
} else if (erp->function == dasd_3990_erp_action_1_sec) {
erp = dasd_3990_erp_action_1_sec(erp);
} else if (erp->function == dasd_3990_erp_action_5) { } else if (erp->function == dasd_3990_erp_action_5) {
/* retries have not been successful */ /* retries have not been successful */
......
...@@ -152,6 +152,7 @@ static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid) ...@@ -152,6 +152,7 @@ static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work); INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work);
INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work); INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
spin_lock_init(&lcu->lock); spin_lock_init(&lcu->lock);
init_completion(&lcu->lcu_setup);
return lcu; return lcu;
out_err4: out_err4:
...@@ -239,6 +240,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device) ...@@ -239,6 +240,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
return is_lcu_known; return is_lcu_known;
} }
/*
* The first device to be registered on an LCU will have to do
* some additional setup steps to configure that LCU on the
* storage server. All further devices should wait with their
* initialization until the first device is done.
* To synchronize this work, the first device will call
* dasd_alias_lcu_setup_complete when it is done, and all
* other devices will wait for it with dasd_alias_wait_for_lcu_setup.
*/
void dasd_alias_lcu_setup_complete(struct dasd_device *device)
{
struct dasd_eckd_private *private;
unsigned long flags;
struct alias_server *server;
struct alias_lcu *lcu;
struct dasd_uid *uid;
private = (struct dasd_eckd_private *) device->private;
uid = &private->uid;
lcu = NULL;
spin_lock_irqsave(&aliastree.lock, flags);
server = _find_server(uid);
if (server)
lcu = _find_lcu(server, uid);
spin_unlock_irqrestore(&aliastree.lock, flags);
if (!lcu) {
DBF_EVENT_DEVID(DBF_ERR, device->cdev,
"could not find lcu for %04x %02x",
uid->ssid, uid->real_unit_addr);
WARN_ON(1);
return;
}
complete_all(&lcu->lcu_setup);
}
void dasd_alias_wait_for_lcu_setup(struct dasd_device *device)
{
struct dasd_eckd_private *private;
unsigned long flags;
struct alias_server *server;
struct alias_lcu *lcu;
struct dasd_uid *uid;
private = (struct dasd_eckd_private *) device->private;
uid = &private->uid;
lcu = NULL;
spin_lock_irqsave(&aliastree.lock, flags);
server = _find_server(uid);
if (server)
lcu = _find_lcu(server, uid);
spin_unlock_irqrestore(&aliastree.lock, flags);
if (!lcu) {
DBF_EVENT_DEVID(DBF_ERR, device->cdev,
"could not find lcu for %04x %02x",
uid->ssid, uid->real_unit_addr);
WARN_ON(1);
return;
}
wait_for_completion(&lcu->lcu_setup);
}
/* /*
* This function removes a device from the scope of alias management. * This function removes a device from the scope of alias management.
* The complicated part is to make sure that it is not in use by * The complicated part is to make sure that it is not in use by
...@@ -755,11 +817,11 @@ static void __stop_device_on_lcu(struct dasd_device *device, ...@@ -755,11 +817,11 @@ static void __stop_device_on_lcu(struct dasd_device *device,
{ {
/* If pos == device then device is already locked! */ /* If pos == device then device is already locked! */
if (pos == device) { if (pos == device) {
pos->stopped |= DASD_STOPPED_SU; dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
return; return;
} }
spin_lock(get_ccwdev_lock(pos->cdev)); spin_lock(get_ccwdev_lock(pos->cdev));
pos->stopped |= DASD_STOPPED_SU; dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
spin_unlock(get_ccwdev_lock(pos->cdev)); spin_unlock(get_ccwdev_lock(pos->cdev));
} }
...@@ -793,26 +855,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu) ...@@ -793,26 +855,26 @@ static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
list_for_each_entry(device, &lcu->active_devices, alias_list) { list_for_each_entry(device, &lcu->active_devices, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
list_for_each_entry(device, &lcu->inactive_devices, alias_list) { list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
} }
list_for_each_entry(pavgroup, &lcu->grouplist, group) { list_for_each_entry(pavgroup, &lcu->grouplist, group) {
list_for_each_entry(device, &pavgroup->baselist, alias_list) { list_for_each_entry(device, &pavgroup->baselist, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
flags); flags);
} }
list_for_each_entry(device, &pavgroup->aliaslist, alias_list) { list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_SU; dasd_device_remove_stop_bits(device, DASD_STOPPED_SU);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
flags); flags);
} }
...@@ -836,7 +898,8 @@ static void summary_unit_check_handling_work(struct work_struct *work) ...@@ -836,7 +898,8 @@ static void summary_unit_check_handling_work(struct work_struct *work)
/* 2. reset summary unit check */ /* 2. reset summary unit check */
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING); dasd_device_remove_stop_bits(device,
(DASD_STOPPED_SU | DASD_STOPPED_PENDING));
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
reset_summary_unit_check(lcu, device, suc_data->reason); reset_summary_unit_check(lcu, device, suc_data->reason);
......
...@@ -24,7 +24,6 @@ ...@@ -24,7 +24,6 @@
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/s390_ext.h> #include <asm/s390_ext.h>
#include <asm/todclk.h>
#include <asm/vtoc.h> #include <asm/vtoc.h>
#include <asm/diag.h> #include <asm/diag.h>
...@@ -145,6 +144,15 @@ dasd_diag_erp(struct dasd_device *device) ...@@ -145,6 +144,15 @@ dasd_diag_erp(struct dasd_device *device)
mdsk_term_io(device); mdsk_term_io(device);
rc = mdsk_init_io(device, device->block->bp_block, 0, NULL); rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
if (rc == 4) {
if (!(device->features & DASD_FEATURE_READONLY)) {
dev_warn(&device->cdev->dev,
"The access mode of a DIAG device changed"
" to read-only");
device->features |= DASD_FEATURE_READONLY;
}
rc = 0;
}
if (rc) if (rc)
dev_warn(&device->cdev->dev, "DIAG ERP failed with " dev_warn(&device->cdev->dev, "DIAG ERP failed with "
"rc=%d\n", rc); "rc=%d\n", rc);
...@@ -433,16 +441,20 @@ dasd_diag_check_device(struct dasd_device *device) ...@@ -433,16 +441,20 @@ dasd_diag_check_device(struct dasd_device *device)
for (sb = 512; sb < bsize; sb = sb << 1) for (sb = 512; sb < bsize; sb = sb << 1)
block->s2b_shift++; block->s2b_shift++;
rc = mdsk_init_io(device, block->bp_block, 0, NULL); rc = mdsk_init_io(device, block->bp_block, 0, NULL);
if (rc) { if (rc && (rc != 4)) {
dev_warn(&device->cdev->dev, "DIAG initialization " dev_warn(&device->cdev->dev, "DIAG initialization "
"failed with rc=%d\n", rc); "failed with rc=%d\n", rc);
rc = -EIO; rc = -EIO;
} else { } else {
if (rc == 4)
device->features |= DASD_FEATURE_READONLY;
dev_info(&device->cdev->dev, dev_info(&device->cdev->dev,
"New DASD with %ld byte/block, total size %ld KB\n", "New DASD with %ld byte/block, total size %ld KB%s\n",
(unsigned long) block->bp_block, (unsigned long) block->bp_block,
(unsigned long) (block->blocks << (unsigned long) (block->blocks <<
block->s2b_shift) >> 1); block->s2b_shift) >> 1,
(rc == 4) ? ", read-only device" : "");
rc = 0;
} }
out_label: out_label:
free_page((long) label); free_page((long) label);
......
This diff is collapsed.
...@@ -414,6 +414,7 @@ struct alias_lcu { ...@@ -414,6 +414,7 @@ struct alias_lcu {
struct summary_unit_check_work_data suc_data; struct summary_unit_check_work_data suc_data;
struct read_uac_work_data ruac_data; struct read_uac_work_data ruac_data;
struct dasd_ccw_req *rsu_cqr; struct dasd_ccw_req *rsu_cqr;
struct completion lcu_setup;
}; };
struct alias_pav_group { struct alias_pav_group {
...@@ -460,5 +461,6 @@ int dasd_alias_remove_device(struct dasd_device *); ...@@ -460,5 +461,6 @@ int dasd_alias_remove_device(struct dasd_device *);
struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *); struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *);
void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *); void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
void dasd_alias_lcu_setup_complete(struct dasd_device *);
void dasd_alias_wait_for_lcu_setup(struct dasd_device *);
#endif /* DASD_ECKD_H */ #endif /* DASD_ECKD_H */
...@@ -536,7 +536,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) ...@@ -536,7 +536,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL); eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);
if (!eerb) if (!eerb)
return -ENOMEM; return -ENOMEM;
lock_kernel();
eerb->buffer_page_count = eer_pages; eerb->buffer_page_count = eer_pages;
if (eerb->buffer_page_count < 1 || if (eerb->buffer_page_count < 1 ||
eerb->buffer_page_count > INT_MAX / PAGE_SIZE) { eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {
...@@ -544,7 +543,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) ...@@ -544,7 +543,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
DBF_EVENT(DBF_WARNING, "can't open device since module " DBF_EVENT(DBF_WARNING, "can't open device since module "
"parameter eer_pages is smaller than 1 or" "parameter eer_pages is smaller than 1 or"
" bigger than %d", (int)(INT_MAX / PAGE_SIZE)); " bigger than %d", (int)(INT_MAX / PAGE_SIZE));
unlock_kernel();
return -EINVAL; return -EINVAL;
} }
eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE; eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE;
...@@ -552,14 +550,12 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) ...@@ -552,14 +550,12 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
GFP_KERNEL); GFP_KERNEL);
if (!eerb->buffer) { if (!eerb->buffer) {
kfree(eerb); kfree(eerb);
unlock_kernel();
return -ENOMEM; return -ENOMEM;
} }
if (dasd_eer_allocate_buffer_pages(eerb->buffer, if (dasd_eer_allocate_buffer_pages(eerb->buffer,
eerb->buffer_page_count)) { eerb->buffer_page_count)) {
kfree(eerb->buffer); kfree(eerb->buffer);
kfree(eerb); kfree(eerb);
unlock_kernel();
return -ENOMEM; return -ENOMEM;
} }
filp->private_data = eerb; filp->private_data = eerb;
...@@ -567,7 +563,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp) ...@@ -567,7 +563,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
list_add(&eerb->list, &bufferlist); list_add(&eerb->list, &bufferlist);
spin_unlock_irqrestore(&bufferlock, flags); spin_unlock_irqrestore(&bufferlock, flags);
unlock_kernel();
return nonseekable_open(inp,filp); return nonseekable_open(inp,filp);
} }
......
...@@ -20,7 +20,6 @@ ...@@ -20,7 +20,6 @@
#include <asm/idals.h> #include <asm/idals.h>
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/todclk.h>
#include <asm/ccwdev.h> #include <asm/ccwdev.h>
#include "dasd_int.h" #include "dasd_int.h"
...@@ -141,9 +140,8 @@ dasd_fba_check_characteristics(struct dasd_device *device) ...@@ -141,9 +140,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
} }
block = dasd_alloc_block(); block = dasd_alloc_block();
if (IS_ERR(block)) { if (IS_ERR(block)) {
DBF_EVENT(DBF_WARNING, "could not allocate dasd block " DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate "
"structure for device: %s", "dasd block structure");
dev_name(&device->cdev->dev));
device->private = NULL; device->private = NULL;
kfree(private); kfree(private);
return PTR_ERR(block); return PTR_ERR(block);
...@@ -155,9 +153,8 @@ dasd_fba_check_characteristics(struct dasd_device *device) ...@@ -155,9 +153,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC, rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC,
&private->rdc_data, 32); &private->rdc_data, 32);
if (rc) { if (rc) {
DBF_EVENT(DBF_WARNING, "Read device characteristics returned " DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device "
"error %d for device: %s", "characteristics returned error %d", rc);
rc, dev_name(&device->cdev->dev));
device->block = NULL; device->block = NULL;
dasd_free_block(block); dasd_free_block(block);
device->private = NULL; device->private = NULL;
......
...@@ -108,6 +108,16 @@ do { \ ...@@ -108,6 +108,16 @@ do { \
d_data); \ d_data); \
} while(0) } while(0)
#define DBF_EVENT_DEVID(d_level, d_cdev, d_str, d_data...) \
do { \
struct ccw_dev_id __dev_id; \
ccw_device_get_id(d_cdev, &__dev_id); \
debug_sprintf_event(dasd_debug_area, \
d_level, \
"0.%x.%04x " d_str "\n", \
__dev_id.ssid, __dev_id.devno, d_data); \
} while (0)
#define DBF_EXC(d_level, d_str, d_data...)\ #define DBF_EXC(d_level, d_str, d_data...)\
do { \ do { \
debug_sprintf_exception(dasd_debug_area, \ debug_sprintf_exception(dasd_debug_area, \
...@@ -595,6 +605,9 @@ int dasd_generic_restore_device(struct ccw_device *); ...@@ -595,6 +605,9 @@ int dasd_generic_restore_device(struct ccw_device *);
int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int);
char *dasd_get_sense(struct irb *); char *dasd_get_sense(struct irb *);
void dasd_device_set_stop_bits(struct dasd_device *, int);
void dasd_device_remove_stop_bits(struct dasd_device *, int);
/* externals in dasd_devmap.c */ /* externals in dasd_devmap.c */
extern int dasd_max_devindex; extern int dasd_max_devindex;
extern int dasd_probeonly; extern int dasd_probeonly;
......
...@@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block) ...@@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block)
pr_info("%s: The DASD has been put in the quiesce " pr_info("%s: The DASD has been put in the quiesce "
"state\n", dev_name(&base->cdev->dev)); "state\n", dev_name(&base->cdev->dev));
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
base->stopped |= DASD_STOPPED_QUIESCE; dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
return 0; return 0;
} }
...@@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block) ...@@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block)
pr_info("%s: I/O operations have been resumed " pr_info("%s: I/O operations have been resumed "
"on the DASD\n", dev_name(&base->cdev->dev)); "on the DASD\n", dev_name(&base->cdev->dev));
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
base->stopped &= ~DASD_STOPPED_QUIESCE; dasd_device_remove_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
dasd_schedule_block_bh(block); dasd_schedule_block_bh(block);
......
...@@ -857,7 +857,6 @@ static struct console con3215 = { ...@@ -857,7 +857,6 @@ static struct console con3215 = {
/* /*
* 3215 console initialization code called from console_init(). * 3215 console initialization code called from console_init().
* NOTE: This is called before kmalloc is available.
*/ */
static int __init con3215_init(void) static int __init con3215_init(void)
{ {
......
...@@ -572,7 +572,6 @@ static struct console con3270 = { ...@@ -572,7 +572,6 @@ static struct console con3270 = {
/* /*
* 3270 console initialization code called from console_init(). * 3270 console initialization code called from console_init().
* NOTE: This is called before kmalloc is available.
*/ */
static int __init static int __init
con3270_init(void) con3270_init(void)
......
...@@ -38,6 +38,8 @@ struct fs3270 { ...@@ -38,6 +38,8 @@ struct fs3270 {
size_t rdbuf_size; /* size of data returned by RDBUF */ size_t rdbuf_size; /* size of data returned by RDBUF */
}; };
static DEFINE_MUTEX(fs3270_mutex);
static void static void
fs3270_wake_up(struct raw3270_request *rq, void *data) fs3270_wake_up(struct raw3270_request *rq, void *data)
{ {
...@@ -328,7 +330,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -328,7 +330,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
if (!fp) if (!fp)
return -ENODEV; return -ENODEV;
rc = 0; rc = 0;
lock_kernel(); mutex_lock(&fs3270_mutex);
switch (cmd) { switch (cmd) {
case TUBICMD: case TUBICMD:
fp->read_command = arg; fp->read_command = arg;
...@@ -354,7 +356,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ...@@ -354,7 +356,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
rc = -EFAULT; rc = -EFAULT;
break; break;
} }
unlock_kernel(); mutex_unlock(&fs3270_mutex);
return rc; return rc;
} }
...@@ -437,7 +439,7 @@ fs3270_open(struct inode *inode, struct file *filp) ...@@ -437,7 +439,7 @@ fs3270_open(struct inode *inode, struct file *filp)
minor = tty->index + RAW3270_FIRSTMINOR; minor = tty->index + RAW3270_FIRSTMINOR;
tty_kref_put(tty); tty_kref_put(tty);
} }
lock_kernel(); mutex_lock(&fs3270_mutex);
/* Check if some other program is already using fullscreen mode. */ /* Check if some other program is already using fullscreen mode. */
fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor); fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
if (!IS_ERR(fp)) { if (!IS_ERR(fp)) {
...@@ -478,7 +480,7 @@ fs3270_open(struct inode *inode, struct file *filp) ...@@ -478,7 +480,7 @@ fs3270_open(struct inode *inode, struct file *filp)
} }
filp->private_data = fp; filp->private_data = fp;
out: out:
unlock_kernel(); mutex_unlock(&fs3270_mutex);
return rc; return rc;
} }
......
...@@ -12,7 +12,6 @@ ...@@ -12,7 +12,6 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -283,7 +282,6 @@ static int mon_open(struct inode *inode, struct file *filp) ...@@ -283,7 +282,6 @@ static int mon_open(struct inode *inode, struct file *filp)
/* /*
* only one user allowed * only one user allowed
*/ */
lock_kernel();
rc = -EBUSY; rc = -EBUSY;
if (test_and_set_bit(MON_IN_USE, &mon_in_use)) if (test_and_set_bit(MON_IN_USE, &mon_in_use))
goto out; goto out;
...@@ -321,7 +319,6 @@ static int mon_open(struct inode *inode, struct file *filp) ...@@ -321,7 +319,6 @@ static int mon_open(struct inode *inode, struct file *filp)
} }
filp->private_data = monpriv; filp->private_data = monpriv;
dev_set_drvdata(monreader_device, monpriv); dev_set_drvdata(monreader_device, monpriv);
unlock_kernel();
return nonseekable_open(inode, filp); return nonseekable_open(inode, filp);
out_path: out_path:
...@@ -331,7 +328,6 @@ static int mon_open(struct inode *inode, struct file *filp) ...@@ -331,7 +328,6 @@ static int mon_open(struct inode *inode, struct file *filp)
out_use: out_use:
clear_bit(MON_IN_USE, &mon_in_use); clear_bit(MON_IN_USE, &mon_in_use);
out: out:
unlock_kernel();
return rc; return rc;
} }
...@@ -607,6 +603,10 @@ static int __init mon_init(void) ...@@ -607,6 +603,10 @@ static int __init mon_init(void)
} }
dcss_mkname(mon_dcss_name, &user_data_connect[8]); dcss_mkname(mon_dcss_name, &user_data_connect[8]);
/*
* misc_register() has to be the last action in module_init(), because
* file operations will be available right after this.
*/
rc = misc_register(&mon_dev); rc = misc_register(&mon_dev);
if (rc < 0 ) if (rc < 0 )
goto out; goto out;
......
...@@ -13,7 +13,6 @@ ...@@ -13,7 +13,6 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/smp_lock.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/miscdevice.h> #include <linux/miscdevice.h>
...@@ -185,13 +184,11 @@ static int monwrite_open(struct inode *inode, struct file *filp) ...@@ -185,13 +184,11 @@ static int monwrite_open(struct inode *inode, struct file *filp)
monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL); monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
if (!monpriv) if (!monpriv)
return -ENOMEM; return -ENOMEM;
lock_kernel();
INIT_LIST_HEAD(&monpriv->list); INIT_LIST_HEAD(&monpriv->list);
monpriv->hdr_to_read = sizeof(monpriv->hdr); monpriv->hdr_to_read = sizeof(monpriv->hdr);
mutex_init(&monpriv->thread_mutex); mutex_init(&monpriv->thread_mutex);
filp->private_data = monpriv; filp->private_data = monpriv;
list_add_tail(&monpriv->priv_list, &mon_priv_list); list_add_tail(&monpriv->priv_list, &mon_priv_list);
unlock_kernel();
return nonseekable_open(inode, filp); return nonseekable_open(inode, filp);
} }
...@@ -364,6 +361,10 @@ static int __init mon_init(void) ...@@ -364,6 +361,10 @@ static int __init mon_init(void)
goto out_driver; goto out_driver;
} }
/*
* misc_register() has to be the last action in module_init(), because
* file operations will be available right after this.
*/
rc = misc_register(&mon_dev); rc = misc_register(&mon_dev);
if (rc) if (rc)
goto out_device; goto out_device;
......
...@@ -84,6 +84,7 @@ static void __init sclp_read_info_early(void) ...@@ -84,6 +84,7 @@ static void __init sclp_read_info_early(void)
do { do {
memset(sccb, 0, sizeof(*sccb)); memset(sccb, 0, sizeof(*sccb));
sccb->header.length = sizeof(*sccb); sccb->header.length = sizeof(*sccb);
sccb->header.function_code = 0x80;
sccb->header.control_mask[2] = 0x80; sccb->header.control_mask[2] = 0x80;
rc = sclp_cmd_sync_early(commands[i], sccb); rc = sclp_cmd_sync_early(commands[i], sccb);
} while (rc == -EBUSY); } while (rc == -EBUSY);
......
...@@ -212,6 +212,9 @@ struct tape_device { ...@@ -212,6 +212,9 @@ struct tape_device {
struct tape_class_device * nt; struct tape_class_device * nt;
struct tape_class_device * rt; struct tape_class_device * rt;
/* Device mutex to serialize tape commands. */
struct mutex mutex;
/* Device discipline information. */ /* Device discipline information. */
struct tape_discipline * discipline; struct tape_discipline * discipline;
void * discdata; void * discdata;
...@@ -292,9 +295,9 @@ extern int tape_generic_pm_suspend(struct ccw_device *); ...@@ -292,9 +295,9 @@ extern int tape_generic_pm_suspend(struct ccw_device *);
extern int tape_generic_probe(struct ccw_device *); extern int tape_generic_probe(struct ccw_device *);
extern void tape_generic_remove(struct ccw_device *); extern void tape_generic_remove(struct ccw_device *);
extern struct tape_device *tape_get_device(int devindex); extern struct tape_device *tape_find_device(int devindex);
extern struct tape_device *tape_get_device_reference(struct tape_device *); extern struct tape_device *tape_get_device(struct tape_device *);
extern struct tape_device *tape_put_device(struct tape_device *); extern void tape_put_device(struct tape_device *);
/* Externals from tape_char.c */ /* Externals from tape_char.c */
extern int tapechar_init(void); extern int tapechar_init(void);
......
...@@ -113,16 +113,16 @@ tape_34xx_work_handler(struct work_struct *work) ...@@ -113,16 +113,16 @@ tape_34xx_work_handler(struct work_struct *work)
{ {
struct tape_34xx_work *p = struct tape_34xx_work *p =
container_of(work, struct tape_34xx_work, work); container_of(work, struct tape_34xx_work, work);
struct tape_device *device = p->device;
switch(p->op) { switch(p->op) {
case TO_MSEN: case TO_MSEN:
tape_34xx_medium_sense(p->device); tape_34xx_medium_sense(device);
break; break;
default: default:
DBF_EVENT(3, "T34XX: internal error: unknown work\n"); DBF_EVENT(3, "T34XX: internal error: unknown work\n");
} }
tape_put_device(device);
p->device = tape_put_device(p->device);
kfree(p); kfree(p);
} }
...@@ -136,7 +136,7 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op) ...@@ -136,7 +136,7 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
INIT_WORK(&p->work, tape_34xx_work_handler); INIT_WORK(&p->work, tape_34xx_work_handler);
p->device = tape_get_device_reference(device); p->device = tape_get_device(device);
p->op = op; p->op = op;
schedule_work(&p->work); schedule_work(&p->work);
......
...@@ -608,7 +608,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op) ...@@ -608,7 +608,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op)
INIT_WORK(&p->work, tape_3590_work_handler); INIT_WORK(&p->work, tape_3590_work_handler);
p->device = tape_get_device_reference(device); p->device = tape_get_device(device);
p->op = op; p->op = op;
schedule_work(&p->work); schedule_work(&p->work);
......
...@@ -54,7 +54,7 @@ static const struct block_device_operations tapeblock_fops = { ...@@ -54,7 +54,7 @@ static const struct block_device_operations tapeblock_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
.open = tapeblock_open, .open = tapeblock_open,
.release = tapeblock_release, .release = tapeblock_release,
.locked_ioctl = tapeblock_ioctl, .ioctl = tapeblock_ioctl,
.media_changed = tapeblock_medium_changed, .media_changed = tapeblock_medium_changed,
.revalidate_disk = tapeblock_revalidate_disk, .revalidate_disk = tapeblock_revalidate_disk,
}; };
...@@ -239,7 +239,7 @@ tapeblock_setup_device(struct tape_device * device) ...@@ -239,7 +239,7 @@ tapeblock_setup_device(struct tape_device * device)
disk->major = tapeblock_major; disk->major = tapeblock_major;
disk->first_minor = device->first_minor; disk->first_minor = device->first_minor;
disk->fops = &tapeblock_fops; disk->fops = &tapeblock_fops;
disk->private_data = tape_get_device_reference(device); disk->private_data = tape_get_device(device);
disk->queue = blkdat->request_queue; disk->queue = blkdat->request_queue;
set_capacity(disk, 0); set_capacity(disk, 0);
sprintf(disk->disk_name, "btibm%d", sprintf(disk->disk_name, "btibm%d",
...@@ -247,11 +247,11 @@ tapeblock_setup_device(struct tape_device * device) ...@@ -247,11 +247,11 @@ tapeblock_setup_device(struct tape_device * device)
blkdat->disk = disk; blkdat->disk = disk;
blkdat->medium_changed = 1; blkdat->medium_changed = 1;
blkdat->request_queue->queuedata = tape_get_device_reference(device); blkdat->request_queue->queuedata = tape_get_device(device);
add_disk(disk); add_disk(disk);
tape_get_device_reference(device); tape_get_device(device);
INIT_WORK(&blkdat->requeue_task, tapeblock_requeue); INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
return 0; return 0;
...@@ -274,13 +274,14 @@ tapeblock_cleanup_device(struct tape_device *device) ...@@ -274,13 +274,14 @@ tapeblock_cleanup_device(struct tape_device *device)
} }
del_gendisk(device->blk_data.disk); del_gendisk(device->blk_data.disk);
device->blk_data.disk->private_data = device->blk_data.disk->private_data = NULL;
tape_put_device(device->blk_data.disk->private_data); tape_put_device(device);
put_disk(device->blk_data.disk); put_disk(device->blk_data.disk);
device->blk_data.disk = NULL; device->blk_data.disk = NULL;
cleanup_queue: cleanup_queue:
device->blk_data.request_queue->queuedata = tape_put_device(device); device->blk_data.request_queue->queuedata = NULL;
tape_put_device(device);
blk_cleanup_queue(device->blk_data.request_queue); blk_cleanup_queue(device->blk_data.request_queue);
device->blk_data.request_queue = NULL; device->blk_data.request_queue = NULL;
...@@ -363,7 +364,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode) ...@@ -363,7 +364,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode)
struct tape_device * device; struct tape_device * device;
int rc; int rc;
device = tape_get_device_reference(disk->private_data); device = tape_get_device(disk->private_data);
if (device->required_tapemarks) { if (device->required_tapemarks) {
DBF_EVENT(2, "TBLOCK: missing tapemarks\n"); DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
......
...@@ -33,8 +33,7 @@ static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *); ...@@ -33,8 +33,7 @@ static ssize_t tapechar_read(struct file *, char __user *, size_t, loff_t *);
static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *); static ssize_t tapechar_write(struct file *, const char __user *, size_t, loff_t *);
static int tapechar_open(struct inode *,struct file *); static int tapechar_open(struct inode *,struct file *);
static int tapechar_release(struct inode *,struct file *); static int tapechar_release(struct inode *,struct file *);
static int tapechar_ioctl(struct inode *, struct file *, unsigned int, static long tapechar_ioctl(struct file *, unsigned int, unsigned long);
unsigned long);
static long tapechar_compat_ioctl(struct file *, unsigned int, static long tapechar_compat_ioctl(struct file *, unsigned int,
unsigned long); unsigned long);
...@@ -43,7 +42,7 @@ static const struct file_operations tape_fops = ...@@ -43,7 +42,7 @@ static const struct file_operations tape_fops =
.owner = THIS_MODULE, .owner = THIS_MODULE,
.read = tapechar_read, .read = tapechar_read,
.write = tapechar_write, .write = tapechar_write,
.ioctl = tapechar_ioctl, .unlocked_ioctl = tapechar_ioctl,
.compat_ioctl = tapechar_compat_ioctl, .compat_ioctl = tapechar_compat_ioctl,
.open = tapechar_open, .open = tapechar_open,
.release = tapechar_release, .release = tapechar_release,
...@@ -170,7 +169,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos) ...@@ -170,7 +169,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
if (rc == 0) { if (rc == 0) {
rc = block_size - request->rescnt; rc = block_size - request->rescnt;
DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc); DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc);
filp->f_pos += rc;
/* Copy data from idal buffer to user space. */ /* Copy data from idal buffer to user space. */
if (idal_buffer_to_user(device->char_data.idal_buf, if (idal_buffer_to_user(device->char_data.idal_buf,
data, rc) != 0) data, rc) != 0)
...@@ -238,7 +236,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t ...@@ -238,7 +236,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t
break; break;
DBF_EVENT(6, "TCHAR:wbytes: %lx\n", DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
block_size - request->rescnt); block_size - request->rescnt);
filp->f_pos += block_size - request->rescnt;
written += block_size - request->rescnt; written += block_size - request->rescnt;
if (request->rescnt != 0) if (request->rescnt != 0)
break; break;
...@@ -286,26 +283,20 @@ tapechar_open (struct inode *inode, struct file *filp) ...@@ -286,26 +283,20 @@ tapechar_open (struct inode *inode, struct file *filp)
if (imajor(filp->f_path.dentry->d_inode) != tapechar_major) if (imajor(filp->f_path.dentry->d_inode) != tapechar_major)
return -ENODEV; return -ENODEV;
lock_kernel();
minor = iminor(filp->f_path.dentry->d_inode); minor = iminor(filp->f_path.dentry->d_inode);
device = tape_get_device(minor / TAPE_MINORS_PER_DEV); device = tape_find_device(minor / TAPE_MINORS_PER_DEV);
if (IS_ERR(device)) { if (IS_ERR(device)) {
DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n"); DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n");
rc = PTR_ERR(device); return PTR_ERR(device);
goto out;
} }
rc = tape_open(device); rc = tape_open(device);
if (rc == 0) { if (rc == 0) {
filp->private_data = device; filp->private_data = device;
rc = nonseekable_open(inode, filp); nonseekable_open(inode, filp);
} } else
else
tape_put_device(device); tape_put_device(device);
out:
unlock_kernel();
return rc; return rc;
} }
...@@ -342,7 +333,8 @@ tapechar_release(struct inode *inode, struct file *filp) ...@@ -342,7 +333,8 @@ tapechar_release(struct inode *inode, struct file *filp)
device->char_data.idal_buf = NULL; device->char_data.idal_buf = NULL;
} }
tape_release(device); tape_release(device);
filp->private_data = tape_put_device(device); filp->private_data = NULL;
tape_put_device(device);
return 0; return 0;
} }
...@@ -351,16 +343,11 @@ tapechar_release(struct inode *inode, struct file *filp) ...@@ -351,16 +343,11 @@ tapechar_release(struct inode *inode, struct file *filp)
* Tape device io controls. * Tape device io controls.
*/ */
static int static int
tapechar_ioctl(struct inode *inp, struct file *filp, __tapechar_ioctl(struct tape_device *device,
unsigned int no, unsigned long data) unsigned int no, unsigned long data)
{ {
struct tape_device *device;
int rc; int rc;
DBF_EVENT(6, "TCHAR:ioct\n");
device = (struct tape_device *) filp->private_data;
if (no == MTIOCTOP) { if (no == MTIOCTOP) {
struct mtop op; struct mtop op;
...@@ -452,6 +439,21 @@ tapechar_ioctl(struct inode *inp, struct file *filp, ...@@ -452,6 +439,21 @@ tapechar_ioctl(struct inode *inp, struct file *filp,
return device->discipline->ioctl_fn(device, no, data); return device->discipline->ioctl_fn(device, no, data);
} }
static long
tapechar_ioctl(struct file *filp, unsigned int no, unsigned long data)
{
struct tape_device *device;
long rc;
DBF_EVENT(6, "TCHAR:ioct\n");
device = (struct tape_device *) filp->private_data;
mutex_lock(&device->mutex);
rc = __tapechar_ioctl(device, no, data);
mutex_unlock(&device->mutex);
return rc;
}
static long static long
tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data) tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data)
{ {
...@@ -459,9 +461,9 @@ tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data) ...@@ -459,9 +461,9 @@ tapechar_compat_ioctl(struct file *filp, unsigned int no, unsigned long data)
int rval = -ENOIOCTLCMD; int rval = -ENOIOCTLCMD;
if (device->discipline->ioctl_fn) { if (device->discipline->ioctl_fn) {
lock_kernel(); mutex_lock(&device->mutex);
rval = device->discipline->ioctl_fn(device, no, data); rval = device->discipline->ioctl_fn(device, no, data);
unlock_kernel(); mutex_unlock(&device->mutex);
if (rval == -EINVAL) if (rval == -EINVAL)
rval = -ENOIOCTLCMD; rval = -ENOIOCTLCMD;
} }
......
...@@ -492,6 +492,7 @@ tape_alloc_device(void) ...@@ -492,6 +492,7 @@ tape_alloc_device(void)
kfree(device); kfree(device);
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
mutex_init(&device->mutex);
INIT_LIST_HEAD(&device->req_queue); INIT_LIST_HEAD(&device->req_queue);
INIT_LIST_HEAD(&device->node); INIT_LIST_HEAD(&device->node);
init_waitqueue_head(&device->state_change_wq); init_waitqueue_head(&device->state_change_wq);
...@@ -511,11 +512,12 @@ tape_alloc_device(void) ...@@ -511,11 +512,12 @@ tape_alloc_device(void)
* increment the reference count. * increment the reference count.
*/ */
struct tape_device * struct tape_device *
tape_get_device_reference(struct tape_device *device) tape_get_device(struct tape_device *device)
{ {
DBF_EVENT(4, "tape_get_device_reference(%p) = %i\n", device, int count;
atomic_inc_return(&device->ref_count));
count = atomic_inc_return(&device->ref_count);
DBF_EVENT(4, "tape_get_device(%p) = %i\n", device, count);
return device; return device;
} }
...@@ -525,32 +527,25 @@ tape_get_device_reference(struct tape_device *device) ...@@ -525,32 +527,25 @@ tape_get_device_reference(struct tape_device *device)
* The function returns a NULL pointer to be used by the caller * The function returns a NULL pointer to be used by the caller
* for clearing reference pointers. * for clearing reference pointers.
*/ */
struct tape_device * void
tape_put_device(struct tape_device *device) tape_put_device(struct tape_device *device)
{ {
int remain; int count;
remain = atomic_dec_return(&device->ref_count); count = atomic_dec_return(&device->ref_count);
if (remain > 0) { DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, count);
DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain); BUG_ON(count < 0);
} else { if (count == 0) {
if (remain < 0) {
DBF_EVENT(4, "put device without reference\n");
} else {
DBF_EVENT(4, "tape_free_device(%p)\n", device);
kfree(device->modeset_byte); kfree(device->modeset_byte);
kfree(device); kfree(device);
} }
}
return NULL;
} }
/* /*
* Find tape device by a device index. * Find tape device by a device index.
*/ */
struct tape_device * struct tape_device *
tape_get_device(int devindex) tape_find_device(int devindex)
{ {
struct tape_device *device, *tmp; struct tape_device *device, *tmp;
...@@ -558,7 +553,7 @@ tape_get_device(int devindex) ...@@ -558,7 +553,7 @@ tape_get_device(int devindex)
read_lock(&tape_device_lock); read_lock(&tape_device_lock);
list_for_each_entry(tmp, &tape_device_list, node) { list_for_each_entry(tmp, &tape_device_list, node) {
if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) { if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) {
device = tape_get_device_reference(tmp); device = tape_get_device(tmp);
break; break;
} }
} }
...@@ -579,7 +574,8 @@ tape_generic_probe(struct ccw_device *cdev) ...@@ -579,7 +574,8 @@ tape_generic_probe(struct ccw_device *cdev)
device = tape_alloc_device(); device = tape_alloc_device();
if (IS_ERR(device)) if (IS_ERR(device))
return -ENODEV; return -ENODEV;
ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP); ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP |
CCWDEV_DO_MULTIPATH);
ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group); ret = sysfs_create_group(&cdev->dev.kobj, &tape_attr_group);
if (ret) { if (ret) {
tape_put_device(device); tape_put_device(device);
...@@ -606,7 +602,8 @@ __tape_discard_requests(struct tape_device *device) ...@@ -606,7 +602,8 @@ __tape_discard_requests(struct tape_device *device)
list_del(&request->list); list_del(&request->list);
/* Decrease ref_count for removed request. */ /* Decrease ref_count for removed request. */
request->device = tape_put_device(device); request->device = NULL;
tape_put_device(device);
request->rc = -EIO; request->rc = -EIO;
if (request->callback != NULL) if (request->callback != NULL)
request->callback(request, request->callback_data); request->callback(request, request->callback_data);
...@@ -664,9 +661,11 @@ tape_generic_remove(struct ccw_device *cdev) ...@@ -664,9 +661,11 @@ tape_generic_remove(struct ccw_device *cdev)
tape_cleanup_device(device); tape_cleanup_device(device);
} }
if (!dev_get_drvdata(&cdev->dev)) { device = dev_get_drvdata(&cdev->dev);
if (device) {
sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group); sysfs_remove_group(&cdev->dev.kobj, &tape_attr_group);
dev_set_drvdata(&cdev->dev, tape_put_device(dev_get_drvdata(&cdev->dev))); dev_set_drvdata(&cdev->dev, NULL);
tape_put_device(device);
} }
} }
...@@ -721,9 +720,8 @@ tape_free_request (struct tape_request * request) ...@@ -721,9 +720,8 @@ tape_free_request (struct tape_request * request)
{ {
DBF_LH(6, "Free request %p\n", request); DBF_LH(6, "Free request %p\n", request);
if (request->device != NULL) { if (request->device)
request->device = tape_put_device(request->device); tape_put_device(request->device);
}
kfree(request->cpdata); kfree(request->cpdata);
kfree(request->cpaddr); kfree(request->cpaddr);
kfree(request); kfree(request);
...@@ -838,7 +836,8 @@ static void tape_long_busy_timeout(unsigned long data) ...@@ -838,7 +836,8 @@ static void tape_long_busy_timeout(unsigned long data)
BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY); BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY);
DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id); DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id);
__tape_start_next_request(device); __tape_start_next_request(device);
device->lb_timeout.data = (unsigned long) tape_put_device(device); device->lb_timeout.data = 0UL;
tape_put_device(device);
spin_unlock_irq(get_ccwdev_lock(device->cdev)); spin_unlock_irq(get_ccwdev_lock(device->cdev));
} }
...@@ -918,7 +917,7 @@ __tape_start_request(struct tape_device *device, struct tape_request *request) ...@@ -918,7 +917,7 @@ __tape_start_request(struct tape_device *device, struct tape_request *request)
} }
/* Increase use count of device for the added request. */ /* Increase use count of device for the added request. */
request->device = tape_get_device_reference(device); request->device = tape_get_device(device);
if (list_empty(&device->req_queue)) { if (list_empty(&device->req_queue)) {
/* No other requests are on the queue. Start this one. */ /* No other requests are on the queue. Start this one. */
...@@ -1117,7 +1116,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -1117,7 +1116,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
if (req->status == TAPE_REQUEST_LONG_BUSY) { if (req->status == TAPE_REQUEST_LONG_BUSY) {
DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id); DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id);
if (del_timer(&device->lb_timeout)) { if (del_timer(&device->lb_timeout)) {
device->lb_timeout.data = (unsigned long) device->lb_timeout.data = 0UL;
tape_put_device(device); tape_put_device(device);
__tape_start_next_request(device); __tape_start_next_request(device);
} }
...@@ -1173,7 +1172,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) ...@@ -1173,7 +1172,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
break; break;
case TAPE_IO_LONG_BUSY: case TAPE_IO_LONG_BUSY:
device->lb_timeout.data = device->lb_timeout.data =
(unsigned long)tape_get_device_reference(device); (unsigned long) tape_get_device(device);
device->lb_timeout.expires = jiffies + device->lb_timeout.expires = jiffies +
LONG_BUSY_TIMEOUT * HZ; LONG_BUSY_TIMEOUT * HZ;
DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id); DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id);
...@@ -1326,7 +1325,7 @@ EXPORT_SYMBOL(tape_generic_online); ...@@ -1326,7 +1325,7 @@ EXPORT_SYMBOL(tape_generic_online);
EXPORT_SYMBOL(tape_generic_offline); EXPORT_SYMBOL(tape_generic_offline);
EXPORT_SYMBOL(tape_generic_pm_suspend); EXPORT_SYMBOL(tape_generic_pm_suspend);
EXPORT_SYMBOL(tape_put_device); EXPORT_SYMBOL(tape_put_device);
EXPORT_SYMBOL(tape_get_device_reference); EXPORT_SYMBOL(tape_get_device);
EXPORT_SYMBOL(tape_state_verbose); EXPORT_SYMBOL(tape_state_verbose);
EXPORT_SYMBOL(tape_op_verbose); EXPORT_SYMBOL(tape_op_verbose);
EXPORT_SYMBOL(tape_state_set); EXPORT_SYMBOL(tape_state_set);
......
...@@ -45,7 +45,7 @@ static int tape_proc_show(struct seq_file *m, void *v) ...@@ -45,7 +45,7 @@ static int tape_proc_show(struct seq_file *m, void *v)
seq_printf(m, "TapeNo\tBusID CuType/Model\t" seq_printf(m, "TapeNo\tBusID CuType/Model\t"
"DevType/Model\tBlkSize\tState\tOp\tMedState\n"); "DevType/Model\tBlkSize\tState\tOp\tMedState\n");
} }
device = tape_get_device(n); device = tape_find_device(n);
if (IS_ERR(device)) if (IS_ERR(device))
return 0; return 0;
spin_lock_irq(get_ccwdev_lock(device->cdev)); spin_lock_irq(get_ccwdev_lock(device->cdev));
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/bootmem.h> #include <linux/bootmem.h>
#include <linux/compat.h>
#include <asm/ccwdev.h> #include <asm/ccwdev.h>
#include <asm/cio.h> #include <asm/cio.h>
...@@ -1731,6 +1732,22 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file, ...@@ -1731,6 +1732,22 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file,
return kbd_ioctl(tp->kbd, file, cmd, arg); return kbd_ioctl(tp->kbd, file, cmd, arg);
} }
#ifdef CONFIG_COMPAT
static long
tty3270_compat_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tty3270 *tp;
tp = tty->driver_data;
if (!tp)
return -ENODEV;
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
return kbd_ioctl(tp->kbd, file, cmd, (unsigned long)compat_ptr(arg));
}
#endif
static const struct tty_operations tty3270_ops = { static const struct tty_operations tty3270_ops = {
.open = tty3270_open, .open = tty3270_open,
.close = tty3270_close, .close = tty3270_close,
...@@ -1745,6 +1762,9 @@ static const struct tty_operations tty3270_ops = { ...@@ -1745,6 +1762,9 @@ static const struct tty_operations tty3270_ops = {
.hangup = tty3270_hangup, .hangup = tty3270_hangup,
.wait_until_sent = tty3270_wait_until_sent, .wait_until_sent = tty3270_wait_until_sent,
.ioctl = tty3270_ioctl, .ioctl = tty3270_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = tty3270_compat_ioctl,
#endif
.set_termios = tty3270_set_termios .set_termios = tty3270_set_termios
}; };
......
...@@ -312,11 +312,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) ...@@ -312,11 +312,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
return -ENOSYS; return -ENOSYS;
/* Besure this device hasn't already been opened */ /* Besure this device hasn't already been opened */
lock_kernel();
spin_lock_bh(&logptr->priv_lock); spin_lock_bh(&logptr->priv_lock);
if (logptr->dev_in_use) { if (logptr->dev_in_use) {
spin_unlock_bh(&logptr->priv_lock); spin_unlock_bh(&logptr->priv_lock);
unlock_kernel();
return -EBUSY; return -EBUSY;
} }
logptr->dev_in_use = 1; logptr->dev_in_use = 1;
...@@ -360,9 +358,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) ...@@ -360,9 +358,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
|| (logptr->iucv_path_severed)); || (logptr->iucv_path_severed));
if (logptr->iucv_path_severed) if (logptr->iucv_path_severed)
goto out_record; goto out_record;
ret = nonseekable_open(inode, filp); nonseekable_open(inode, filp);
unlock_kernel(); return 0;
return ret;
out_record: out_record:
if (logptr->autorecording) if (logptr->autorecording)
...@@ -372,7 +369,6 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp) ...@@ -372,7 +369,6 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
logptr->path = NULL; logptr->path = NULL;
out_dev: out_dev:
logptr->dev_in_use = 0; logptr->dev_in_use = 0;
unlock_kernel();
return -EIO; return -EIO;
} }
......
...@@ -695,7 +695,6 @@ static int ur_open(struct inode *inode, struct file *file) ...@@ -695,7 +695,6 @@ static int ur_open(struct inode *inode, struct file *file)
if (accmode == O_RDWR) if (accmode == O_RDWR)
return -EACCES; return -EACCES;
lock_kernel();
/* /*
* We treat the minor number as the devno of the ur device * We treat the minor number as the devno of the ur device
* to find in the driver tree. * to find in the driver tree.
...@@ -749,7 +748,6 @@ static int ur_open(struct inode *inode, struct file *file) ...@@ -749,7 +748,6 @@ static int ur_open(struct inode *inode, struct file *file)
goto fail_urfile_free; goto fail_urfile_free;
urf->file_reclen = rc; urf->file_reclen = rc;
file->private_data = urf; file->private_data = urf;
unlock_kernel();
return 0; return 0;
fail_urfile_free: fail_urfile_free:
...@@ -761,7 +759,6 @@ static int ur_open(struct inode *inode, struct file *file) ...@@ -761,7 +759,6 @@ static int ur_open(struct inode *inode, struct file *file)
fail_put: fail_put:
urdev_put(urd); urdev_put(urd);
out: out:
unlock_kernel();
return rc; return rc;
} }
......
...@@ -19,7 +19,6 @@ ...@@ -19,7 +19,6 @@
#include <linux/moduleparam.h> #include <linux/moduleparam.h>
#include <linux/suspend.h> #include <linux/suspend.h>
#include <linux/watchdog.h> #include <linux/watchdog.h>
#include <linux/smp_lock.h>
#include <asm/ebcdic.h> #include <asm/ebcdic.h>
#include <asm/io.h> #include <asm/io.h>
...@@ -49,6 +48,8 @@ static unsigned int vmwdt_interval = 60; ...@@ -49,6 +48,8 @@ static unsigned int vmwdt_interval = 60;
static unsigned long vmwdt_is_open; static unsigned long vmwdt_is_open;
static int vmwdt_expect_close; static int vmwdt_expect_close;
static DEFINE_MUTEX(vmwdt_mutex);
#define VMWDT_OPEN 0 /* devnode is open or suspend in progress */ #define VMWDT_OPEN 0 /* devnode is open or suspend in progress */
#define VMWDT_RUNNING 1 /* The watchdog is armed */ #define VMWDT_RUNNING 1 /* The watchdog is armed */
...@@ -133,15 +134,11 @@ static int __init vmwdt_probe(void) ...@@ -133,15 +134,11 @@ static int __init vmwdt_probe(void)
static int vmwdt_open(struct inode *i, struct file *f) static int vmwdt_open(struct inode *i, struct file *f)
{ {
int ret; int ret;
lock_kernel(); if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open))
if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) {
unlock_kernel();
return -EBUSY; return -EBUSY;
}
ret = vmwdt_keepalive(); ret = vmwdt_keepalive();
if (ret) if (ret)
clear_bit(VMWDT_OPEN, &vmwdt_is_open); clear_bit(VMWDT_OPEN, &vmwdt_is_open);
unlock_kernel();
return ret ? ret : nonseekable_open(i, f); return ret ? ret : nonseekable_open(i, f);
} }
...@@ -160,8 +157,7 @@ static struct watchdog_info vmwdt_info = { ...@@ -160,8 +157,7 @@ static struct watchdog_info vmwdt_info = {
.identity = "z/VM Watchdog Timer", .identity = "z/VM Watchdog Timer",
}; };
static int vmwdt_ioctl(struct inode *i, struct file *f, static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg)
unsigned int cmd, unsigned long arg)
{ {
switch (cmd) { switch (cmd) {
case WDIOC_GETSUPPORT: case WDIOC_GETSUPPORT:
...@@ -205,10 +201,19 @@ static int vmwdt_ioctl(struct inode *i, struct file *f, ...@@ -205,10 +201,19 @@ static int vmwdt_ioctl(struct inode *i, struct file *f,
case WDIOC_KEEPALIVE: case WDIOC_KEEPALIVE:
return vmwdt_keepalive(); return vmwdt_keepalive();
} }
return -EINVAL; return -EINVAL;
} }
static long vmwdt_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
int rc;
mutex_lock(&vmwdt_mutex);
rc = __vmwdt_ioctl(cmd, arg);
mutex_unlock(&vmwdt_mutex);
return (long) rc;
}
static ssize_t vmwdt_write(struct file *f, const char __user *buf, static ssize_t vmwdt_write(struct file *f, const char __user *buf,
size_t count, loff_t *ppos) size_t count, loff_t *ppos)
{ {
...@@ -288,7 +293,7 @@ static struct notifier_block vmwdt_power_notifier = { ...@@ -288,7 +293,7 @@ static struct notifier_block vmwdt_power_notifier = {
static const struct file_operations vmwdt_fops = { static const struct file_operations vmwdt_fops = {
.open = &vmwdt_open, .open = &vmwdt_open,
.release = &vmwdt_close, .release = &vmwdt_close,
.ioctl = &vmwdt_ioctl, .unlocked_ioctl = &vmwdt_ioctl,
.write = &vmwdt_write, .write = &vmwdt_write,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}; };
...@@ -309,6 +314,10 @@ static int __init vmwdt_init(void) ...@@ -309,6 +314,10 @@ static int __init vmwdt_init(void)
ret = register_pm_notifier(&vmwdt_power_notifier); ret = register_pm_notifier(&vmwdt_power_notifier);
if (ret) if (ret)
return ret; return ret;
/*
* misc_register() has to be the last action in module_init(), because
* file operations will be available right after this.
*/
ret = misc_register(&vmwdt_dev); ret = misc_register(&vmwdt_dev);
if (ret) { if (ret) {
unregister_pm_notifier(&vmwdt_power_notifier); unregister_pm_notifier(&vmwdt_power_notifier);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
# #
obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \ obj-y += airq.o blacklist.o chsc.o cio.o css.o chp.o idset.o isc.o \
fcx.o itcw.o crw.o fcx.o itcw.o crw.o ccwreq.o
ccw_device-objs += device.o device_fsm.o device_ops.o ccw_device-objs += device.o device_fsm.o device_ops.o
ccw_device-objs += device_id.o device_pgid.o device_status.o ccw_device-objs += device_id.o device_pgid.o device_status.o
obj-y += ccw_device.o cmf.o obj-y += ccw_device.o cmf.o
......
/*
* Handling of internal CCW device requests.
*
* Copyright IBM Corp. 2009
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
#include <linux/types.h>
#include <linux/err.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
#include "io_sch.h"
#include "cio.h"
#include "device.h"
#include "cio_debug.h"
/**
* lpm_adjust - adjust path mask
* @lpm: path mask to adjust
* @mask: mask of available paths
*
* Shift @lpm right until @lpm and @mask have at least one bit in common or
* until @lpm is zero. Return the resulting lpm.
*/
int lpm_adjust(int lpm, int mask)
{
while (lpm && ((lpm & mask) == 0))
lpm >>= 1;
return lpm;
}
/*
* Adjust path mask to use next path and reset retry count. Return resulting
* path mask.
*/
static u16 ccwreq_next_path(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask >>= 1, req->lpm);
return req->mask;
}
/*
* Clean up device state and report to callback.
*/
static void ccwreq_stop(struct ccw_device *cdev, int rc)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
if (req->done)
return;
req->done = 1;
ccw_device_set_timeout(cdev, 0);
memset(&cdev->private->irb, 0, sizeof(struct irb));
sch->lpm = sch->schib.pmcw.pam;
if (rc && rc != -ENODEV && req->drc)
rc = req->drc;
req->callback(cdev, req->data, rc);
}
/*
* (Re-)Start the operation until retries and paths are exhausted.
*/
static void ccwreq_do(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw1 *cp = req->cp;
int rc = -EACCES;
while (req->mask) {
if (req->retries-- == 0) {
/* Retries exhausted, try next path. */
ccwreq_next_path(cdev);
continue;
}
/* Perform start function. */
sch->lpm = 0xff;
memset(&cdev->private->irb, 0, sizeof(struct irb));
rc = cio_start(sch, cp, (u8) req->mask);
if (rc == 0) {
/* I/O started successfully. */
ccw_device_set_timeout(cdev, req->timeout);
return;
}
if (rc == -ENODEV) {
/* Permanent device error. */
break;
}
if (rc == -EACCES) {
/* Permant path error. */
ccwreq_next_path(cdev);
continue;
}
/* Temporary improper status. */
rc = cio_clear(sch);
if (rc)
break;
return;
}
ccwreq_stop(cdev, rc);
}
/**
* ccw_request_start - perform I/O request
* @cdev: ccw device
*
* Perform the I/O request specified by cdev->req.
*/
void ccw_request_start(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
/* Try all paths twice to counter link flapping. */
req->mask = 0x8080;
req->retries = req->maxretries;
req->mask = lpm_adjust(req->mask, req->lpm);
req->drc = 0;
req->done = 0;
req->cancel = 0;
if (!req->mask)
goto out_nopath;
ccwreq_do(cdev);
return;
out_nopath:
ccwreq_stop(cdev, -EACCES);
}
/**
* ccw_request_cancel - cancel running I/O request
* @cdev: ccw device
*
* Cancel the I/O request specified by cdev->req. Return non-zero if request
* has already finished, zero otherwise.
*/
int ccw_request_cancel(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
int rc;
if (req->done)
return 1;
req->cancel = 1;
rc = cio_clear(sch);
if (rc)
ccwreq_stop(cdev, rc);
return 0;
}
/*
* Return the status of the internal I/O started on the specified ccw device.
* Perform BASIC SENSE if required.
*/
static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)
{
struct irb *irb = &cdev->private->irb;
struct cmd_scsw *scsw = &irb->scsw.cmd;
/* Perform BASIC SENSE if needed. */
if (ccw_device_accumulate_and_sense(cdev, lcirb))
return IO_RUNNING;
/* Check for halt/clear interrupt. */
if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))
return IO_KILLED;
/* Check for path error. */
if (scsw->cc == 3 || scsw->pno)
return IO_PATH_ERROR;
/* Handle BASIC SENSE data. */
if (irb->esw.esw0.erw.cons) {
CIO_TRACE_EVENT(2, "sensedata");
CIO_HEX_EVENT(2, &cdev->private->dev_id,
sizeof(struct ccw_dev_id));
CIO_HEX_EVENT(2, &cdev->private->irb.ecw, SENSE_MAX_COUNT);
/* Check for command reject. */
if (irb->ecw[0] & SNS0_CMD_REJECT)
return IO_REJECTED;
/* Assume that unexpected SENSE data implies an error. */
return IO_STATUS_ERROR;
}
/* Check for channel errors. */
if (scsw->cstat != 0)
return IO_STATUS_ERROR;
/* Check for device errors. */
if (scsw->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return IO_STATUS_ERROR;
/* Check for final state. */
if (!(scsw->dstat & DEV_STAT_DEV_END))
return IO_RUNNING;
/* Check for other improper status. */
if (scsw->cc == 1 && (scsw->stctl & SCSW_STCTL_ALERT_STATUS))
return IO_STATUS_ERROR;
return IO_DONE;
}
/*
* Log ccw request status.
*/
static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status)
{
struct ccw_request *req = &cdev->private->req;
struct {
struct ccw_dev_id dev_id;
u16 retries;
u8 lpm;
u8 status;
} __attribute__ ((packed)) data;
data.dev_id = cdev->private->dev_id;
data.retries = req->retries;
data.lpm = (u8) req->mask;
data.status = (u8) status;
CIO_TRACE_EVENT(2, "reqstat");
CIO_HEX_EVENT(2, &data, sizeof(data));
}
/**
* ccw_request_handler - interrupt handler for I/O request procedure.
* @cdev: ccw device
*
* Handle interrupt during I/O request procedure.
*/
void ccw_request_handler(struct ccw_device *cdev)
{
struct ccw_request *req = &cdev->private->req;
struct irb *irb = (struct irb *) __LC_IRB;
enum io_status status;
int rc = -EOPNOTSUPP;
/* Check status of I/O request. */
status = ccwreq_status(cdev, irb);
if (req->filter)
status = req->filter(cdev, req->data, irb, status);
if (status != IO_RUNNING)
ccw_device_set_timeout(cdev, 0);
if (status != IO_DONE && status != IO_RUNNING)
ccwreq_log_status(cdev, status);
switch (status) {
case IO_DONE:
break;
case IO_RUNNING:
return;
case IO_REJECTED:
goto err;
case IO_PATH_ERROR:
goto out_next_path;
case IO_STATUS_ERROR:
goto out_restart;
case IO_KILLED:
/* Check if request was cancelled on purpose. */
if (req->cancel) {
rc = -EIO;
goto err;
}
goto out_restart;
}
/* Check back with request initiator. */
if (!req->check)
goto out;
switch (req->check(cdev, req->data)) {
case 0:
break;
case -EAGAIN:
goto out_restart;
case -EACCES:
goto out_next_path;
default:
goto err;
}
out:
ccwreq_stop(cdev, 0);
return;
out_next_path:
/* Try next path and restart I/O. */
if (!ccwreq_next_path(cdev)) {
rc = -EACCES;
goto err;
}
out_restart:
/* Restart. */
ccwreq_do(cdev);
return;
err:
ccwreq_stop(cdev, rc);
}
/**
* ccw_request_timeout - timeout handler for I/O request procedure
* @cdev: ccw device
*
* Handle timeout during I/O request procedure.
*/
void ccw_request_timeout(struct ccw_device *cdev)
{
struct subchannel *sch = to_subchannel(cdev->dev.parent);
struct ccw_request *req = &cdev->private->req;
int rc;
if (!ccwreq_next_path(cdev)) {
/* set the final return code for this request */
req->drc = -ETIME;
}
rc = cio_clear(sch);
if (rc)
goto err;
return;
err:
ccwreq_stop(cdev, rc);
}
/**
* ccw_request_notoper - notoper handler for I/O request procedure
* @cdev: ccw device
*
* Handle timeout during I/O request procedure.
*/
void ccw_request_notoper(struct ccw_device *cdev)
{
ccwreq_stop(cdev, -ENODEV);
}
...@@ -68,6 +68,11 @@ struct schib { ...@@ -68,6 +68,11 @@ struct schib {
__u8 mda[4]; /* model dependent area */ __u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4))); } __attribute__ ((packed,aligned(4)));
enum sch_todo {
SCH_TODO_NOTHING,
SCH_TODO_UNREG,
};
/* subchannel data structure used by I/O subroutines */ /* subchannel data structure used by I/O subroutines */
struct subchannel { struct subchannel {
struct subchannel_id schid; struct subchannel_id schid;
...@@ -95,7 +100,8 @@ struct subchannel { ...@@ -95,7 +100,8 @@ struct subchannel {
struct device dev; /* entry in device tree */ struct device dev; /* entry in device tree */
struct css_driver *driver; struct css_driver *driver;
void *private; /* private per subchannel type data */ void *private; /* private per subchannel type data */
struct work_struct work; enum sch_todo todo;
struct work_struct todo_work;
struct schib_config config; struct schib_config config;
} __attribute__ ((aligned(8))); } __attribute__ ((aligned(8)));
......
...@@ -133,6 +133,8 @@ int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *), ...@@ -133,6 +133,8 @@ int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
return rc; return rc;
} }
static void css_sch_todo(struct work_struct *work);
static struct subchannel * static struct subchannel *
css_alloc_subchannel(struct subchannel_id schid) css_alloc_subchannel(struct subchannel_id schid)
{ {
...@@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid) ...@@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid)
kfree(sch); kfree(sch);
return ERR_PTR(ret); return ERR_PTR(ret);
} }
INIT_WORK(&sch->todo_work, css_sch_todo);
return sch; return sch;
} }
...@@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch) ...@@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch)
} }
EXPORT_SYMBOL_GPL(css_sch_device_unregister); EXPORT_SYMBOL_GPL(css_sch_device_unregister);
static void css_sch_todo(struct work_struct *work)
{
struct subchannel *sch;
enum sch_todo todo;
sch = container_of(work, struct subchannel, todo_work);
/* Find out todo. */
spin_lock_irq(sch->lock);
todo = sch->todo;
CIO_MSG_EVENT(4, "sch_todo: sch=0.%x.%04x, todo=%d\n", sch->schid.ssid,
sch->schid.sch_no, todo);
sch->todo = SCH_TODO_NOTHING;
spin_unlock_irq(sch->lock);
/* Perform todo. */
if (todo == SCH_TODO_UNREG)
css_sch_device_unregister(sch);
/* Release workqueue ref. */
put_device(&sch->dev);
}
/**
* css_sched_sch_todo - schedule a subchannel operation
* @sch: subchannel
* @todo: todo
*
* Schedule the operation identified by @todo to be performed on the slow path
* workqueue. Do nothing if another operation with higher priority is already
* scheduled. Needs to be called with subchannel lock held.
*/
void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
{
CIO_MSG_EVENT(4, "sch_todo: sched sch=0.%x.%04x todo=%d\n",
sch->schid.ssid, sch->schid.sch_no, todo);
if (sch->todo >= todo)
return;
/* Get workqueue ref. */
if (!get_device(&sch->dev))
return;
sch->todo = todo;
if (!queue_work(slow_path_wq, &sch->todo_work)) {
/* Already queued, release workqueue ref. */
put_device(&sch->dev);
}
}
static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw) static void ssd_from_pmcw(struct chsc_ssd_info *ssd, struct pmcw *pmcw)
{ {
int i; int i;
...@@ -376,8 +424,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow) ...@@ -376,8 +424,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
/* Unusable - ignore. */ /* Unusable - ignore. */
return 0; return 0;
} }
CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, " CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid,
"slow path.\n", schid.ssid, schid.sch_no, CIO_OPER); schid.sch_no);
return css_probe_device(schid); return css_probe_device(schid);
} }
...@@ -394,6 +442,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow) ...@@ -394,6 +442,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
"Got subchannel machine check but " "Got subchannel machine check but "
"no sch_event handler provided.\n"); "no sch_event handler provided.\n");
} }
if (ret != 0 && ret != -EAGAIN) {
CIO_MSG_EVENT(2, "eval: sch 0.%x.%04x, rc=%d\n",
sch->schid.ssid, sch->schid.sch_no, ret);
}
return ret; return ret;
} }
...@@ -684,6 +736,7 @@ static int __init setup_css(int nr) ...@@ -684,6 +736,7 @@ static int __init setup_css(int nr)
css->pseudo_subchannel->dev.parent = &css->device; css->pseudo_subchannel->dev.parent = &css->device;
css->pseudo_subchannel->dev.release = css_subchannel_release; css->pseudo_subchannel->dev.release = css_subchannel_release;
dev_set_name(&css->pseudo_subchannel->dev, "defunct"); dev_set_name(&css->pseudo_subchannel->dev, "defunct");
mutex_init(&css->pseudo_subchannel->reg_mutex);
ret = cio_create_sch_lock(css->pseudo_subchannel); ret = cio_create_sch_lock(css->pseudo_subchannel);
if (ret) { if (ret) {
kfree(css->pseudo_subchannel); kfree(css->pseudo_subchannel);
......
...@@ -11,6 +11,8 @@ ...@@ -11,6 +11,8 @@
#include <asm/chpid.h> #include <asm/chpid.h>
#include <asm/schid.h> #include <asm/schid.h>
#include "cio.h"
/* /*
* path grouping stuff * path grouping stuff
*/ */
...@@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *); ...@@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq; extern struct workqueue_struct *slow_path_wq;
void css_wait_for_slow_path(void); void css_wait_for_slow_path(void);
void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo);
#endif #endif
This diff is collapsed.
...@@ -21,7 +21,6 @@ enum dev_state { ...@@ -21,7 +21,6 @@ enum dev_state {
DEV_STATE_DISBAND_PGID, DEV_STATE_DISBAND_PGID,
DEV_STATE_BOXED, DEV_STATE_BOXED,
/* states to wait for i/o completion before doing something */ /* states to wait for i/o completion before doing something */
DEV_STATE_CLEAR_VERIFY,
DEV_STATE_TIMEOUT_KILL, DEV_STATE_TIMEOUT_KILL,
DEV_STATE_QUIESCE, DEV_STATE_QUIESCE,
/* special states for devices gone not operational */ /* special states for devices gone not operational */
...@@ -29,6 +28,7 @@ enum dev_state { ...@@ -29,6 +28,7 @@ enum dev_state {
DEV_STATE_DISCONNECTED_SENSE_ID, DEV_STATE_DISCONNECTED_SENSE_ID,
DEV_STATE_CMFCHANGE, DEV_STATE_CMFCHANGE,
DEV_STATE_CMFUPDATE, DEV_STATE_CMFUPDATE,
DEV_STATE_STEAL_LOCK,
/* last element! */ /* last element! */
NR_DEV_STATES NR_DEV_STATES
}; };
...@@ -81,17 +81,16 @@ void io_subchannel_init_config(struct subchannel *sch); ...@@ -81,17 +81,16 @@ void io_subchannel_init_config(struct subchannel *sch);
int ccw_device_cancel_halt_clear(struct ccw_device *); int ccw_device_cancel_halt_clear(struct ccw_device *);
void ccw_device_do_unbind_bind(struct work_struct *);
void ccw_device_move_to_orphanage(struct work_struct *);
int ccw_device_is_orphan(struct ccw_device *); int ccw_device_is_orphan(struct ccw_device *);
int ccw_device_recognition(struct ccw_device *); void ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *); int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *); int ccw_device_offline(struct ccw_device *);
void ccw_device_update_sense_data(struct ccw_device *); void ccw_device_update_sense_data(struct ccw_device *);
int ccw_device_test_sense_data(struct ccw_device *); int ccw_device_test_sense_data(struct ccw_device *);
void ccw_device_schedule_sch_unregister(struct ccw_device *); void ccw_device_schedule_sch_unregister(struct ccw_device *);
int ccw_purge_blacklisted(void); int ccw_purge_blacklisted(void);
void ccw_device_sched_todo(struct ccw_device *cdev, enum cdev_todo todo);
/* Function prototypes for device status and basic sense stuff. */ /* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *); void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
...@@ -99,24 +98,28 @@ void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *); ...@@ -99,24 +98,28 @@ void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
int ccw_device_accumulate_and_sense(struct ccw_device *, struct irb *); int ccw_device_accumulate_and_sense(struct ccw_device *, struct irb *);
int ccw_device_do_sense(struct ccw_device *, struct irb *); int ccw_device_do_sense(struct ccw_device *, struct irb *);
/* Function prototype for internal request handling. */
int lpm_adjust(int lpm, int mask);
void ccw_request_start(struct ccw_device *);
int ccw_request_cancel(struct ccw_device *cdev);
void ccw_request_handler(struct ccw_device *cdev);
void ccw_request_timeout(struct ccw_device *cdev);
void ccw_request_notoper(struct ccw_device *cdev);
/* Function prototypes for sense id stuff. */ /* Function prototypes for sense id stuff. */
void ccw_device_sense_id_start(struct ccw_device *); void ccw_device_sense_id_start(struct ccw_device *);
void ccw_device_sense_id_irq(struct ccw_device *, enum dev_event);
void ccw_device_sense_id_done(struct ccw_device *, int); void ccw_device_sense_id_done(struct ccw_device *, int);
/* Function prototypes for path grouping stuff. */ /* Function prototypes for path grouping stuff. */
void ccw_device_sense_pgid_start(struct ccw_device *);
void ccw_device_sense_pgid_irq(struct ccw_device *, enum dev_event);
void ccw_device_sense_pgid_done(struct ccw_device *, int);
void ccw_device_verify_start(struct ccw_device *); void ccw_device_verify_start(struct ccw_device *);
void ccw_device_verify_irq(struct ccw_device *, enum dev_event);
void ccw_device_verify_done(struct ccw_device *, int); void ccw_device_verify_done(struct ccw_device *, int);
void ccw_device_disband_start(struct ccw_device *); void ccw_device_disband_start(struct ccw_device *);
void ccw_device_disband_irq(struct ccw_device *, enum dev_event);
void ccw_device_disband_done(struct ccw_device *, int); void ccw_device_disband_done(struct ccw_device *, int);
void ccw_device_stlck_start(struct ccw_device *, void *, void *, void *);
void ccw_device_stlck_done(struct ccw_device *, void *, int);
int ccw_device_call_handler(struct ccw_device *); int ccw_device_call_handler(struct ccw_device *);
int ccw_device_stlck(struct ccw_device *); int ccw_device_stlck(struct ccw_device *);
......
This diff is collapsed.
This diff is collapsed.
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/completion.h>
#include <asm/ccwdev.h> #include <asm/ccwdev.h>
#include <asm/idals.h> #include <asm/idals.h>
...@@ -46,6 +47,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags) ...@@ -46,6 +47,7 @@ int ccw_device_set_options_mask(struct ccw_device *cdev, unsigned long flags)
cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0; cdev->private->options.repall = (flags & CCWDEV_REPORT_ALL) != 0;
cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0; cdev->private->options.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0;
cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0; cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0;
cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0;
return 0; return 0;
} }
...@@ -74,6 +76,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags) ...@@ -74,6 +76,7 @@ int ccw_device_set_options(struct ccw_device *cdev, unsigned long flags)
cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0; cdev->private->options.repall |= (flags & CCWDEV_REPORT_ALL) != 0;
cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0; cdev->private->options.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0;
cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0; cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0;
cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0;
return 0; return 0;
} }
...@@ -90,8 +93,33 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags) ...@@ -90,8 +93,33 @@ void ccw_device_clear_options(struct ccw_device *cdev, unsigned long flags)
cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0; cdev->private->options.repall &= (flags & CCWDEV_REPORT_ALL) == 0;
cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0; cdev->private->options.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 0;
cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0; cdev->private->options.force &= (flags & CCWDEV_ALLOW_FORCE) == 0;
cdev->private->options.mpath &= (flags & CCWDEV_DO_MULTIPATH) == 0;
} }
/**
* ccw_device_is_pathgroup - determine if paths to this device are grouped
* @cdev: ccw device
*
* Return non-zero if there is a path group, zero otherwise.
*/
int ccw_device_is_pathgroup(struct ccw_device *cdev)
{
return cdev->private->flags.pgroup;
}
EXPORT_SYMBOL(ccw_device_is_pathgroup);
/**
* ccw_device_is_multipath - determine if device is operating in multipath mode
* @cdev: ccw device
*
* Return non-zero if device is operating in multipath mode, zero otherwise.
*/
int ccw_device_is_multipath(struct ccw_device *cdev)
{
return cdev->private->flags.mpath;
}
EXPORT_SYMBOL(ccw_device_is_multipath);
/** /**
* ccw_device_clear() - terminate I/O request processing * ccw_device_clear() - terminate I/O request processing
* @cdev: target ccw device * @cdev: target ccw device
...@@ -167,8 +195,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa, ...@@ -167,8 +195,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
return -EINVAL; return -EINVAL;
if (cdev->private->state == DEV_STATE_NOT_OPER) if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV; return -ENODEV;
if (cdev->private->state == DEV_STATE_VERIFY || if (cdev->private->state == DEV_STATE_VERIFY) {
cdev->private->state == DEV_STATE_CLEAR_VERIFY) {
/* Remember to fake irb when finished. */ /* Remember to fake irb when finished. */
if (!cdev->private->flags.fake_irb) { if (!cdev->private->flags.fake_irb) {
cdev->private->flags.fake_irb = 1; cdev->private->flags.fake_irb = 1;
...@@ -478,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev) ...@@ -478,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
return sch->lpm; return sch->lpm;
} }
struct stlck_data {
struct completion done;
int rc;
};
void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
{
struct stlck_data *sdata = data;
sdata->rc = rc;
complete(&sdata->done);
}
/* /*
* Try to break the lock on a boxed device. * Perform unconditional reserve + release.
*/ */
int int ccw_device_stlck(struct ccw_device *cdev)
ccw_device_stlck(struct ccw_device *cdev)
{ {
void *buf, *buf2; struct subchannel *sch = to_subchannel(cdev->dev.parent);
unsigned long flags; struct stlck_data data;
struct subchannel *sch; u8 *buffer;
int ret; int rc;
if (!cdev)
return -ENODEV;
if (cdev->drv && !cdev->private->options.force) /* Check if steal lock operation is valid for this device. */
if (cdev->drv) {
if (!cdev->private->options.force)
return -EINVAL; return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
CIO_TRACE_EVENT(2, "stl lock");
CIO_TRACE_EVENT(2, dev_name(&cdev->dev));
buf = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
if (!buf)
return -ENOMEM;
buf2 = kmalloc(32*sizeof(char), GFP_DMA|GFP_KERNEL);
if (!buf2) {
kfree(buf);
return -ENOMEM;
} }
spin_lock_irqsave(sch->lock, flags); buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
ret = cio_enable_subchannel(sch, (u32)(addr_t)sch); if (!buffer)
if (ret) return -ENOMEM;
goto out_unlock; init_completion(&data.done);
/* data.rc = -EIO;
* Setup ccw. We chain an unconditional reserve and a release so we spin_lock_irq(sch->lock);
* only break the lock. rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
*/ if (rc)
cdev->private->iccws[0].cmd_code = CCW_CMD_STLCK;
cdev->private->iccws[0].cda = (__u32) __pa(buf);
cdev->private->iccws[0].count = 32;
cdev->private->iccws[0].flags = CCW_FLAG_CC;
cdev->private->iccws[1].cmd_code = CCW_CMD_RELEASE;
cdev->private->iccws[1].cda = (__u32) __pa(buf2);
cdev->private->iccws[1].count = 32;
cdev->private->iccws[1].flags = 0;
ret = cio_start(sch, cdev->private->iccws, 0);
if (ret) {
cio_disable_subchannel(sch); //FIXME: return code?
goto out_unlock; goto out_unlock;
/* Perform operation. */
cdev->private->state = DEV_STATE_STEAL_LOCK,
ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
spin_unlock_irq(sch->lock);
/* Wait for operation to finish. */
if (wait_for_completion_interruptible(&data.done)) {
/* Got a signal. */
spin_lock_irq(sch->lock);
ccw_request_cancel(cdev);
spin_unlock_irq(sch->lock);
wait_for_completion(&data.done);
} }
cdev->private->irb.scsw.cmd.actl |= SCSW_ACTL_START_PEND; rc = data.rc;
spin_unlock_irqrestore(sch->lock, flags); /* Check results. */
wait_event(cdev->private->wait_q, spin_lock_irq(sch->lock);
cdev->private->irb.scsw.cmd.actl == 0); cio_disable_subchannel(sch);
spin_lock_irqsave(sch->lock, flags); cdev->private->state = DEV_STATE_BOXED;
cio_disable_subchannel(sch); //FIXME: return code?
if ((cdev->private->irb.scsw.cmd.dstat !=
(DEV_STAT_CHN_END|DEV_STAT_DEV_END)) ||
(cdev->private->irb.scsw.cmd.cstat != 0))
ret = -EIO;
/* Clear irb. */
memset(&cdev->private->irb, 0, sizeof(struct irb));
out_unlock: out_unlock:
kfree(buf); spin_unlock_irq(sch->lock);
kfree(buf2); kfree(buffer);
spin_unlock_irqrestore(sch->lock, flags);
return ret; return rc;
} }
void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no) void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)
......
This diff is collapsed.
...@@ -336,9 +336,6 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) ...@@ -336,9 +336,6 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
sense_ccw->count = SENSE_MAX_COUNT; sense_ccw->count = SENSE_MAX_COUNT;
sense_ccw->flags = CCW_FLAG_SLI; sense_ccw->flags = CCW_FLAG_SLI;
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
rc = cio_start(sch, sense_ccw, 0xff); rc = cio_start(sch, sense_ccw, 0xff);
if (rc == -ENODEV || rc == -EACCES) if (rc == -ENODEV || rc == -EACCES)
dev_fsm_event(cdev, DEV_EVENT_VERIFY); dev_fsm_event(cdev, DEV_EVENT_VERIFY);
......
#ifndef S390_IO_SCH_H #ifndef S390_IO_SCH_H
#define S390_IO_SCH_H #define S390_IO_SCH_H
#include <linux/types.h>
#include <asm/schid.h> #include <asm/schid.h>
#include <asm/ccwdev.h>
#include "css.h"
/* /*
* command-mode operation request block * command-mode operation request block
...@@ -67,6 +70,52 @@ struct io_subchannel_private { ...@@ -67,6 +70,52 @@ struct io_subchannel_private {
#define MAX_CIWS 8 #define MAX_CIWS 8
/*
* Possible status values for a CCW request's I/O.
*/
enum io_status {
IO_DONE,
IO_RUNNING,
IO_STATUS_ERROR,
IO_PATH_ERROR,
IO_REJECTED,
IO_KILLED
};
/**
* ccw_request - Internal CCW request.
* @cp: channel program to start
* @timeout: maximum allowable time in jiffies between start I/O and interrupt
* @maxretries: number of retries per I/O operation and path
* @lpm: mask of paths to use
* @check: optional callback that determines if results are final
* @filter: optional callback to adjust request status based on IRB data
* @callback: final callback
* @data: user-defined pointer passed to all callbacks
* @mask: current path mask
* @retries: current number of retries
* @drc: delayed return code
* @cancel: non-zero if request was cancelled
* @done: non-zero if request was finished
*/
struct ccw_request {
struct ccw1 *cp;
unsigned long timeout;
u16 maxretries;
u8 lpm;
int (*check)(struct ccw_device *, void *);
enum io_status (*filter)(struct ccw_device *, void *, struct irb *,
enum io_status);
void (*callback)(struct ccw_device *, void *, int);
void *data;
/* These fields are used internally. */
u16 mask;
u16 retries;
int drc;
int cancel:1;
int done:1;
} __attribute__((packed));
/* /*
* sense-id response buffer layout * sense-id response buffer layout
*/ */
...@@ -82,32 +131,43 @@ struct senseid { ...@@ -82,32 +131,43 @@ struct senseid {
struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */ struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */
} __attribute__ ((packed, aligned(4))); } __attribute__ ((packed, aligned(4)));
enum cdev_todo {
CDEV_TODO_NOTHING,
CDEV_TODO_ENABLE_CMF,
CDEV_TODO_REBIND,
CDEV_TODO_REGISTER,
CDEV_TODO_UNREG,
CDEV_TODO_UNREG_EVAL,
};
struct ccw_device_private { struct ccw_device_private {
struct ccw_device *cdev; struct ccw_device *cdev;
struct subchannel *sch; struct subchannel *sch;
int state; /* device state */ int state; /* device state */
atomic_t onoff; atomic_t onoff;
unsigned long registered;
struct ccw_dev_id dev_id; /* device id */ struct ccw_dev_id dev_id; /* device id */
struct subchannel_id schid; /* subchannel number */ struct subchannel_id schid; /* subchannel number */
u8 imask; /* lpm mask for SNID/SID/SPGID */ struct ccw_request req; /* internal I/O request */
int iretry; /* retry counter SNID/SID/SPGID */ int iretry;
u8 pgid_valid_mask; /* mask of valid PGIDs */
struct { struct {
unsigned int fast:1; /* post with "channel end" */ unsigned int fast:1; /* post with "channel end" */
unsigned int repall:1; /* report every interrupt status */ unsigned int repall:1; /* report every interrupt status */
unsigned int pgroup:1; /* do path grouping */ unsigned int pgroup:1; /* do path grouping */
unsigned int force:1; /* allow forced online */ unsigned int force:1; /* allow forced online */
unsigned int mpath:1; /* do multipathing */
} __attribute__ ((packed)) options; } __attribute__ ((packed)) options;
struct { struct {
unsigned int pgid_single:1; /* use single path for Set PGID */
unsigned int esid:1; /* Ext. SenseID supported by HW */ unsigned int esid:1; /* Ext. SenseID supported by HW */
unsigned int dosense:1; /* delayed SENSE required */ unsigned int dosense:1; /* delayed SENSE required */
unsigned int doverify:1; /* delayed path verification */ unsigned int doverify:1; /* delayed path verification */
unsigned int donotify:1; /* call notify function */ unsigned int donotify:1; /* call notify function */
unsigned int recog_done:1; /* dev. recog. complete */ unsigned int recog_done:1; /* dev. recog. complete */
unsigned int fake_irb:1; /* deliver faked irb */ unsigned int fake_irb:1; /* deliver faked irb */
unsigned int intretry:1; /* retry internal operation */
unsigned int resuming:1; /* recognition while resume */ unsigned int resuming:1; /* recognition while resume */
unsigned int pgroup:1; /* pathgroup is set up */
unsigned int mpath:1; /* multipathing is set up */
unsigned int initialized:1; /* set if initial reference held */
} __attribute__((packed)) flags; } __attribute__((packed)) flags;
unsigned long intparm; /* user interruption parameter */ unsigned long intparm; /* user interruption parameter */
struct qdio_irq *qdio_data; struct qdio_irq *qdio_data;
...@@ -115,7 +175,8 @@ struct ccw_device_private { ...@@ -115,7 +175,8 @@ struct ccw_device_private {
struct senseid senseid; /* SenseID info */ struct senseid senseid; /* SenseID info */
struct pgid pgid[8]; /* path group IDs per chpid*/ struct pgid pgid[8]; /* path group IDs per chpid*/
struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */ struct ccw1 iccws[2]; /* ccws for SNID/SID/SPGID commands */
struct work_struct kick_work; struct work_struct todo_work;
enum cdev_todo todo;
wait_queue_head_t wait_q; wait_queue_head_t wait_q;
struct timer_list timer; struct timer_list timer;
void *cmb; /* measurement information */ void *cmb; /* measurement information */
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
...@@ -71,6 +71,8 @@ struct ica_z90_status { ...@@ -71,6 +71,8 @@ struct ica_z90_status {
#define ZCRYPT_PCIXCC_MCL3 4 #define ZCRYPT_PCIXCC_MCL3 4
#define ZCRYPT_CEX2C 5 #define ZCRYPT_CEX2C 5
#define ZCRYPT_CEX2A 6 #define ZCRYPT_CEX2A 6
#define ZCRYPT_CEX3C 7
#define ZCRYPT_CEX3A 8
/** /**
* Large random numbers are pulled in 4096 byte chunks from the crypto cards * Large random numbers are pulled in 4096 byte chunks from the crypto cards
......
This diff is collapsed.
...@@ -281,6 +281,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev, ...@@ -281,6 +281,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
struct completion work; struct completion work;
int rc; int rc;
ap_init_message(&ap_msg);
ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message) if (!ap_msg.message)
return -ENOMEM; return -ENOMEM;
...@@ -318,6 +319,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev, ...@@ -318,6 +319,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
struct completion work; struct completion work;
int rc; int rc;
ap_init_message(&ap_msg);
ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL); ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
if (!ap_msg.message) if (!ap_msg.message)
return -ENOMEM; return -ENOMEM;
......
This diff is collapsed.
This diff is collapsed.
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