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. ...
This commit is contained in:
commit
67dd2f5a66
@ -3139,6 +3139,7 @@ S: Supported
|
||||
F: Documentation/s390/kvm.txt
|
||||
F: arch/s390/include/asm/kvm*
|
||||
F: arch/s390/kvm/
|
||||
F: drivers/s390/kvm/
|
||||
|
||||
KEXEC
|
||||
M: Eric Biederman <ebiederm@xmission.com>
|
||||
@ -4553,6 +4554,7 @@ L: linux-s390@vger.kernel.org
|
||||
W: http://www.ibm.com/developerworks/linux/linux390/
|
||||
S: Supported
|
||||
F: arch/s390/
|
||||
F: drivers/s390/
|
||||
|
||||
S390 NETWORK DRIVERS
|
||||
M: Ursula Braun <ursula.braun@de.ibm.com>
|
||||
@ -4568,6 +4570,7 @@ M: Felix Beck <felix.beck@de.ibm.com>
|
||||
M: Ralph Wuerthner <ralph.wuerthner@de.ibm.com>
|
||||
M: linux390@de.ibm.com
|
||||
L: linux-s390@vger.kernel.org
|
||||
W: http://www.ibm.com/developerworks/linux/linux390/
|
||||
S: Supported
|
||||
F: drivers/s390/crypto/
|
||||
|
||||
|
@ -220,23 +220,8 @@ config AUDIT_ARCH
|
||||
bool
|
||||
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
|
||||
bool "Data execute protection"
|
||||
select S390_SWITCH_AMODE
|
||||
help
|
||||
This option allows to enable a buffer overflow protection for user
|
||||
space programs and it also selects the addressing mode option above.
|
||||
|
@ -185,7 +185,6 @@ CONFIG_HOTPLUG_CPU=y
|
||||
CONFIG_COMPAT=y
|
||||
CONFIG_SYSVIPC_COMPAT=y
|
||||
CONFIG_AUDIT_ARCH=y
|
||||
CONFIG_S390_SWITCH_AMODE=y
|
||||
CONFIG_S390_EXEC_PROTECT=y
|
||||
|
||||
#
|
||||
|
@ -21,7 +21,7 @@
|
||||
#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 2)
|
||||
|
||||
#define __CS_LOOP(ptr, op_val, op_string) ({ \
|
||||
typeof(ptr->counter) old_val, new_val; \
|
||||
int old_val, new_val; \
|
||||
asm volatile( \
|
||||
" l %0,%2\n" \
|
||||
"0: lr %1,%0\n" \
|
||||
@ -38,7 +38,7 @@
|
||||
#else /* __GNUC__ */
|
||||
|
||||
#define __CS_LOOP(ptr, op_val, op_string) ({ \
|
||||
typeof(ptr->counter) old_val, new_val; \
|
||||
int old_val, new_val; \
|
||||
asm volatile( \
|
||||
" l %0,0(%3)\n" \
|
||||
"0: lr %1,%0\n" \
|
||||
@ -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)
|
||||
|
||||
#define __CSG_LOOP(ptr, op_val, op_string) ({ \
|
||||
typeof(ptr->counter) old_val, new_val; \
|
||||
long long old_val, new_val; \
|
||||
asm volatile( \
|
||||
" lg %0,%2\n" \
|
||||
"0: lgr %1,%0\n" \
|
||||
@ -160,7 +160,7 @@ static inline int atomic_add_unless(atomic_t *v, int a, int u)
|
||||
#else /* __GNUC__ */
|
||||
|
||||
#define __CSG_LOOP(ptr, op_val, op_string) ({ \
|
||||
typeof(ptr->counter) old_val, new_val; \
|
||||
long long old_val, new_val; \
|
||||
asm volatile( \
|
||||
" lg %0,0(%3)\n" \
|
||||
"0: lgr %1,%0\n" \
|
||||
|
@ -142,6 +142,8 @@ struct ccw1;
|
||||
extern int ccw_device_set_options_mask(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);
|
||||
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. */
|
||||
#define CCWDEV_EARLY_NOTIFICATION 0x0001
|
||||
@ -151,6 +153,8 @@ extern void ccw_device_clear_options(struct ccw_device *, unsigned long);
|
||||
#define CCWDEV_DO_PATHGROUP 0x0004
|
||||
/* Allow forced onlining of boxed devices. */
|
||||
#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 *,
|
||||
unsigned long, __u8, unsigned long);
|
||||
|
@ -36,7 +36,7 @@ static inline int init_new_context(struct task_struct *tsk,
|
||||
mm->context.has_pgste = 1;
|
||||
mm->context.alloc_pgste = 1;
|
||||
} else {
|
||||
mm->context.noexec = s390_noexec;
|
||||
mm->context.noexec = (user_mode == SECONDARY_SPACE_MODE);
|
||||
mm->context.has_pgste = 0;
|
||||
mm->context.alloc_pgste = 0;
|
||||
}
|
||||
@ -58,7 +58,7 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
|
||||
pgd_t *pgd = mm->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. */
|
||||
pgd = mm->context.noexec ? get_shadow_table(pgd) : 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)
|
||||
spin_lock_init(&mm->context.list_lock);
|
||||
INIT_LIST_HEAD(&mm->context.crst_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)
|
||||
|
||||
|
@ -169,12 +169,13 @@ extern unsigned long VMALLOC_START;
|
||||
* STL Segment-Table-Length: Segment-table length (STL+1*16 entries -> up to 2048)
|
||||
*
|
||||
* A 64 bit pagetable entry of S390 has following format:
|
||||
* | PFRA |0IP0| OS |
|
||||
* | PFRA |0IPC| OS |
|
||||
* 0000000000111111111122222222223333333333444444444455555555556666
|
||||
* 0123456789012345678901234567890123456789012345678901234567890123
|
||||
*
|
||||
* I Page-Invalid Bit: Page is not available for address-translation
|
||||
* 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:
|
||||
* | P-table origin | TT
|
||||
@ -218,6 +219,7 @@ extern unsigned long VMALLOC_START;
|
||||
*/
|
||||
|
||||
/* 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_INVALID 0x400 /* HW invalid bit */
|
||||
|
||||
|
@ -49,17 +49,12 @@ extern unsigned long memory_end;
|
||||
|
||||
void detect_memory_layout(struct mem_chunk chunk[]);
|
||||
|
||||
#ifdef CONFIG_S390_SWITCH_AMODE
|
||||
extern unsigned int switch_amode;
|
||||
#else
|
||||
#define switch_amode (0)
|
||||
#endif
|
||||
#define PRIMARY_SPACE_MODE 0
|
||||
#define ACCESS_REGISTER_MODE 1
|
||||
#define SECONDARY_SPACE_MODE 2
|
||||
#define HOME_SPACE_MODE 3
|
||||
|
||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||
extern unsigned int s390_noexec;
|
||||
#else
|
||||
#define s390_noexec (0)
|
||||
#endif
|
||||
extern unsigned int user_mode;
|
||||
|
||||
/*
|
||||
* Machine features detected in head.S
|
||||
|
@ -1,57 +1,22 @@
|
||||
/*
|
||||
* include/asm-s390/smp.h
|
||||
*
|
||||
* S390 version
|
||||
* Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation
|
||||
* 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)
|
||||
* Copyright IBM Corp. 1999,2009
|
||||
* Author(s): Denis Joseph Barrow,
|
||||
* Martin Schwidefsky <schwidefsky@de.ibm.com>,
|
||||
* Heiko Carstens <heiko.carstens@de.ibm.com>,
|
||||
*/
|
||||
#ifndef __ASM_SMP_H
|
||||
#define __ASM_SMP_H
|
||||
|
||||
#include <linux/threads.h>
|
||||
#include <linux/cpumask.h>
|
||||
#include <linux/bitops.h>
|
||||
#ifdef CONFIG_SMP
|
||||
|
||||
#if defined(__KERNEL__) && defined(CONFIG_SMP) && !defined(__ASSEMBLY__)
|
||||
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/sigp.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/system.h>
|
||||
|
||||
/*
|
||||
s390 specific smp.c headers
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
int intresting;
|
||||
sigp_ccode ccode;
|
||||
__u32 status;
|
||||
__u16 cpu;
|
||||
} sigp_info;
|
||||
#include <asm/sigp.h>
|
||||
|
||||
extern void machine_restart_smp(char *);
|
||||
extern void machine_halt_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 cpu_logical_map(cpu) (cpu)
|
||||
|
||||
extern int __cpu_disable (void);
|
||||
extern void __cpu_die (unsigned int cpu);
|
||||
@ -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_ipi_mask(const struct cpumask *mask);
|
||||
|
||||
#endif
|
||||
extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
|
||||
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
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; }
|
||||
#endif
|
||||
|
||||
extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
|
||||
#endif
|
||||
#endif /* __ASM_SMP_H */
|
||||
|
@ -1,21 +1,6 @@
|
||||
/*
|
||||
* include/asm-s390/sockios.h
|
||||
*
|
||||
* S390 version
|
||||
*
|
||||
* Derived from "include/asm-i386/sockios.h"
|
||||
*/
|
||||
#ifndef _ASM_S390_SOCKIOS_H
|
||||
#define _ASM_S390_SOCKIOS_H
|
||||
|
||||
#ifndef __ARCH_S390_SOCKIOS__
|
||||
#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) */
|
||||
#include <asm-generic/sockios.h>
|
||||
|
||||
#endif
|
||||
|
@ -1,206 +1,6 @@
|
||||
/*
|
||||
* include/asm-s390/termbits.h
|
||||
*
|
||||
* S390 version
|
||||
*
|
||||
* Derived from "include/asm-i386/termbits.h"
|
||||
*/
|
||||
#ifndef _ASM_S390_TERMBITS_H
|
||||
#define _ASM_S390_TERMBITS_H
|
||||
|
||||
#ifndef __ARCH_S390_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
|
||||
#include <asm-generic/termbits.h>
|
||||
|
||||
#endif
|
||||
|
@ -1,23 +0,0 @@
|
||||
/*
|
||||
* 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;
|
||||
extern struct uaccess_ops uaccess_mvcos_switch;
|
||||
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)
|
||||
{
|
||||
size = uaccess.copy_to_user_small(size, ptr, x);
|
||||
|
@ -44,6 +44,7 @@ obj-$(CONFIG_KPROBES) += kprobes.o
|
||||
obj-$(CONFIG_FUNCTION_TRACER) += $(if $(CONFIG_64BIT),mcount64.o,mcount.o)
|
||||
obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
|
||||
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
|
||||
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
|
||||
|
||||
# Kexec part
|
||||
S390_KEXEC_OBJS := machine_kexec.o crash.o
|
||||
|
@ -31,14 +31,8 @@
|
||||
#include <linux/shm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/uio.h>
|
||||
#include <linux/nfs_fs.h>
|
||||
#include <linux/quota.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/personality.h>
|
||||
#include <linux/stat.h>
|
||||
|
@ -4,10 +4,6 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/socket.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*/
|
||||
/* to a 64 bit pointer */
|
||||
|
@ -83,6 +83,8 @@ startup_continue:
|
||||
slr %r0,%r0 # set cpuid to zero
|
||||
sigp %r1,%r0,0x12 # switch to esame 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
|
||||
lg %r12,.Lparmaddr-.LPG1(%r13) # pointer to parameter area
|
||||
# move IPL device to lowcore
|
||||
@ -127,6 +129,7 @@ startup_continue:
|
||||
.L4malign:.quad 0xffffffffffc00000
|
||||
.Lscan2g:.quad 0x80000000 + 0x20000 - 8 # 2GB + 128K - 8
|
||||
.Lnop: .long 0x07000700
|
||||
.Lzero64:.fill 16,4,0x0
|
||||
#ifdef CONFIG_ZFCPDUMP
|
||||
.Lcurrent_cpu:
|
||||
.long 0x0
|
||||
|
@ -305,9 +305,8 @@ static int __init early_parse_mem(char *p)
|
||||
}
|
||||
early_param("mem", early_parse_mem);
|
||||
|
||||
#ifdef CONFIG_S390_SWITCH_AMODE
|
||||
unsigned int switch_amode = 0;
|
||||
EXPORT_SYMBOL_GPL(switch_amode);
|
||||
unsigned int user_mode = HOME_SPACE_MODE;
|
||||
EXPORT_SYMBOL_GPL(user_mode);
|
||||
|
||||
static int set_amode_and_uaccess(unsigned long user_amode,
|
||||
unsigned long user32_amode)
|
||||
@ -340,23 +339,29 @@ static int set_amode_and_uaccess(unsigned long user_amode,
|
||||
*/
|
||||
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;
|
||||
}
|
||||
early_param("switch_amode", early_parse_switch_amode);
|
||||
|
||||
#else /* CONFIG_S390_SWITCH_AMODE */
|
||||
static inline int set_amode_and_uaccess(unsigned long user_amode,
|
||||
unsigned long user32_amode)
|
||||
static int __init early_parse_user_mode(char *p)
|
||||
{
|
||||
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;
|
||||
}
|
||||
#endif /* CONFIG_S390_SWITCH_AMODE */
|
||||
early_param("user_mode", early_parse_user_mode);
|
||||
|
||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||
unsigned int s390_noexec = 0;
|
||||
EXPORT_SYMBOL_GPL(s390_noexec);
|
||||
|
||||
/*
|
||||
* Enable execute protection?
|
||||
*/
|
||||
@ -364,8 +369,7 @@ static int __init early_parse_noexec(char *p)
|
||||
{
|
||||
if (!strncmp(p, "off", 3))
|
||||
return 0;
|
||||
switch_amode = 1;
|
||||
s390_noexec = 1;
|
||||
user_mode = SECONDARY_SPACE_MODE;
|
||||
return 0;
|
||||
}
|
||||
early_param("noexec", early_parse_noexec);
|
||||
@ -373,7 +377,7 @@ early_param("noexec", early_parse_noexec);
|
||||
|
||||
static void setup_addressing_mode(void)
|
||||
{
|
||||
if (s390_noexec) {
|
||||
if (user_mode == SECONDARY_SPACE_MODE) {
|
||||
if (set_amode_and_uaccess(PSW_ASC_SECONDARY,
|
||||
PSW32_ASC_SECONDARY))
|
||||
pr_info("Execute protection active, "
|
||||
@ -381,7 +385,7 @@ static void setup_addressing_mode(void)
|
||||
else
|
||||
pr_info("Execute protection active, "
|
||||
"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))
|
||||
pr_info("Address spaces switched, "
|
||||
"mvcos available\n");
|
||||
@ -411,7 +415,7 @@ setup_lowcore(void)
|
||||
lc->restart_psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
|
||||
lc->restart_psw.addr =
|
||||
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->external_new_psw.mask = psw_kernel_bits;
|
||||
lc->external_new_psw.addr =
|
||||
|
@ -335,7 +335,7 @@ int get_sync_clock(unsigned long long *clock)
|
||||
sw0 = atomic_read(sw_ptr);
|
||||
*clock = get_clock();
|
||||
sw1 = atomic_read(sw_ptr);
|
||||
put_cpu_var(clock_sync_sync);
|
||||
put_cpu_var(clock_sync_word);
|
||||
if (sw0 == sw1 && (sw0 & 0x80000000U))
|
||||
/* Success: time is in sync. */
|
||||
return 0;
|
||||
@ -385,7 +385,7 @@ static inline int check_sync_clock(void)
|
||||
|
||||
sw_ptr = &get_cpu_var(clock_sync_word);
|
||||
rc = (atomic_read(sw_ptr) & 0x80000000U) != 0;
|
||||
put_cpu_var(clock_sync_sync);
|
||||
put_cpu_var(clock_sync_word);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -86,7 +86,8 @@ static void vdso_init_data(struct vdso_data *vd)
|
||||
unsigned int facility_list;
|
||||
|
||||
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
|
||||
@ -114,7 +115,7 @@ int vdso_alloc_per_cpu(int cpu, struct _lowcore *lowcore)
|
||||
|
||||
lowcore->vdso_per_cpu_data = __LC_PASTE;
|
||||
|
||||
if (!switch_amode || !vdso_enabled)
|
||||
if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
return 0;
|
||||
|
||||
segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER);
|
||||
@ -160,7 +161,7 @@ void vdso_free_per_cpu(int cpu, struct _lowcore *lowcore)
|
||||
unsigned long segment_table, page_table, page_frame;
|
||||
u32 *psal, *aste;
|
||||
|
||||
if (!switch_amode || !vdso_enabled)
|
||||
if (user_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
return;
|
||||
|
||||
psal = (u32 *)(addr_t) lowcore->paste[4];
|
||||
@ -184,7 +185,7 @@ static void __vdso_init_cr5(void *dummy)
|
||||
|
||||
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);
|
||||
}
|
||||
#endif /* CONFIG_64BIT */
|
||||
|
@ -20,7 +20,6 @@ config KVM
|
||||
depends on HAVE_KVM && EXPERIMENTAL
|
||||
select PREEMPT_NOTIFIERS
|
||||
select ANON_INODES
|
||||
select S390_SWITCH_AMODE
|
||||
---help---
|
||||
Support hosting paravirtualized guest machines using the SIE
|
||||
virtualization capability on the mainframe. This should work
|
||||
|
@ -162,7 +162,6 @@ static size_t clear_user_mvcos(size_t size, void __user *to)
|
||||
return size;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_S390_SWITCH_AMODE
|
||||
static size_t strnlen_user_mvcos(size_t count, const char __user *src)
|
||||
{
|
||||
char buf[256];
|
||||
@ -200,7 +199,6 @@ static size_t strncpy_from_user_mvcos(size_t count, const char __user *src,
|
||||
} while ((len_str == len) && (done < count));
|
||||
return done;
|
||||
}
|
||||
#endif /* CONFIG_S390_SWITCH_AMODE */
|
||||
|
||||
struct uaccess_ops uaccess_mvcos = {
|
||||
.copy_from_user = copy_from_user_mvcos_check,
|
||||
@ -215,7 +213,6 @@ struct uaccess_ops uaccess_mvcos = {
|
||||
.futex_atomic_cmpxchg = futex_atomic_cmpxchg_std,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_S390_SWITCH_AMODE
|
||||
struct uaccess_ops uaccess_mvcos_switch = {
|
||||
.copy_from_user = copy_from_user_mvcos,
|
||||
.copy_from_user_small = copy_from_user_mvcos,
|
||||
@ -228,4 +225,3 @@ struct uaccess_ops uaccess_mvcos_switch = {
|
||||
.futex_atomic_op = futex_atomic_op_pt,
|
||||
.futex_atomic_cmpxchg = futex_atomic_cmpxchg_pt,
|
||||
};
|
||||
#endif
|
||||
|
@ -23,86 +23,21 @@ static inline pte_t *follow_table(struct mm_struct *mm, unsigned long addr)
|
||||
|
||||
pgd = pgd_offset(mm, addr);
|
||||
if (pgd_none(*pgd) || unlikely(pgd_bad(*pgd)))
|
||||
return NULL;
|
||||
return (pte_t *) 0x3a;
|
||||
|
||||
pud = pud_offset(pgd, addr);
|
||||
if (pud_none(*pud) || unlikely(pud_bad(*pud)))
|
||||
return NULL;
|
||||
return (pte_t *) 0x3b;
|
||||
|
||||
pmd = pmd_offset(pud, addr);
|
||||
if (pmd_none(*pmd) || unlikely(pmd_bad(*pmd)))
|
||||
return NULL;
|
||||
return (pte_t *) 0x10;
|
||||
|
||||
return pte_offset_map(pmd, addr);
|
||||
}
|
||||
|
||||
static int __handle_fault(struct mm_struct *mm, unsigned long address,
|
||||
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)
|
||||
static __always_inline size_t __user_copy_pt(unsigned long uaddr, void *kptr,
|
||||
size_t n, int write_user)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long offset, pfn, done, size;
|
||||
@ -114,12 +49,17 @@ retry:
|
||||
spin_lock(&mm->page_table_lock);
|
||||
do {
|
||||
pte = follow_table(mm, uaddr);
|
||||
if (!pte || !pte_present(*pte) ||
|
||||
(write_user && !pte_write(*pte)))
|
||||
if ((unsigned long) pte < 0x1000)
|
||||
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;
|
||||
}
|
||||
|
||||
pfn = pte_pfn(*pte);
|
||||
|
||||
offset = uaddr & (PAGE_SIZE - 1);
|
||||
size = min(n - done, PAGE_SIZE - offset);
|
||||
if (write_user) {
|
||||
@ -137,7 +77,7 @@ retry:
|
||||
return n - done;
|
||||
fault:
|
||||
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;
|
||||
goto retry;
|
||||
}
|
||||
@ -146,30 +86,31 @@ fault:
|
||||
* 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.
|
||||
*/
|
||||
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;
|
||||
unsigned long pfn, ret;
|
||||
unsigned long pfn;
|
||||
pte_t *pte;
|
||||
int rc;
|
||||
|
||||
ret = 0;
|
||||
retry:
|
||||
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;
|
||||
}
|
||||
|
||||
pfn = pte_pfn(*pte);
|
||||
ret = (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
|
||||
out:
|
||||
return ret;
|
||||
return (pfn << PAGE_SHIFT) + (uaddr & (PAGE_SIZE - 1));
|
||||
fault:
|
||||
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);
|
||||
if (rc)
|
||||
goto out;
|
||||
goto retry;
|
||||
if (!rc)
|
||||
goto retry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t copy_from_user_pt(size_t n, const void __user *from, void *to)
|
||||
@ -234,8 +175,12 @@ retry:
|
||||
spin_lock(&mm->page_table_lock);
|
||||
do {
|
||||
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;
|
||||
}
|
||||
|
||||
pfn = pte_pfn(*pte);
|
||||
offset = uaddr & (PAGE_SIZE-1);
|
||||
@ -249,9 +194,8 @@ retry:
|
||||
return done + 1;
|
||||
fault:
|
||||
spin_unlock(&mm->page_table_lock);
|
||||
if (__handle_fault(mm, uaddr, 0)) {
|
||||
if (__handle_fault(uaddr, (unsigned long) pte, 0))
|
||||
return 0;
|
||||
}
|
||||
goto retry;
|
||||
}
|
||||
|
||||
@ -284,7 +228,7 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
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_to = (unsigned long) to;
|
||||
pte_t *pte_from, *pte_to;
|
||||
@ -298,17 +242,28 @@ static size_t copy_in_user_pt(size_t n, void __user *to,
|
||||
retry:
|
||||
spin_lock(&mm->page_table_lock);
|
||||
do {
|
||||
write_user = 0;
|
||||
uaddr = uaddr_from;
|
||||
pte_from = follow_table(mm, uaddr_from);
|
||||
if (!pte_from || !pte_present(*pte_from)) {
|
||||
uaddr = uaddr_from;
|
||||
write_user = 0;
|
||||
error_code = (unsigned long) pte_from;
|
||||
if (error_code < 0x1000)
|
||||
goto fault;
|
||||
if (!pte_present(*pte_from)) {
|
||||
error_code = 0x11;
|
||||
goto fault;
|
||||
}
|
||||
|
||||
write_user = 1;
|
||||
uaddr = uaddr_to;
|
||||
pte_to = follow_table(mm, uaddr_to);
|
||||
if (!pte_to || !pte_present(*pte_to) || !pte_write(*pte_to)) {
|
||||
uaddr = uaddr_to;
|
||||
write_user = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -329,7 +284,7 @@ retry:
|
||||
return n - done;
|
||||
fault:
|
||||
spin_unlock(&mm->page_table_lock);
|
||||
if (__handle_fault(mm, uaddr, write_user))
|
||||
if (__handle_fault(uaddr, error_code, write_user))
|
||||
return n - done;
|
||||
goto retry;
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <linux/swap.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/oom.h>
|
||||
#include <linux/suspend.h>
|
||||
|
||||
#include <asm/pgalloc.h>
|
||||
#include <asm/uaccess.h>
|
||||
@ -44,6 +45,7 @@ static volatile long cmm_pages_target;
|
||||
static volatile long cmm_timed_pages_target;
|
||||
static long cmm_timeout_pages;
|
||||
static long cmm_timeout_seconds;
|
||||
static int cmm_suspended;
|
||||
|
||||
static struct cmm_page_array *cmm_page_list;
|
||||
static struct cmm_page_array *cmm_timed_page_list;
|
||||
@ -147,9 +149,9 @@ cmm_thread(void *dummy)
|
||||
|
||||
while (1) {
|
||||
rc = wait_event_interruptible(cmm_thread_wait,
|
||||
(cmm_pages != cmm_pages_target ||
|
||||
cmm_timed_pages != cmm_timed_pages_target ||
|
||||
kthread_should_stop()));
|
||||
(!cmm_suspended && (cmm_pages != cmm_pages_target ||
|
||||
cmm_timed_pages != cmm_timed_pages_target)) ||
|
||||
kthread_should_stop());
|
||||
if (kthread_should_stop() || rc == -ERESTARTSYS) {
|
||||
cmm_pages_target = cmm_pages;
|
||||
cmm_timed_pages_target = cmm_timed_pages;
|
||||
@ -410,6 +412,38 @@ cmm_smsg_target(char *from, char *msg)
|
||||
|
||||
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
|
||||
cmm_init (void)
|
||||
{
|
||||
@ -418,7 +452,7 @@ cmm_init (void)
|
||||
#ifdef CONFIG_CMM_PROC
|
||||
cmm_sysctl_header = register_sysctl_table(cmm_dir_table);
|
||||
if (!cmm_sysctl_header)
|
||||
goto out;
|
||||
goto out_sysctl;
|
||||
#endif
|
||||
#ifdef CONFIG_CMM_IUCV
|
||||
rc = smsg_register_callback(SMSG_PREFIX, cmm_smsg_target);
|
||||
@ -428,17 +462,21 @@ cmm_init (void)
|
||||
rc = register_oom_notifier(&cmm_oom_nb);
|
||||
if (rc < 0)
|
||||
goto out_oom_notify;
|
||||
rc = register_pm_notifier(&cmm_power_notifier);
|
||||
if (rc)
|
||||
goto out_pm;
|
||||
init_waitqueue_head(&cmm_thread_wait);
|
||||
init_timer(&cmm_timer);
|
||||
cmm_thread_ptr = kthread_run(cmm_thread, NULL, "cmmthread");
|
||||
rc = IS_ERR(cmm_thread_ptr) ? PTR_ERR(cmm_thread_ptr) : 0;
|
||||
if (!rc)
|
||||
goto out;
|
||||
/*
|
||||
* kthread_create failed. undo all the stuff from above again.
|
||||
*/
|
||||
unregister_oom_notifier(&cmm_oom_nb);
|
||||
if (rc)
|
||||
goto out_kthread;
|
||||
return 0;
|
||||
|
||||
out_kthread:
|
||||
unregister_pm_notifier(&cmm_power_notifier);
|
||||
out_pm:
|
||||
unregister_oom_notifier(&cmm_oom_nb);
|
||||
out_oom_notify:
|
||||
#ifdef CONFIG_CMM_IUCV
|
||||
smsg_unregister_callback(SMSG_PREFIX, cmm_smsg_target);
|
||||
@ -446,8 +484,8 @@ out_smsg:
|
||||
#endif
|
||||
#ifdef CONFIG_CMM_PROC
|
||||
unregister_sysctl_table(cmm_sysctl_header);
|
||||
out_sysctl:
|
||||
#endif
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -455,6 +493,7 @@ static void
|
||||
cmm_exit(void)
|
||||
{
|
||||
kthread_stop(cmm_thread_ptr);
|
||||
unregister_pm_notifier(&cmm_power_notifier);
|
||||
unregister_oom_notifier(&cmm_oom_nb);
|
||||
cmm_free_pages(cmm_pages, &cmm_pages, &cmm_page_list);
|
||||
cmm_free_pages(cmm_timed_pages, &cmm_timed_pages, &cmm_timed_page_list);
|
||||
|
@ -34,16 +34,15 @@
|
||||
#include <asm/pgtable.h>
|
||||
#include <asm/s390_ext.h>
|
||||
#include <asm/mmu_context.h>
|
||||
#include <asm/compat.h>
|
||||
#include "../kernel/entry.h"
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
#define __FAIL_ADDR_MASK 0x7ffff000
|
||||
#define __FIXUP_MASK 0x7fffffff
|
||||
#define __SUBCODE_MASK 0x0200
|
||||
#define __PF_RES_FIELD 0ULL
|
||||
#else /* CONFIG_64BIT */
|
||||
#define __FAIL_ADDR_MASK -4096L
|
||||
#define __FIXUP_MASK ~0L
|
||||
#define __SUBCODE_MASK 0x0600
|
||||
#define __PF_RES_FIELD 0x8000000000000000ULL
|
||||
#endif /* CONFIG_64BIT */
|
||||
@ -52,11 +51,15 @@
|
||||
extern int sysctl_userprocess_debug;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
static inline int notify_page_fault(struct pt_regs *regs, long err)
|
||||
#define VM_FAULT_BADCONTEXT 0x010000
|
||||
#define VM_FAULT_BADMAP 0x020000
|
||||
#define VM_FAULT_BADACCESS 0x040000
|
||||
|
||||
static inline int notify_page_fault(struct pt_regs *regs)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
/* kprobe_running() needs smp_processor_id() */
|
||||
if (!user_mode(regs)) {
|
||||
preempt_disable();
|
||||
@ -64,15 +67,9 @@ static inline int notify_page_fault(struct pt_regs *regs, long err)
|
||||
ret = 1;
|
||||
preempt_enable();
|
||||
}
|
||||
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static inline int notify_page_fault(struct pt_regs *regs, long err)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
@ -100,57 +97,50 @@ void bust_spinlocks(int yes)
|
||||
|
||||
/*
|
||||
* Returns the address space associated with the fault.
|
||||
* Returns 0 for kernel space, 1 for user space and
|
||||
* 2 for code execution in user space with noexec=on.
|
||||
* Returns 0 for kernel space and 1 for user space.
|
||||
*/
|
||||
static inline int check_space(struct task_struct *tsk)
|
||||
static inline int user_space_fault(unsigned long trans_exc_code)
|
||||
{
|
||||
/*
|
||||
* The lowest two bits of S390_lowcore.trans_exc_code
|
||||
* indicate which paging table was used.
|
||||
* The lowest two bits of the translation exception
|
||||
* identification indicate which paging table was used.
|
||||
*/
|
||||
int desc = S390_lowcore.trans_exc_code & 3;
|
||||
|
||||
if (desc == 3) /* Home Segment Table Descriptor */
|
||||
return switch_amode == 0;
|
||||
if (desc == 2) /* Secondary Segment Table Descriptor */
|
||||
return tsk->thread.mm_segment.ar4;
|
||||
#ifdef CONFIG_S390_SWITCH_AMODE
|
||||
if (unlikely(desc == 1)) { /* STD determined via access register */
|
||||
/* %a0 always indicates primary space. */
|
||||
if (S390_lowcore.exc_access_id != 0) {
|
||||
save_access_regs(tsk->thread.acrs);
|
||||
/*
|
||||
* An alet of 0 indicates primary space.
|
||||
* An alet of 1 indicates secondary space.
|
||||
* Any other alet values generate an
|
||||
* alen-translation exception.
|
||||
*/
|
||||
if (tsk->thread.acrs[S390_lowcore.exc_access_id])
|
||||
return tsk->thread.mm_segment.ar4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
/* Primary Segment Table Descriptor */
|
||||
return switch_amode << s390_noexec;
|
||||
trans_exc_code &= 3;
|
||||
if (trans_exc_code == 2)
|
||||
/* Access via secondary space, set_fs setting decides */
|
||||
return current->thread.mm_segment.ar4;
|
||||
if (user_mode == HOME_SPACE_MODE)
|
||||
/* User space if the access has been done via home space. */
|
||||
return trans_exc_code == 3;
|
||||
/*
|
||||
* If the user space is not the home space the kernel runs in home
|
||||
* space. Access via secondary space has already been covered,
|
||||
* access via primary space or access register is from user space
|
||||
* and access via home space is from the kernel.
|
||||
*/
|
||||
return trans_exc_code != 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Send SIGSEGV to task. This is an external routine
|
||||
* to keep the stack usage of do_page_fault small.
|
||||
*/
|
||||
static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
|
||||
int si_code, unsigned long address)
|
||||
static noinline void do_sigsegv(struct pt_regs *regs, long int_code,
|
||||
int si_code, unsigned long trans_exc_code)
|
||||
{
|
||||
struct siginfo si;
|
||||
unsigned long address;
|
||||
|
||||
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
current->thread.prot_addr = address;
|
||||
current->thread.trap_no = int_code;
|
||||
#if defined(CONFIG_SYSCTL) || defined(CONFIG_PROCESS_DEBUG)
|
||||
#if defined(CONFIG_SYSCTL)
|
||||
if (sysctl_userprocess_debug)
|
||||
#endif
|
||||
{
|
||||
printk("User process fault: interruption code 0x%lX\n",
|
||||
error_code);
|
||||
int_code);
|
||||
printk("failing address: %lX\n", address);
|
||||
show_regs(regs);
|
||||
}
|
||||
@ -161,13 +151,14 @@ static void do_sigsegv(struct pt_regs *regs, unsigned long error_code,
|
||||
force_sig_info(SIGSEGV, &si, current);
|
||||
}
|
||||
|
||||
static void do_no_context(struct pt_regs *regs, unsigned long error_code,
|
||||
unsigned long address)
|
||||
static noinline void do_no_context(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
const struct exception_table_entry *fixup;
|
||||
unsigned long address;
|
||||
|
||||
/* Are we prepared to handle this kernel fault? */
|
||||
fixup = search_exception_tables(regs->psw.addr & __FIXUP_MASK);
|
||||
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
|
||||
if (fixup) {
|
||||
regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
|
||||
return;
|
||||
@ -177,129 +168,149 @@ static void do_no_context(struct pt_regs *regs, unsigned long error_code,
|
||||
* Oops. The kernel tried to access some bad page. We'll have to
|
||||
* terminate things with extreme prejudice.
|
||||
*/
|
||||
if (check_space(current) == 0)
|
||||
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
if (!user_space_fault(trans_exc_code))
|
||||
printk(KERN_ALERT "Unable to handle kernel pointer dereference"
|
||||
" at virtual kernel address %p\n", (void *)address);
|
||||
else
|
||||
printk(KERN_ALERT "Unable to handle kernel paging request"
|
||||
" at virtual user address %p\n", (void *)address);
|
||||
|
||||
die("Oops", regs, error_code);
|
||||
die("Oops", regs, int_code);
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
static void do_low_address(struct pt_regs *regs, unsigned long error_code)
|
||||
static noinline void do_low_address(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
/* Low-address protection hit in kernel mode means
|
||||
NULL pointer write access in kernel mode. */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
/* Low-address protection hit in user mode 'cannot happen'. */
|
||||
die ("Low-address protection", regs, error_code);
|
||||
die ("Low-address protection", regs, int_code);
|
||||
do_exit(SIGKILL);
|
||||
}
|
||||
|
||||
do_no_context(regs, error_code, 0);
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
}
|
||||
|
||||
static void do_sigbus(struct pt_regs *regs, unsigned long error_code,
|
||||
unsigned long address)
|
||||
static noinline void do_sigbus(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
struct mm_struct *mm = tsk->mm;
|
||||
|
||||
up_read(&mm->mmap_sem);
|
||||
/*
|
||||
* Send a sigbus, regardless of whether we were in kernel
|
||||
* or user mode.
|
||||
*/
|
||||
tsk->thread.prot_addr = address;
|
||||
tsk->thread.trap_no = error_code;
|
||||
tsk->thread.prot_addr = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
tsk->thread.trap_no = int_code;
|
||||
force_sig(SIGBUS, tsk);
|
||||
|
||||
/* Kernel mode? Handle exceptions or die */
|
||||
if (!(regs->psw.mask & PSW_MASK_PSTATE))
|
||||
do_no_context(regs, error_code, address);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||
static int signal_return(struct mm_struct *mm, struct pt_regs *regs,
|
||||
unsigned long address, unsigned long error_code)
|
||||
static noinline int signal_return(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
u16 instruction;
|
||||
int rc;
|
||||
#ifdef CONFIG_COMPAT
|
||||
int compat;
|
||||
#endif
|
||||
|
||||
pagefault_disable();
|
||||
rc = __get_user(instruction, (u16 __user *) regs->psw.addr);
|
||||
pagefault_enable();
|
||||
if (rc)
|
||||
return -EFAULT;
|
||||
|
||||
up_read(&mm->mmap_sem);
|
||||
clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
|
||||
#ifdef CONFIG_COMPAT
|
||||
compat = is_compat_task();
|
||||
if (compat && instruction == 0x0a77)
|
||||
sys32_sigreturn();
|
||||
else if (compat && instruction == 0x0aad)
|
||||
sys32_rt_sigreturn();
|
||||
else
|
||||
#endif
|
||||
if (instruction == 0x0a77)
|
||||
sys_sigreturn();
|
||||
else if (instruction == 0x0aad)
|
||||
sys_rt_sigreturn();
|
||||
else {
|
||||
current->thread.prot_addr = address;
|
||||
current->thread.trap_no = error_code;
|
||||
do_sigsegv(regs, error_code, SEGV_MAPERR, address);
|
||||
}
|
||||
if (!rc && instruction == 0x0a77) {
|
||||
clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
|
||||
if (is_compat_task())
|
||||
sys32_sigreturn();
|
||||
else
|
||||
sys_sigreturn();
|
||||
} else if (!rc && instruction == 0x0aad) {
|
||||
clear_tsk_thread_flag(current, TIF_SINGLE_STEP);
|
||||
if (is_compat_task())
|
||||
sys32_rt_sigreturn();
|
||||
else
|
||||
sys_rt_sigreturn();
|
||||
} else
|
||||
do_sigsegv(regs, int_code, SEGV_MAPERR, trans_exc_code);
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_S390_EXEC_PROTECT */
|
||||
|
||||
static noinline void do_fault_error(struct pt_regs *regs, long int_code,
|
||||
unsigned long trans_exc_code, int fault)
|
||||
{
|
||||
int si_code;
|
||||
|
||||
switch (fault) {
|
||||
case VM_FAULT_BADACCESS:
|
||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||
if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_SECONDARY &&
|
||||
(trans_exc_code & 3) == 0) {
|
||||
signal_return(regs, int_code, trans_exc_code);
|
||||
break;
|
||||
}
|
||||
#endif /* CONFIG_S390_EXEC_PROTECT */
|
||||
case VM_FAULT_BADMAP:
|
||||
/* Bad memory access. Check if it is kernel or user space. */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
/* User mode accesses just cause a SIGSEGV */
|
||||
si_code = (fault == VM_FAULT_BADMAP) ?
|
||||
SEGV_MAPERR : SEGV_ACCERR;
|
||||
do_sigsegv(regs, int_code, si_code, trans_exc_code);
|
||||
return;
|
||||
}
|
||||
case VM_FAULT_BADCONTEXT:
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
break;
|
||||
default: /* fault & VM_FAULT_ERROR */
|
||||
if (fault & VM_FAULT_OOM)
|
||||
pagefault_out_of_memory();
|
||||
else if (fault & VM_FAULT_SIGBUS) {
|
||||
do_sigbus(regs, int_code, trans_exc_code);
|
||||
/* Kernel mode? Handle exceptions or die */
|
||||
if (!(regs->psw.mask & PSW_MASK_PSTATE))
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
} else
|
||||
BUG();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* This routine handles page faults. It determines the address,
|
||||
* and the problem, and then passes it off to one of the appropriate
|
||||
* routines.
|
||||
*
|
||||
* error_code:
|
||||
* interruption code (int_code):
|
||||
* 04 Protection -> Write-Protection (suprression)
|
||||
* 10 Segment translation -> Not present (nullification)
|
||||
* 11 Page translation -> Not present (nullification)
|
||||
* 3b Region third trans. -> Not present (nullification)
|
||||
*/
|
||||
static inline void
|
||||
do_exception(struct pt_regs *regs, unsigned long error_code, int write)
|
||||
static inline int do_exception(struct pt_regs *regs, int access,
|
||||
unsigned long trans_exc_code)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
struct mm_struct *mm;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long address;
|
||||
int space;
|
||||
int si_code;
|
||||
int fault;
|
||||
|
||||
if (notify_page_fault(regs, error_code))
|
||||
return;
|
||||
if (notify_page_fault(regs))
|
||||
return 0;
|
||||
|
||||
tsk = current;
|
||||
mm = tsk->mm;
|
||||
|
||||
/* get the failing address and the affected space */
|
||||
address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
|
||||
space = check_space(tsk);
|
||||
|
||||
/*
|
||||
* Verify that the fault happened in user space, that
|
||||
* we are not in an interrupt and that there is a
|
||||
* user context.
|
||||
*/
|
||||
if (unlikely(space == 0 || in_atomic() || !mm))
|
||||
goto no_context;
|
||||
fault = VM_FAULT_BADCONTEXT;
|
||||
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
|
||||
goto out;
|
||||
|
||||
address = trans_exc_code & __FAIL_ADDR_MASK;
|
||||
/*
|
||||
* When we get here, the fault happened in the current
|
||||
* task's user address space, so we can switch on the
|
||||
@ -309,42 +320,26 @@ do_exception(struct pt_regs *regs, unsigned long error_code, int write)
|
||||
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address);
|
||||
down_read(&mm->mmap_sem);
|
||||
|
||||
si_code = SEGV_MAPERR;
|
||||
fault = VM_FAULT_BADMAP;
|
||||
vma = find_vma(mm, address);
|
||||
if (!vma)
|
||||
goto bad_area;
|
||||
goto out_up;
|
||||
|
||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||
if (unlikely((space == 2) && !(vma->vm_flags & VM_EXEC)))
|
||||
if (!signal_return(mm, regs, address, error_code))
|
||||
/*
|
||||
* signal_return() has done an up_read(&mm->mmap_sem)
|
||||
* if it returns 0.
|
||||
*/
|
||||
return;
|
||||
#endif
|
||||
|
||||
if (vma->vm_start <= address)
|
||||
goto good_area;
|
||||
if (!(vma->vm_flags & VM_GROWSDOWN))
|
||||
goto bad_area;
|
||||
if (expand_stack(vma, address))
|
||||
goto bad_area;
|
||||
/*
|
||||
* Ok, we have a good vm_area for this memory access, so
|
||||
* we can handle it..
|
||||
*/
|
||||
good_area:
|
||||
si_code = SEGV_ACCERR;
|
||||
if (!write) {
|
||||
/* page not present, check vm flags */
|
||||
if (!(vma->vm_flags & (VM_READ | VM_EXEC | VM_WRITE)))
|
||||
goto bad_area;
|
||||
} else {
|
||||
if (!(vma->vm_flags & VM_WRITE))
|
||||
goto bad_area;
|
||||
if (unlikely(vma->vm_start > address)) {
|
||||
if (!(vma->vm_flags & VM_GROWSDOWN))
|
||||
goto out_up;
|
||||
if (expand_stack(vma, address))
|
||||
goto out_up;
|
||||
}
|
||||
|
||||
/*
|
||||
* Ok, we have a good vm_area for this memory access, so
|
||||
* we can handle it..
|
||||
*/
|
||||
fault = VM_FAULT_BADACCESS;
|
||||
if (unlikely(!(vma->vm_flags & access)))
|
||||
goto out_up;
|
||||
|
||||
if (is_vm_hugetlb_page(vma))
|
||||
address &= HPAGE_MASK;
|
||||
/*
|
||||
@ -352,18 +347,11 @@ good_area:
|
||||
* make sure we exit gracefully rather than endlessly redo
|
||||
* the fault.
|
||||
*/
|
||||
fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0);
|
||||
if (unlikely(fault & VM_FAULT_ERROR)) {
|
||||
if (fault & VM_FAULT_OOM) {
|
||||
up_read(&mm->mmap_sem);
|
||||
pagefault_out_of_memory();
|
||||
return;
|
||||
} else if (fault & VM_FAULT_SIGBUS) {
|
||||
do_sigbus(regs, error_code, address);
|
||||
return;
|
||||
}
|
||||
BUG();
|
||||
}
|
||||
fault = handle_mm_fault(mm, vma, address,
|
||||
(access == VM_WRITE) ? FAULT_FLAG_WRITE : 0);
|
||||
if (unlikely(fault & VM_FAULT_ERROR))
|
||||
goto out_up;
|
||||
|
||||
if (fault & VM_FAULT_MAJOR) {
|
||||
tsk->maj_flt++;
|
||||
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0,
|
||||
@ -373,74 +361,69 @@ good_area:
|
||||
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0,
|
||||
regs, address);
|
||||
}
|
||||
up_read(&mm->mmap_sem);
|
||||
/*
|
||||
* The instruction that caused the program check will
|
||||
* be repeated. Don't signal single step via SIGTRAP.
|
||||
*/
|
||||
clear_tsk_thread_flag(tsk, TIF_SINGLE_STEP);
|
||||
return;
|
||||
|
||||
/*
|
||||
* Something tried to access memory that isn't in our memory map..
|
||||
* Fix it, but check if it's kernel or user first..
|
||||
*/
|
||||
bad_area:
|
||||
fault = 0;
|
||||
out_up:
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
/* User mode accesses just cause a SIGSEGV */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
tsk->thread.prot_addr = address;
|
||||
tsk->thread.trap_no = error_code;
|
||||
do_sigsegv(regs, error_code, si_code, address);
|
||||
return;
|
||||
}
|
||||
|
||||
no_context:
|
||||
do_no_context(regs, error_code, address);
|
||||
out:
|
||||
return fault;
|
||||
}
|
||||
|
||||
void __kprobes do_protection_exception(struct pt_regs *regs,
|
||||
long error_code)
|
||||
void __kprobes do_protection_exception(struct pt_regs *regs, long int_code)
|
||||
{
|
||||
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||
int fault;
|
||||
|
||||
/* Protection exception is supressing, decrement psw address. */
|
||||
regs->psw.addr -= (error_code >> 16);
|
||||
regs->psw.addr -= (int_code >> 16);
|
||||
/*
|
||||
* Check for low-address protection. This needs to be treated
|
||||
* as a special case because the translation exception code
|
||||
* field is not guaranteed to contain valid data in this case.
|
||||
*/
|
||||
if (unlikely(!(S390_lowcore.trans_exc_code & 4))) {
|
||||
do_low_address(regs, error_code);
|
||||
if (unlikely(!(trans_exc_code & 4))) {
|
||||
do_low_address(regs, int_code, trans_exc_code);
|
||||
return;
|
||||
}
|
||||
do_exception(regs, 4, 1);
|
||||
fault = do_exception(regs, VM_WRITE, trans_exc_code);
|
||||
if (unlikely(fault))
|
||||
do_fault_error(regs, 4, trans_exc_code, fault);
|
||||
}
|
||||
|
||||
void __kprobes do_dat_exception(struct pt_regs *regs, long error_code)
|
||||
void __kprobes do_dat_exception(struct pt_regs *regs, long int_code)
|
||||
{
|
||||
do_exception(regs, error_code & 0xff, 0);
|
||||
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||
int access, fault;
|
||||
|
||||
access = VM_READ | VM_EXEC | VM_WRITE;
|
||||
#ifdef CONFIG_S390_EXEC_PROTECT
|
||||
if ((regs->psw.mask & PSW_MASK_ASC) == PSW_ASC_SECONDARY &&
|
||||
(trans_exc_code & 3) == 0)
|
||||
access = VM_EXEC;
|
||||
#endif
|
||||
fault = do_exception(regs, access, trans_exc_code);
|
||||
if (unlikely(fault))
|
||||
do_fault_error(regs, int_code & 255, trans_exc_code, fault);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
|
||||
void __kprobes do_asce_exception(struct pt_regs *regs, long int_code)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
unsigned long trans_exc_code = S390_lowcore.trans_exc_code;
|
||||
struct mm_struct *mm = current->mm;
|
||||
struct vm_area_struct *vma;
|
||||
unsigned long address;
|
||||
int space;
|
||||
|
||||
mm = current->mm;
|
||||
address = S390_lowcore.trans_exc_code & __FAIL_ADDR_MASK;
|
||||
space = check_space(current);
|
||||
|
||||
if (unlikely(space == 0 || in_atomic() || !mm))
|
||||
if (unlikely(!user_space_fault(trans_exc_code) || in_atomic() || !mm))
|
||||
goto no_context;
|
||||
|
||||
local_irq_enable();
|
||||
|
||||
down_read(&mm->mmap_sem);
|
||||
vma = find_vma(mm, address);
|
||||
vma = find_vma(mm, trans_exc_code & __FAIL_ADDR_MASK);
|
||||
up_read(&mm->mmap_sem);
|
||||
|
||||
if (vma) {
|
||||
@ -450,17 +433,38 @@ void __kprobes do_asce_exception(struct pt_regs *regs, unsigned long error_code)
|
||||
|
||||
/* User mode accesses just cause a SIGSEGV */
|
||||
if (regs->psw.mask & PSW_MASK_PSTATE) {
|
||||
current->thread.prot_addr = address;
|
||||
current->thread.trap_no = error_code;
|
||||
do_sigsegv(regs, error_code, SEGV_MAPERR, address);
|
||||
do_sigsegv(regs, int_code, SEGV_MAPERR, trans_exc_code);
|
||||
return;
|
||||
}
|
||||
|
||||
no_context:
|
||||
do_no_context(regs, error_code, address);
|
||||
do_no_context(regs, int_code, trans_exc_code);
|
||||
}
|
||||
#endif
|
||||
|
||||
int __handle_fault(unsigned long uaddr, unsigned long int_code, int write_user)
|
||||
{
|
||||
struct pt_regs regs;
|
||||
int access, fault;
|
||||
|
||||
regs.psw.mask = psw_kernel_bits;
|
||||
if (!irqs_disabled())
|
||||
regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT;
|
||||
regs.psw.addr = (unsigned long) __builtin_return_address(0);
|
||||
regs.psw.addr |= PSW_ADDR_AMODE;
|
||||
uaddr &= PAGE_MASK;
|
||||
access = write_user ? VM_WRITE : VM_READ;
|
||||
fault = do_exception(®s, access, uaddr | 2);
|
||||
if (unlikely(fault)) {
|
||||
if (fault & VM_FAULT_OOM) {
|
||||
pagefault_out_of_memory();
|
||||
fault = 0;
|
||||
} else if (fault & VM_FAULT_SIGBUS)
|
||||
do_sigbus(®s, int_code, uaddr);
|
||||
}
|
||||
return fault ? -EFAULT : 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PFAULT
|
||||
/*
|
||||
* 'pfault' pseudo page faults routines.
|
||||
@ -522,7 +526,7 @@ void pfault_fini(void)
|
||||
: : "a" (&refbk), "m" (refbk) : "cc");
|
||||
}
|
||||
|
||||
static void pfault_interrupt(__u16 error_code)
|
||||
static void pfault_interrupt(__u16 int_code)
|
||||
{
|
||||
struct task_struct *tsk;
|
||||
__u16 subcode;
|
||||
|
@ -269,7 +269,7 @@ int s390_enable_sie(void)
|
||||
struct mm_struct *mm, *old_mm;
|
||||
|
||||
/* Do we have switched amode? If no, we cannot do sie */
|
||||
if (!switch_amode)
|
||||
if (user_mode == HOME_SPACE_MODE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Do we have pgstes? if yes, we are done */
|
||||
|
@ -70,8 +70,12 @@ static pte_t __ref *vmem_pte_alloc(void)
|
||||
pte = alloc_bootmem(PTRS_PER_PTE * sizeof(pte_t));
|
||||
if (!pte)
|
||||
return NULL;
|
||||
clear_table((unsigned long *) pte, _PAGE_TYPE_EMPTY,
|
||||
PTRS_PER_PTE * sizeof(pte_t));
|
||||
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,
|
||||
PTRS_PER_PTE * sizeof(pte_t));
|
||||
return pte;
|
||||
}
|
||||
|
||||
@ -112,7 +116,8 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
|
||||
if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
|
||||
(address + HPAGE_SIZE <= start + 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);
|
||||
address += HPAGE_SIZE - PAGE_SIZE;
|
||||
continue;
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <asm/ccwdev.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/idals.h>
|
||||
#include <asm/todclk.h>
|
||||
#include <asm/itcw.h>
|
||||
|
||||
/* This is ugly... */
|
||||
@ -64,6 +63,7 @@ static void do_restore_device(struct work_struct *);
|
||||
static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
|
||||
static void dasd_device_timeout(unsigned long);
|
||||
static void dasd_block_timeout(unsigned long);
|
||||
static void __dasd_process_erp(struct dasd_device *, struct dasd_ccw_req *);
|
||||
|
||||
/*
|
||||
* SECTION: Operations on the device structure.
|
||||
@ -960,7 +960,7 @@ static void dasd_device_timeout(unsigned long ptr)
|
||||
device = (struct dasd_device *) ptr;
|
||||
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
|
||||
/* re-activate request queue */
|
||||
device->stopped &= ~DASD_STOPPED_PENDING;
|
||||
dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
|
||||
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
|
||||
dasd_schedule_device_bh(device);
|
||||
}
|
||||
@ -994,10 +994,9 @@ static void dasd_handle_killed_request(struct ccw_device *cdev,
|
||||
return;
|
||||
cqr = (struct dasd_ccw_req *) intparm;
|
||||
if (cqr->status != DASD_CQR_IN_IO) {
|
||||
DBF_EVENT(DBF_DEBUG,
|
||||
"invalid status in handle_killed_request: "
|
||||
"bus_id %s, status %02x",
|
||||
dev_name(&cdev->dev), cqr->status);
|
||||
DBF_EVENT_DEVID(DBF_DEBUG, cdev,
|
||||
"invalid status in handle_killed_request: "
|
||||
"%02x", cqr->status);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1023,7 +1022,7 @@ void dasd_generic_handle_state_change(struct dasd_device *device)
|
||||
/* First of all start sense subsystem status request. */
|
||||
dasd_eer_snss(device);
|
||||
|
||||
device->stopped &= ~DASD_STOPPED_PENDING;
|
||||
dasd_device_remove_stop_bits(device, DASD_STOPPED_PENDING);
|
||||
dasd_schedule_device_bh(device);
|
||||
if (device->block)
|
||||
dasd_schedule_block_bh(device->block);
|
||||
@ -1045,12 +1044,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
|
||||
case -EIO:
|
||||
break;
|
||||
case -ETIMEDOUT:
|
||||
DBF_EVENT(DBF_WARNING, "%s(%s): request timed out\n",
|
||||
__func__, dev_name(&cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
|
||||
"request timed out\n", __func__);
|
||||
break;
|
||||
default:
|
||||
DBF_EVENT(DBF_WARNING, "%s(%s): unknown error %ld\n",
|
||||
__func__, dev_name(&cdev->dev), PTR_ERR(irb));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s: "
|
||||
"unknown error %ld\n", __func__,
|
||||
PTR_ERR(irb));
|
||||
}
|
||||
dasd_handle_killed_request(cdev, intparm);
|
||||
return;
|
||||
@ -1405,6 +1405,20 @@ void dasd_schedule_device_bh(struct dasd_device *device)
|
||||
tasklet_hi_schedule(&device->tasklet);
|
||||
}
|
||||
|
||||
void dasd_device_set_stop_bits(struct dasd_device *device, int bits)
|
||||
{
|
||||
device->stopped |= bits;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dasd_device_set_stop_bits);
|
||||
|
||||
void dasd_device_remove_stop_bits(struct dasd_device *device, int bits)
|
||||
{
|
||||
device->stopped &= ~bits;
|
||||
if (!device->stopped)
|
||||
wake_up(&generic_waitq);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(dasd_device_remove_stop_bits);
|
||||
|
||||
/*
|
||||
* Queue a request to the head of the device ccw_queue.
|
||||
* Start the I/O if possible.
|
||||
@ -1464,29 +1478,127 @@ static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* checks if error recovery is necessary, returns 1 if yes, 0 otherwise.
|
||||
*/
|
||||
static int __dasd_sleep_on_erp(struct dasd_ccw_req *cqr)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
dasd_erp_fn_t erp_fn;
|
||||
|
||||
if (cqr->status == DASD_CQR_FILLED)
|
||||
return 0;
|
||||
device = cqr->startdev;
|
||||
if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
|
||||
if (cqr->status == DASD_CQR_TERMINATED) {
|
||||
device->discipline->handle_terminated_request(cqr);
|
||||
return 1;
|
||||
}
|
||||
if (cqr->status == DASD_CQR_NEED_ERP) {
|
||||
erp_fn = device->discipline->erp_action(cqr);
|
||||
erp_fn(cqr);
|
||||
return 1;
|
||||
}
|
||||
if (cqr->status == DASD_CQR_FAILED)
|
||||
dasd_log_sense(cqr, &cqr->irb);
|
||||
if (cqr->refers) {
|
||||
__dasd_process_erp(device, cqr);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __dasd_sleep_on_loop_condition(struct dasd_ccw_req *cqr)
|
||||
{
|
||||
if (test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) {
|
||||
if (cqr->refers) /* erp is not done yet */
|
||||
return 1;
|
||||
return ((cqr->status != DASD_CQR_DONE) &&
|
||||
(cqr->status != DASD_CQR_FAILED));
|
||||
} else
|
||||
return (cqr->status == DASD_CQR_FILLED);
|
||||
}
|
||||
|
||||
static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
int rc;
|
||||
struct list_head ccw_queue;
|
||||
struct dasd_ccw_req *cqr;
|
||||
|
||||
INIT_LIST_HEAD(&ccw_queue);
|
||||
maincqr->status = DASD_CQR_FILLED;
|
||||
device = maincqr->startdev;
|
||||
list_add(&maincqr->blocklist, &ccw_queue);
|
||||
for (cqr = maincqr; __dasd_sleep_on_loop_condition(cqr);
|
||||
cqr = list_first_entry(&ccw_queue,
|
||||
struct dasd_ccw_req, blocklist)) {
|
||||
|
||||
if (__dasd_sleep_on_erp(cqr))
|
||||
continue;
|
||||
if (cqr->status != DASD_CQR_FILLED) /* could be failed */
|
||||
continue;
|
||||
|
||||
/* Non-temporary stop condition will trigger fail fast */
|
||||
if (device->stopped & ~DASD_STOPPED_PENDING &&
|
||||
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
|
||||
(!dasd_eer_enabled(device))) {
|
||||
cqr->status = DASD_CQR_FAILED;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Don't try to start requests if device is stopped */
|
||||
if (interruptible) {
|
||||
rc = wait_event_interruptible(
|
||||
generic_waitq, !(device->stopped));
|
||||
if (rc == -ERESTARTSYS) {
|
||||
cqr->status = DASD_CQR_FAILED;
|
||||
maincqr->intrc = rc;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
wait_event(generic_waitq, !(device->stopped));
|
||||
|
||||
cqr->callback = dasd_wakeup_cb;
|
||||
cqr->callback_data = (void *) &generic_waitq;
|
||||
dasd_add_request_tail(cqr);
|
||||
if (interruptible) {
|
||||
rc = wait_event_interruptible(
|
||||
generic_waitq, _wait_for_wakeup(cqr));
|
||||
if (rc == -ERESTARTSYS) {
|
||||
dasd_cancel_req(cqr);
|
||||
/* wait (non-interruptible) for final status */
|
||||
wait_event(generic_waitq,
|
||||
_wait_for_wakeup(cqr));
|
||||
cqr->status = DASD_CQR_FAILED;
|
||||
maincqr->intrc = rc;
|
||||
continue;
|
||||
}
|
||||
} else
|
||||
wait_event(generic_waitq, _wait_for_wakeup(cqr));
|
||||
}
|
||||
|
||||
maincqr->endclk = get_clock();
|
||||
if ((maincqr->status != DASD_CQR_DONE) &&
|
||||
(maincqr->intrc != -ERESTARTSYS))
|
||||
dasd_log_sense(maincqr, &maincqr->irb);
|
||||
if (maincqr->status == DASD_CQR_DONE)
|
||||
rc = 0;
|
||||
else if (maincqr->intrc)
|
||||
rc = maincqr->intrc;
|
||||
else
|
||||
rc = -EIO;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Queue a request to the tail of the device ccw_queue and wait for
|
||||
* it's completion.
|
||||
*/
|
||||
int dasd_sleep_on(struct dasd_ccw_req *cqr)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
int rc;
|
||||
|
||||
device = cqr->startdev;
|
||||
|
||||
cqr->callback = dasd_wakeup_cb;
|
||||
cqr->callback_data = (void *) &generic_waitq;
|
||||
dasd_add_request_tail(cqr);
|
||||
wait_event(generic_waitq, _wait_for_wakeup(cqr));
|
||||
|
||||
if (cqr->status == DASD_CQR_DONE)
|
||||
rc = 0;
|
||||
else if (cqr->intrc)
|
||||
rc = cqr->intrc;
|
||||
else
|
||||
rc = -EIO;
|
||||
return rc;
|
||||
return _dasd_sleep_on(cqr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1495,28 +1607,7 @@ int dasd_sleep_on(struct dasd_ccw_req *cqr)
|
||||
*/
|
||||
int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
int rc;
|
||||
|
||||
device = cqr->startdev;
|
||||
cqr->callback = dasd_wakeup_cb;
|
||||
cqr->callback_data = (void *) &generic_waitq;
|
||||
dasd_add_request_tail(cqr);
|
||||
rc = wait_event_interruptible(generic_waitq, _wait_for_wakeup(cqr));
|
||||
if (rc == -ERESTARTSYS) {
|
||||
dasd_cancel_req(cqr);
|
||||
/* wait (non-interruptible) for final status */
|
||||
wait_event(generic_waitq, _wait_for_wakeup(cqr));
|
||||
cqr->intrc = rc;
|
||||
}
|
||||
|
||||
if (cqr->status == DASD_CQR_DONE)
|
||||
rc = 0;
|
||||
else if (cqr->intrc)
|
||||
rc = cqr->intrc;
|
||||
else
|
||||
rc = -EIO;
|
||||
return rc;
|
||||
return _dasd_sleep_on(cqr, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1630,7 +1721,7 @@ static void dasd_block_timeout(unsigned long ptr)
|
||||
block = (struct dasd_block *) ptr;
|
||||
spin_lock_irqsave(get_ccwdev_lock(block->base->cdev), flags);
|
||||
/* re-activate request queue */
|
||||
block->base->stopped &= ~DASD_STOPPED_PENDING;
|
||||
dasd_device_remove_stop_bits(block->base, DASD_STOPPED_PENDING);
|
||||
spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags);
|
||||
dasd_schedule_block_bh(block);
|
||||
}
|
||||
@ -1657,11 +1748,10 @@ void dasd_block_clear_timer(struct dasd_block *block)
|
||||
/*
|
||||
* Process finished error recovery ccw.
|
||||
*/
|
||||
static inline void __dasd_block_process_erp(struct dasd_block *block,
|
||||
struct dasd_ccw_req *cqr)
|
||||
static void __dasd_process_erp(struct dasd_device *device,
|
||||
struct dasd_ccw_req *cqr)
|
||||
{
|
||||
dasd_erp_fn_t erp_fn;
|
||||
struct dasd_device *device = block->base;
|
||||
|
||||
if (cqr->status == DASD_CQR_DONE)
|
||||
DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
|
||||
@ -1725,9 +1815,12 @@ static void __dasd_process_request_queue(struct dasd_block *block)
|
||||
*/
|
||||
if (!list_empty(&block->ccw_queue))
|
||||
break;
|
||||
spin_lock_irqsave(get_ccwdev_lock(basedev->cdev), flags);
|
||||
basedev->stopped |= DASD_STOPPED_PENDING;
|
||||
spin_unlock_irqrestore(get_ccwdev_lock(basedev->cdev), flags);
|
||||
spin_lock_irqsave(
|
||||
get_ccwdev_lock(basedev->cdev), flags);
|
||||
dasd_device_set_stop_bits(basedev,
|
||||
DASD_STOPPED_PENDING);
|
||||
spin_unlock_irqrestore(
|
||||
get_ccwdev_lock(basedev->cdev), flags);
|
||||
dasd_block_set_timer(block, HZ/2);
|
||||
break;
|
||||
}
|
||||
@ -1813,7 +1906,7 @@ restart:
|
||||
cqr->status = DASD_CQR_FILLED;
|
||||
cqr->retries = 255;
|
||||
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);
|
||||
goto restart;
|
||||
@ -1821,7 +1914,7 @@ restart:
|
||||
|
||||
/* Process finished ERP request. */
|
||||
if (cqr->refers) {
|
||||
__dasd_block_process_erp(block, cqr);
|
||||
__dasd_process_erp(base, cqr);
|
||||
goto restart;
|
||||
}
|
||||
|
||||
@ -1952,7 +2045,7 @@ restart_cb:
|
||||
/* Process finished ERP request. */
|
||||
if (cqr->refers) {
|
||||
spin_lock_bh(&block->queue_lock);
|
||||
__dasd_block_process_erp(block, cqr);
|
||||
__dasd_process_erp(block->base, cqr);
|
||||
spin_unlock_bh(&block->queue_lock);
|
||||
/* restart list_for_xx loop since dasd_process_erp
|
||||
* might remove multiple elements */
|
||||
@ -2208,18 +2301,11 @@ int dasd_generic_probe(struct ccw_device *cdev,
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
|
||||
if (ret) {
|
||||
DBF_EVENT(DBF_WARNING,
|
||||
"dasd_generic_probe: could not set ccw-device options "
|
||||
"for %s\n", dev_name(&cdev->dev));
|
||||
return ret;
|
||||
}
|
||||
ret = dasd_add_sysfs_files(cdev);
|
||||
if (ret) {
|
||||
DBF_EVENT(DBF_WARNING,
|
||||
"dasd_generic_probe: could not add sysfs entries "
|
||||
"for %s\n", dev_name(&cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
|
||||
"dasd_generic_probe: could not add "
|
||||
"sysfs entries");
|
||||
return ret;
|
||||
}
|
||||
cdev->handler = &dasd_int_handler;
|
||||
@ -2418,16 +2504,16 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
|
||||
cqr->status = DASD_CQR_QUEUED;
|
||||
cqr->retries++;
|
||||
}
|
||||
device->stopped |= DASD_STOPPED_DC_WAIT;
|
||||
dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT);
|
||||
dasd_device_clear_timer(device);
|
||||
dasd_schedule_device_bh(device);
|
||||
ret = 1;
|
||||
break;
|
||||
case CIO_OPER:
|
||||
/* FIXME: add a sanity check. */
|
||||
device->stopped &= ~DASD_STOPPED_DC_WAIT;
|
||||
dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT);
|
||||
if (device->stopped & DASD_UNRESUMED_PM) {
|
||||
device->stopped &= ~DASD_UNRESUMED_PM;
|
||||
dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM);
|
||||
dasd_restore_device(device);
|
||||
ret = 1;
|
||||
break;
|
||||
@ -2452,7 +2538,7 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev)
|
||||
if (IS_ERR(device))
|
||||
return PTR_ERR(device);
|
||||
/* disallow new I/O */
|
||||
device->stopped |= DASD_STOPPED_PM;
|
||||
dasd_device_set_stop_bits(device, DASD_STOPPED_PM);
|
||||
/* clear active requests */
|
||||
INIT_LIST_HEAD(&freeze_queue);
|
||||
spin_lock_irq(get_ccwdev_lock(cdev));
|
||||
@ -2504,14 +2590,18 @@ int dasd_generic_restore_device(struct ccw_device *cdev)
|
||||
return PTR_ERR(device);
|
||||
|
||||
/* allow new IO again */
|
||||
device->stopped &= ~DASD_STOPPED_PM;
|
||||
device->stopped &= ~DASD_UNRESUMED_PM;
|
||||
dasd_device_remove_stop_bits(device,
|
||||
(DASD_STOPPED_PM | DASD_UNRESUMED_PM));
|
||||
|
||||
dasd_schedule_device_bh(device);
|
||||
|
||||
if (device->discipline->restore)
|
||||
/*
|
||||
* call discipline restore function
|
||||
* if device is stopped do nothing e.g. for disconnected devices
|
||||
*/
|
||||
if (device->discipline->restore && !(device->stopped))
|
||||
rc = device->discipline->restore(device);
|
||||
if (rc)
|
||||
if (rc || device->stopped)
|
||||
/*
|
||||
* if the resume failed for the DASD we put it in
|
||||
* an UNRESUMED stop state
|
||||
@ -2561,8 +2651,7 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
|
||||
cqr->startdev = device;
|
||||
cqr->memdev = device;
|
||||
cqr->expires = 10*HZ;
|
||||
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
|
||||
cqr->retries = 2;
|
||||
cqr->retries = 256;
|
||||
cqr->buildclk = get_clock();
|
||||
cqr->status = DASD_CQR_FILLED;
|
||||
return cqr;
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/timer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/idals.h>
|
||||
#include <asm/todclk.h>
|
||||
|
||||
#define PRINTK_HEADER "dasd_erp(3990): "
|
||||
|
||||
@ -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
|
||||
* interrupt was received.
|
||||
*/
|
||||
static void
|
||||
dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
|
||||
static void dasd_3990_erp_block_queue(struct dasd_ccw_req *erp, int expires)
|
||||
{
|
||||
|
||||
struct dasd_device *device = erp->startdev;
|
||||
@ -81,10 +79,13 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
|
||||
"blocking request queue for %is", expires/HZ);
|
||||
|
||||
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);
|
||||
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)
|
||||
* DESCRIPTION
|
||||
* Setup ERP to do the ERP action 1 (see Reference manual).
|
||||
* Repeat the operation on a different channel path.
|
||||
* If all alternate paths have been tried, the request is posted with a
|
||||
* permanent error.
|
||||
* Note: duplex handling is not implemented (yet).
|
||||
* As deviation from the recommended recovery action, we reset the path mask
|
||||
* after we have tried each path and go through all paths a second time.
|
||||
* 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
|
||||
* erp pointer to the current ERP
|
||||
@ -254,17 +259,25 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
|
||||
* erp pointer to the ERP
|
||||
*
|
||||
*/
|
||||
static struct dasd_ccw_req *
|
||||
dasd_3990_erp_action_1(struct dasd_ccw_req * erp)
|
||||
static struct dasd_ccw_req *dasd_3990_erp_action_1_sec(struct dasd_ccw_req *erp)
|
||||
{
|
||||
|
||||
erp->function = dasd_3990_erp_action_1;
|
||||
|
||||
erp->function = dasd_3990_erp_action_1_sec;
|
||||
dasd_3990_erp_alternate_path(erp);
|
||||
|
||||
return erp;
|
||||
}
|
||||
|
||||
} /* end dasd_3990_erp_action_1 */
|
||||
static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
|
||||
{
|
||||
erp->function = dasd_3990_erp_action_1;
|
||||
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;
|
||||
} /* end dasd_3990_erp_action_1(b) */
|
||||
|
||||
/*
|
||||
* DASD_3990_ERP_ACTION_4
|
||||
@ -2295,6 +2308,7 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
|
||||
return cqr;
|
||||
}
|
||||
|
||||
ccw = cqr->cpaddr;
|
||||
if (cqr->cpmode == 1) {
|
||||
/* make a shallow copy of the original tcw but set new tsb */
|
||||
erp->cpmode = 1;
|
||||
@ -2303,6 +2317,9 @@ static struct dasd_ccw_req *dasd_3990_erp_add_erp(struct dasd_ccw_req *cqr)
|
||||
tsb = (struct tsb *) &tcw[1];
|
||||
*tcw = *((struct tcw *)cqr->cpaddr);
|
||||
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 {
|
||||
/* initialize request with default TIC to current ERP/CQR */
|
||||
ccw = erp->cpaddr;
|
||||
@ -2487,6 +2504,8 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *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) {
|
||||
|
||||
/* retries have not been successful */
|
||||
|
@ -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_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
|
||||
spin_lock_init(&lcu->lock);
|
||||
init_completion(&lcu->lcu_setup);
|
||||
return lcu;
|
||||
|
||||
out_err4:
|
||||
@ -239,6 +240,67 @@ int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
|
||||
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.
|
||||
* 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,
|
||||
{
|
||||
/* If pos == device then device is already locked! */
|
||||
if (pos == device) {
|
||||
pos->stopped |= DASD_STOPPED_SU;
|
||||
dasd_device_set_stop_bits(pos, DASD_STOPPED_SU);
|
||||
return;
|
||||
}
|
||||
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));
|
||||
}
|
||||
|
||||
@ -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) {
|
||||
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);
|
||||
}
|
||||
|
||||
list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
|
||||
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);
|
||||
}
|
||||
|
||||
list_for_each_entry(pavgroup, &lcu->grouplist, group) {
|
||||
list_for_each_entry(device, &pavgroup->baselist, alias_list) {
|
||||
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);
|
||||
}
|
||||
list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
|
||||
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);
|
||||
}
|
||||
@ -836,7 +898,8 @@ static void summary_unit_check_handling_work(struct work_struct *work)
|
||||
|
||||
/* 2. reset summary unit check */
|
||||
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);
|
||||
reset_summary_unit_check(lcu, device, suc_data->reason);
|
||||
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/s390_ext.h>
|
||||
#include <asm/todclk.h>
|
||||
#include <asm/vtoc.h>
|
||||
#include <asm/diag.h>
|
||||
|
||||
@ -145,6 +144,15 @@ dasd_diag_erp(struct dasd_device *device)
|
||||
|
||||
mdsk_term_io(device);
|
||||
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)
|
||||
dev_warn(&device->cdev->dev, "DIAG ERP failed with "
|
||||
"rc=%d\n", rc);
|
||||
@ -433,16 +441,20 @@ dasd_diag_check_device(struct dasd_device *device)
|
||||
for (sb = 512; sb < bsize; sb = sb << 1)
|
||||
block->s2b_shift++;
|
||||
rc = mdsk_init_io(device, block->bp_block, 0, NULL);
|
||||
if (rc) {
|
||||
if (rc && (rc != 4)) {
|
||||
dev_warn(&device->cdev->dev, "DIAG initialization "
|
||||
"failed with rc=%d\n", rc);
|
||||
rc = -EIO;
|
||||
} else {
|
||||
if (rc == 4)
|
||||
device->features |= DASD_FEATURE_READONLY;
|
||||
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->blocks <<
|
||||
block->s2b_shift) >> 1);
|
||||
block->s2b_shift) >> 1,
|
||||
(rc == 4) ? ", read-only device" : "");
|
||||
rc = 0;
|
||||
}
|
||||
out_label:
|
||||
free_page((long) label);
|
||||
|
@ -24,7 +24,6 @@
|
||||
#include <asm/idals.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/todclk.h>
|
||||
#include <asm/uaccess.h>
|
||||
#include <asm/cio.h>
|
||||
#include <asm/ccwdev.h>
|
||||
@ -78,6 +77,11 @@ MODULE_DEVICE_TABLE(ccw, dasd_eckd_ids);
|
||||
|
||||
static struct ccw_driver dasd_eckd_driver; /* see below */
|
||||
|
||||
#define INIT_CQR_OK 0
|
||||
#define INIT_CQR_UNFORMATTED 1
|
||||
#define INIT_CQR_ERROR 2
|
||||
|
||||
|
||||
/* initial attempt at a probe function. this can be simplified once
|
||||
* the other detection code is gone */
|
||||
static int
|
||||
@ -86,11 +90,12 @@ dasd_eckd_probe (struct ccw_device *cdev)
|
||||
int ret;
|
||||
|
||||
/* set ECKD specific ccw-device options */
|
||||
ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
|
||||
ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE |
|
||||
CCWDEV_DO_PATHGROUP | CCWDEV_DO_MULTIPATH);
|
||||
if (ret) {
|
||||
DBF_EVENT(DBF_WARNING,
|
||||
"dasd_eckd_probe: could not set ccw-device options "
|
||||
"for %s\n", dev_name(&cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s",
|
||||
"dasd_eckd_probe: could not set "
|
||||
"ccw-device options");
|
||||
return ret;
|
||||
}
|
||||
ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
|
||||
@ -749,8 +754,7 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
|
||||
cqr->block = NULL;
|
||||
cqr->expires = 10*HZ;
|
||||
cqr->lpm = lpm;
|
||||
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
|
||||
cqr->retries = 2;
|
||||
cqr->retries = 256;
|
||||
cqr->buildclk = get_clock();
|
||||
cqr->status = DASD_CQR_FILLED;
|
||||
return cqr;
|
||||
@ -885,16 +889,15 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
|
||||
rc = dasd_eckd_read_conf_lpm(device, &conf_data,
|
||||
&conf_len, lpm);
|
||||
if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */
|
||||
DBF_EVENT(DBF_WARNING,
|
||||
DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
|
||||
"Read configuration data returned "
|
||||
"error %d for device: %s", rc,
|
||||
dev_name(&device->cdev->dev));
|
||||
"error %d", rc);
|
||||
return rc;
|
||||
}
|
||||
if (conf_data == NULL) {
|
||||
DBF_EVENT(DBF_WARNING, "No configuration "
|
||||
"data retrieved for device: %s",
|
||||
dev_name(&device->cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
|
||||
"No configuration data "
|
||||
"retrieved");
|
||||
continue; /* no error */
|
||||
}
|
||||
/* save first valid configuration data */
|
||||
@ -941,16 +944,14 @@ static int dasd_eckd_read_features(struct dasd_device *device)
|
||||
sizeof(struct dasd_rssd_features)),
|
||||
device);
|
||||
if (IS_ERR(cqr)) {
|
||||
DBF_EVENT(DBF_WARNING, "Could not allocate initialization "
|
||||
"request for device: %s",
|
||||
dev_name(&device->cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "Could not "
|
||||
"allocate initialization request");
|
||||
return PTR_ERR(cqr);
|
||||
}
|
||||
cqr->startdev = device;
|
||||
cqr->memdev = device;
|
||||
cqr->block = NULL;
|
||||
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
|
||||
cqr->retries = 5;
|
||||
cqr->retries = 256;
|
||||
cqr->expires = 10 * HZ;
|
||||
|
||||
/* Prepare for Read Subsystem Data */
|
||||
@ -1012,9 +1013,9 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
|
||||
}
|
||||
psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
|
||||
psf_ssc_data->order = PSF_ORDER_SSC;
|
||||
psf_ssc_data->suborder = 0x40;
|
||||
psf_ssc_data->suborder = 0xc0;
|
||||
if (enable_pav) {
|
||||
psf_ssc_data->suborder |= 0x88;
|
||||
psf_ssc_data->suborder |= 0x08;
|
||||
psf_ssc_data->reserved[0] = 0x88;
|
||||
}
|
||||
ccw = cqr->cpaddr;
|
||||
@ -1025,6 +1026,7 @@ static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device,
|
||||
cqr->startdev = device;
|
||||
cqr->memdev = device;
|
||||
cqr->block = NULL;
|
||||
cqr->retries = 256;
|
||||
cqr->expires = 10*HZ;
|
||||
cqr->buildclk = get_clock();
|
||||
cqr->status = DASD_CQR_FILLED;
|
||||
@ -1057,7 +1059,7 @@ dasd_eckd_psf_ssc(struct dasd_device *device, int enable_pav)
|
||||
/*
|
||||
* Valide storage server of current device.
|
||||
*/
|
||||
static int dasd_eckd_validate_server(struct dasd_device *device)
|
||||
static void dasd_eckd_validate_server(struct dasd_device *device)
|
||||
{
|
||||
int rc;
|
||||
struct dasd_eckd_private *private;
|
||||
@ -1068,15 +1070,12 @@ static int dasd_eckd_validate_server(struct dasd_device *device)
|
||||
else
|
||||
enable_pav = 1;
|
||||
rc = dasd_eckd_psf_ssc(device, enable_pav);
|
||||
|
||||
/* may be requested feature is not available on server,
|
||||
* therefore just report error and go ahead */
|
||||
private = (struct dasd_eckd_private *) device->private;
|
||||
DBF_EVENT(DBF_WARNING, "PSF-SSC on storage subsystem %s.%s.%04x "
|
||||
"returned rc=%d for device: %s",
|
||||
private->uid.vendor, private->uid.serial,
|
||||
private->uid.ssid, rc, dev_name(&device->cdev->dev));
|
||||
/* RE-Read Configuration Data */
|
||||
return dasd_eckd_read_conf(device);
|
||||
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "PSF-SSC for SSID %04x "
|
||||
"returned rc=%d", private->uid.ssid, rc);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -1090,6 +1089,15 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
|
||||
struct dasd_block *block;
|
||||
int is_known, rc;
|
||||
|
||||
if (!ccw_device_is_pathgroup(device->cdev)) {
|
||||
dev_warn(&device->cdev->dev,
|
||||
"A channel path group could not be established\n");
|
||||
return -EIO;
|
||||
}
|
||||
if (!ccw_device_is_multipath(device->cdev)) {
|
||||
dev_info(&device->cdev->dev,
|
||||
"The DASD is not operating in multipath mode\n");
|
||||
}
|
||||
private = (struct dasd_eckd_private *) device->private;
|
||||
if (!private) {
|
||||
private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
|
||||
@ -1123,9 +1131,9 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
|
||||
if (private->uid.type == UA_BASE_DEVICE) {
|
||||
block = dasd_alloc_block();
|
||||
if (IS_ERR(block)) {
|
||||
DBF_EVENT(DBF_WARNING, "could not allocate dasd "
|
||||
"block structure for device: %s",
|
||||
dev_name(&device->cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
|
||||
"could not allocate dasd "
|
||||
"block structure");
|
||||
rc = PTR_ERR(block);
|
||||
goto out_err1;
|
||||
}
|
||||
@ -1139,12 +1147,21 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
|
||||
rc = is_known;
|
||||
goto out_err2;
|
||||
}
|
||||
/*
|
||||
* dasd_eckd_vaildate_server is done on the first device that
|
||||
* is found for an LCU. All later other devices have to wait
|
||||
* for it, so they will read the correct feature codes.
|
||||
*/
|
||||
if (!is_known) {
|
||||
/* new lcu found */
|
||||
rc = dasd_eckd_validate_server(device); /* will switch pav on */
|
||||
if (rc)
|
||||
goto out_err3;
|
||||
}
|
||||
dasd_eckd_validate_server(device);
|
||||
dasd_alias_lcu_setup_complete(device);
|
||||
} else
|
||||
dasd_alias_wait_for_lcu_setup(device);
|
||||
|
||||
/* device may report different configuration data after LCU setup */
|
||||
rc = dasd_eckd_read_conf(device);
|
||||
if (rc)
|
||||
goto out_err3;
|
||||
|
||||
/* Read Feature Codes */
|
||||
dasd_eckd_read_features(device);
|
||||
@ -1153,9 +1170,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
|
||||
rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
|
||||
&private->rdc_data, 64);
|
||||
if (rc) {
|
||||
DBF_EVENT(DBF_WARNING,
|
||||
"Read device characteristics failed, rc=%d for "
|
||||
"device: %s", rc, dev_name(&device->cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
|
||||
"Read device characteristic failed, rc=%d", rc);
|
||||
goto out_err3;
|
||||
}
|
||||
/* find the vaild cylinder size */
|
||||
@ -1256,12 +1272,29 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
|
||||
cqr->block = NULL;
|
||||
cqr->startdev = device;
|
||||
cqr->memdev = device;
|
||||
cqr->retries = 0;
|
||||
cqr->retries = 255;
|
||||
cqr->buildclk = get_clock();
|
||||
cqr->status = DASD_CQR_FILLED;
|
||||
return cqr;
|
||||
}
|
||||
|
||||
/* differentiate between 'no record found' and any other error */
|
||||
static int dasd_eckd_analysis_evaluation(struct dasd_ccw_req *init_cqr)
|
||||
{
|
||||
char *sense;
|
||||
if (init_cqr->status == DASD_CQR_DONE)
|
||||
return INIT_CQR_OK;
|
||||
else if (init_cqr->status == DASD_CQR_NEED_ERP ||
|
||||
init_cqr->status == DASD_CQR_FAILED) {
|
||||
sense = dasd_get_sense(&init_cqr->irb);
|
||||
if (sense && (sense[1] & SNS1_NO_REC_FOUND))
|
||||
return INIT_CQR_UNFORMATTED;
|
||||
else
|
||||
return INIT_CQR_ERROR;
|
||||
} else
|
||||
return INIT_CQR_ERROR;
|
||||
}
|
||||
|
||||
/*
|
||||
* This is the callback function for the init_analysis cqr. It saves
|
||||
* the status of the initial analysis ccw before it frees it and kicks
|
||||
@ -1269,21 +1302,20 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
|
||||
* dasd_eckd_do_analysis again (if the devices has not been marked
|
||||
* for deletion in the meantime).
|
||||
*/
|
||||
static void
|
||||
dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
|
||||
static void dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr,
|
||||
void *data)
|
||||
{
|
||||
struct dasd_eckd_private *private;
|
||||
struct dasd_device *device;
|
||||
|
||||
device = init_cqr->startdev;
|
||||
private = (struct dasd_eckd_private *) device->private;
|
||||
private->init_cqr_status = init_cqr->status;
|
||||
private->init_cqr_status = dasd_eckd_analysis_evaluation(init_cqr);
|
||||
dasd_sfree_request(init_cqr, device);
|
||||
dasd_kick_device(device);
|
||||
}
|
||||
|
||||
static int
|
||||
dasd_eckd_start_analysis(struct dasd_block *block)
|
||||
static int dasd_eckd_start_analysis(struct dasd_block *block)
|
||||
{
|
||||
struct dasd_eckd_private *private;
|
||||
struct dasd_ccw_req *init_cqr;
|
||||
@ -1295,27 +1327,44 @@ dasd_eckd_start_analysis(struct dasd_block *block)
|
||||
init_cqr->callback = dasd_eckd_analysis_callback;
|
||||
init_cqr->callback_data = NULL;
|
||||
init_cqr->expires = 5*HZ;
|
||||
/* first try without ERP, so we can later handle unformatted
|
||||
* devices as special case
|
||||
*/
|
||||
clear_bit(DASD_CQR_FLAGS_USE_ERP, &init_cqr->flags);
|
||||
init_cqr->retries = 0;
|
||||
dasd_add_request_head(init_cqr);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
static int
|
||||
dasd_eckd_end_analysis(struct dasd_block *block)
|
||||
static int dasd_eckd_end_analysis(struct dasd_block *block)
|
||||
{
|
||||
struct dasd_device *device;
|
||||
struct dasd_eckd_private *private;
|
||||
struct eckd_count *count_area;
|
||||
unsigned int sb, blk_per_trk;
|
||||
int status, i;
|
||||
struct dasd_ccw_req *init_cqr;
|
||||
|
||||
device = block->base;
|
||||
private = (struct dasd_eckd_private *) device->private;
|
||||
status = private->init_cqr_status;
|
||||
private->init_cqr_status = -1;
|
||||
if (status != DASD_CQR_DONE) {
|
||||
dev_warn(&device->cdev->dev,
|
||||
"The DASD is not formatted\n");
|
||||
if (status == INIT_CQR_ERROR) {
|
||||
/* try again, this time with full ERP */
|
||||
init_cqr = dasd_eckd_analysis_ccw(device);
|
||||
dasd_sleep_on(init_cqr);
|
||||
status = dasd_eckd_analysis_evaluation(init_cqr);
|
||||
dasd_sfree_request(init_cqr, device);
|
||||
}
|
||||
|
||||
if (status == INIT_CQR_UNFORMATTED) {
|
||||
dev_warn(&device->cdev->dev, "The DASD is not formatted\n");
|
||||
return -EMEDIUMTYPE;
|
||||
} else if (status == INIT_CQR_ERROR) {
|
||||
dev_err(&device->cdev->dev,
|
||||
"Detecting the DASD disk layout failed because "
|
||||
"of an I/O error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
private->uses_cdl = 1;
|
||||
@ -1607,8 +1656,7 @@ dasd_eckd_format_device(struct dasd_device * device,
|
||||
}
|
||||
fcp->startdev = device;
|
||||
fcp->memdev = device;
|
||||
clear_bit(DASD_CQR_FLAGS_USE_ERP, &fcp->flags);
|
||||
fcp->retries = 5; /* set retry counter to enable default ERP */
|
||||
fcp->retries = 256;
|
||||
fcp->buildclk = get_clock();
|
||||
fcp->status = DASD_CQR_FILLED;
|
||||
return fcp;
|
||||
@ -2690,6 +2738,7 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
|
||||
cqr->startdev = device;
|
||||
cqr->memdev = device;
|
||||
cqr->retries = 0;
|
||||
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
|
||||
cqr->expires = 10 * HZ;
|
||||
|
||||
/* Prepare for Read Subsystem Data */
|
||||
@ -3240,11 +3289,15 @@ int dasd_eckd_restore_device(struct dasd_device *device)
|
||||
if (is_known < 0)
|
||||
return is_known;
|
||||
if (!is_known) {
|
||||
/* new lcu found */
|
||||
rc = dasd_eckd_validate_server(device); /* will switch pav on */
|
||||
if (rc)
|
||||
goto out_err;
|
||||
}
|
||||
dasd_eckd_validate_server(device);
|
||||
dasd_alias_lcu_setup_complete(device);
|
||||
} else
|
||||
dasd_alias_wait_for_lcu_setup(device);
|
||||
|
||||
/* RE-Read Configuration Data */
|
||||
rc = dasd_eckd_read_conf(device);
|
||||
if (rc)
|
||||
goto out_err;
|
||||
|
||||
/* Read Feature Codes */
|
||||
dasd_eckd_read_features(device);
|
||||
@ -3253,9 +3306,8 @@ int dasd_eckd_restore_device(struct dasd_device *device)
|
||||
rc = dasd_generic_read_dev_chars(device, DASD_ECKD_MAGIC,
|
||||
&temp_rdc_data, 64);
|
||||
if (rc) {
|
||||
DBF_EVENT(DBF_WARNING,
|
||||
"Read device characteristics failed, rc=%d for "
|
||||
"device: %s", rc, dev_name(&device->cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, device->cdev,
|
||||
"Read device characteristic failed, rc=%d", rc);
|
||||
goto out_err;
|
||||
}
|
||||
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
|
||||
|
@ -414,6 +414,7 @@ struct alias_lcu {
|
||||
struct summary_unit_check_work_data suc_data;
|
||||
struct read_uac_work_data ruac_data;
|
||||
struct dasd_ccw_req *rsu_cqr;
|
||||
struct completion lcu_setup;
|
||||
};
|
||||
|
||||
struct alias_pav_group {
|
||||
@ -460,5 +461,6 @@ int dasd_alias_remove_device(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_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 */
|
||||
|
@ -536,7 +536,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
|
||||
eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);
|
||||
if (!eerb)
|
||||
return -ENOMEM;
|
||||
lock_kernel();
|
||||
eerb->buffer_page_count = eer_pages;
|
||||
if (eerb->buffer_page_count < 1 ||
|
||||
eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {
|
||||
@ -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 "
|
||||
"parameter eer_pages is smaller than 1 or"
|
||||
" bigger than %d", (int)(INT_MAX / PAGE_SIZE));
|
||||
unlock_kernel();
|
||||
return -EINVAL;
|
||||
}
|
||||
eerb->buffersize = eerb->buffer_page_count * PAGE_SIZE;
|
||||
@ -552,14 +550,12 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
|
||||
GFP_KERNEL);
|
||||
if (!eerb->buffer) {
|
||||
kfree(eerb);
|
||||
unlock_kernel();
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (dasd_eer_allocate_buffer_pages(eerb->buffer,
|
||||
eerb->buffer_page_count)) {
|
||||
kfree(eerb->buffer);
|
||||
kfree(eerb);
|
||||
unlock_kernel();
|
||||
return -ENOMEM;
|
||||
}
|
||||
filp->private_data = eerb;
|
||||
@ -567,7 +563,6 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
|
||||
list_add(&eerb->list, &bufferlist);
|
||||
spin_unlock_irqrestore(&bufferlock, flags);
|
||||
|
||||
unlock_kernel();
|
||||
return nonseekable_open(inp,filp);
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include <asm/idals.h>
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/io.h>
|
||||
#include <asm/todclk.h>
|
||||
#include <asm/ccwdev.h>
|
||||
|
||||
#include "dasd_int.h"
|
||||
@ -141,9 +140,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
|
||||
}
|
||||
block = dasd_alloc_block();
|
||||
if (IS_ERR(block)) {
|
||||
DBF_EVENT(DBF_WARNING, "could not allocate dasd block "
|
||||
"structure for device: %s",
|
||||
dev_name(&device->cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, cdev, "%s", "could not allocate "
|
||||
"dasd block structure");
|
||||
device->private = NULL;
|
||||
kfree(private);
|
||||
return PTR_ERR(block);
|
||||
@ -155,9 +153,8 @@ dasd_fba_check_characteristics(struct dasd_device *device)
|
||||
rc = dasd_generic_read_dev_chars(device, DASD_FBA_MAGIC,
|
||||
&private->rdc_data, 32);
|
||||
if (rc) {
|
||||
DBF_EVENT(DBF_WARNING, "Read device characteristics returned "
|
||||
"error %d for device: %s",
|
||||
rc, dev_name(&device->cdev->dev));
|
||||
DBF_EVENT_DEVID(DBF_WARNING, cdev, "Read device "
|
||||
"characteristics returned error %d", rc);
|
||||
device->block = NULL;
|
||||
dasd_free_block(block);
|
||||
device->private = NULL;
|
||||
|
@ -108,6 +108,16 @@ do { \
|
||||
d_data); \
|
||||
} 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...)\
|
||||
do { \
|
||||
debug_sprintf_exception(dasd_debug_area, \
|
||||
@ -595,6 +605,9 @@ int dasd_generic_restore_device(struct ccw_device *);
|
||||
int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int);
|
||||
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 */
|
||||
extern int dasd_max_devindex;
|
||||
extern int dasd_probeonly;
|
||||
|
@ -101,7 +101,7 @@ static int dasd_ioctl_quiesce(struct dasd_block *block)
|
||||
pr_info("%s: The DASD has been put in the quiesce "
|
||||
"state\n", dev_name(&base->cdev->dev));
|
||||
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);
|
||||
return 0;
|
||||
}
|
||||
@ -122,7 +122,7 @@ static int dasd_ioctl_resume(struct dasd_block *block)
|
||||
pr_info("%s: I/O operations have been resumed "
|
||||
"on the DASD\n", dev_name(&base->cdev->dev));
|
||||
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);
|
||||
|
||||
dasd_schedule_block_bh(block);
|
||||
|
@ -857,7 +857,6 @@ static struct console con3215 = {
|
||||
|
||||
/*
|
||||
* 3215 console initialization code called from console_init().
|
||||
* NOTE: This is called before kmalloc is available.
|
||||
*/
|
||||
static int __init con3215_init(void)
|
||||
{
|
||||
|
@ -572,7 +572,6 @@ static struct console con3270 = {
|
||||
|
||||
/*
|
||||
* 3270 console initialization code called from console_init().
|
||||
* NOTE: This is called before kmalloc is available.
|
||||
*/
|
||||
static int __init
|
||||
con3270_init(void)
|
||||
|
@ -38,6 +38,8 @@ struct fs3270 {
|
||||
size_t rdbuf_size; /* size of data returned by RDBUF */
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(fs3270_mutex);
|
||||
|
||||
static void
|
||||
fs3270_wake_up(struct raw3270_request *rq, void *data)
|
||||
{
|
||||
@ -328,7 +330,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
if (!fp)
|
||||
return -ENODEV;
|
||||
rc = 0;
|
||||
lock_kernel();
|
||||
mutex_lock(&fs3270_mutex);
|
||||
switch (cmd) {
|
||||
case TUBICMD:
|
||||
fp->read_command = arg;
|
||||
@ -354,7 +356,7 @@ fs3270_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
||||
rc = -EFAULT;
|
||||
break;
|
||||
}
|
||||
unlock_kernel();
|
||||
mutex_unlock(&fs3270_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -437,7 +439,7 @@ fs3270_open(struct inode *inode, struct file *filp)
|
||||
minor = tty->index + RAW3270_FIRSTMINOR;
|
||||
tty_kref_put(tty);
|
||||
}
|
||||
lock_kernel();
|
||||
mutex_lock(&fs3270_mutex);
|
||||
/* Check if some other program is already using fullscreen mode. */
|
||||
fp = (struct fs3270 *) raw3270_find_view(&fs3270_fn, minor);
|
||||
if (!IS_ERR(fp)) {
|
||||
@ -478,7 +480,7 @@ fs3270_open(struct inode *inode, struct file *filp)
|
||||
}
|
||||
filp->private_data = fp;
|
||||
out:
|
||||
unlock_kernel();
|
||||
mutex_unlock(&fs3270_mutex);
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -12,7 +12,6 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
@ -283,7 +282,6 @@ static int mon_open(struct inode *inode, struct file *filp)
|
||||
/*
|
||||
* only one user allowed
|
||||
*/
|
||||
lock_kernel();
|
||||
rc = -EBUSY;
|
||||
if (test_and_set_bit(MON_IN_USE, &mon_in_use))
|
||||
goto out;
|
||||
@ -321,7 +319,6 @@ static int mon_open(struct inode *inode, struct file *filp)
|
||||
}
|
||||
filp->private_data = monpriv;
|
||||
dev_set_drvdata(monreader_device, monpriv);
|
||||
unlock_kernel();
|
||||
return nonseekable_open(inode, filp);
|
||||
|
||||
out_path:
|
||||
@ -331,7 +328,6 @@ out_priv:
|
||||
out_use:
|
||||
clear_bit(MON_IN_USE, &mon_in_use);
|
||||
out:
|
||||
unlock_kernel();
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -607,6 +603,10 @@ static int __init mon_init(void)
|
||||
}
|
||||
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);
|
||||
if (rc < 0 )
|
||||
goto out;
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/smp_lock.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/miscdevice.h>
|
||||
@ -185,13 +184,11 @@ static int monwrite_open(struct inode *inode, struct file *filp)
|
||||
monpriv = kzalloc(sizeof(struct mon_private), GFP_KERNEL);
|
||||
if (!monpriv)
|
||||
return -ENOMEM;
|
||||
lock_kernel();
|
||||
INIT_LIST_HEAD(&monpriv->list);
|
||||
monpriv->hdr_to_read = sizeof(monpriv->hdr);
|
||||
mutex_init(&monpriv->thread_mutex);
|
||||
filp->private_data = monpriv;
|
||||
list_add_tail(&monpriv->priv_list, &mon_priv_list);
|
||||
unlock_kernel();
|
||||
return nonseekable_open(inode, filp);
|
||||
}
|
||||
|
||||
@ -364,6 +361,10 @@ static int __init mon_init(void)
|
||||
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);
|
||||
if (rc)
|
||||
goto out_device;
|
||||
|
@ -84,6 +84,7 @@ static void __init sclp_read_info_early(void)
|
||||
do {
|
||||
memset(sccb, 0, sizeof(*sccb));
|
||||
sccb->header.length = sizeof(*sccb);
|
||||
sccb->header.function_code = 0x80;
|
||||
sccb->header.control_mask[2] = 0x80;
|
||||
rc = sclp_cmd_sync_early(commands[i], sccb);
|
||||
} while (rc == -EBUSY);
|
||||
|
@ -212,6 +212,9 @@ struct tape_device {
|
||||
struct tape_class_device * nt;
|
||||
struct tape_class_device * rt;
|
||||
|
||||
/* Device mutex to serialize tape commands. */
|
||||
struct mutex mutex;
|
||||
|
||||
/* Device discipline information. */
|
||||
struct tape_discipline * discipline;
|
||||
void * discdata;
|
||||
@ -292,9 +295,9 @@ extern int tape_generic_pm_suspend(struct ccw_device *);
|
||||
extern int tape_generic_probe(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_get_device_reference(struct tape_device *);
|
||||
extern struct tape_device *tape_put_device(struct tape_device *);
|
||||
extern struct tape_device *tape_find_device(int devindex);
|
||||
extern struct tape_device *tape_get_device(struct tape_device *);
|
||||
extern void tape_put_device(struct tape_device *);
|
||||
|
||||
/* Externals from tape_char.c */
|
||||
extern int tapechar_init(void);
|
||||
|
@ -113,16 +113,16 @@ tape_34xx_work_handler(struct work_struct *work)
|
||||
{
|
||||
struct tape_34xx_work *p =
|
||||
container_of(work, struct tape_34xx_work, work);
|
||||
struct tape_device *device = p->device;
|
||||
|
||||
switch(p->op) {
|
||||
case TO_MSEN:
|
||||
tape_34xx_medium_sense(p->device);
|
||||
tape_34xx_medium_sense(device);
|
||||
break;
|
||||
default:
|
||||
DBF_EVENT(3, "T34XX: internal error: unknown work\n");
|
||||
}
|
||||
|
||||
p->device = tape_put_device(p->device);
|
||||
tape_put_device(device);
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
@ -136,7 +136,7 @@ tape_34xx_schedule_work(struct tape_device *device, enum tape_op op)
|
||||
|
||||
INIT_WORK(&p->work, tape_34xx_work_handler);
|
||||
|
||||
p->device = tape_get_device_reference(device);
|
||||
p->device = tape_get_device(device);
|
||||
p->op = op;
|
||||
|
||||
schedule_work(&p->work);
|
||||
|
@ -608,7 +608,7 @@ tape_3590_schedule_work(struct tape_device *device, enum tape_op op)
|
||||
|
||||
INIT_WORK(&p->work, tape_3590_work_handler);
|
||||
|
||||
p->device = tape_get_device_reference(device);
|
||||
p->device = tape_get_device(device);
|
||||
p->op = op;
|
||||
|
||||
schedule_work(&p->work);
|
||||
|
@ -54,7 +54,7 @@ static const struct block_device_operations tapeblock_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = tapeblock_open,
|
||||
.release = tapeblock_release,
|
||||
.locked_ioctl = tapeblock_ioctl,
|
||||
.ioctl = tapeblock_ioctl,
|
||||
.media_changed = tapeblock_medium_changed,
|
||||
.revalidate_disk = tapeblock_revalidate_disk,
|
||||
};
|
||||
@ -239,7 +239,7 @@ tapeblock_setup_device(struct tape_device * device)
|
||||
disk->major = tapeblock_major;
|
||||
disk->first_minor = device->first_minor;
|
||||
disk->fops = &tapeblock_fops;
|
||||
disk->private_data = tape_get_device_reference(device);
|
||||
disk->private_data = tape_get_device(device);
|
||||
disk->queue = blkdat->request_queue;
|
||||
set_capacity(disk, 0);
|
||||
sprintf(disk->disk_name, "btibm%d",
|
||||
@ -247,11 +247,11 @@ tapeblock_setup_device(struct tape_device * device)
|
||||
|
||||
blkdat->disk = disk;
|
||||
blkdat->medium_changed = 1;
|
||||
blkdat->request_queue->queuedata = tape_get_device_reference(device);
|
||||
blkdat->request_queue->queuedata = tape_get_device(device);
|
||||
|
||||
add_disk(disk);
|
||||
|
||||
tape_get_device_reference(device);
|
||||
tape_get_device(device);
|
||||
INIT_WORK(&blkdat->requeue_task, tapeblock_requeue);
|
||||
|
||||
return 0;
|
||||
@ -274,13 +274,14 @@ tapeblock_cleanup_device(struct tape_device *device)
|
||||
}
|
||||
|
||||
del_gendisk(device->blk_data.disk);
|
||||
device->blk_data.disk->private_data =
|
||||
tape_put_device(device->blk_data.disk->private_data);
|
||||
device->blk_data.disk->private_data = NULL;
|
||||
tape_put_device(device);
|
||||
put_disk(device->blk_data.disk);
|
||||
|
||||
device->blk_data.disk = NULL;
|
||||
cleanup_queue:
|
||||
device->blk_data.request_queue->queuedata = tape_put_device(device);
|
||||
device->blk_data.request_queue->queuedata = NULL;
|
||||
tape_put_device(device);
|
||||
|
||||
blk_cleanup_queue(device->blk_data.request_queue);
|
||||
device->blk_data.request_queue = NULL;
|
||||
@ -363,7 +364,7 @@ tapeblock_open(struct block_device *bdev, fmode_t mode)
|
||||
struct tape_device * device;
|
||||
int rc;
|
||||
|
||||
device = tape_get_device_reference(disk->private_data);
|
||||
device = tape_get_device(disk->private_data);
|
||||
|
||||
if (device->required_tapemarks) {
|
||||
DBF_EVENT(2, "TBLOCK: missing tapemarks\n");
|
||||
|
@ -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 int tapechar_open(struct inode *,struct file *);
|
||||
static int tapechar_release(struct inode *,struct file *);
|
||||
static int tapechar_ioctl(struct inode *, struct file *, unsigned int,
|
||||
unsigned long);
|
||||
static long tapechar_ioctl(struct file *, unsigned int, unsigned long);
|
||||
static long tapechar_compat_ioctl(struct file *, unsigned int,
|
||||
unsigned long);
|
||||
|
||||
@ -43,7 +42,7 @@ static const struct file_operations tape_fops =
|
||||
.owner = THIS_MODULE,
|
||||
.read = tapechar_read,
|
||||
.write = tapechar_write,
|
||||
.ioctl = tapechar_ioctl,
|
||||
.unlocked_ioctl = tapechar_ioctl,
|
||||
.compat_ioctl = tapechar_compat_ioctl,
|
||||
.open = tapechar_open,
|
||||
.release = tapechar_release,
|
||||
@ -170,7 +169,6 @@ tapechar_read(struct file *filp, char __user *data, size_t count, loff_t *ppos)
|
||||
if (rc == 0) {
|
||||
rc = block_size - request->rescnt;
|
||||
DBF_EVENT(6, "TCHAR:rbytes: %x\n", rc);
|
||||
filp->f_pos += rc;
|
||||
/* Copy data from idal buffer to user space. */
|
||||
if (idal_buffer_to_user(device->char_data.idal_buf,
|
||||
data, rc) != 0)
|
||||
@ -238,7 +236,6 @@ tapechar_write(struct file *filp, const char __user *data, size_t count, loff_t
|
||||
break;
|
||||
DBF_EVENT(6, "TCHAR:wbytes: %lx\n",
|
||||
block_size - request->rescnt);
|
||||
filp->f_pos += block_size - request->rescnt;
|
||||
written += block_size - request->rescnt;
|
||||
if (request->rescnt != 0)
|
||||
break;
|
||||
@ -286,26 +283,20 @@ tapechar_open (struct inode *inode, struct file *filp)
|
||||
if (imajor(filp->f_path.dentry->d_inode) != tapechar_major)
|
||||
return -ENODEV;
|
||||
|
||||
lock_kernel();
|
||||
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)) {
|
||||
DBF_EVENT(3, "TCHAR:open: tape_get_device() failed\n");
|
||||
rc = PTR_ERR(device);
|
||||
goto out;
|
||||
DBF_EVENT(3, "TCHAR:open: tape_find_device() failed\n");
|
||||
return PTR_ERR(device);
|
||||
}
|
||||
|
||||
|
||||
rc = tape_open(device);
|
||||
if (rc == 0) {
|
||||
filp->private_data = device;
|
||||
rc = nonseekable_open(inode, filp);
|
||||
}
|
||||
else
|
||||
nonseekable_open(inode, filp);
|
||||
} else
|
||||
tape_put_device(device);
|
||||
|
||||
out:
|
||||
unlock_kernel();
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -342,7 +333,8 @@ tapechar_release(struct inode *inode, struct file *filp)
|
||||
device->char_data.idal_buf = NULL;
|
||||
}
|
||||
tape_release(device);
|
||||
filp->private_data = tape_put_device(device);
|
||||
filp->private_data = NULL;
|
||||
tape_put_device(device);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@ -351,16 +343,11 @@ tapechar_release(struct inode *inode, struct file *filp)
|
||||
* Tape device io controls.
|
||||
*/
|
||||
static int
|
||||
tapechar_ioctl(struct inode *inp, struct file *filp,
|
||||
unsigned int no, unsigned long data)
|
||||
__tapechar_ioctl(struct tape_device *device,
|
||||
unsigned int no, unsigned long data)
|
||||
{
|
||||
struct tape_device *device;
|
||||
int rc;
|
||||
|
||||
DBF_EVENT(6, "TCHAR:ioct\n");
|
||||
|
||||
device = (struct tape_device *) filp->private_data;
|
||||
|
||||
if (no == MTIOCTOP) {
|
||||
struct mtop op;
|
||||
|
||||
@ -452,6 +439,21 @@ tapechar_ioctl(struct inode *inp, struct file *filp,
|
||||
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
|
||||
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;
|
||||
|
||||
if (device->discipline->ioctl_fn) {
|
||||
lock_kernel();
|
||||
mutex_lock(&device->mutex);
|
||||
rval = device->discipline->ioctl_fn(device, no, data);
|
||||
unlock_kernel();
|
||||
mutex_unlock(&device->mutex);
|
||||
if (rval == -EINVAL)
|
||||
rval = -ENOIOCTLCMD;
|
||||
}
|
||||
|
@ -492,6 +492,7 @@ tape_alloc_device(void)
|
||||
kfree(device);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
mutex_init(&device->mutex);
|
||||
INIT_LIST_HEAD(&device->req_queue);
|
||||
INIT_LIST_HEAD(&device->node);
|
||||
init_waitqueue_head(&device->state_change_wq);
|
||||
@ -511,11 +512,12 @@ tape_alloc_device(void)
|
||||
* increment the reference count.
|
||||
*/
|
||||
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,
|
||||
atomic_inc_return(&device->ref_count));
|
||||
int count;
|
||||
|
||||
count = atomic_inc_return(&device->ref_count);
|
||||
DBF_EVENT(4, "tape_get_device(%p) = %i\n", device, count);
|
||||
return 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
|
||||
* for clearing reference pointers.
|
||||
*/
|
||||
struct tape_device *
|
||||
void
|
||||
tape_put_device(struct tape_device *device)
|
||||
{
|
||||
int remain;
|
||||
int count;
|
||||
|
||||
remain = atomic_dec_return(&device->ref_count);
|
||||
if (remain > 0) {
|
||||
DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, remain);
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
count = atomic_dec_return(&device->ref_count);
|
||||
DBF_EVENT(4, "tape_put_device(%p) -> %i\n", device, count);
|
||||
BUG_ON(count < 0);
|
||||
if (count == 0) {
|
||||
kfree(device->modeset_byte);
|
||||
kfree(device);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Find tape device by a device index.
|
||||
*/
|
||||
struct tape_device *
|
||||
tape_get_device(int devindex)
|
||||
tape_find_device(int devindex)
|
||||
{
|
||||
struct tape_device *device, *tmp;
|
||||
|
||||
@ -558,7 +553,7 @@ tape_get_device(int devindex)
|
||||
read_lock(&tape_device_lock);
|
||||
list_for_each_entry(tmp, &tape_device_list, node) {
|
||||
if (tmp->first_minor / TAPE_MINORS_PER_DEV == devindex) {
|
||||
device = tape_get_device_reference(tmp);
|
||||
device = tape_get_device(tmp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -579,7 +574,8 @@ tape_generic_probe(struct ccw_device *cdev)
|
||||
device = tape_alloc_device();
|
||||
if (IS_ERR(device))
|
||||
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);
|
||||
if (ret) {
|
||||
tape_put_device(device);
|
||||
@ -606,7 +602,8 @@ __tape_discard_requests(struct tape_device *device)
|
||||
list_del(&request->list);
|
||||
|
||||
/* Decrease ref_count for removed request. */
|
||||
request->device = tape_put_device(device);
|
||||
request->device = NULL;
|
||||
tape_put_device(device);
|
||||
request->rc = -EIO;
|
||||
if (request->callback != NULL)
|
||||
request->callback(request, request->callback_data);
|
||||
@ -664,9 +661,11 @@ tape_generic_remove(struct ccw_device *cdev)
|
||||
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);
|
||||
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)
|
||||
{
|
||||
DBF_LH(6, "Free request %p\n", request);
|
||||
|
||||
if (request->device != NULL) {
|
||||
request->device = tape_put_device(request->device);
|
||||
}
|
||||
if (request->device)
|
||||
tape_put_device(request->device);
|
||||
kfree(request->cpdata);
|
||||
kfree(request->cpaddr);
|
||||
kfree(request);
|
||||
@ -838,7 +836,8 @@ static void tape_long_busy_timeout(unsigned long data)
|
||||
BUG_ON(request->status != TAPE_REQUEST_LONG_BUSY);
|
||||
DBF_LH(6, "%08x: Long busy timeout.\n", device->cdev_id);
|
||||
__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));
|
||||
}
|
||||
|
||||
@ -918,7 +917,7 @@ __tape_start_request(struct tape_device *device, struct tape_request *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)) {
|
||||
/* No other requests are on the queue. Start this one. */
|
||||
@ -1117,8 +1116,8 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
|
||||
if (req->status == TAPE_REQUEST_LONG_BUSY) {
|
||||
DBF_EVENT(3, "(%08x): del timer\n", device->cdev_id);
|
||||
if (del_timer(&device->lb_timeout)) {
|
||||
device->lb_timeout.data = (unsigned long)
|
||||
tape_put_device(device);
|
||||
device->lb_timeout.data = 0UL;
|
||||
tape_put_device(device);
|
||||
__tape_start_next_request(device);
|
||||
}
|
||||
return;
|
||||
@ -1173,7 +1172,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb)
|
||||
break;
|
||||
case TAPE_IO_LONG_BUSY:
|
||||
device->lb_timeout.data =
|
||||
(unsigned long)tape_get_device_reference(device);
|
||||
(unsigned long) tape_get_device(device);
|
||||
device->lb_timeout.expires = jiffies +
|
||||
LONG_BUSY_TIMEOUT * HZ;
|
||||
DBF_EVENT(3, "(%08x): add timer\n", device->cdev_id);
|
||||
@ -1326,7 +1325,7 @@ EXPORT_SYMBOL(tape_generic_online);
|
||||
EXPORT_SYMBOL(tape_generic_offline);
|
||||
EXPORT_SYMBOL(tape_generic_pm_suspend);
|
||||
EXPORT_SYMBOL(tape_put_device);
|
||||
EXPORT_SYMBOL(tape_get_device_reference);
|
||||
EXPORT_SYMBOL(tape_get_device);
|
||||
EXPORT_SYMBOL(tape_state_verbose);
|
||||
EXPORT_SYMBOL(tape_op_verbose);
|
||||
EXPORT_SYMBOL(tape_state_set);
|
||||
|
@ -45,7 +45,7 @@ static int tape_proc_show(struct seq_file *m, void *v)
|
||||
seq_printf(m, "TapeNo\tBusID CuType/Model\t"
|
||||
"DevType/Model\tBlkSize\tState\tOp\tMedState\n");
|
||||
}
|
||||
device = tape_get_device(n);
|
||||
device = tape_find_device(n);
|
||||
if (IS_ERR(device))
|
||||
return 0;
|
||||
spin_lock_irq(get_ccwdev_lock(device->cdev));
|
||||
|
@ -19,6 +19,7 @@
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/compat.h>
|
||||
|
||||
#include <asm/ccwdev.h>
|
||||
#include <asm/cio.h>
|
||||
@ -1731,6 +1732,22 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file,
|
||||
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 = {
|
||||
.open = tty3270_open,
|
||||
.close = tty3270_close,
|
||||
@ -1745,6 +1762,9 @@ static const struct tty_operations tty3270_ops = {
|
||||
.hangup = tty3270_hangup,
|
||||
.wait_until_sent = tty3270_wait_until_sent,
|
||||
.ioctl = tty3270_ioctl,
|
||||
#ifdef CONFIG_COMPAT
|
||||
.compat_ioctl = tty3270_compat_ioctl,
|
||||
#endif
|
||||
.set_termios = tty3270_set_termios
|
||||
};
|
||||
|
||||
|
@ -312,11 +312,9 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
|
||||
return -ENOSYS;
|
||||
|
||||
/* Besure this device hasn't already been opened */
|
||||
lock_kernel();
|
||||
spin_lock_bh(&logptr->priv_lock);
|
||||
if (logptr->dev_in_use) {
|
||||
spin_unlock_bh(&logptr->priv_lock);
|
||||
unlock_kernel();
|
||||
return -EBUSY;
|
||||
}
|
||||
logptr->dev_in_use = 1;
|
||||
@ -360,9 +358,8 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
|
||||
|| (logptr->iucv_path_severed));
|
||||
if (logptr->iucv_path_severed)
|
||||
goto out_record;
|
||||
ret = nonseekable_open(inode, filp);
|
||||
unlock_kernel();
|
||||
return ret;
|
||||
nonseekable_open(inode, filp);
|
||||
return 0;
|
||||
|
||||
out_record:
|
||||
if (logptr->autorecording)
|
||||
@ -372,7 +369,6 @@ out_path:
|
||||
logptr->path = NULL;
|
||||
out_dev:
|
||||
logptr->dev_in_use = 0;
|
||||
unlock_kernel();
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
|
@ -695,7 +695,6 @@ static int ur_open(struct inode *inode, struct file *file)
|
||||
|
||||
if (accmode == O_RDWR)
|
||||
return -EACCES;
|
||||
lock_kernel();
|
||||
/*
|
||||
* We treat the minor number as the devno of the ur device
|
||||
* to find in the driver tree.
|
||||
@ -749,7 +748,6 @@ static int ur_open(struct inode *inode, struct file *file)
|
||||
goto fail_urfile_free;
|
||||
urf->file_reclen = rc;
|
||||
file->private_data = urf;
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
|
||||
fail_urfile_free:
|
||||
@ -761,7 +759,6 @@ fail_unlock:
|
||||
fail_put:
|
||||
urdev_put(urd);
|
||||
out:
|
||||
unlock_kernel();
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/smp_lock.h>
|
||||
|
||||
#include <asm/ebcdic.h>
|
||||
#include <asm/io.h>
|
||||
@ -49,6 +48,8 @@ static unsigned int vmwdt_interval = 60;
|
||||
static unsigned long vmwdt_is_open;
|
||||
static int vmwdt_expect_close;
|
||||
|
||||
static DEFINE_MUTEX(vmwdt_mutex);
|
||||
|
||||
#define VMWDT_OPEN 0 /* devnode is open or suspend in progress */
|
||||
#define VMWDT_RUNNING 1 /* The watchdog is armed */
|
||||
|
||||
@ -133,15 +134,11 @@ static int __init vmwdt_probe(void)
|
||||
static int vmwdt_open(struct inode *i, struct file *f)
|
||||
{
|
||||
int ret;
|
||||
lock_kernel();
|
||||
if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open)) {
|
||||
unlock_kernel();
|
||||
if (test_and_set_bit(VMWDT_OPEN, &vmwdt_is_open))
|
||||
return -EBUSY;
|
||||
}
|
||||
ret = vmwdt_keepalive();
|
||||
if (ret)
|
||||
clear_bit(VMWDT_OPEN, &vmwdt_is_open);
|
||||
unlock_kernel();
|
||||
return ret ? ret : nonseekable_open(i, f);
|
||||
}
|
||||
|
||||
@ -160,8 +157,7 @@ static struct watchdog_info vmwdt_info = {
|
||||
.identity = "z/VM Watchdog Timer",
|
||||
};
|
||||
|
||||
static int vmwdt_ioctl(struct inode *i, struct file *f,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
static int __vmwdt_ioctl(unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
@ -205,10 +201,19 @@ static int vmwdt_ioctl(struct inode *i, struct file *f,
|
||||
case WDIOC_KEEPALIVE:
|
||||
return vmwdt_keepalive();
|
||||
}
|
||||
|
||||
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,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
@ -288,7 +293,7 @@ static struct notifier_block vmwdt_power_notifier = {
|
||||
static const struct file_operations vmwdt_fops = {
|
||||
.open = &vmwdt_open,
|
||||
.release = &vmwdt_close,
|
||||
.ioctl = &vmwdt_ioctl,
|
||||
.unlocked_ioctl = &vmwdt_ioctl,
|
||||
.write = &vmwdt_write,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
@ -309,6 +314,10 @@ static int __init vmwdt_init(void)
|
||||
ret = register_pm_notifier(&vmwdt_power_notifier);
|
||||
if (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);
|
||||
if (ret) {
|
||||
unregister_pm_notifier(&vmwdt_power_notifier);
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
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_id.o device_pgid.o device_status.o
|
||||
obj-y += ccw_device.o cmf.o
|
||||
|
328
drivers/s390/cio/ccwreq.c
Normal file
328
drivers/s390/cio/ccwreq.c
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
* 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 {
|
||||
__u8 mda[4]; /* model dependent area */
|
||||
} __attribute__ ((packed,aligned(4)));
|
||||
|
||||
enum sch_todo {
|
||||
SCH_TODO_NOTHING,
|
||||
SCH_TODO_UNREG,
|
||||
};
|
||||
|
||||
/* subchannel data structure used by I/O subroutines */
|
||||
struct subchannel {
|
||||
struct subchannel_id schid;
|
||||
@ -95,7 +100,8 @@ struct subchannel {
|
||||
struct device dev; /* entry in device tree */
|
||||
struct css_driver *driver;
|
||||
void *private; /* private per subchannel type data */
|
||||
struct work_struct work;
|
||||
enum sch_todo todo;
|
||||
struct work_struct todo_work;
|
||||
struct schib_config config;
|
||||
} __attribute__ ((aligned(8)));
|
||||
|
||||
|
@ -133,6 +133,8 @@ out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void css_sch_todo(struct work_struct *work);
|
||||
|
||||
static struct subchannel *
|
||||
css_alloc_subchannel(struct subchannel_id schid)
|
||||
{
|
||||
@ -147,6 +149,7 @@ css_alloc_subchannel(struct subchannel_id schid)
|
||||
kfree(sch);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
INIT_WORK(&sch->todo_work, css_sch_todo);
|
||||
return sch;
|
||||
}
|
||||
|
||||
@ -190,6 +193,51 @@ void css_sch_device_unregister(struct subchannel *sch)
|
||||
}
|
||||
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)
|
||||
{
|
||||
int i;
|
||||
@ -376,8 +424,8 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
|
||||
/* Unusable - ignore. */
|
||||
return 0;
|
||||
}
|
||||
CIO_MSG_EVENT(4, "Evaluating schid 0.%x.%04x, event %d, unknown, "
|
||||
"slow path.\n", schid.ssid, schid.sch_no, CIO_OPER);
|
||||
CIO_MSG_EVENT(4, "event: sch 0.%x.%04x, new\n", schid.ssid,
|
||||
schid.sch_no);
|
||||
|
||||
return css_probe_device(schid);
|
||||
}
|
||||
@ -394,6 +442,10 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
|
||||
"Got subchannel machine check but "
|
||||
"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;
|
||||
}
|
||||
|
||||
@ -684,6 +736,7 @@ static int __init setup_css(int nr)
|
||||
css->pseudo_subchannel->dev.parent = &css->device;
|
||||
css->pseudo_subchannel->dev.release = css_subchannel_release;
|
||||
dev_set_name(&css->pseudo_subchannel->dev, "defunct");
|
||||
mutex_init(&css->pseudo_subchannel->reg_mutex);
|
||||
ret = cio_create_sch_lock(css->pseudo_subchannel);
|
||||
if (ret) {
|
||||
kfree(css->pseudo_subchannel);
|
||||
|
@ -11,6 +11,8 @@
|
||||
#include <asm/chpid.h>
|
||||
#include <asm/schid.h>
|
||||
|
||||
#include "cio.h"
|
||||
|
||||
/*
|
||||
* path grouping stuff
|
||||
*/
|
||||
@ -151,4 +153,5 @@ int css_sch_is_valid(struct schib *);
|
||||
|
||||
extern struct workqueue_struct *slow_path_wq;
|
||||
void css_wait_for_slow_path(void);
|
||||
void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo);
|
||||
#endif
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -21,7 +21,6 @@ enum dev_state {
|
||||
DEV_STATE_DISBAND_PGID,
|
||||
DEV_STATE_BOXED,
|
||||
/* states to wait for i/o completion before doing something */
|
||||
DEV_STATE_CLEAR_VERIFY,
|
||||
DEV_STATE_TIMEOUT_KILL,
|
||||
DEV_STATE_QUIESCE,
|
||||
/* special states for devices gone not operational */
|
||||
@ -29,6 +28,7 @@ enum dev_state {
|
||||
DEV_STATE_DISCONNECTED_SENSE_ID,
|
||||
DEV_STATE_CMFCHANGE,
|
||||
DEV_STATE_CMFUPDATE,
|
||||
DEV_STATE_STEAL_LOCK,
|
||||
/* last element! */
|
||||
NR_DEV_STATES
|
||||
};
|
||||
@ -81,17 +81,16 @@ void io_subchannel_init_config(struct subchannel *sch);
|
||||
|
||||
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_recognition(struct ccw_device *);
|
||||
void ccw_device_recognition(struct ccw_device *);
|
||||
int ccw_device_online(struct ccw_device *);
|
||||
int ccw_device_offline(struct ccw_device *);
|
||||
void ccw_device_update_sense_data(struct ccw_device *);
|
||||
int ccw_device_test_sense_data(struct ccw_device *);
|
||||
void ccw_device_schedule_sch_unregister(struct ccw_device *);
|
||||
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. */
|
||||
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 *);
|
||||
int ccw_device_accumulate_and_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. */
|
||||
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);
|
||||
|
||||
/* 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_irq(struct ccw_device *, enum dev_event);
|
||||
void ccw_device_verify_done(struct ccw_device *, int);
|
||||
|
||||
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_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_stlck(struct ccw_device *);
|
||||
|
@ -229,8 +229,8 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
|
||||
ccw_device_set_timeout(cdev, 0);
|
||||
cio_disable_subchannel(sch);
|
||||
if (cio_disable_subchannel(sch))
|
||||
state = DEV_STATE_NOT_OPER;
|
||||
/*
|
||||
* Now that we tried recognition, we have performed device selection
|
||||
* through ssch() and the path information is up to date.
|
||||
@ -263,22 +263,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
|
||||
}
|
||||
switch (state) {
|
||||
case DEV_STATE_NOT_OPER:
|
||||
CIO_MSG_EVENT(2, "SenseID : unknown device %04x on "
|
||||
"subchannel 0.%x.%04x\n",
|
||||
cdev->private->dev_id.devno,
|
||||
sch->schid.ssid, sch->schid.sch_no);
|
||||
break;
|
||||
case DEV_STATE_OFFLINE:
|
||||
if (!cdev->online) {
|
||||
ccw_device_update_sense_data(cdev);
|
||||
/* Issue device info message. */
|
||||
CIO_MSG_EVENT(4, "SenseID : device 0.%x.%04x reports: "
|
||||
"CU Type/Mod = %04X/%02X, Dev Type/Mod "
|
||||
"= %04X/%02X\n",
|
||||
cdev->private->dev_id.ssid,
|
||||
cdev->private->dev_id.devno,
|
||||
cdev->id.cu_type, cdev->id.cu_model,
|
||||
cdev->id.dev_type, cdev->id.dev_model);
|
||||
break;
|
||||
}
|
||||
cdev->private->state = DEV_STATE_OFFLINE;
|
||||
@ -289,16 +277,10 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
|
||||
wake_up(&cdev->private->wait_q);
|
||||
} else {
|
||||
ccw_device_update_sense_data(cdev);
|
||||
PREPARE_WORK(&cdev->private->kick_work,
|
||||
ccw_device_do_unbind_bind);
|
||||
queue_work(ccw_device_work, &cdev->private->kick_work);
|
||||
ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
|
||||
}
|
||||
return;
|
||||
case DEV_STATE_BOXED:
|
||||
CIO_MSG_EVENT(0, "SenseID : boxed device %04x on "
|
||||
" subchannel 0.%x.%04x\n",
|
||||
cdev->private->dev_id.devno,
|
||||
sch->schid.ssid, sch->schid.sch_no);
|
||||
if (cdev->id.cu_type != 0) { /* device was recognized before */
|
||||
cdev->private->flags.recog_done = 1;
|
||||
cdev->private->state = DEV_STATE_BOXED;
|
||||
@ -343,28 +325,16 @@ int ccw_device_notify(struct ccw_device *cdev, int event)
|
||||
return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
|
||||
}
|
||||
|
||||
static void cmf_reenable_delayed(struct work_struct *work)
|
||||
{
|
||||
struct ccw_device_private *priv;
|
||||
struct ccw_device *cdev;
|
||||
|
||||
priv = container_of(work, struct ccw_device_private, kick_work);
|
||||
cdev = priv->cdev;
|
||||
cmf_reenable(cdev);
|
||||
}
|
||||
|
||||
static void ccw_device_oper_notify(struct ccw_device *cdev)
|
||||
{
|
||||
if (ccw_device_notify(cdev, CIO_OPER)) {
|
||||
/* Reenable channel measurements, if needed. */
|
||||
PREPARE_WORK(&cdev->private->kick_work, cmf_reenable_delayed);
|
||||
queue_work(ccw_device_work, &cdev->private->kick_work);
|
||||
ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF);
|
||||
return;
|
||||
}
|
||||
/* Driver doesn't want device back. */
|
||||
ccw_device_set_notoper(cdev);
|
||||
PREPARE_WORK(&cdev->private->kick_work, ccw_device_do_unbind_bind);
|
||||
queue_work(ccw_device_work, &cdev->private->kick_work);
|
||||
ccw_device_sched_todo(cdev, CDEV_TODO_REBIND);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -392,14 +362,14 @@ ccw_device_done(struct ccw_device *cdev, int state)
|
||||
CIO_MSG_EVENT(0, "Boxed device %04x on subchannel %04x\n",
|
||||
cdev->private->dev_id.devno, sch->schid.sch_no);
|
||||
if (cdev->online && !ccw_device_notify(cdev, CIO_BOXED))
|
||||
ccw_device_schedule_sch_unregister(cdev);
|
||||
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
|
||||
cdev->private->flags.donotify = 0;
|
||||
break;
|
||||
case DEV_STATE_NOT_OPER:
|
||||
CIO_MSG_EVENT(0, "Device %04x gone on subchannel %04x\n",
|
||||
cdev->private->dev_id.devno, sch->schid.sch_no);
|
||||
if (!ccw_device_notify(cdev, CIO_GONE))
|
||||
ccw_device_schedule_sch_unregister(cdev);
|
||||
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
|
||||
else
|
||||
ccw_device_set_disconnected(cdev);
|
||||
cdev->private->flags.donotify = 0;
|
||||
@ -409,7 +379,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
|
||||
"%04x\n", cdev->private->dev_id.devno,
|
||||
sch->schid.sch_no);
|
||||
if (!ccw_device_notify(cdev, CIO_NO_PATH))
|
||||
ccw_device_schedule_sch_unregister(cdev);
|
||||
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
|
||||
else
|
||||
ccw_device_set_disconnected(cdev);
|
||||
cdev->private->flags.donotify = 0;
|
||||
@ -425,107 +395,12 @@ ccw_device_done(struct ccw_device *cdev, int state)
|
||||
wake_up(&cdev->private->wait_q);
|
||||
}
|
||||
|
||||
static int cmp_pgid(struct pgid *p1, struct pgid *p2)
|
||||
{
|
||||
char *c1;
|
||||
char *c2;
|
||||
|
||||
c1 = (char *)p1;
|
||||
c2 = (char *)p2;
|
||||
|
||||
return memcmp(c1 + 1, c2 + 1, sizeof(struct pgid) - 1);
|
||||
}
|
||||
|
||||
static void __ccw_device_get_common_pgid(struct ccw_device *cdev)
|
||||
{
|
||||
int i;
|
||||
int last;
|
||||
|
||||
last = 0;
|
||||
for (i = 0; i < 8; i++) {
|
||||
if (cdev->private->pgid[i].inf.ps.state1 == SNID_STATE1_RESET)
|
||||
/* No PGID yet */
|
||||
continue;
|
||||
if (cdev->private->pgid[last].inf.ps.state1 ==
|
||||
SNID_STATE1_RESET) {
|
||||
/* First non-zero PGID */
|
||||
last = i;
|
||||
continue;
|
||||
}
|
||||
if (cmp_pgid(&cdev->private->pgid[i],
|
||||
&cdev->private->pgid[last]) == 0)
|
||||
/* Non-conflicting PGIDs */
|
||||
continue;
|
||||
|
||||
/* PGID mismatch, can't pathgroup. */
|
||||
CIO_MSG_EVENT(0, "SNID - pgid mismatch for device "
|
||||
"0.%x.%04x, can't pathgroup\n",
|
||||
cdev->private->dev_id.ssid,
|
||||
cdev->private->dev_id.devno);
|
||||
cdev->private->options.pgroup = 0;
|
||||
return;
|
||||
}
|
||||
if (cdev->private->pgid[last].inf.ps.state1 ==
|
||||
SNID_STATE1_RESET)
|
||||
/* No previous pgid found */
|
||||
memcpy(&cdev->private->pgid[0],
|
||||
&channel_subsystems[0]->global_pgid,
|
||||
sizeof(struct pgid));
|
||||
else
|
||||
/* Use existing pgid */
|
||||
memcpy(&cdev->private->pgid[0], &cdev->private->pgid[last],
|
||||
sizeof(struct pgid));
|
||||
}
|
||||
|
||||
/*
|
||||
* Function called from device_pgid.c after sense path ground has completed.
|
||||
*/
|
||||
void
|
||||
ccw_device_sense_pgid_done(struct ccw_device *cdev, int err)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
switch (err) {
|
||||
case -EOPNOTSUPP: /* path grouping not supported, use nop instead. */
|
||||
cdev->private->options.pgroup = 0;
|
||||
break;
|
||||
case 0: /* success */
|
||||
case -EACCES: /* partial success, some paths not operational */
|
||||
/* Check if all pgids are equal or 0. */
|
||||
__ccw_device_get_common_pgid(cdev);
|
||||
break;
|
||||
case -ETIME: /* Sense path group id stopped by timeout. */
|
||||
case -EUSERS: /* device is reserved for someone else. */
|
||||
ccw_device_done(cdev, DEV_STATE_BOXED);
|
||||
return;
|
||||
default:
|
||||
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
|
||||
return;
|
||||
}
|
||||
/* Start Path Group verification. */
|
||||
cdev->private->state = DEV_STATE_VERIFY;
|
||||
cdev->private->flags.doverify = 0;
|
||||
ccw_device_verify_start(cdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start device recognition.
|
||||
*/
|
||||
int
|
||||
ccw_device_recognition(struct ccw_device *cdev)
|
||||
void ccw_device_recognition(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
int ret;
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
|
||||
if (ret != 0)
|
||||
/* Couldn't enable the subchannel for i/o. Sick device. */
|
||||
return ret;
|
||||
|
||||
/* After 60s the device recognition is considered to have failed. */
|
||||
ccw_device_set_timeout(cdev, 60*HZ);
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
|
||||
/*
|
||||
* We used to start here with a sense pgid to find out whether a device
|
||||
@ -537,32 +412,33 @@ ccw_device_recognition(struct ccw_device *cdev)
|
||||
*/
|
||||
cdev->private->flags.recog_done = 0;
|
||||
cdev->private->state = DEV_STATE_SENSE_ID;
|
||||
if (cio_enable_subchannel(sch, (u32) (addr_t) sch)) {
|
||||
ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
|
||||
return;
|
||||
}
|
||||
ccw_device_sense_id_start(cdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle timeout in device recognition.
|
||||
* Handle events for states that use the ccw request infrastructure.
|
||||
*/
|
||||
static void
|
||||
ccw_device_recog_timeout(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
static void ccw_device_request_event(struct ccw_device *cdev, enum dev_event e)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ccw_device_cancel_halt_clear(cdev);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
ccw_device_recog_done(cdev, DEV_STATE_BOXED);
|
||||
switch (e) {
|
||||
case DEV_EVENT_NOTOPER:
|
||||
ccw_request_notoper(cdev);
|
||||
break;
|
||||
case -ENODEV:
|
||||
ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
|
||||
case DEV_EVENT_INTERRUPT:
|
||||
ccw_request_handler(cdev);
|
||||
break;
|
||||
case DEV_EVENT_TIMEOUT:
|
||||
ccw_request_timeout(cdev);
|
||||
break;
|
||||
default:
|
||||
ccw_device_set_timeout(cdev, 3*HZ);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
ccw_device_verify_done(struct ccw_device *cdev, int err)
|
||||
{
|
||||
@ -571,21 +447,18 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
/* Update schib - pom may have changed. */
|
||||
if (cio_update_schib(sch)) {
|
||||
cdev->private->flags.donotify = 0;
|
||||
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
|
||||
return;
|
||||
err = -ENODEV;
|
||||
goto callback;
|
||||
}
|
||||
/* Update lpm with verified path mask. */
|
||||
sch->lpm = sch->vpm;
|
||||
/* Repeat path verification? */
|
||||
if (cdev->private->flags.doverify) {
|
||||
cdev->private->flags.doverify = 0;
|
||||
ccw_device_verify_start(cdev);
|
||||
return;
|
||||
}
|
||||
callback:
|
||||
switch (err) {
|
||||
case -EOPNOTSUPP: /* path grouping not supported, just set online. */
|
||||
cdev->private->options.pgroup = 0;
|
||||
case 0:
|
||||
ccw_device_done(cdev, DEV_STATE_ONLINE);
|
||||
/* Deliver fake irb to device driver, if needed. */
|
||||
@ -604,18 +477,20 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
|
||||
}
|
||||
break;
|
||||
case -ETIME:
|
||||
case -EUSERS:
|
||||
/* Reset oper notify indication after verify error. */
|
||||
cdev->private->flags.donotify = 0;
|
||||
ccw_device_done(cdev, DEV_STATE_BOXED);
|
||||
break;
|
||||
case -EACCES:
|
||||
/* Reset oper notify indication after verify error. */
|
||||
cdev->private->flags.donotify = 0;
|
||||
ccw_device_done(cdev, DEV_STATE_DISCONNECTED);
|
||||
break;
|
||||
default:
|
||||
/* Reset oper notify indication after verify error. */
|
||||
cdev->private->flags.donotify = 0;
|
||||
if (cdev->online) {
|
||||
ccw_device_set_timeout(cdev, 0);
|
||||
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
|
||||
} else
|
||||
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
|
||||
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -640,17 +515,9 @@ ccw_device_online(struct ccw_device *cdev)
|
||||
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
|
||||
return ret;
|
||||
}
|
||||
/* Do we want to do path grouping? */
|
||||
if (!cdev->private->options.pgroup) {
|
||||
/* Start initial path verification. */
|
||||
cdev->private->state = DEV_STATE_VERIFY;
|
||||
cdev->private->flags.doverify = 0;
|
||||
ccw_device_verify_start(cdev);
|
||||
return 0;
|
||||
}
|
||||
/* Do a SensePGID first. */
|
||||
cdev->private->state = DEV_STATE_SENSE_PGID;
|
||||
ccw_device_sense_pgid_start(cdev);
|
||||
/* Start initial path verification. */
|
||||
cdev->private->state = DEV_STATE_VERIFY;
|
||||
ccw_device_verify_start(cdev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -666,7 +533,6 @@ ccw_device_disband_done(struct ccw_device *cdev, int err)
|
||||
break;
|
||||
default:
|
||||
cdev->private->flags.donotify = 0;
|
||||
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
|
||||
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
|
||||
break;
|
||||
}
|
||||
@ -703,7 +569,7 @@ ccw_device_offline(struct ccw_device *cdev)
|
||||
if (cdev->private->state != DEV_STATE_ONLINE)
|
||||
return -EINVAL;
|
||||
/* Are we doing path grouping? */
|
||||
if (!cdev->private->options.pgroup) {
|
||||
if (!cdev->private->flags.pgroup) {
|
||||
/* No, set state offline immediately. */
|
||||
ccw_device_done(cdev, DEV_STATE_OFFLINE);
|
||||
return 0;
|
||||
@ -714,36 +580,6 @@ ccw_device_offline(struct ccw_device *cdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle timeout in device online/offline process.
|
||||
*/
|
||||
static void
|
||||
ccw_device_onoff_timeout(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = ccw_device_cancel_halt_clear(cdev);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
ccw_device_done(cdev, DEV_STATE_BOXED);
|
||||
break;
|
||||
case -ENODEV:
|
||||
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
|
||||
break;
|
||||
default:
|
||||
ccw_device_set_timeout(cdev, 3*HZ);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle not oper event in device recognition.
|
||||
*/
|
||||
static void
|
||||
ccw_device_recog_notoper(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
ccw_device_recog_done(cdev, DEV_STATE_NOT_OPER);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle not operational event in non-special state.
|
||||
*/
|
||||
@ -751,7 +587,7 @@ static void ccw_device_generic_notoper(struct ccw_device *cdev,
|
||||
enum dev_event dev_event)
|
||||
{
|
||||
if (!ccw_device_notify(cdev, CIO_GONE))
|
||||
ccw_device_schedule_sch_unregister(cdev);
|
||||
ccw_device_sched_todo(cdev, CDEV_TODO_UNREG);
|
||||
else
|
||||
ccw_device_set_disconnected(cdev);
|
||||
}
|
||||
@ -802,10 +638,26 @@ ccw_device_online_verify(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
}
|
||||
/* Device is idle, we can do the path verification. */
|
||||
cdev->private->state = DEV_STATE_VERIFY;
|
||||
cdev->private->flags.doverify = 0;
|
||||
ccw_device_verify_start(cdev);
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle path verification event in boxed state.
|
||||
*/
|
||||
static void ccw_device_boxed_verify(struct ccw_device *cdev,
|
||||
enum dev_event dev_event)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
|
||||
if (cdev->online) {
|
||||
if (cio_enable_subchannel(sch, (u32) (addr_t) sch))
|
||||
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
|
||||
else
|
||||
ccw_device_online_verify(cdev, dev_event);
|
||||
} else
|
||||
css_schedule_eval(sch->schid);
|
||||
}
|
||||
|
||||
/*
|
||||
* Got an interrupt for a normal io (state online).
|
||||
*/
|
||||
@ -904,12 +756,6 @@ ccw_device_w4sense(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
*/
|
||||
if (scsw_fctl(&irb->scsw) &
|
||||
(SCSW_FCTL_CLEAR_FUNC | SCSW_FCTL_HALT_FUNC)) {
|
||||
/* Retry Basic Sense if requested. */
|
||||
if (cdev->private->flags.intretry) {
|
||||
cdev->private->flags.intretry = 0;
|
||||
ccw_device_do_sense(cdev, irb);
|
||||
return;
|
||||
}
|
||||
cdev->private->flags.dosense = 0;
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
ccw_device_accumulate_irb(cdev, irb);
|
||||
@ -932,21 +778,6 @@ call_handler:
|
||||
ccw_device_online_verify(cdev, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
struct irb *irb;
|
||||
|
||||
irb = (struct irb *) __LC_IRB;
|
||||
/* Accumulate status. We don't do basic sense. */
|
||||
ccw_device_accumulate_irb(cdev, irb);
|
||||
/* Remember to clear irb to avoid residuals. */
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
/* Try to start delayed device verification. */
|
||||
ccw_device_online_verify(cdev, 0);
|
||||
/* Note: Don't call handler for cio initiated clear! */
|
||||
}
|
||||
|
||||
static void
|
||||
ccw_device_killing_irq(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
@ -1003,32 +834,6 @@ ccw_device_delay_verify(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
cdev->private->flags.doverify = 1;
|
||||
}
|
||||
|
||||
static void
|
||||
ccw_device_stlck_done(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
struct irb *irb;
|
||||
|
||||
switch (dev_event) {
|
||||
case DEV_EVENT_INTERRUPT:
|
||||
irb = (struct irb *) __LC_IRB;
|
||||
/* Check for unsolicited interrupt. */
|
||||
if ((scsw_stctl(&irb->scsw) ==
|
||||
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) &&
|
||||
(!scsw_cc(&irb->scsw)))
|
||||
/* FIXME: we should restart stlck here, but this
|
||||
* is extremely unlikely ... */
|
||||
goto out_wakeup;
|
||||
|
||||
ccw_device_accumulate_irb(cdev, irb);
|
||||
/* We don't care about basic sense etc. */
|
||||
break;
|
||||
default: /* timeout */
|
||||
break;
|
||||
}
|
||||
out_wakeup:
|
||||
wake_up(&cdev->private->wait_q);
|
||||
}
|
||||
|
||||
static void
|
||||
ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
@ -1038,10 +843,6 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
if (cio_enable_subchannel(sch, (u32)(addr_t)sch) != 0)
|
||||
/* Couldn't enable the subchannel for i/o. Sick device. */
|
||||
return;
|
||||
|
||||
/* After 60s the device recognition is considered to have failed. */
|
||||
ccw_device_set_timeout(cdev, 60*HZ);
|
||||
|
||||
cdev->private->state = DEV_STATE_DISCONNECTED_SENSE_ID;
|
||||
ccw_device_sense_id_start(cdev);
|
||||
}
|
||||
@ -1072,22 +873,20 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev)
|
||||
|
||||
/* We should also udate ssd info, but this has to wait. */
|
||||
/* Check if this is another device which appeared on the same sch. */
|
||||
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
|
||||
PREPARE_WORK(&cdev->private->kick_work,
|
||||
ccw_device_move_to_orphanage);
|
||||
queue_work(slow_path_wq, &cdev->private->kick_work);
|
||||
} else
|
||||
if (sch->schib.pmcw.dev != cdev->private->dev_id.devno)
|
||||
css_schedule_eval(sch->schid);
|
||||
else
|
||||
ccw_device_start_id(cdev, 0);
|
||||
}
|
||||
|
||||
static void
|
||||
ccw_device_offline_irq(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
static void ccw_device_disabled_irq(struct ccw_device *cdev,
|
||||
enum dev_event dev_event)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
/*
|
||||
* An interrupt in state offline means a previous disable was not
|
||||
* An interrupt in a disabled state means a previous disable was not
|
||||
* successful - should not happen, but we try to disable again.
|
||||
*/
|
||||
cio_disable_subchannel(sch);
|
||||
@ -1113,10 +912,7 @@ static void
|
||||
ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
ccw_device_set_timeout(cdev, 0);
|
||||
if (dev_event == DEV_EVENT_NOTOPER)
|
||||
cdev->private->state = DEV_STATE_NOT_OPER;
|
||||
else
|
||||
cdev->private->state = DEV_STATE_OFFLINE;
|
||||
cdev->private->state = DEV_STATE_NOT_OPER;
|
||||
wake_up(&cdev->private->wait_q);
|
||||
}
|
||||
|
||||
@ -1126,17 +922,11 @@ ccw_device_quiesce_timeout(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
int ret;
|
||||
|
||||
ret = ccw_device_cancel_halt_clear(cdev);
|
||||
switch (ret) {
|
||||
case 0:
|
||||
cdev->private->state = DEV_STATE_OFFLINE;
|
||||
wake_up(&cdev->private->wait_q);
|
||||
break;
|
||||
case -ENODEV:
|
||||
if (ret == -EBUSY) {
|
||||
ccw_device_set_timeout(cdev, HZ/10);
|
||||
} else {
|
||||
cdev->private->state = DEV_STATE_NOT_OPER;
|
||||
wake_up(&cdev->private->wait_q);
|
||||
break;
|
||||
default:
|
||||
ccw_device_set_timeout(cdev, HZ/10);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1149,51 +939,38 @@ ccw_device_nop(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Bug operation action.
|
||||
*/
|
||||
static void
|
||||
ccw_device_bug(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
{
|
||||
CIO_MSG_EVENT(0, "Internal state [%i][%i] not handled for device "
|
||||
"0.%x.%04x\n", cdev->private->state, dev_event,
|
||||
cdev->private->dev_id.ssid,
|
||||
cdev->private->dev_id.devno);
|
||||
BUG();
|
||||
}
|
||||
|
||||
/*
|
||||
* device statemachine
|
||||
*/
|
||||
fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
|
||||
[DEV_STATE_NOT_OPER] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_nop,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_bug,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
},
|
||||
[DEV_STATE_SENSE_PGID] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_sense_pgid_irq,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout,
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_request_event,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_request_event,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_request_event,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
},
|
||||
[DEV_STATE_SENSE_ID] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_recog_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout,
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_request_event,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_request_event,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_request_event,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
},
|
||||
[DEV_STATE_OFFLINE] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_offline_irq,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_disabled_irq,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_offline_verify,
|
||||
},
|
||||
[DEV_STATE_VERIFY] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_verify_irq,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout,
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_request_event,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_request_event,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_request_event,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_delay_verify,
|
||||
},
|
||||
[DEV_STATE_ONLINE] = {
|
||||
@ -1209,24 +986,18 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
|
||||
[DEV_EVENT_VERIFY] = ccw_device_online_verify,
|
||||
},
|
||||
[DEV_STATE_DISBAND_PGID] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_disband_irq,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_onoff_timeout,
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_request_event,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_request_event,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_request_event,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
},
|
||||
[DEV_STATE_BOXED] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_stlck_done,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_stlck_done,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_nop,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_boxed_verify,
|
||||
},
|
||||
/* states to wait for i/o completion before doing something */
|
||||
[DEV_STATE_CLEAR_VERIFY] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_clear_verify,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
},
|
||||
[DEV_STATE_TIMEOUT_KILL] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_generic_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_killing_irq,
|
||||
@ -1243,13 +1014,13 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
|
||||
[DEV_STATE_DISCONNECTED] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_nop,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_start_id,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_bug,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_nop,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_start_id,
|
||||
},
|
||||
[DEV_STATE_DISCONNECTED_SENSE_ID] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_recog_notoper,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_sense_id_irq,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_recog_timeout,
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_request_event,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_request_event,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_request_event,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
},
|
||||
[DEV_STATE_CMFCHANGE] = {
|
||||
@ -1264,6 +1035,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_update_cmfblock,
|
||||
},
|
||||
[DEV_STATE_STEAL_LOCK] = {
|
||||
[DEV_EVENT_NOTOPER] = ccw_device_request_event,
|
||||
[DEV_EVENT_INTERRUPT] = ccw_device_request_event,
|
||||
[DEV_EVENT_TIMEOUT] = ccw_device_request_event,
|
||||
[DEV_EVENT_VERIFY] = ccw_device_nop,
|
||||
},
|
||||
};
|
||||
|
||||
EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
|
||||
|
@ -1,40 +1,39 @@
|
||||
/*
|
||||
* drivers/s390/cio/device_id.c
|
||||
* CCW device SENSE ID I/O handling.
|
||||
*
|
||||
* Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
|
||||
* IBM Corporation
|
||||
* Author(s): Cornelia Huck (cornelia.huck@de.ibm.com)
|
||||
* Martin Schwidefsky (schwidefsky@de.ibm.com)
|
||||
*
|
||||
* Sense ID functions.
|
||||
* Copyright IBM Corp. 2002,2009
|
||||
* Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
|
||||
* Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
* Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <linux/string.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/errno.h>
|
||||
#include <asm/ccwdev.h>
|
||||
#include <asm/delay.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/cio.h>
|
||||
#include <asm/lowcore.h>
|
||||
#include <asm/diag.h>
|
||||
|
||||
#include "cio.h"
|
||||
#include "cio_debug.h"
|
||||
#include "css.h"
|
||||
#include "device.h"
|
||||
#include "ioasm.h"
|
||||
#include "io_sch.h"
|
||||
|
||||
#define SENSE_ID_RETRIES 256
|
||||
#define SENSE_ID_TIMEOUT (10 * HZ)
|
||||
#define SENSE_ID_MIN_LEN 4
|
||||
#define SENSE_ID_BASIC_LEN 7
|
||||
|
||||
/**
|
||||
* vm_vdev_to_cu_type - Convert vm virtual device into control unit type
|
||||
* for certain devices.
|
||||
* @class: virtual device class
|
||||
* @type: virtual device type
|
||||
* diag210_to_senseid - convert diag 0x210 data to sense id information
|
||||
* @senseid: sense id
|
||||
* @diag: diag 0x210 data
|
||||
*
|
||||
* Returns control unit type if a match was made or %0xffff otherwise.
|
||||
* Return 0 on success, non-zero otherwise.
|
||||
*/
|
||||
static int vm_vdev_to_cu_type(int class, int type)
|
||||
static int diag210_to_senseid(struct senseid *senseid, struct diag210 *diag)
|
||||
{
|
||||
static struct {
|
||||
int class, type, cu_type;
|
||||
@ -71,253 +70,153 @@ static int vm_vdev_to_cu_type(int class, int type)
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(vm_devices); i++)
|
||||
if (class == vm_devices[i].class && type == vm_devices[i].type)
|
||||
return vm_devices[i].cu_type;
|
||||
|
||||
return 0xffff;
|
||||
}
|
||||
|
||||
/**
|
||||
* diag_get_dev_info - retrieve device information via DIAG X'210'
|
||||
* @devno: device number
|
||||
* @ps: pointer to sense ID data area
|
||||
*
|
||||
* Returns zero on success, non-zero otherwise.
|
||||
*/
|
||||
static int diag_get_dev_info(u16 devno, struct senseid *ps)
|
||||
{
|
||||
struct diag210 diag_data;
|
||||
int ccode;
|
||||
|
||||
CIO_TRACE_EVENT (4, "VMvdinf");
|
||||
|
||||
diag_data = (struct diag210) {
|
||||
.vrdcdvno = devno,
|
||||
.vrdclen = sizeof (diag_data),
|
||||
};
|
||||
|
||||
ccode = diag210 (&diag_data);
|
||||
if ((ccode == 0) || (ccode == 2)) {
|
||||
ps->reserved = 0xff;
|
||||
|
||||
/* Special case for osa devices. */
|
||||
if (diag_data.vrdcvcla == 0x02 && diag_data.vrdcvtyp == 0x20) {
|
||||
ps->cu_type = 0x3088;
|
||||
ps->cu_model = 0x60;
|
||||
/* Special case for osa devices. */
|
||||
if (diag->vrdcvcla == 0x02 && diag->vrdcvtyp == 0x20) {
|
||||
senseid->cu_type = 0x3088;
|
||||
senseid->cu_model = 0x60;
|
||||
senseid->reserved = 0xff;
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < ARRAY_SIZE(vm_devices); i++) {
|
||||
if (diag->vrdcvcla == vm_devices[i].class &&
|
||||
diag->vrdcvtyp == vm_devices[i].type) {
|
||||
senseid->cu_type = vm_devices[i].cu_type;
|
||||
senseid->reserved = 0xff;
|
||||
return 0;
|
||||
}
|
||||
ps->cu_type = vm_vdev_to_cu_type(diag_data.vrdcvcla,
|
||||
diag_data.vrdcvtyp);
|
||||
if (ps->cu_type != 0xffff)
|
||||
return 0;
|
||||
}
|
||||
|
||||
CIO_MSG_EVENT(0, "DIAG X'210' for device %04X returned (cc = %d):"
|
||||
"vdev class : %02X, vdev type : %04X \n ... "
|
||||
"rdev class : %02X, rdev type : %04X, "
|
||||
"rdev model: %02X\n",
|
||||
devno, ccode,
|
||||
diag_data.vrdcvcla, diag_data.vrdcvtyp,
|
||||
diag_data.vrdcrccl, diag_data.vrdccrty,
|
||||
diag_data.vrdccrmd);
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Start Sense ID helper function.
|
||||
* Try to obtain the 'control unit'/'device type' information
|
||||
* associated with the subchannel.
|
||||
/**
|
||||
* diag_get_dev_info - retrieve device information via diag 0x210
|
||||
* @cdev: ccw device
|
||||
*
|
||||
* Returns zero on success, non-zero otherwise.
|
||||
*/
|
||||
static int
|
||||
__ccw_device_sense_id_start(struct ccw_device *cdev)
|
||||
static int diag210_get_dev_info(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
struct ccw1 *ccw;
|
||||
int ret;
|
||||
struct ccw_dev_id *dev_id = &cdev->private->dev_id;
|
||||
struct senseid *senseid = &cdev->private->senseid;
|
||||
struct diag210 diag_data;
|
||||
int rc;
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
/* Setup sense channel program. */
|
||||
ccw = cdev->private->iccws;
|
||||
ccw->cmd_code = CCW_CMD_SENSE_ID;
|
||||
ccw->cda = (__u32) __pa (&cdev->private->senseid);
|
||||
ccw->count = sizeof (struct senseid);
|
||||
ccw->flags = CCW_FLAG_SLI;
|
||||
if (dev_id->ssid != 0)
|
||||
return -ENODEV;
|
||||
memset(&diag_data, 0, sizeof(diag_data));
|
||||
diag_data.vrdcdvno = dev_id->devno;
|
||||
diag_data.vrdclen = sizeof(diag_data);
|
||||
rc = diag210(&diag_data);
|
||||
CIO_TRACE_EVENT(4, "diag210");
|
||||
CIO_HEX_EVENT(4, &rc, sizeof(rc));
|
||||
CIO_HEX_EVENT(4, &diag_data, sizeof(diag_data));
|
||||
if (rc != 0 && rc != 2)
|
||||
goto err_failed;
|
||||
if (diag210_to_senseid(senseid, &diag_data))
|
||||
goto err_unknown;
|
||||
return 0;
|
||||
|
||||
/* Reset device status. */
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
|
||||
/* Try on every path. */
|
||||
ret = -ENODEV;
|
||||
while (cdev->private->imask != 0) {
|
||||
cdev->private->senseid.cu_type = 0xFFFF;
|
||||
if ((sch->opm & cdev->private->imask) != 0 &&
|
||||
cdev->private->iretry > 0) {
|
||||
cdev->private->iretry--;
|
||||
/* Reset internal retry indication. */
|
||||
cdev->private->flags.intretry = 0;
|
||||
ret = cio_start (sch, cdev->private->iccws,
|
||||
cdev->private->imask);
|
||||
/* ret is 0, -EBUSY, -EACCES or -ENODEV */
|
||||
if (ret != -EACCES)
|
||||
return ret;
|
||||
}
|
||||
cdev->private->imask >>= 1;
|
||||
cdev->private->iretry = 5;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
ccw_device_sense_id_start(struct ccw_device *cdev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
memset (&cdev->private->senseid, 0, sizeof (struct senseid));
|
||||
cdev->private->imask = 0x80;
|
||||
cdev->private->iretry = 5;
|
||||
ret = __ccw_device_sense_id_start(cdev);
|
||||
if (ret && ret != -EBUSY)
|
||||
ccw_device_sense_id_done(cdev, ret);
|
||||
err_unknown:
|
||||
CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: unknown diag210 data\n",
|
||||
dev_id->ssid, dev_id->devno);
|
||||
return -ENODEV;
|
||||
err_failed:
|
||||
CIO_MSG_EVENT(0, "snsid: device 0.%x.%04x: diag210 failed (rc=%d)\n",
|
||||
dev_id->ssid, dev_id->devno, rc);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called from interrupt context to check if a valid answer
|
||||
* to Sense ID was received.
|
||||
* Initialize SENSE ID data.
|
||||
*/
|
||||
static int
|
||||
ccw_device_check_sense_id(struct ccw_device *cdev)
|
||||
static void snsid_init(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
struct irb *irb;
|
||||
cdev->private->flags.esid = 0;
|
||||
memset(&cdev->private->senseid, 0, sizeof(cdev->private->senseid));
|
||||
cdev->private->senseid.cu_type = 0xffff;
|
||||
}
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
irb = &cdev->private->irb;
|
||||
/*
|
||||
* Check for complete SENSE ID data.
|
||||
*/
|
||||
static int snsid_check(struct ccw_device *cdev, void *data)
|
||||
{
|
||||
struct cmd_scsw *scsw = &cdev->private->irb.scsw.cmd;
|
||||
int len = sizeof(struct senseid) - scsw->count;
|
||||
|
||||
/* Check the error cases. */
|
||||
if (irb->scsw.cmd.fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC)) {
|
||||
/* Retry Sense ID if requested. */
|
||||
if (cdev->private->flags.intretry) {
|
||||
cdev->private->flags.intretry = 0;
|
||||
return -EAGAIN;
|
||||
}
|
||||
return -ETIME;
|
||||
}
|
||||
if (irb->esw.esw0.erw.cons && (irb->ecw[0] & SNS0_CMD_REJECT)) {
|
||||
/*
|
||||
* if the device doesn't support the SenseID
|
||||
* command further retries wouldn't help ...
|
||||
* NB: We don't check here for intervention required like we
|
||||
* did before, because tape devices with no tape inserted
|
||||
* may present this status *in conjunction with* the
|
||||
* sense id information. So, for intervention required,
|
||||
* we use the "whack it until it talks" strategy...
|
||||
*/
|
||||
CIO_MSG_EVENT(0, "SenseID : device %04x on Subchannel "
|
||||
"0.%x.%04x reports cmd reject\n",
|
||||
cdev->private->dev_id.devno, sch->schid.ssid,
|
||||
sch->schid.sch_no);
|
||||
/* Check for incomplete SENSE ID data. */
|
||||
if (len < SENSE_ID_MIN_LEN)
|
||||
goto out_restart;
|
||||
if (cdev->private->senseid.cu_type == 0xffff)
|
||||
goto out_restart;
|
||||
/* Check for incompatible SENSE ID data. */
|
||||
if (cdev->private->senseid.reserved != 0xff)
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
if (irb->esw.esw0.erw.cons) {
|
||||
CIO_MSG_EVENT(2, "SenseID : UC on dev 0.%x.%04x, "
|
||||
"lpum %02X, cnt %02d, sns :"
|
||||
" %02X%02X%02X%02X %02X%02X%02X%02X ...\n",
|
||||
cdev->private->dev_id.ssid,
|
||||
cdev->private->dev_id.devno,
|
||||
irb->esw.esw0.sublog.lpum,
|
||||
irb->esw.esw0.erw.scnt,
|
||||
irb->ecw[0], irb->ecw[1],
|
||||
irb->ecw[2], irb->ecw[3],
|
||||
irb->ecw[4], irb->ecw[5],
|
||||
irb->ecw[6], irb->ecw[7]);
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (irb->scsw.cmd.cc == 3) {
|
||||
u8 lpm;
|
||||
/* Check for extended-identification information. */
|
||||
if (len > SENSE_ID_BASIC_LEN)
|
||||
cdev->private->flags.esid = 1;
|
||||
return 0;
|
||||
|
||||
lpm = to_io_private(sch)->orb.cmd.lpm;
|
||||
if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
|
||||
CIO_MSG_EVENT(4, "SenseID : path %02X for device %04x "
|
||||
"on subchannel 0.%x.%04x is "
|
||||
"'not operational'\n", lpm,
|
||||
cdev->private->dev_id.devno,
|
||||
sch->schid.ssid, sch->schid.sch_no);
|
||||
return -EACCES;
|
||||
}
|
||||
|
||||
/* Did we get a proper answer ? */
|
||||
if (irb->scsw.cmd.cc == 0 && cdev->private->senseid.cu_type != 0xFFFF &&
|
||||
cdev->private->senseid.reserved == 0xFF) {
|
||||
if (irb->scsw.cmd.count < sizeof(struct senseid) - 8)
|
||||
cdev->private->flags.esid = 1;
|
||||
return 0; /* Success */
|
||||
}
|
||||
|
||||
/* Hmm, whatever happened, try again. */
|
||||
CIO_MSG_EVENT(2, "SenseID : start_IO() for device %04x on "
|
||||
"subchannel 0.%x.%04x returns status %02X%02X\n",
|
||||
cdev->private->dev_id.devno, sch->schid.ssid,
|
||||
sch->schid.sch_no,
|
||||
irb->scsw.cmd.dstat, irb->scsw.cmd.cstat);
|
||||
out_restart:
|
||||
snsid_init(cdev);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
/*
|
||||
* Got interrupt for Sense ID.
|
||||
* Process SENSE ID request result.
|
||||
*/
|
||||
void
|
||||
ccw_device_sense_id_irq(struct ccw_device *cdev, enum dev_event dev_event)
|
||||
static void snsid_callback(struct ccw_device *cdev, void *data, int rc)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
struct irb *irb;
|
||||
int ret;
|
||||
struct ccw_dev_id *id = &cdev->private->dev_id;
|
||||
struct senseid *senseid = &cdev->private->senseid;
|
||||
int vm = 0;
|
||||
|
||||
sch = to_subchannel(cdev->dev.parent);
|
||||
irb = (struct irb *) __LC_IRB;
|
||||
/* Retry sense id, if needed. */
|
||||
if (irb->scsw.cmd.stctl ==
|
||||
(SCSW_STCTL_STATUS_PEND | SCSW_STCTL_ALERT_STATUS)) {
|
||||
if ((irb->scsw.cmd.cc == 1) || !irb->scsw.cmd.actl) {
|
||||
ret = __ccw_device_sense_id_start(cdev);
|
||||
if (ret && ret != -EBUSY)
|
||||
ccw_device_sense_id_done(cdev, ret);
|
||||
if (rc && MACHINE_IS_VM) {
|
||||
/* Try diag 0x210 fallback on z/VM. */
|
||||
snsid_init(cdev);
|
||||
if (diag210_get_dev_info(cdev) == 0) {
|
||||
rc = 0;
|
||||
vm = 1;
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (ccw_device_accumulate_and_sense(cdev, irb) != 0)
|
||||
return;
|
||||
ret = ccw_device_check_sense_id(cdev);
|
||||
memset(&cdev->private->irb, 0, sizeof(struct irb));
|
||||
switch (ret) {
|
||||
/* 0, -ETIME, -EOPNOTSUPP, -EAGAIN or -EACCES */
|
||||
case 0: /* Sense id succeeded. */
|
||||
case -ETIME: /* Sense id stopped by timeout. */
|
||||
ccw_device_sense_id_done(cdev, ret);
|
||||
break;
|
||||
case -EACCES: /* channel is not operational. */
|
||||
sch->lpm &= ~cdev->private->imask;
|
||||
cdev->private->imask >>= 1;
|
||||
cdev->private->iretry = 5;
|
||||
/* fall through. */
|
||||
case -EAGAIN: /* try again. */
|
||||
ret = __ccw_device_sense_id_start(cdev);
|
||||
if (ret == 0 || ret == -EBUSY)
|
||||
break;
|
||||
/* fall through. */
|
||||
default: /* Sense ID failed. Try asking VM. */
|
||||
if (MACHINE_IS_VM)
|
||||
ret = diag_get_dev_info(cdev->private->dev_id.devno,
|
||||
&cdev->private->senseid);
|
||||
else
|
||||
/*
|
||||
* If we can't couldn't identify the device type we
|
||||
* consider the device "not operational".
|
||||
*/
|
||||
ret = -ENODEV;
|
||||
|
||||
ccw_device_sense_id_done(cdev, ret);
|
||||
break;
|
||||
}
|
||||
CIO_MSG_EVENT(2, "snsid: device 0.%x.%04x: rc=%d %04x/%02x "
|
||||
"%04x/%02x%s\n", id->ssid, id->devno, rc,
|
||||
senseid->cu_type, senseid->cu_model, senseid->dev_type,
|
||||
senseid->dev_model, vm ? " (diag210)" : "");
|
||||
ccw_device_sense_id_done(cdev, rc);
|
||||
}
|
||||
|
||||
/**
|
||||
* ccw_device_sense_id_start - perform SENSE ID
|
||||
* @cdev: ccw device
|
||||
*
|
||||
* Execute a SENSE ID channel program on @cdev to update its sense id
|
||||
* information. When finished, call ccw_device_sense_id_done with a
|
||||
* return code specifying the result.
|
||||
*/
|
||||
void ccw_device_sense_id_start(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
struct ccw_request *req = &cdev->private->req;
|
||||
struct ccw1 *cp = cdev->private->iccws;
|
||||
|
||||
CIO_TRACE_EVENT(4, "snsid");
|
||||
CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
|
||||
/* Data setup. */
|
||||
snsid_init(cdev);
|
||||
/* Channel program setup. */
|
||||
cp->cmd_code = CCW_CMD_SENSE_ID;
|
||||
cp->cda = (u32) (addr_t) &cdev->private->senseid;
|
||||
cp->count = sizeof(struct senseid);
|
||||
cp->flags = CCW_FLAG_SLI;
|
||||
/* Request setup. */
|
||||
memset(req, 0, sizeof(*req));
|
||||
req->cp = cp;
|
||||
req->timeout = SENSE_ID_TIMEOUT;
|
||||
req->maxretries = SENSE_ID_RETRIES;
|
||||
req->lpm = sch->schib.pmcw.pam & sch->opm;
|
||||
req->check = snsid_check;
|
||||
req->callback = snsid_callback;
|
||||
ccw_request_start(cdev);
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include <linux/list.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/completion.h>
|
||||
|
||||
#include <asm/ccwdev.h>
|
||||
#include <asm/idals.h>
|
||||
@ -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.pgroup = (flags & CCWDEV_DO_PATHGROUP) != 0;
|
||||
cdev->private->options.force = (flags & CCWDEV_ALLOW_FORCE) != 0;
|
||||
cdev->private->options.mpath = (flags & CCWDEV_DO_MULTIPATH) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -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.pgroup |= (flags & CCWDEV_DO_PATHGROUP) != 0;
|
||||
cdev->private->options.force |= (flags & CCWDEV_ALLOW_FORCE) != 0;
|
||||
cdev->private->options.mpath |= (flags & CCWDEV_DO_MULTIPATH) != 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -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.pgroup &= (flags & CCWDEV_DO_PATHGROUP) == 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
|
||||
* @cdev: target ccw device
|
||||
@ -167,8 +195,7 @@ int ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
|
||||
return -EINVAL;
|
||||
if (cdev->private->state == DEV_STATE_NOT_OPER)
|
||||
return -ENODEV;
|
||||
if (cdev->private->state == DEV_STATE_VERIFY ||
|
||||
cdev->private->state == DEV_STATE_CLEAR_VERIFY) {
|
||||
if (cdev->private->state == DEV_STATE_VERIFY) {
|
||||
/* Remember to fake irb when finished. */
|
||||
if (!cdev->private->flags.fake_irb) {
|
||||
cdev->private->flags.fake_irb = 1;
|
||||
@ -478,74 +505,65 @@ __u8 ccw_device_get_path_mask(struct ccw_device *cdev)
|
||||
return sch->lpm;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to break the lock on a boxed device.
|
||||
*/
|
||||
int
|
||||
ccw_device_stlck(struct ccw_device *cdev)
|
||||
struct stlck_data {
|
||||
struct completion done;
|
||||
int rc;
|
||||
};
|
||||
|
||||
void ccw_device_stlck_done(struct ccw_device *cdev, void *data, int rc)
|
||||
{
|
||||
void *buf, *buf2;
|
||||
unsigned long flags;
|
||||
struct subchannel *sch;
|
||||
int ret;
|
||||
struct stlck_data *sdata = data;
|
||||
|
||||
if (!cdev)
|
||||
return -ENODEV;
|
||||
sdata->rc = rc;
|
||||
complete(&sdata->done);
|
||||
}
|
||||
|
||||
if (cdev->drv && !cdev->private->options.force)
|
||||
return -EINVAL;
|
||||
/*
|
||||
* Perform unconditional reserve + release.
|
||||
*/
|
||||
int ccw_device_stlck(struct ccw_device *cdev)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(cdev->dev.parent);
|
||||
struct stlck_data data;
|
||||
u8 *buffer;
|
||||
int rc;
|
||||
|
||||
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;
|
||||
/* Check if steal lock operation is valid for this device. */
|
||||
if (cdev->drv) {
|
||||
if (!cdev->private->options.force)
|
||||
return -EINVAL;
|
||||
}
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
ret = cio_enable_subchannel(sch, (u32)(addr_t)sch);
|
||||
if (ret)
|
||||
goto out_unlock;
|
||||
/*
|
||||
* Setup ccw. We chain an unconditional reserve and a release so we
|
||||
* only break the lock.
|
||||
*/
|
||||
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?
|
||||
buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
|
||||
if (!buffer)
|
||||
return -ENOMEM;
|
||||
init_completion(&data.done);
|
||||
data.rc = -EIO;
|
||||
spin_lock_irq(sch->lock);
|
||||
rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
|
||||
if (rc)
|
||||
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;
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
wait_event(cdev->private->wait_q,
|
||||
cdev->private->irb.scsw.cmd.actl == 0);
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
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));
|
||||
rc = data.rc;
|
||||
/* Check results. */
|
||||
spin_lock_irq(sch->lock);
|
||||
cio_disable_subchannel(sch);
|
||||
cdev->private->state = DEV_STATE_BOXED;
|
||||
out_unlock:
|
||||
kfree(buf);
|
||||
kfree(buf2);
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
return ret;
|
||||
spin_unlock_irq(sch->lock);
|
||||
kfree(buffer);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void *ccw_device_get_chp_desc(struct ccw_device *cdev, int chp_no)
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -336,9 +336,6 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
|
||||
sense_ccw->count = SENSE_MAX_COUNT;
|
||||
sense_ccw->flags = CCW_FLAG_SLI;
|
||||
|
||||
/* Reset internal retry indication. */
|
||||
cdev->private->flags.intretry = 0;
|
||||
|
||||
rc = cio_start(sch, sense_ccw, 0xff);
|
||||
if (rc == -ENODEV || rc == -EACCES)
|
||||
dev_fsm_event(cdev, DEV_EVENT_VERIFY);
|
||||
|
@ -1,7 +1,10 @@
|
||||
#ifndef S390_IO_SCH_H
|
||||
#define S390_IO_SCH_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/schid.h>
|
||||
#include <asm/ccwdev.h>
|
||||
#include "css.h"
|
||||
|
||||
/*
|
||||
* command-mode operation request block
|
||||
@ -67,6 +70,52 @@ struct io_subchannel_private {
|
||||
|
||||
#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
|
||||
*/
|
||||
@ -82,32 +131,43 @@ struct senseid {
|
||||
struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */
|
||||
} __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 *cdev;
|
||||
struct subchannel *sch;
|
||||
int state; /* device state */
|
||||
atomic_t onoff;
|
||||
unsigned long registered;
|
||||
struct ccw_dev_id dev_id; /* device id */
|
||||
struct subchannel_id schid; /* subchannel number */
|
||||
u8 imask; /* lpm mask for SNID/SID/SPGID */
|
||||
int iretry; /* retry counter SNID/SID/SPGID */
|
||||
struct ccw_request req; /* internal I/O request */
|
||||
int iretry;
|
||||
u8 pgid_valid_mask; /* mask of valid PGIDs */
|
||||
struct {
|
||||
unsigned int fast:1; /* post with "channel end" */
|
||||
unsigned int repall:1; /* report every interrupt status */
|
||||
unsigned int pgroup:1; /* do path grouping */
|
||||
unsigned int force:1; /* allow forced online */
|
||||
unsigned int mpath:1; /* do multipathing */
|
||||
} __attribute__ ((packed)) options;
|
||||
struct {
|
||||
unsigned int pgid_single:1; /* use single path for Set PGID */
|
||||
unsigned int esid:1; /* Ext. SenseID supported by HW */
|
||||
unsigned int dosense:1; /* delayed SENSE required */
|
||||
unsigned int doverify:1; /* delayed path verification */
|
||||
unsigned int donotify:1; /* call notify function */
|
||||
unsigned int recog_done:1; /* dev. recog. complete */
|
||||
unsigned int fake_irb:1; /* deliver faked irb */
|
||||
unsigned int intretry:1; /* retry internal operation */
|
||||
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;
|
||||
unsigned long intparm; /* user interruption parameter */
|
||||
struct qdio_irq *qdio_data;
|
||||
@ -115,7 +175,8 @@ struct ccw_device_private {
|
||||
struct senseid senseid; /* SenseID info */
|
||||
struct pgid pgid[8]; /* path group IDs per chpid*/
|
||||
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;
|
||||
struct timer_list timer;
|
||||
void *cmb; /* measurement information */
|
||||
|
@ -102,6 +102,7 @@ static atomic_t ap_poll_requests = ATOMIC_INIT(0);
|
||||
static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
|
||||
static struct task_struct *ap_poll_kthread = NULL;
|
||||
static DEFINE_MUTEX(ap_poll_thread_mutex);
|
||||
static DEFINE_SPINLOCK(ap_poll_timer_lock);
|
||||
static void *ap_interrupt_indicator;
|
||||
static struct hrtimer ap_poll_timer;
|
||||
/* In LPAR poll with 4kHz frequency. Poll every 250000 nanoseconds.
|
||||
@ -282,6 +283,7 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
|
||||
* @psmid: The program supplied message identifier
|
||||
* @msg: The message text
|
||||
* @length: The message length
|
||||
* @special: Special Bit
|
||||
*
|
||||
* Returns AP queue status structure.
|
||||
* Condition code 1 on NQAP can't happen because the L bit is 1.
|
||||
@ -289,7 +291,8 @@ static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
|
||||
* because a segment boundary was reached. The NQAP is repeated.
|
||||
*/
|
||||
static inline struct ap_queue_status
|
||||
__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
|
||||
__ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length,
|
||||
unsigned int special)
|
||||
{
|
||||
typedef struct { char _[length]; } msgblock;
|
||||
register unsigned long reg0 asm ("0") = qid | 0x40000000UL;
|
||||
@ -299,6 +302,9 @@ __ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
|
||||
register unsigned long reg4 asm ("4") = (unsigned int) (psmid >> 32);
|
||||
register unsigned long reg5 asm ("5") = (unsigned int) psmid;
|
||||
|
||||
if (special == 1)
|
||||
reg0 |= 0x400000UL;
|
||||
|
||||
asm volatile (
|
||||
"0: .long 0xb2ad0042\n" /* DQAP */
|
||||
" brc 2,0b"
|
||||
@ -312,13 +318,15 @@ int ap_send(ap_qid_t qid, unsigned long long psmid, void *msg, size_t length)
|
||||
{
|
||||
struct ap_queue_status status;
|
||||
|
||||
status = __ap_send(qid, psmid, msg, length);
|
||||
status = __ap_send(qid, psmid, msg, length, 0);
|
||||
switch (status.response_code) {
|
||||
case AP_RESPONSE_NORMAL:
|
||||
return 0;
|
||||
case AP_RESPONSE_Q_FULL:
|
||||
case AP_RESPONSE_RESET_IN_PROGRESS:
|
||||
return -EBUSY;
|
||||
case AP_RESPONSE_REQ_FAC_NOT_INST:
|
||||
return -EINVAL;
|
||||
default: /* Device is gone. */
|
||||
return -ENODEV;
|
||||
}
|
||||
@ -1008,7 +1016,7 @@ static int ap_probe_device_type(struct ap_device *ap_dev)
|
||||
}
|
||||
|
||||
status = __ap_send(ap_dev->qid, 0x0102030405060708ULL,
|
||||
msg, sizeof(msg));
|
||||
msg, sizeof(msg), 0);
|
||||
if (status.response_code != AP_RESPONSE_NORMAL) {
|
||||
rc = -ENODEV;
|
||||
goto out_free;
|
||||
@ -1163,16 +1171,19 @@ ap_config_timeout(unsigned long ptr)
|
||||
static inline void ap_schedule_poll_timer(void)
|
||||
{
|
||||
ktime_t hr_time;
|
||||
|
||||
spin_lock_bh(&ap_poll_timer_lock);
|
||||
if (ap_using_interrupts() || ap_suspend_flag)
|
||||
return;
|
||||
goto out;
|
||||
if (hrtimer_is_queued(&ap_poll_timer))
|
||||
return;
|
||||
goto out;
|
||||
if (ktime_to_ns(hrtimer_expires_remaining(&ap_poll_timer)) <= 0) {
|
||||
hr_time = ktime_set(0, poll_timeout);
|
||||
hrtimer_forward_now(&ap_poll_timer, hr_time);
|
||||
hrtimer_restart(&ap_poll_timer);
|
||||
}
|
||||
return;
|
||||
out:
|
||||
spin_unlock_bh(&ap_poll_timer_lock);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1243,7 +1254,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
|
||||
/* Start the next request on the queue. */
|
||||
ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
|
||||
status = __ap_send(ap_dev->qid, ap_msg->psmid,
|
||||
ap_msg->message, ap_msg->length);
|
||||
ap_msg->message, ap_msg->length, ap_msg->special);
|
||||
switch (status.response_code) {
|
||||
case AP_RESPONSE_NORMAL:
|
||||
atomic_inc(&ap_poll_requests);
|
||||
@ -1261,6 +1272,7 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
|
||||
*flags |= 2;
|
||||
break;
|
||||
case AP_RESPONSE_MESSAGE_TOO_BIG:
|
||||
case AP_RESPONSE_REQ_FAC_NOT_INST:
|
||||
return -EINVAL;
|
||||
default:
|
||||
return -ENODEV;
|
||||
@ -1302,7 +1314,8 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
|
||||
if (list_empty(&ap_dev->requestq) &&
|
||||
ap_dev->queue_count < ap_dev->queue_depth) {
|
||||
status = __ap_send(ap_dev->qid, ap_msg->psmid,
|
||||
ap_msg->message, ap_msg->length);
|
||||
ap_msg->message, ap_msg->length,
|
||||
ap_msg->special);
|
||||
switch (status.response_code) {
|
||||
case AP_RESPONSE_NORMAL:
|
||||
list_add_tail(&ap_msg->list, &ap_dev->pendingq);
|
||||
@ -1317,6 +1330,7 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
|
||||
ap_dev->requestq_count++;
|
||||
ap_dev->total_request_count++;
|
||||
return -EBUSY;
|
||||
case AP_RESPONSE_REQ_FAC_NOT_INST:
|
||||
case AP_RESPONSE_MESSAGE_TOO_BIG:
|
||||
ap_dev->drv->receive(ap_dev, ap_msg, ERR_PTR(-EINVAL));
|
||||
return -EINVAL;
|
||||
@ -1658,6 +1672,7 @@ int __init ap_module_init(void)
|
||||
*/
|
||||
if (MACHINE_IS_VM)
|
||||
poll_timeout = 1500000;
|
||||
spin_lock_init(&ap_poll_timer_lock);
|
||||
hrtimer_init(&ap_poll_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
|
||||
ap_poll_timer.function = ap_poll_timeout;
|
||||
|
||||
|
@ -87,6 +87,7 @@ struct ap_queue_status {
|
||||
#define AP_RESPONSE_INDEX_TOO_BIG 0x11
|
||||
#define AP_RESPONSE_NO_FIRST_PART 0x13
|
||||
#define AP_RESPONSE_MESSAGE_TOO_BIG 0x15
|
||||
#define AP_RESPONSE_REQ_FAC_NOT_INST 0x16
|
||||
|
||||
/*
|
||||
* Known device types
|
||||
@ -96,8 +97,8 @@ struct ap_queue_status {
|
||||
#define AP_DEVICE_TYPE_PCIXCC 5
|
||||
#define AP_DEVICE_TYPE_CEX2A 6
|
||||
#define AP_DEVICE_TYPE_CEX2C 7
|
||||
#define AP_DEVICE_TYPE_CEX2A2 8
|
||||
#define AP_DEVICE_TYPE_CEX2C2 9
|
||||
#define AP_DEVICE_TYPE_CEX3A 8
|
||||
#define AP_DEVICE_TYPE_CEX3C 9
|
||||
|
||||
/*
|
||||
* AP reset flag states
|
||||
@ -161,12 +162,25 @@ struct ap_message {
|
||||
size_t length; /* Message length. */
|
||||
|
||||
void *private; /* ap driver private pointer. */
|
||||
unsigned int special:1; /* Used for special commands. */
|
||||
};
|
||||
|
||||
#define AP_DEVICE(dt) \
|
||||
.dev_type=(dt), \
|
||||
.match_flags=AP_DEVICE_ID_MATCH_DEVICE_TYPE,
|
||||
|
||||
/**
|
||||
* ap_init_message() - Initialize ap_message.
|
||||
* Initialize a message before using. Otherwise this might result in
|
||||
* unexpected behaviour.
|
||||
*/
|
||||
static inline void ap_init_message(struct ap_message *ap_msg)
|
||||
{
|
||||
ap_msg->psmid = 0;
|
||||
ap_msg->length = 0;
|
||||
ap_msg->special = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note: don't use ap_send/ap_recv after using ap_queue_message
|
||||
* for the first time. Otherwise the ap message queue will get
|
||||
|
@ -299,9 +299,7 @@ static ssize_t zcrypt_write(struct file *filp, const char __user *buf,
|
||||
*/
|
||||
static int zcrypt_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
lock_kernel();
|
||||
atomic_inc(&zcrypt_open_count);
|
||||
unlock_kernel();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1009,6 +1007,10 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
|
||||
zcrypt_count_type(ZCRYPT_CEX2C));
|
||||
len += sprintf(resp_buff + len, "CEX2A count: %d\n",
|
||||
zcrypt_count_type(ZCRYPT_CEX2A));
|
||||
len += sprintf(resp_buff + len, "CEX3C count: %d\n",
|
||||
zcrypt_count_type(ZCRYPT_CEX3C));
|
||||
len += sprintf(resp_buff + len, "CEX3A count: %d\n",
|
||||
zcrypt_count_type(ZCRYPT_CEX3A));
|
||||
len += sprintf(resp_buff + len, "requestq count: %d\n",
|
||||
zcrypt_requestq_count());
|
||||
len += sprintf(resp_buff + len, "pendingq count: %d\n",
|
||||
@ -1017,7 +1019,7 @@ static int zcrypt_status_read(char *resp_buff, char **start, off_t offset,
|
||||
atomic_read(&zcrypt_open_count));
|
||||
zcrypt_status_mask(workarea);
|
||||
len += sprinthx("Online devices: 1=PCICA 2=PCICC 3=PCIXCC(MCL2) "
|
||||
"4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A",
|
||||
"4=PCIXCC(MCL3) 5=CEX2C 6=CEX2A 7=CEX3C 8=CEX3A",
|
||||
resp_buff+len, workarea, AP_DEVICES);
|
||||
zcrypt_qdepth_mask(workarea);
|
||||
len += sprinthx("Waiting work element counts",
|
||||
@ -1095,8 +1097,9 @@ static int zcrypt_status_write(struct file *file, const char __user *buffer,
|
||||
* '0' for no device, '1' for PCICA, '2' for PCICC,
|
||||
* '3' for PCIXCC_MCL2, '4' for PCIXCC_MCL3,
|
||||
* '5' for CEX2C and '6' for CEX2A'
|
||||
* '7' for CEX3C and '8' for CEX3A
|
||||
*/
|
||||
if (*ptr >= '0' && *ptr <= '6')
|
||||
if (*ptr >= '0' && *ptr <= '8')
|
||||
j++;
|
||||
else if (*ptr == 'd' || *ptr == 'D')
|
||||
zcrypt_disable_card(j++);
|
||||
|
@ -71,6 +71,8 @@ struct ica_z90_status {
|
||||
#define ZCRYPT_PCIXCC_MCL3 4
|
||||
#define ZCRYPT_CEX2C 5
|
||||
#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
|
||||
|
@ -39,17 +39,24 @@
|
||||
|
||||
#define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */
|
||||
#define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */
|
||||
#define CEX3A_MIN_MOD_SIZE CEX2A_MIN_MOD_SIZE
|
||||
#define CEX3A_MAX_MOD_SIZE CEX2A_MAX_MOD_SIZE
|
||||
|
||||
#define CEX2A_SPEED_RATING 970
|
||||
#define CEX3A_SPEED_RATING 900 /* Fixme: Needs finetuning */
|
||||
|
||||
#define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */
|
||||
#define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */
|
||||
|
||||
#define CEX3A_MAX_MESSAGE_SIZE CEX2A_MAX_MESSAGE_SIZE
|
||||
#define CEX3A_MAX_RESPONSE_SIZE CEX2A_MAX_RESPONSE_SIZE
|
||||
|
||||
#define CEX2A_CLEANUP_TIME (15*HZ)
|
||||
#define CEX3A_CLEANUP_TIME CEX2A_CLEANUP_TIME
|
||||
|
||||
static struct ap_device_id zcrypt_cex2a_ids[] = {
|
||||
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2A) },
|
||||
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2A2) },
|
||||
{ AP_DEVICE(AP_DEVICE_TYPE_CEX3A) },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
|
||||
@ -298,6 +305,7 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev,
|
||||
struct completion work;
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -335,6 +343,7 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev,
|
||||
struct completion work;
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -373,31 +382,45 @@ static struct zcrypt_ops zcrypt_cex2a_ops = {
|
||||
*/
|
||||
static int zcrypt_cex2a_probe(struct ap_device *ap_dev)
|
||||
{
|
||||
struct zcrypt_device *zdev;
|
||||
int rc;
|
||||
struct zcrypt_device *zdev = NULL;
|
||||
int rc = 0;
|
||||
|
||||
zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
|
||||
if (!zdev)
|
||||
return -ENOMEM;
|
||||
zdev->ap_dev = ap_dev;
|
||||
zdev->ops = &zcrypt_cex2a_ops;
|
||||
zdev->online = 1;
|
||||
zdev->user_space_type = ZCRYPT_CEX2A;
|
||||
zdev->type_string = "CEX2A";
|
||||
zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
|
||||
zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
|
||||
zdev->short_crt = 1;
|
||||
zdev->speed_rating = CEX2A_SPEED_RATING;
|
||||
ap_dev->reply = &zdev->reply;
|
||||
ap_dev->private = zdev;
|
||||
rc = zcrypt_device_register(zdev);
|
||||
if (rc)
|
||||
goto out_free;
|
||||
return 0;
|
||||
|
||||
out_free:
|
||||
ap_dev->private = NULL;
|
||||
zcrypt_device_free(zdev);
|
||||
switch (ap_dev->device_type) {
|
||||
case AP_DEVICE_TYPE_CEX2A:
|
||||
zdev = zcrypt_device_alloc(CEX2A_MAX_RESPONSE_SIZE);
|
||||
if (!zdev)
|
||||
return -ENOMEM;
|
||||
zdev->user_space_type = ZCRYPT_CEX2A;
|
||||
zdev->type_string = "CEX2A";
|
||||
zdev->min_mod_size = CEX2A_MIN_MOD_SIZE;
|
||||
zdev->max_mod_size = CEX2A_MAX_MOD_SIZE;
|
||||
zdev->short_crt = 1;
|
||||
zdev->speed_rating = CEX2A_SPEED_RATING;
|
||||
break;
|
||||
case AP_DEVICE_TYPE_CEX3A:
|
||||
zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE);
|
||||
if (!zdev)
|
||||
return -ENOMEM;
|
||||
zdev->user_space_type = ZCRYPT_CEX3A;
|
||||
zdev->type_string = "CEX3A";
|
||||
zdev->min_mod_size = CEX3A_MIN_MOD_SIZE;
|
||||
zdev->max_mod_size = CEX3A_MAX_MOD_SIZE;
|
||||
zdev->short_crt = 1;
|
||||
zdev->speed_rating = CEX3A_SPEED_RATING;
|
||||
break;
|
||||
}
|
||||
if (zdev != NULL) {
|
||||
zdev->ap_dev = ap_dev;
|
||||
zdev->ops = &zcrypt_cex2a_ops;
|
||||
zdev->online = 1;
|
||||
ap_dev->reply = &zdev->reply;
|
||||
ap_dev->private = zdev;
|
||||
rc = zcrypt_device_register(zdev);
|
||||
}
|
||||
if (rc) {
|
||||
ap_dev->private = NULL;
|
||||
zcrypt_device_free(zdev);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
@ -281,6 +281,7 @@ static long zcrypt_pcica_modexpo(struct zcrypt_device *zdev,
|
||||
struct completion work;
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -318,6 +319,7 @@ static long zcrypt_pcica_modexpo_crt(struct zcrypt_device *zdev,
|
||||
struct completion work;
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = kmalloc(PCICA_MAX_MESSAGE_SIZE, GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
|
@ -483,6 +483,7 @@ static long zcrypt_pcicc_modexpo(struct zcrypt_device *zdev,
|
||||
struct completion work;
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -521,6 +522,7 @@ static long zcrypt_pcicc_modexpo_crt(struct zcrypt_device *zdev,
|
||||
struct completion work;
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
|
@ -43,10 +43,13 @@
|
||||
#define PCIXCC_MIN_MOD_SIZE 16 /* 128 bits */
|
||||
#define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */
|
||||
#define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */
|
||||
#define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE
|
||||
#define CEX3C_MAX_MOD_SIZE PCIXCC_MAX_MOD_SIZE
|
||||
|
||||
#define PCIXCC_MCL2_SPEED_RATING 7870 /* FIXME: needs finetuning */
|
||||
#define PCIXCC_MCL2_SPEED_RATING 7870
|
||||
#define PCIXCC_MCL3_SPEED_RATING 7870
|
||||
#define CEX2C_SPEED_RATING 8540
|
||||
#define CEX2C_SPEED_RATING 7000
|
||||
#define CEX3C_SPEED_RATING 6500 /* FIXME: needs finetuning */
|
||||
|
||||
#define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */
|
||||
#define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */
|
||||
@ -72,7 +75,7 @@ struct response_type {
|
||||
static struct ap_device_id zcrypt_pcixcc_ids[] = {
|
||||
{ AP_DEVICE(AP_DEVICE_TYPE_PCIXCC) },
|
||||
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2C) },
|
||||
{ AP_DEVICE(AP_DEVICE_TYPE_CEX2C2) },
|
||||
{ AP_DEVICE(AP_DEVICE_TYPE_CEX3C) },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
|
||||
@ -326,6 +329,11 @@ static int XCRB_msg_to_type6CPRB_msgX(struct zcrypt_device *zdev,
|
||||
function_code = ((unsigned char *)&msg->cprbx) + msg->cprbx.cprb_len;
|
||||
memcpy(msg->hdr.function_code, function_code, sizeof(msg->hdr.function_code));
|
||||
|
||||
if (memcmp(function_code, "US", 2) == 0)
|
||||
ap_msg->special = 1;
|
||||
else
|
||||
ap_msg->special = 0;
|
||||
|
||||
/* copy data block */
|
||||
if (xcRB->request_data_length &&
|
||||
copy_from_user(req_data, xcRB->request_data_address,
|
||||
@ -688,6 +696,7 @@ static long zcrypt_pcixcc_modexpo(struct zcrypt_device *zdev,
|
||||
};
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -727,6 +736,7 @@ static long zcrypt_pcixcc_modexpo_crt(struct zcrypt_device *zdev,
|
||||
};
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -766,6 +776,7 @@ static long zcrypt_pcixcc_send_cprb(struct zcrypt_device *zdev,
|
||||
};
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -805,6 +816,7 @@ static long zcrypt_pcixcc_rng(struct zcrypt_device *zdev,
|
||||
};
|
||||
int rc;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = kmalloc(PCIXCC_MAX_XCRB_MESSAGE_SIZE, GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -972,6 +984,7 @@ static int zcrypt_pcixcc_rng_supported(struct ap_device *ap_dev)
|
||||
} __attribute__((packed)) *reply;
|
||||
int rc, i;
|
||||
|
||||
ap_init_message(&ap_msg);
|
||||
ap_msg.message = (void *) get_zeroed_page(GFP_KERNEL);
|
||||
if (!ap_msg.message)
|
||||
return -ENOMEM;
|
||||
@ -1016,14 +1029,15 @@ out_free:
|
||||
static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
|
||||
{
|
||||
struct zcrypt_device *zdev;
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
zdev = zcrypt_device_alloc(PCIXCC_MAX_RESPONSE_SIZE);
|
||||
if (!zdev)
|
||||
return -ENOMEM;
|
||||
zdev->ap_dev = ap_dev;
|
||||
zdev->online = 1;
|
||||
if (ap_dev->device_type == AP_DEVICE_TYPE_PCIXCC) {
|
||||
switch (ap_dev->device_type) {
|
||||
case AP_DEVICE_TYPE_PCIXCC:
|
||||
rc = zcrypt_pcixcc_mcl(ap_dev);
|
||||
if (rc < 0) {
|
||||
zcrypt_device_free(zdev);
|
||||
@ -1041,13 +1055,25 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev)
|
||||
zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
|
||||
zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
case AP_DEVICE_TYPE_CEX2C:
|
||||
zdev->user_space_type = ZCRYPT_CEX2C;
|
||||
zdev->type_string = "CEX2C";
|
||||
zdev->speed_rating = CEX2C_SPEED_RATING;
|
||||
zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE;
|
||||
zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE;
|
||||
break;
|
||||
case AP_DEVICE_TYPE_CEX3C:
|
||||
zdev->user_space_type = ZCRYPT_CEX3C;
|
||||
zdev->type_string = "CEX3C";
|
||||
zdev->speed_rating = CEX3C_SPEED_RATING;
|
||||
zdev->min_mod_size = CEX3C_MIN_MOD_SIZE;
|
||||
zdev->max_mod_size = CEX3C_MAX_MOD_SIZE;
|
||||
break;
|
||||
default:
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
rc = zcrypt_pcixcc_rng_supported(ap_dev);
|
||||
if (rc < 0) {
|
||||
zcrypt_device_free(zdev);
|
||||
|
Loading…
Reference in New Issue
Block a user