summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/DocBook/s390-drivers.tmpl1
-rw-r--r--Documentation/cpu-hotplug.txt2
-rw-r--r--Documentation/kernel-parameters.txt9
-rw-r--r--Documentation/s390/CommonIO5
-rw-r--r--arch/s390/Kconfig3
-rw-r--r--arch/s390/crypto/Kconfig60
-rw-r--r--arch/s390/crypto/aes_s390.c2
-rw-r--r--arch/s390/crypto/prng.c4
-rw-r--r--arch/s390/kernel/Makefile4
-rw-r--r--arch/s390/kernel/early.c2
-rw-r--r--arch/s390/kernel/head64.S2
-rw-r--r--arch/s390/kernel/ipl.c948
-rw-r--r--arch/s390/kernel/process.c18
-rw-r--r--arch/s390/kernel/ptrace.c15
-rw-r--r--arch/s390/kernel/setup.c139
-rw-r--r--arch/s390/kernel/signal.c20
-rw-r--r--arch/s390/kernel/smp.c575
-rw-r--r--arch/s390/kernel/traps.c20
-rw-r--r--arch/s390/kernel/vmlinux.lds.S12
-rw-r--r--arch/s390/lib/spinlock.c35
-rw-r--r--arch/s390/mm/extmem.c2
-rw-r--r--arch/s390/mm/vmem.c26
-rw-r--r--drivers/crypto/Kconfig63
-rw-r--r--drivers/s390/block/Makefile4
-rw-r--r--drivers/s390/block/dasd.c1690
-rw-r--r--drivers/s390/block/dasd_3370_erp.c84
-rw-r--r--drivers/s390/block/dasd_3990_erp.c360
-rw-r--r--drivers/s390/block/dasd_9336_erp.c41
-rw-r--r--drivers/s390/block/dasd_9343_erp.c21
-rw-r--r--drivers/s390/block/dasd_alias.c903
-rw-r--r--drivers/s390/block/dasd_devmap.c94
-rw-r--r--drivers/s390/block/dasd_diag.c107
-rw-r--r--drivers/s390/block/dasd_eckd.c789
-rw-r--r--drivers/s390/block/dasd_eckd.h125
-rw-r--r--drivers/s390/block/dasd_eer.c11
-rw-r--r--drivers/s390/block/dasd_erp.c25
-rw-r--r--drivers/s390/block/dasd_fba.c119
-rw-r--r--drivers/s390/block/dasd_genhd.c76
-rw-r--r--drivers/s390/block/dasd_int.h209
-rw-r--r--drivers/s390/block/dasd_ioctl.c172
-rw-r--r--drivers/s390/block/dasd_proc.c23
-rw-r--r--drivers/s390/block/dcssblk.c2
-rw-r--r--drivers/s390/char/Makefile2
-rw-r--r--drivers/s390/char/monwriter.c2
-rw-r--r--drivers/s390/char/raw3270.c4
-rw-r--r--drivers/s390/char/sclp.h4
-rw-r--r--drivers/s390/char/sclp_chp.c200
-rw-r--r--drivers/s390/char/sclp_cmd.c398
-rw-r--r--drivers/s390/char/sclp_cpi.c246
-rw-r--r--drivers/s390/char/sclp_cpi_sys.c400
-rw-r--r--drivers/s390/char/sclp_cpi_sys.h15
-rw-r--r--drivers/s390/char/sclp_info.c116
-rw-r--r--drivers/s390/char/sclp_rw.c2
-rw-r--r--drivers/s390/char/tape_3590.c2
-rw-r--r--drivers/s390/char/tape_core.c2
-rw-r--r--drivers/s390/char/tape_proc.c2
-rw-r--r--drivers/s390/char/vmlogrdr.c2
-rw-r--r--drivers/s390/char/vmur.c2
-rw-r--r--drivers/s390/char/zcore.c2
-rw-r--r--drivers/s390/cio/airq.c171
-rw-r--r--drivers/s390/cio/airq.h10
-rw-r--r--drivers/s390/cio/blacklist.c2
-rw-r--r--drivers/s390/cio/ccwgroup.c25
-rw-r--r--drivers/s390/cio/chsc.c121
-rw-r--r--drivers/s390/cio/cio.c106
-rw-r--r--drivers/s390/cio/cio.h89
-rw-r--r--drivers/s390/cio/cio_debug.h22
-rw-r--r--drivers/s390/cio/css.c198
-rw-r--r--drivers/s390/cio/css.h78
-rw-r--r--drivers/s390/cio/device.c157
-rw-r--r--drivers/s390/cio/device.h5
-rw-r--r--drivers/s390/cio/device_fsm.c124
-rw-r--r--drivers/s390/cio/device_id.c9
-rw-r--r--drivers/s390/cio/device_ops.c2
-rw-r--r--drivers/s390/cio/device_pgid.c6
-rw-r--r--drivers/s390/cio/device_status.c13
-rw-r--r--drivers/s390/cio/io_sch.h163
-rw-r--r--drivers/s390/cio/ioasm.h66
-rw-r--r--drivers/s390/cio/qdio.c38
-rw-r--r--drivers/s390/cio/qdio.h2
-rw-r--r--drivers/s390/net/claw.c2
-rw-r--r--drivers/s390/net/lcs.c2
-rw-r--r--drivers/s390/net/netiucv.c3
-rw-r--r--drivers/s390/net/qeth_proc.c4
-rw-r--r--drivers/s390/net/smsgiucv.c2
-rw-r--r--drivers/s390/scsi/zfcp_erp.c2
-rw-r--r--drivers/s390/scsi/zfcp_fsf.c10
-rw-r--r--drivers/s390/scsi/zfcp_qdio.c2
-rw-r--r--include/asm-s390/airq.h19
-rw-r--r--include/asm-s390/cio.h4
-rw-r--r--include/asm-s390/dasd.h2
-rw-r--r--include/asm-s390/ipl.h8
-rw-r--r--include/asm-s390/mmu_context.h27
-rw-r--r--include/asm-s390/pgtable.h46
-rw-r--r--include/asm-s390/processor.h4
-rw-r--r--include/asm-s390/ptrace.h8
-rw-r--r--include/asm-s390/qdio.h2
-rw-r--r--include/asm-s390/rwsem.h4
-rw-r--r--include/asm-s390/sclp.h20
-rw-r--r--include/asm-s390/smp.h5
-rw-r--r--include/asm-s390/spinlock.h32
-rw-r--r--include/asm-s390/spinlock_types.h1
-rw-r--r--include/asm-s390/tlbflush.h32
-rw-r--r--include/asm-s390/zcrypt.h2
-rw-r--r--kernel/sysctl_check.c1
105 files changed, 6216 insertions, 3658 deletions
diff --git a/Documentation/DocBook/s390-drivers.tmpl b/Documentation/DocBook/s390-drivers.tmpl
index 254e769282a..3d2f31b99dd 100644
--- a/Documentation/DocBook/s390-drivers.tmpl
+++ b/Documentation/DocBook/s390-drivers.tmpl
@@ -116,6 +116,7 @@
!Iinclude/asm-s390/ccwdev.h
!Edrivers/s390/cio/device.c
!Edrivers/s390/cio/device_ops.c
+!Edrivers/s390/cio/airq.c
</sect1>
<sect1 id="cmf">
<title>The channel-measurement facility</title>
diff --git a/Documentation/cpu-hotplug.txt b/Documentation/cpu-hotplug.txt
index fb94f5a71b6..ba0aacde94f 100644
--- a/Documentation/cpu-hotplug.txt
+++ b/Documentation/cpu-hotplug.txt
@@ -50,7 +50,7 @@ additional_cpus=n (*) Use this to limit hotpluggable cpus. This option sets
cpu_possible_map = cpu_present_map + additional_cpus
(*) Option valid only for following architectures
-- x86_64, ia64, s390
+- x86_64, ia64
ia64 and x86_64 use the number of disabled local apics in ACPI tables MADT
to determine the number of potentially hot-pluggable cpus. The implementation
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 65de5ba7b74..880f882160e 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -370,7 +370,8 @@ and is between 256 and 4096 characters. It is defined in the file
configured. Potentially dangerous and should only be
used if you are entirely sure of the consequences.
- chandev= [HW,NET] Generic channel device initialisation
+ ccw_timeout_log [S390]
+ See Documentation/s390/CommonIO for details.
checkreqprot [SELINUX] Set initial checkreqprot flag value.
Format: { "0" | "1" }
@@ -382,6 +383,12 @@ and is between 256 and 4096 characters. It is defined in the file
Value can be changed at runtime via
/selinux/checkreqprot.
+ cio_ignore= [S390]
+ See Documentation/s390/CommonIO for details.
+
+ cio_msg= [S390]
+ See Documentation/s390/CommonIO for details.
+
clock= [BUGS=X86-32, HW] gettimeofday clocksource override.
[Deprecated]
Forces specified clocksource (if available) to be used
diff --git a/Documentation/s390/CommonIO b/Documentation/s390/CommonIO
index 86320aa3fb0..8fbc0a85287 100644
--- a/Documentation/s390/CommonIO
+++ b/Documentation/s390/CommonIO
@@ -4,6 +4,11 @@ S/390 common I/O-Layer - command line parameters, procfs and debugfs entries
Command line parameters
-----------------------
+* ccw_timeout_log
+
+ Enable logging of debug information in case of ccw device timeouts.
+
+
* cio_msg = yes | no
Determines whether information on found devices and sensed device
diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig
index 1330061020a..6ef54d27fc0 100644
--- a/arch/s390/Kconfig
+++ b/arch/s390/Kconfig
@@ -276,9 +276,6 @@ source "kernel/Kconfig.preempt"
source "mm/Kconfig"
-config HOLES_IN_ZONE
- def_bool y
-
comment "I/O subsystem configuration"
config MACHCHK_WARNING
diff --git a/arch/s390/crypto/Kconfig b/arch/s390/crypto/Kconfig
deleted file mode 100644
index d1defbbfcd8..00000000000
--- a/arch/s390/crypto/Kconfig
+++ /dev/null
@@ -1,60 +0,0 @@
-config CRYPTO_SHA1_S390
- tristate "SHA1 digest algorithm"
- depends on S390
- select CRYPTO_ALGAPI
- help
- This is the s390 hardware accelerated implementation of the
- SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
-
-config CRYPTO_SHA256_S390
- tristate "SHA256 digest algorithm"
- depends on S390
- select CRYPTO_ALGAPI
- help
- This is the s390 hardware accelerated implementation of the
- SHA256 secure hash standard (DFIPS 180-2).
-
- This version of SHA implements a 256 bit hash with 128 bits of
- security against collision attacks.
-
-config CRYPTO_DES_S390
- tristate "DES and Triple DES cipher algorithms"
- depends on S390
- select CRYPTO_ALGAPI
- select CRYPTO_BLKCIPHER
- help
- This us the s390 hardware accelerated implementation of the
- DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3).
-
-config CRYPTO_AES_S390
- tristate "AES cipher algorithms"
- depends on S390
- select CRYPTO_ALGAPI
- select CRYPTO_BLKCIPHER
- help
- This is the s390 hardware accelerated implementation of the
- AES cipher algorithms (FIPS-197). AES uses the Rijndael
- algorithm.
-
- Rijndael appears to be consistently a very good performer in
- both hardware and software across a wide range of computing
- environments regardless of its use in feedback or non-feedback
- modes. Its key setup time is excellent, and its key agility is
- good. Rijndael's very low memory requirements make it very well
- suited for restricted-space environments, in which it also
- demonstrates excellent performance. Rijndael's operations are
- among the easiest to defend against power and timing attacks.
-
- On s390 the System z9-109 currently only supports the key size
- of 128 bit.
-
-config S390_PRNG
- tristate "Pseudo random number generator device driver"
- depends on S390
- default "m"
- help
- Select this option if you want to use the s390 pseudo random number
- generator. The PRNG is part of the cryptographic processor functions
- and uses triple-DES to generate secure random numbers like the
- ANSI X9.17 standard. The PRNG is usable via the char device
- /dev/prandom.
diff --git a/arch/s390/crypto/aes_s390.c b/arch/s390/crypto/aes_s390.c
index 46c97058ebe..a3f67f8b542 100644
--- a/arch/s390/crypto/aes_s390.c
+++ b/arch/s390/crypto/aes_s390.c
@@ -516,7 +516,7 @@ static int __init aes_init(void)
/* z9 109 and z9 BC/EC only support 128 bit key length */
if (keylen_flag == AES_KEYLEN_128)
printk(KERN_INFO
- "aes_s390: hardware acceleration only available for"
+ "aes_s390: hardware acceleration only available for "
"128 bit keys\n");
ret = crypto_register_alg(&aes_alg);
diff --git a/arch/s390/crypto/prng.c b/arch/s390/crypto/prng.c
index 8eb3a1aedc2..0cfefddd837 100644
--- a/arch/s390/crypto/prng.c
+++ b/arch/s390/crypto/prng.c
@@ -90,7 +90,7 @@ static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes,
int ret = 0;
int tmp;
- /* nbytes can be arbitrary long, we spilt it into chunks */
+ /* nbytes can be arbitrary length, we split it into chunks */
while (nbytes) {
/* same as in extract_entropy_user in random.c */
if (need_resched()) {
@@ -146,7 +146,7 @@ static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes,
return ret;
}
-static struct file_operations prng_fops = {
+static const struct file_operations prng_fops = {
.owner = THIS_MODULE,
.open = &prng_open,
.release = NULL,
diff --git a/arch/s390/kernel/Makefile b/arch/s390/kernel/Makefile
index 56cb71007cd..b3b650a93c7 100644
--- a/arch/s390/kernel/Makefile
+++ b/arch/s390/kernel/Makefile
@@ -31,7 +31,3 @@ S390_KEXEC_OBJS := machine_kexec.o crash.o
S390_KEXEC_OBJS += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o)
obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS)
-#
-# This is just to get the dependencies...
-#
-binfmt_elf32.o: $(TOPDIR)/fs/binfmt_elf.c
diff --git a/arch/s390/kernel/early.c b/arch/s390/kernel/early.c
index 1b3af7dab81..9f7b73b180f 100644
--- a/arch/s390/kernel/early.c
+++ b/arch/s390/kernel/early.c
@@ -276,7 +276,7 @@ void __init startup_init(void)
create_kernel_nss();
sort_main_extable();
setup_lowcore_early();
- sclp_readinfo_early();
+ sclp_read_info_early();
sclp_facilities_detect();
memsize = sclp_memory_detect();
#ifndef CONFIG_64BIT
diff --git a/arch/s390/kernel/head64.S b/arch/s390/kernel/head64.S
index a87b1976d40..79dccd206a6 100644
--- a/arch/s390/kernel/head64.S
+++ b/arch/s390/kernel/head64.S
@@ -157,7 +157,7 @@ startup_continue:
.long 0xb2b10000 # store facility list
tm 0xc8,0x08 # check bit for clearing-by-ASCE
bno 0f-.LPG1(%r13)
- lhi %r1,2094
+ lhi %r1,2048
lhi %r2,0
.long 0xb98e2001
oi 7(%r12),0x80 # set IDTE flag
diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c
index b97694fa62e..db28cca81fe 100644
--- a/arch/s390/kernel/ipl.c
+++ b/arch/s390/kernel/ipl.c
@@ -2,7 +2,7 @@
* arch/s390/kernel/ipl.c
* ipl/reipl/dump support for Linux on s390.
*
- * Copyright (C) IBM Corp. 2005,2006
+ * Copyright IBM Corp. 2005,2007
* Author(s): Michael Holzheu <holzheu@de.ibm.com>
* Heiko Carstens <heiko.carstens@de.ibm.com>
* Volker Sameske <sameske@de.ibm.com>
@@ -31,6 +31,43 @@
#define IPL_FCP_DUMP_STR "fcp_dump"
#define IPL_NSS_STR "nss"
+#define DUMP_CCW_STR "ccw"
+#define DUMP_FCP_STR "fcp"
+#define DUMP_NONE_STR "none"
+
+/*
+ * Four shutdown trigger types are supported:
+ * - panic
+ * - halt
+ * - power off
+ * - reipl
+ */
+#define ON_PANIC_STR "on_panic"
+#define ON_HALT_STR "on_halt"
+#define ON_POFF_STR "on_poff"
+#define ON_REIPL_STR "on_reboot"
+
+struct shutdown_action;
+struct shutdown_trigger {
+ char *name;
+ struct shutdown_action *action;
+};
+
+/*
+ * Five shutdown action types are supported:
+ */
+#define SHUTDOWN_ACTION_IPL_STR "ipl"
+#define SHUTDOWN_ACTION_REIPL_STR "reipl"
+#define SHUTDOWN_ACTION_DUMP_STR "dump"
+#define SHUTDOWN_ACTION_VMCMD_STR "vmcmd"
+#define SHUTDOWN_ACTION_STOP_STR "stop"
+
+struct shutdown_action {
+ char *name;
+ void (*fn) (struct shutdown_trigger *trigger);
+ int (*init) (void);
+};
+
static char *ipl_type_str(enum ipl_type type)
{
switch (type) {
@@ -54,10 +91,6 @@ enum dump_type {
DUMP_TYPE_FCP = 4,
};
-#define DUMP_NONE_STR "none"
-#define DUMP_CCW_STR "ccw"
-#define DUMP_FCP_STR "fcp"
-
static char *dump_type_str(enum dump_type type)
{
switch (type) {
@@ -99,30 +132,6 @@ enum dump_method {
DUMP_METHOD_FCP_DIAG,
};
-enum shutdown_action {
- SHUTDOWN_REIPL,
- SHUTDOWN_DUMP,
- SHUTDOWN_STOP,
-};
-
-#define SHUTDOWN_REIPL_STR "reipl"
-#define SHUTDOWN_DUMP_STR "dump"
-#define SHUTDOWN_STOP_STR "stop"
-
-static char *shutdown_action_str(enum shutdown_action action)
-{
- switch (action) {
- case SHUTDOWN_REIPL:
- return SHUTDOWN_REIPL_STR;
- case SHUTDOWN_DUMP:
- return SHUTDOWN_DUMP_STR;
- case SHUTDOWN_STOP:
- return SHUTDOWN_STOP_STR;
- default:
- return NULL;
- }
-}
-
static int diag308_set_works = 0;
static int reipl_capabilities = IPL_TYPE_UNKNOWN;
@@ -140,8 +149,6 @@ static enum dump_method dump_method = DUMP_METHOD_NONE;
static struct ipl_parameter_block *dump_block_fcp;
static struct ipl_parameter_block *dump_block_ccw;
-static enum shutdown_action on_panic_action = SHUTDOWN_STOP;
-
static struct sclp_ipl_info sclp_ipl_info;
int diag308(unsigned long subcode, void *addr)
@@ -205,8 +212,8 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \
struct kobj_attribute *attr, \
const char *buf, size_t len) \
{ \
- if (sscanf(buf, _fmt_in, _value) != 1) \
- return -EINVAL; \
+ strncpy(_value, buf, sizeof(_value) - 1); \
+ strstrip(_value); \
return len; \
} \
static struct kobj_attribute sys_##_prefix##_##_name##_attr = \
@@ -245,33 +252,6 @@ static __init enum ipl_type get_ipl_type(void)
return IPL_TYPE_FCP;
}
-void __init setup_ipl_info(void)
-{
- ipl_info.type = get_ipl_type();
- switch (ipl_info.type) {
- case IPL_TYPE_CCW:
- ipl_info.data.ccw.dev_id.devno = ipl_devno;
- ipl_info.data.ccw.dev_id.ssid = 0;
- break;
- case IPL_TYPE_FCP:
- case IPL_TYPE_FCP_DUMP:
- ipl_info.data.fcp.dev_id.devno =
- IPL_PARMBLOCK_START->ipl_info.fcp.devno;
- ipl_info.data.fcp.dev_id.ssid = 0;
- ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
- ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
- break;
- case IPL_TYPE_NSS:
- strncpy(ipl_info.data.nss.name, kernel_nss_name,
- sizeof(ipl_info.data.nss.name));
- break;
- case IPL_TYPE_UNKNOWN:
- default:
- /* We have no info to copy */
- break;
- }
-}
-
struct ipl_info ipl_info;
EXPORT_SYMBOL_GPL(ipl_info);
@@ -428,8 +408,74 @@ static struct attribute_group ipl_unknown_attr_group = {
static struct kset *ipl_kset;
+static int __init ipl_register_fcp_files(void)
+{
+ int rc;
+
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
+ if (rc)
+ goto out;
+ rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
+ if (rc)
+ goto out_ipl_parm;
+ rc = sysfs_create_bin_file(&ipl_kset->kobj, &ipl_scp_data_attr);
+ if (!rc)
+ goto out;
+
+ sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
+
+out_ipl_parm:
+ sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
+out:
+ return rc;
+}
+
+static void ipl_run(struct shutdown_trigger *trigger)
+{
+ diag308(DIAG308_IPL, NULL);
+ if (MACHINE_IS_VM)
+ __cpcmd("IPL", NULL, 0, NULL);
+ else if (ipl_info.type == IPL_TYPE_CCW)
+ reipl_ccw_dev(&ipl_info.data.ccw.dev_id);
+}
+
+static int ipl_init(void)
+{
+ int rc;
+
+ ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj);
+ if (!ipl_kset) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ switch (ipl_info.type) {
+ case IPL_TYPE_CCW:
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_ccw_attr_group);
+ break;
+ case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
+ rc = ipl_register_fcp_files();
+ break;
+ case IPL_TYPE_NSS:
+ rc = sysfs_create_group(&ipl_kset->kobj, &ipl_nss_attr_group);
+ break;
+ default:
+ rc = sysfs_create_group(&ipl_kset->kobj,
+ &ipl_unknown_attr_group);
+ break;
+ }
+out:
+ if (rc)
+ panic("ipl_init failed: rc = %i\n", rc);
+
+ return 0;
+}
+
+static struct shutdown_action ipl_action = {SHUTDOWN_ACTION_IPL_STR, ipl_run,
+ ipl_init};
+
/*
- * reipl section
+ * reipl shutdown action: Reboot Linux on shutdown.
*/
/* FCP reipl device attributes */
@@ -549,7 +595,9 @@ static int reipl_set_type(enum ipl_type type)
switch(type) {
case IPL_TYPE_CCW:
- if (MACHINE_IS_VM)
+ if (diag308_set_works)
+ reipl_method = REIPL_METHOD_CCW_DIAG;
+ else if (MACHINE_IS_VM)
reipl_method = REIPL_METHOD_CCW_VM;
else
reipl_method = REIPL_METHOD_CCW_CIO;
@@ -600,143 +648,11 @@ static ssize_t reipl_type_store(struct kobject *kobj,
}
static struct kobj_attribute reipl_type_attr =
- __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store);
+ __ATTR(reipl_type, 0644, reipl_type_show, reipl_type_store);
static struct kset *reipl_kset;
-/*
- * dump section
- */
-
-/* FCP dump device attributes */
-
-DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n",
- dump_block_fcp->ipl_info.fcp.wwpn);
-DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n",
- dump_block_fcp->ipl_info.fcp.lun);
-DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
- dump_block_fcp->ipl_info.fcp.bootprog);
-DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
- dump_block_fcp->ipl_info.fcp.br_lba);
-DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
- dump_block_fcp->ipl_info.fcp.devno);
-
-static struct attribute *dump_fcp_attrs[] = {
- &sys_dump_fcp_device_attr.attr,
- &sys_dump_fcp_wwpn_attr.attr,
- &sys_dump_fcp_lun_attr.attr,
- &sys_dump_fcp_bootprog_attr.attr,
- &sys_dump_fcp_br_lba_attr.attr,
- NULL,
-};
-
-static struct attribute_group dump_fcp_attr_group = {
- .name = IPL_FCP_STR,
- .attrs = dump_fcp_attrs,
-};
-
-/* CCW dump device attributes */
-
-DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
- dump_block_ccw->ipl_info.ccw.devno);
-
-static struct attribute *dump_ccw_attrs[] = {
- &sys_dump_ccw_device_attr.attr,
- NULL,
-};
-
-static struct attribute_group dump_ccw_attr_group = {
- .name = IPL_CCW_STR,
- .attrs = dump_ccw_attrs,
-};
-
-/* dump type */
-
-static int dump_set_type(enum dump_type type)
-{
- if (!(dump_capabilities & type))
- return -EINVAL;
- switch(type) {
- case DUMP_TYPE_CCW:
- if (MACHINE_IS_VM)
- dump_method = DUMP_METHOD_CCW_VM;
- else if (diag308_set_works)
- dump_method = DUMP_METHOD_CCW_DIAG;
- else
- dump_method = DUMP_METHOD_CCW_CIO;
- break;
- case DUMP_TYPE_FCP:
- dump_method = DUMP_METHOD_FCP_DIAG;
- break;
- default:
- dump_method = DUMP_METHOD_NONE;
- }
- dump_type = type;
- return 0;
-}
-
-static ssize_t dump_type_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *page)
-{
- return sprintf(page, "%s\n", dump_type_str(dump_type));
-}
-
-static ssize_t dump_type_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t len)
-{
- int rc = -EINVAL;
-
- if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
- rc = dump_set_type(DUMP_TYPE_NONE);
- else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
- rc = dump_set_type(DUMP_TYPE_CCW);
- else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
- rc = dump_set_type(DUMP_TYPE_FCP);
- return (rc != 0) ? rc : len;
-}
-
-static struct kobj_attribute dump_type_attr =
- __ATTR(dump_type, 0644, dump_type_show, dump_type_store);
-
-static struct kset *dump_kset;
-
-/*
- * Shutdown actions section
- */
-
-static struct kset *shutdown_actions_kset;
-
-/* on panic */
-
-static ssize_t on_panic_show(struct kobject *kobj,
- struct kobj_attribute *attr, char *page)
-{
- return sprintf(page, "%s\n", shutdown_action_str(on_panic_action));
-}
-
-static ssize_t on_panic_store(struct kobject *kobj,
- struct kobj_attribute *attr,
- const char *buf, size_t len)
-{
- if (strncmp(buf, SHUTDOWN_REIPL_STR, strlen(SHUTDOWN_REIPL_STR)) == 0)
- on_panic_action = SHUTDOWN_REIPL;
- else if (strncmp(buf, SHUTDOWN_DUMP_STR,
- strlen(SHUTDOWN_DUMP_STR)) == 0)
- on_panic_action = SHUTDOWN_DUMP;
- else if (strncmp(buf, SHUTDOWN_STOP_STR,
- strlen(SHUTDOWN_STOP_STR)) == 0)
- on_panic_action = SHUTDOWN_STOP;
- else
- return -EINVAL;
-
- return len;
-}
-
-static struct kobj_attribute on_panic_attr =
- __ATTR(on_panic, 0644, on_panic_show, on_panic_store);
-
-void do_reipl(void)
+void reipl_run(struct shutdown_trigger *trigger)
{
struct ccw_dev_id devid;
static char buf[100];
@@ -745,8 +661,6 @@ void do_reipl(void)
switch (reipl_method) {
case REIPL_METHOD_CCW_CIO:
devid.devno = reipl_block_ccw->ipl_info.ccw.devno;
- if (ipl_info.type == IPL_TYPE_CCW && devid.devno == ipl_devno)
- diag308(DIAG308_IPL, NULL);
devid.ssid = 0;
reipl_ccw_dev(&devid);
break;
@@ -787,98 +701,6 @@ void do_reipl(void)
default:
break;
}
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
-}
-
-static void do_dump(void)
-{
- struct ccw_dev_id devid;
- static char buf[100];
-
- switch (dump_method) {
- case DUMP_METHOD_CCW_CIO:
- smp_send_stop();
- devid.devno = dump_block_ccw->ipl_info.ccw.devno;
- devid.ssid = 0;
- reipl_ccw_dev(&devid);
- break;
- case DUMP_METHOD_CCW_VM:
- smp_send_stop();
- sprintf(buf, "STORE STATUS");
- __cpcmd(buf, NULL, 0, NULL);
- sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
- __cpcmd(buf, NULL, 0, NULL);
- break;
- case DUMP_METHOD_CCW_DIAG:
- diag308(DIAG308_SET, dump_block_ccw);
- diag308(DIAG308_DUMP, NULL);
- break;
- case DUMP_METHOD_FCP_DIAG:
- diag308(DIAG308_SET, dump_block_fcp);
- diag308(DIAG308_DUMP, NULL);
- break;
- case DUMP_METHOD_NONE:
- default:
- return;
- }
- printk(KERN_EMERG "Dump failed!\n");
-}
-
-/* init functions */
-
-static int __init ipl_register_fcp_files(void)
-{
- int rc;
-
- rc = sysfs_create_group(&ipl_kset->kobj,
- &ipl_fcp_attr_group);
- if (rc)
- goto out;
- rc = sysfs_create_bin_file(&ipl_kset->kobj,
- &ipl_parameter_attr);
- if (rc)
- goto out_ipl_parm;
- rc = sysfs_create_bin_file(&ipl_kset->kobj,
- &ipl_scp_data_attr);
- if (!rc)
- goto out;
-
- sysfs_remove_bin_file(&ipl_kset->kobj, &ipl_parameter_attr);
-
-out_ipl_parm:
- sysfs_remove_group(&ipl_kset->kobj, &ipl_fcp_attr_group);
-out:
- return rc;
-}
-
-static int __init ipl_init(void)
-{
- int rc;
-
- ipl_kset = kset_create_and_add("ipl", NULL, firmware_kobj);
- if (!ipl_kset)
- return -ENOMEM;
- switch (ipl_info.type) {
- case IPL_TYPE_CCW:
- rc = sysfs_create_group(&ipl_kset->kobj,
- &ipl_ccw_attr_group);
- break;
- case IPL_TYPE_FCP:
- case IPL_TYPE_FCP_DUMP:
- rc = ipl_register_fcp_files();
- break;
- case IPL_TYPE_NSS:
- rc = sysfs_create_group(&ipl_kset->kobj,
- &ipl_nss_attr_group);
- break;
- default:
- rc = sysfs_create_group(&ipl_kset->kobj,
- &ipl_unknown_attr_group);
- break;
- }
- if (rc)
- kset_unregister(ipl_kset);
- return rc;
}
static void __init reipl_probe(void)
@@ -923,6 +745,7 @@ static int __init reipl_ccw_init(void)
reipl_block_ccw->hdr.version = IPL_PARM_BLOCK_VERSION;
reipl_block_ccw->hdr.blk0_len = IPL_PARM_BLK0_CCW_LEN;
reipl_block_ccw->hdr.pbt = DIAG308_IPL_TYPE_CCW;
+ reipl_block_ccw->hdr.flags = DIAG308_FLAGS_LP_VALID;
/* check if read scp info worked and set loadparm */
if (sclp_ipl_info.is_valid)
memcpy(reipl_block_ccw->ipl_info.ccw.load_param,
@@ -931,8 +754,7 @@ static int __init reipl_ccw_init(void)
/* read scp info failed: set empty loadparm (EBCDIC blanks) */
memset(reipl_block_ccw->ipl_info.ccw.load_param, 0x40,
LOADPARM_LEN);
- /* FIXME: check for diag308_set_works when enabling diag ccw reipl */
- if (!MACHINE_IS_VM)
+ if (!MACHINE_IS_VM && !diag308_set_works)
sys_reipl_ccw_loadparm_attr.attr.mode = S_IRUGO;
if (ipl_info.type == IPL_TYPE_CCW)
reipl_block_ccw->ipl_info.ccw.devno = ipl_devno;
@@ -970,7 +792,7 @@ static int __init reipl_fcp_init(void)
return 0;
}
-static int __init reipl_init(void)
+static int reipl_init(void)
{
int rc;
@@ -997,6 +819,140 @@ static int __init reipl_init(void)
return 0;
}
+static struct shutdown_action reipl_action = {SHUTDOWN_ACTION_REIPL_STR,
+ reipl_run, reipl_init};
+
+/*
+ * dump shutdown action: Dump Linux on shutdown.
+ */
+
+/* FCP dump device attributes */
+
+DEFINE_IPL_ATTR_RW(dump_fcp, wwpn, "0x%016llx\n", "%016llx\n",
+ dump_block_fcp->ipl_info.fcp.wwpn);
+DEFINE_IPL_ATTR_RW(dump_fcp, lun, "0x%016llx\n", "%016llx\n",
+ dump_block_fcp->ipl_info.fcp.lun);
+DEFINE_IPL_ATTR_RW(dump_fcp, bootprog, "%lld\n", "%lld\n",
+ dump_block_fcp->ipl_info.fcp.bootprog);
+DEFINE_IPL_ATTR_RW(dump_fcp, br_lba, "%lld\n", "%lld\n",
+ dump_block_fcp->ipl_info.fcp.br_lba);
+DEFINE_IPL_ATTR_RW(dump_fcp, device, "0.0.%04llx\n", "0.0.%llx\n",
+ dump_block_fcp->ipl_info.fcp.devno);
+
+static struct attribute *dump_fcp_attrs[] = {
+ &sys_dump_fcp_device_attr.attr,
+ &sys_dump_fcp_wwpn_attr.attr,
+ &sys_dump_fcp_lun_attr.attr,
+ &sys_dump_fcp_bootprog_attr.attr,
+ &sys_dump_fcp_br_lba_attr.attr,
+ NULL,
+};
+
+static struct attribute_group dump_fcp_attr_group = {
+ .name = IPL_FCP_STR,
+ .attrs = dump_fcp_attrs,
+};
+
+/* CCW dump device attributes */
+
+DEFINE_IPL_ATTR_RW(dump_ccw, device, "0.0.%04llx\n", "0.0.%llx\n",
+ dump_block_ccw->ipl_info.ccw.devno);
+
+static struct attribute *dump_ccw_attrs[] = {
+ &sys_dump_ccw_device_attr.attr,
+ NULL,
+};
+
+static struct attribute_group dump_ccw_attr_group = {
+ .name = IPL_CCW_STR,
+ .attrs = dump_ccw_attrs,
+};
+
+/* dump type */
+
+static int dump_set_type(enum dump_type type)
+{
+ if (!(dump_capabilities & type))
+ return -EINVAL;
+ switch (type) {
+ case DUMP_TYPE_CCW:
+ if (diag308_set_works)
+ dump_method = DUMP_METHOD_CCW_DIAG;
+ else if (MACHINE_IS_VM)
+ dump_method = DUMP_METHOD_CCW_VM;
+ else
+ dump_method = DUMP_METHOD_CCW_CIO;
+ break;
+ case DUMP_TYPE_FCP:
+ dump_method = DUMP_METHOD_FCP_DIAG;
+ break;
+ default:
+ dump_method = DUMP_METHOD_NONE;
+ }
+ dump_type = type;
+ return 0;
+}
+
+static ssize_t dump_type_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", dump_type_str(dump_type));
+}
+
+static ssize_t dump_type_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ int rc = -EINVAL;
+
+ if (strncmp(buf, DUMP_NONE_STR, strlen(DUMP_NONE_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_NONE);
+ else if (strncmp(buf, DUMP_CCW_STR, strlen(DUMP_CCW_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_CCW);
+ else if (strncmp(buf, DUMP_FCP_STR, strlen(DUMP_FCP_STR)) == 0)
+ rc = dump_set_type(DUMP_TYPE_FCP);
+ return (rc != 0) ? rc : len;
+}
+
+static struct kobj_attribute dump_type_attr =
+ __ATTR(dump_type, 0644, dump_type_show, dump_type_store);
+
+static struct kset *dump_kset;
+
+static void dump_run(struct shutdown_trigger *trigger)
+{
+ struct ccw_dev_id devid;
+ static char buf[100];
+
+ switch (dump_method) {
+ case DUMP_METHOD_CCW_CIO:
+ smp_send_stop();
+ devid.devno = dump_block_ccw->ipl_info.ccw.devno;
+ devid.ssid = 0;
+ reipl_ccw_dev(&devid);
+ break;
+ case DUMP_METHOD_CCW_VM:
+ smp_send_stop();
+ sprintf(buf, "STORE STATUS");
+ __cpcmd(buf, NULL, 0, NULL);
+ sprintf(buf, "IPL %X", dump_block_ccw->ipl_info.ccw.devno);
+ __cpcmd(buf, NULL, 0, NULL);
+ break;
+ case DUMP_METHOD_CCW_DIAG:
+ diag308(DIAG308_SET, dump_block_ccw);
+ diag308(DIAG308_DUMP, NULL);
+ break;
+ case DUMP_METHOD_FCP_DIAG:
+ diag308(DIAG308_SET, dump_block_fcp);
+ diag308(DIAG308_DUMP, NULL);
+ break;
+ case DUMP_METHOD_NONE:
+ default:
+ return;
+ }
+ printk(KERN_EMERG "Dump failed!\n");
+}
+
static int __init dump_ccw_init(void)
{
int rc;
@@ -1042,31 +998,14 @@ static int __init dump_fcp_init(void)
return 0;
}
-#define SHUTDOWN_ON_PANIC_PRIO 0
-
-static int shutdown_on_panic_notify(struct notifier_block *self,
- unsigned long event, void *data)
-{
- if (on_panic_action == SHUTDOWN_DUMP)
- do_dump();
- else if (on_panic_action == SHUTDOWN_REIPL)
- do_reipl();
- return NOTIFY_OK;
-}
-
-static struct notifier_block shutdown_on_panic_nb = {
- .notifier_call = shutdown_on_panic_notify,
- .priority = SHUTDOWN_ON_PANIC_PRIO
-};
-
-static int __init dump_init(void)
+static int dump_init(void)
{
int rc;
dump_kset = kset_create_and_add("dump", NULL, firmware_kobj);
if (!dump_kset)
return -ENOMEM;
- rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr);
+ rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr.attr);
if (rc) {
kset_unregister(dump_kset);
return rc;
@@ -1081,47 +1020,381 @@ static int __init dump_init(void)
return 0;
}
-static int __init shutdown_actions_init(void)
+static struct shutdown_action dump_action = {SHUTDOWN_ACTION_DUMP_STR,
+ dump_run, dump_init};
+
+/*
+ * vmcmd shutdown action: Trigger vm command on shutdown.
+ */
+
+static char vmcmd_on_reboot[128];
+static char vmcmd_on_panic[128];
+static char vmcmd_on_halt[128];
+static char vmcmd_on_poff[128];
+
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_reboot, "%s\n", "%s\n", vmcmd_on_reboot);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_panic, "%s\n", "%s\n", vmcmd_on_panic);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_halt, "%s\n", "%s\n", vmcmd_on_halt);
+DEFINE_IPL_ATTR_STR_RW(vmcmd, on_poff, "%s\n", "%s\n", vmcmd_on_poff);
+
+static struct attribute *vmcmd_attrs[] = {
+ &sys_vmcmd_on_reboot_attr.attr,
+ &sys_vmcmd_on_panic_attr.attr,
+ &sys_vmcmd_on_halt_attr.attr,
+ &sys_vmcmd_on_poff_attr.attr,
+ NULL,
+};
+
+static struct attribute_group vmcmd_attr_group = {
+ .attrs = vmcmd_attrs,
+};
+
+static struct kset *vmcmd_kset;
+
+static void vmcmd_run(struct shutdown_trigger *trigger)
+{
+ char *cmd, *next_cmd;
+
+ if (strcmp(trigger->name, ON_REIPL_STR) == 0)
+ cmd = vmcmd_on_reboot;
+ else if (strcmp(trigger->name, ON_PANIC_STR) == 0)
+ cmd = vmcmd_on_panic;
+ else if (strcmp(trigger->name, ON_HALT_STR) == 0)
+ cmd = vmcmd_on_halt;
+ else if (strcmp(trigger->name, ON_POFF_STR) == 0)
+ cmd = vmcmd_on_poff;
+ else
+ return;
+
+ if (strlen(cmd) == 0)
+ return;
+ do {
+ next_cmd = strchr(cmd, '\n');
+ if (next_cmd) {
+ next_cmd[0] = 0;
+ next_cmd += 1;
+ }
+ __cpcmd(cmd, NULL, 0, NULL);
+ cmd = next_cmd;
+ } while (cmd != NULL);
+}
+
+static int vmcmd_init(void)
{
- int rc;
+ if (!MACHINE_IS_VM)
+ return -ENOTSUPP;
+ vmcmd_kset = kset_create_and_add("vmcmd", NULL, firmware_kobj);
+ if (!vmcmd_kset)
+ return -ENOMEM;
+ return sysfs_create_group(&vmcmd_kset->kobj, &vmcmd_attr_group);
+}
+
+static struct shutdown_action vmcmd_action = {SHUTDOWN_ACTION_VMCMD_STR,
+ vmcmd_run, vmcmd_init};
+
+/*
+ * stop shutdown action: Stop Linux on shutdown.
+ */
+
+static void stop_run(struct shutdown_trigger *trigger)
+{
+ if (strcmp(trigger->name, ON_PANIC_STR) == 0)
+ disabled_wait((unsigned long) __builtin_return_address(0));
+ else {
+ signal_processor(smp_processor_id(), sigp_stop);
+ for (;;);
+ }
+}
+
+static struct shutdown_action stop_action = {SHUTDOWN_ACTION_STOP_STR,
+ stop_run, NULL};
+
+/* action list */
+
+static struct shutdown_action *shutdown_actions_list[] = {
+ &ipl_action, &reipl_action, &dump_action, &vmcmd_action, &stop_action};
+#define SHUTDOWN_ACTIONS_COUNT (sizeof(shutdown_actions_list) / sizeof(void *))
+
+/*
+ * Trigger section
+ */
+
+static struct kset *shutdown_actions_kset;
+
+static int set_trigger(const char *buf, struct shutdown_trigger *trigger,
+ size_t len)
+{
+ int i;
+ for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
+ if (!shutdown_actions_list[i])
+ continue;
+ if (strncmp(buf, shutdown_actions_list[i]->name,
+ strlen(shutdown_actions_list[i]->name)) == 0) {
+ trigger->action = shutdown_actions_list[i];
+ return len;
+ }
+ }
+ return -EINVAL;
+}
+
+/* on reipl */
+
+static struct shutdown_trigger on_reboot_trigger = {ON_REIPL_STR,
+ &reipl_action};
+
+static ssize_t on_reboot_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_reboot_trigger.action->name);
+}
+
+static ssize_t on_reboot_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_reboot_trigger, len);
+}
+
+static struct kobj_attribute on_reboot_attr =
+ __ATTR(on_reboot, 0644, on_reboot_show, on_reboot_store);
+
+static void do_machine_restart(char *__unused)
+{
+ smp_send_stop();
+ on_reboot_trigger.action->fn(&on_reboot_trigger);
+ reipl_run(NULL);
+}
+void (*_machine_restart)(char *command) = do_machine_restart;
+
+/* on panic */
+
+static struct shutdown_trigger on_panic_trigger = {ON_PANIC_STR, &stop_action};
+
+static ssize_t on_panic_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_panic_trigger.action->name);
+}
+static ssize_t on_panic_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_panic_trigger, len);
+}
+
+static struct kobj_attribute on_panic_attr =
+ __ATTR(on_panic, 0644, on_panic_show, on_panic_store);
+
+static void do_panic(void)
+{
+ on_panic_trigger.action->fn(&on_panic_trigger);
+ stop_run(&on_panic_trigger);
+}
+
+/* on halt */
+
+static struct shutdown_trigger on_halt_trigger = {ON_HALT_STR, &stop_action};
+
+static ssize_t on_halt_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_halt_trigger.action->name);
+}
+
+static ssize_t on_halt_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_halt_trigger, len);
+}
+
+static struct kobj_attribute on_halt_attr =
+ __ATTR(on_halt, 0644, on_halt_show, on_halt_store);
+
+
+static void do_machine_halt(void)
+{
+ smp_send_stop();
+ on_halt_trigger.action->fn(&on_halt_trigger);
+ stop_run(&on_halt_trigger);
+}
+void (*_machine_halt)(void) = do_machine_halt;
+
+/* on power off */
+
+static struct shutdown_trigger on_poff_trigger = {ON_POFF_STR, &stop_action};
+
+static ssize_t on_poff_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return sprintf(page, "%s\n", on_poff_trigger.action->name);
+}
+
+static ssize_t on_poff_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ return set_trigger(buf, &on_poff_trigger, len);
+}
+
+static struct kobj_attribute on_poff_attr =
+ __ATTR(on_poff, 0644, on_poff_show, on_poff_store);
+
+
+static void do_machine_power_off(void)
+{
+ smp_send_stop();
+ on_poff_trigger.action->fn(&on_poff_trigger);
+ stop_run(&on_poff_trigger);
+}
+void (*_machine_power_off)(void) = do_machine_power_off;
+
+static void __init shutdown_triggers_init(void)
+{
shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL,
firmware_kobj);
if (!shutdown_actions_kset)
- return -ENOMEM;
- rc = sysfs_create_file(&shutdown_actions_kset->kobj, &on_panic_attr);
- if (rc) {
- kset_unregister(shutdown_actions_kset);
- return rc;
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_reboot_attr.attr))
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_panic_attr.attr))
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_halt_attr.attr))
+ goto fail;
+ if (sysfs_create_file(&shutdown_actions_kset->kobj,
+ &on_poff_attr.attr))
+ goto fail;
+
+ return;
+fail:
+ panic("shutdown_triggers_init failed\n");
+}
+
+static void __init shutdown_actions_init(void)
+{
+ int i;
+
+ for (i = 0; i < SHUTDOWN_ACTIONS_COUNT; i++) {
+ if (!shutdown_actions_list[i]->init)
+ continue;
+ if (shutdown_actions_list[i]->init())
+ shutdown_actions_list[i] = NULL;
}
- atomic_notifier_chain_register(&panic_notifier_list,
- &shutdown_on_panic_nb);
- return 0;
}
static int __init s390_ipl_init(void)
{
- int rc;
-
- sclp_get_ipl_info(&sclp_ipl_info);
reipl_probe();
- rc = ipl_init();
- if (rc)
- return rc;
- rc = reipl_init();
- if (rc)
- return rc;
- rc = dump_init();
- if (rc)
- return rc;
- rc = shutdown_actions_init();
- if (rc)
- return rc;
+ sclp_get_ipl_info(&sclp_ipl_info);
+ shutdown_actions_init();
+ shutdown_triggers_init();
return 0;
}
__initcall(s390_ipl_init);
+static void __init strncpy_skip_quote(char *dst, char *src, int n)
+{
+ int sx, dx;
+
+ dx = 0;
+ for (sx = 0; src[sx] != 0; sx++) {
+ if (src[sx] == '"')
+ continue;
+ dst[dx++] = src[sx];
+ if (dx >= n)
+ break;
+ }
+}
+
+static int __init vmcmd_on_reboot_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_reboot, str, 127);
+ vmcmd_on_reboot[127] = 0;
+ on_reboot_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmreboot=", vmcmd_on_reboot_setup);
+
+static int __init vmcmd_on_panic_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_panic, str, 127);
+ vmcmd_on_panic[127] = 0;
+ on_panic_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmpanic=", vmcmd_on_panic_setup);
+
+static int __init vmcmd_on_halt_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_halt, str, 127);
+ vmcmd_on_halt[127] = 0;
+ on_halt_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmhalt=", vmcmd_on_halt_setup);
+
+static int __init vmcmd_on_poff_setup(char *str)
+{
+ if (!MACHINE_IS_VM)
+ return 1;
+ strncpy_skip_quote(vmcmd_on_poff, str, 127);
+ vmcmd_on_poff[127] = 0;
+ on_poff_trigger.action = &vmcmd_action;
+ return 1;
+}
+__setup("vmpoff=", vmcmd_on_poff_setup);
+
+static int on_panic_notify(struct notifier_block *self,
+ unsigned long event, void *data)
+{
+ do_panic();
+ return NOTIFY_OK;
+}
+
+static struct notifier_block on_panic_nb = {
+ .notifier_call = on_panic_notify,
+ .priority = 0,
+};
+
+void __init setup_ipl(void)
+{
+ ipl_info.type = get_ipl_type();
+ switch (ipl_info.type) {
+ case IPL_TYPE_CCW:
+ ipl_info.data.ccw.dev_id.devno = ipl_devno;
+ ipl_info.data.ccw.dev_id.ssid = 0;
+ break;
+ case IPL_TYPE_FCP:
+ case IPL_TYPE_FCP_DUMP:
+ ipl_info.data.fcp.dev_id.devno =
+ IPL_PARMBLOCK_START->ipl_info.fcp.devno;
+ ipl_info.data.fcp.dev_id.ssid = 0;
+ ipl_info.data.fcp.wwpn = IPL_PARMBLOCK_START->ipl_info.fcp.wwpn;
+ ipl_info.data.fcp.lun = IPL_PARMBLOCK_START->ipl_info.fcp.lun;
+ break;
+ case IPL_TYPE_NSS:
+ strncpy(ipl_info.data.nss.name, kernel_nss_name,
+ sizeof(ipl_info.data.nss.name));
+ break;
+ case IPL_TYPE_UNKNOWN:
+ default:
+ /* We have no info to copy */
+ break;
+ }
+ atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
+}
+
void __init ipl_save_parameters(void)
{
struct cio_iplinfo iplinfo;
@@ -1202,3 +1475,4 @@ void s390_reset_system(void)
do_reset_calls();
}
+
diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c
index 29f7884b4ff..0e7aca03930 100644
--- a/arch/s390/kernel/process.c
+++ b/arch/s390/kernel/process.c
@@ -36,7 +36,7 @@
#include <linux/init.h>
#include <linux/module.h>
#include <linux/notifier.h>
-
+#include <linux/utsname.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/system.h>
@@ -182,13 +182,15 @@ void cpu_idle(void)
void show_regs(struct pt_regs *regs)
{
- struct task_struct *tsk = current;
-
- printk("CPU: %d %s\n", task_thread_info(tsk)->cpu, print_tainted());
- printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
- current->comm, task_pid_nr(current), (void *) tsk,
- (void *) tsk->thread.ksp);
-
+ print_modules();
+ printk("CPU: %d %s %s %.*s\n",
+ task_thread_info(current)->cpu, print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
+ current->comm, current->pid, current,
+ (void *) current->thread.ksp);
show_registers(regs);
/* Show stack backtrace if pt_regs is from kernel mode */
if (!(regs->psw.mask & PSW_MASK_PSTATE))
diff --git a/arch/s390/kernel/ptrace.c b/arch/s390/kernel/ptrace.c
index 1d81bf9488a..6e036bae987 100644
--- a/arch/s390/kernel/ptrace.c
+++ b/arch/s390/kernel/ptrace.c
@@ -86,13 +86,13 @@ FixPerRegisters(struct task_struct *task)
per_info->control_regs.bits.storage_alt_space_ctl = 0;
}
-static void set_single_step(struct task_struct *task)
+void user_enable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 1;
FixPerRegisters(task);
}
-static void clear_single_step(struct task_struct *task)
+void user_disable_single_step(struct task_struct *task)
{
task->thread.per_info.single_step = 0;
FixPerRegisters(task);
@@ -107,7 +107,7 @@ void
ptrace_disable(struct task_struct *child)
{
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ user_disable_single_step(child);
}
#ifndef CONFIG_64BIT
@@ -651,7 +651,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ user_disable_single_step(child);
wake_up_process(child);
return 0;
@@ -665,7 +665,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
return 0;
child->exit_code = SIGKILL;
/* make sure the single step bit is not set. */
- clear_single_step(child);
+ user_disable_single_step(child);
wake_up_process(child);
return 0;
@@ -675,10 +675,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
return -EIO;
clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
child->exit_code = data;
- if (data)
- set_tsk_thread_flag(child, TIF_SINGLE_STEP);
- else
- set_single_step(child);
+ user_enable_single_step(child);
/* give it a chance to run. */
wake_up_process(child);
return 0;
diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c
index 577aa7dd660..766c783bd7a 100644
--- a/arch/s390/kernel/setup.c
+++ b/arch/s390/kernel/setup.c
@@ -126,75 +126,6 @@ void __cpuinit cpu_init(void)
}
/*
- * VM halt and poweroff setup routines
- */
-char vmhalt_cmd[128] = "";
-char vmpoff_cmd[128] = "";
-static char vmpanic_cmd[128] = "";
-
-static void strncpy_skip_quote(char *dst, char *src, int n)
-{
- int sx, dx;
-
- dx = 0;
- for (sx = 0; src[sx] != 0; sx++) {
- if (src[sx] == '"') continue;
- dst[dx++] = src[sx];
- if (dx >= n) break;
- }
-}
-
-static int __init vmhalt_setup(char *str)
-{
- strncpy_skip_quote(vmhalt_cmd, str, 127);
- vmhalt_cmd[127] = 0;
- return 1;
-}
-
-__setup("vmhalt=", vmhalt_setup);
-
-static int __init vmpoff_setup(char *str)
-{
- strncpy_skip_quote(vmpoff_cmd, str, 127);
- vmpoff_cmd[127] = 0;
- return 1;
-}
-
-__setup("vmpoff=", vmpoff_setup);
-
-static int vmpanic_notify(struct notifier_block *self, unsigned long event,
- void *data)
-{
- if (MACHINE_IS_VM && strlen(vmpanic_cmd) > 0)
- cpcmd(vmpanic_cmd, NULL, 0, NULL);
-
- return NOTIFY_OK;
-}
-
-#define PANIC_PRI_VMPANIC 0
-
-static struct notifier_block vmpanic_nb = {
- .notifier_call = vmpanic_notify,
- .priority = PANIC_PRI_VMPANIC
-};
-
-static int __init vmpanic_setup(char *str)
-{
- static int register_done __initdata = 0;
-
- strncpy_skip_quote(vmpanic_cmd, str, 127);
- vmpanic_cmd[127] = 0;
- if (!register_done) {
- register_done = 1;
- atomic_notifier_chain_register(&panic_notifier_list,
- &vmpanic_nb);
- }
- return 1;
-}
-
-__setup("vmpanic=", vmpanic_setup);
-
-/*
* condev= and conmode= setup parameter.
*/
@@ -308,38 +239,6 @@ static void __init setup_zfcpdump(unsigned int console_devno)
static inline void setup_zfcpdump(unsigned int console_devno) {}
#endif /* CONFIG_ZFCPDUMP */
-#ifdef CONFIG_SMP
-void (*_machine_restart)(char *command) = machine_restart_smp;
-void (*_machine_halt)(void) = machine_halt_smp;
-void (*_machine_power_off)(void) = machine_power_off_smp;
-#else
-/*
- * Reboot, halt and power_off routines for non SMP.
- */
-static void do_machine_restart_nonsmp(char * __unused)
-{
- do_reipl();
-}
-
-static void do_machine_halt_nonsmp(void)
-{
- if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
- __cpcmd(vmhalt_cmd, NULL, 0, NULL);
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
-}
-
-static void do_machine_power_off_nonsmp(void)
-{
- if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
- __cpcmd(vmpoff_cmd, NULL, 0, NULL);
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
-}
-
-void (*_machine_restart)(char *command) = do_machine_restart_nonsmp;
-void (*_machine_halt)(void) = do_machine_halt_nonsmp;
-void (*_machine_power_off)(void) = do_machine_power_off_nonsmp;
-#endif
-
/*
* Reboot, halt and power_off stubs. They just call _machine_restart,
* _machine_halt or _machine_power_off.
@@ -559,7 +458,9 @@ setup_resources(void)
data_resource.start = (unsigned long) &_etext;
data_resource.end = (unsigned long) &_edata - 1;
- for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ if (!memory_chunk[i].size)
+ continue;
res = alloc_bootmem_low(sizeof(struct resource));
res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
switch (memory_chunk[i].type) {
@@ -617,7 +518,7 @@ EXPORT_SYMBOL_GPL(real_memory_size);
static void __init setup_memory_end(void)
{
unsigned long memory_size;
- unsigned long max_mem, max_phys;
+ unsigned long max_mem;
int i;
#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
@@ -625,10 +526,31 @@ static void __init setup_memory_end(void)
memory_end = ZFCPDUMP_HSA_SIZE;
#endif
memory_size = 0;
- max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE;
memory_end &= PAGE_MASK;
- max_mem = memory_end ? min(max_phys, memory_end) : max_phys;
+ max_mem = memory_end ? min(VMALLOC_START, memory_end) : VMALLOC_START;
+ memory_end = min(max_mem, memory_end);
+
+ /*
+ * Make sure all chunks are MAX_ORDER aligned so we don't need the
+ * extra checks that HOLES_IN_ZONE would require.
+ */
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
+ unsigned long start, end;
+ struct mem_chunk *chunk;
+ unsigned long align;
+
+ chunk = &memory_chunk[i];
+ align = 1UL << (MAX_ORDER + PAGE_SHIFT - 1);
+ start = (chunk->addr + align - 1) & ~(align - 1);
+ end = (chunk->addr + chunk->size) & ~(align - 1);
+ if (start >= end)
+ memset(chunk, 0, sizeof(*chunk));
+ else {
+ chunk->addr = start;
+ chunk->size = end - start;
+ }
+ }
for (i = 0; i < MEMORY_CHUNKS; i++) {
struct mem_chunk *chunk = &memory_chunk[i];
@@ -890,7 +812,7 @@ setup_arch(char **cmdline_p)
parse_early_param();
- setup_ipl_info();
+ setup_ipl();
setup_memory_end();
setup_addressing_mode();
setup_memory();
@@ -899,7 +821,6 @@ setup_arch(char **cmdline_p)
cpu_init();
__cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
- smp_setup_cpu_possible_map();
/*
* Setup capabilities (ELF_HWCAP & ELF_PLATFORM).
@@ -920,7 +841,7 @@ setup_arch(char **cmdline_p)
void __cpuinit print_cpu_info(struct cpuinfo_S390 *cpuinfo)
{
- printk("cpu %d "
+ printk(KERN_INFO "cpu %d "
#ifdef CONFIG_SMP
"phys_idx=%d "
#endif
@@ -996,7 +917,7 @@ static void *c_next(struct seq_file *m, void *v, loff_t *pos)
static void c_stop(struct seq_file *m, void *v)
{
}
-struct seq_operations cpuinfo_op = {
+const struct seq_operations cpuinfo_op = {
.start = c_start,
.next = c_next,
.stop = c_stop,
diff --git a/arch/s390/kernel/signal.c b/arch/s390/kernel/signal.c
index d264671c1b7..4449bf32cbf 100644
--- a/arch/s390/kernel/signal.c
+++ b/arch/s390/kernel/signal.c
@@ -471,6 +471,7 @@ void do_signal(struct pt_regs *regs)
if (signr > 0) {
/* Whee! Actually deliver the signal. */
+ int ret;
#ifdef CONFIG_COMPAT
if (test_thread_flag(TIF_31BIT)) {
extern int handle_signal32(unsigned long sig,
@@ -478,15 +479,12 @@ void do_signal(struct pt_regs *regs)
siginfo_t *info,
sigset_t *oldset,
struct pt_regs *regs);
- if (handle_signal32(
- signr, &ka, &info, oldset, regs) == 0) {
- if (test_thread_flag(TIF_RESTORE_SIGMASK))
- clear_thread_flag(TIF_RESTORE_SIGMASK);
- }
- return;
+ ret = handle_signal32(signr, &ka, &info, oldset, regs);
}
+ else
#endif
- if (handle_signal(signr, &ka, &info, oldset, regs) == 0) {
+ ret = handle_signal(signr, &ka, &info, oldset, regs);
+ if (!ret) {
/*
* A signal was successfully delivered; the saved
* sigmask will have been stored in the signal frame,
@@ -495,6 +493,14 @@ void do_signal(struct pt_regs *regs)
*/
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
+
+ /*
+ * If we would have taken a single-step trap
+ * for a normal instruction, act like we took
+ * one for the handler setup.
+ */
+ if (current->thread.per_info.single_step)
+ set_thread_flag(TIF_SINGLE_STEP);
}
return;
}
diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c
index 264ea906db4..aa37fa15451 100644
--- a/arch/s390/kernel/smp.c
+++ b/arch/s390/kernel/smp.c
@@ -42,6 +42,7 @@
#include <asm/tlbflush.h>
#include <asm/timer.h>
#include <asm/lowcore.h>
+#include <asm/sclp.h>
#include <asm/cpu.h>
/*
@@ -53,11 +54,27 @@ EXPORT_SYMBOL(lowcore_ptr);
cpumask_t cpu_online_map = CPU_MASK_NONE;
EXPORT_SYMBOL(cpu_online_map);
-cpumask_t cpu_possible_map = CPU_MASK_NONE;
+cpumask_t cpu_possible_map = CPU_MASK_ALL;
EXPORT_SYMBOL(cpu_possible_map);
static struct task_struct *current_set[NR_CPUS];
+static u8 smp_cpu_type;
+static int smp_use_sigp_detection;
+
+enum s390_cpu_state {
+ CPU_STATE_STANDBY,
+ CPU_STATE_CONFIGURED,
+};
+
+#ifdef CONFIG_HOTPLUG_CPU
+static DEFINE_MUTEX(smp_cpu_state_mutex);
+#endif
+static int smp_cpu_state[NR_CPUS];
+
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
+DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
+
static void smp_ext_bitcall(int, ec_bit_sig);
/*
@@ -193,6 +210,33 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
}
EXPORT_SYMBOL(smp_call_function_single);
+/**
+ * smp_call_function_mask(): Run a function on a set of other CPUs.
+ * @mask: The set of cpus to run on. Must not include the current cpu.
+ * @func: The function to run. This must be fast and non-blocking.
+ * @info: An arbitrary pointer to pass to the function.
+ * @wait: If true, wait (atomically) until function has completed on other CPUs.
+ *
+ * Returns 0 on success, else a negative status code.
+ *
+ * If @wait is true, then returns once @func has returned; otherwise
+ * it returns just before the target cpu calls @func.
+ *
+ * You must not call this function with disabled interrupts or from a
+ * hardware interrupt handler or from a bottom half handler.
+ */
+int
+smp_call_function_mask(cpumask_t mask,
+ void (*func)(void *), void *info,
+ int wait)
+{
+ preempt_disable();
+ __smp_call_function_map(func, info, 0, wait, mask);
+ preempt_enable();
+ return 0;
+}
+EXPORT_SYMBOL(smp_call_function_mask);
+
void smp_send_stop(void)
{
int cpu, rc;
@@ -217,33 +261,6 @@ void smp_send_stop(void)
}
/*
- * Reboot, halt and power_off routines for SMP.
- */
-void machine_restart_smp(char *__unused)
-{
- smp_send_stop();
- do_reipl();
-}
-
-void machine_halt_smp(void)
-{
- smp_send_stop();
- if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
- __cpcmd(vmhalt_cmd, NULL, 0, NULL);
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
- for (;;);
-}
-
-void machine_power_off_smp(void)
-{
- smp_send_stop();
- if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
- __cpcmd(vmpoff_cmd, NULL, 0, NULL);
- signal_processor(smp_processor_id(), sigp_stop_and_store_status);
- for (;;);
-}
-
-/*
* This is the main routine where commands issued by other
* cpus are handled.
*/
@@ -355,6 +372,13 @@ void smp_ctl_clear_bit(int cr, int bit)
}
EXPORT_SYMBOL(smp_ctl_clear_bit);
+/*
+ * In early ipl state a temp. logically cpu number is needed, so the sigp
+ * functions can be used to sense other cpus. Since NR_CPUS is >= 2 on
+ * CONFIG_SMP and the ipl cpu is logical cpu 0, it must be 1.
+ */
+#define CPU_INIT_NO 1
+
#if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
/*
@@ -375,9 +399,10 @@ static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu)
"kernel was compiled with NR_CPUS=%i\n", cpu, NR_CPUS);
return;
}
- zfcpdump_save_areas[cpu] = alloc_bootmem(sizeof(union save_area));
- __cpu_logical_map[1] = (__u16) phy_cpu;
- while (signal_processor(1, sigp_stop_and_store_status) == sigp_busy)
+ zfcpdump_save_areas[cpu] = kmalloc(sizeof(union save_area), GFP_KERNEL);
+ __cpu_logical_map[CPU_INIT_NO] = (__u16) phy_cpu;
+ while (signal_processor(CPU_INIT_NO, sigp_stop_and_store_status) ==
+ sigp_busy)
cpu_relax();
memcpy(zfcpdump_save_areas[cpu],
(void *)(unsigned long) store_prefix() + SAVE_AREA_BASE,
@@ -397,32 +422,155 @@ static inline void smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) { }
#endif /* CONFIG_ZFCPDUMP || CONFIG_ZFCPDUMP_MODULE */
-/*
- * Lets check how many CPUs we have.
- */
-static unsigned int __init smp_count_cpus(void)
+static int cpu_stopped(int cpu)
{
- unsigned int cpu, num_cpus;
- __u16 boot_cpu_addr;
+ __u32 status;
- /*
- * cpu 0 is the boot cpu. See smp_prepare_boot_cpu.
- */
+ /* Check for stopped state */
+ if (signal_processor_ps(&status, 0, cpu, sigp_sense) ==
+ sigp_status_stored) {
+ if (status & 0x40)
+ return 1;
+ }
+ return 0;
+}
+
+static int cpu_known(int cpu_id)
+{
+ int cpu;
+
+ for_each_present_cpu(cpu) {
+ if (__cpu_logical_map[cpu] == cpu_id)
+ return 1;
+ }
+ return 0;
+}
+
+static int smp_rescan_cpus_sigp(cpumask_t avail)
+{
+ int cpu_id, logical_cpu;
+
+ logical_cpu = first_cpu(avail);
+ if (logical_cpu == NR_CPUS)
+ return 0;
+ for (cpu_id = 0; cpu_id <= 65535; cpu_id++) {
+ if (cpu_known(cpu_id))
+ continue;
+ __cpu_logical_map[logical_cpu] = cpu_id;
+ if (!cpu_stopped(logical_cpu))
+ continue;
+ cpu_set(logical_cpu, cpu_present_map);
+ smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED;
+ logical_cpu = next_cpu(logical_cpu, avail);
+ if (logical_cpu == NR_CPUS)
+ break;
+ }
+ return 0;
+}
+
+static int smp_rescan_cpus_sclp(cpumask_t avail)
+{
+ struct sclp_cpu_info *info;
+ int cpu_id, logical_cpu, cpu;
+ int rc;
+
+ logical_cpu = first_cpu(avail);
+ if (logical_cpu == NR_CPUS)
+ return 0;
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+ rc = sclp_get_cpu_info(info);
+ if (rc)
+ goto out;
+ for (cpu = 0; cpu < info->combined; cpu++) {
+ if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type)
+ continue;
+ cpu_id = info->cpu[cpu].address;
+ if (cpu_known(cpu_id))
+ continue;
+ __cpu_logical_map[logical_cpu] = cpu_id;
+ cpu_set(logical_cpu, cpu_present_map);
+ if (cpu >= info->configured)
+ smp_cpu_state[logical_cpu] = CPU_STATE_STANDBY;
+ else
+ smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED;
+ logical_cpu = next_cpu(logical_cpu, avail);
+ if (logical_cpu == NR_CPUS)
+ break;
+ }
+out:
+ kfree(info);
+ return rc;
+}
+
+static int smp_rescan_cpus(void)
+{
+ cpumask_t avail;
+
+ cpus_xor(avail, cpu_possible_map, cpu_present_map);
+ if (smp_use_sigp_detection)
+ return smp_rescan_cpus_sigp(avail);
+ else
+ return smp_rescan_cpus_sclp(avail);
+}
+
+static void __init smp_detect_cpus(void)
+{
+ unsigned int cpu, c_cpus, s_cpus;
+ struct sclp_cpu_info *info;
+ u16 boot_cpu_addr, cpu_addr;
+
+ c_cpus = 1;
+ s_cpus = 0;
boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
- current_thread_info()->cpu = 0;
- num_cpus = 1;
- for (cpu = 0; cpu <= 65535; cpu++) {
- if ((__u16) cpu == boot_cpu_addr)
+ info = kmalloc(sizeof(*info), GFP_KERNEL);
+ if (!info)
+ panic("smp_detect_cpus failed to allocate memory\n");
+ /* Use sigp detection algorithm if sclp doesn't work. */
+ if (sclp_get_cpu_info(info)) {
+ smp_use_sigp_detection = 1;
+ for (cpu = 0; cpu <= 65535; cpu++) {
+ if (cpu == boot_cpu_addr)
+ continue;
+ __cpu_logical_map[CPU_INIT_NO] = cpu;
+ if (!cpu_stopped(CPU_INIT_NO))
+ continue;
+ smp_get_save_area(c_cpus, cpu);
+ c_cpus++;
+ }
+ goto out;
+ }
+
+ if (info->has_cpu_type) {
+ for (cpu = 0; cpu < info->combined; cpu++) {
+ if (info->cpu[cpu].address == boot_cpu_addr) {
+ smp_cpu_type = info->cpu[cpu].type;
+ break;
+ }
+ }
+ }
+
+ for (cpu = 0; cpu < info->combined; cpu++) {
+ if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type)
+ continue;
+ cpu_addr = info->cpu[cpu].address;
+ if (cpu_addr == boot_cpu_addr)
continue;
- __cpu_logical_map[1] = (__u16) cpu;
- if (signal_processor(1, sigp_sense) == sigp_not_operational)
+ __cpu_logical_map[CPU_INIT_NO] = cpu_addr;
+ if (!cpu_stopped(CPU_INIT_NO)) {
+ s_cpus++;
continue;
- smp_get_save_area(num_cpus, cpu);
- num_cpus++;
+ }
+ smp_get_save_area(c_cpus, cpu_addr);
+ c_cpus++;
}
- printk("Detected %d CPU's\n", (int) num_cpus);
- printk("Boot cpu address %2X\n", boot_cpu_addr);
- return num_cpus;
+out:
+ kfree(info);
+ printk(KERN_INFO "CPUs: %d configured, %d standby\n", c_cpus, s_cpus);
+ get_online_cpus();
+ smp_rescan_cpus();
+ put_online_cpus();
}
/*
@@ -453,8 +601,6 @@ int __cpuinit start_secondary(void *cpuvoid)
return 0;
}
-DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
-
static void __init smp_create_idle(unsigned int cpu)
{
struct task_struct *p;
@@ -470,37 +616,82 @@ static void __init smp_create_idle(unsigned int cpu)
spin_lock_init(&(&per_cpu(s390_idle, cpu))->lock);
}
-static int cpu_stopped(int cpu)
+static int __cpuinit smp_alloc_lowcore(int cpu)
{
- __u32 status;
+ unsigned long async_stack, panic_stack;
+ struct _lowcore *lowcore;
+ int lc_order;
+
+ lc_order = sizeof(long) == 8 ? 1 : 0;
+ lowcore = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, lc_order);
+ if (!lowcore)
+ return -ENOMEM;
+ async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
+ if (!async_stack)
+ goto out_async_stack;
+ panic_stack = __get_free_page(GFP_KERNEL);
+ if (!panic_stack)
+ goto out_panic_stack;
+
+ *lowcore = S390_lowcore;
+ lowcore->async_stack = async_stack + ASYNC_SIZE;
+ lowcore->panic_stack = panic_stack + PAGE_SIZE;
- /* Check for stopped state */
- if (signal_processor_ps(&status, 0, cpu, sigp_sense) ==
- sigp_status_stored) {
- if (status & 0x40)
- return 1;
+#ifndef CONFIG_64BIT
+ if (MACHINE_HAS_IEEE) {
+ unsigned long save_area;
+
+ save_area = get_zeroed_page(GFP_KERNEL);
+ if (!save_area)
+ goto out_save_area;
+ lowcore->extended_save_area_addr = (u32) save_area;
}
+#endif
+ lowcore_ptr[cpu] = lowcore;
return 0;
+
+#ifndef CONFIG_64BIT
+out_save_area:
+ free_page(panic_stack);
+#endif
+out_panic_stack:
+ free_pages(async_stack, ASYNC_ORDER);
+out_async_stack:
+ free_pages((unsigned long) lowcore, lc_order);
+ return -ENOMEM;
}
-/* Upping and downing of CPUs */
+#ifdef CONFIG_HOTPLUG_CPU
+static void smp_free_lowcore(int cpu)
+{
+ struct _lowcore *lowcore;
+ int lc_order;
+
+ lc_order = sizeof(long) == 8 ? 1 : 0;
+ lowcore = lowcore_ptr[cpu];
+#ifndef CONFIG_64BIT
+ if (MACHINE_HAS_IEEE)
+ free_page((unsigned long) lowcore->extended_save_area_addr);
+#endif
+ free_page(lowcore->panic_stack - PAGE_SIZE);
+ free_pages(lowcore->async_stack - ASYNC_SIZE, ASYNC_ORDER);
+ free_pages((unsigned long) lowcore, lc_order);
+ lowcore_ptr[cpu] = NULL;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
-int __cpu_up(unsigned int cpu)
+/* Upping and downing of CPUs */
+int __cpuinit __cpu_up(unsigned int cpu)
{
struct task_struct *idle;
struct _lowcore *cpu_lowcore;
struct stack_frame *sf;
sigp_ccode ccode;
- int curr_cpu;
- for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) {
- __cpu_logical_map[cpu] = (__u16) curr_cpu;
- if (cpu_stopped(cpu))
- break;
- }
-
- if (!cpu_stopped(cpu))
- return -ENODEV;
+ if (smp_cpu_state[cpu] != CPU_STATE_CONFIGURED)
+ return -EIO;
+ if (smp_alloc_lowcore(cpu))
+ return -ENOMEM;
ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]),
cpu, sigp_set_prefix);
@@ -515,6 +706,7 @@ int __cpu_up(unsigned int cpu)
cpu_lowcore = lowcore_ptr[cpu];
cpu_lowcore->kernel_stack = (unsigned long)
task_stack_page(idle) + THREAD_SIZE;
+ cpu_lowcore->thread_info = (unsigned long) task_thread_info(idle);
sf = (struct stack_frame *) (cpu_lowcore->kernel_stack
- sizeof(struct pt_regs)
- sizeof(struct stack_frame));
@@ -528,6 +720,8 @@ int __cpu_up(unsigned int cpu)
cpu_lowcore->percpu_offset = __per_cpu_offset[cpu];
cpu_lowcore->current_task = (unsigned long) idle;
cpu_lowcore->cpu_data.cpu_nr = cpu;
+ cpu_lowcore->softirq_pending = 0;
+ cpu_lowcore->ext_call_fast = 0;
eieio();
while (signal_processor(cpu, sigp_restart) == sigp_busy)
@@ -538,44 +732,20 @@ int __cpu_up(unsigned int cpu)
return 0;
}
-static unsigned int __initdata additional_cpus;
-static unsigned int __initdata possible_cpus;
-
-void __init smp_setup_cpu_possible_map(void)
+static int __init setup_possible_cpus(char *s)
{
- unsigned int phy_cpus, pos_cpus, cpu;
-
- phy_cpus = smp_count_cpus();
- pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);
-
- if (possible_cpus)
- pos_cpus = min(possible_cpus, (unsigned int) NR_CPUS);
+ int pcpus, cpu;
- for (cpu = 0; cpu < pos_cpus; cpu++)
+ pcpus = simple_strtoul(s, NULL, 0);
+ cpu_possible_map = cpumask_of_cpu(0);
+ for (cpu = 1; cpu < pcpus && cpu < NR_CPUS; cpu++)
cpu_set(cpu, cpu_possible_map);
-
- phy_cpus = min(phy_cpus, pos_cpus);
-
- for (cpu = 0; cpu < phy_cpus; cpu++)
- cpu_set(cpu, cpu_present_map);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-
-static int __init setup_additional_cpus(char *s)
-{
- additional_cpus = simple_strtoul(s, NULL, 0);
- return 0;
-}
-early_param("additional_cpus", setup_additional_cpus);
-
-static int __init setup_possible_cpus(char *s)
-{
- possible_cpus = simple_strtoul(s, NULL, 0);
return 0;
}
early_param("possible_cpus", setup_possible_cpus);
+#ifdef CONFIG_HOTPLUG_CPU
+
int __cpu_disable(void)
{
struct ec_creg_mask_parms cr_parms;
@@ -612,7 +782,8 @@ void __cpu_die(unsigned int cpu)
/* Wait until target cpu is down */
while (!smp_cpu_not_running(cpu))
cpu_relax();
- printk("Processor %d spun down\n", cpu);
+ smp_free_lowcore(cpu);
+ printk(KERN_INFO "Processor %d spun down\n", cpu);
}
void cpu_die(void)
@@ -625,49 +796,19 @@ void cpu_die(void)
#endif /* CONFIG_HOTPLUG_CPU */
-/*
- * Cycle through the processors and setup structures.
- */
-
void __init smp_prepare_cpus(unsigned int max_cpus)
{
- unsigned long stack;
unsigned int cpu;
- int i;
+
+ smp_detect_cpus();
/* request the 0x1201 emergency signal external interrupt */
if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0)
panic("Couldn't request external interrupt 0x1201");
memset(lowcore_ptr, 0, sizeof(lowcore_ptr));
- /*
- * Initialize prefix pages and stacks for all possible cpus
- */
print_cpu_info(&S390_lowcore.cpu_data);
+ smp_alloc_lowcore(smp_processor_id());
- for_each_possible_cpu(i) {
- lowcore_ptr[i] = (struct _lowcore *)
- __get_free_pages(GFP_KERNEL | GFP_DMA,
- sizeof(void*) == 8 ? 1 : 0);
- stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
- if (!lowcore_ptr[i] || !stack)
- panic("smp_boot_cpus failed to allocate memory\n");
-
- *(lowcore_ptr[i]) = S390_lowcore;
- lowcore_ptr[i]->async_stack = stack + ASYNC_SIZE;
- stack = __get_free_pages(GFP_KERNEL, 0);
- if (!stack)
- panic("smp_boot_cpus failed to allocate memory\n");
- lowcore_ptr[i]->panic_stack = stack + PAGE_SIZE;
-#ifndef CONFIG_64BIT
- if (MACHINE_HAS_IEEE) {
- lowcore_ptr[i]->extended_save_area_addr =
- (__u32) __get_free_pages(GFP_KERNEL, 0);
- if (!lowcore_ptr[i]->extended_save_area_addr)
- panic("smp_boot_cpus failed to "
- "allocate memory\n");
- }
-#endif
- }
#ifndef CONFIG_64BIT
if (MACHINE_HAS_IEEE)
ctl_set_bit(14, 29); /* enable extended save area */
@@ -683,15 +824,17 @@ void __init smp_prepare_boot_cpu(void)
{
BUG_ON(smp_processor_id() != 0);
+ current_thread_info()->cpu = 0;
+ cpu_set(0, cpu_present_map);
cpu_set(0, cpu_online_map);
S390_lowcore.percpu_offset = __per_cpu_offset[0];
current_set[0] = current;
+ smp_cpu_state[0] = CPU_STATE_CONFIGURED;
spin_lock_init(&(&__get_cpu_var(s390_idle))->lock);
}
void __init smp_cpus_done(unsigned int max_cpus)
{
- cpu_present_map = cpu_possible_map;
}
/*
@@ -705,7 +848,79 @@ int setup_profiling_timer(unsigned int multiplier)
return 0;
}
-static DEFINE_PER_CPU(struct cpu, cpu_devices);
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t cpu_configure_show(struct sys_device *dev, char *buf)
+{
+ ssize_t count;
+
+ mutex_lock(&smp_cpu_state_mutex);
+ count = sprintf(buf, "%d\n", smp_cpu_state[dev->id]);
+ mutex_unlock(&smp_cpu_state_mutex);
+ return count;
+}
+
+static ssize_t cpu_configure_store(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ int cpu = dev->id;
+ int val, rc;
+ char delim;
+
+ if (sscanf(buf, "%d %c", &val, &delim) != 1)
+ return -EINVAL;
+ if (val != 0 && val != 1)
+ return -EINVAL;
+
+ mutex_lock(&smp_cpu_state_mutex);
+ get_online_cpus();
+ rc = -EBUSY;
+ if (cpu_online(cpu))
+ goto out;
+ rc = 0;
+ switch (val) {
+ case 0:
+ if (smp_cpu_state[cpu] == CPU_STATE_CONFIGURED) {
+ rc = sclp_cpu_deconfigure(__cpu_logical_map[cpu]);
+ if (!rc)
+ smp_cpu_state[cpu] = CPU_STATE_STANDBY;
+ }
+ break;
+ case 1:
+ if (smp_cpu_state[cpu] == CPU_STATE_STANDBY) {
+ rc = sclp_cpu_configure(__cpu_logical_map[cpu]);
+ if (!rc)
+ smp_cpu_state[cpu] = CPU_STATE_CONFIGURED;
+ }
+ break;
+ default:
+ break;
+ }
+out:
+ put_online_cpus();
+ mutex_unlock(&smp_cpu_state_mutex);
+ return rc ? rc : count;
+}
+static SYSDEV_ATTR(configure, 0644, cpu_configure_show, cpu_configure_store);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static ssize_t show_cpu_address(struct sys_device *dev, char *buf)
+{
+ return sprintf(buf, "%d\n", __cpu_logical_map[dev->id]);
+}
+static SYSDEV_ATTR(address, 0444, show_cpu_address, NULL);
+
+
+static struct attribute *cpu_common_attrs[] = {
+#ifdef CONFIG_HOTPLUG_CPU
+ &attr_configure.attr,
+#endif
+ &attr_address.attr,
+ NULL,
+};
+
+static struct attribute_group cpu_common_attr_group = {
+ .attrs = cpu_common_attrs,
+};
static ssize_t show_capability(struct sys_device *dev, char *buf)
{
@@ -750,15 +965,15 @@ static ssize_t show_idle_time(struct sys_device *dev, char *buf)
}
static SYSDEV_ATTR(idle_time_us, 0444, show_idle_time, NULL);
-static struct attribute *cpu_attrs[] = {
+static struct attribute *cpu_online_attrs[] = {
&attr_capability.attr,
&attr_idle_count.attr,
&attr_idle_time_us.attr,
NULL,
};
-static struct attribute_group cpu_attr_group = {
- .attrs = cpu_attrs,
+static struct attribute_group cpu_online_attr_group = {
+ .attrs = cpu_online_attrs,
};
static int __cpuinit smp_cpu_notify(struct notifier_block *self,
@@ -778,12 +993,12 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self,
idle->idle_time = 0;
idle->idle_count = 0;
spin_unlock_irq(&idle->lock);
- if (sysfs_create_group(&s->kobj, &cpu_attr_group))
+ if (sysfs_create_group(&s->kobj, &cpu_online_attr_group))
return NOTIFY_BAD;
break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
- sysfs_remove_group(&s->kobj, &cpu_attr_group);
+ sysfs_remove_group(&s->kobj, &cpu_online_attr_group);
break;
}
return NOTIFY_OK;
@@ -793,6 +1008,62 @@ static struct notifier_block __cpuinitdata smp_cpu_nb = {
.notifier_call = smp_cpu_notify,
};
+static int smp_add_present_cpu(int cpu)
+{
+ struct cpu *c = &per_cpu(cpu_devices, cpu);
+ struct sys_device *s = &c->sysdev;
+ int rc;
+
+ c->hotpluggable = 1;
+ rc = register_cpu(c, cpu);
+ if (rc)
+ goto out;
+ rc = sysfs_create_group(&s->kobj, &cpu_common_attr_group);
+ if (rc)
+ goto out_cpu;
+ if (!cpu_online(cpu))
+ goto out;
+ rc = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
+ if (!rc)
+ return 0;
+ sysfs_remove_group(&s->kobj, &cpu_common_attr_group);
+out_cpu:
+#ifdef CONFIG_HOTPLUG_CPU
+ unregister_cpu(c);
+#endif
+out:
+ return rc;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t rescan_store(struct sys_device *dev, const char *buf,
+ size_t count)
+{
+ cpumask_t newcpus;
+ int cpu;
+ int rc;
+
+ mutex_lock(&smp_cpu_state_mutex);
+ get_online_cpus();
+ newcpus = cpu_present_map;
+ rc = smp_rescan_cpus();
+ if (rc)
+ goto out;
+ cpus_andnot(newcpus, cpu_present_map, newcpus);
+ for_each_cpu_mask(cpu, newcpus) {
+ rc = smp_add_present_cpu(cpu);
+ if (rc)
+ cpu_clear(cpu, cpu_present_map);
+ }
+ rc = 0;
+out:
+ put_online_cpus();
+ mutex_unlock(&smp_cpu_state_mutex);
+ return rc ? rc : count;
+}
+static SYSDEV_ATTR(rescan, 0200, NULL, rescan_store);
+#endif /* CONFIG_HOTPLUG_CPU */
+
static int __init topology_init(void)
{
int cpu;
@@ -800,16 +1071,14 @@ static int __init topology_init(void)
register_cpu_notifier(&smp_cpu_nb);
- for_each_possible_cpu(cpu) {
- struct cpu *c = &per_cpu(cpu_devices, cpu);
- struct sys_device *s = &c->sysdev;
-
- c->hotpluggable = 1;
- register_cpu(c, cpu);
- if (!cpu_online(cpu))
- continue;
- s = &c->sysdev;
- rc = sysfs_create_group(&s->kobj, &cpu_attr_group);
+#ifdef CONFIG_HOTPLUG_CPU
+ rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
+ &attr_rescan.attr);
+ if (rc)
+ return rc;
+#endif
+ for_each_present_cpu(cpu) {
+ rc = smp_add_present_cpu(cpu);
if (rc)
return rc;
}
diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
index 8ed16a83fba..52b8342c6bf 100644
--- a/arch/s390/kernel/traps.c
+++ b/arch/s390/kernel/traps.c
@@ -31,6 +31,7 @@
#include <linux/reboot.h>
#include <linux/kprobes.h>
#include <linux/bug.h>
+#include <linux/utsname.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -168,9 +169,16 @@ void show_stack(struct task_struct *task, unsigned long *sp)
*/
void dump_stack(void)
{
+ printk("CPU: %d %s %s %.*s\n",
+ task_thread_info(current)->cpu, print_tainted(),
+ init_utsname()->release,
+ (int)strcspn(init_utsname()->version, " "),
+ init_utsname()->version);
+ printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
+ current->comm, current->pid, current,
+ (void *) current->thread.ksp);
show_stack(NULL, NULL);
}
-
EXPORT_SYMBOL(dump_stack);
static inline int mask_bits(struct pt_regs *regs, unsigned long bits)
@@ -258,8 +266,14 @@ void die(const char * str, struct pt_regs * regs, long err)
console_verbose();
spin_lock_irq(&die_lock);
bust_spinlocks(1);
- printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
- print_modules();
+ printk("%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
+#ifdef CONFIG_PREEMPT
+ printk("PREEMPT ");
+#endif
+#ifdef CONFIG_SMP
+ printk("SMP");
+#endif
+ printk("\n");
notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV);
show_regs(regs);
bust_spinlocks(0);
diff --git a/arch/s390/kernel/vmlinux.lds.S b/arch/s390/kernel/vmlinux.lds.S
index 849120e3e28..93615919934 100644
--- a/arch/s390/kernel/vmlinux.lds.S
+++ b/arch/s390/kernel/vmlinux.lds.S
@@ -17,6 +17,12 @@ ENTRY(_start)
jiffies = jiffies_64;
#endif
+PHDRS {
+ text PT_LOAD FLAGS(5); /* R_E */
+ data PT_LOAD FLAGS(7); /* RWE */
+ note PT_NOTE FLAGS(0); /* ___ */
+}
+
SECTIONS
{
. = 0x00000000;
@@ -33,6 +39,9 @@ SECTIONS
_etext = .; /* End of text section */
+ NOTES :text :note
+ BUG_TABLE :text
+
RODATA
#ifdef CONFIG_SHARED_KERNEL
@@ -49,9 +58,6 @@ SECTIONS
__stop___ex_table = .;
}
- NOTES
- BUG_TABLE
-
.data : { /* Data */
DATA_DATA
CONSTRUCTORS
diff --git a/arch/s390/lib/spinlock.c b/arch/s390/lib/spinlock.c
index 8d76403fcf8..e41f4008afc 100644
--- a/arch/s390/lib/spinlock.c
+++ b/arch/s390/lib/spinlock.c
@@ -39,7 +39,7 @@ static inline void _raw_yield_cpu(int cpu)
_raw_yield();
}
-void _raw_spin_lock_wait(raw_spinlock_t *lp, unsigned int pc)
+void _raw_spin_lock_wait(raw_spinlock_t *lp)
{
int count = spin_retry;
unsigned int cpu = ~smp_processor_id();
@@ -53,15 +53,36 @@ void _raw_spin_lock_wait(raw_spinlock_t *lp, unsigned int pc)
}
if (__raw_spin_is_locked(lp))
continue;
- if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) {
- lp->owner_pc = pc;
+ if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0)
return;
- }
}
}
EXPORT_SYMBOL(_raw_spin_lock_wait);
-int _raw_spin_trylock_retry(raw_spinlock_t *lp, unsigned int pc)
+void _raw_spin_lock_wait_flags(raw_spinlock_t *lp, unsigned long flags)
+{
+ int count = spin_retry;
+ unsigned int cpu = ~smp_processor_id();
+
+ local_irq_restore(flags);
+ while (1) {
+ if (count-- <= 0) {
+ unsigned int owner = lp->owner_cpu;
+ if (owner != 0)
+ _raw_yield_cpu(~owner);
+ count = spin_retry;
+ }
+ if (__raw_spin_is_locked(lp))
+ continue;
+ local_irq_disable();
+ if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0)
+ return;
+ local_irq_restore(flags);
+ }
+}
+EXPORT_SYMBOL(_raw_spin_lock_wait_flags);
+
+int _raw_spin_trylock_retry(raw_spinlock_t *lp)
{
unsigned int cpu = ~smp_processor_id();
int count;
@@ -69,10 +90,8 @@ int _raw_spin_trylock_retry(raw_spinlock_t *lp, unsigned int pc)
for (count = spin_retry; count > 0; count--) {
if (__raw_spin_is_locked(lp))
continue;
- if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) {
- lp->owner_pc = pc;
+ if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0)
return 1;
- }
}
return 0;
}
diff --git a/arch/s390/mm/extmem.c b/arch/s390/mm/extmem.c
index 394980b05e6..880b0ebf894 100644
--- a/arch/s390/mm/extmem.c
+++ b/arch/s390/mm/extmem.c
@@ -83,7 +83,7 @@ struct dcss_segment {
};
static DEFINE_MUTEX(dcss_lock);
-static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
+static LIST_HEAD(dcss_list);
static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
"EW/EN-MIXED" };
diff --git a/arch/s390/mm/vmem.c b/arch/s390/mm/vmem.c
index fb9c5a85aa5..79d13a166a3 100644
--- a/arch/s390/mm/vmem.c
+++ b/arch/s390/mm/vmem.c
@@ -15,10 +15,6 @@
#include <asm/setup.h>
#include <asm/tlbflush.h>
-unsigned long vmalloc_end;
-EXPORT_SYMBOL(vmalloc_end);
-
-static struct page *vmem_map;
static DEFINE_MUTEX(vmem_mutex);
struct memory_segment {
@@ -188,8 +184,8 @@ static int vmem_add_mem_map(unsigned long start, unsigned long size)
pte_t pte;
int ret = -ENOMEM;
- map_start = vmem_map + PFN_DOWN(start);
- map_end = vmem_map + PFN_DOWN(start + size);
+ map_start = VMEM_MAP + PFN_DOWN(start);
+ map_end = VMEM_MAP + PFN_DOWN(start + size);
start_addr = (unsigned long) map_start & PAGE_MASK;
end_addr = PFN_ALIGN((unsigned long) map_end);
@@ -240,10 +236,10 @@ static int vmem_add_mem(unsigned long start, unsigned long size)
{
int ret;
- ret = vmem_add_range(start, size);
+ ret = vmem_add_mem_map(start, size);
if (ret)
return ret;
- return vmem_add_mem_map(start, size);
+ return vmem_add_range(start, size);
}
/*
@@ -254,7 +250,7 @@ static int insert_memory_segment(struct memory_segment *seg)
{
struct memory_segment *tmp;
- if (PFN_DOWN(seg->start + seg->size) > max_pfn ||
+ if (seg->start + seg->size >= VMALLOC_START ||
seg->start + seg->size < seg->start)
return -ERANGE;
@@ -357,17 +353,15 @@ out:
/*
* map whole physical memory to virtual memory (identity mapping)
+ * we reserve enough space in the vmalloc area for vmemmap to hotplug
+ * additional memory segments.
*/
void __init vmem_map_init(void)
{
- unsigned long map_size;
int i;
- map_size = ALIGN(max_low_pfn, MAX_ORDER_NR_PAGES) * sizeof(struct page);
- vmalloc_end = PFN_ALIGN(VMALLOC_END_INIT) - PFN_ALIGN(map_size);
- vmem_map = (struct page *) vmalloc_end;
- NODE_DATA(0)->node_mem_map = vmem_map;
-
+ BUILD_BUG_ON((unsigned long)VMEM_MAP + VMEM_MAP_SIZE > VMEM_MAP_MAX);
+ NODE_DATA(0)->node_mem_map = VMEM_MAP;
for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++)
vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size);
}
@@ -382,7 +376,7 @@ static int __init vmem_convert_memory_chunk(void)
int i;
mutex_lock(&vmem_mutex);
- for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
+ for (i = 0; i < MEMORY_CHUNKS; i++) {
if (!memory_chunk[i].size)
continue;
seg = kzalloc(sizeof(*seg), GFP_KERNEL);
diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig
index 8a70a9edabd..6b658d84d52 100644
--- a/drivers/crypto/Kconfig
+++ b/drivers/crypto/Kconfig
@@ -48,8 +48,6 @@ config CRYPTO_DEV_PADLOCK_SHA
If unsure say M. The compiled module will be
called padlock-sha.ko
-source "arch/s390/crypto/Kconfig"
-
config CRYPTO_DEV_GEODE
tristate "Support for the Geode LX AES engine"
depends on X86_32 && PCI
@@ -83,6 +81,67 @@ config ZCRYPT_MONOLITHIC
that contains all parts of the crypto device driver (ap bus,
request router and all the card drivers).
+config CRYPTO_SHA1_S390
+ tristate "SHA1 digest algorithm"
+ depends on S390
+ select CRYPTO_ALGAPI
+ help
+ This is the s390 hardware accelerated implementation of the
+ SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
+
+config CRYPTO_SHA256_S390
+ tristate "SHA256 digest algorithm"
+ depends on S390
+ select CRYPTO_ALGAPI
+ help
+ This is the s390 hardware accelerated implementation of the
+ SHA256 secure hash standard (DFIPS 180-2).
+
+ This version of SHA implements a 256 bit hash with 128 bits of
+ security against collision attacks.
+
+config CRYPTO_DES_S390
+ tristate "DES and Triple DES cipher algorithms"
+ depends on S390
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ help
+ This us the s390 hardware accelerated implementation of the
+ DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3).
+
+config CRYPTO_AES_S390
+ tristate "AES cipher algorithms"
+ depends on S390
+ select CRYPTO_ALGAPI
+ select CRYPTO_BLKCIPHER
+ help
+ This is the s390 hardware accelerated implementation of the
+ AES cipher algorithms (FIPS-197). AES uses the Rijndael
+ algorithm.
+
+ Rijndael appears to be consistently a very good performer in
+ both hardware and software across a wide range of computing
+ environments regardless of its use in feedback or non-feedback
+ modes. Its key setup time is excellent, and its key agility is
+ good. Rijndael's very low memory requirements make it very well
+ suited for restricted-space environments, in which it also
+ demonstrates excellent performance. Rijndael's operations are
+ among the easiest to defend against power and timing attacks.
+
+ On s390 the System z9-109 currently only supports the key size
+ of 128 bit.
+
+config S390_PRNG
+ tristate "Pseudo random number generator device driver"
+ depends on S390
+ default "m"
+ help
+ Select this option if you want to use the s390 pseudo random number
+ generator. The PRNG is part of the cryptographic processor functions
+ and uses triple-DES to generate secure random numbers like the
+ ANSI X9.17 standard. The PRNG is usable via the char device
+ /dev/prandom.
+
config CRYPTO_DEV_HIFN_795X
tristate "Driver HIFN 795x crypto accelerator chips"
select CRYPTO_DES
diff --git a/drivers/s390/block/Makefile b/drivers/s390/block/Makefile
index be9f22d52fd..0a89e080b38 100644
--- a/drivers/s390/block/Makefile
+++ b/drivers/s390/block/Makefile
@@ -2,8 +2,8 @@
# S/390 block devices
#
-dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o
-dasd_fba_mod-objs := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o
+dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o
+dasd_fba_mod-objs := dasd_fba.o
dasd_diag_mod-objs := dasd_diag.o
dasd_mod-objs := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \
dasd_genhd.o dasd_erp.o
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index e6bfce690ca..1db15f3e5d2 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -48,13 +48,15 @@ MODULE_LICENSE("GPL");
/*
* SECTION: prototypes for static functions of dasd.c
*/
-static int dasd_alloc_queue(struct dasd_device * device);
-static void dasd_setup_queue(struct dasd_device * device);
-static void dasd_free_queue(struct dasd_device * device);
-static void dasd_flush_request_queue(struct dasd_device *);
-static int dasd_flush_ccw_queue(struct dasd_device *, int);
-static void dasd_tasklet(struct dasd_device *);
+static int dasd_alloc_queue(struct dasd_block *);
+static void dasd_setup_queue(struct dasd_block *);
+static void dasd_free_queue(struct dasd_block *);
+static void dasd_flush_request_queue(struct dasd_block *);
+static int dasd_flush_block_queue(struct dasd_block *);
+static void dasd_device_tasklet(struct dasd_device *);
+static void dasd_block_tasklet(struct dasd_block *);
static void do_kick_device(struct work_struct *);
+static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
/*
* SECTION: Operations on the device structure.
@@ -65,26 +67,23 @@ static wait_queue_head_t dasd_flush_wq;
/*
* Allocate memory for a new device structure.
*/
-struct dasd_device *
-dasd_alloc_device(void)
+struct dasd_device *dasd_alloc_device(void)
{
struct dasd_device *device;
- device = kzalloc(sizeof (struct dasd_device), GFP_ATOMIC);
- if (device == NULL)
+ device = kzalloc(sizeof(struct dasd_device), GFP_ATOMIC);
+ if (!device)
return ERR_PTR(-ENOMEM);
- /* open_count = 0 means device online but not in use */
- atomic_set(&device->open_count, -1);
/* Get two pages for normal block device operations. */
device->ccw_mem = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, 1);
- if (device->ccw_mem == NULL) {
+ if (!device->ccw_mem) {
kfree(device);
return ERR_PTR(-ENOMEM);
}
/* Get one page for error recovery. */
device->erp_mem = (void *) get_zeroed_page(GFP_ATOMIC | GFP_DMA);
- if (device->erp_mem == NULL) {
+ if (!device->erp_mem) {
free_pages((unsigned long) device->ccw_mem, 1);
kfree(device);
return ERR_PTR(-ENOMEM);
@@ -93,10 +92,9 @@ dasd_alloc_device(void)
dasd_init_chunklist(&device->ccw_chunks, device->ccw_mem, PAGE_SIZE*2);
dasd_init_chunklist(&device->erp_chunks, device->erp_mem, PAGE_SIZE);
spin_lock_init(&device->mem_lock);
- spin_lock_init(&device->request_queue_lock);
- atomic_set (&device->tasklet_scheduled, 0);
+ atomic_set(&device->tasklet_scheduled, 0);
tasklet_init(&device->tasklet,
- (void (*)(unsigned long)) dasd_tasklet,
+ (void (*)(unsigned long)) dasd_device_tasklet,
(unsigned long) device);
INIT_LIST_HEAD(&device->ccw_queue);
init_timer(&device->timer);
@@ -110,8 +108,7 @@ dasd_alloc_device(void)
/*
* Free memory of a device structure.
*/
-void
-dasd_free_device(struct dasd_device *device)
+void dasd_free_device(struct dasd_device *device)
{
kfree(device->private);
free_page((unsigned long) device->erp_mem);
@@ -120,10 +117,42 @@ dasd_free_device(struct dasd_device *device)
}
/*
+ * Allocate memory for a new device structure.
+ */
+struct dasd_block *dasd_alloc_block(void)
+{
+ struct dasd_block *block;
+
+ block = kzalloc(sizeof(*block), GFP_ATOMIC);
+ if (!block)
+ return ERR_PTR(-ENOMEM);
+ /* open_count = 0 means device online but not in use */
+ atomic_set(&block->open_count, -1);
+
+ spin_lock_init(&block->request_queue_lock);
+ atomic_set(&block->tasklet_scheduled, 0);
+ tasklet_init(&block->tasklet,
+ (void (*)(unsigned long)) dasd_block_tasklet,
+ (unsigned long) block);
+ INIT_LIST_HEAD(&block->ccw_queue);
+ spin_lock_init(&block->queue_lock);
+ init_timer(&block->timer);
+
+ return block;
+}
+
+/*
+ * Free memory of a device structure.
+ */
+void dasd_free_block(struct dasd_block *block)
+{
+ kfree(block);
+}
+
+/*
* Make a new device known to the system.
*/
-static int
-dasd_state_new_to_known(struct dasd_device *device)
+static int dasd_state_new_to_known(struct dasd_device *device)
{
int rc;
@@ -133,12 +162,13 @@ dasd_state_new_to_known(struct dasd_device *device)
*/
dasd_get_device(device);
- rc = dasd_alloc_queue(device);
- if (rc) {
- dasd_put_device(device);
- return rc;
+ if (device->block) {
+ rc = dasd_alloc_queue(device->block);
+ if (rc) {
+ dasd_put_device(device);
+ return rc;
+ }
}
-
device->state = DASD_STATE_KNOWN;
return 0;
}
@@ -146,21 +176,24 @@ dasd_state_new_to_known(struct dasd_device *device)
/*
* Let the system forget about a device.
*/
-static int
-dasd_state_known_to_new(struct dasd_device * device)
+static int dasd_state_known_to_new(struct dasd_device *device)
{
/* Disable extended error reporting for this device. */
dasd_eer_disable(device);
/* Forget the discipline information. */
- if (device->discipline)
+ if (device->discipline) {
+ if (device->discipline->uncheck_device)
+ device->discipline->uncheck_device(device);
module_put(device->discipline->owner);
+ }
device->discipline = NULL;
if (device->base_discipline)
module_put(device->base_discipline->owner);
device->base_discipline = NULL;
device->state = DASD_STATE_NEW;
- dasd_free_queue(device);
+ if (device->block)
+ dasd_free_queue(device->block);
/* Give up reference we took in dasd_state_new_to_known. */
dasd_put_device(device);
@@ -170,19 +203,19 @@ dasd_state_known_to_new(struct dasd_device * device)
/*
* Request the irq line for the device.
*/
-static int
-dasd_state_known_to_basic(struct dasd_device * device)
+static int dasd_state_known_to_basic(struct dasd_device *device)
{
int rc;
/* Allocate and register gendisk structure. */
- rc = dasd_gendisk_alloc(device);
- if (rc)
- return rc;
-
+ if (device->block) {
+ rc = dasd_gendisk_alloc(device->block);
+ if (rc)
+ return rc;
+ }
/* register 'device' debug area, used for all DBF_DEV_XXX calls */
- device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2,
- 8 * sizeof (long));
+ device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 1,
+ 8 * sizeof(long));
debug_register_view(device->debug_area, &debug_sprintf_view);
debug_set_level(device->debug_area, DBF_WARNING);
DBF_DEV_EVENT(DBF_EMERG, device, "%s", "debug area created");
@@ -194,16 +227,17 @@ dasd_state_known_to_basic(struct dasd_device * device)
/*
* Release the irq line for the device. Terminate any running i/o.
*/
-static int
-dasd_state_basic_to_known(struct dasd_device * device)
+static int dasd_state_basic_to_known(struct dasd_device *device)
{
int rc;
-
- dasd_gendisk_free(device);
- rc = dasd_flush_ccw_queue(device, 1);
+ if (device->block) {
+ dasd_gendisk_free(device->block);
+ dasd_block_clear_timer(device->block);
+ }
+ rc = dasd_flush_device_queue(device);
if (rc)
return rc;
- dasd_clear_timer(device);
+ dasd_device_clear_timer(device);
DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device);
if (device->debug_area != NULL) {
@@ -228,26 +262,32 @@ dasd_state_basic_to_known(struct dasd_device * device)
* In case the analysis returns an error, the device setup is stopped
* (a fake disk was already added to allow formatting).
*/
-static int
-dasd_state_basic_to_ready(struct dasd_device * device)
+static int dasd_state_basic_to_ready(struct dasd_device *device)
{
int rc;
+ struct dasd_block *block;
rc = 0;
- if (device->discipline->do_analysis != NULL)
- rc = device->discipline->do_analysis(device);
- if (rc) {
- if (rc != -EAGAIN)
- device->state = DASD_STATE_UNFMT;
- return rc;
- }
+ block = device->block;
/* make disk known with correct capacity */
- dasd_setup_queue(device);
- set_capacity(device->gdp, device->blocks << device->s2b_shift);
- device->state = DASD_STATE_READY;
- rc = dasd_scan_partitions(device);
- if (rc)
- device->state = DASD_STATE_BASIC;
+ if (block) {
+ if (block->base->discipline->do_analysis != NULL)
+ rc = block->base->discipline->do_analysis(block);
+ if (rc) {
+ if (rc != -EAGAIN)
+ device->state = DASD_STATE_UNFMT;
+ return rc;
+ }
+ dasd_setup_queue(block);
+ set_capacity(block->gdp,
+ block->blocks << block->s2b_shift);
+ device->state = DASD_STATE_READY;
+ rc = dasd_scan_partitions(block);
+ if (rc)
+ device->state = DASD_STATE_BASIC;
+ } else {
+ device->state = DASD_STATE_READY;
+ }
return rc;
}
@@ -256,28 +296,31 @@ dasd_state_basic_to_ready(struct dasd_device * device)
* Forget format information. Check if the target level is basic
* and if it is create fake disk for formatting.
*/
-static int
-dasd_state_ready_to_basic(struct dasd_device * device)
+static int dasd_state_ready_to_basic(struct dasd_device *device)
{
int rc;
- rc = dasd_flush_ccw_queue(device, 0);
- if (rc)
- return rc;
- dasd_destroy_partitions(device);
- dasd_flush_request_queue(device);
- device->blocks = 0;
- device->bp_block = 0;
- device->s2b_shift = 0;
device->state = DASD_STATE_BASIC;
+ if (device->block) {
+ struct dasd_block *block = device->block;
+ rc = dasd_flush_block_queue(block);
+ if (rc) {
+ device->state = DASD_STATE_READY;
+ return rc;
+ }
+ dasd_destroy_partitions(block);
+ dasd_flush_request_queue(block);
+ block->blocks = 0;
+ block->bp_block = 0;
+ block->s2b_shift = 0;
+ }
return 0;
}
/*
* Back to basic.
*/
-static int
-dasd_state_unfmt_to_basic(struct dasd_device * device)
+static int dasd_state_unfmt_to_basic(struct dasd_device *device)
{
device->state = DASD_STATE_BASIC;
return 0;
@@ -291,17 +334,31 @@ dasd_state_unfmt_to_basic(struct dasd_device * device)
static int
dasd_state_ready_to_online(struct dasd_device * device)
{
+ int rc;
+
+ if (device->discipline->ready_to_online) {
+ rc = device->discipline->ready_to_online(device);
+ if (rc)
+ return rc;
+ }
device->state = DASD_STATE_ONLINE;
- dasd_schedule_bh(device);
+ if (device->block)
+ dasd_schedule_block_bh(device->block);
return 0;
}
/*
* Stop the requeueing of requests again.
*/
-static int
-dasd_state_online_to_ready(struct dasd_device * device)
+static int dasd_state_online_to_ready(struct dasd_device *device)
{
+ int rc;
+
+ if (device->discipline->online_to_ready) {
+ rc = device->discipline->online_to_ready(device);
+ if (rc)
+ return rc;
+ }
device->state = DASD_STATE_READY;
return 0;
}
@@ -309,8 +366,7 @@ dasd_state_online_to_ready(struct dasd_device * device)
/*
* Device startup state changes.
*/
-static int
-dasd_increase_state(struct dasd_device *device)
+static int dasd_increase_state(struct dasd_device *device)
{
int rc;
@@ -345,8 +401,7 @@ dasd_increase_state(struct dasd_device *device)
/*
* Device shutdown state changes.
*/
-static int
-dasd_decrease_state(struct dasd_device *device)
+static int dasd_decrease_state(struct dasd_device *device)
{
int rc;
@@ -381,8 +436,7 @@ dasd_decrease_state(struct dasd_device *device)
/*
* This is the main startup/shutdown routine.
*/
-static void
-dasd_change_state(struct dasd_device *device)
+static void dasd_change_state(struct dasd_device *device)
{
int rc;
@@ -409,17 +463,15 @@ dasd_change_state(struct dasd_device *device)
* dasd_kick_device will schedule a call do do_kick_device to the kernel
* event daemon.
*/
-static void
-do_kick_device(struct work_struct *work)
+static void do_kick_device(struct work_struct *work)
{
struct dasd_device *device = container_of(work, struct dasd_device, kick_work);
dasd_change_state(device);
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
dasd_put_device(device);
}
-void
-dasd_kick_device(struct dasd_device *device)
+void dasd_kick_device(struct dasd_device *device)
{
dasd_get_device(device);
/* queue call to dasd_kick_device to the kernel event daemon. */
@@ -429,8 +481,7 @@ dasd_kick_device(struct dasd_device *device)
/*
* Set the target state for a device and starts the state change.
*/
-void
-dasd_set_target_state(struct dasd_device *device, int target)
+void dasd_set_target_state(struct dasd_device *device, int target)
{
/* If we are in probeonly mode stop at DASD_STATE_READY. */
if (dasd_probeonly && target > DASD_STATE_READY)
@@ -447,14 +498,12 @@ dasd_set_target_state(struct dasd_device *device, int target)
/*
* Enable devices with device numbers in [from..to].
*/
-static inline int
-_wait_for_device(struct dasd_device *device)
+static inline int _wait_for_device(struct dasd_device *device)
{
return (device->state == device->target);
}
-void
-dasd_enable_device(struct dasd_device *device)
+void dasd_enable_device(struct dasd_device *device)
{
dasd_set_target_state(device, DASD_STATE_ONLINE);
if (device->state <= DASD_STATE_KNOWN)
@@ -475,20 +524,20 @@ unsigned int dasd_profile_level = DASD_PROFILE_OFF;
/*
* Increments counter in global and local profiling structures.
*/
-#define dasd_profile_counter(value, counter, device) \
+#define dasd_profile_counter(value, counter, block) \
{ \
int index; \
for (index = 0; index < 31 && value >> (2+index); index++); \
dasd_global_profile.counter[index]++; \
- device->profile.counter[index]++; \
+ block->profile.counter[index]++; \
}
/*
* Add profiling information for cqr before execution.
*/
-static void
-dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr,
- struct request *req)
+static void dasd_profile_start(struct dasd_block *block,
+ struct dasd_ccw_req *cqr,
+ struct request *req)
{
struct list_head *l;
unsigned int counter;
@@ -498,19 +547,19 @@ dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr,
/* count the length of the chanq for statistics */
counter = 0;
- list_for_each(l, &device->ccw_queue)
+ list_for_each(l, &block->ccw_queue)
if (++counter >= 31)
break;
dasd_global_profile.dasd_io_nr_req[counter]++;
- device->profile.dasd_io_nr_req[counter]++;
+ block->profile.dasd_io_nr_req[counter]++;
}
/*
* Add profiling information for cqr after execution.
*/
-static void
-dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr,
- struct request *req)
+static void dasd_profile_end(struct dasd_block *block,
+ struct dasd_ccw_req *cqr,
+ struct request *req)
{
long strtime, irqtime, endtime, tottime; /* in microseconds */
long tottimeps, sectors;
@@ -532,27 +581,27 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr,
if (!dasd_global_profile.dasd_io_reqs)
memset(&dasd_global_profile, 0,
- sizeof (struct dasd_profile_info_t));
+ sizeof(struct dasd_profile_info_t));
dasd_global_profile.dasd_io_reqs++;
dasd_global_profile.dasd_io_sects += sectors;
- if (!device->profile.dasd_io_reqs)
- memset(&device->profile, 0,
- sizeof (struct dasd_profile_info_t));
- device->profile.dasd_io_reqs++;
- device->profile.dasd_io_sects += sectors;
+ if (!block->profile.dasd_io_reqs)
+ memset(&block->profile, 0,
+ sizeof(struct dasd_profile_info_t));
+ block->profile.dasd_io_reqs++;
+ block->profile.dasd_io_sects += sectors;
- dasd_profile_counter(sectors, dasd_io_secs, device);
- dasd_profile_counter(tottime, dasd_io_times, device);
- dasd_profile_counter(tottimeps, dasd_io_timps, device);
- dasd_profile_counter(strtime, dasd_io_time1, device);
- dasd_profile_counter(irqtime, dasd_io_time2, device);
- dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, device);
- dasd_profile_counter(endtime, dasd_io_time3, device);
+ dasd_profile_counter(sectors, dasd_io_secs, block);
+ dasd_profile_counter(tottime, dasd_io_times, block);
+ dasd_profile_counter(tottimeps, dasd_io_timps, block);
+ dasd_profile_counter(strtime, dasd_io_time1, block);
+ dasd_profile_counter(irqtime, dasd_io_time2, block);
+ dasd_profile_counter(irqtime / sectors, dasd_io_time2ps, block);
+ dasd_profile_counter(endtime, dasd_io_time3, block);
}
#else
-#define dasd_profile_start(device, cqr, req) do {} while (0)
-#define dasd_profile_end(device, cqr, req) do {} while (0)
+#define dasd_profile_start(block, cqr, req) do {} while (0)
+#define dasd_profile_end(block, cqr, req) do {} while (0)
#endif /* CONFIG_DASD_PROFILE */
/*
@@ -562,9 +611,9 @@ dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr,
* memory and 2) dasd_smalloc_request uses the static ccw memory
* that gets allocated for each device.
*/
-struct dasd_ccw_req *
-dasd_kmalloc_request(char *magic, int cplength, int datasize,
- struct dasd_device * device)
+struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength,
+ int datasize,
+ struct dasd_device *device)
{
struct dasd_ccw_req *cqr;
@@ -600,9 +649,9 @@ dasd_kmalloc_request(char *magic, int cplength, int datasize,
return cqr;
}
-struct dasd_ccw_req *
-dasd_smalloc_request(char *magic, int cplength, int datasize,
- struct dasd_device * device)
+struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength,
+ int datasize,
+ struct dasd_device *device)
{
unsigned long flags;
struct dasd_ccw_req *cqr;
@@ -649,8 +698,7 @@ dasd_smalloc_request(char *magic, int cplength, int datasize,
* idal lists that might have been created by dasd_set_cda and the
* struct dasd_ccw_req itself.
*/
-void
-dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
+void dasd_kfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
{
#ifdef CONFIG_64BIT
struct ccw1 *ccw;
@@ -667,8 +715,7 @@ dasd_kfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
dasd_put_device(device);
}
-void
-dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
+void dasd_sfree_request(struct dasd_ccw_req *cqr, struct dasd_device *device)
{
unsigned long flags;
@@ -681,14 +728,13 @@ dasd_sfree_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
/*
* Check discipline magic in cqr.
*/
-static inline int
-dasd_check_cqr(struct dasd_ccw_req *cqr)
+static inline int dasd_check_cqr(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
if (cqr == NULL)
return -EINVAL;
- device = cqr->device;
+ device = cqr->startdev;
if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) {
DEV_MESSAGE(KERN_WARNING, device,
" dasd_ccw_req 0x%08x magic doesn't match"
@@ -706,8 +752,7 @@ dasd_check_cqr(struct dasd_ccw_req *cqr)
* ccw_device_clear can fail if the i/o subsystem
* is in a bad mood.
*/
-int
-dasd_term_IO(struct dasd_ccw_req * cqr)
+int dasd_term_IO(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
int retries, rc;
@@ -717,13 +762,13 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
if (rc)
return rc;
retries = 0;
- device = (struct dasd_device *) cqr->device;
+ device = (struct dasd_device *) cqr->startdev;
while ((retries < 5) && (cqr->status == DASD_CQR_IN_IO)) {
rc = ccw_device_clear(device->cdev, (long) cqr);
switch (rc) {
case 0: /* termination successful */
cqr->retries--;
- cqr->status = DASD_CQR_CLEAR;
+ cqr->status = DASD_CQR_CLEAR_PENDING;
cqr->stopclk = get_clock();
cqr->starttime = 0;
DBF_DEV_EVENT(DBF_DEBUG, device,
@@ -753,7 +798,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
}
retries++;
}
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
return rc;
}
@@ -761,8 +806,7 @@ dasd_term_IO(struct dasd_ccw_req * cqr)
* Start the i/o. This start_IO can fail if the channel is really busy.
* In that case set up a timer to start the request later.
*/
-int
-dasd_start_IO(struct dasd_ccw_req * cqr)
+int dasd_start_IO(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
int rc;
@@ -771,12 +815,12 @@ dasd_start_IO(struct dasd_ccw_req * cqr)
rc = dasd_check_cqr(cqr);
if (rc)
return rc;
- device = (struct dasd_device *) cqr->device;
+ device = (struct dasd_device *) cqr->startdev;
if (cqr->retries < 0) {
DEV_MESSAGE(KERN_DEBUG, device,
"start_IO: request %p (%02x/%i) - no retry left.",
cqr, cqr->status, cqr->retries);
- cqr->status = DASD_CQR_FAILED;
+ cqr->status = DASD_CQR_ERROR;
return -EIO;
}
cqr->startclk = get_clock();
@@ -833,8 +877,7 @@ dasd_start_IO(struct dasd_ccw_req * cqr)
* The head of the ccw queue will have status DASD_CQR_IN_IO for 1),
* DASD_CQR_QUEUED for 2) and 3).
*/
-static void
-dasd_timeout_device(unsigned long ptr)
+static void dasd_device_timeout(unsigned long ptr)
{
unsigned long flags;
struct dasd_device *device;
@@ -844,14 +887,13 @@ dasd_timeout_device(unsigned long ptr)
/* re-activate request queue */
device->stopped &= ~DASD_STOPPED_PENDING;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
}
/*
* Setup timeout for a device in jiffies.
*/
-void
-dasd_set_timer(struct dasd_device *device, int expires)
+void dasd_device_set_timer(struct dasd_device *device, int expires)
{
if (expires == 0) {
if (timer_pending(&device->timer))
@@ -862,7 +904,7 @@ dasd_set_timer(struct dasd_device *device, int expires)
if (mod_timer(&device->timer, jiffies + expires))
return;
}
- device->timer.function = dasd_timeout_device;
+ device->timer.function = dasd_device_timeout;
device->timer.data = (unsigned long) device;
device->timer.expires = jiffies + expires;
add_timer(&device->timer);
@@ -871,15 +913,14 @@ dasd_set_timer(struct dasd_device *device, int expires)
/*
* Clear timeout for a device.
*/
-void
-dasd_clear_timer(struct dasd_device *device)
+void dasd_device_clear_timer(struct dasd_device *device)
{
if (timer_pending(&device->timer))
del_timer(&device->timer);
}
-static void
-dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
+static void dasd_handle_killed_request(struct ccw_device *cdev,
+ unsigned long intparm)
{
struct dasd_ccw_req *cqr;
struct dasd_device *device;
@@ -893,7 +934,7 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
return;
}
- device = (struct dasd_device *) cqr->device;
+ device = (struct dasd_device *) cqr->startdev;
if (device == NULL ||
device != dasd_device_from_cdev_locked(cdev) ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
@@ -905,46 +946,32 @@ dasd_handle_killed_request(struct ccw_device *cdev, unsigned long intparm)
/* Schedule request to be retried. */
cqr->status = DASD_CQR_QUEUED;
- dasd_clear_timer(device);
- dasd_schedule_bh(device);
+ dasd_device_clear_timer(device);
+ dasd_schedule_device_bh(device);
dasd_put_device(device);
}
-static void
-dasd_handle_state_change_pending(struct dasd_device *device)
+void dasd_generic_handle_state_change(struct dasd_device *device)
{
- struct dasd_ccw_req *cqr;
- struct list_head *l, *n;
-
/* First of all start sense subsystem status request. */
dasd_eer_snss(device);
device->stopped &= ~DASD_STOPPED_PENDING;
-
- /* restart all 'running' IO on queue */
- list_for_each_safe(l, n, &device->ccw_queue) {
- cqr = list_entry(l, struct dasd_ccw_req, list);
- if (cqr->status == DASD_CQR_IN_IO) {
- cqr->status = DASD_CQR_QUEUED;
- }
- }
- dasd_clear_timer(device);
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
+ if (device->block)
+ dasd_schedule_block_bh(device->block);
}
/*
* Interrupt handler for "normal" ssch-io based dasd devices.
*/
-void
-dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
- struct irb *irb)
+void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
+ struct irb *irb)
{
struct dasd_ccw_req *cqr, *next;
struct dasd_device *device;
unsigned long long now;
int expires;
- dasd_era_t era;
- char mask;
if (IS_ERR(irb)) {
switch (PTR_ERR(irb)) {
@@ -969,29 +996,25 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat),
(unsigned int) intparm);
- /* first of all check for state change pending interrupt */
- mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
- if ((irb->scsw.dstat & mask) == mask) {
+ /* check for unsolicited interrupts */
+ cqr = (struct dasd_ccw_req *) intparm;
+ if (!cqr || ((irb->scsw.cc == 1) &&
+ (irb->scsw.fctl & SCSW_FCTL_START_FUNC) &&
+ (irb->scsw.stctl & SCSW_STCTL_STATUS_PEND)) ) {
+ if (cqr && cqr->status == DASD_CQR_IN_IO)
+ cqr->status = DASD_CQR_QUEUED;
device = dasd_device_from_cdev_locked(cdev);
if (!IS_ERR(device)) {
- dasd_handle_state_change_pending(device);
+ dasd_device_clear_timer(device);
+ device->discipline->handle_unsolicited_interrupt(device,
+ irb);
dasd_put_device(device);
}
return;
}
- cqr = (struct dasd_ccw_req *) intparm;
-
- /* check for unsolicited interrupts */
- if (cqr == NULL) {
- MESSAGE(KERN_DEBUG,
- "unsolicited interrupt received: bus_id %s",
- cdev->dev.bus_id);
- return;
- }
-
- device = (struct dasd_device *) cqr->device;
- if (device == NULL ||
+ device = (struct dasd_device *) cqr->startdev;
+ if (!device ||
strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s",
cdev->dev.bus_id);
@@ -999,12 +1022,12 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
}
/* Check for clear pending */
- if (cqr->status == DASD_CQR_CLEAR &&
+ if (cqr->status == DASD_CQR_CLEAR_PENDING &&
irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) {
- cqr->status = DASD_CQR_QUEUED;
- dasd_clear_timer(device);
+ cqr->status = DASD_CQR_CLEARED;
+ dasd_device_clear_timer(device);
wake_up(&dasd_flush_wq);
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
return;
}
@@ -1017,277 +1040,170 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
}
DBF_DEV_EVENT(DBF_DEBUG, device, "Int: CS/DS 0x%04x for cqr %p",
((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
-
- /* Find out the appropriate era_action. */
- if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
- era = dasd_era_fatal;
- else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
- irb->scsw.cstat == 0 &&
- !irb->esw.esw0.erw.cons)
- era = dasd_era_none;
- else if (irb->esw.esw0.erw.cons)
- era = device->discipline->examine_error(cqr, irb);
- else
- era = dasd_era_recover;
-
- DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era);
+ next = NULL;
expires = 0;
- if (era == dasd_era_none) {
- cqr->status = DASD_CQR_DONE;
+ if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
+ irb->scsw.cstat == 0 && !irb->esw.esw0.erw.cons) {
+ /* request was completed successfully */
+ cqr->status = DASD_CQR_SUCCESS;
cqr->stopclk = now;
/* Start first request on queue if possible -> fast_io. */
- if (cqr->list.next != &device->ccw_queue) {
- next = list_entry(cqr->list.next,
- struct dasd_ccw_req, list);
- if ((next->status == DASD_CQR_QUEUED) &&
- (!device->stopped)) {
- if (device->discipline->start_IO(next) == 0)
- expires = next->expires;
- else
- DEV_MESSAGE(KERN_DEBUG, device, "%s",
- "Interrupt fastpath "
- "failed!");
- }
+ if (cqr->devlist.next != &device->ccw_queue) {
+ next = list_entry(cqr->devlist.next,
+ struct dasd_ccw_req, devlist);
}
- } else { /* error */
- memcpy(&cqr->irb, irb, sizeof (struct irb));
+ } else { /* error */
+ memcpy(&cqr->irb, irb, sizeof(struct irb));
if (device->features & DASD_FEATURE_ERPLOG) {
- /* dump sense data */
dasd_log_sense(cqr, irb);
}
- switch (era) {
- case dasd_era_fatal:
- cqr->status = DASD_CQR_FAILED;
- cqr->stopclk = now;
- break;
- case dasd_era_recover:
+ /* If we have no sense data, or we just don't want complex ERP
+ * for this request, but if we have retries left, then just
+ * reset this request and retry it in the fastpath
+ */
+ if (!(cqr->irb.esw.esw0.erw.cons &&
+ test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags)) &&
+ cqr->retries > 0) {
+ DEV_MESSAGE(KERN_DEBUG, device,
+ "default ERP in fastpath (%i retries left)",
+ cqr->retries);
+ cqr->lpm = LPM_ANYPATH;
+ cqr->status = DASD_CQR_QUEUED;
+ next = cqr;
+ } else
cqr->status = DASD_CQR_ERROR;
- break;
- default:
- BUG();
- }
+ }
+ if (next && (next->status == DASD_CQR_QUEUED) &&
+ (!device->stopped)) {
+ if (device->discipline->start_IO(next) == 0)
+ expires = next->expires;
+ else
+ DEV_MESSAGE(KERN_DEBUG, device, "%s",
+ "Interrupt fastpath "
+ "failed!");
}
if (expires != 0)
- dasd_set_timer(device, expires);
+ dasd_device_set_timer(device, expires);
else
- dasd_clear_timer(device);
- dasd_schedule_bh(device);
+ dasd_device_clear_timer(device);
+ dasd_schedule_device_bh(device);
}
/*
- * posts the buffer_cache about a finalized request
+ * If we have an error on a dasd_block layer request then we cancel
+ * and return all further requests from the same dasd_block as well.
*/
-static inline void
-dasd_end_request(struct request *req, int uptodate)
+static void __dasd_device_recovery(struct dasd_device *device,
+ struct dasd_ccw_req *ref_cqr)
{
- if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
- BUG();
- add_disk_randomness(req->rq_disk);
- end_that_request_last(req, uptodate);
-}
+ struct list_head *l, *n;
+ struct dasd_ccw_req *cqr;
-/*
- * Process finished error recovery ccw.
- */
-static inline void
-__dasd_process_erp(struct dasd_device *device, struct dasd_ccw_req *cqr)
-{
- dasd_erp_fn_t erp_fn;
+ /*
+ * only requeue request that came from the dasd_block layer
+ */
+ if (!ref_cqr->block)
+ return;
- if (cqr->status == DASD_CQR_DONE)
- DBF_DEV_EVENT(DBF_NOTICE, device, "%s", "ERP successful");
- else
- DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful");
- erp_fn = device->discipline->erp_postaction(cqr);
- erp_fn(cqr);
-}
+ list_for_each_safe(l, n, &device->ccw_queue) {
+ cqr = list_entry(l, struct dasd_ccw_req, devlist);
+ if (cqr->status == DASD_CQR_QUEUED &&
+ ref_cqr->block == cqr->block) {
+ cqr->status = DASD_CQR_CLEARED;
+ }
+ }
+};
/*
- * Process ccw request queue.
+ * Remove those ccw requests from the queue that need to be returned
+ * to the upper layer.
*/
-static void
-__dasd_process_ccw_queue(struct dasd_device * device,
- struct list_head *final_queue)
+static void __dasd_device_process_ccw_queue(struct dasd_device *device,
+ struct list_head *final_queue)
{
struct list_head *l, *n;
struct dasd_ccw_req *cqr;
- dasd_erp_fn_t erp_fn;
-restart:
/* Process request with final status. */
list_for_each_safe(l, n, &device->ccw_queue) {
- cqr = list_entry(l, struct dasd_ccw_req, list);
+ cqr = list_entry(l, struct dasd_ccw_req, devlist);
+
/* Stop list processing at the first non-final request. */
- if (cqr->status != DASD_CQR_DONE &&
- cqr->status != DASD_CQR_FAILED &&
- cqr->status != DASD_CQR_ERROR)
+ if (cqr->status == DASD_CQR_QUEUED ||
+ cqr->status == DASD_CQR_IN_IO ||
+ cqr->status == DASD_CQR_CLEAR_PENDING)
break;
- /* Process requests with DASD_CQR_ERROR */
if (cqr->status == DASD_CQR_ERROR) {
- if (cqr->irb.scsw.fctl & SCSW_FCTL_HALT_FUNC) {
- cqr->status = DASD_CQR_FAILED;
- cqr->stopclk = get_clock();
- } else {
- if (cqr->irb.esw.esw0.erw.cons &&
- test_bit(DASD_CQR_FLAGS_USE_ERP,
- &cqr->flags)) {
- erp_fn = device->discipline->
- erp_action(cqr);
- erp_fn(cqr);
- } else
- dasd_default_erp_action(cqr);
- }
- goto restart;
- }
-
- /* First of all call extended error reporting. */
- if (dasd_eer_enabled(device) &&
- cqr->status == DASD_CQR_FAILED) {
- dasd_eer_write(device, cqr, DASD_EER_FATALERROR);
-
- /* restart request */
- cqr->status = DASD_CQR_QUEUED;
- cqr->retries = 255;
- device->stopped |= DASD_STOPPED_QUIESCE;
- goto restart;
+ __dasd_device_recovery(device, cqr);
}
-
- /* Process finished ERP request. */
- if (cqr->refers) {
- __dasd_process_erp(device, cqr);
- goto restart;
- }
-
/* Rechain finished requests to final queue */
- cqr->endclk = get_clock();
- list_move_tail(&cqr->list, final_queue);
+ list_move_tail(&cqr->devlist, final_queue);
}
}
-static void
-dasd_end_request_cb(struct dasd_ccw_req * cqr, void *data)
-{
- struct request *req;
- struct dasd_device *device;
- int status;
-
- req = (struct request *) data;
- device = cqr->device;
- dasd_profile_end(device, cqr, req);
- status = cqr->device->discipline->free_cp(cqr,req);
- spin_lock_irq(&device->request_queue_lock);
- dasd_end_request(req, status);
- spin_unlock_irq(&device->request_queue_lock);
-}
-
-
/*
- * Fetch requests from the block device queue.
+ * the cqrs from the final queue are returned to the upper layer
+ * by setting a dasd_block state and calling the callback function
*/
-static void
-__dasd_process_blk_queue(struct dasd_device * device)
+static void __dasd_device_process_final_queue(struct dasd_device *device,
+ struct list_head *final_queue)
{
- struct request_queue *queue;
- struct request *req;
+ struct list_head *l, *n;
struct dasd_ccw_req *cqr;
- int nr_queued;
-
- queue = device->request_queue;
- /* No queue ? Then there is nothing to do. */
- if (queue == NULL)
- return;
-
- /*
- * We requeue request from the block device queue to the ccw
- * queue only in two states. In state DASD_STATE_READY the
- * partition detection is done and we need to requeue requests
- * for that. State DASD_STATE_ONLINE is normal block device
- * operation.
- */
- if (device->state != DASD_STATE_READY &&
- device->state != DASD_STATE_ONLINE)
- return;
- nr_queued = 0;
- /* Now we try to fetch requests from the request queue */
- list_for_each_entry(cqr, &device->ccw_queue, list)
- if (cqr->status == DASD_CQR_QUEUED)
- nr_queued++;
- while (!blk_queue_plugged(queue) &&
- elv_next_request(queue) &&
- nr_queued < DASD_CHANQ_MAX_SIZE) {
- req = elv_next_request(queue);
- if (device->features & DASD_FEATURE_READONLY &&
- rq_data_dir(req) == WRITE) {
- DBF_DEV_EVENT(DBF_ERR, device,
- "Rejecting write request %p",
- req);
- blkdev_dequeue_request(req);
- dasd_end_request(req, 0);
- continue;
- }
- if (device->stopped & DASD_STOPPED_DC_EIO) {
- blkdev_dequeue_request(req);
- dasd_end_request(req, 0);
- continue;
- }
- cqr = device->discipline->build_cp(device, req);
- if (IS_ERR(cqr)) {
- if (PTR_ERR(cqr) == -ENOMEM)
- break; /* terminate request queue loop */
- if (PTR_ERR(cqr) == -EAGAIN) {
- /*
- * The current request cannot be build right
- * now, we have to try later. If this request
- * is the head-of-queue we stop the device
- * for 1/2 second.
- */
- if (!list_empty(&device->ccw_queue))
- break;
- device->stopped |= DASD_STOPPED_PENDING;
- dasd_set_timer(device, HZ/2);
- break;
- }
- DBF_DEV_EVENT(DBF_ERR, device,
- "CCW creation failed (rc=%ld) "
- "on request %p",
- PTR_ERR(cqr), req);
- blkdev_dequeue_request(req);
- dasd_end_request(req, 0);
- continue;
+ list_for_each_safe(l, n, final_queue) {
+ cqr = list_entry(l, struct dasd_ccw_req, devlist);
+ list_del_init(&cqr->devlist);
+ if (cqr->block)
+ spin_lock_bh(&cqr->block->queue_lock);
+ switch (cqr->status) {
+ case DASD_CQR_SUCCESS:
+ cqr->status = DASD_CQR_DONE;
+ break;
+ case DASD_CQR_ERROR:
+ cqr->status = DASD_CQR_NEED_ERP;
+ break;
+ case DASD_CQR_CLEARED:
+ cqr->status = DASD_CQR_TERMINATED;
+ break;
+ default:
+ DEV_MESSAGE(KERN_ERR, device,
+ "wrong cqr status in __dasd_process_final_queue "
+ "for cqr %p, status %x",
+ cqr, cqr->status);
+ BUG();
}
- cqr->callback = dasd_end_request_cb;
- cqr->callback_data = (void *) req;
- cqr->status = DASD_CQR_QUEUED;
- blkdev_dequeue_request(req);
- list_add_tail(&cqr->list, &device->ccw_queue);
- dasd_profile_start(device, cqr, req);
- nr_queued++;
+ if (cqr->block)
+ spin_unlock_bh(&cqr->block->queue_lock);
+ if (cqr->callback != NULL)
+ (cqr->callback)(cqr, cqr->callback_data);
}
}
+
+
/*
* Take a look at the first request on the ccw queue and check
* if it reached its expire time. If so, terminate the IO.
*/
-static void
-__dasd_check_expire(struct dasd_device * device)
+static void __dasd_device_check_expire(struct dasd_device *device)
{
struct dasd_ccw_req *cqr;
if (list_empty(&device->ccw_queue))
return;
- cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
+ cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) &&
(time_after_eq(jiffies, cqr->expires + cqr->starttime))) {
if (device->discipline->term_IO(cqr) != 0) {
/* Hmpf, try again in 5 sec */
- dasd_set_timer(device, 5*HZ);
DEV_MESSAGE(KERN_ERR, device,
"internal error - timeout (%is) expired "
"for cqr %p, termination failed, "
"retrying in 5s",
(cqr->expires/HZ), cqr);
+ cqr->expires += 5*HZ;
+ dasd_device_set_timer(device, 5*HZ);
} else {
DEV_MESSAGE(KERN_ERR, device,
"internal error - timeout (%is) expired "
@@ -1301,77 +1217,53 @@ __dasd_check_expire(struct dasd_device * device)
* Take a look at the first request on the ccw queue and check
* if it needs to be started.
*/
-static void
-__dasd_start_head(struct dasd_device * device)
+static void __dasd_device_start_head(struct dasd_device *device)
{
struct dasd_ccw_req *cqr;
int rc;
if (list_empty(&device->ccw_queue))
return;
- cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
+ cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
if (cqr->status != DASD_CQR_QUEUED)
return;
- /* 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;
- dasd_schedule_bh(device);
+ /* when device is stopped, return request to previous layer */
+ if (device->stopped) {
+ cqr->status = DASD_CQR_CLEARED;
+ dasd_schedule_device_bh(device);
return;
}
- /* Don't try to start requests if device is stopped */
- if (device->stopped)
- return;
rc = device->discipline->start_IO(cqr);
if (rc == 0)
- dasd_set_timer(device, cqr->expires);
+ dasd_device_set_timer(device, cqr->expires);
else if (rc == -EACCES) {
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
} else
/* Hmpf, try again in 1/2 sec */
- dasd_set_timer(device, 50);
-}
-
-static inline int
-_wait_for_clear(struct dasd_ccw_req *cqr)
-{
- return (cqr->status == DASD_CQR_QUEUED);
+ dasd_device_set_timer(device, 50);
}
/*
- * Remove all requests from the ccw queue (all = '1') or only block device
- * requests in case all = '0'.
- * Take care of the erp-chain (chained via cqr->refers) and remove either
- * the whole erp-chain or none of the erp-requests.
- * If a request is currently running, term_IO is called and the request
- * is re-queued. Prior to removing the terminated request we need to wait
- * for the clear-interrupt.
- * In case termination is not possible we stop processing and just finishing
- * the already moved requests.
+ * Go through all request on the dasd_device request queue,
+ * terminate them on the cdev if necessary, and return them to the
+ * submitting layer via callback.
+ * Note:
+ * Make sure that all 'submitting layers' still exist when
+ * this function is called!. In other words, when 'device' is a base
+ * device then all block layer requests must have been removed before
+ * via dasd_flush_block_queue.
*/
-static int
-dasd_flush_ccw_queue(struct dasd_device * device, int all)
+int dasd_flush_device_queue(struct dasd_device *device)
{
- struct dasd_ccw_req *cqr, *orig, *n;
- int rc, i;
-
+ struct dasd_ccw_req *cqr, *n;
+ int rc;
struct list_head flush_queue;
INIT_LIST_HEAD(&flush_queue);
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = 0;
-restart:
- list_for_each_entry_safe(cqr, n, &device->ccw_queue, list) {
- /* get original request of erp request-chain */
- for (orig = cqr; orig->refers != NULL; orig = orig->refers);
-
- /* Flush all request or only block device requests? */
- if (all == 0 && cqr->callback != dasd_end_request_cb &&
- orig->callback != dasd_end_request_cb) {
- continue;
- }
+ list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) {
/* Check status and move request to flush_queue */
switch (cqr->status) {
case DASD_CQR_IN_IO:
@@ -1387,90 +1279,60 @@ restart:
}
break;
case DASD_CQR_QUEUED:
- case DASD_CQR_ERROR:
- /* set request to FAILED */
cqr->stopclk = get_clock();
- cqr->status = DASD_CQR_FAILED;
+ cqr->status = DASD_CQR_CLEARED;
break;
- default: /* do not touch the others */
+ default: /* no need to modify the others */
break;
}
- /* Rechain request (including erp chain) */
- for (i = 0; cqr != NULL; cqr = cqr->refers, i++) {
- cqr->endclk = get_clock();
- list_move_tail(&cqr->list, &flush_queue);
- }
- if (i > 1)
- /* moved more than one request - need to restart */
- goto restart;
+ list_move_tail(&cqr->devlist, &flush_queue);
}
-
finished:
spin_unlock_irq(get_ccwdev_lock(device->cdev));
- /* Now call the callback function of flushed requests */
-restart_cb:
- list_for_each_entry_safe(cqr, n, &flush_queue, list) {
- if (cqr->status == DASD_CQR_CLEAR) {
- /* wait for clear interrupt! */
- wait_event(dasd_flush_wq, _wait_for_clear(cqr));
- cqr->status = DASD_CQR_FAILED;
- }
- /* Process finished ERP request. */
- if (cqr->refers) {
- __dasd_process_erp(device, cqr);
- /* restart list_for_xx loop since dasd_process_erp
- * might remove multiple elements */
- goto restart_cb;
- }
- /* call the callback function */
- cqr->endclk = get_clock();
- if (cqr->callback != NULL)
- (cqr->callback)(cqr, cqr->callback_data);
- }
+ /*
+ * After this point all requests must be in state CLEAR_PENDING,
+ * CLEARED, SUCCESS or ERROR. Now wait for CLEAR_PENDING to become
+ * one of the others.
+ */
+ list_for_each_entry_safe(cqr, n, &flush_queue, devlist)
+ wait_event(dasd_flush_wq,
+ (cqr->status != DASD_CQR_CLEAR_PENDING));
+ /*
+ * Now set each request back to TERMINATED, DONE or NEED_ERP
+ * and call the callback function of flushed requests
+ */
+ __dasd_device_process_final_queue(device, &flush_queue);
return rc;
}
/*
* Acquire the device lock and process queues for the device.
*/
-static void
-dasd_tasklet(struct dasd_device * device)
+static void dasd_device_tasklet(struct dasd_device *device)
{
struct list_head final_queue;
- struct list_head *l, *n;
- struct dasd_ccw_req *cqr;
atomic_set (&device->tasklet_scheduled, 0);
INIT_LIST_HEAD(&final_queue);
spin_lock_irq(get_ccwdev_lock(device->cdev));
/* Check expire time of first request on the ccw queue. */
- __dasd_check_expire(device);
- /* Finish off requests on ccw queue */
- __dasd_process_ccw_queue(device, &final_queue);
+ __dasd_device_check_expire(device);
+ /* find final requests on ccw queue */
+ __dasd_device_process_ccw_queue(device, &final_queue);
spin_unlock_irq(get_ccwdev_lock(device->cdev));
/* Now call the callback function of requests with final status */
- list_for_each_safe(l, n, &final_queue) {
- cqr = list_entry(l, struct dasd_ccw_req, list);
- list_del_init(&cqr->list);
- if (cqr->callback != NULL)
- (cqr->callback)(cqr, cqr->callback_data);
- }
- spin_lock_irq(&device->request_queue_lock);
- spin_lock(get_ccwdev_lock(device->cdev));
- /* Get new request from the block device request queue */
- __dasd_process_blk_queue(device);
+ __dasd_device_process_final_queue(device, &final_queue);
+ spin_lock_irq(get_ccwdev_lock(device->cdev));
/* Now check if the head of the ccw queue needs to be started. */
- __dasd_start_head(device);
- spin_unlock(get_ccwdev_lock(device->cdev));
- spin_unlock_irq(&device->request_queue_lock);
+ __dasd_device_start_head(device);
+ spin_unlock_irq(get_ccwdev_lock(device->cdev));
dasd_put_device(device);
}
/*
* Schedules a call to dasd_tasklet over the device tasklet.
*/
-void
-dasd_schedule_bh(struct dasd_device * device)
+void dasd_schedule_device_bh(struct dasd_device *device)
{
/* Protect against rescheduling. */
if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0)
@@ -1480,160 +1342,109 @@ dasd_schedule_bh(struct dasd_device * device)
}
/*
- * Queue a request to the head of the ccw_queue. Start the I/O if
- * possible.
+ * Queue a request to the head of the device ccw_queue.
+ * Start the I/O if possible.
*/
-void
-dasd_add_request_head(struct dasd_ccw_req *req)
+void dasd_add_request_head(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
unsigned long flags;
- device = req->device;
+ device = cqr->startdev;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- req->status = DASD_CQR_QUEUED;
- req->device = device;
- list_add(&req->list, &device->ccw_queue);
+ cqr->status = DASD_CQR_QUEUED;
+ list_add(&cqr->devlist, &device->ccw_queue);
/* let the bh start the request to keep them in order */
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
/*
- * Queue a request to the tail of the ccw_queue. Start the I/O if
- * possible.
+ * Queue a request to the tail of the device ccw_queue.
+ * Start the I/O if possible.
*/
-void
-dasd_add_request_tail(struct dasd_ccw_req *req)
+void dasd_add_request_tail(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
unsigned long flags;
- device = req->device;
+ device = cqr->startdev;
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- req->status = DASD_CQR_QUEUED;
- req->device = device;
- list_add_tail(&req->list, &device->ccw_queue);
+ cqr->status = DASD_CQR_QUEUED;
+ list_add_tail(&cqr->devlist, &device->ccw_queue);
/* let the bh start the request to keep them in order */
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
/*
- * Wakeup callback.
+ * Wakeup helper for the 'sleep_on' functions.
*/
-static void
-dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
+static void dasd_wakeup_cb(struct dasd_ccw_req *cqr, void *data)
{
wake_up((wait_queue_head_t *) data);
}
-static inline int
-_wait_for_wakeup(struct dasd_ccw_req *cqr)
+static inline int _wait_for_wakeup(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
int rc;
- device = cqr->device;
+ device = cqr->startdev;
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = ((cqr->status == DASD_CQR_DONE ||
- cqr->status == DASD_CQR_FAILED) &&
- list_empty(&cqr->list));
+ cqr->status == DASD_CQR_NEED_ERP ||
+ cqr->status == DASD_CQR_TERMINATED) &&
+ list_empty(&cqr->devlist));
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc;
}
/*
- * Attempts to start a special ccw queue and waits for its completion.
+ * 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)
+int dasd_sleep_on(struct dasd_ccw_req *cqr)
{
wait_queue_head_t wait_q;
struct dasd_device *device;
int rc;
- device = cqr->device;
- spin_lock_irq(get_ccwdev_lock(device->cdev));
+ device = cqr->startdev;
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q;
- cqr->status = DASD_CQR_QUEUED;
- list_add_tail(&cqr->list, &device->ccw_queue);
-
- /* let the bh start the request to keep them in order */
- dasd_schedule_bh(device);
-
- spin_unlock_irq(get_ccwdev_lock(device->cdev));
-
+ dasd_add_request_tail(cqr);
wait_event(wait_q, _wait_for_wakeup(cqr));
/* Request status is either done or failed. */
- rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+ rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
return rc;
}
/*
- * Attempts to start a special ccw queue and wait interruptible
- * for its completion.
+ * Queue a request to the tail of the device ccw_queue and wait
+ * interruptible for it's completion.
*/
-int
-dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr)
+int dasd_sleep_on_interruptible(struct dasd_ccw_req *cqr)
{
wait_queue_head_t wait_q;
struct dasd_device *device;
- int rc, finished;
-
- device = cqr->device;
- spin_lock_irq(get_ccwdev_lock(device->cdev));
+ int rc;
+ device = cqr->startdev;
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q;
- cqr->status = DASD_CQR_QUEUED;
- list_add_tail(&cqr->list, &device->ccw_queue);
-
- /* let the bh start the request to keep them in order */
- dasd_schedule_bh(device);
- spin_unlock_irq(get_ccwdev_lock(device->cdev));
-
- finished = 0;
- while (!finished) {
- rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr));
- if (rc != -ERESTARTSYS) {
- /* Request is final (done or failed) */
- rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
- break;
- }
- spin_lock_irq(get_ccwdev_lock(device->cdev));
- switch (cqr->status) {
- case DASD_CQR_IN_IO:
- /* terminate runnig cqr */
- if (device->discipline->term_IO) {
- cqr->retries = -1;
- device->discipline->term_IO(cqr);
- /* wait (non-interruptible) for final status
- * because signal ist still pending */
- spin_unlock_irq(get_ccwdev_lock(device->cdev));
- wait_event(wait_q, _wait_for_wakeup(cqr));
- spin_lock_irq(get_ccwdev_lock(device->cdev));
- rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
- finished = 1;
- }
- break;
- case DASD_CQR_QUEUED:
- /* request */
- list_del_init(&cqr->list);
- rc = -EIO;
- finished = 1;
- break;
- default:
- /* cqr with 'non-interruptable' status - just wait */
- break;
- }
- spin_unlock_irq(get_ccwdev_lock(device->cdev));
+ dasd_add_request_tail(cqr);
+ rc = wait_event_interruptible(wait_q, _wait_for_wakeup(cqr));
+ if (rc == -ERESTARTSYS) {
+ dasd_cancel_req(cqr);
+ /* wait (non-interruptible) for final status */
+ wait_event(wait_q, _wait_for_wakeup(cqr));
}
+ rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
return rc;
}
@@ -1643,25 +1454,23 @@ dasd_sleep_on_interruptible(struct dasd_ccw_req * cqr)
* and be put back to status queued, before the special request is added
* to the head of the queue. Then the special request is waited on normally.
*/
-static inline int
-_dasd_term_running_cqr(struct dasd_device *device)
+static inline int _dasd_term_running_cqr(struct dasd_device *device)
{
struct dasd_ccw_req *cqr;
if (list_empty(&device->ccw_queue))
return 0;
- cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list);
+ cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
return device->discipline->term_IO(cqr);
}
-int
-dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
+int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr)
{
wait_queue_head_t wait_q;
struct dasd_device *device;
int rc;
- device = cqr->device;
+ device = cqr->startdev;
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = _dasd_term_running_cqr(device);
if (rc) {
@@ -1673,17 +1482,17 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED;
- list_add(&cqr->list, &device->ccw_queue);
+ list_add(&cqr->devlist, &device->ccw_queue);
/* let the bh start the request to keep them in order */
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
/* Request status is either done or failed. */
- rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+ rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
return rc;
}
@@ -1692,11 +1501,14 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
* This is useful to timeout requests. The request will be
* terminated if it is currently in i/o.
* Returns 1 if the request has been terminated.
+ * 0 if there was no need to terminate the request (not started yet)
+ * negative error code if termination failed
+ * Cancellation of a request is an asynchronous operation! The calling
+ * function has to wait until the request is properly returned via callback.
*/
-int
-dasd_cancel_req(struct dasd_ccw_req *cqr)
+int dasd_cancel_req(struct dasd_ccw_req *cqr)
{
- struct dasd_device *device = cqr->device;
+ struct dasd_device *device = cqr->startdev;
unsigned long flags;
int rc;
@@ -1704,74 +1516,453 @@ dasd_cancel_req(struct dasd_ccw_req *cqr)
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
switch (cqr->status) {
case DASD_CQR_QUEUED:
- /* request was not started - just set to failed */
- cqr->status = DASD_CQR_FAILED;
+ /* request was not started - just set to cleared */
+ cqr->status = DASD_CQR_CLEARED;
break;
case DASD_CQR_IN_IO:
/* request in IO - terminate IO and release again */
- if (device->discipline->term_IO(cqr) != 0)
- /* what to do if unable to terminate ??????
- e.g. not _IN_IO */
- cqr->status = DASD_CQR_FAILED;
- cqr->stopclk = get_clock();
- rc = 1;
+ rc = device->discipline->term_IO(cqr);
+ if (rc) {
+ DEV_MESSAGE(KERN_ERR, device,
+ "dasd_cancel_req is unable "
+ " to terminate request %p, rc = %d",
+ cqr, rc);
+ } else {
+ cqr->stopclk = get_clock();
+ rc = 1;
+ }
break;
- case DASD_CQR_DONE:
- case DASD_CQR_FAILED:
- /* already finished - do nothing */
+ default: /* already finished or clear pending - do nothing */
break;
- default:
- DEV_MESSAGE(KERN_ALERT, device,
- "invalid status %02x in request",
- cqr->status);
+ }
+ spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+ dasd_schedule_device_bh(device);
+ return rc;
+}
+
+
+/*
+ * SECTION: Operations of the dasd_block layer.
+ */
+
+/*
+ * Timeout function for dasd_block. This is used when the block layer
+ * is waiting for something that may not come reliably, (e.g. a state
+ * change interrupt)
+ */
+static void dasd_block_timeout(unsigned long ptr)
+{
+ unsigned long flags;
+ struct dasd_block *block;
+
+ 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;
+ spin_unlock_irqrestore(get_ccwdev_lock(block->base->cdev), flags);
+ dasd_schedule_block_bh(block);
+}
+
+/*
+ * Setup timeout for a dasd_block in jiffies.
+ */
+void dasd_block_set_timer(struct dasd_block *block, int expires)
+{
+ if (expires == 0) {
+ if (timer_pending(&block->timer))
+ del_timer(&block->timer);
+ return;
+ }
+ if (timer_pending(&block->timer)) {
+ if (mod_timer(&block->timer, jiffies + expires))
+ return;
+ }
+ block->timer.function = dasd_block_timeout;
+ block->timer.data = (unsigned long) block;
+ block->timer.expires = jiffies + expires;
+ add_timer(&block->timer);
+}
+
+/*
+ * Clear timeout for a dasd_block.
+ */
+void dasd_block_clear_timer(struct dasd_block *block)
+{
+ if (timer_pending(&block->timer))
+ del_timer(&block->timer);
+}
+
+/*
+ * posts the buffer_cache about a finalized request
+ */
+static inline void dasd_end_request(struct request *req, int uptodate)
+{
+ if (end_that_request_first(req, uptodate, req->hard_nr_sectors))
BUG();
+ add_disk_randomness(req->rq_disk);
+ end_that_request_last(req, uptodate);
+}
+
+/*
+ * Process finished error recovery ccw.
+ */
+static inline void __dasd_block_process_erp(struct dasd_block *block,
+ 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");
+ else
+ DEV_MESSAGE(KERN_ERR, device, "%s", "ERP unsuccessful");
+ erp_fn = device->discipline->erp_postaction(cqr);
+ erp_fn(cqr);
+}
+
+/*
+ * Fetch requests from the block device queue.
+ */
+static void __dasd_process_request_queue(struct dasd_block *block)
+{
+ struct request_queue *queue;
+ struct request *req;
+ struct dasd_ccw_req *cqr;
+ struct dasd_device *basedev;
+ unsigned long flags;
+ queue = block->request_queue;
+ basedev = block->base;
+ /* No queue ? Then there is nothing to do. */
+ if (queue == NULL)
+ return;
+
+ /*
+ * We requeue request from the block device queue to the ccw
+ * queue only in two states. In state DASD_STATE_READY the
+ * partition detection is done and we need to requeue requests
+ * for that. State DASD_STATE_ONLINE is normal block device
+ * operation.
+ */
+ if (basedev->state < DASD_STATE_READY)
+ return;
+ /* Now we try to fetch requests from the request queue */
+ while (!blk_queue_plugged(queue) &&
+ elv_next_request(queue)) {
+
+ req = elv_next_request(queue);
+
+ if (basedev->features & DASD_FEATURE_READONLY &&
+ rq_data_dir(req) == WRITE) {
+ DBF_DEV_EVENT(DBF_ERR, basedev,
+ "Rejecting write request %p",
+ req);
+ blkdev_dequeue_request(req);
+ dasd_end_request(req, 0);
+ continue;
+ }
+ cqr = basedev->discipline->build_cp(basedev, block, req);
+ if (IS_ERR(cqr)) {
+ if (PTR_ERR(cqr) == -EBUSY)
+ break; /* normal end condition */
+ if (PTR_ERR(cqr) == -ENOMEM)
+ break; /* terminate request queue loop */
+ if (PTR_ERR(cqr) == -EAGAIN) {
+ /*
+ * The current request cannot be build right
+ * now, we have to try later. If this request
+ * is the head-of-queue we stop the device
+ * for 1/2 second.
+ */
+ 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);
+ dasd_block_set_timer(block, HZ/2);
+ break;
+ }
+ DBF_DEV_EVENT(DBF_ERR, basedev,
+ "CCW creation failed (rc=%ld) "
+ "on request %p",
+ PTR_ERR(cqr), req);
+ blkdev_dequeue_request(req);
+ dasd_end_request(req, 0);
+ continue;
+ }
+ /*
+ * Note: callback is set to dasd_return_cqr_cb in
+ * __dasd_block_start_head to cover erp requests as well
+ */
+ cqr->callback_data = (void *) req;
+ cqr->status = DASD_CQR_FILLED;
+ blkdev_dequeue_request(req);
+ list_add_tail(&cqr->blocklist, &block->ccw_queue);
+ dasd_profile_start(block, cqr, req);
+ }
+}
+
+static void __dasd_cleanup_cqr(struct dasd_ccw_req *cqr)
+{
+ struct request *req;
+ int status;
+
+ req = (struct request *) cqr->callback_data;
+ dasd_profile_end(cqr->block, cqr, req);
+ status = cqr->memdev->discipline->free_cp(cqr, req);
+ dasd_end_request(req, status);
+}
+
+/*
+ * Process ccw request queue.
+ */
+static void __dasd_process_block_ccw_queue(struct dasd_block *block,
+ struct list_head *final_queue)
+{
+ struct list_head *l, *n;
+ struct dasd_ccw_req *cqr;
+ dasd_erp_fn_t erp_fn;
+ unsigned long flags;
+ struct dasd_device *base = block->base;
+
+restart:
+ /* Process request with final status. */
+ list_for_each_safe(l, n, &block->ccw_queue) {
+ cqr = list_entry(l, struct dasd_ccw_req, blocklist);
+ if (cqr->status != DASD_CQR_DONE &&
+ cqr->status != DASD_CQR_FAILED &&
+ cqr->status != DASD_CQR_NEED_ERP &&
+ cqr->status != DASD_CQR_TERMINATED)
+ continue;
+
+ if (cqr->status == DASD_CQR_TERMINATED) {
+ base->discipline->handle_terminated_request(cqr);
+ goto restart;
+ }
+
+ /* Process requests that may be recovered */
+ if (cqr->status == DASD_CQR_NEED_ERP) {
+ if (cqr->irb.esw.esw0.erw.cons &&
+ test_bit(DASD_CQR_FLAGS_USE_ERP,
+ &cqr->flags)) {
+ erp_fn = base->discipline->erp_action(cqr);
+ erp_fn(cqr);
+ }
+ goto restart;
+ }
+
+ /* First of all call extended error reporting. */
+ if (dasd_eer_enabled(base) &&
+ cqr->status == DASD_CQR_FAILED) {
+ dasd_eer_write(base, cqr, DASD_EER_FATALERROR);
+
+ /* restart request */
+ cqr->status = DASD_CQR_FILLED;
+ cqr->retries = 255;
+ spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+ base->stopped |= DASD_STOPPED_QUIESCE;
+ spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
+ flags);
+ goto restart;
+ }
+
+ /* Process finished ERP request. */
+ if (cqr->refers) {
+ __dasd_block_process_erp(block, cqr);
+ goto restart;
+ }
+
+ /* Rechain finished requests to final queue */
+ cqr->endclk = get_clock();
+ list_move_tail(&cqr->blocklist, final_queue);
+ }
+}
+
+static void dasd_return_cqr_cb(struct dasd_ccw_req *cqr, void *data)
+{
+ dasd_schedule_block_bh(cqr->block);
+}
+
+static void __dasd_block_start_head(struct dasd_block *block)
+{
+ struct dasd_ccw_req *cqr;
+
+ if (list_empty(&block->ccw_queue))
+ return;
+ /* We allways begin with the first requests on the queue, as some
+ * of previously started requests have to be enqueued on a
+ * dasd_device again for error recovery.
+ */
+ list_for_each_entry(cqr, &block->ccw_queue, blocklist) {
+ if (cqr->status != DASD_CQR_FILLED)
+ continue;
+ /* Non-temporary stop condition will trigger fail fast */
+ if (block->base->stopped & ~DASD_STOPPED_PENDING &&
+ test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
+ (!dasd_eer_enabled(block->base))) {
+ cqr->status = DASD_CQR_FAILED;
+ dasd_schedule_block_bh(block);
+ continue;
+ }
+ /* Don't try to start requests if device is stopped */
+ if (block->base->stopped)
+ return;
+
+ /* just a fail safe check, should not happen */
+ if (!cqr->startdev)
+ cqr->startdev = block->base;
+
+ /* make sure that the requests we submit find their way back */
+ cqr->callback = dasd_return_cqr_cb;
+
+ dasd_add_request_tail(cqr);
+ }
+}
+
+/*
+ * Central dasd_block layer routine. Takes requests from the generic
+ * block layer request queue, creates ccw requests, enqueues them on
+ * a dasd_device and processes ccw requests that have been returned.
+ */
+static void dasd_block_tasklet(struct dasd_block *block)
+{
+ struct list_head final_queue;
+ struct list_head *l, *n;
+ struct dasd_ccw_req *cqr;
+
+ atomic_set(&block->tasklet_scheduled, 0);
+ INIT_LIST_HEAD(&final_queue);
+ spin_lock(&block->queue_lock);
+ /* Finish off requests on ccw queue */
+ __dasd_process_block_ccw_queue(block, &final_queue);
+ spin_unlock(&block->queue_lock);
+ /* Now call the callback function of requests with final status */
+ spin_lock_irq(&block->request_queue_lock);
+ list_for_each_safe(l, n, &final_queue) {
+ cqr = list_entry(l, struct dasd_ccw_req, blocklist);
+ list_del_init(&cqr->blocklist);
+ __dasd_cleanup_cqr(cqr);
+ }
+ spin_lock(&block->queue_lock);
+ /* Get new request from the block device request queue */
+ __dasd_process_request_queue(block);
+ /* Now check if the head of the ccw queue needs to be started. */
+ __dasd_block_start_head(block);
+ spin_unlock(&block->queue_lock);
+ spin_unlock_irq(&block->request_queue_lock);
+ dasd_put_device(block->base);
+}
+
+static void _dasd_wake_block_flush_cb(struct dasd_ccw_req *cqr, void *data)
+{
+ wake_up(&dasd_flush_wq);
+}
+
+/*
+ * Go through all request on the dasd_block request queue, cancel them
+ * on the respective dasd_device, and return them to the generic
+ * block layer.
+ */
+static int dasd_flush_block_queue(struct dasd_block *block)
+{
+ struct dasd_ccw_req *cqr, *n;
+ int rc, i;
+ struct list_head flush_queue;
+
+ INIT_LIST_HEAD(&flush_queue);
+ spin_lock_bh(&block->queue_lock);
+ rc = 0;
+restart:
+ list_for_each_entry_safe(cqr, n, &block->ccw_queue, blocklist) {
+ /* if this request currently owned by a dasd_device cancel it */
+ if (cqr->status >= DASD_CQR_QUEUED)
+ rc = dasd_cancel_req(cqr);
+ if (rc < 0)
+ break;
+ /* Rechain request (including erp chain) so it won't be
+ * touched by the dasd_block_tasklet anymore.
+ * Replace the callback so we notice when the request
+ * is returned from the dasd_device layer.
+ */
+ cqr->callback = _dasd_wake_block_flush_cb;
+ for (i = 0; cqr != NULL; cqr = cqr->refers, i++)
+ list_move_tail(&cqr->blocklist, &flush_queue);
+ if (i > 1)
+ /* moved more than one request - need to restart */
+ goto restart;
+ }
+ spin_unlock_bh(&block->queue_lock);
+ /* Now call the callback function of flushed requests */
+restart_cb:
+ list_for_each_entry_safe(cqr, n, &flush_queue, blocklist) {
+ wait_event(dasd_flush_wq, (cqr->status < DASD_CQR_QUEUED));
+ /* Process finished ERP request. */
+ if (cqr->refers) {
+ __dasd_block_process_erp(block, cqr);
+ /* restart list_for_xx loop since dasd_process_erp
+ * might remove multiple elements */
+ goto restart_cb;
+ }
+ /* call the callback function */
+ cqr->endclk = get_clock();
+ list_del_init(&cqr->blocklist);
+ __dasd_cleanup_cqr(cqr);
}
- spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
- dasd_schedule_bh(device);
return rc;
}
/*
- * SECTION: Block device operations (request queue, partitions, open, release).
+ * Schedules a call to dasd_tasklet over the device tasklet.
+ */
+void dasd_schedule_block_bh(struct dasd_block *block)
+{
+ /* Protect against rescheduling. */
+ if (atomic_cmpxchg(&block->tasklet_scheduled, 0, 1) != 0)
+ return;
+ /* life cycle of block is bound to it's base device */
+ dasd_get_device(block->base);
+ tasklet_hi_schedule(&block->tasklet);
+}
+
+
+/*
+ * SECTION: external block device operations
+ * (request queue handling, open, release, etc.)
*/
/*
* Dasd request queue function. Called from ll_rw_blk.c
*/
-static void
-do_dasd_request(struct request_queue * queue)
+static void do_dasd_request(struct request_queue *queue)
{
- struct dasd_device *device;
+ struct dasd_block *block;
- device = (struct dasd_device *) queue->queuedata;
- spin_lock(get_ccwdev_lock(device->cdev));
+ block = queue->queuedata;
+ spin_lock(&block->queue_lock);
/* Get new request from the block device request queue */
- __dasd_process_blk_queue(device);
+ __dasd_process_request_queue(block);
/* Now check if the head of the ccw queue needs to be started. */
- __dasd_start_head(device);
- spin_unlock(get_ccwdev_lock(device->cdev));
+ __dasd_block_start_head(block);
+ spin_unlock(&block->queue_lock);
}
/*
* Allocate and initialize request queue and default I/O scheduler.
*/
-static int
-dasd_alloc_queue(struct dasd_device * device)
+static int dasd_alloc_queue(struct dasd_block *block)
{
int rc;
- device->request_queue = blk_init_queue(do_dasd_request,
- &device->request_queue_lock);
- if (device->request_queue == NULL)
+ block->request_queue = blk_init_queue(do_dasd_request,
+ &block->request_queue_lock);
+ if (block->request_queue == NULL)
return -ENOMEM;
- device->request_queue->queuedata = device;
+ block->request_queue->queuedata = block;
- elevator_exit(device->request_queue->elevator);
- rc = elevator_init(device->request_queue, "deadline");
+ elevator_exit(block->request_queue->elevator);
+ rc = elevator_init(block->request_queue, "deadline");
if (rc) {
- blk_cleanup_queue(device->request_queue);
+ blk_cleanup_queue(block->request_queue);
return rc;
}
return 0;
@@ -1780,79 +1971,76 @@ dasd_alloc_queue(struct dasd_device * device)
/*
* Allocate and initialize request queue.
*/
-static void
-dasd_setup_queue(struct dasd_device * device)
+static void dasd_setup_queue(struct dasd_block *block)
{
int max;
- blk_queue_hardsect_size(device->request_queue, device->bp_block);
- max = device->discipline->max_blocks << device->s2b_shift;
- blk_queue_max_sectors(device->request_queue, max);
- blk_queue_max_phys_segments(device->request_queue, -1L);
- blk_queue_max_hw_segments(device->request_queue, -1L);
- blk_queue_max_segment_size(device->request_queue, -1L);
- blk_queue_segment_boundary(device->request_queue, -1L);
- blk_queue_ordered(device->request_queue, QUEUE_ORDERED_TAG, NULL);
+ blk_queue_hardsect_size(block->request_queue, block->bp_block);
+ max = block->base->discipline->max_blocks << block->s2b_shift;
+ blk_queue_max_sectors(block->request_queue, max);
+ blk_queue_max_phys_segments(block->request_queue, -1L);
+ blk_queue_max_hw_segments(block->request_queue, -1L);
+ blk_queue_max_segment_size(block->request_queue, -1L);
+ blk_queue_segment_boundary(block->request_queue, -1L);
+ blk_queue_ordered(block->request_queue, QUEUE_ORDERED_DRAIN, NULL);
}
/*
* Deactivate and free request queue.
*/
-static void
-dasd_free_queue(struct dasd_device * device)
+static void dasd_free_queue(struct dasd_block *block)
{
- if (device->request_queue) {
- blk_cleanup_queue(device->request_queue);
- device->request_queue = NULL;
+ if (block->request_queue) {
+ blk_cleanup_queue(block->request_queue);
+ block->request_queue = NULL;
}
}
/*
* Flush request on the request queue.
*/
-static void
-dasd_flush_request_queue(struct dasd_device * device)
+static void dasd_flush_request_queue(struct dasd_block *block)
{
struct request *req;
- if (!device->request_queue)
+ if (!block->request_queue)
return;
- spin_lock_irq(&device->request_queue_lock);
- while ((req = elv_next_request(device->request_queue))) {
+ spin_lock_irq(&block->request_queue_lock);
+ while ((req = elv_next_request(block->request_queue))) {
blkdev_dequeue_request(req);
dasd_end_request(req, 0);
}
- spin_unlock_irq(&device->request_queue_lock);
+ spin_unlock_irq(&block->request_queue_lock);
}
-static int
-dasd_open(struct inode *inp, struct file *filp)
+static int dasd_open(struct inode *inp, struct file *filp)
{
struct gendisk *disk = inp->i_bdev->bd_disk;
- struct dasd_device *device = disk->private_data;
+ struct dasd_block *block = disk->private_data;
+ struct dasd_device *base = block->base;
int rc;
- atomic_inc(&device->open_count);
- if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
+ atomic_inc(&block->open_count);
+ if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) {
rc = -ENODEV;
goto unlock;
}
- if (!try_module_get(device->discipline->owner)) {
+ if (!try_module_get(base->discipline->owner)) {
rc = -EINVAL;
goto unlock;
}
if (dasd_probeonly) {
- DEV_MESSAGE(KERN_INFO, device, "%s",
+ DEV_MESSAGE(KERN_INFO, base, "%s",
"No access to device due to probeonly mode");
rc = -EPERM;
goto out;
}
- if (device->state <= DASD_STATE_BASIC) {
- DBF_DEV_EVENT(DBF_ERR, device, " %s",
+ if (base->state <= DASD_STATE_BASIC) {
+ DBF_DEV_EVENT(DBF_ERR, base, " %s",
" Cannot open unrecognized device");
rc = -ENODEV;
goto out;
@@ -1861,41 +2049,41 @@ dasd_open(struct inode *inp, struct file *filp)
return 0;
out:
- module_put(device->discipline->owner);
+ module_put(base->discipline->owner);
unlock:
- atomic_dec(&device->open_count);
+ atomic_dec(&block->open_count);
return rc;
}
-static int
-dasd_release(struct inode *inp, struct file *filp)
+static int dasd_release(struct inode *inp, struct file *filp)
{
struct gendisk *disk = inp->i_bdev->bd_disk;
- struct dasd_device *device = disk->private_data;
+ struct dasd_block *block = disk->private_data;
- atomic_dec(&device->open_count);
- module_put(device->discipline->owner);
+ atomic_dec(&block->open_count);
+ module_put(block->base->discipline->owner);
return 0;
}
/*
* Return disk geometry.
*/
-static int
-dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
+static int dasd_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
- struct dasd_device *device;
+ struct dasd_block *block;
+ struct dasd_device *base;
- device = bdev->bd_disk->private_data;
- if (!device)
+ block = bdev->bd_disk->private_data;
+ base = block->base;
+ if (!block)
return -ENODEV;
- if (!device->discipline ||
- !device->discipline->fill_geometry)
+ if (!base->discipline ||
+ !base->discipline->fill_geometry)
return -EINVAL;
- device->discipline->fill_geometry(device, geo);
- geo->start = get_start_sect(bdev) >> device->s2b_shift;
+ base->discipline->fill_geometry(block, geo);
+ geo->start = get_start_sect(bdev) >> block->s2b_shift;
return 0;
}
@@ -1909,6 +2097,9 @@ dasd_device_operations = {
.getgeo = dasd_getgeo,
};
+/*******************************************************************************
+ * end of block device operations
+ */
static void
dasd_exit(void)
@@ -1937,9 +2128,8 @@ dasd_exit(void)
* Initial attempt at a probe function. this can be simplified once
* the other detection code is gone.
*/
-int
-dasd_generic_probe (struct ccw_device *cdev,
- struct dasd_discipline *discipline)
+int dasd_generic_probe(struct ccw_device *cdev,
+ struct dasd_discipline *discipline)
{
int ret;
@@ -1969,19 +2159,20 @@ dasd_generic_probe (struct ccw_device *cdev,
ret = ccw_device_set_online(cdev);
if (ret)
printk(KERN_WARNING
- "dasd_generic_probe: could not initially online "
- "ccw-device %s\n", cdev->dev.bus_id);
- return ret;
+ "dasd_generic_probe: could not initially "
+ "online ccw-device %s; return code: %d\n",
+ cdev->dev.bus_id, ret);
+ return 0;
}
/*
* This will one day be called from a global not_oper handler.
* It is also used by driver_unregister during module unload.
*/
-void
-dasd_generic_remove (struct ccw_device *cdev)
+void dasd_generic_remove(struct ccw_device *cdev)
{
struct dasd_device *device;
+ struct dasd_block *block;
cdev->handler = NULL;
@@ -2001,7 +2192,15 @@ dasd_generic_remove (struct ccw_device *cdev)
*/
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
+ block = device->block;
+ device->block = NULL;
dasd_delete_device(device);
+ /*
+ * life cycle of block is bound to device, so delete it after
+ * device was safely removed
+ */
+ if (block)
+ dasd_free_block(block);
}
/*
@@ -2009,10 +2208,8 @@ dasd_generic_remove (struct ccw_device *cdev)
* the device is detected for the first time and is supposed to be used
* or the user has started activation through sysfs.
*/
-int
-dasd_generic_set_online (struct ccw_device *cdev,
- struct dasd_discipline *base_discipline)
-
+int dasd_generic_set_online(struct ccw_device *cdev,
+ struct dasd_discipline *base_discipline)
{
struct dasd_discipline *discipline;
struct dasd_device *device;
@@ -2048,6 +2245,7 @@ dasd_generic_set_online (struct ccw_device *cdev,
device->base_discipline = base_discipline;
device->discipline = discipline;
+ /* check_device will allocate block device if necessary */
rc = discipline->check_device(device);
if (rc) {
printk (KERN_WARNING
@@ -2067,6 +2265,8 @@ dasd_generic_set_online (struct ccw_device *cdev,
cdev->dev.bus_id);
rc = -ENODEV;
dasd_set_target_state(device, DASD_STATE_NEW);
+ if (device->block)
+ dasd_free_block(device->block);
dasd_delete_device(device);
} else
pr_debug("dasd_generic device %s found\n",
@@ -2081,10 +2281,10 @@ dasd_generic_set_online (struct ccw_device *cdev,
return rc;
}
-int
-dasd_generic_set_offline (struct ccw_device *cdev)
+int dasd_generic_set_offline(struct ccw_device *cdev)
{
struct dasd_device *device;
+ struct dasd_block *block;
int max_count, open_count;
device = dasd_device_from_cdev(cdev);
@@ -2101,30 +2301,39 @@ dasd_generic_set_offline (struct ccw_device *cdev)
* the blkdev_get in dasd_scan_partitions. We are only interested
* in the other openers.
*/
- max_count = device->bdev ? 0 : -1;
- open_count = (int) atomic_read(&device->open_count);
- if (open_count > max_count) {
- if (open_count > 0)
- printk (KERN_WARNING "Can't offline dasd device with "
- "open count = %i.\n",
- open_count);
- else
- printk (KERN_WARNING "%s",
- "Can't offline dasd device due to internal "
- "use\n");
- clear_bit(DASD_FLAG_OFFLINE, &device->flags);
- dasd_put_device(device);
- return -EBUSY;
+ if (device->block) {
+ struct dasd_block *block = device->block;
+ max_count = block->bdev ? 0 : -1;
+ open_count = (int) atomic_read(&block->open_count);
+ if (open_count > max_count) {
+ if (open_count > 0)
+ printk(KERN_WARNING "Can't offline dasd "
+ "device with open count = %i.\n",
+ open_count);
+ else
+ printk(KERN_WARNING "%s",
+ "Can't offline dasd device due "
+ "to internal use\n");
+ clear_bit(DASD_FLAG_OFFLINE, &device->flags);
+ dasd_put_device(device);
+ return -EBUSY;
+ }
}
dasd_set_target_state(device, DASD_STATE_NEW);
/* dasd_delete_device destroys the device reference. */
+ block = device->block;
+ device->block = NULL;
dasd_delete_device(device);
-
+ /*
+ * life cycle of block is bound to device, so delete it after
+ * device was safely removed
+ */
+ if (block)
+ dasd_free_block(block);
return 0;
}
-int
-dasd_generic_notify(struct ccw_device *cdev, int event)
+int dasd_generic_notify(struct ccw_device *cdev, int event)
{
struct dasd_device *device;
struct dasd_ccw_req *cqr;
@@ -2145,27 +2354,22 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
if (device->state < DASD_STATE_BASIC)
break;
/* Device is active. We want to keep it. */
- if (test_bit(DASD_FLAG_DSC_ERROR, &device->flags)) {
- list_for_each_entry(cqr, &device->ccw_queue, list)
- if (cqr->status == DASD_CQR_IN_IO)
- cqr->status = DASD_CQR_FAILED;
- device->stopped |= DASD_STOPPED_DC_EIO;
- } else {
- list_for_each_entry(cqr, &device->ccw_queue, list)
- if (cqr->status == DASD_CQR_IN_IO) {
- cqr->status = DASD_CQR_QUEUED;
- cqr->retries++;
- }
- device->stopped |= DASD_STOPPED_DC_WAIT;
- dasd_set_timer(device, 0);
- }
- dasd_schedule_bh(device);
+ list_for_each_entry(cqr, &device->ccw_queue, devlist)
+ if (cqr->status == DASD_CQR_IN_IO) {
+ cqr->status = DASD_CQR_QUEUED;
+ cqr->retries++;
+ }
+ device->stopped |= 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_STOPPED_DC_EIO);
- dasd_schedule_bh(device);
+ device->stopped &= ~DASD_STOPPED_DC_WAIT;
+ dasd_schedule_device_bh(device);
+ if (device->block)
+ dasd_schedule_block_bh(device->block);
ret = 1;
break;
}
@@ -2195,7 +2399,8 @@ static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
ccw->cda = (__u32)(addr_t)rdc_buffer;
ccw->count = rdc_buffer_size;
- cqr->device = device;
+ cqr->startdev = device;
+ cqr->memdev = device;
cqr->expires = 10*HZ;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
cqr->retries = 2;
@@ -2217,13 +2422,12 @@ int dasd_generic_read_dev_chars(struct dasd_device *device, char *magic,
return PTR_ERR(cqr);
ret = dasd_sleep_on(cqr);
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return ret;
}
EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars);
-static int __init
-dasd_init(void)
+static int __init dasd_init(void)
{
int rc;
@@ -2231,7 +2435,7 @@ dasd_init(void)
init_waitqueue_head(&dasd_flush_wq);
/* register 'common' DASD debug area, used for all DBF_XXX calls */
- dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof (long));
+ dasd_debug_area = debug_register("dasd", 1, 1, 8 * sizeof(long));
if (dasd_debug_area == NULL) {
rc = -ENOMEM;
goto failed;
@@ -2277,15 +2481,18 @@ EXPORT_SYMBOL(dasd_diag_discipline_pointer);
EXPORT_SYMBOL(dasd_add_request_head);
EXPORT_SYMBOL(dasd_add_request_tail);
EXPORT_SYMBOL(dasd_cancel_req);
-EXPORT_SYMBOL(dasd_clear_timer);
+EXPORT_SYMBOL(dasd_device_clear_timer);
+EXPORT_SYMBOL(dasd_block_clear_timer);
EXPORT_SYMBOL(dasd_enable_device);
EXPORT_SYMBOL(dasd_int_handler);
EXPORT_SYMBOL(dasd_kfree_request);
EXPORT_SYMBOL(dasd_kick_device);
EXPORT_SYMBOL(dasd_kmalloc_request);
-EXPORT_SYMBOL(dasd_schedule_bh);
+EXPORT_SYMBOL(dasd_schedule_device_bh);
+EXPORT_SYMBOL(dasd_schedule_block_bh);
EXPORT_SYMBOL(dasd_set_target_state);
-EXPORT_SYMBOL(dasd_set_timer);
+EXPORT_SYMBOL(dasd_device_set_timer);
+EXPORT_SYMBOL(dasd_block_set_timer);
EXPORT_SYMBOL(dasd_sfree_request);
EXPORT_SYMBOL(dasd_sleep_on);
EXPORT_SYMBOL(dasd_sleep_on_immediatly);
@@ -2299,4 +2506,7 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove);
EXPORT_SYMBOL_GPL(dasd_generic_notify);
EXPORT_SYMBOL_GPL(dasd_generic_set_online);
EXPORT_SYMBOL_GPL(dasd_generic_set_offline);
-
+EXPORT_SYMBOL_GPL(dasd_generic_handle_state_change);
+EXPORT_SYMBOL_GPL(dasd_flush_device_queue);
+EXPORT_SYMBOL_GPL(dasd_alloc_block);
+EXPORT_SYMBOL_GPL(dasd_free_block);
diff --git a/drivers/s390/block/dasd_3370_erp.c b/drivers/s390/block/dasd_3370_erp.c
deleted file mode 100644
index 1ddab8991d9..00000000000
--- a/drivers/s390/block/dasd_3370_erp.c
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * File...........: linux/drivers/s390/block/dasd_3370_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(3370)"
-
-#include "dasd_int.h"
-
-
-/*
- * DASD_3370_ERP_EXAMINE
- *
- * DESCRIPTION
- * Checks only for fatal/no/recover error.
- * A detailed examination of the sense data is done later outside
- * the interrupt handler.
- *
- * The logic is based on the 'IBM 3880 Storage Control Reference' manual
- * 'Chapter 7. 3370 Sense Data'.
- *
- * RETURN VALUES
- * dasd_era_none no error
- * dasd_era_fatal for all fatal (unrecoverable errors)
- * dasd_era_recover for all others.
- */
-dasd_era_t
-dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
- char *sense = irb->ecw;
-
- /* check for successful execution first */
- if (irb->scsw.cstat == 0x00 &&
- irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
- return dasd_era_none;
- if (sense[0] & 0x80) { /* CMD reject */
- return dasd_era_fatal;
- }
- if (sense[0] & 0x40) { /* Drive offline */
- return dasd_era_recover;
- }
- if (sense[0] & 0x20) { /* Bus out parity */
- return dasd_era_recover;
- }
- if (sense[0] & 0x10) { /* equipment check */
- if (sense[1] & 0x80) {
- return dasd_era_fatal;
- }
- return dasd_era_recover;
- }
- if (sense[0] & 0x08) { /* data check */
- if (sense[1] & 0x80) {
- return dasd_era_fatal;
- }
- return dasd_era_recover;
- }
- if (sense[0] & 0x04) { /* overrun */
- if (sense[1] & 0x80) {
- return dasd_era_fatal;
- }
- return dasd_era_recover;
- }
- if (sense[1] & 0x40) { /* invalid blocksize */
- return dasd_era_fatal;
- }
- if (sense[1] & 0x04) { /* file protected */
- return dasd_era_recover;
- }
- if (sense[1] & 0x01) { /* operation incomplete */
- return dasd_era_recover;
- }
- if (sense[2] & 0x80) { /* check data erroor */
- return dasd_era_recover;
- }
- if (sense[2] & 0x10) { /* Env. data present */
- return dasd_era_recover;
- }
- /* examine the 24 byte sense data */
- return dasd_era_recover;
-
-} /* END dasd_3370_erp_examine */
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 5b7385e430e..c361ab69ec0 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -26,158 +26,6 @@ struct DCTL_data {
/*
*****************************************************************************
- * SECTION ERP EXAMINATION
- *****************************************************************************
- */
-
-/*
- * DASD_3990_ERP_EXAMINE_24
- *
- * DESCRIPTION
- * Checks only for fatal (unrecoverable) error.
- * A detailed examination of the sense data is done later outside
- * the interrupt handler.
- *
- * Each bit configuration leading to an action code 2 (Exit with
- * programming error or unusual condition indication)
- * are handled as fatal errors.
- *
- * All other configurations are handled as recoverable errors.
- *
- * RETURN VALUES
- * dasd_era_fatal for all fatal (unrecoverable errors)
- * dasd_era_recover for all others.
- */
-static dasd_era_t
-dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense)
-{
-
- struct dasd_device *device = cqr->device;
-
- /* check for 'Command Reject' */
- if ((sense[0] & SNS0_CMD_REJECT) &&
- (!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
-
- DEV_MESSAGE(KERN_ERR, device, "%s",
- "EXAMINE 24: Command Reject detected - "
- "fatal error");
-
- return dasd_era_fatal;
- }
-
- /* check for 'Invalid Track Format' */
- if ((sense[1] & SNS1_INV_TRACK_FORMAT) &&
- (!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
-
- DEV_MESSAGE(KERN_ERR, device, "%s",
- "EXAMINE 24: Invalid Track Format detected "
- "- fatal error");
-
- return dasd_era_fatal;
- }
-
- /* check for 'No Record Found' */
- if (sense[1] & SNS1_NO_REC_FOUND) {
-
- /* FIXME: fatal error ?!? */
- DEV_MESSAGE(KERN_ERR, device,
- "EXAMINE 24: No Record Found detected %s",
- device->state <= DASD_STATE_BASIC ?
- " " : "- fatal error");
-
- return dasd_era_fatal;
- }
-
- /* return recoverable for all others */
- return dasd_era_recover;
-} /* END dasd_3990_erp_examine_24 */
-
-/*
- * DASD_3990_ERP_EXAMINE_32
- *
- * DESCRIPTION
- * Checks only for fatal/no/recoverable error.
- * A detailed examination of the sense data is done later outside
- * the interrupt handler.
- *
- * RETURN VALUES
- * dasd_era_none no error
- * dasd_era_fatal for all fatal (unrecoverable errors)
- * dasd_era_recover for recoverable others.
- */
-static dasd_era_t
-dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
-{
-
- struct dasd_device *device = cqr->device;
-
- switch (sense[25]) {
- case 0x00:
- return dasd_era_none;
-
- case 0x01:
- DEV_MESSAGE(KERN_ERR, device, "%s", "EXAMINE 32: fatal error");
-
- return dasd_era_fatal;
-
- default:
-
- return dasd_era_recover;
- }
-
-} /* end dasd_3990_erp_examine_32 */
-
-/*
- * DASD_3990_ERP_EXAMINE
- *
- * DESCRIPTION
- * Checks only for fatal/no/recover error.
- * A detailed examination of the sense data is done later outside
- * the interrupt handler.
- *
- * The logic is based on the 'IBM 3990 Storage Control Reference' manual
- * 'Chapter 7. Error Recovery Procedures'.
- *
- * RETURN VALUES
- * dasd_era_none no error
- * dasd_era_fatal for all fatal (unrecoverable errors)
- * dasd_era_recover for all others.
- */
-dasd_era_t
-dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-
- char *sense = irb->ecw;
- dasd_era_t era = dasd_era_recover;
- struct dasd_device *device = cqr->device;
-
- /* check for successful execution first */
- if (irb->scsw.cstat == 0x00 &&
- irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
- return dasd_era_none;
-
- /* distinguish between 24 and 32 byte sense data */
- if (sense[27] & DASD_SENSE_BIT_0) {
-
- era = dasd_3990_erp_examine_24(cqr, sense);
-
- } else {
-
- era = dasd_3990_erp_examine_32(cqr, sense);
-
- }
-
- /* log the erp chain if fatal error occurred */
- if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) {
- dasd_log_sense(cqr, irb);
- }
-
- return era;
-
-} /* END dasd_3990_erp_examine */
-
-/*
- *****************************************************************************
* SECTION ERP HANDLING
*****************************************************************************
*/
@@ -206,7 +54,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
{
struct dasd_ccw_req *cqr = erp->refers;
- dasd_free_erp_request(erp, erp->device);
+ dasd_free_erp_request(erp, erp->memdev);
cqr->status = final_status;
return cqr;
@@ -224,15 +72,17 @@ static void
dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
+ unsigned long flags;
DEV_MESSAGE(KERN_INFO, device,
"blocking request queue for %is", expires/HZ);
+ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped |= DASD_STOPPED_PENDING;
- erp->status = DASD_CQR_QUEUED;
-
- dasd_set_timer(device, expires);
+ spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+ erp->status = DASD_CQR_FILLED;
+ dasd_block_set_timer(device->block, expires);
}
/*
@@ -251,7 +101,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
/* first time set initial retry counter and erp_function */
/* and retry once without blocking queue */
@@ -292,11 +142,14 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
static void
dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
__u8 opm;
+ unsigned long flags;
/* try alternate valid path */
+ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
opm = ccw_device_get_path_mask(device->cdev);
+ spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
//FIXME: start with get_opm ?
if (erp->lpm == 0)
erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum);
@@ -309,9 +162,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
"try alternate lpm=%x (lpum=%x / opm=%x)",
erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm);
- /* reset status to queued to handle the request again... */
- if (erp->status > DASD_CQR_QUEUED)
- erp->status = DASD_CQR_QUEUED;
+ /* reset status to submit the request again... */
+ erp->status = DASD_CQR_FILLED;
erp->retries = 1;
} else {
DEV_MESSAGE(KERN_ERR, device,
@@ -320,8 +172,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
erp->irb.esw.esw0.sublog.lpum, opm);
/* post request with permanent error */
- if (erp->status > DASD_CQR_QUEUED)
- erp->status = DASD_CQR_FAILED;
+ erp->status = DASD_CQR_FAILED;
}
} /* end dasd_3990_erp_alternate_path */
@@ -344,14 +195,14 @@ static struct dasd_ccw_req *
dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
struct DCTL_data *DCTL_data;
struct ccw1 *ccw;
struct dasd_ccw_req *dctl_cqr;
dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1,
- sizeof (struct DCTL_data),
- erp->device);
+ sizeof(struct DCTL_data),
+ device);
if (IS_ERR(dctl_cqr)) {
DEV_MESSAGE(KERN_ERR, device, "%s",
"Unable to allocate DCTL-CQR");
@@ -365,13 +216,14 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
DCTL_data->modifier = modifier;
ccw = dctl_cqr->cpaddr;
- memset(ccw, 0, sizeof (struct ccw1));
+ memset(ccw, 0, sizeof(struct ccw1));
ccw->cmd_code = CCW_CMD_DCTL;
ccw->count = 4;
ccw->cda = (__u32)(addr_t) DCTL_data;
dctl_cqr->function = dasd_3990_erp_DCTL;
dctl_cqr->refers = erp;
- dctl_cqr->device = erp->device;
+ dctl_cqr->startdev = device;
+ dctl_cqr->memdev = device;
dctl_cqr->magic = erp->magic;
dctl_cqr->expires = 5 * 60 * HZ;
dctl_cqr->retries = 2;
@@ -435,7 +287,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
/* first time set initial retry counter and erp_function */
/* and retry once without waiting for state change pending */
@@ -472,7 +324,7 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
"redriving request immediately, "
"%d retries left",
erp->retries);
- erp->status = DASD_CQR_QUEUED;
+ erp->status = DASD_CQR_FILLED;
}
}
@@ -530,7 +382,7 @@ static void
dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
char msg_format = (sense[7] & 0xF0);
char msg_no = (sense[7] & 0x0F);
@@ -1157,7 +1009,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_com_rej;
@@ -1198,7 +1050,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_bus_out(struct dasd_ccw_req * erp)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
/* first time set initial retry counter and erp_function */
/* and retry once without blocking queue */
@@ -1237,7 +1089,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_equip_check;
@@ -1279,7 +1131,6 @@ dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense)
erp = dasd_3990_erp_action_5(erp);
}
-
return erp;
} /* end dasd_3990_erp_equip_check */
@@ -1299,7 +1150,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_data_check;
@@ -1358,7 +1209,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_overrun;
@@ -1387,7 +1238,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_inv_format;
@@ -1403,8 +1254,7 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense)
} else {
DEV_MESSAGE(KERN_ERR, device, "%s",
- "Invalid Track Format - Fatal error should have "
- "been handled within the interrupt handler");
+ "Invalid Track Format - Fatal error");
erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
}
@@ -1428,7 +1278,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense)
{
- struct dasd_device *device = default_erp->device;
+ struct dasd_device *device = default_erp->startdev;
DEV_MESSAGE(KERN_ERR, device, "%s",
"End-of-Cylinder - must never happen");
@@ -1453,7 +1303,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_env_data;
@@ -1463,11 +1313,9 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
/* don't retry on disabled interface */
if (sense[7] != 0x0F) {
-
erp = dasd_3990_erp_action_4(erp, sense);
} else {
-
- erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO);
+ erp->status = DASD_CQR_FILLED;
}
return erp;
@@ -1490,11 +1338,10 @@ static struct dasd_ccw_req *
dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense)
{
- struct dasd_device *device = default_erp->device;
+ struct dasd_device *device = default_erp->startdev;
DEV_MESSAGE(KERN_ERR, device, "%s",
- "No Record Found - Fatal error should "
- "have been handled within the interrupt handler");
+ "No Record Found - Fatal error ");
return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED);
@@ -1517,7 +1364,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected");
@@ -1526,6 +1373,43 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_file_prot */
/*
+ * DASD_3990_ERP_INSPECT_ALIAS
+ *
+ * DESCRIPTION
+ * Checks if the original request was started on an alias device.
+ * If yes, it modifies the original and the erp request so that
+ * the erp request can be started on a base device.
+ *
+ * PARAMETER
+ * erp pointer to the currently created default ERP
+ *
+ * RETURN VALUES
+ * erp pointer to the modified ERP, or NULL
+ */
+
+static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
+ struct dasd_ccw_req *erp)
+{
+ struct dasd_ccw_req *cqr = erp->refers;
+
+ if (cqr->block &&
+ (cqr->block->base != cqr->startdev)) {
+ if (cqr->startdev->features & DASD_FEATURE_ERPLOG) {
+ DEV_MESSAGE(KERN_ERR, cqr->startdev,
+ "ERP on alias device for request %p,"
+ " recover on base device %s", cqr,
+ cqr->block->base->cdev->dev.bus_id);
+ }
+ dasd_eckd_reset_ccw_to_base_io(cqr);
+ erp->startdev = cqr->block->base;
+ erp->function = dasd_3990_erp_inspect_alias;
+ return erp;
+ } else
+ return NULL;
+}
+
+
+/*
* DASD_3990_ERP_INSPECT_24
*
* DESCRIPTION
@@ -1623,7 +1507,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->retries = 256;
erp->function = dasd_3990_erp_action_10_32;
@@ -1657,13 +1541,14 @@ static struct dasd_ccw_req *
dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
{
- struct dasd_device *device = default_erp->device;
+ struct dasd_device *device = default_erp->startdev;
__u32 cpa = 0;
struct dasd_ccw_req *cqr;
struct dasd_ccw_req *erp;
struct DE_eckd_data *DE_data;
+ struct PFX_eckd_data *PFX_data;
char *LO_data; /* LO_eckd_data_t */
- struct ccw1 *ccw;
+ struct ccw1 *ccw, *oldccw;
DEV_MESSAGE(KERN_DEBUG, device, "%s",
"Write not finished because of unexpected condition");
@@ -1702,8 +1587,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* Build new ERP request including DE/LO */
erp = dasd_alloc_erp_request((char *) &cqr->magic,
2 + 1,/* DE/LO + TIC */
- sizeof (struct DE_eckd_data) +
- sizeof (struct LO_eckd_data), device);
+ sizeof(struct DE_eckd_data) +
+ sizeof(struct LO_eckd_data), device);
if (IS_ERR(erp)) {
DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate ERP");
@@ -1712,10 +1597,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* use original DE */
DE_data = erp->data;
- memcpy(DE_data, cqr->data, sizeof (struct DE_eckd_data));
+ oldccw = cqr->cpaddr;
+ if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) {
+ PFX_data = cqr->data;
+ memcpy(DE_data, &PFX_data->define_extend,
+ sizeof(struct DE_eckd_data));
+ } else
+ memcpy(DE_data, cqr->data, sizeof(struct DE_eckd_data));
/* create LO */
- LO_data = erp->data + sizeof (struct DE_eckd_data);
+ LO_data = erp->data + sizeof(struct DE_eckd_data);
if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) {
@@ -1748,7 +1639,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* create DE ccw */
ccw = erp->cpaddr;
- memset(ccw, 0, sizeof (struct ccw1));
+ memset(ccw, 0, sizeof(struct ccw1));
ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
ccw->flags = CCW_FLAG_CC;
ccw->count = 16;
@@ -1756,7 +1647,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* create LO ccw */
ccw++;
- memset(ccw, 0, sizeof (struct ccw1));
+ memset(ccw, 0, sizeof(struct ccw1));
ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
ccw->flags = CCW_FLAG_CC;
ccw->count = 16;
@@ -1770,7 +1661,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
/* fill erp related fields */
erp->function = dasd_3990_erp_action_1B_32;
erp->refers = default_erp->refers;
- erp->device = device;
+ erp->startdev = device;
+ erp->memdev = device;
erp->magic = default_erp->magic;
erp->expires = 0;
erp->retries = 256;
@@ -1803,7 +1695,7 @@ static struct dasd_ccw_req *
dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
{
- struct dasd_device *device = previous_erp->device;
+ struct dasd_device *device = previous_erp->startdev;
__u32 cpa = 0;
struct dasd_ccw_req *cqr;
struct dasd_ccw_req *erp;
@@ -1827,7 +1719,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
DEV_MESSAGE(KERN_DEBUG, device, "%s",
"Imprecise ending is set - just retry");
- previous_erp->status = DASD_CQR_QUEUED;
+ previous_erp->status = DASD_CQR_FILLED;
return previous_erp;
}
@@ -1850,7 +1742,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
erp = previous_erp;
/* update the LO with the new returned sense data */
- LO_data = erp->data + sizeof (struct DE_eckd_data);
+ LO_data = erp->data + sizeof(struct DE_eckd_data);
if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) {
@@ -1889,7 +1781,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
ccw++; /* addr of TIC ccw */
ccw->cda = cpa;
- erp->status = DASD_CQR_QUEUED;
+ erp->status = DASD_CQR_FILLED;
return erp;
@@ -1968,9 +1860,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
* try further actions. */
erp->lpm = 0;
-
- erp->status = DASD_CQR_ERROR;
-
+ erp->status = DASD_CQR_NEED_ERP;
}
}
@@ -2047,7 +1937,7 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense)
if ((sense[25] & DASD_SENSE_BIT_1) && (sense[26] & DASD_SENSE_BIT_2)) {
/* set to suspended duplex state then restart */
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
DEV_MESSAGE(KERN_ERR, device, "%s",
"Set device to suspended duplex state should be "
@@ -2081,28 +1971,26 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense)
{
if ((erp->function == dasd_3990_erp_compound_retry) &&
- (erp->status == DASD_CQR_ERROR)) {
+ (erp->status == DASD_CQR_NEED_ERP)) {
dasd_3990_erp_compound_path(erp, sense);
}
if ((erp->function == dasd_3990_erp_compound_path) &&
- (erp->status == DASD_CQR_ERROR)) {
+ (erp->status == DASD_CQR_NEED_ERP)) {
erp = dasd_3990_erp_compound_code(erp, sense);
}
if ((erp->function == dasd_3990_erp_compound_code) &&
- (erp->status == DASD_CQR_ERROR)) {
+ (erp->status == DASD_CQR_NEED_ERP)) {
dasd_3990_erp_compound_config(erp, sense);
}
/* if no compound action ERP specified, the request failed */
- if (erp->status == DASD_CQR_ERROR) {
-
+ if (erp->status == DASD_CQR_NEED_ERP)
erp->status = DASD_CQR_FAILED;
- }
return erp;
@@ -2127,7 +2015,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
erp->function = dasd_3990_erp_inspect_32;
@@ -2149,8 +2037,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
case 0x01: /* fatal error */
DEV_MESSAGE(KERN_ERR, device, "%s",
- "Fatal error should have been "
- "handled within the interrupt handler");
+ "Retry not recommended - Fatal error");
erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
break;
@@ -2253,6 +2140,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
/* already set up new ERP ! */
char *sense = erp->refers->irb.ecw;
+ /* if this problem occured on an alias retry on base */
+ erp_new = dasd_3990_erp_inspect_alias(erp);
+ if (erp_new)
+ return erp_new;
+
/* distinguish between 24 and 32 byte sense data */
if (sense[27] & DASD_SENSE_BIT_0) {
@@ -2287,13 +2179,13 @@ static struct dasd_ccw_req *
dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
{
- struct dasd_device *device = cqr->device;
+ struct dasd_device *device = cqr->startdev;
struct ccw1 *ccw;
/* allocate additional request block */
struct dasd_ccw_req *erp;
- erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, cqr->device);
+ erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device);
if (IS_ERR(erp)) {
if (cqr->retries <= 0) {
DEV_MESSAGE(KERN_ERR, device, "%s",
@@ -2305,7 +2197,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
"Unable to allocate ERP request "
"(%i retries left)",
cqr->retries);
- dasd_set_timer(device, (HZ << 3));
+ dasd_block_set_timer(device->block, (HZ << 3));
}
return cqr;
}
@@ -2319,7 +2211,9 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
ccw->cda = (long)(cqr->cpaddr);
erp->function = dasd_3990_erp_add_erp;
erp->refers = cqr;
- erp->device = cqr->device;
+ erp->startdev = device;
+ erp->memdev = device;
+ erp->block = cqr->block;
erp->magic = cqr->magic;
erp->expires = 0;
erp->retries = 256;
@@ -2466,7 +2360,7 @@ static struct dasd_ccw_req *
dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
{
- struct dasd_device *device = erp->device;
+ struct dasd_device *device = erp->startdev;
char *sense = erp->irb.ecw;
/* check for 24 byte sense ERP */
@@ -2557,7 +2451,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
struct dasd_ccw_req *erp)
{
- struct dasd_device *device = erp_head->device;
+ struct dasd_device *device = erp_head->startdev;
struct dasd_ccw_req *erp_done = erp_head; /* finished req */
struct dasd_ccw_req *erp_free = NULL; /* req to be freed */
@@ -2569,13 +2463,13 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
"original request was lost\n");
/* remove the request from the device queue */
- list_del(&erp_done->list);
+ list_del(&erp_done->blocklist);
erp_free = erp_done;
erp_done = erp_done->refers;
/* free the finished erp request */
- dasd_free_erp_request(erp_free, erp_free->device);
+ dasd_free_erp_request(erp_free, erp_free->memdev);
} /* end while */
@@ -2603,7 +2497,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
erp->retries, erp);
/* handle the request again... */
- erp->status = DASD_CQR_QUEUED;
+ erp->status = DASD_CQR_FILLED;
}
} else {
@@ -2620,7 +2514,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
* DASD_3990_ERP_ACTION
*
* DESCRIPTION
- * controll routine for 3990 erp actions.
+ * control routine for 3990 erp actions.
* Has to be called with the queue lock (namely the s390_irq_lock) acquired.
*
* PARAMETER
@@ -2636,9 +2530,8 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
struct dasd_ccw_req *
dasd_3990_erp_action(struct dasd_ccw_req * cqr)
{
-
struct dasd_ccw_req *erp = NULL;
- struct dasd_device *device = cqr->device;
+ struct dasd_device *device = cqr->startdev;
struct dasd_ccw_req *temp_erp = NULL;
if (device->features & DASD_FEATURE_ERPLOG) {
@@ -2704,10 +2597,11 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
}
}
- /* enqueue added ERP request */
- if (erp->status == DASD_CQR_FILLED) {
- erp->status = DASD_CQR_QUEUED;
- list_add(&erp->list, &device->ccw_queue);
+ /* enqueue ERP request if it's a new one */
+ if (list_empty(&erp->blocklist)) {
+ cqr->status = DASD_CQR_IN_ERP;
+ /* add erp request before the cqr */
+ list_add_tail(&erp->blocklist, &cqr->blocklist);
}
return erp;
diff --git a/drivers/s390/block/dasd_9336_erp.c b/drivers/s390/block/dasd_9336_erp.c
deleted file mode 100644
index 6e082688475..00000000000
--- a/drivers/s390/block/dasd_9336_erp.c
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * File...........: linux/drivers/s390/block/dasd_9336_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(9336)"
-
-#include "dasd_int.h"
-
-
-/*
- * DASD_9336_ERP_EXAMINE
- *
- * DESCRIPTION
- * Checks only for fatal/no/recover error.
- * A detailed examination of the sense data is done later outside
- * the interrupt handler.
- *
- * The logic is based on the 'IBM 3880 Storage Control Reference' manual
- * 'Chapter 7. 9336 Sense Data'.
- *
- * RETURN VALUES
- * dasd_era_none no error
- * dasd_era_fatal for all fatal (unrecoverable errors)
- * dasd_era_recover for all others.
- */
-dasd_era_t
-dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
- /* check for successful execution first */
- if (irb->scsw.cstat == 0x00 &&
- irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
- return dasd_era_none;
-
- /* examine the 24 byte sense data */
- return dasd_era_recover;
-
-} /* END dasd_9336_erp_examine */
diff --git a/drivers/s390/block/dasd_9343_erp.c b/drivers/s390/block/dasd_9343_erp.c
deleted file mode 100644
index ddecb9808ed..00000000000
--- a/drivers/s390/block/dasd_9343_erp.c
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * File...........: linux/drivers/s390/block/dasd_9345_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(9343)"
-
-#include "dasd_int.h"
-
-dasd_era_t
-dasd_9343_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
- if (irb->scsw.cstat == 0x00 &&
- irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
- return dasd_era_none;
-
- return dasd_era_recover;
-}
diff --git a/drivers/s390/block/dasd_alias.c b/drivers/s390/block/dasd_alias.c
new file mode 100644
index 00000000000..3a40bee9d35
--- /dev/null
+++ b/drivers/s390/block/dasd_alias.c
@@ -0,0 +1,903 @@
+/*
+ * PAV alias management for the DASD ECKD discipline
+ *
+ * Copyright IBM Corporation, 2007
+ * Author(s): Stefan Weinhuber <wein@de.ibm.com>
+ */
+
+#include <linux/list.h>
+#include <asm/ebcdic.h>
+#include "dasd_int.h"
+#include "dasd_eckd.h"
+
+#ifdef PRINTK_HEADER
+#undef PRINTK_HEADER
+#endif /* PRINTK_HEADER */
+#define PRINTK_HEADER "dasd(eckd):"
+
+
+/*
+ * General concept of alias management:
+ * - PAV and DASD alias management is specific to the eckd discipline.
+ * - A device is connected to an lcu as long as the device exists.
+ * dasd_alias_make_device_known_to_lcu will be called wenn the
+ * device is checked by the eckd discipline and
+ * dasd_alias_disconnect_device_from_lcu will be called
+ * before the device is deleted.
+ * - The dasd_alias_add_device / dasd_alias_remove_device
+ * functions mark the point when a device is 'ready for service'.
+ * - A summary unit check is a rare occasion, but it is mandatory to
+ * support it. It requires some complex recovery actions before the
+ * devices can be used again (see dasd_alias_handle_summary_unit_check).
+ * - dasd_alias_get_start_dev will find an alias device that can be used
+ * instead of the base device and does some (very simple) load balancing.
+ * This is the function that gets called for each I/O, so when improving
+ * something, this function should get faster or better, the rest has just
+ * to be correct.
+ */
+
+
+static void summary_unit_check_handling_work(struct work_struct *);
+static void lcu_update_work(struct work_struct *);
+static int _schedule_lcu_update(struct alias_lcu *, struct dasd_device *);
+
+static struct alias_root aliastree = {
+ .serverlist = LIST_HEAD_INIT(aliastree.serverlist),
+ .lock = __SPIN_LOCK_UNLOCKED(aliastree.lock),
+};
+
+static struct alias_server *_find_server(struct dasd_uid *uid)
+{
+ struct alias_server *pos;
+ list_for_each_entry(pos, &aliastree.serverlist, server) {
+ if (!strncmp(pos->uid.vendor, uid->vendor,
+ sizeof(uid->vendor))
+ && !strncmp(pos->uid.serial, uid->serial,
+ sizeof(uid->serial)))
+ return pos;
+ };
+ return NULL;
+}
+
+static struct alias_lcu *_find_lcu(struct alias_server *server,
+ struct dasd_uid *uid)
+{
+ struct alias_lcu *pos;
+ list_for_each_entry(pos, &server->lculist, lcu) {
+ if (pos->uid.ssid == uid->ssid)
+ return pos;
+ };
+ return NULL;
+}
+
+static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
+ struct dasd_uid *uid)
+{
+ struct alias_pav_group *pos;
+ __u8 search_unit_addr;
+
+ /* for hyper pav there is only one group */
+ if (lcu->pav == HYPER_PAV) {
+ if (list_empty(&lcu->grouplist))
+ return NULL;
+ else
+ return list_first_entry(&lcu->grouplist,
+ struct alias_pav_group, group);
+ }
+
+ /* for base pav we have to find the group that matches the base */
+ if (uid->type == UA_BASE_DEVICE)
+ search_unit_addr = uid->real_unit_addr;
+ else
+ search_unit_addr = uid->base_unit_addr;
+ list_for_each_entry(pos, &lcu->grouplist, group) {
+ if (pos->uid.base_unit_addr == search_unit_addr)
+ return pos;
+ };
+ return NULL;
+}
+
+static struct alias_server *_allocate_server(struct dasd_uid *uid)
+{
+ struct alias_server *server;
+
+ server = kzalloc(sizeof(*server), GFP_KERNEL);
+ if (!server)
+ return ERR_PTR(-ENOMEM);
+ memcpy(server->uid.vendor, uid->vendor, sizeof(uid->vendor));
+ memcpy(server->uid.serial, uid->serial, sizeof(uid->serial));
+ INIT_LIST_HEAD(&server->server);
+ INIT_LIST_HEAD(&server->lculist);
+ return server;
+}
+
+static void _free_server(struct alias_server *server)
+{
+ kfree(server);
+}
+
+static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
+{
+ struct alias_lcu *lcu;
+
+ lcu = kzalloc(sizeof(*lcu), GFP_KERNEL);
+ if (!lcu)
+ return ERR_PTR(-ENOMEM);
+ lcu->uac = kzalloc(sizeof(*(lcu->uac)), GFP_KERNEL | GFP_DMA);
+ if (!lcu->uac)
+ goto out_err1;
+ lcu->rsu_cqr = kzalloc(sizeof(*lcu->rsu_cqr), GFP_KERNEL | GFP_DMA);
+ if (!lcu->rsu_cqr)
+ goto out_err2;
+ lcu->rsu_cqr->cpaddr = kzalloc(sizeof(struct ccw1),
+ GFP_KERNEL | GFP_DMA);
+ if (!lcu->rsu_cqr->cpaddr)
+ goto out_err3;
+ lcu->rsu_cqr->data = kzalloc(16, GFP_KERNEL | GFP_DMA);
+ if (!lcu->rsu_cqr->data)
+ goto out_err4;
+
+ memcpy(lcu->uid.vendor, uid->vendor, sizeof(uid->vendor));
+ memcpy(lcu->uid.serial, uid->serial, sizeof(uid->serial));
+ lcu->uid.ssid = uid->ssid;
+ lcu->pav = NO_PAV;
+ lcu->flags = NEED_UAC_UPDATE | UPDATE_PENDING;
+ INIT_LIST_HEAD(&lcu->lcu);
+ INIT_LIST_HEAD(&lcu->inactive_devices);
+ INIT_LIST_HEAD(&lcu->active_devices);
+ INIT_LIST_HEAD(&lcu->grouplist);
+ 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);
+ return lcu;
+
+out_err4:
+ kfree(lcu->rsu_cqr->cpaddr);
+out_err3:
+ kfree(lcu->rsu_cqr);
+out_err2:
+ kfree(lcu->uac);
+out_err1:
+ kfree(lcu);
+ return ERR_PTR(-ENOMEM);
+}
+
+static void _free_lcu(struct alias_lcu *lcu)
+{
+ kfree(lcu->rsu_cqr->data);
+ kfree(lcu->rsu_cqr->cpaddr);
+ kfree(lcu->rsu_cqr);
+ kfree(lcu->uac);
+ kfree(lcu);
+}
+
+/*
+ * This is the function that will allocate all the server and lcu data,
+ * so this function must be called first for a new device.
+ * If the return value is 1, the lcu was already known before, if it
+ * is 0, this is a new lcu.
+ * Negative return code indicates that something went wrong (e.g. -ENOMEM)
+ */
+int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ unsigned long flags;
+ struct alias_server *server, *newserver;
+ struct alias_lcu *lcu, *newlcu;
+ int is_lcu_known;
+ struct dasd_uid *uid;
+
+ private = (struct dasd_eckd_private *) device->private;
+ uid = &private->uid;
+ spin_lock_irqsave(&aliastree.lock, flags);
+ is_lcu_known = 1;
+ server = _find_server(uid);
+ if (!server) {
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+ newserver = _allocate_server(uid);
+ if (IS_ERR(newserver))
+ return PTR_ERR(newserver);
+ spin_lock_irqsave(&aliastree.lock, flags);
+ server = _find_server(uid);
+ if (!server) {
+ list_add(&newserver->server, &aliastree.serverlist);
+ server = newserver;
+ is_lcu_known = 0;
+ } else {
+ /* someone was faster */
+ _free_server(newserver);
+ }
+ }
+
+ lcu = _find_lcu(server, uid);
+ if (!lcu) {
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+ newlcu = _allocate_lcu(uid);
+ if (IS_ERR(newlcu))
+ return PTR_ERR(lcu);
+ spin_lock_irqsave(&aliastree.lock, flags);
+ lcu = _find_lcu(server, uid);
+ if (!lcu) {
+ list_add(&newlcu->lcu, &server->lculist);
+ lcu = newlcu;
+ is_lcu_known = 0;
+ } else {
+ /* someone was faster */
+ _free_lcu(newlcu);
+ }
+ is_lcu_known = 0;
+ }
+ spin_lock(&lcu->lock);
+ list_add(&device->alias_list, &lcu->inactive_devices);
+ private->lcu = lcu;
+ spin_unlock(&lcu->lock);
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+
+ return is_lcu_known;
+}
+
+/*
+ * 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
+ * any of the workers. If necessary cancel the work.
+ */
+void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ unsigned long flags;
+ struct alias_lcu *lcu;
+ struct alias_server *server;
+ int was_pending;
+
+ private = (struct dasd_eckd_private *) device->private;
+ lcu = private->lcu;
+ spin_lock_irqsave(&lcu->lock, flags);
+ list_del_init(&device->alias_list);
+ /* make sure that the workers don't use this device */
+ if (device == lcu->suc_data.device) {
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ cancel_work_sync(&lcu->suc_data.worker);
+ spin_lock_irqsave(&lcu->lock, flags);
+ if (device == lcu->suc_data.device)
+ lcu->suc_data.device = NULL;
+ }
+ was_pending = 0;
+ if (device == lcu->ruac_data.device) {
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ was_pending = 1;
+ cancel_delayed_work_sync(&lcu->ruac_data.dwork);
+ spin_lock_irqsave(&lcu->lock, flags);
+ if (device == lcu->ruac_data.device)
+ lcu->ruac_data.device = NULL;
+ }
+ private->lcu = NULL;
+ spin_unlock_irqrestore(&lcu->lock, flags);
+
+ spin_lock_irqsave(&aliastree.lock, flags);
+ spin_lock(&lcu->lock);
+ if (list_empty(&lcu->grouplist) &&
+ list_empty(&lcu->active_devices) &&
+ list_empty(&lcu->inactive_devices)) {
+ list_del(&lcu->lcu);
+ spin_unlock(&lcu->lock);
+ _free_lcu(lcu);
+ lcu = NULL;
+ } else {
+ if (was_pending)
+ _schedule_lcu_update(lcu, NULL);
+ spin_unlock(&lcu->lock);
+ }
+ server = _find_server(&private->uid);
+ if (server && list_empty(&server->lculist)) {
+ list_del(&server->server);
+ _free_server(server);
+ }
+ spin_unlock_irqrestore(&aliastree.lock, flags);
+}
+
+/*
+ * This function assumes that the unit address configuration stored
+ * in the lcu is up to date and will update the device uid before
+ * adding it to a pav group.
+ */
+static int _add_device_to_lcu(struct alias_lcu *lcu,
+ struct dasd_device *device)
+{
+
+ struct dasd_eckd_private *private;
+ struct alias_pav_group *group;
+ struct dasd_uid *uid;
+
+ private = (struct dasd_eckd_private *) device->private;
+ uid = &private->uid;
+ uid->type = lcu->uac->unit[uid->real_unit_addr].ua_type;
+ uid->base_unit_addr = lcu->uac->unit[uid->real_unit_addr].base_ua;
+ dasd_set_uid(device->cdev, &private->uid);
+
+ /* if we have no PAV anyway, we don't need to bother with PAV groups */
+ if (lcu->pav == NO_PAV) {
+ list_move(&device->alias_list, &lcu->active_devices);
+ return 0;
+ }
+
+ group = _find_group(lcu, uid);
+ if (!group) {
+ group = kzalloc(sizeof(*group), GFP_ATOMIC);
+ if (!group)
+ return -ENOMEM;
+ memcpy(group->uid.vendor, uid->vendor, sizeof(uid->vendor));
+ memcpy(group->uid.serial, uid->serial, sizeof(uid->serial));
+ group->uid.ssid = uid->ssid;
+ if (uid->type == UA_BASE_DEVICE)
+ group->uid.base_unit_addr = uid->real_unit_addr;
+ else
+ group->uid.base_unit_addr = uid->base_unit_addr;
+ INIT_LIST_HEAD(&group->group);
+ INIT_LIST_HEAD(&group->baselist);
+ INIT_LIST_HEAD(&group->aliaslist);
+ list_add(&group->group, &lcu->grouplist);
+ }
+ if (uid->type == UA_BASE_DEVICE)
+ list_move(&device->alias_list, &group->baselist);
+ else
+ list_move(&device->alias_list, &group->aliaslist);
+ private->pavgroup = group;
+ return 0;
+};
+
+static void _remove_device_from_lcu(struct alias_lcu *lcu,
+ struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ struct alias_pav_group *group;
+
+ private = (struct dasd_eckd_private *) device->private;
+ list_move(&device->alias_list, &lcu->inactive_devices);
+ group = private->pavgroup;
+ if (!group)
+ return;
+ private->pavgroup = NULL;
+ if (list_empty(&group->baselist) && list_empty(&group->aliaslist)) {
+ list_del(&group->group);
+ kfree(group);
+ return;
+ }
+ if (group->next == device)
+ group->next = NULL;
+};
+
+static int read_unit_address_configuration(struct dasd_device *device,
+ struct alias_lcu *lcu)
+{
+ struct dasd_psf_prssd_data *prssdp;
+ struct dasd_ccw_req *cqr;
+ struct ccw1 *ccw;
+ int rc;
+ unsigned long flags;
+
+ cqr = dasd_kmalloc_request("ECKD",
+ 1 /* PSF */ + 1 /* RSSD */ ,
+ (sizeof(struct dasd_psf_prssd_data)),
+ device);
+ if (IS_ERR(cqr))
+ return PTR_ERR(cqr);
+ cqr->startdev = device;
+ cqr->memdev = device;
+ clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+ cqr->retries = 10;
+ cqr->expires = 20 * HZ;
+
+ /* Prepare for Read Subsystem Data */
+ prssdp = (struct dasd_psf_prssd_data *) cqr->data;
+ memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
+ prssdp->order = PSF_ORDER_PRSSD;
+ prssdp->suborder = 0x0e; /* Read unit address configuration */
+ /* all other bytes of prssdp must be zero */
+
+ ccw = cqr->cpaddr;
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->count = sizeof(struct dasd_psf_prssd_data);
+ ccw->flags |= CCW_FLAG_CC;
+ ccw->cda = (__u32)(addr_t) prssdp;
+
+ /* Read Subsystem Data - feature codes */
+ memset(lcu->uac, 0, sizeof(*(lcu->uac)));
+
+ ccw++;
+ ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+ ccw->count = sizeof(*(lcu->uac));
+ ccw->cda = (__u32)(addr_t) lcu->uac;
+
+ cqr->buildclk = get_clock();
+ cqr->status = DASD_CQR_FILLED;
+
+ /* need to unset flag here to detect race with summary unit check */
+ spin_lock_irqsave(&lcu->lock, flags);
+ lcu->flags &= ~NEED_UAC_UPDATE;
+ spin_unlock_irqrestore(&lcu->lock, flags);
+
+ do {
+ rc = dasd_sleep_on(cqr);
+ } while (rc && (cqr->retries > 0));
+ if (rc) {
+ spin_lock_irqsave(&lcu->lock, flags);
+ lcu->flags |= NEED_UAC_UPDATE;
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ }
+ dasd_kfree_request(cqr, cqr->memdev);
+ return rc;
+}
+
+static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu)
+{
+ unsigned long flags;
+ struct alias_pav_group *pavgroup, *tempgroup;
+ struct dasd_device *device, *tempdev;
+ int i, rc;
+ struct dasd_eckd_private *private;
+
+ spin_lock_irqsave(&lcu->lock, flags);
+ list_for_each_entry_safe(pavgroup, tempgroup, &lcu->grouplist, group) {
+ list_for_each_entry_safe(device, tempdev, &pavgroup->baselist,
+ alias_list) {
+ list_move(&device->alias_list, &lcu->active_devices);
+ private = (struct dasd_eckd_private *) device->private;
+ private->pavgroup = NULL;
+ }
+ list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist,
+ alias_list) {
+ list_move(&device->alias_list, &lcu->active_devices);
+ private = (struct dasd_eckd_private *) device->private;
+ private->pavgroup = NULL;
+ }
+ list_del(&pavgroup->group);
+ kfree(pavgroup);
+ }
+ spin_unlock_irqrestore(&lcu->lock, flags);
+
+ rc = read_unit_address_configuration(refdev, lcu);
+ if (rc)
+ return rc;
+
+ spin_lock_irqsave(&lcu->lock, flags);
+ lcu->pav = NO_PAV;
+ for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) {
+ switch (lcu->uac->unit[i].ua_type) {
+ case UA_BASE_PAV_ALIAS:
+ lcu->pav = BASE_PAV;
+ break;
+ case UA_HYPER_PAV_ALIAS:
+ lcu->pav = HYPER_PAV;
+ break;
+ }
+ if (lcu->pav != NO_PAV)
+ break;
+ }
+
+ list_for_each_entry_safe(device, tempdev, &lcu->active_devices,
+ alias_list) {
+ _add_device_to_lcu(lcu, device);
+ }
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ return 0;
+}
+
+static void lcu_update_work(struct work_struct *work)
+{
+ struct alias_lcu *lcu;
+ struct read_uac_work_data *ruac_data;
+ struct dasd_device *device;
+ unsigned long flags;
+ int rc;
+
+ ruac_data = container_of(work, struct read_uac_work_data, dwork.work);
+ lcu = container_of(ruac_data, struct alias_lcu, ruac_data);
+ device = ruac_data->device;
+ rc = _lcu_update(device, lcu);
+ /*
+ * Need to check flags again, as there could have been another
+ * prepare_update or a new device a new device while we were still
+ * processing the data
+ */
+ spin_lock_irqsave(&lcu->lock, flags);
+ if (rc || (lcu->flags & NEED_UAC_UPDATE)) {
+ DEV_MESSAGE(KERN_WARNING, device, "could not update"
+ " alias data in lcu (rc = %d), retry later", rc);
+ schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
+ } else {
+ lcu->ruac_data.device = NULL;
+ lcu->flags &= ~UPDATE_PENDING;
+ }
+ spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+static int _schedule_lcu_update(struct alias_lcu *lcu,
+ struct dasd_device *device)
+{
+ struct dasd_device *usedev = NULL;
+ struct alias_pav_group *group;
+
+ lcu->flags |= NEED_UAC_UPDATE;
+ if (lcu->ruac_data.device) {
+ /* already scheduled or running */
+ return 0;
+ }
+ if (device && !list_empty(&device->alias_list))
+ usedev = device;
+
+ if (!usedev && !list_empty(&lcu->grouplist)) {
+ group = list_first_entry(&lcu->grouplist,
+ struct alias_pav_group, group);
+ if (!list_empty(&group->baselist))
+ usedev = list_first_entry(&group->baselist,
+ struct dasd_device,
+ alias_list);
+ else if (!list_empty(&group->aliaslist))
+ usedev = list_first_entry(&group->aliaslist,
+ struct dasd_device,
+ alias_list);
+ }
+ if (!usedev && !list_empty(&lcu->active_devices)) {
+ usedev = list_first_entry(&lcu->active_devices,
+ struct dasd_device, alias_list);
+ }
+ /*
+ * if we haven't found a proper device yet, give up for now, the next
+ * device that will be set active will trigger an lcu update
+ */
+ if (!usedev)
+ return -EINVAL;
+ lcu->ruac_data.device = usedev;
+ schedule_delayed_work(&lcu->ruac_data.dwork, 0);
+ return 0;
+}
+
+int dasd_alias_add_device(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ struct alias_lcu *lcu;
+ unsigned long flags;
+ int rc;
+
+ private = (struct dasd_eckd_private *) device->private;
+ lcu = private->lcu;
+ rc = 0;
+ spin_lock_irqsave(&lcu->lock, flags);
+ if (!(lcu->flags & UPDATE_PENDING)) {
+ rc = _add_device_to_lcu(lcu, device);
+ if (rc)
+ lcu->flags |= UPDATE_PENDING;
+ }
+ if (lcu->flags & UPDATE_PENDING) {
+ list_move(&device->alias_list, &lcu->active_devices);
+ _schedule_lcu_update(lcu, device);
+ }
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ return rc;
+}
+
+int dasd_alias_remove_device(struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ struct alias_lcu *lcu;
+ unsigned long flags;
+
+ private = (struct dasd_eckd_private *) device->private;
+ lcu = private->lcu;
+ spin_lock_irqsave(&lcu->lock, flags);
+ _remove_device_from_lcu(lcu, device);
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ return 0;
+}
+
+struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
+{
+
+ struct dasd_device *alias_device;
+ struct alias_pav_group *group;
+ struct alias_lcu *lcu;
+ struct dasd_eckd_private *private, *alias_priv;
+ unsigned long flags;
+
+ private = (struct dasd_eckd_private *) base_device->private;
+ group = private->pavgroup;
+ lcu = private->lcu;
+ if (!group || !lcu)
+ return NULL;
+ if (lcu->pav == NO_PAV ||
+ lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING))
+ return NULL;
+
+ spin_lock_irqsave(&lcu->lock, flags);
+ alias_device = group->next;
+ if (!alias_device) {
+ if (list_empty(&group->aliaslist)) {
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ return NULL;
+ } else {
+ alias_device = list_first_entry(&group->aliaslist,
+ struct dasd_device,
+ alias_list);
+ }
+ }
+ if (list_is_last(&alias_device->alias_list, &group->aliaslist))
+ group->next = list_first_entry(&group->aliaslist,
+ struct dasd_device, alias_list);
+ else
+ group->next = list_first_entry(&alias_device->alias_list,
+ struct dasd_device, alias_list);
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ alias_priv = (struct dasd_eckd_private *) alias_device->private;
+ if ((alias_priv->count < private->count) && !alias_device->stopped)
+ return alias_device;
+ else
+ return NULL;
+}
+
+/*
+ * Summary unit check handling depends on the way alias devices
+ * are handled so it is done here rather then in dasd_eckd.c
+ */
+static int reset_summary_unit_check(struct alias_lcu *lcu,
+ struct dasd_device *device,
+ char reason)
+{
+ struct dasd_ccw_req *cqr;
+ int rc = 0;
+
+ cqr = lcu->rsu_cqr;
+ strncpy((char *) &cqr->magic, "ECKD", 4);
+ ASCEBC((char *) &cqr->magic, 4);
+ cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RSCK;
+ cqr->cpaddr->flags = 0 ;
+ cqr->cpaddr->count = 16;
+ cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
+ ((char *)cqr->data)[0] = reason;
+
+ clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+ cqr->retries = 255; /* set retry counter to enable basic ERP */
+ cqr->startdev = device;
+ cqr->memdev = device;
+ cqr->block = NULL;
+ cqr->expires = 5 * HZ;
+ cqr->buildclk = get_clock();
+ cqr->status = DASD_CQR_FILLED;
+
+ rc = dasd_sleep_on_immediatly(cqr);
+ return rc;
+}
+
+static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu)
+{
+ struct alias_pav_group *pavgroup;
+ struct dasd_device *device;
+ struct dasd_eckd_private *private;
+
+ /* active and inactive list can contain alias as well as base devices */
+ list_for_each_entry(device, &lcu->active_devices, alias_list) {
+ private = (struct dasd_eckd_private *) device->private;
+ if (private->uid.type != UA_BASE_DEVICE)
+ continue;
+ dasd_schedule_block_bh(device->block);
+ dasd_schedule_device_bh(device);
+ }
+ list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
+ private = (struct dasd_eckd_private *) device->private;
+ if (private->uid.type != UA_BASE_DEVICE)
+ continue;
+ dasd_schedule_block_bh(device->block);
+ dasd_schedule_device_bh(device);
+ }
+ list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+ list_for_each_entry(device, &pavgroup->baselist, alias_list) {
+ dasd_schedule_block_bh(device->block);
+ dasd_schedule_device_bh(device);
+ }
+ }
+}
+
+static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu)
+{
+ struct alias_pav_group *pavgroup;
+ struct dasd_device *device, *temp;
+ struct dasd_eckd_private *private;
+ int rc;
+ unsigned long flags;
+ LIST_HEAD(active);
+
+ /*
+ * Problem here ist that dasd_flush_device_queue may wait
+ * for termination of a request to complete. We can't keep
+ * the lcu lock during that time, so we must assume that
+ * the lists may have changed.
+ * Idea: first gather all active alias devices in a separate list,
+ * then flush the first element of this list unlocked, and afterwards
+ * check if it is still on the list before moving it to the
+ * active_devices list.
+ */
+
+ spin_lock_irqsave(&lcu->lock, flags);
+ list_for_each_entry_safe(device, temp, &lcu->active_devices,
+ alias_list) {
+ private = (struct dasd_eckd_private *) device->private;
+ if (private->uid.type == UA_BASE_DEVICE)
+ continue;
+ list_move(&device->alias_list, &active);
+ }
+
+ list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+ list_splice_init(&pavgroup->aliaslist, &active);
+ }
+ while (!list_empty(&active)) {
+ device = list_first_entry(&active, struct dasd_device,
+ alias_list);
+ spin_unlock_irqrestore(&lcu->lock, flags);
+ rc = dasd_flush_device_queue(device);
+ spin_lock_irqsave(&lcu->lock, flags);
+ /*
+ * only move device around if it wasn't moved away while we
+ * were waiting for the flush
+ */
+ if (device == list_first_entry(&active,
+ struct dasd_device, alias_list))
+ list_move(&device->alias_list, &lcu->active_devices);
+ }
+ spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+/*
+ * This function is called in interrupt context, so the
+ * cdev lock for device is already locked!
+ */
+static void _stop_all_devices_on_lcu(struct alias_lcu *lcu,
+ struct dasd_device *device)
+{
+ struct alias_pav_group *pavgroup;
+ struct dasd_device *pos;
+
+ list_for_each_entry(pos, &lcu->active_devices, alias_list) {
+ if (pos != device)
+ spin_lock(get_ccwdev_lock(pos->cdev));
+ pos->stopped |= DASD_STOPPED_SU;
+ if (pos != device)
+ spin_unlock(get_ccwdev_lock(pos->cdev));
+ }
+ list_for_each_entry(pos, &lcu->inactive_devices, alias_list) {
+ if (pos != device)
+ spin_lock(get_ccwdev_lock(pos->cdev));
+ pos->stopped |= DASD_STOPPED_SU;
+ if (pos != device)
+ spin_unlock(get_ccwdev_lock(pos->cdev));
+ }
+ list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+ list_for_each_entry(pos, &pavgroup->baselist, alias_list) {
+ if (pos != device)
+ spin_lock(get_ccwdev_lock(pos->cdev));
+ pos->stopped |= DASD_STOPPED_SU;
+ if (pos != device)
+ spin_unlock(get_ccwdev_lock(pos->cdev));
+ }
+ list_for_each_entry(pos, &pavgroup->aliaslist, alias_list) {
+ if (pos != device)
+ spin_lock(get_ccwdev_lock(pos->cdev));
+ pos->stopped |= DASD_STOPPED_SU;
+ if (pos != device)
+ spin_unlock(get_ccwdev_lock(pos->cdev));
+ }
+ }
+}
+
+static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
+{
+ struct alias_pav_group *pavgroup;
+ struct dasd_device *device;
+ unsigned long flags;
+
+ list_for_each_entry(device, &lcu->active_devices, alias_list) {
+ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+ device->stopped &= ~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;
+ 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;
+ 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;
+ spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+ flags);
+ }
+ }
+}
+
+static void summary_unit_check_handling_work(struct work_struct *work)
+{
+ struct alias_lcu *lcu;
+ struct summary_unit_check_work_data *suc_data;
+ unsigned long flags;
+ struct dasd_device *device;
+
+ suc_data = container_of(work, struct summary_unit_check_work_data,
+ worker);
+ lcu = container_of(suc_data, struct alias_lcu, suc_data);
+ device = suc_data->device;
+
+ /* 1. flush alias devices */
+ flush_all_alias_devices_on_lcu(lcu);
+
+ /* 2. reset summary unit check */
+ spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+ device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING);
+ spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+ reset_summary_unit_check(lcu, device, suc_data->reason);
+
+ spin_lock_irqsave(&lcu->lock, flags);
+ _unstop_all_devices_on_lcu(lcu);
+ _restart_all_base_devices_on_lcu(lcu);
+ /* 3. read new alias configuration */
+ _schedule_lcu_update(lcu, device);
+ lcu->suc_data.device = NULL;
+ spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+/*
+ * note: this will be called from int handler context (cdev locked)
+ */
+void dasd_alias_handle_summary_unit_check(struct dasd_device *device,
+ struct irb *irb)
+{
+ struct alias_lcu *lcu;
+ char reason;
+ struct dasd_eckd_private *private;
+
+ private = (struct dasd_eckd_private *) device->private;
+
+ reason = irb->ecw[8];
+ DEV_MESSAGE(KERN_WARNING, device, "%s %x",
+ "eckd handle summary unit check: reason", reason);
+
+ lcu = private->lcu;
+ if (!lcu) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "device not ready to handle summary"
+ " unit check (no lcu structure)");
+ return;
+ }
+ spin_lock(&lcu->lock);
+ _stop_all_devices_on_lcu(lcu, device);
+ /* prepare for lcu_update */
+ private->lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING;
+ /* If this device is about to be removed just return and wait for
+ * the next interrupt on a different device
+ */
+ if (list_empty(&device->alias_list)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "device is in offline processing,"
+ " don't do summary unit check handling");
+ spin_unlock(&lcu->lock);
+ return;
+ }
+ if (lcu->suc_data.device) {
+ /* already scheduled or running */
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "previous instance of summary unit check worker"
+ " still pending");
+ spin_unlock(&lcu->lock);
+ return ;
+ }
+ lcu->suc_data.reason = reason;
+ lcu->suc_data.device = device;
+ spin_unlock(&lcu->lock);
+ schedule_work(&lcu->suc_data.worker);
+};
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 0c67258fb9e..f4fb4025734 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -49,22 +49,6 @@ struct dasd_devmap {
};
/*
- * dasd_server_ssid_map contains a globally unique storage server subsystem ID.
- * dasd_server_ssid_list contains the list of all subsystem IDs accessed by
- * the DASD device driver.
- */
-struct dasd_server_ssid_map {
- struct list_head list;
- struct system_id {
- char vendor[4];
- char serial[15];
- __u16 ssid;
- } sid;
-};
-
-static struct list_head dasd_server_ssid_list;
-
-/*
* Parameter parsing functions for dasd= parameter. The syntax is:
* <devno> : (0x)?[0-9a-fA-F]+
* <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
@@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
devmap->features &= ~DASD_FEATURE_READONLY;
if (devmap->device)
devmap->device->features = devmap->features;
- if (devmap->device && devmap->device->gdp)
- set_disk_ro(devmap->device->gdp, val);
+ if (devmap->device && devmap->device->block
+ && devmap->device->block->gdp)
+ set_disk_ro(devmap->device->block->gdp, val);
spin_unlock(&dasd_devmap_lock);
return count;
}
@@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
devmap = dasd_find_busid(dev->bus_id);
spin_lock(&dasd_devmap_lock);
- if (!IS_ERR(devmap))
- alias = devmap->uid.alias;
+ if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+ spin_unlock(&dasd_devmap_lock);
+ return sprintf(buf, "0\n");
+ }
+ if (devmap->uid.type == UA_BASE_PAV_ALIAS ||
+ devmap->uid.type == UA_HYPER_PAV_ALIAS)
+ alias = 1;
else
alias = 0;
spin_unlock(&dasd_devmap_lock);
-
return sprintf(buf, alias ? "1\n" : "0\n");
}
@@ -930,19 +919,36 @@ static ssize_t
dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dasd_devmap *devmap;
- char uid[UID_STRLEN];
+ char uid_string[UID_STRLEN];
+ char ua_string[3];
+ struct dasd_uid *uid;
devmap = dasd_find_busid(dev->bus_id);
spin_lock(&dasd_devmap_lock);
- if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
- snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x",
- devmap->uid.vendor, devmap->uid.serial,
- devmap->uid.ssid, devmap->uid.unit_addr);
- else
- uid[0] = 0;
+ if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+ spin_unlock(&dasd_devmap_lock);
+ return sprintf(buf, "\n");
+ }
+ uid = &devmap->uid;
+ switch (uid->type) {
+ case UA_BASE_DEVICE:
+ sprintf(ua_string, "%02x", uid->real_unit_addr);
+ break;
+ case UA_BASE_PAV_ALIAS:
+ sprintf(ua_string, "%02x", uid->base_unit_addr);
+ break;
+ case UA_HYPER_PAV_ALIAS:
+ sprintf(ua_string, "xx");
+ break;
+ default:
+ /* should not happen, treat like base device */
+ sprintf(ua_string, "%02x", uid->real_unit_addr);
+ break;
+ }
+ snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s",
+ uid->vendor, uid->serial, uid->ssid, ua_string);
spin_unlock(&dasd_devmap_lock);
-
- return snprintf(buf, PAGE_SIZE, "%s\n", uid);
+ return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
}
static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
@@ -1040,39 +1046,16 @@ int
dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
struct dasd_devmap *devmap;
- struct dasd_server_ssid_map *srv, *tmp;
devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap))
return PTR_ERR(devmap);
- /* generate entry for server_ssid_map */
- srv = (struct dasd_server_ssid_map *)
- kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL);
- if (!srv)
- return -ENOMEM;
- strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1);
- strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1);
- srv->sid.ssid = uid->ssid;
-
- /* server is already contained ? */
spin_lock(&dasd_devmap_lock);
devmap->uid = *uid;
- list_for_each_entry(tmp, &dasd_server_ssid_list, list) {
- if (!memcmp(&srv->sid, &tmp->sid,
- sizeof(struct system_id))) {
- kfree(srv);
- srv = NULL;
- break;
- }
- }
-
- /* add servermap to serverlist */
- if (srv)
- list_add(&srv->list, &dasd_server_ssid_list);
spin_unlock(&dasd_devmap_lock);
- return (srv ? 1 : 0);
+ return 0;
}
EXPORT_SYMBOL_GPL(dasd_set_uid);
@@ -1138,9 +1121,6 @@ dasd_devmap_init(void)
dasd_max_devindex = 0;
for (i = 0; i < 256; i++)
INIT_LIST_HEAD(&dasd_hashlists[i]);
-
- /* Initialize servermap structure. */
- INIT_LIST_HEAD(&dasd_server_ssid_list);
return 0;
}
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 571320ab9e1..d91df38ee4f 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device)
int rc;
mdsk_term_io(device);
- rc = mdsk_init_io(device, device->bp_block, 0, NULL);
+ rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
if (rc)
DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, "
"rc=%d", rc);
@@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
struct dasd_diag_req *dreq;
int rc;
- device = cqr->device;
+ device = cqr->startdev;
if (cqr->retries < 0) {
DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p "
"- no retry left)", cqr);
- cqr->status = DASD_CQR_FAILED;
+ cqr->status = DASD_CQR_ERROR;
return -EIO;
}
private = (struct dasd_diag_private *) device->private;
@@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
switch (rc) {
case 0: /* Synchronous I/O finished successfully */
cqr->stopclk = get_clock();
- cqr->status = DASD_CQR_DONE;
+ cqr->status = DASD_CQR_SUCCESS;
/* Indicate to calling function that only a dasd_schedule_bh()
and no timer is needed */
rc = -EACCES;
@@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr)
{
struct dasd_device *device;
- device = cqr->device;
+ device = cqr->startdev;
mdsk_term_io(device);
- mdsk_init_io(device, device->bp_block, 0, NULL);
- cqr->status = DASD_CQR_CLEAR;
+ mdsk_init_io(device, device->block->bp_block, 0, NULL);
+ cqr->status = DASD_CQR_CLEAR_PENDING;
cqr->stopclk = get_clock();
- dasd_schedule_bh(device);
+ dasd_schedule_device_bh(device);
return 0;
}
@@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code)
return;
}
cqr = (struct dasd_ccw_req *) ip;
- device = (struct dasd_device *) cqr->device;
+ device = (struct dasd_device *) cqr->startdev;
if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
DEV_MESSAGE(KERN_WARNING, device,
" magic number of dasd_ccw_req 0x%08X doesn't"
@@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code)
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
/* Check for a pending clear operation */
- if (cqr->status == DASD_CQR_CLEAR) {
- cqr->status = DASD_CQR_QUEUED;
- dasd_clear_timer(device);
- dasd_schedule_bh(device);
+ if (cqr->status == DASD_CQR_CLEAR_PENDING) {
+ cqr->status = DASD_CQR_CLEARED;
+ dasd_device_clear_timer(device);
+ dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
return;
}
@@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code)
expires = 0;
if (status == 0) {
- cqr->status = DASD_CQR_DONE;
+ cqr->status = DASD_CQR_SUCCESS;
/* Start first request on queue if possible -> fast_io. */
if (!list_empty(&device->ccw_queue)) {
next = list_entry(device->ccw_queue.next,
- struct dasd_ccw_req, list);
+ struct dasd_ccw_req, devlist);
if (next->status == DASD_CQR_QUEUED) {
rc = dasd_start_diag(next);
if (rc == 0)
@@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code)
}
if (expires != 0)
- dasd_set_timer(device, expires);
+ dasd_device_set_timer(device, expires);
else
- dasd_clear_timer(device);
- dasd_schedule_bh(device);
+ dasd_device_clear_timer(device);
+ dasd_schedule_device_bh(device);
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
}
@@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code)
static int
dasd_diag_check_device(struct dasd_device *device)
{
+ struct dasd_block *block;
struct dasd_diag_private *private;
struct dasd_diag_characteristics *rdc_data;
struct dasd_diag_bio bio;
@@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device)
ccw_device_get_id(device->cdev, &private->dev_id);
device->private = (void *) private;
}
+ block = dasd_alloc_block();
+ if (IS_ERR(block)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "could not allocate dasd block structure");
+ kfree(device->private);
+ return PTR_ERR(block);
+ }
+ device->block = block;
+ block->base = device;
+
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
rdc_data->dev_nr = private->dev_id.devno;
@@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device)
sizeof(DASD_DIAG_CMS1)) == 0) {
/* get formatted blocksize from label block */
bsize = (unsigned int) label->block_size;
- device->blocks = (unsigned long) label->block_count;
+ block->blocks = (unsigned long) label->block_count;
} else
- device->blocks = end_block;
- device->bp_block = bsize;
- device->s2b_shift = 0; /* bits to shift 512 to get a block */
+ block->blocks = end_block;
+ block->bp_block = bsize;
+ block->s2b_shift = 0; /* bits to shift 512 to get a block */
for (sb = 512; sb < bsize; sb = sb << 1)
- device->s2b_shift++;
- rc = mdsk_init_io(device, device->bp_block, 0, NULL);
+ block->s2b_shift++;
+ rc = mdsk_init_io(device, block->bp_block, 0, NULL);
if (rc) {
DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization "
"failed (rc=%d)", rc);
@@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device)
} else {
DEV_MESSAGE(KERN_INFO, device,
"(%ld B/blk): %ldkB",
- (unsigned long) device->bp_block,
- (unsigned long) (device->blocks <<
- device->s2b_shift) >> 1);
+ (unsigned long) block->bp_block,
+ (unsigned long) (block->blocks <<
+ block->s2b_shift) >> 1);
}
out:
free_page((long) label);
@@ -436,22 +447,16 @@ out:
/* Fill in virtual disk geometry for device. Return zero on success, non-zero
* otherwise. */
static int
-dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
{
- if (dasd_check_blocksize(device->bp_block) != 0)
+ if (dasd_check_blocksize(block->bp_block) != 0)
return -EINVAL;
- geo->cylinders = (device->blocks << device->s2b_shift) >> 10;
+ geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
geo->heads = 16;
- geo->sectors = 128 >> device->s2b_shift;
+ geo->sectors = 128 >> block->s2b_shift;
return 0;
}
-static dasd_era_t
-dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat)
-{
- return dasd_era_fatal;
-}
-
static dasd_erp_fn_t
dasd_diag_erp_action(struct dasd_ccw_req * cqr)
{
@@ -466,8 +471,9 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr)
/* Create DASD request from block device request. Return pointer to new
* request on success, ERR_PTR otherwise. */
-static struct dasd_ccw_req *
-dasd_diag_build_cp(struct dasd_device * device, struct request *req)
+static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
+ struct dasd_block *block,
+ struct request *req)
{
struct dasd_ccw_req *cqr;
struct dasd_diag_req *dreq;
@@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
rw_cmd = MDSK_WRITE_REQ;
else
return ERR_PTR(-EINVAL);
- blksize = device->bp_block;
+ blksize = block->bp_block;
/* Calculate record id of first and last block. */
- first_rec = req->sector >> device->s2b_shift;
- last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+ first_rec = req->sector >> block->s2b_shift;
+ last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
/* Check struct bio and count the number of blocks for the request. */
count = 0;
rq_for_each_segment(bv, req, iter) {
if (bv->bv_len & (blksize - 1))
/* Fba can only do full blocks. */
return ERR_PTR(-EINVAL);
- count += bv->bv_len >> (device->s2b_shift + 9);
+ count += bv->bv_len >> (block->s2b_shift + 9);
}
/* Paranoia. */
if (count != last_rec - first_rec + 1)
@@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
datasize = sizeof(struct dasd_diag_req) +
count*sizeof(struct dasd_diag_bio);
cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0,
- datasize, device);
+ datasize, memdev);
if (IS_ERR(cqr))
return cqr;
@@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
cqr->buildclk = get_clock();
if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
- cqr->device = device;
+ cqr->startdev = memdev;
+ cqr->memdev = memdev;
+ cqr->block = block;
cqr->expires = DIAG_TIMEOUT;
cqr->status = DASD_CQR_FILLED;
return cqr;
@@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
int status;
status = cqr->status == DASD_CQR_DONE;
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return status;
}
+static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr)
+{
+ cqr->status = DASD_CQR_FILLED;
+};
+
/* Fill in IOCTL data for device. */
static int
dasd_diag_fill_info(struct dasd_device * device,
@@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = {
.fill_geometry = dasd_diag_fill_geometry,
.start_IO = dasd_start_diag,
.term_IO = dasd_diag_term_IO,
- .examine_error = dasd_diag_examine_error,
+ .handle_terminated_request = dasd_diag_handle_terminated_request,
.erp_action = dasd_diag_erp_action,
.erp_postaction = dasd_diag_erp_postaction,
.build_cp = dasd_diag_build_cp,
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 44adf8496bd..61f16937c1e 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -52,16 +52,6 @@ MODULE_LICENSE("GPL");
static struct dasd_discipline dasd_eckd_discipline;
-struct dasd_eckd_private {
- struct dasd_eckd_characteristics rdc_data;
- struct dasd_eckd_confdata conf_data;
- struct dasd_eckd_path path_data;
- struct eckd_count count_area[5];
- int init_cqr_status;
- int uses_cdl;
- struct attrib_data_t attrib; /* e.g. cache operations */
-};
-
/* The ccw bus type uses this table to find devices that it sends to
* dasd_eckd_probe */
static struct ccw_device_id dasd_eckd_ids[] = {
@@ -188,7 +178,7 @@ check_XRC (struct ccw1 *de_ccw,
if (rc == -ENOSYS || rc == -EACCES)
rc = 0;
- de_ccw->count = sizeof (struct DE_eckd_data);
+ de_ccw->count = sizeof(struct DE_eckd_data);
de_ccw->flags |= CCW_FLAG_SLI;
return rc;
}
@@ -208,7 +198,7 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
ccw->count = 16;
ccw->cda = (__u32) __pa(data);
- memset(data, 0, sizeof (struct DE_eckd_data));
+ memset(data, 0, sizeof(struct DE_eckd_data));
switch (cmd) {
case DASD_ECKD_CCW_READ_HOME_ADDRESS:
case DASD_ECKD_CCW_READ_RECORD_ZERO:
@@ -280,6 +270,132 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
return rc;
}
+static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata,
+ struct dasd_device *device)
+{
+ struct dasd_eckd_private *private;
+ int rc;
+
+ private = (struct dasd_eckd_private *) device->private;
+ if (!private->rdc_data.facilities.XRC_supported)
+ return 0;
+
+ /* switch on System Time Stamp - needed for XRC Support */
+ pfxdata->define_extend.ga_extended |= 0x08; /* 'Time Stamp Valid' */
+ pfxdata->define_extend.ga_extended |= 0x02; /* 'Extended Parameter' */
+ pfxdata->validity.time_stamp = 1; /* 'Time Stamp Valid' */
+
+ rc = get_sync_clock(&pfxdata->define_extend.ep_sys_time);
+ /* Ignore return code if sync clock is switched off. */
+ if (rc == -ENOSYS || rc == -EACCES)
+ rc = 0;
+ return rc;
+}
+
+static int prefix(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, int trk,
+ int totrk, int cmd, struct dasd_device *basedev,
+ struct dasd_device *startdev)
+{
+ struct dasd_eckd_private *basepriv, *startpriv;
+ struct DE_eckd_data *data;
+ struct ch_t geo, beg, end;
+ int rc = 0;
+
+ basepriv = (struct dasd_eckd_private *) basedev->private;
+ startpriv = (struct dasd_eckd_private *) startdev->private;
+ data = &pfxdata->define_extend;
+
+ ccw->cmd_code = DASD_ECKD_CCW_PFX;
+ ccw->flags = 0;
+ ccw->count = sizeof(*pfxdata);
+ ccw->cda = (__u32) __pa(pfxdata);
+
+ memset(pfxdata, 0, sizeof(*pfxdata));
+ /* prefix data */
+ pfxdata->format = 0;
+ pfxdata->base_address = basepriv->conf_data.ned1.unit_addr;
+ pfxdata->base_lss = basepriv->conf_data.ned1.ID;
+ pfxdata->validity.define_extend = 1;
+
+ /* private uid is kept up to date, conf_data may be outdated */
+ if (startpriv->uid.type != UA_BASE_DEVICE) {
+ pfxdata->validity.verify_base = 1;
+ if (startpriv->uid.type == UA_HYPER_PAV_ALIAS)
+ pfxdata->validity.hyper_pav = 1;
+ }
+
+ /* define extend data (mostly)*/
+ switch (cmd) {
+ case DASD_ECKD_CCW_READ_HOME_ADDRESS:
+ case DASD_ECKD_CCW_READ_RECORD_ZERO:
+ case DASD_ECKD_CCW_READ:
+ case DASD_ECKD_CCW_READ_MT:
+ case DASD_ECKD_CCW_READ_CKD:
+ case DASD_ECKD_CCW_READ_CKD_MT:
+ case DASD_ECKD_CCW_READ_KD:
+ case DASD_ECKD_CCW_READ_KD_MT:
+ case DASD_ECKD_CCW_READ_COUNT:
+ data->mask.perm = 0x1;
+ data->attributes.operation = basepriv->attrib.operation;
+ break;
+ case DASD_ECKD_CCW_WRITE:
+ case DASD_ECKD_CCW_WRITE_MT:
+ case DASD_ECKD_CCW_WRITE_KD:
+ case DASD_ECKD_CCW_WRITE_KD_MT:
+ data->mask.perm = 0x02;
+ data->attributes.operation = basepriv->attrib.operation;
+ rc = check_XRC_on_prefix(pfxdata, basedev);
+ break;
+ case DASD_ECKD_CCW_WRITE_CKD:
+ case DASD_ECKD_CCW_WRITE_CKD_MT:
+ data->attributes.operation = DASD_BYPASS_CACHE;
+ rc = check_XRC_on_prefix(pfxdata, basedev);
+ break;
+ case DASD_ECKD_CCW_ERASE:
+ case DASD_ECKD_CCW_WRITE_HOME_ADDRESS:
+ case DASD_ECKD_CCW_WRITE_RECORD_ZERO:
+ data->mask.perm = 0x3;
+ data->mask.auth = 0x1;
+ data->attributes.operation = DASD_BYPASS_CACHE;
+ rc = check_XRC_on_prefix(pfxdata, basedev);
+ break;
+ default:
+ DEV_MESSAGE(KERN_ERR, basedev, "unknown opcode 0x%x", cmd);
+ break;
+ }
+
+ data->attributes.mode = 0x3; /* ECKD */
+
+ if ((basepriv->rdc_data.cu_type == 0x2105 ||
+ basepriv->rdc_data.cu_type == 0x2107 ||
+ basepriv->rdc_data.cu_type == 0x1750)
+ && !(basepriv->uses_cdl && trk < 2))
+ data->ga_extended |= 0x40; /* Regular Data Format Mode */
+
+ geo.cyl = basepriv->rdc_data.no_cyl;
+ geo.head = basepriv->rdc_data.trk_per_cyl;
+ beg.cyl = trk / geo.head;
+ beg.head = trk % geo.head;
+ end.cyl = totrk / geo.head;
+ end.head = totrk % geo.head;
+
+ /* check for sequential prestage - enhance cylinder range */
+ if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
+ data->attributes.operation == DASD_SEQ_ACCESS) {
+
+ if (end.cyl + basepriv->attrib.nr_cyl < geo.cyl)
+ end.cyl += basepriv->attrib.nr_cyl;
+ else
+ end.cyl = (geo.cyl - 1);
+ }
+
+ data->beg_ext.cyl = beg.cyl;
+ data->beg_ext.head = beg.head;
+ data->end_ext.cyl = end.cyl;
+ data->end_ext.head = end.head;
+ return rc;
+}
+
static void
locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
int rec_on_trk, int no_rec, int cmd,
@@ -300,7 +416,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
ccw->count = 16;
ccw->cda = (__u32) __pa(data);
- memset(data, 0, sizeof (struct LO_eckd_data));
+ memset(data, 0, sizeof(struct LO_eckd_data));
sector = 0;
if (rec_on_trk) {
switch (private->rdc_data.dev_type) {
@@ -441,12 +557,15 @@ dasd_eckd_generate_uid(struct dasd_device *device, struct dasd_uid *uid)
sizeof(uid->serial) - 1);
EBCASC(uid->serial, sizeof(uid->serial) - 1);
uid->ssid = confdata->neq.subsystemID;
- if (confdata->ned2.sneq.flags == 0x40) {
- uid->alias = 1;
- uid->unit_addr = confdata->ned2.sneq.base_unit_addr;
- } else
- uid->unit_addr = confdata->ned1.unit_addr;
-
+ uid->real_unit_addr = confdata->ned1.unit_addr;
+ if (confdata->ned2.sneq.flags == 0x40 &&
+ confdata->ned2.sneq.format == 0x0001) {
+ uid->type = confdata->ned2.sneq.sua_flags;
+ if (uid->type == UA_BASE_PAV_ALIAS)
+ uid->base_unit_addr = confdata->ned2.sneq.base_unit_addr;
+ } else {
+ uid->type = UA_BASE_DEVICE;
+ }
return 0;
}
@@ -470,7 +589,9 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
ccw->cda = (__u32)(addr_t)rcd_buffer;
ccw->count = ciw->count;
- cqr->device = device;
+ cqr->startdev = device;
+ cqr->memdev = device;
+ cqr->block = NULL;
cqr->expires = 10*HZ;
cqr->lpm = lpm;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
@@ -511,7 +632,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
/*
* on success we update the user input parms
*/
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
if (ret)
goto out_error;
@@ -557,19 +678,19 @@ dasd_eckd_read_conf(struct dasd_device *device)
"data retrieved");
continue; /* no error */
}
- if (conf_len != sizeof (struct dasd_eckd_confdata)) {
+ if (conf_len != sizeof(struct dasd_eckd_confdata)) {
MESSAGE(KERN_WARNING,
"sizes of configuration data mismatch"
"%d (read) vs %ld (expected)",
conf_len,
- sizeof (struct dasd_eckd_confdata));
+ sizeof(struct dasd_eckd_confdata));
kfree(conf_data);
continue; /* no error */
}
/* save first valid configuration data */
if (!conf_data_saved){
memcpy(&private->conf_data, conf_data,
- sizeof (struct dasd_eckd_confdata));
+ sizeof(struct dasd_eckd_confdata));
conf_data_saved++;
}
switch (((char *)conf_data)[242] & 0x07){
@@ -586,39 +707,104 @@ dasd_eckd_read_conf(struct dasd_device *device)
return 0;
}
+static int dasd_eckd_read_features(struct dasd_device *device)
+{
+ struct dasd_psf_prssd_data *prssdp;
+ struct dasd_rssd_features *features;
+ struct dasd_ccw_req *cqr;
+ struct ccw1 *ccw;
+ int rc;
+ struct dasd_eckd_private *private;
+
+ private = (struct dasd_eckd_private *) device->private;
+ cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
+ 1 /* PSF */ + 1 /* RSSD */ ,
+ (sizeof(struct dasd_psf_prssd_data) +
+ sizeof(struct dasd_rssd_features)),
+ device);
+ if (IS_ERR(cqr)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%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->expires = 10 * HZ;
+
+ /* Prepare for Read Subsystem Data */
+ prssdp = (struct dasd_psf_prssd_data *) cqr->data;
+ memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
+ prssdp->order = PSF_ORDER_PRSSD;
+ prssdp->suborder = 0x41; /* Read Feature Codes */
+ /* all other bytes of prssdp must be zero */
+
+ ccw = cqr->cpaddr;
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->count = sizeof(struct dasd_psf_prssd_data);
+ ccw->flags |= CCW_FLAG_CC;
+ ccw->cda = (__u32)(addr_t) prssdp;
+
+ /* Read Subsystem Data - feature codes */
+ features = (struct dasd_rssd_features *) (prssdp + 1);
+ memset(features, 0, sizeof(struct dasd_rssd_features));
+
+ ccw++;
+ ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+ ccw->count = sizeof(struct dasd_rssd_features);
+ ccw->cda = (__u32)(addr_t) features;
+
+ cqr->buildclk = get_clock();
+ cqr->status = DASD_CQR_FILLED;
+ rc = dasd_sleep_on(cqr);
+ if (rc == 0) {
+ prssdp = (struct dasd_psf_prssd_data *) cqr->data;
+ features = (struct dasd_rssd_features *) (prssdp + 1);
+ memcpy(&private->features, features,
+ sizeof(struct dasd_rssd_features));
+ }
+ dasd_sfree_request(cqr, cqr->memdev);
+ return rc;
+}
+
+
/*
* Build CP for Perform Subsystem Function - SSC.
*/
-static struct dasd_ccw_req *
-dasd_eckd_build_psf_ssc(struct dasd_device *device)
+static struct dasd_ccw_req *dasd_eckd_build_psf_ssc(struct dasd_device *device)
{
- struct dasd_ccw_req *cqr;
- struct dasd_psf_ssc_data *psf_ssc_data;
- struct ccw1 *ccw;
+ struct dasd_ccw_req *cqr;
+ struct dasd_psf_ssc_data *psf_ssc_data;
+ struct ccw1 *ccw;
- cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
+ cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
sizeof(struct dasd_psf_ssc_data),
device);
- if (IS_ERR(cqr)) {
- DEV_MESSAGE(KERN_WARNING, device, "%s",
+ if (IS_ERR(cqr)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
"Could not allocate PSF-SSC request");
- return cqr;
- }
- psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
- psf_ssc_data->order = PSF_ORDER_SSC;
- psf_ssc_data->suborder = 0x08;
-
- ccw = cqr->cpaddr;
- ccw->cmd_code = DASD_ECKD_CCW_PSF;
- ccw->cda = (__u32)(addr_t)psf_ssc_data;
- ccw->count = 66;
-
- cqr->device = device;
- cqr->expires = 10*HZ;
- cqr->buildclk = get_clock();
- cqr->status = DASD_CQR_FILLED;
- return cqr;
+ return cqr;
+ }
+ psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
+ psf_ssc_data->order = PSF_ORDER_SSC;
+ psf_ssc_data->suborder = 0x88;
+ psf_ssc_data->reserved[0] = 0x88;
+
+ ccw = cqr->cpaddr;
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->cda = (__u32)(addr_t)psf_ssc_data;
+ ccw->count = 66;
+
+ cqr->startdev = device;
+ cqr->memdev = device;
+ cqr->block = NULL;
+ cqr->expires = 10*HZ;
+ cqr->buildclk = get_clock();
+ cqr->status = DASD_CQR_FILLED;
+ return cqr;
}
/*
@@ -629,28 +815,28 @@ dasd_eckd_build_psf_ssc(struct dasd_device *device)
static int
dasd_eckd_psf_ssc(struct dasd_device *device)
{
- struct dasd_ccw_req *cqr;
- int rc;
-
- cqr = dasd_eckd_build_psf_ssc(device);
- if (IS_ERR(cqr))
- return PTR_ERR(cqr);
-
- rc = dasd_sleep_on(cqr);
- if (!rc)
- /* trigger CIO to reprobe devices */
- css_schedule_reprobe();
- dasd_sfree_request(cqr, cqr->device);
- return rc;
+ struct dasd_ccw_req *cqr;
+ int rc;
+
+ cqr = dasd_eckd_build_psf_ssc(device);
+ if (IS_ERR(cqr))
+ return PTR_ERR(cqr);
+
+ rc = dasd_sleep_on(cqr);
+ if (!rc)
+ /* trigger CIO to reprobe devices */
+ css_schedule_reprobe();
+ dasd_sfree_request(cqr, cqr->memdev);
+ return rc;
}
/*
* Valide storage server of current device.
*/
-static int
-dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
+static int dasd_eckd_validate_server(struct dasd_device *device)
{
int rc;
+ struct dasd_eckd_private *private;
/* Currently PAV is the only reason to 'validate' server on LPAR */
if (dasd_nopav || MACHINE_IS_VM)
@@ -659,9 +845,11 @@ dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid)
rc = dasd_eckd_psf_ssc(device);
/* may be requested feature is not available on server,
* therefore just report error and go ahead */
+ private = (struct dasd_eckd_private *) device->private;
DEV_MESSAGE(KERN_INFO, device,
"PSF-SSC on storage subsystem %s.%s.%04x returned rc=%d",
- uid->vendor, uid->serial, uid->ssid, rc);
+ private->uid.vendor, private->uid.serial,
+ private->uid.ssid, rc);
/* RE-Read Configuration Data */
return dasd_eckd_read_conf(device);
}
@@ -674,9 +862,9 @@ static int
dasd_eckd_check_characteristics(struct dasd_device *device)
{
struct dasd_eckd_private *private;
- struct dasd_uid uid;
+ struct dasd_block *block;
void *rdc_data;
- int rc;
+ int is_known, rc;
private = (struct dasd_eckd_private *) device->private;
if (private == NULL) {
@@ -699,27 +887,54 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
/* Read Configuration Data */
rc = dasd_eckd_read_conf(device);
if (rc)
- return rc;
+ goto out_err1;
/* Generate device unique id and register in devmap */
- rc = dasd_eckd_generate_uid(device, &uid);
+ rc = dasd_eckd_generate_uid(device, &private->uid);
if (rc)
- return rc;
- rc = dasd_set_uid(device->cdev, &uid);
- if (rc == 1) /* new server found */
- rc = dasd_eckd_validate_server(device, &uid);
+ goto out_err1;
+ dasd_set_uid(device->cdev, &private->uid);
+
+ if (private->uid.type == UA_BASE_DEVICE) {
+ block = dasd_alloc_block();
+ if (IS_ERR(block)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "could not allocate dasd block structure");
+ rc = PTR_ERR(block);
+ goto out_err1;
+ }
+ device->block = block;
+ block->base = device;
+ }
+
+ /* register lcu with alias handling, enable PAV if this is a new lcu */
+ is_known = dasd_alias_make_device_known_to_lcu(device);
+ if (is_known < 0) {
+ rc = is_known;
+ goto out_err2;
+ }
+ if (!is_known) {
+ /* new lcu found */
+ rc = dasd_eckd_validate_server(device); /* will switch pav on */
+ if (rc)
+ goto out_err3;
+ }
+
+ /* Read Feature Codes */
+ rc = dasd_eckd_read_features(device);
if (rc)
- return rc;
+ goto out_err3;
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
memset(rdc_data, 0, sizeof(rdc_data));
rc = dasd_generic_read_dev_chars(device, "ECKD", &rdc_data, 64);
- if (rc)
+ if (rc) {
DEV_MESSAGE(KERN_WARNING, device,
"Read device characteristics returned "
"rc=%d", rc);
-
+ goto out_err3;
+ }
DEV_MESSAGE(KERN_INFO, device,
"%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
private->rdc_data.dev_type,
@@ -729,9 +944,24 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private->rdc_data.no_cyl,
private->rdc_data.trk_per_cyl,
private->rdc_data.sec_per_trk);
+ return 0;
+
+out_err3:
+ dasd_alias_disconnect_device_from_lcu(device);
+out_err2:
+ dasd_free_block(device->block);
+ device->block = NULL;
+out_err1:
+ kfree(device->private);
+ device->private = NULL;
return rc;
}
+static void dasd_eckd_uncheck_device(struct dasd_device *device)
+{
+ dasd_alias_disconnect_device_from_lcu(device);
+}
+
static struct dasd_ccw_req *
dasd_eckd_analysis_ccw(struct dasd_device *device)
{
@@ -755,7 +985,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
/* Define extent for the first 3 tracks. */
define_extent(ccw++, cqr->data, 0, 2,
DASD_ECKD_CCW_READ_COUNT, device);
- LO_data = cqr->data + sizeof (struct DE_eckd_data);
+ LO_data = cqr->data + sizeof(struct DE_eckd_data);
/* Locate record for the first 4 records on track 0. */
ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++, 0, 0, 4,
@@ -783,7 +1013,9 @@ dasd_eckd_analysis_ccw(struct dasd_device *device)
ccw->count = 8;
ccw->cda = (__u32)(addr_t) count_data;
- cqr->device = device;
+ cqr->block = NULL;
+ cqr->startdev = device;
+ cqr->memdev = device;
cqr->retries = 0;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
@@ -803,7 +1035,7 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
struct dasd_eckd_private *private;
struct dasd_device *device;
- device = init_cqr->device;
+ device = init_cqr->startdev;
private = (struct dasd_eckd_private *) device->private;
private->init_cqr_status = init_cqr->status;
dasd_sfree_request(init_cqr, device);
@@ -811,13 +1043,13 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data)
}
static int
-dasd_eckd_start_analysis(struct dasd_device *device)
+dasd_eckd_start_analysis(struct dasd_block *block)
{
struct dasd_eckd_private *private;
struct dasd_ccw_req *init_cqr;
- private = (struct dasd_eckd_private *) device->private;
- init_cqr = dasd_eckd_analysis_ccw(device);
+ private = (struct dasd_eckd_private *) block->base->private;
+ init_cqr = dasd_eckd_analysis_ccw(block->base);
if (IS_ERR(init_cqr))
return PTR_ERR(init_cqr);
init_cqr->callback = dasd_eckd_analysis_callback;
@@ -828,13 +1060,15 @@ dasd_eckd_start_analysis(struct dasd_device *device)
}
static int
-dasd_eckd_end_analysis(struct dasd_device *device)
+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;
+ device = block->base;
private = (struct dasd_eckd_private *) device->private;
status = private->init_cqr_status;
private->init_cqr_status = -1;
@@ -846,7 +1080,7 @@ dasd_eckd_end_analysis(struct dasd_device *device)
private->uses_cdl = 1;
/* Calculate number of blocks/records per track. */
- blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);
+ blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
/* Check Track 0 for Compatible Disk Layout */
count_area = NULL;
for (i = 0; i < 3; i++) {
@@ -876,56 +1110,65 @@ dasd_eckd_end_analysis(struct dasd_device *device)
if (count_area != NULL && count_area->kl == 0) {
/* we found notthing violating our disk layout */
if (dasd_check_blocksize(count_area->dl) == 0)
- device->bp_block = count_area->dl;
+ block->bp_block = count_area->dl;
}
- if (device->bp_block == 0) {
+ if (block->bp_block == 0) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"Volume has incompatible disk layout");
return -EMEDIUMTYPE;
}
- device->s2b_shift = 0; /* bits to shift 512 to get a block */
- for (sb = 512; sb < device->bp_block; sb = sb << 1)
- device->s2b_shift++;
+ block->s2b_shift = 0; /* bits to shift 512 to get a block */
+ for (sb = 512; sb < block->bp_block; sb = sb << 1)
+ block->s2b_shift++;
- blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block);
- device->blocks = (private->rdc_data.no_cyl *
+ blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block);
+ block->blocks = (private->rdc_data.no_cyl *
private->rdc_data.trk_per_cyl *
blk_per_trk);
DEV_MESSAGE(KERN_INFO, device,
"(%dkB blks): %dkB at %dkB/trk %s",
- (device->bp_block >> 10),
+ (block->bp_block >> 10),
((private->rdc_data.no_cyl *
private->rdc_data.trk_per_cyl *
- blk_per_trk * (device->bp_block >> 9)) >> 1),
- ((blk_per_trk * device->bp_block) >> 10),
+ blk_per_trk * (block->bp_block >> 9)) >> 1),
+ ((blk_per_trk * block->bp_block) >> 10),
private->uses_cdl ?
"compatible disk layout" : "linux disk layout");
return 0;
}
-static int
-dasd_eckd_do_analysis(struct dasd_device *device)
+static int dasd_eckd_do_analysis(struct dasd_block *block)
{
struct dasd_eckd_private *private;
- private = (struct dasd_eckd_private *) device->private;
+ private = (struct dasd_eckd_private *) block->base->private;
if (private->init_cqr_status < 0)
- return dasd_eckd_start_analysis(device);
+ return dasd_eckd_start_analysis(block);
else
- return dasd_eckd_end_analysis(device);
+ return dasd_eckd_end_analysis(block);
}
+static int dasd_eckd_ready_to_online(struct dasd_device *device)
+{
+ return dasd_alias_add_device(device);
+};
+
+static int dasd_eckd_online_to_ready(struct dasd_device *device)
+{
+ return dasd_alias_remove_device(device);
+};
+
static int
-dasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+dasd_eckd_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
{
struct dasd_eckd_private *private;
- private = (struct dasd_eckd_private *) device->private;
- if (dasd_check_blocksize(device->bp_block) == 0) {
+ private = (struct dasd_eckd_private *) block->base->private;
+ if (dasd_check_blocksize(block->bp_block) == 0) {
geo->sectors = recs_per_track(&private->rdc_data,
- 0, device->bp_block);
+ 0, block->bp_block);
}
geo->cylinders = private->rdc_data.no_cyl;
geo->heads = private->rdc_data.trk_per_cyl;
@@ -1037,7 +1280,7 @@ dasd_eckd_format_device(struct dasd_device * device,
locate_record(ccw++, (struct LO_eckd_data *) data,
fdata->start_unit, 0, rpt + 1,
DASD_ECKD_CCW_WRITE_RECORD_ZERO, device,
- device->bp_block);
+ device->block->bp_block);
data += sizeof(struct LO_eckd_data);
break;
case 0x04: /* Invalidate track. */
@@ -1110,43 +1353,28 @@ dasd_eckd_format_device(struct dasd_device * device,
ccw++;
}
}
- fcp->device = device;
- fcp->retries = 2; /* set retry counter to enable ERP */
+ 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->buildclk = get_clock();
fcp->status = DASD_CQR_FILLED;
return fcp;
}
-static dasd_era_t
-dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
+static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
{
- struct dasd_device *device = (struct dasd_device *) cqr->device;
- struct ccw_device *cdev = device->cdev;
-
- if (irb->scsw.cstat == 0x00 &&
- irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
- return dasd_era_none;
-
- switch (cdev->id.cu_type) {
- case 0x3990:
- case 0x2105:
- case 0x2107:
- case 0x1750:
- return dasd_3990_erp_examine(cqr, irb);
- case 0x9343:
- return dasd_9343_erp_examine(cqr, irb);
- case 0x3880:
- default:
- DEV_MESSAGE(KERN_WARNING, device, "%s",
- "default (unknown CU type) - RECOVERABLE return");
- return dasd_era_recover;
+ cqr->status = DASD_CQR_FILLED;
+ if (cqr->block && (cqr->startdev != cqr->block->base)) {
+ dasd_eckd_reset_ccw_to_base_io(cqr);
+ cqr->startdev = cqr->block->base;
}
-}
+};
static dasd_erp_fn_t
dasd_eckd_erp_action(struct dasd_ccw_req * cqr)
{
- struct dasd_device *device = (struct dasd_device *) cqr->device;
+ struct dasd_device *device = (struct dasd_device *) cqr->startdev;
struct ccw_device *cdev = device->cdev;
switch (cdev->id.cu_type) {
@@ -1168,8 +1396,37 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr)
return dasd_default_erp_postaction;
}
-static struct dasd_ccw_req *
-dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
+
+static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
+ struct irb *irb)
+{
+ char mask;
+
+ /* first of all check for state change pending interrupt */
+ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
+ if ((irb->scsw.dstat & mask) == mask) {
+ dasd_generic_handle_state_change(device);
+ return;
+ }
+
+ /* summary unit check */
+ if ((irb->scsw.dstat & DEV_STAT_UNIT_CHECK) && irb->ecw[7] == 0x0D) {
+ dasd_alias_handle_summary_unit_check(device, irb);
+ return;
+ }
+
+ /* just report other unsolicited interrupts */
+ DEV_MESSAGE(KERN_DEBUG, device, "%s",
+ "unsolicited interrupt received");
+ device->discipline->dump_sense(device, NULL, irb);
+ dasd_schedule_device_bh(device);
+
+ return;
+};
+
+static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev,
+ struct dasd_block *block,
+ struct request *req)
{
struct dasd_eckd_private *private;
unsigned long *idaws;
@@ -1185,8 +1442,11 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
sector_t first_trk, last_trk;
unsigned int first_offs, last_offs;
unsigned char cmd, rcmd;
+ int use_prefix;
+ struct dasd_device *basedev;
- private = (struct dasd_eckd_private *) device->private;
+ basedev = block->base;
+ private = (struct dasd_eckd_private *) basedev->private;
if (rq_data_dir(req) == READ)
cmd = DASD_ECKD_CCW_READ_MT;
else if (rq_data_dir(req) == WRITE)
@@ -1194,13 +1454,13 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
else
return ERR_PTR(-EINVAL);
/* Calculate number of blocks/records per track. */
- blksize = device->bp_block;
+ blksize = block->bp_block;
blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
/* Calculate record id of first and last block. */
- first_rec = first_trk = req->sector >> device->s2b_shift;
+ first_rec = first_trk = req->sector >> block->s2b_shift;
first_offs = sector_div(first_trk, blk_per_trk);
last_rec = last_trk =
- (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+ (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
last_offs = sector_div(last_trk, blk_per_trk);
/* Check struct bio and count the number of blocks for the request. */
count = 0;
@@ -1209,20 +1469,33 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
if (bv->bv_len & (blksize - 1))
/* Eckd can only do full blocks. */
return ERR_PTR(-EINVAL);
- count += bv->bv_len >> (device->s2b_shift + 9);
+ count += bv->bv_len >> (block->s2b_shift + 9);
#if defined(CONFIG_64BIT)
if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
- cidaw += bv->bv_len >> (device->s2b_shift + 9);
+ cidaw += bv->bv_len >> (block->s2b_shift + 9);
#endif
}
/* Paranoia. */
if (count != last_rec - first_rec + 1)
return ERR_PTR(-EINVAL);
- /* 1x define extent + 1x locate record + number of blocks */
- cplength = 2 + count;
- /* 1x define extent + 1x locate record + cidaws*sizeof(long) */
- datasize = sizeof(struct DE_eckd_data) + sizeof(struct LO_eckd_data) +
- cidaw * sizeof(unsigned long);
+
+ /* use the prefix command if available */
+ use_prefix = private->features.feature[8] & 0x01;
+ if (use_prefix) {
+ /* 1x prefix + number of blocks */
+ cplength = 2 + count;
+ /* 1x prefix + cidaws*sizeof(long) */
+ datasize = sizeof(struct PFX_eckd_data) +
+ sizeof(struct LO_eckd_data) +
+ cidaw * sizeof(unsigned long);
+ } else {
+ /* 1x define extent + 1x locate record + number of blocks */
+ cplength = 2 + count;
+ /* 1x define extent + 1x locate record + cidaws*sizeof(long) */
+ datasize = sizeof(struct DE_eckd_data) +
+ sizeof(struct LO_eckd_data) +
+ cidaw * sizeof(unsigned long);
+ }
/* Find out the number of additional locate record ccws for cdl. */
if (private->uses_cdl && first_rec < 2*blk_per_trk) {
if (last_rec >= 2*blk_per_trk)
@@ -1232,26 +1505,42 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
}
/* Allocate the ccw request. */
cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
- cplength, datasize, device);
+ cplength, datasize, startdev);
if (IS_ERR(cqr))
return cqr;
ccw = cqr->cpaddr;
- /* First ccw is define extent. */
- if (define_extent(ccw++, cqr->data, first_trk,
- last_trk, cmd, device) == -EAGAIN) {
- /* Clock not in sync and XRC is enabled. Try again later. */
- dasd_sfree_request(cqr, device);
- return ERR_PTR(-EAGAIN);
+ /* First ccw is define extent or prefix. */
+ if (use_prefix) {
+ if (prefix(ccw++, cqr->data, first_trk,
+ last_trk, cmd, basedev, startdev) == -EAGAIN) {
+ /* Clock not in sync and XRC is enabled.
+ * Try again later.
+ */
+ dasd_sfree_request(cqr, startdev);
+ return ERR_PTR(-EAGAIN);
+ }
+ idaws = (unsigned long *) (cqr->data +
+ sizeof(struct PFX_eckd_data));
+ } else {
+ if (define_extent(ccw++, cqr->data, first_trk,
+ last_trk, cmd, startdev) == -EAGAIN) {
+ /* Clock not in sync and XRC is enabled.
+ * Try again later.
+ */
+ dasd_sfree_request(cqr, startdev);
+ return ERR_PTR(-EAGAIN);
+ }
+ idaws = (unsigned long *) (cqr->data +
+ sizeof(struct DE_eckd_data));
}
/* Build locate_record+read/write/ccws. */
- idaws = (unsigned long *) (cqr->data + sizeof(struct DE_eckd_data));
LO_data = (struct LO_eckd_data *) (idaws + cidaw);
recid = first_rec;
if (private->uses_cdl == 0 || recid > 2*blk_per_trk) {
/* Only standard blocks so there is just one locate record. */
ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++, first_trk, first_offs + 1,
- last_rec - recid + 1, cmd, device, blksize);
+ last_rec - recid + 1, cmd, basedev, blksize);
}
rq_for_each_segment(bv, req, iter) {
dst = page_address(bv->bv_page) + bv->bv_offset;
@@ -1281,7 +1570,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
ccw[-1].flags |= CCW_FLAG_CC;
locate_record(ccw++, LO_data++,
trkid, recoffs + 1,
- 1, rcmd, device, count);
+ 1, rcmd, basedev, count);
}
/* Locate record for standard blocks ? */
if (private->uses_cdl && recid == 2*blk_per_trk) {
@@ -1289,7 +1578,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
locate_record(ccw++, LO_data++,
trkid, recoffs + 1,
last_rec - recid + 1,
- cmd, device, count);
+ cmd, basedev, count);
}
/* Read/write ccw. */
ccw[-1].flags |= CCW_FLAG_CC;
@@ -1310,7 +1599,9 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
}
if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
- cqr->device = device;
+ cqr->startdev = startdev;
+ cqr->memdev = startdev;
+ cqr->block = block;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->lpm = private->path_data.ppm;
cqr->retries = 256;
@@ -1333,10 +1624,10 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
if (!dasd_page_cache)
goto out;
- private = (struct dasd_eckd_private *) cqr->device->private;
- blksize = cqr->device->bp_block;
+ private = (struct dasd_eckd_private *) cqr->block->base->private;
+ blksize = cqr->block->bp_block;
blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize);
- recid = req->sector >> cqr->device->s2b_shift;
+ recid = req->sector >> cqr->block->s2b_shift;
ccw = cqr->cpaddr;
/* Skip over define extent & locate record. */
ccw++;
@@ -1367,10 +1658,71 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req)
}
out:
status = cqr->status == DASD_CQR_DONE;
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return status;
}
+/*
+ * Modify ccw chain in cqr so it can be started on a base device.
+ *
+ * Note that this is not enough to restart the cqr!
+ * Either reset cqr->startdev as well (summary unit check handling)
+ * or restart via separate cqr (as in ERP handling).
+ */
+void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *cqr)
+{
+ struct ccw1 *ccw;
+ struct PFX_eckd_data *pfxdata;
+
+ ccw = cqr->cpaddr;
+ pfxdata = cqr->data;
+
+ if (ccw->cmd_code == DASD_ECKD_CCW_PFX) {
+ pfxdata->validity.verify_base = 0;
+ pfxdata->validity.hyper_pav = 0;
+ }
+}
+
+#define DASD_ECKD_CHANQ_MAX_SIZE 4
+
+static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base,
+ struct dasd_block *block,
+ struct request *req)
+{
+ struct dasd_eckd_private *private;
+ struct dasd_device *startdev;
+ unsigned long flags;
+ struct dasd_ccw_req *cqr;
+
+ startdev = dasd_alias_get_start_dev(base);
+ if (!startdev)
+ startdev = base;
+ private = (struct dasd_eckd_private *) startdev->private;
+ if (private->count >= DASD_ECKD_CHANQ_MAX_SIZE)
+ return ERR_PTR(-EBUSY);
+
+ spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags);
+ private->count++;
+ cqr = dasd_eckd_build_cp(startdev, block, req);
+ if (IS_ERR(cqr))
+ private->count--;
+ spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags);
+ return cqr;
+}
+
+static int dasd_eckd_free_alias_cp(struct dasd_ccw_req *cqr,
+ struct request *req)
+{
+ struct dasd_eckd_private *private;
+ unsigned long flags;
+
+ spin_lock_irqsave(get_ccwdev_lock(cqr->memdev->cdev), flags);
+ private = (struct dasd_eckd_private *) cqr->memdev->private;
+ private->count--;
+ spin_unlock_irqrestore(get_ccwdev_lock(cqr->memdev->cdev), flags);
+ return dasd_eckd_free_cp(cqr, req);
+}
+
static int
dasd_eckd_fill_info(struct dasd_device * device,
struct dasd_information2_t * info)
@@ -1384,9 +1736,9 @@ dasd_eckd_fill_info(struct dasd_device * device,
info->characteristics_size = sizeof(struct dasd_eckd_characteristics);
memcpy(info->characteristics, &private->rdc_data,
sizeof(struct dasd_eckd_characteristics));
- info->confdata_size = sizeof (struct dasd_eckd_confdata);
+ info->confdata_size = sizeof(struct dasd_eckd_confdata);
memcpy(info->configuration_data, &private->conf_data,
- sizeof (struct dasd_eckd_confdata));
+ sizeof(struct dasd_eckd_confdata));
return 0;
}
@@ -1419,7 +1771,8 @@ dasd_eckd_release(struct dasd_device *device)
cqr->cpaddr->flags |= CCW_FLAG_SLI;
cqr->cpaddr->count = 32;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
- cqr->device = device;
+ cqr->startdev = device;
+ cqr->memdev = device;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->retries = 2; /* set retry counter to enable basic ERP */
@@ -1429,7 +1782,7 @@ dasd_eckd_release(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -1459,7 +1812,8 @@ dasd_eckd_reserve(struct dasd_device *device)
cqr->cpaddr->flags |= CCW_FLAG_SLI;
cqr->cpaddr->count = 32;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
- cqr->device = device;
+ cqr->startdev = device;
+ cqr->memdev = device;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->retries = 2; /* set retry counter to enable basic ERP */
@@ -1469,7 +1823,7 @@ dasd_eckd_reserve(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -1498,7 +1852,8 @@ dasd_eckd_steal_lock(struct dasd_device *device)
cqr->cpaddr->flags |= CCW_FLAG_SLI;
cqr->cpaddr->count = 32;
cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
- cqr->device = device;
+ cqr->startdev = device;
+ cqr->memdev = device;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->retries = 2; /* set retry counter to enable basic ERP */
@@ -1508,7 +1863,7 @@ dasd_eckd_steal_lock(struct dasd_device *device)
rc = dasd_sleep_on_immediatly(cqr);
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -1526,52 +1881,52 @@ dasd_eckd_performance(struct dasd_device *device, void __user *argp)
cqr = dasd_smalloc_request(dasd_eckd_discipline.name,
1 /* PSF */ + 1 /* RSSD */ ,
- (sizeof (struct dasd_psf_prssd_data) +
- sizeof (struct dasd_rssd_perf_stats_t)),
+ (sizeof(struct dasd_psf_prssd_data) +
+ sizeof(struct dasd_rssd_perf_stats_t)),
device);
if (IS_ERR(cqr)) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"Could not allocate initialization request");
return PTR_ERR(cqr);
}
- cqr->device = device;
+ cqr->startdev = device;
+ cqr->memdev = device;
cqr->retries = 0;
cqr->expires = 10 * HZ;
/* Prepare for Read Subsystem Data */
prssdp = (struct dasd_psf_prssd_data *) cqr->data;
- memset(prssdp, 0, sizeof (struct dasd_psf_prssd_data));
+ memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
prssdp->order = PSF_ORDER_PRSSD;
- prssdp->suborder = 0x01; /* Perfomance Statistics */
+ prssdp->suborder = 0x01; /* Performance Statistics */
prssdp->varies[1] = 0x01; /* Perf Statistics for the Subsystem */
ccw = cqr->cpaddr;
ccw->cmd_code = DASD_ECKD_CCW_PSF;
- ccw->count = sizeof (struct dasd_psf_prssd_data);
+ ccw->count = sizeof(struct dasd_psf_prssd_data);
ccw->flags |= CCW_FLAG_CC;
ccw->cda = (__u32)(addr_t) prssdp;
/* Read Subsystem Data - Performance Statistics */
stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
- memset(stats, 0, sizeof (struct dasd_rssd_perf_stats_t));
+ memset(stats, 0, sizeof(struct dasd_rssd_perf_stats_t));
ccw++;
ccw->cmd_code = DASD_ECKD_CCW_RSSD;
- ccw->count = sizeof (struct dasd_rssd_perf_stats_t);
+ ccw->count = sizeof(struct dasd_rssd_perf_stats_t);
ccw->cda = (__u32)(addr_t) stats;
cqr->buildclk = get_clock();
cqr->status = DASD_CQR_FILLED;
rc = dasd_sleep_on(cqr);
if (rc == 0) {
- /* Prepare for Read Subsystem Data */
prssdp = (struct dasd_psf_prssd_data *) cqr->data;
stats = (struct dasd_rssd_perf_stats_t *) (prssdp + 1);
if (copy_to_user(argp, stats,
sizeof(struct dasd_rssd_perf_stats_t)))
rc = -EFAULT;
}
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return rc;
}
@@ -1594,7 +1949,7 @@ dasd_eckd_get_attrib(struct dasd_device *device, void __user *argp)
rc = 0;
if (copy_to_user(argp, (long *) &attrib,
- sizeof (struct attrib_data_t)))
+ sizeof(struct attrib_data_t)))
rc = -EFAULT;
return rc;
@@ -1627,8 +1982,10 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp)
}
static int
-dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp)
+dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp)
{
+ struct dasd_device *device = block->base;
+
switch (cmd) {
case BIODASDGATTR:
return dasd_eckd_get_attrib(device, argp);
@@ -1685,9 +2042,8 @@ dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
* Print sense data and related channel program.
* Parts are printed because printk buffer is only 1024 bytes.
*/
-static void
-dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
- struct irb *irb)
+static void dasd_eckd_dump_sense(struct dasd_device *device,
+ struct dasd_ccw_req *req, struct irb *irb)
{
char *page;
struct ccw1 *first, *last, *fail, *from, *to;
@@ -1743,37 +2099,40 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
}
printk("%s", page);
- /* dump the Channel Program (max 140 Bytes per line) */
- /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
- first = req->cpaddr;
- for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
- to = min(first + 6, last);
- len = sprintf(page, KERN_ERR PRINTK_HEADER
- " Related CP in req: %p\n", req);
- dasd_eckd_dump_ccw_range(first, to, page + len);
- printk("%s", page);
+ if (req) {
+ /* req == NULL for unsolicited interrupts */
+ /* dump the Channel Program (max 140 Bytes per line) */
+ /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
+ first = req->cpaddr;
+ for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
+ to = min(first + 6, last);
+ len = sprintf(page, KERN_ERR PRINTK_HEADER
+ " Related CP in req: %p\n", req);
+ dasd_eckd_dump_ccw_range(first, to, page + len);
+ printk("%s", page);
- /* print failing CCW area (maximum 4) */
- /* scsw->cda is either valid or zero */
- len = 0;
- from = ++to;
- fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
- if (from < fail - 2) {
- from = fail - 2; /* there is a gap - print header */
- len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
- }
- to = min(fail + 1, last);
- len += dasd_eckd_dump_ccw_range(from, to, page + len);
-
- /* print last CCWs (maximum 2) */
- from = max(from, ++to);
- if (from < last - 1) {
- from = last - 1; /* there is a gap - print header */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+ /* print failing CCW area (maximum 4) */
+ /* scsw->cda is either valid or zero */
+ len = 0;
+ from = ++to;
+ fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+ if (from < fail - 2) {
+ from = fail - 2; /* there is a gap - print header */
+ len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
+ }
+ to = min(fail + 1, last);
+ len += dasd_eckd_dump_ccw_range(from, to, page + len);
+
+ /* print last CCWs (maximum 2) */
+ from = max(from, ++to);
+ if (from < last - 1) {
+ from = last - 1; /* there is a gap - print header */
+ len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
+ }
+ len += dasd_eckd_dump_ccw_range(from, last, page + len);
+ if (len > 0)
+ printk("%s", page);
}
- len += dasd_eckd_dump_ccw_range(from, last, page + len);
- if (len > 0)
- printk("%s", page);
free_page((unsigned long) page);
}
@@ -1796,16 +2155,20 @@ static struct dasd_discipline dasd_eckd_discipline = {
.ebcname = "ECKD",
.max_blocks = 240,
.check_device = dasd_eckd_check_characteristics,
+ .uncheck_device = dasd_eckd_uncheck_device,
.do_analysis = dasd_eckd_do_analysis,
+ .ready_to_online = dasd_eckd_ready_to_online,
+ .online_to_ready = dasd_eckd_online_to_ready,
.fill_geometry = dasd_eckd_fill_geometry,
.start_IO = dasd_start_IO,
.term_IO = dasd_term_IO,
+ .handle_terminated_request = dasd_eckd_handle_terminated_request,
.format_device = dasd_eckd_format_device,
- .examine_error = dasd_eckd_examine_error,
.erp_action = dasd_eckd_erp_action,
.erp_postaction = dasd_eckd_erp_postaction,
- .build_cp = dasd_eckd_build_cp,
- .free_cp = dasd_eckd_free_cp,
+ .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt,
+ .build_cp = dasd_eckd_build_alias_cp,
+ .free_cp = dasd_eckd_free_alias_cp,
.dump_sense = dasd_eckd_dump_sense,
.fill_info = dasd_eckd_fill_info,
.ioctl = dasd_eckd_ioctl,
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index 712ff165013..fc2509c939b 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -39,6 +39,8 @@
#define DASD_ECKD_CCW_READ_CKD_MT 0x9e
#define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d
#define DASD_ECKD_CCW_RESERVE 0xB4
+#define DASD_ECKD_CCW_PFX 0xE7
+#define DASD_ECKD_CCW_RSCK 0xF9
/*
* Perform Subsystem Function / Sub-Orders
@@ -137,6 +139,25 @@ struct LO_eckd_data {
__u16 length;
} __attribute__ ((packed));
+/* Prefix data for format 0x00 and 0x01 */
+struct PFX_eckd_data {
+ unsigned char format;
+ struct {
+ unsigned char define_extend:1;
+ unsigned char time_stamp:1;
+ unsigned char verify_base:1;
+ unsigned char hyper_pav:1;
+ unsigned char reserved:4;
+ } __attribute__ ((packed)) validity;
+ __u8 base_address;
+ __u8 aux;
+ __u8 base_lss;
+ __u8 reserved[7];
+ struct DE_eckd_data define_extend;
+ struct LO_eckd_data locate_record;
+ __u8 LO_extended_data[4];
+} __attribute__ ((packed));
+
struct dasd_eckd_characteristics {
__u16 cu_type;
struct {
@@ -254,7 +275,9 @@ struct dasd_eckd_confdata {
} __attribute__ ((packed)) ned;
struct {
unsigned char flags; /* byte 0 */
- unsigned char res2[7]; /* byte 1- 7 */
+ unsigned char res1; /* byte 1 */
+ __u16 format; /* byte 2-3 */
+ unsigned char res2[4]; /* byte 4-7 */
unsigned char sua_flags; /* byte 8 */
__u8 base_unit_addr; /* byte 9 */
unsigned char res3[22]; /* byte 10-31 */
@@ -343,6 +366,11 @@ struct dasd_eckd_path {
__u8 npm;
};
+struct dasd_rssd_features {
+ char feature[256];
+} __attribute__((packed));
+
+
/*
* Perform Subsystem Function - Prepare for Read Subsystem Data
*/
@@ -365,4 +393,99 @@ struct dasd_psf_ssc_data {
unsigned char reserved[59];
} __attribute__((packed));
+
+/*
+ * some structures and definitions for alias handling
+ */
+struct dasd_unit_address_configuration {
+ struct {
+ char ua_type;
+ char base_ua;
+ } unit[256];
+} __attribute__((packed));
+
+
+#define MAX_DEVICES_PER_LCU 256
+
+/* flags on the LCU */
+#define NEED_UAC_UPDATE 0x01
+#define UPDATE_PENDING 0x02
+
+enum pavtype {NO_PAV, BASE_PAV, HYPER_PAV};
+
+
+struct alias_root {
+ struct list_head serverlist;
+ spinlock_t lock;
+};
+
+struct alias_server {
+ struct list_head server;
+ struct dasd_uid uid;
+ struct list_head lculist;
+};
+
+struct summary_unit_check_work_data {
+ char reason;
+ struct dasd_device *device;
+ struct work_struct worker;
+};
+
+struct read_uac_work_data {
+ struct dasd_device *device;
+ struct delayed_work dwork;
+};
+
+struct alias_lcu {
+ struct list_head lcu;
+ struct dasd_uid uid;
+ enum pavtype pav;
+ char flags;
+ spinlock_t lock;
+ struct list_head grouplist;
+ struct list_head active_devices;
+ struct list_head inactive_devices;
+ struct dasd_unit_address_configuration *uac;
+ struct summary_unit_check_work_data suc_data;
+ struct read_uac_work_data ruac_data;
+ struct dasd_ccw_req *rsu_cqr;
+};
+
+struct alias_pav_group {
+ struct list_head group;
+ struct dasd_uid uid;
+ struct alias_lcu *lcu;
+ struct list_head baselist;
+ struct list_head aliaslist;
+ struct dasd_device *next;
+};
+
+
+struct dasd_eckd_private {
+ struct dasd_eckd_characteristics rdc_data;
+ struct dasd_eckd_confdata conf_data;
+ struct dasd_eckd_path path_data;
+ struct eckd_count count_area[5];
+ int init_cqr_status;
+ int uses_cdl;
+ struct attrib_data_t attrib; /* e.g. cache operations */
+ struct dasd_rssd_features features;
+
+ /* alias managemnet */
+ struct dasd_uid uid;
+ struct alias_pav_group *pavgroup;
+ struct alias_lcu *lcu;
+ int count;
+};
+
+
+
+int dasd_alias_make_device_known_to_lcu(struct dasd_device *);
+void dasd_alias_disconnect_device_from_lcu(struct dasd_device *);
+int dasd_alias_add_device(struct dasd_device *);
+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 *);
+
#endif /* DASD_ECKD_H */
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index 0c081a664ee..6e53ab606e9 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device,
unsigned long flags;
struct eerbuffer *eerb;
- snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+ snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
if (snss_rc)
data_size = 0;
else
@@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device)
set_bit(DASD_FLAG_EER_SNSS, &device->flags);
return;
}
+ /* cdev is already locked, can't use dasd_add_request_head */
clear_bit(DASD_FLAG_EER_SNSS, &device->flags);
cqr->status = DASD_CQR_QUEUED;
- list_add(&cqr->list, &device->ccw_queue);
- dasd_schedule_bh(device);
+ list_add(&cqr->devlist, &device->ccw_queue);
+ dasd_schedule_device_bh(device);
}
/*
@@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *device)
*/
static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data)
{
- struct dasd_device *device = cqr->device;
+ struct dasd_device *device = cqr->startdev;
unsigned long flags;
dasd_eer_write(device, cqr, DASD_EER_STATECHANGE);
@@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device)
if (!cqr)
return -ENOMEM;
- cqr->device = device;
+ cqr->startdev = device;
cqr->retries = 255;
cqr->expires = 10 * HZ;
clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c
index caa5d91420f..8f10000851a 100644
--- a/drivers/s390/block/dasd_erp.c
+++ b/drivers/s390/block/dasd_erp.c
@@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
if (cqr == NULL)
return ERR_PTR(-ENOMEM);
memset(cqr, 0, sizeof(struct dasd_ccw_req));
+ INIT_LIST_HEAD(&cqr->devlist);
+ INIT_LIST_HEAD(&cqr->blocklist);
data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L);
cqr->cpaddr = NULL;
if (cplength > 0) {
@@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
}
void
-dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
+dasd_free_erp_request(struct dasd_ccw_req *cqr, struct dasd_device * device)
{
unsigned long flags;
@@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
* dasd_default_erp_action just retries the current cqr
*/
struct dasd_ccw_req *
-dasd_default_erp_action(struct dasd_ccw_req * cqr)
+dasd_default_erp_action(struct dasd_ccw_req *cqr)
{
struct dasd_device *device;
- device = cqr->device;
+ device = cqr->startdev;
/* just retry - there is nothing to save ... I got no sense data.... */
if (cqr->retries > 0) {
@@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
"default ERP called (%i retries left)",
cqr->retries);
cqr->lpm = LPM_ANYPATH;
- cqr->status = DASD_CQR_QUEUED;
+ cqr->status = DASD_CQR_FILLED;
} else {
DEV_MESSAGE (KERN_WARNING, device, "%s",
"default ERP called (NO retry left)");
cqr->status = DASD_CQR_FAILED;
- cqr->stopclk = get_clock ();
+ cqr->stopclk = get_clock();
}
return cqr;
} /* end dasd_default_erp_action */
@@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
* RETURN VALUES
* cqr pointer to the original CQR
*/
-struct dasd_ccw_req *
-dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
+struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
{
- struct dasd_device *device;
int success;
BUG_ON(cqr->refers == NULL || cqr->function == NULL);
- device = cqr->device;
success = cqr->status == DASD_CQR_DONE;
/* free all ERPs - but NOT the original cqr */
@@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
struct dasd_ccw_req *refers;
refers = cqr->refers;
- /* remove the request from the device queue */
- list_del(&cqr->list);
+ /* remove the request from the block queue */
+ list_del(&cqr->blocklist);
/* free the finished erp request */
- dasd_free_erp_request(cqr, device);
+ dasd_free_erp_request(cqr, cqr->memdev);
cqr = refers;
}
@@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb)
{
struct dasd_device *device;
- device = cqr->device;
+ device = cqr->startdev;
/* dump sense data */
if (device->discipline && device->discipline->dump_sense)
device->discipline->dump_sense(device, cqr, irb);
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 1d95822e0b8..d13ea05089a 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -117,6 +117,7 @@ locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw,
static int
dasd_fba_check_characteristics(struct dasd_device *device)
{
+ struct dasd_block *block;
struct dasd_fba_private *private;
struct ccw_device *cdev = device->cdev;
void *rdc_data;
@@ -133,6 +134,16 @@ dasd_fba_check_characteristics(struct dasd_device *device)
}
device->private = (void *) private;
}
+ block = dasd_alloc_block();
+ if (IS_ERR(block)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "could not allocate dasd block structure");
+ kfree(device->private);
+ return PTR_ERR(block);
+ }
+ device->block = block;
+ block->base = device;
+
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32);
@@ -155,60 +166,37 @@ dasd_fba_check_characteristics(struct dasd_device *device)
return 0;
}
-static int
-dasd_fba_do_analysis(struct dasd_device *device)
+static int dasd_fba_do_analysis(struct dasd_block *block)
{
struct dasd_fba_private *private;
int sb, rc;
- private = (struct dasd_fba_private *) device->private;
+ private = (struct dasd_fba_private *) block->base->private;
rc = dasd_check_blocksize(private->rdc_data.blk_size);
if (rc) {
- DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d",
+ DEV_MESSAGE(KERN_INFO, block->base, "unknown blocksize %d",
private->rdc_data.blk_size);
return rc;
}
- device->blocks = private->rdc_data.blk_bdsa;
- device->bp_block = private->rdc_data.blk_size;
- device->s2b_shift = 0; /* bits to shift 512 to get a block */
+ block->blocks = private->rdc_data.blk_bdsa;
+ block->bp_block = private->rdc_data.blk_size;
+ block->s2b_shift = 0; /* bits to shift 512 to get a block */
for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1)
- device->s2b_shift++;
+ block->s2b_shift++;
return 0;
}
-static int
-dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+static int dasd_fba_fill_geometry(struct dasd_block *block,
+ struct hd_geometry *geo)
{
- if (dasd_check_blocksize(device->bp_block) != 0)
+ if (dasd_check_blocksize(block->bp_block) != 0)
return -EINVAL;
- geo->cylinders = (device->blocks << device->s2b_shift) >> 10;
+ geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
geo->heads = 16;
- geo->sectors = 128 >> device->s2b_shift;
+ geo->sectors = 128 >> block->s2b_shift;
return 0;
}
-static dasd_era_t
-dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
-{
- struct dasd_device *device;
- struct ccw_device *cdev;
-
- device = (struct dasd_device *) cqr->device;
- if (irb->scsw.cstat == 0x00 &&
- irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
- return dasd_era_none;
-
- cdev = device->cdev;
- switch (cdev->id.dev_type) {
- case 0x3370:
- return dasd_3370_erp_examine(cqr, irb);
- case 0x9336:
- return dasd_9336_erp_examine(cqr, irb);
- default:
- return dasd_era_recover;
- }
-}
-
static dasd_erp_fn_t
dasd_fba_erp_action(struct dasd_ccw_req * cqr)
{
@@ -221,13 +209,34 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr)
if (cqr->function == dasd_default_erp_action)
return dasd_default_erp_postaction;
- DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p",
+ DEV_MESSAGE(KERN_WARNING, cqr->startdev, "unknown ERP action %p",
cqr->function);
return NULL;
}
-static struct dasd_ccw_req *
-dasd_fba_build_cp(struct dasd_device * device, struct request *req)
+static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device,
+ struct irb *irb)
+{
+ char mask;
+
+ /* first of all check for state change pending interrupt */
+ mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
+ if ((irb->scsw.dstat & mask) == mask) {
+ dasd_generic_handle_state_change(device);
+ return;
+ }
+
+ /* check for unsolicited interrupts */
+ DEV_MESSAGE(KERN_DEBUG, device, "%s",
+ "unsolicited interrupt received");
+ device->discipline->dump_sense(device, NULL, irb);
+ dasd_schedule_device_bh(device);
+ return;
+};
+
+static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
+ struct dasd_block *block,
+ struct request *req)
{
struct dasd_fba_private *private;
unsigned long *idaws;
@@ -242,17 +251,17 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
unsigned int blksize, off;
unsigned char cmd;
- private = (struct dasd_fba_private *) device->private;
+ private = (struct dasd_fba_private *) block->base->private;
if (rq_data_dir(req) == READ) {
cmd = DASD_FBA_CCW_READ;
} else if (rq_data_dir(req) == WRITE) {
cmd = DASD_FBA_CCW_WRITE;
} else
return ERR_PTR(-EINVAL);
- blksize = device->bp_block;
+ blksize = block->bp_block;
/* Calculate record id of first and last block. */
- first_rec = req->sector >> device->s2b_shift;
- last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+ first_rec = req->sector >> block->s2b_shift;
+ last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
/* Check struct bio and count the number of blocks for the request. */
count = 0;
cidaw = 0;
@@ -260,7 +269,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
if (bv->bv_len & (blksize - 1))
/* Fba can only do full blocks. */
return ERR_PTR(-EINVAL);
- count += bv->bv_len >> (device->s2b_shift + 9);
+ count += bv->bv_len >> (block->s2b_shift + 9);
#if defined(CONFIG_64BIT)
if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
cidaw += bv->bv_len / blksize;
@@ -284,13 +293,13 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
}
/* Allocate the ccw request. */
cqr = dasd_smalloc_request(dasd_fba_discipline.name,
- cplength, datasize, device);
+ cplength, datasize, memdev);
if (IS_ERR(cqr))
return cqr;
ccw = cqr->cpaddr;
/* First ccw is define extent. */
define_extent(ccw++, cqr->data, rq_data_dir(req),
- device->bp_block, req->sector, req->nr_sectors);
+ block->bp_block, req->sector, req->nr_sectors);
/* Build locate_record + read/write ccws. */
idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data));
LO_data = (struct LO_fba_data *) (idaws + cidaw);
@@ -326,7 +335,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
ccw[-1].flags |= CCW_FLAG_CC;
}
ccw->cmd_code = cmd;
- ccw->count = device->bp_block;
+ ccw->count = block->bp_block;
if (idal_is_needed(dst, blksize)) {
ccw->cda = (__u32)(addr_t) idaws;
ccw->flags = CCW_FLAG_IDA;
@@ -342,7 +351,9 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
}
if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
- cqr->device = device;
+ cqr->startdev = memdev;
+ cqr->memdev = memdev;
+ cqr->block = block;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
cqr->retries = 32;
cqr->buildclk = get_clock();
@@ -363,8 +374,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)
if (!dasd_page_cache)
goto out;
- private = (struct dasd_fba_private *) cqr->device->private;
- blksize = cqr->device->bp_block;
+ private = (struct dasd_fba_private *) cqr->block->base->private;
+ blksize = cqr->block->bp_block;
ccw = cqr->cpaddr;
/* Skip over define extent & locate record. */
ccw++;
@@ -394,10 +405,15 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)
}
out:
status = cqr->status == DASD_CQR_DONE;
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
return status;
}
+static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr)
+{
+ cqr->status = DASD_CQR_FILLED;
+};
+
static int
dasd_fba_fill_info(struct dasd_device * device,
struct dasd_information2_t * info)
@@ -546,9 +562,10 @@ static struct dasd_discipline dasd_fba_discipline = {
.fill_geometry = dasd_fba_fill_geometry,
.start_IO = dasd_start_IO,
.term_IO = dasd_term_IO,
- .examine_error = dasd_fba_examine_error,
+ .handle_terminated_request = dasd_fba_handle_terminated_request,
.erp_action = dasd_fba_erp_action,
.erp_postaction = dasd_fba_erp_postaction,
+ .handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt,
.build_cp = dasd_fba_build_cp,
.free_cp = dasd_fba_free_cp,
.dump_sense = dasd_fba_dump_sense,
diff --git a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c
index 47ba4462708..aee6565aaf9 100644
--- a/drivers/s390/block/dasd_genhd.c
+++ b/drivers/s390/block/dasd_genhd.c
@@ -25,14 +25,15 @@
/*
* Allocate and register gendisk structure for device.
*/
-int
-dasd_gendisk_alloc(struct dasd_device *device)
+int dasd_gendisk_alloc(struct dasd_block *block)
{
struct gendisk *gdp;
+ struct dasd_device *base;
int len;
/* Make sure the minor for this device exists. */
- if (device->devindex >= DASD_PER_MAJOR)
+ base = block->base;
+ if (base->devindex >= DASD_PER_MAJOR)
return -EBUSY;
gdp = alloc_disk(1 << DASD_PARTN_BITS);
@@ -41,9 +42,9 @@ dasd_gendisk_alloc(struct dasd_device *device)
/* Initialize gendisk structure. */
gdp->major = DASD_MAJOR;
- gdp->first_minor = device->devindex << DASD_PARTN_BITS;
+ gdp->first_minor = base->devindex << DASD_PARTN_BITS;
gdp->fops = &dasd_device_operations;
- gdp->driverfs_dev = &device->cdev->dev;
+ gdp->driverfs_dev = &base->cdev->dev;
/*
* Set device name.
@@ -53,53 +54,51 @@ dasd_gendisk_alloc(struct dasd_device *device)
* dasdaaaa - dasdzzzz : 456976 devices, added up = 475252
*/
len = sprintf(gdp->disk_name, "dasd");
- if (device->devindex > 25) {
- if (device->devindex > 701) {
- if (device->devindex > 18277)
+ if (base->devindex > 25) {
+ if (base->devindex > 701) {
+ if (base->devindex > 18277)
len += sprintf(gdp->disk_name + len, "%c",
- 'a'+(((device->devindex-18278)
+ 'a'+(((base->devindex-18278)
/17576)%26));
len += sprintf(gdp->disk_name + len, "%c",
- 'a'+(((device->devindex-702)/676)%26));
+ 'a'+(((base->devindex-702)/676)%26));
}
len += sprintf(gdp->disk_name + len, "%c",
- 'a'+(((device->devindex-26)/26)%26));
+ 'a'+(((base->devindex-26)/26)%26));
}
- len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26));
+ len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26));
- if (device->features & DASD_FEATURE_READONLY)
+ if (block->base->features & DASD_FEATURE_READONLY)
set_disk_ro(gdp, 1);
- gdp->private_data = device;
- gdp->queue = device->request_queue;
- device->gdp = gdp;
- set_capacity(device->gdp, 0);
- add_disk(device->gdp);
+ gdp->private_data = block;
+ gdp->queue = block->request_queue;
+ block->gdp = gdp;
+ set_capacity(block->gdp, 0);
+ add_disk(block->gdp);
return 0;
}
/*
* Unregister and free gendisk structure for device.
*/
-void
-dasd_gendisk_free(struct dasd_device *device)
+void dasd_gendisk_free(struct dasd_block *block)
{
- if (device->gdp) {
- del_gendisk(device->gdp);
- device->gdp->queue = NULL;
- put_disk(device->gdp);
- device->gdp = NULL;
+ if (block->gdp) {
+ del_gendisk(block->gdp);
+ block->gdp->queue = NULL;
+ put_disk(block->gdp);
+ block->gdp = NULL;
}
}
/*
* Trigger a partition detection.
*/
-int
-dasd_scan_partitions(struct dasd_device * device)
+int dasd_scan_partitions(struct dasd_block *block)
{
struct block_device *bdev;
- bdev = bdget_disk(device->gdp, 0);
+ bdev = bdget_disk(block->gdp, 0);
if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0)
return -ENODEV;
/*
@@ -117,7 +116,7 @@ dasd_scan_partitions(struct dasd_device * device)
* is why the assignment to device->bdev is done AFTER
* the BLKRRPART ioctl.
*/
- device->bdev = bdev;
+ block->bdev = bdev;
return 0;
}
@@ -125,8 +124,7 @@ dasd_scan_partitions(struct dasd_device * device)
* Remove all inodes in the system for a device, delete the
* partitions and make device unusable by setting its size to zero.
*/
-void
-dasd_destroy_partitions(struct dasd_device * device)
+void dasd_destroy_partitions(struct dasd_block *block)
{
/* The two structs have 168/176 byte on 31/64 bit. */
struct blkpg_partition bpart;
@@ -137,8 +135,8 @@ dasd_destroy_partitions(struct dasd_device * device)
* Get the bdev pointer from the device structure and clear
* device->bdev to lower the offline open_count limit again.
*/
- bdev = device->bdev;
- device->bdev = NULL;
+ bdev = block->bdev;
+ block->bdev = NULL;
/*
* See fs/partition/check.c:delete_partition
@@ -149,17 +147,16 @@ dasd_destroy_partitions(struct dasd_device * device)
memset(&barg, 0, sizeof(struct blkpg_ioctl_arg));
barg.data = (void __force __user *) &bpart;
barg.op = BLKPG_DEL_PARTITION;
- for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
+ for (bpart.pno = block->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg);
- invalidate_partition(device->gdp, 0);
+ invalidate_partition(block->gdp, 0);
/* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
blkdev_put(bdev);
- set_capacity(device->gdp, 0);
+ set_capacity(block->gdp, 0);
}
-int
-dasd_gendisk_init(void)
+int dasd_gendisk_init(void)
{
int rc;
@@ -174,8 +171,7 @@ dasd_gendisk_init(void)
return 0;
}
-void
-dasd_gendisk_exit(void)
+void dasd_gendisk_exit(void)
{
unregister_blkdev(DASD_MAJOR, "dasd");
}
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index d427daeef51..44b2984dfbe 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -64,13 +64,7 @@
* SECTION: Type definitions
*/
struct dasd_device;
-
-typedef enum {
- dasd_era_fatal = -1, /* no chance to recover */
- dasd_era_none = 0, /* don't recover, everything alright */
- dasd_era_msg = 1, /* don't recover, just report... */
- dasd_era_recover = 2 /* recovery action recommended */
-} dasd_era_t;
+struct dasd_block;
/* BIT DEFINITIONS FOR SENSE DATA */
#define DASD_SENSE_BIT_0 0x80
@@ -151,19 +145,22 @@ do { \
struct dasd_ccw_req {
unsigned int magic; /* Eye catcher */
- struct list_head list; /* list_head for request queueing. */
+ struct list_head devlist; /* for dasd_device request queue */
+ struct list_head blocklist; /* for dasd_block request queue */
/* Where to execute what... */
- struct dasd_device *device; /* device the request is for */
+ struct dasd_block *block; /* the originating block device */
+ struct dasd_device *memdev; /* the device used to allocate this */
+ struct dasd_device *startdev; /* device the request is started on */
struct ccw1 *cpaddr; /* address of channel program */
- char status; /* status of this request */
+ char status; /* status of this request */
short retries; /* A retry counter */
unsigned long flags; /* flags of this request */
/* ... and how */
unsigned long starttime; /* jiffies time of request start */
int expires; /* expiration period in jiffies */
- char lpm; /* logical path mask */
+ char lpm; /* logical path mask */
void *data; /* pointer to data area */
/* these are important for recovering erroneous requests */
@@ -178,20 +175,27 @@ struct dasd_ccw_req {
unsigned long long endclk; /* TOD-clock of request termination */
/* Callback that is called after reaching final status. */
- void (*callback)(struct dasd_ccw_req *, void *data);
- void *callback_data;
+ void (*callback)(struct dasd_ccw_req *, void *data);
+ void *callback_data;
};
/*
* dasd_ccw_req -> status can be:
*/
-#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */
-#define DASD_CQR_QUEUED 0x01 /* request is queued to be processed */
-#define DASD_CQR_IN_IO 0x02 /* request is currently in IO */
-#define DASD_CQR_DONE 0x03 /* request is completed successfully */
-#define DASD_CQR_ERROR 0x04 /* request is completed with error */
-#define DASD_CQR_FAILED 0x05 /* request is finally failed */
-#define DASD_CQR_CLEAR 0x06 /* request is clear pending */
+#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */
+#define DASD_CQR_DONE 0x01 /* request is completed successfully */
+#define DASD_CQR_NEED_ERP 0x02 /* request needs recovery action */
+#define DASD_CQR_IN_ERP 0x03 /* request is in recovery */
+#define DASD_CQR_FAILED 0x04 /* request is finally failed */
+#define DASD_CQR_TERMINATED 0x05 /* request was stopped by driver */
+
+#define DASD_CQR_QUEUED 0x80 /* request is queued to be processed */
+#define DASD_CQR_IN_IO 0x81 /* request is currently in IO */
+#define DASD_CQR_ERROR 0x82 /* request is completed with error */
+#define DASD_CQR_CLEAR_PENDING 0x83 /* request is clear pending */
+#define DASD_CQR_CLEARED 0x84 /* request was cleared */
+#define DASD_CQR_SUCCESS 0x85 /* request was successfull */
+
/* per dasd_ccw_req flags */
#define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */
@@ -214,52 +218,71 @@ struct dasd_discipline {
struct list_head list; /* used for list of disciplines */
- /*
- * Device recognition functions. check_device is used to verify
- * the sense data and the information returned by read device
- * characteristics. It returns 0 if the discipline can be used
- * for the device in question.
- * do_analysis is used in the step from device state "basic" to
- * state "accept". It returns 0 if the device can be made ready,
- * it returns -EMEDIUMTYPE if the device can't be made ready or
- * -EAGAIN if do_analysis started a ccw that needs to complete
- * before the analysis may be repeated.
- */
- int (*check_device)(struct dasd_device *);
- int (*do_analysis) (struct dasd_device *);
-
- /*
- * Device operation functions. build_cp creates a ccw chain for
- * a block device request, start_io starts the request and
- * term_IO cancels it (e.g. in case of a timeout). format_device
- * returns a ccw chain to be used to format the device.
- */
+ /*
+ * Device recognition functions. check_device is used to verify
+ * the sense data and the information returned by read device
+ * characteristics. It returns 0 if the discipline can be used
+ * for the device in question. uncheck_device is called during
+ * device shutdown to deregister a device from its discipline.
+ */
+ int (*check_device) (struct dasd_device *);
+ void (*uncheck_device) (struct dasd_device *);
+
+ /*
+ * do_analysis is used in the step from device state "basic" to
+ * state "accept". It returns 0 if the device can be made ready,
+ * it returns -EMEDIUMTYPE if the device can't be made ready or
+ * -EAGAIN if do_analysis started a ccw that needs to complete
+ * before the analysis may be repeated.
+ */
+ int (*do_analysis) (struct dasd_block *);
+
+ /*
+ * Last things to do when a device is set online, and first things
+ * when it is set offline.
+ */
+ int (*ready_to_online) (struct dasd_device *);
+ int (*online_to_ready) (struct dasd_device *);
+
+ /*
+ * Device operation functions. build_cp creates a ccw chain for
+ * a block device request, start_io starts the request and
+ * term_IO cancels it (e.g. in case of a timeout). format_device
+ * returns a ccw chain to be used to format the device.
+ * handle_terminated_request allows to examine a cqr and prepare
+ * it for retry.
+ */
struct dasd_ccw_req *(*build_cp) (struct dasd_device *,
+ struct dasd_block *,
struct request *);
int (*start_IO) (struct dasd_ccw_req *);
int (*term_IO) (struct dasd_ccw_req *);
+ void (*handle_terminated_request) (struct dasd_ccw_req *);
struct dasd_ccw_req *(*format_device) (struct dasd_device *,
struct format_data_t *);
int (*free_cp) (struct dasd_ccw_req *, struct request *);
- /*
- * Error recovery functions. examine_error() returns a value that
- * indicates what to do for an error condition. If examine_error()
+
+ /*
+ * Error recovery functions. examine_error() returns a value that
+ * indicates what to do for an error condition. If examine_error()
* returns 'dasd_era_recover' erp_action() is called to create a
- * special error recovery ccw. erp_postaction() is called after
- * an error recovery ccw has finished its execution. dump_sense
- * is called for every error condition to print the sense data
- * to the console.
- */
- dasd_era_t(*examine_error) (struct dasd_ccw_req *, struct irb *);
+ * special error recovery ccw. erp_postaction() is called after
+ * an error recovery ccw has finished its execution. dump_sense
+ * is called for every error condition to print the sense data
+ * to the console.
+ */
dasd_erp_fn_t(*erp_action) (struct dasd_ccw_req *);
dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *);
void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *,
struct irb *);
+ void (*handle_unsolicited_interrupt) (struct dasd_device *,
+ struct irb *);
+
/* i/o control functions. */
- int (*fill_geometry) (struct dasd_device *, struct hd_geometry *);
+ int (*fill_geometry) (struct dasd_block *, struct hd_geometry *);
int (*fill_info) (struct dasd_device *, struct dasd_information2_t *);
- int (*ioctl) (struct dasd_device *, unsigned int, void __user *);
+ int (*ioctl) (struct dasd_block *, unsigned int, void __user *);
};
extern struct dasd_discipline *dasd_diag_discipline_pointer;
@@ -267,12 +290,18 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer;
/*
* Unique identifier for dasd device.
*/
+#define UA_NOT_CONFIGURED 0x00
+#define UA_BASE_DEVICE 0x01
+#define UA_BASE_PAV_ALIAS 0x02
+#define UA_HYPER_PAV_ALIAS 0x03
+
struct dasd_uid {
- __u8 alias;
+ __u8 type;
char vendor[4];
char serial[15];
__u16 ssid;
- __u8 unit_addr;
+ __u8 real_unit_addr;
+ __u8 base_unit_addr;
};
/*
@@ -293,14 +322,9 @@ struct dasd_uid {
struct dasd_device {
/* Block device stuff. */
- struct gendisk *gdp;
- struct request_queue *request_queue;
- spinlock_t request_queue_lock;
- struct block_device *bdev;
+ struct dasd_block *block;
+
unsigned int devindex;
- unsigned long blocks; /* size of volume in blocks */
- unsigned int bp_block; /* bytes per block */
- unsigned int s2b_shift; /* log2 (bp_block/512) */
unsigned long flags; /* per device flags */
unsigned short features; /* copy of devmap-features (read-only!) */
@@ -316,9 +340,8 @@ struct dasd_device {
int state, target;
int stopped; /* device (ccw_device_start) was stopped */
- /* Open and reference count. */
+ /* reference count. */
atomic_t ref_count;
- atomic_t open_count;
/* ccw queue and memory for static ccw/erp buffers. */
struct list_head ccw_queue;
@@ -337,20 +360,45 @@ struct dasd_device {
struct ccw_device *cdev;
+ /* hook for alias management */
+ struct list_head alias_list;
+};
+
+struct dasd_block {
+ /* Block device stuff. */
+ struct gendisk *gdp;
+ struct request_queue *request_queue;
+ spinlock_t request_queue_lock;
+ struct block_device *bdev;
+ atomic_t open_count;
+
+ unsigned long blocks; /* size of volume in blocks */
+ unsigned int bp_block; /* bytes per block */
+ unsigned int s2b_shift; /* log2 (bp_block/512) */
+
+ struct dasd_device *base;
+ struct list_head ccw_queue;
+ spinlock_t queue_lock;
+
+ atomic_t tasklet_scheduled;
+ struct tasklet_struct tasklet;
+ struct timer_list timer;
+
#ifdef CONFIG_DASD_PROFILE
struct dasd_profile_info_t profile;
#endif
};
+
+
/* reasons why device (ccw_device_start) was stopped */
#define DASD_STOPPED_NOT_ACC 1 /* not accessible */
#define DASD_STOPPED_QUIESCE 2 /* Quiesced */
#define DASD_STOPPED_PENDING 4 /* long busy */
#define DASD_STOPPED_DC_WAIT 8 /* disconnected, wait */
-#define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */
+#define DASD_STOPPED_SU 16 /* summary unit check handling */
/* per device flags */
-#define DASD_FLAG_DSC_ERROR 2 /* return -EIO when disconnected */
#define DASD_FLAG_OFFLINE 3 /* device is in offline processing */
#define DASD_FLAG_EER_SNSS 4 /* A SNSS is required */
#define DASD_FLAG_EER_IN_USE 5 /* A SNSS request is running */
@@ -489,6 +537,9 @@ dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device)
struct dasd_device *dasd_alloc_device(void);
void dasd_free_device(struct dasd_device *);
+struct dasd_block *dasd_alloc_block(void);
+void dasd_free_block(struct dasd_block *);
+
void dasd_enable_device(struct dasd_device *);
void dasd_set_target_state(struct dasd_device *, int);
void dasd_kick_device(struct dasd_device *);
@@ -497,18 +548,23 @@ void dasd_add_request_head(struct dasd_ccw_req *);
void dasd_add_request_tail(struct dasd_ccw_req *);
int dasd_start_IO(struct dasd_ccw_req *);
int dasd_term_IO(struct dasd_ccw_req *);
-void dasd_schedule_bh(struct dasd_device *);
+void dasd_schedule_device_bh(struct dasd_device *);
+void dasd_schedule_block_bh(struct dasd_block *);
int dasd_sleep_on(struct dasd_ccw_req *);
int dasd_sleep_on_immediatly(struct dasd_ccw_req *);
int dasd_sleep_on_interruptible(struct dasd_ccw_req *);
-void dasd_set_timer(struct dasd_device *, int);
-void dasd_clear_timer(struct dasd_device *);
+void dasd_device_set_timer(struct dasd_device *, int);
+void dasd_device_clear_timer(struct dasd_device *);
+void dasd_block_set_timer(struct dasd_block *, int);
+void dasd_block_clear_timer(struct dasd_block *);
int dasd_cancel_req(struct dasd_ccw_req *);
+int dasd_flush_device_queue(struct dasd_device *);
int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *);
void dasd_generic_remove (struct ccw_device *cdev);
int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
int dasd_generic_set_offline (struct ccw_device *cdev);
int dasd_generic_notify(struct ccw_device *, int);
+void dasd_generic_handle_state_change(struct dasd_device *);
int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
@@ -542,10 +598,10 @@ int dasd_busid_known(char *);
/* externals in dasd_gendisk.c */
int dasd_gendisk_init(void);
void dasd_gendisk_exit(void);
-int dasd_gendisk_alloc(struct dasd_device *);
-void dasd_gendisk_free(struct dasd_device *);
-int dasd_scan_partitions(struct dasd_device *);
-void dasd_destroy_partitions(struct dasd_device *);
+int dasd_gendisk_alloc(struct dasd_block *);
+void dasd_gendisk_free(struct dasd_block *);
+int dasd_scan_partitions(struct dasd_block *);
+void dasd_destroy_partitions(struct dasd_block *);
/* externals in dasd_ioctl.c */
int dasd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
@@ -563,20 +619,9 @@ struct dasd_ccw_req *dasd_alloc_erp_request(char *, int, int,
void dasd_free_erp_request(struct dasd_ccw_req *, struct dasd_device *);
void dasd_log_sense(struct dasd_ccw_req *, struct irb *);
-/* externals in dasd_3370_erp.c */
-dasd_era_t dasd_3370_erp_examine(struct dasd_ccw_req *, struct irb *);
-
/* externals in dasd_3990_erp.c */
-dasd_era_t dasd_3990_erp_examine(struct dasd_ccw_req *, struct irb *);
struct dasd_ccw_req *dasd_3990_erp_action(struct dasd_ccw_req *);
-/* externals in dasd_9336_erp.c */
-dasd_era_t dasd_9336_erp_examine(struct dasd_ccw_req *, struct irb *);
-
-/* externals in dasd_9336_erp.c */
-dasd_era_t dasd_9343_erp_examine(struct dasd_ccw_req *, struct irb *);
-struct dasd_ccw_req *dasd_9343_erp_action(struct dasd_ccw_req *);
-
/* externals in dasd_eer.c */
#ifdef CONFIG_DASD_EER
int dasd_eer_init(void);
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index 672eb0a3dd0..91a64630cb0 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -38,15 +38,15 @@ dasd_ioctl_api_version(void __user *argp)
static int
dasd_ioctl_enable(struct block_device *bdev)
{
- struct dasd_device *device = bdev->bd_disk->private_data;
+ struct dasd_block *block = bdev->bd_disk->private_data;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
- dasd_enable_device(device);
+ dasd_enable_device(block->base);
/* Formatting the dasd device can change the capacity. */
mutex_lock(&bdev->bd_mutex);
- i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->gdp) << 9);
+ i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9);
mutex_unlock(&bdev->bd_mutex);
return 0;
}
@@ -58,7 +58,7 @@ dasd_ioctl_enable(struct block_device *bdev)
static int
dasd_ioctl_disable(struct block_device *bdev)
{
- struct dasd_device *device = bdev->bd_disk->private_data;
+ struct dasd_block *block = bdev->bd_disk->private_data;
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
@@ -71,7 +71,7 @@ dasd_ioctl_disable(struct block_device *bdev)
* using the BIODASDFMT ioctl. Therefore the correct state for the
* device is DASD_STATE_BASIC that allows to do basic i/o.
*/
- dasd_set_target_state(device, DASD_STATE_BASIC);
+ dasd_set_target_state(block->base, DASD_STATE_BASIC);
/*
* Set i_size to zero, since read, write, etc. check against this
* value.
@@ -85,19 +85,19 @@ dasd_ioctl_disable(struct block_device *bdev)
/*
* Quiesce device.
*/
-static int
-dasd_ioctl_quiesce(struct dasd_device *device)
+static int dasd_ioctl_quiesce(struct dasd_block *block)
{
unsigned long flags;
+ struct dasd_device *base;
+ base = block->base;
if (!capable (CAP_SYS_ADMIN))
return -EACCES;
- DEV_MESSAGE (KERN_DEBUG, device, "%s",
- "Quiesce IO on device");
- spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped |= DASD_STOPPED_QUIESCE;
- spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+ DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device");
+ spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+ base->stopped |= DASD_STOPPED_QUIESCE;
+ spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
return 0;
}
@@ -105,22 +105,21 @@ dasd_ioctl_quiesce(struct dasd_device *device)
/*
* Quiesce device.
*/
-static int
-dasd_ioctl_resume(struct dasd_device *device)
+static int dasd_ioctl_resume(struct dasd_block *block)
{
unsigned long flags;
+ struct dasd_device *base;
+ base = block->base;
if (!capable (CAP_SYS_ADMIN))
return -EACCES;
- DEV_MESSAGE (KERN_DEBUG, device, "%s",
- "resume IO on device");
-
- spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- device->stopped &= ~DASD_STOPPED_QUIESCE;
- spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+ DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device");
+ spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+ base->stopped &= ~DASD_STOPPED_QUIESCE;
+ spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
- dasd_schedule_bh (device);
+ dasd_schedule_block_bh(block);
return 0;
}
@@ -130,22 +129,23 @@ dasd_ioctl_resume(struct dasd_device *device)
* commands to format a single unit of the device. In terms of the ECKD
* devices this means CCWs are generated to format a single track.
*/
-static int
-dasd_format(struct dasd_device * device, struct format_data_t * fdata)
+static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
{
struct dasd_ccw_req *cqr;
+ struct dasd_device *base;
int rc;
- if (device->discipline->format_device == NULL)
+ base = block->base;
+ if (base->discipline->format_device == NULL)
return -EPERM;
- if (device->state != DASD_STATE_BASIC) {
- DEV_MESSAGE(KERN_WARNING, device, "%s",
+ if (base->state != DASD_STATE_BASIC) {
+ DEV_MESSAGE(KERN_WARNING, base, "%s",
"dasd_format: device is not disabled! ");
return -EBUSY;
}
- DBF_DEV_EVENT(DBF_NOTICE, device,
+ DBF_DEV_EVENT(DBF_NOTICE, base,
"formatting units %d to %d (%d B blocks) flags %d",
fdata->start_unit,
fdata->stop_unit, fdata->blksize, fdata->intensity);
@@ -156,20 +156,20 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata)
* enabling the device later.
*/
if (fdata->start_unit == 0) {
- struct block_device *bdev = bdget_disk(device->gdp, 0);
+ struct block_device *bdev = bdget_disk(block->gdp, 0);
bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
bdput(bdev);
}
while (fdata->start_unit <= fdata->stop_unit) {
- cqr = device->discipline->format_device(device, fdata);
+ cqr = base->discipline->format_device(base, fdata);
if (IS_ERR(cqr))
return PTR_ERR(cqr);
rc = dasd_sleep_on_interruptible(cqr);
- dasd_sfree_request(cqr, cqr->device);
+ dasd_sfree_request(cqr, cqr->memdev);
if (rc) {
if (rc != -ERESTARTSYS)
- DEV_MESSAGE(KERN_ERR, device,
+ DEV_MESSAGE(KERN_ERR, base,
" Formatting of unit %d failed "
"with rc = %d",
fdata->start_unit, rc);
@@ -186,7 +186,7 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata)
static int
dasd_ioctl_format(struct block_device *bdev, void __user *argp)
{
- struct dasd_device *device = bdev->bd_disk->private_data;
+ struct dasd_block *block = bdev->bd_disk->private_data;
struct format_data_t fdata;
if (!capable(CAP_SYS_ADMIN))
@@ -194,51 +194,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
if (!argp)
return -EINVAL;
- if (device->features & DASD_FEATURE_READONLY)
+ if (block->base->features & DASD_FEATURE_READONLY)
return -EROFS;
if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
return -EFAULT;
if (bdev != bdev->bd_contains) {
- DEV_MESSAGE(KERN_WARNING, device, "%s",
+ DEV_MESSAGE(KERN_WARNING, block->base, "%s",
"Cannot low-level format a partition");
return -EINVAL;
}
- return dasd_format(device, &fdata);
+ return dasd_format(block, &fdata);
}
#ifdef CONFIG_DASD_PROFILE
/*
* Reset device profile information
*/
-static int
-dasd_ioctl_reset_profile(struct dasd_device *device)
+static int dasd_ioctl_reset_profile(struct dasd_block *block)
{
- memset(&device->profile, 0, sizeof (struct dasd_profile_info_t));
+ memset(&block->profile, 0, sizeof(struct dasd_profile_info_t));
return 0;
}
/*
* Return device profile information
*/
-static int
-dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
+static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
{
if (dasd_profile_level == DASD_PROFILE_OFF)
return -EIO;
- if (copy_to_user(argp, &device->profile,
- sizeof (struct dasd_profile_info_t)))
+ if (copy_to_user(argp, &block->profile,
+ sizeof(struct dasd_profile_info_t)))
return -EFAULT;
return 0;
}
#else
-static int
-dasd_ioctl_reset_profile(struct dasd_device *device)
+static int dasd_ioctl_reset_profile(struct dasd_block *block)
{
return -ENOSYS;
}
-static int
-dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
+static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
{
return -ENOSYS;
}
@@ -247,87 +243,88 @@ dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
/*
* Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
*/
-static int
-dasd_ioctl_information(struct dasd_device *device,
- unsigned int cmd, void __user *argp)
+static int dasd_ioctl_information(struct dasd_block *block,
+ unsigned int cmd, void __user *argp)
{
struct dasd_information2_t *dasd_info;
unsigned long flags;
int rc;
+ struct dasd_device *base;
struct ccw_device *cdev;
struct ccw_dev_id dev_id;
- if (!device->discipline->fill_info)
+ base = block->base;
+ if (!base->discipline->fill_info)
return -EINVAL;
dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
if (dasd_info == NULL)
return -ENOMEM;
- rc = device->discipline->fill_info(device, dasd_info);
+ rc = base->discipline->fill_info(base, dasd_info);
if (rc) {
kfree(dasd_info);
return rc;
}
- cdev = device->cdev;
+ cdev = base->cdev;
ccw_device_get_id(cdev, &dev_id);
dasd_info->devno = dev_id.devno;
- dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev);
+ dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev);
dasd_info->cu_type = cdev->id.cu_type;
dasd_info->cu_model = cdev->id.cu_model;
dasd_info->dev_type = cdev->id.dev_type;
dasd_info->dev_model = cdev->id.dev_model;
- dasd_info->status = device->state;
+ dasd_info->status = base->state;
/*
* The open_count is increased for every opener, that includes
* the blkdev_get in dasd_scan_partitions.
* This must be hidden from user-space.
*/
- dasd_info->open_count = atomic_read(&device->open_count);
- if (!device->bdev)
+ dasd_info->open_count = atomic_read(&block->open_count);
+ if (!block->bdev)
dasd_info->open_count++;
/*
* check if device is really formatted
* LDL / CDL was returned by 'fill_info'
*/
- if ((device->state < DASD_STATE_READY) ||
- (dasd_check_blocksize(device->bp_block)))
+ if ((base->state < DASD_STATE_READY) ||
+ (dasd_check_blocksize(block->bp_block)))
dasd_info->format = DASD_FORMAT_NONE;
dasd_info->features |=
- ((device->features & DASD_FEATURE_READONLY) != 0);
+ ((base->features & DASD_FEATURE_READONLY) != 0);
- if (device->discipline)
- memcpy(dasd_info->type, device->discipline->name, 4);
+ if (base->discipline)
+ memcpy(dasd_info->type, base->discipline->name, 4);
else
memcpy(dasd_info->type, "none", 4);
- if (device->request_queue->request_fn) {
+ if (block->request_queue->request_fn) {
struct list_head *l;
#ifdef DASD_EXTENDED_PROFILING
{
struct list_head *l;
- spin_lock_irqsave(&device->lock, flags);
- list_for_each(l, &device->request_queue->queue_head)
+ spin_lock_irqsave(&block->lock, flags);
+ list_for_each(l, &block->request_queue->queue_head)
dasd_info->req_queue_len++;
- spin_unlock_irqrestore(&device->lock, flags);
+ spin_unlock_irqrestore(&block->lock, flags);
}
#endif /* DASD_EXTENDED_PROFILING */
- spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
- list_for_each(l, &device->ccw_queue)
+ spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+ list_for_each(l, &base->ccw_queue)
dasd_info->chanq_len++;
- spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+ spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
flags);
}
rc = 0;
if (copy_to_user(argp, dasd_info,
((cmd == (unsigned int) BIODASDINFO2) ?
- sizeof (struct dasd_information2_t) :
- sizeof (struct dasd_information_t))))
+ sizeof(struct dasd_information2_t) :
+ sizeof(struct dasd_information_t))))
rc = -EFAULT;
kfree(dasd_info);
return rc;
@@ -339,7 +336,7 @@ dasd_ioctl_information(struct dasd_device *device,
static int
dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
{
- struct dasd_device *device = bdev->bd_disk->private_data;
+ struct dasd_block *block = bdev->bd_disk->private_data;
int intval;
if (!capable(CAP_SYS_ADMIN))
@@ -351,11 +348,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
return -EFAULT;
set_disk_ro(bdev->bd_disk, intval);
- return dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval);
+ return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
}
-static int
-dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
+static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
unsigned long arg)
{
struct cmbdata __user *argp = (void __user *) arg;
@@ -363,7 +359,7 @@ dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
struct cmbdata data;
int ret;
- ret = cmf_readall(device->cdev, &data);
+ ret = cmf_readall(block->base->cdev, &data);
if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp))))
return -EFAULT;
return ret;
@@ -374,10 +370,10 @@ dasd_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct block_device *bdev = inode->i_bdev;
- struct dasd_device *device = bdev->bd_disk->private_data;
+ struct dasd_block *block = bdev->bd_disk->private_data;
void __user *argp = (void __user *)arg;
- if (!device)
+ if (!block)
return -ENODEV;
if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
@@ -391,33 +387,33 @@ dasd_ioctl(struct inode *inode, struct file *file,
case BIODASDENABLE:
return dasd_ioctl_enable(bdev);
case BIODASDQUIESCE:
- return dasd_ioctl_quiesce(device);
+ return dasd_ioctl_quiesce(block);
case BIODASDRESUME:
- return dasd_ioctl_resume(device);
+ return dasd_ioctl_resume(block);
case BIODASDFMT:
return dasd_ioctl_format(bdev, argp);
case BIODASDINFO:
- return dasd_ioctl_information(device, cmd, argp);
+ return dasd_ioctl_information(block, cmd, argp);
case BIODASDINFO2:
- return dasd_ioctl_information(device, cmd, argp);
+ return dasd_ioctl_information(block, cmd, argp);
case BIODASDPRRD:
- return dasd_ioctl_read_profile(device, argp);
+ return dasd_ioctl_read_profile(block, argp);
case BIODASDPRRST:
- return dasd_ioctl_reset_profile(device);
+ return dasd_ioctl_reset_profile(block);
case BLKROSET:
return dasd_ioctl_set_ro(bdev, argp);
case DASDAPIVER:
return dasd_ioctl_api_version(argp);
case BIODASDCMFENABLE:
- return enable_cmf(device->cdev);
+ return enable_cmf(block->base->cdev);
case BIODASDCMFDISABLE:
- return disable_cmf(device->cdev);
+ return disable_cmf(block->base->cdev);
case BIODASDREADALLCMB:
- return dasd_ioctl_readall_cmb(device, cmd, arg);
+ return dasd_ioctl_readall_cmb(block, cmd, arg);
default:
/* if the discipline has an ioctl method try it. */
- if (device->discipline->ioctl) {
- int rval = device->discipline->ioctl(device, cmd, argp);
+ if (block->base->discipline->ioctl) {
+ int rval = block->base->discipline->ioctl(block, cmd, argp);
if (rval != -ENOIOCTLCMD)
return rval;
}
diff --git a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c
index ac7e8ef504c..28a86f07004 100644
--- a/drivers/s390/block/dasd_proc.c
+++ b/drivers/s390/block/dasd_proc.c
@@ -54,11 +54,16 @@ static int
dasd_devices_show(struct seq_file *m, void *v)
{
struct dasd_device *device;
+ struct dasd_block *block;
char *substr;
device = dasd_device_from_devindex((unsigned long) v - 1);
if (IS_ERR(device))
return 0;
+ if (device->block)
+ block = device->block;
+ else
+ return 0;
/* Print device number. */
seq_printf(m, "%s", device->cdev->dev.bus_id);
/* Print discipline string. */
@@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v)
else
seq_printf(m, "(none)");
/* Print kdev. */
- if (device->gdp)
+ if (block->gdp)
seq_printf(m, " at (%3d:%6d)",
- device->gdp->major, device->gdp->first_minor);
+ block->gdp->major, block->gdp->first_minor);
else
seq_printf(m, " at (???:??????)");
/* Print device name. */
- if (device->gdp)
- seq_printf(m, " is %-8s", device->gdp->disk_name);
+ if (block->gdp)
+ seq_printf(m, " is %-8s", block->gdp->disk_name);
else
seq_printf(m, " is ????????");
/* Print devices features. */
@@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v)
case DASD_STATE_READY:
case DASD_STATE_ONLINE:
seq_printf(m, "active ");
- if (dasd_check_blocksize(device->bp_block))
+ if (dasd_check_blocksize(block->bp_block))
seq_printf(m, "n/f ");
else
seq_printf(m,
"at blocksize: %d, %ld blocks, %ld MB",
- device->bp_block, device->blocks,
- ((device->bp_block >> 9) *
- device->blocks) >> 11);
+ block->bp_block, block->blocks,
+ ((block->bp_block >> 9) *
+ block->blocks) >> 11);
break;
default:
seq_printf(m, "no stat");
@@ -137,7 +142,7 @@ static void dasd_devices_stop(struct seq_file *m, void *v)
{
}
-static struct seq_operations dasd_devices_seq_ops = {
+static const struct seq_operations dasd_devices_seq_ops = {
.start = dasd_devices_start,
.next = dasd_devices_next,
.stop = dasd_devices_stop,
diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c
index 15a5789b773..7779bfce1c3 100644
--- a/drivers/s390/block/dcssblk.c
+++ b/drivers/s390/block/dcssblk.c
@@ -82,7 +82,7 @@ struct dcssblk_dev_info {
struct request_queue *dcssblk_queue;
};
-static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
+static LIST_HEAD(dcssblk_devices);
static struct rw_semaphore dcssblk_devices_sem;
/*
diff --git a/drivers/s390/char/Makefile b/drivers/s390/char/Makefile
index 130de19916f..7e73e39a174 100644
--- a/drivers/s390/char/Makefile
+++ b/drivers/s390/char/Makefile
@@ -3,7 +3,7 @@
#
obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
- sclp_info.o sclp_config.o sclp_chp.o
+ sclp_cmd.o sclp_config.o sclp_cpi_sys.o
obj-$(CONFIG_TN3270) += raw3270.o
obj-$(CONFIG_TN3270_CONSOLE) += con3270.o
diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c
index 20442fbf934..a86c0534cd4 100644
--- a/drivers/s390/char/monwriter.c
+++ b/drivers/s390/char/monwriter.c
@@ -295,7 +295,7 @@ module_init(mon_init);
module_exit(mon_exit);
module_param_named(max_bufs, mon_max_bufs, int, 0644);
-MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers"
+MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers "
"that can be active at one time");
MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index 8d1c64a24de..0d98f1ff2ed 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -66,7 +66,7 @@ struct raw3270 {
static DEFINE_MUTEX(raw3270_mutex);
/* List of 3270 devices. */
-static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices);
+static LIST_HEAD(raw3270_devices);
/*
* Flag to indicate if the driver has been registered. Some operations
@@ -1210,7 +1210,7 @@ struct raw3270_notifier {
void (*notifier)(int, int);
};
-static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier);
+static LIST_HEAD(raw3270_notifier);
int raw3270_register_notifier(void (*notifier)(int, int))
{
diff --git a/drivers/s390/char/sclp.h b/drivers/s390/char/sclp.h
index c7318a12585..aa8186d18ae 100644
--- a/drivers/s390/char/sclp.h
+++ b/drivers/s390/char/sclp.h
@@ -56,8 +56,6 @@ typedef unsigned int sclp_cmdw_t;
#define SCLP_CMDW_READ_EVENT_DATA 0x00770005
#define SCLP_CMDW_WRITE_EVENT_DATA 0x00760005
#define SCLP_CMDW_WRITE_EVENT_MASK 0x00780005
-#define SCLP_CMDW_READ_SCP_INFO 0x00020001
-#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
#define GDS_ID_MDSMU 0x1310
#define GDS_ID_MDSROUTEINFO 0x1311
@@ -83,6 +81,8 @@ extern u64 sclp_facilities;
#define SCLP_HAS_CHP_INFO (sclp_facilities & 0x8000000000000000ULL)
#define SCLP_HAS_CHP_RECONFIG (sclp_facilities & 0x2000000000000000ULL)
+#define SCLP_HAS_CPU_INFO (sclp_facilities & 0x0800000000000000ULL)
+#define SCLP_HAS_CPU_RECONFIG (sclp_facilities & 0x0400000000000000ULL)
struct gds_subvector {
u8 length;
diff --git a/drivers/s390/char/sclp_chp.c b/drivers/s390/char/sclp_chp.c
deleted file mode 100644
index c68f5e7e63a..00000000000
--- a/drivers/s390/char/sclp_chp.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * drivers/s390/char/sclp_chp.c
- *
- * Copyright IBM Corp. 2007
- * Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
- */
-
-#include <linux/types.h>
-#include <linux/gfp.h>
-#include <linux/errno.h>
-#include <linux/completion.h>
-#include <asm/sclp.h>
-#include <asm/chpid.h>
-
-#include "sclp.h"
-
-#define TAG "sclp_chp: "
-
-#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH 0x000f0001
-#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH 0x000e0001
-#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION 0x00030001
-
-static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid)
-{
- return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8;
-}
-
-static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid)
-{
- return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8;
-}
-
-static void chp_callback(struct sclp_req *req, void *data)
-{
- struct completion *completion = data;
-
- complete(completion);
-}
-
-struct chp_cfg_sccb {
- struct sccb_header header;
- u8 ccm;
- u8 reserved[6];
- u8 cssid;
-} __attribute__((packed));
-
-struct chp_cfg_data {
- struct chp_cfg_sccb sccb;
- struct sclp_req req;
- struct completion completion;
-} __attribute__((packed));
-
-static int do_configure(sclp_cmdw_t cmd)
-{
- struct chp_cfg_data *data;
- int rc;
-
- if (!SCLP_HAS_CHP_RECONFIG)
- return -EOPNOTSUPP;
- /* Prepare sccb. */
- data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!data)
- return -ENOMEM;
- data->sccb.header.length = sizeof(struct chp_cfg_sccb);
- data->req.command = cmd;
- data->req.sccb = &(data->sccb);
- data->req.status = SCLP_REQ_FILLED;
- data->req.callback = chp_callback;
- data->req.callback_data = &(data->completion);
- init_completion(&data->completion);
-
- /* Perform sclp request. */
- rc = sclp_add_request(&(data->req));
- if (rc)
- goto out;
- wait_for_completion(&data->completion);
-
- /* Check response .*/
- if (data->req.status != SCLP_REQ_DONE) {
- printk(KERN_WARNING TAG "configure channel-path request failed "
- "(status=0x%02x)\n", data->req.status);
- rc = -EIO;
- goto out;
- }
- switch (data->sccb.header.response_code) {
- case 0x0020:
- case 0x0120:
- case 0x0440:
- case 0x0450:
- break;
- default:
- printk(KERN_WARNING TAG "configure channel-path failed "
- "(cmd=0x%08x, response=0x%04x)\n", cmd,
- data->sccb.header.response_code);
- rc = -EIO;
- break;
- }
-out:
- free_page((unsigned long) data);
-
- return rc;
-}
-
-/**
- * sclp_chp_configure - perform configure channel-path sclp command
- * @chpid: channel-path ID
- *
- * Perform configure channel-path command sclp command for specified chpid.
- * Return 0 after command successfully finished, non-zero otherwise.
- */
-int sclp_chp_configure(struct chp_id chpid)
-{
- return do_configure(get_configure_cmdw(chpid));
-}
-
-/**
- * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
- * @chpid: channel-path ID
- *
- * Perform deconfigure channel-path command sclp command for specified chpid
- * and wait for completion. On success return 0. Return non-zero otherwise.
- */
-int sclp_chp_deconfigure(struct chp_id chpid)
-{
- return do_configure(get_deconfigure_cmdw(chpid));
-}
-
-struct chp_info_sccb {
- struct sccb_header header;
- u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
- u8 standby[SCLP_CHP_INFO_MASK_SIZE];
- u8 configured[SCLP_CHP_INFO_MASK_SIZE];
- u8 ccm;
- u8 reserved[6];
- u8 cssid;
-} __attribute__((packed));
-
-struct chp_info_data {
- struct chp_info_sccb sccb;
- struct sclp_req req;
- struct completion completion;
-} __attribute__((packed));
-
-/**
- * sclp_chp_read_info - perform read channel-path information sclp command
- * @info: resulting channel-path information data
- *
- * Perform read channel-path information sclp command and wait for completion.
- * On success, store channel-path information in @info and return 0. Return
- * non-zero otherwise.
- */
-int sclp_chp_read_info(struct sclp_chp_info *info)
-{
- struct chp_info_data *data;
- int rc;
-
- if (!SCLP_HAS_CHP_INFO)
- return -EOPNOTSUPP;
- /* Prepare sccb. */
- data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
- if (!data)
- return -ENOMEM;
- data->sccb.header.length = sizeof(struct chp_info_sccb);
- data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION;
- data->req.sccb = &(data->sccb);
- data->req.status = SCLP_REQ_FILLED;
- data->req.callback = chp_callback;
- data->req.callback_data = &(data->completion);
- init_completion(&data->completion);
-
- /* Perform sclp request. */
- rc = sclp_add_request(&(data->req));
- if (rc)
- goto out;
- wait_for_completion(&data->completion);
-
- /* Check response .*/
- if (data->req.status != SCLP_REQ_DONE) {
- printk(KERN_WARNING TAG "read channel-path info request failed "
- "(status=0x%02x)\n", data->req.status);
- rc = -EIO;
- goto out;
- }
- if (data->sccb.header.response_code != 0x0010) {
- printk(KERN_WARNING TAG "read channel-path info failed "
- "(response=0x%04x)\n", data->sccb.header.response_code);
- rc = -EIO;
- goto out;
- }
- memcpy(info->recognized, data->sccb.recognized,
- SCLP_CHP_INFO_MASK_SIZE);
- memcpy(info->standby, data->sccb.standby,
- SCLP_CHP_INFO_MASK_SIZE);
- memcpy(info->configured, data->sccb.configured,
- SCLP_CHP_INFO_MASK_SIZE);
-out:
- free_page((unsigned long) data);
-
- return rc;
-}
diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c
new file mode 100644
index 00000000000..b5c23396f8f
--- /dev/null
+++ b/drivers/s390/char/sclp_cmd.c
@@ -0,0 +1,398 @@
+/*
+ * drivers/s390/char/sclp_cmd.c
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/chpid.h>
+#include <asm/sclp.h>
+#include "sclp.h"
+
+#define TAG "sclp_cmd: "
+
+#define SCLP_CMDW_READ_SCP_INFO 0x00020001
+#define SCLP_CMDW_READ_SCP_INFO_FORCED 0x00120001
+
+struct read_info_sccb {
+ struct sccb_header header; /* 0-7 */
+ u16 rnmax; /* 8-9 */
+ u8 rnsize; /* 10 */
+ u8 _reserved0[24 - 11]; /* 11-15 */
+ u8 loadparm[8]; /* 24-31 */
+ u8 _reserved1[48 - 32]; /* 32-47 */
+ u64 facilities; /* 48-55 */
+ u8 _reserved2[84 - 56]; /* 56-83 */
+ u8 fac84; /* 84 */
+ u8 _reserved3[91 - 85]; /* 85-90 */
+ u8 flags; /* 91 */
+ u8 _reserved4[100 - 92]; /* 92-99 */
+ u32 rnsize2; /* 100-103 */
+ u64 rnmax2; /* 104-111 */
+ u8 _reserved5[4096 - 112]; /* 112-4095 */
+} __attribute__((packed, aligned(PAGE_SIZE)));
+
+static struct read_info_sccb __initdata early_read_info_sccb;
+static int __initdata early_read_info_sccb_valid;
+
+u64 sclp_facilities;
+static u8 sclp_fac84;
+
+static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
+{
+ int rc;
+
+ __ctl_set_bit(0, 9);
+ rc = sclp_service_call(cmd, sccb);
+ if (rc)
+ goto out;
+ __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
+ PSW_MASK_WAIT | PSW_DEFAULT_KEY);
+ local_irq_disable();
+out:
+ /* Contents of the sccb might have changed. */
+ barrier();
+ __ctl_clear_bit(0, 9);
+ return rc;
+}
+
+void __init sclp_read_info_early(void)
+{
+ int rc;
+ int i;
+ struct read_info_sccb *sccb;
+ sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
+ SCLP_CMDW_READ_SCP_INFO};
+
+ sccb = &early_read_info_sccb;
+ for (i = 0; i < ARRAY_SIZE(commands); i++) {
+ do {
+ memset(sccb, 0, sizeof(*sccb));
+ sccb->header.length = sizeof(*sccb);
+ sccb->header.control_mask[2] = 0x80;
+ rc = sclp_cmd_sync_early(commands[i], sccb);
+ } while (rc == -EBUSY);
+
+ if (rc)
+ break;
+ if (sccb->header.response_code == 0x10) {
+ early_read_info_sccb_valid = 1;
+ break;
+ }
+ if (sccb->header.response_code != 0x1f0)
+ break;
+ }
+}
+
+void __init sclp_facilities_detect(void)
+{
+ if (!early_read_info_sccb_valid)
+ return;
+ sclp_facilities = early_read_info_sccb.facilities;
+ sclp_fac84 = early_read_info_sccb.fac84;
+}
+
+unsigned long long __init sclp_memory_detect(void)
+{
+ unsigned long long memsize;
+ struct read_info_sccb *sccb;
+
+ if (!early_read_info_sccb_valid)
+ return 0;
+ sccb = &early_read_info_sccb;
+ if (sccb->rnsize)
+ memsize = sccb->rnsize << 20;
+ else
+ memsize = sccb->rnsize2 << 20;
+ if (sccb->rnmax)
+ memsize *= sccb->rnmax;
+ else
+ memsize *= sccb->rnmax2;
+ return memsize;
+}
+
+/*
+ * This function will be called after sclp_memory_detect(), which gets called
+ * early from early.c code. Therefore the sccb should have valid contents.
+ */
+void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
+{
+ struct read_info_sccb *sccb;
+
+ if (!early_read_info_sccb_valid)
+ return;
+ sccb = &early_read_info_sccb;
+ info->is_valid = 1;
+ if (sccb->flags & 0x2)
+ info->has_dump = 1;
+ memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
+}
+
+static void sclp_sync_callback(struct sclp_req *req, void *data)
+{
+ struct completion *completion = data;
+
+ complete(completion);
+}
+
+static int do_sync_request(sclp_cmdw_t cmd, void *sccb)
+{
+ struct completion completion;
+ struct sclp_req *request;
+ int rc;
+
+ request = kzalloc(sizeof(*request), GFP_KERNEL);
+ if (!request)
+ return -ENOMEM;
+ request->command = cmd;
+ request->sccb = sccb;
+ request->status = SCLP_REQ_FILLED;
+ request->callback = sclp_sync_callback;
+ request->callback_data = &completion;
+ init_completion(&completion);
+
+ /* Perform sclp request. */
+ rc = sclp_add_request(request);
+ if (rc)
+ goto out;
+ wait_for_completion(&completion);
+
+ /* Check response. */
+ if (request->status != SCLP_REQ_DONE) {
+ printk(KERN_WARNING TAG "sync request failed "
+ "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status);
+ rc = -EIO;
+ }
+out:
+ kfree(request);
+ return rc;
+}
+
+/*
+ * CPU configuration related functions.
+ */
+
+#define SCLP_CMDW_READ_CPU_INFO 0x00010001
+#define SCLP_CMDW_CONFIGURE_CPU 0x00110001
+#define SCLP_CMDW_DECONFIGURE_CPU 0x00100001
+
+struct read_cpu_info_sccb {
+ struct sccb_header header;
+ u16 nr_configured;
+ u16 offset_configured;
+ u16 nr_standby;
+ u16 offset_standby;
+ u8 reserved[4096 - 16];
+} __attribute__((packed, aligned(PAGE_SIZE)));
+
+static void sclp_fill_cpu_info(struct sclp_cpu_info *info,
+ struct read_cpu_info_sccb *sccb)
+{
+ char *page = (char *) sccb;
+
+ memset(info, 0, sizeof(*info));
+ info->configured = sccb->nr_configured;
+ info->standby = sccb->nr_standby;
+ info->combined = sccb->nr_configured + sccb->nr_standby;
+ info->has_cpu_type = sclp_fac84 & 0x1;
+ memcpy(&info->cpu, page + sccb->offset_configured,
+ info->combined * sizeof(struct sclp_cpu_entry));
+}
+
+int sclp_get_cpu_info(struct sclp_cpu_info *info)
+{
+ int rc;
+ struct read_cpu_info_sccb *sccb;
+
+ if (!SCLP_HAS_CPU_INFO)
+ return -EOPNOTSUPP;
+ sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = sizeof(*sccb);
+ rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
+ if (rc)
+ goto out;
+ if (sccb->header.response_code != 0x0010) {
+ printk(KERN_WARNING TAG "readcpuinfo failed "
+ "(response=0x%04x)\n", sccb->header.response_code);
+ rc = -EIO;
+ goto out;
+ }
+ sclp_fill_cpu_info(info, sccb);
+out:
+ free_page((unsigned long) sccb);
+ return rc;
+}
+
+struct cpu_configure_sccb {
+ struct sccb_header header;
+} __attribute__((packed, aligned(8)));
+
+static int do_cpu_configure(sclp_cmdw_t cmd)
+{
+ struct cpu_configure_sccb *sccb;
+ int rc;
+
+ if (!SCLP_HAS_CPU_RECONFIG)
+ return -EOPNOTSUPP;
+ /*
+ * This is not going to cross a page boundary since we force
+ * kmalloc to have a minimum alignment of 8 bytes on s390.
+ */
+ sccb = kzalloc(sizeof(*sccb), GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = sizeof(*sccb);
+ rc = do_sync_request(cmd, sccb);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0020:
+ case 0x0120:
+ break;
+ default:
+ printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, "
+ "response=0x%04x)\n", cmd, sccb->header.response_code);
+ rc = -EIO;
+ break;
+ }
+out:
+ kfree(sccb);
+ return rc;
+}
+
+int sclp_cpu_configure(u8 cpu)
+{
+ return do_cpu_configure(SCLP_CMDW_CONFIGURE_CPU | cpu << 8);
+}
+
+int sclp_cpu_deconfigure(u8 cpu)
+{
+ return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8);
+}
+
+/*
+ * Channel path configuration related functions.
+ */
+
+#define SCLP_CMDW_CONFIGURE_CHPATH 0x000f0001
+#define SCLP_CMDW_DECONFIGURE_CHPATH 0x000e0001
+#define SCLP_CMDW_READ_CHPATH_INFORMATION 0x00030001
+
+struct chp_cfg_sccb {
+ struct sccb_header header;
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __attribute__((packed));
+
+static int do_chp_configure(sclp_cmdw_t cmd)
+{
+ struct chp_cfg_sccb *sccb;
+ int rc;
+
+ if (!SCLP_HAS_CHP_RECONFIG)
+ return -EOPNOTSUPP;
+ /* Prepare sccb. */
+ sccb = (struct chp_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = sizeof(*sccb);
+ rc = do_sync_request(cmd, sccb);
+ if (rc)
+ goto out;
+ switch (sccb->header.response_code) {
+ case 0x0020:
+ case 0x0120:
+ case 0x0440:
+ case 0x0450:
+ break;
+ default:
+ printk(KERN_WARNING TAG "configure channel-path failed "
+ "(cmd=0x%08x, response=0x%04x)\n", cmd,
+ sccb->header.response_code);
+ rc = -EIO;
+ break;
+ }
+out:
+ free_page((unsigned long) sccb);
+ return rc;
+}
+
+/**
+ * sclp_chp_configure - perform configure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform configure channel-path command sclp command for specified chpid.
+ * Return 0 after command successfully finished, non-zero otherwise.
+ */
+int sclp_chp_configure(struct chp_id chpid)
+{
+ return do_chp_configure(SCLP_CMDW_CONFIGURE_CHPATH | chpid.id << 8);
+}
+
+/**
+ * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform deconfigure channel-path command sclp command for specified chpid
+ * and wait for completion. On success return 0. Return non-zero otherwise.
+ */
+int sclp_chp_deconfigure(struct chp_id chpid)
+{
+ return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8);
+}
+
+struct chp_info_sccb {
+ struct sccb_header header;
+ u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
+ u8 standby[SCLP_CHP_INFO_MASK_SIZE];
+ u8 configured[SCLP_CHP_INFO_MASK_SIZE];
+ u8 ccm;
+ u8 reserved[6];
+ u8 cssid;
+} __attribute__((packed));
+
+/**
+ * sclp_chp_read_info - perform read channel-path information sclp command
+ * @info: resulting channel-path information data
+ *
+ * Perform read channel-path information sclp command and wait for completion.
+ * On success, store channel-path information in @info and return 0. Return
+ * non-zero otherwise.
+ */
+int sclp_chp_read_info(struct sclp_chp_info *info)
+{
+ struct chp_info_sccb *sccb;
+ int rc;
+
+ if (!SCLP_HAS_CHP_INFO)
+ return -EOPNOTSUPP;
+ /* Prepare sccb. */
+ sccb = (struct chp_info_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb)
+ return -ENOMEM;
+ sccb->header.length = sizeof(*sccb);
+ rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);
+ if (rc)
+ goto out;
+ if (sccb->header.response_code != 0x0010) {
+ printk(KERN_WARNING TAG "read channel-path info failed "
+ "(response=0x%04x)\n", sccb->header.response_code);
+ rc = -EIO;
+ goto out;
+ }
+ memcpy(info->recognized, sccb->recognized, SCLP_CHP_INFO_MASK_SIZE);
+ memcpy(info->standby, sccb->standby, SCLP_CHP_INFO_MASK_SIZE);
+ memcpy(info->configured, sccb->configured, SCLP_CHP_INFO_MASK_SIZE);
+out:
+ free_page((unsigned long) sccb);
+ return rc;
+}
diff --git a/drivers/s390/char/sclp_cpi.c b/drivers/s390/char/sclp_cpi.c
index 82a13d9fdfe..5716487b8c9 100644
--- a/drivers/s390/char/sclp_cpi.c
+++ b/drivers/s390/char/sclp_cpi.c
@@ -1,255 +1,41 @@
/*
- * Author: Martin Peschke <mpeschke@de.ibm.com>
- * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
+ * drivers/s390/char/sclp_cpi.c
+ * SCLP control programm identification
*
- * SCLP Control-Program Identification.
+ * Copyright IBM Corp. 2001, 2007
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Michael Ernst <mernst@de.ibm.com>
*/
-#include <linux/version.h>
#include <linux/kmod.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <asm/ebcdic.h>
-#include <asm/semaphore.h>
-
-#include "sclp.h"
-#include "sclp_rw.h"
-
-#define CPI_LENGTH_SYSTEM_TYPE 8
-#define CPI_LENGTH_SYSTEM_NAME 8
-#define CPI_LENGTH_SYSPLEX_NAME 8
-
-struct cpi_evbuf {
- struct evbuf_header header;
- u8 id_format;
- u8 reserved0;
- u8 system_type[CPI_LENGTH_SYSTEM_TYPE];
- u64 reserved1;
- u8 system_name[CPI_LENGTH_SYSTEM_NAME];
- u64 reserved2;
- u64 system_level;
- u64 reserved3;
- u8 sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
- u8 reserved4[16];
-} __attribute__((packed));
-
-struct cpi_sccb {
- struct sccb_header header;
- struct cpi_evbuf cpi_evbuf;
-} __attribute__((packed));
-
-/* Event type structure for write message and write priority message */
-static struct sclp_register sclp_cpi_event =
-{
- .send_mask = EVTYP_CTLPROGIDENT_MASK
-};
+#include <linux/version.h>
+#include "sclp_cpi_sys.h"
MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Identify this operating system instance "
+ "to the System z hardware");
+MODULE_AUTHOR("Martin Peschke <mpeschke@de.ibm.com>, "
+ "Michael Ernst <mernst@de.ibm.com>");
-MODULE_AUTHOR(
- "Martin Peschke, IBM Deutschland Entwicklung GmbH "
- "<mpeschke@de.ibm.com>");
-
-MODULE_DESCRIPTION(
- "identify this operating system instance to the S/390 "
- "or zSeries hardware");
+static char *system_name = "";
+static char *sysplex_name = "";
-static char *system_name = NULL;
module_param(system_name, charp, 0);
MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");
-
-static char *sysplex_name = NULL;
-#ifdef ALLOW_SYSPLEX_NAME
module_param(sysplex_name, charp, 0);
MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
-#endif
-
-/* use default value for this field (as well as for system level) */
-static char *system_type = "LINUX";
-static int
-cpi_check_parms(void)
+static int __init cpi_module_init(void)
{
- /* reject if no system type specified */
- if (!system_type) {
- printk("cpi: bug: no system type specified\n");
- return -EINVAL;
- }
-
- /* reject if system type larger than 8 characters */
- if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) {
- printk("cpi: bug: system type has length of %li characters - "
- "only %i characters supported\n",
- strlen(system_type), CPI_LENGTH_SYSTEM_TYPE);
- return -EINVAL;
- }
-
- /* reject if no system name specified */
- if (!system_name) {
- printk("cpi: no system name specified\n");
- return -EINVAL;
- }
-
- /* reject if system name larger than 8 characters */
- if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) {
- printk("cpi: system name has length of %li characters - "
- "only %i characters supported\n",
- strlen(system_name), CPI_LENGTH_SYSTEM_NAME);
- return -EINVAL;
- }
-
- /* reject if specified sysplex name larger than 8 characters */
- if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) {
- printk("cpi: sysplex name has length of %li characters"
- " - only %i characters supported\n",
- strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME);
- return -EINVAL;
- }
- return 0;
+ return sclp_cpi_set_data(system_name, sysplex_name, "LINUX",
+ LINUX_VERSION_CODE);
}
-static void
-cpi_callback(struct sclp_req *req, void *data)
-{
- struct semaphore *sem;
-
- sem = (struct semaphore *) data;
- up(sem);
-}
-
-static struct sclp_req *
-cpi_prepare_req(void)
-{
- struct sclp_req *req;
- struct cpi_sccb *sccb;
- struct cpi_evbuf *evb;
-
- req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
- if (req == NULL)
- return ERR_PTR(-ENOMEM);
- sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA);
- if (sccb == NULL) {
- kfree(req);
- return ERR_PTR(-ENOMEM);
- }
- memset(sccb, 0, sizeof(struct cpi_sccb));
-
- /* setup SCCB for Control-Program Identification */
- sccb->header.length = sizeof(struct cpi_sccb);
- sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
- sccb->cpi_evbuf.header.type = 0x0B;
- evb = &sccb->cpi_evbuf;
-
- /* set system type */
- memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
- memcpy(evb->system_type, system_type, strlen(system_type));
- sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
- EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
-
- /* set system name */
- memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
- memcpy(evb->system_name, system_name, strlen(system_name));
- sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
- EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
-
- /* set system level */
- evb->system_level = LINUX_VERSION_CODE;
-
- /* set sysplex name */
- if (sysplex_name) {
- memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
- memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name));
- sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
- EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
- }
-
- /* prepare request data structure presented to SCLP driver */
- req->command = SCLP_CMDW_WRITE_EVENT_DATA;
- req->sccb = sccb;
- req->status = SCLP_REQ_FILLED;
- req->callback = cpi_callback;
- return req;
-}
-
-static void
-cpi_free_req(struct sclp_req *req)
-{
- free_page((unsigned long) req->sccb);
- kfree(req);
-}
-
-static int __init
-cpi_module_init(void)
-{
- struct semaphore sem;
- struct sclp_req *req;
- int rc;
-
- rc = cpi_check_parms();
- if (rc)
- return rc;
-
- rc = sclp_register(&sclp_cpi_event);
- if (rc) {
- /* could not register sclp event. Die. */
- printk(KERN_WARNING "cpi: could not register to hardware "
- "console.\n");
- return -EINVAL;
- }
- if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) {
- printk(KERN_WARNING "cpi: no control program identification "
- "support\n");
- sclp_unregister(&sclp_cpi_event);
- return -EOPNOTSUPP;
- }
-
- req = cpi_prepare_req();
- if (IS_ERR(req)) {
- printk(KERN_WARNING "cpi: couldn't allocate request\n");
- sclp_unregister(&sclp_cpi_event);
- return PTR_ERR(req);
- }
-
- /* Prepare semaphore */
- sema_init(&sem, 0);
- req->callback_data = &sem;
- /* Add request to sclp queue */
- rc = sclp_add_request(req);
- if (rc) {
- printk(KERN_WARNING "cpi: could not start request\n");
- cpi_free_req(req);
- sclp_unregister(&sclp_cpi_event);
- return rc;
- }
- /* make "insmod" sleep until callback arrives */
- down(&sem);
-
- rc = ((struct cpi_sccb *) req->sccb)->header.response_code;
- if (rc != 0x0020) {
- printk(KERN_WARNING "cpi: failed with response code 0x%x\n",
- rc);
- rc = -ECOMM;
- } else
- rc = 0;
-
- cpi_free_req(req);
- sclp_unregister(&sclp_cpi_event);
-
- return rc;
-}
-
-
static void __exit cpi_module_exit(void)
{
}
-
-/* declare driver module init/cleanup functions */
module_init(cpi_module_init);
module_exit(cpi_module_exit);
-
diff --git a/drivers/s390/char/sclp_cpi_sys.c b/drivers/s390/char/sclp_cpi_sys.c
new file mode 100644
index 00000000000..41617032afd
--- /dev/null
+++ b/drivers/s390/char/sclp_cpi_sys.c
@@ -0,0 +1,400 @@
+/*
+ * drivers/s390/char/sclp_cpi_sys.c
+ * SCLP control program identification sysfs interface
+ *
+ * Copyright IBM Corp. 2001, 2007
+ * Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ * Michael Ernst <mernst@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/kmod.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/ebcdic.h>
+#include <asm/sclp.h>
+#include "sclp.h"
+#include "sclp_rw.h"
+#include "sclp_cpi_sys.h"
+
+#define CPI_LENGTH_NAME 8
+#define CPI_LENGTH_LEVEL 16
+
+struct cpi_evbuf {
+ struct evbuf_header header;
+ u8 id_format;
+ u8 reserved0;
+ u8 system_type[CPI_LENGTH_NAME];
+ u64 reserved1;
+ u8 system_name[CPI_LENGTH_NAME];
+ u64 reserved2;
+ u64 system_level;
+ u64 reserved3;
+ u8 sysplex_name[CPI_LENGTH_NAME];
+ u8 reserved4[16];
+} __attribute__((packed));
+
+struct cpi_sccb {
+ struct sccb_header header;
+ struct cpi_evbuf cpi_evbuf;
+} __attribute__((packed));
+
+static struct sclp_register sclp_cpi_event = {
+ .send_mask = EVTYP_CTLPROGIDENT_MASK,
+};
+
+static char system_name[CPI_LENGTH_NAME + 1];
+static char sysplex_name[CPI_LENGTH_NAME + 1];
+static char system_type[CPI_LENGTH_NAME + 1];
+static u64 system_level;
+
+static void set_data(char *field, char *data)
+{
+ memset(field, ' ', CPI_LENGTH_NAME);
+ memcpy(field, data, strlen(data));
+ sclp_ascebc_str(field, CPI_LENGTH_NAME);
+}
+
+static void cpi_callback(struct sclp_req *req, void *data)
+{
+ struct completion *completion = data;
+
+ complete(completion);
+}
+
+static struct sclp_req *cpi_prepare_req(void)
+{
+ struct sclp_req *req;
+ struct cpi_sccb *sccb;
+ struct cpi_evbuf *evb;
+
+ req = kzalloc(sizeof(struct sclp_req), GFP_KERNEL);
+ if (!req)
+ return ERR_PTR(-ENOMEM);
+ sccb = (struct cpi_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+ if (!sccb) {
+ kfree(req);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ /* setup SCCB for Control-Program Identification */
+ sccb->header.length = sizeof(struct cpi_sccb);
+ sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
+ sccb->cpi_evbuf.header.type = 0x0b;
+ evb = &sccb->cpi_evbuf;
+
+ /* set system type */
+ set_data(evb->system_type, system_type);
+
+ /* set system name */
+ set_data(evb->system_name, system_name);
+
+ /* set sytem level */
+ evb->system_level = system_level;
+
+ /* set sysplex name */
+ set_data(evb->sysplex_name, sysplex_name);
+
+ /* prepare request data structure presented to SCLP driver */
+ req->command = SCLP_CMDW_WRITE_EVENT_DATA;
+ req->sccb = sccb;
+ req->status = SCLP_REQ_FILLED;
+ req->callback = cpi_callback;
+ return req;
+}
+
+static void cpi_free_req(struct sclp_req *req)
+{
+ free_page((unsigned long) req->sccb);
+ kfree(req);
+}
+
+static int cpi_req(void)
+{
+ struct completion completion;
+ struct sclp_req *req;
+ int rc;
+ int response;
+
+ rc = sclp_register(&sclp_cpi_event);
+ if (rc) {
+ printk(KERN_WARNING "cpi: could not register "
+ "to hardware console.\n");
+ goto out;
+ }
+ if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) {
+ printk(KERN_WARNING "cpi: no control program "
+ "identification support\n");
+ rc = -EOPNOTSUPP;
+ goto out_unregister;
+ }
+
+ req = cpi_prepare_req();
+ if (IS_ERR(req)) {
+ printk(KERN_WARNING "cpi: could not allocate request\n");
+ rc = PTR_ERR(req);
+ goto out_unregister;
+ }
+
+ init_completion(&completion);
+ req->callback_data = &completion;
+
+ /* Add request to sclp queue */
+ rc = sclp_add_request(req);
+ if (rc) {
+ printk(KERN_WARNING "cpi: could not start request\n");
+ goto out_free_req;
+ }
+
+ wait_for_completion(&completion);
+
+ if (req->status != SCLP_REQ_DONE) {
+ printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n",
+ req->status);
+ rc = -EIO;
+ goto out_free_req;
+ }
+
+ response = ((struct cpi_sccb *) req->sccb)->header.response_code;
+ if (response != 0x0020) {
+ printk(KERN_WARNING "cpi: failed with "
+ "response code 0x%x\n", response);
+ rc = -EIO;
+ }
+
+out_free_req:
+ cpi_free_req(req);
+
+out_unregister:
+ sclp_unregister(&sclp_cpi_event);
+
+out:
+ return rc;
+}
+
+static int check_string(const char *attr, const char *str)
+{
+ size_t len;
+ size_t i;
+
+ len = strlen(str);
+
+ if ((len > 0) && (str[len - 1] == '\n'))
+ len--;
+
+ if (len > CPI_LENGTH_NAME)
+ return -EINVAL;
+
+ for (i = 0; i < len ; i++) {
+ if (isalpha(str[i]) || isdigit(str[i]) ||
+ strchr("$@# ", str[i]))
+ continue;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void set_string(char *attr, const char *value)
+{
+ size_t len;
+ size_t i;
+
+ len = strlen(value);
+
+ if ((len > 0) && (value[len - 1] == '\n'))
+ len--;
+
+ for (i = 0; i < CPI_LENGTH_NAME; i++) {
+ if (i < len)
+ attr[i] = toupper(value[i]);
+ else
+ attr[i] = ' ';
+ }
+}
+
+static ssize_t system_name_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", system_name);
+}
+
+static ssize_t system_name_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int rc;
+
+ rc = check_string("system_name", buf);
+ if (rc)
+ return rc;
+
+ set_string(system_name, buf);
+
+ return len;
+}
+
+static struct kobj_attribute system_name_attr =
+ __ATTR(system_name, 0644, system_name_show, system_name_store);
+
+static ssize_t sysplex_name_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
+}
+
+static ssize_t sysplex_name_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int rc;
+
+ rc = check_string("sysplex_name", buf);
+ if (rc)
+ return rc;
+
+ set_string(sysplex_name, buf);
+
+ return len;
+}
+
+static struct kobj_attribute sysplex_name_attr =
+ __ATTR(sysplex_name, 0644, sysplex_name_show, sysplex_name_store);
+
+static ssize_t system_type_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ return snprintf(page, PAGE_SIZE, "%s\n", system_type);
+}
+
+static ssize_t system_type_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ int rc;
+
+ rc = check_string("system_type", buf);
+ if (rc)
+ return rc;
+
+ set_string(system_type, buf);
+
+ return len;
+}
+
+static struct kobj_attribute system_type_attr =
+ __ATTR(system_type, 0644, system_type_show, system_type_store);
+
+static ssize_t system_level_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *page)
+{
+ unsigned long long level = system_level;
+
+ return snprintf(page, PAGE_SIZE, "%#018llx\n", level);
+}
+
+static ssize_t system_level_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ unsigned long long level;
+ char *endp;
+
+ level = simple_strtoull(buf, &endp, 16);
+
+ if (endp == buf)
+ return -EINVAL;
+ if (*endp == '\n')
+ endp++;
+ if (*endp)
+ return -EINVAL;
+
+ system_level = level;
+
+ return len;
+}
+
+static struct kobj_attribute system_level_attr =
+ __ATTR(system_level, 0644, system_level_show, system_level_store);
+
+static ssize_t set_store(struct kobject *kobj,
+ struct kobj_attribute *attr,
+ const char *buf, size_t len)
+{
+ int rc;
+
+ rc = cpi_req();
+ if (rc)
+ return rc;
+
+ return len;
+}
+
+static struct kobj_attribute set_attr = __ATTR(set, 0200, NULL, set_store);
+
+static struct attribute *cpi_attrs[] = {
+ &system_name_attr.attr,
+ &sysplex_name_attr.attr,
+ &system_type_attr.attr,
+ &system_level_attr.attr,
+ &set_attr.attr,
+ NULL,
+};
+
+static struct attribute_group cpi_attr_group = {
+ .attrs = cpi_attrs,
+};
+
+static struct kset *cpi_kset;
+
+int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type,
+ const u64 level)
+{
+ int rc;
+
+ rc = check_string("system_name", system);
+ if (rc)
+ return rc;
+ rc = check_string("sysplex_name", sysplex);
+ if (rc)
+ return rc;
+ rc = check_string("system_type", type);
+ if (rc)
+ return rc;
+
+ set_string(system_name, system);
+ set_string(sysplex_name, sysplex);
+ set_string(system_type, type);
+ system_level = level;
+
+ return cpi_req();
+}
+EXPORT_SYMBOL(sclp_cpi_set_data);
+
+static int __init cpi_init(void)
+{
+ int rc;
+
+ cpi_kset = kset_create_and_add("cpi", NULL, firmware_kobj);
+ if (!cpi_kset)
+ return -ENOMEM;
+
+ rc = sysfs_create_group(&cpi_kset->kobj, &cpi_attr_group);
+ if (rc)
+ kset_unregister(cpi_kset);
+
+ return rc;
+}
+
+__initcall(cpi_init);
diff --git a/drivers/s390/char/sclp_cpi_sys.h b/drivers/s390/char/sclp_cpi_sys.h
new file mode 100644
index 00000000000..deef3e6ff49
--- /dev/null
+++ b/drivers/s390/char/sclp_cpi_sys.h
@@ -0,0 +1,15 @@
+/*
+ * drivers/s390/char/sclp_cpi_sys.h
+ * SCLP control program identification sysfs interface
+ *
+ * Copyright IBM Corp. 2007
+ * Author(s): Michael Ernst <mernst@de.ibm.com>
+ */
+
+#ifndef __SCLP_CPI_SYS_H__
+#define __SCLP_CPI_SYS_H__
+
+int sclp_cpi_set_data(const char *system, const char *sysplex,
+ const char *type, u64 level);
+
+#endif /* __SCLP_CPI_SYS_H__ */
diff --git a/drivers/s390/char/sclp_info.c b/drivers/s390/char/sclp_info.c
deleted file mode 100644
index a1136e05275..00000000000
--- a/drivers/s390/char/sclp_info.c
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * drivers/s390/char/sclp_info.c
- *
- * Copyright IBM Corp. 2007
- * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
- */
-
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <asm/sclp.h>
-#include "sclp.h"
-
-struct sclp_readinfo_sccb {
- struct sccb_header header; /* 0-7 */
- u16 rnmax; /* 8-9 */
- u8 rnsize; /* 10 */
- u8 _reserved0[24 - 11]; /* 11-23 */
- u8 loadparm[8]; /* 24-31 */
- u8 _reserved1[48 - 32]; /* 32-47 */
- u64 facilities; /* 48-55 */
- u8 _reserved2[91 - 56]; /* 56-90 */
- u8 flags; /* 91 */
- u8 _reserved3[100 - 92]; /* 92-99 */
- u32 rnsize2; /* 100-103 */
- u64 rnmax2; /* 104-111 */
- u8 _reserved4[4096 - 112]; /* 112-4095 */
-} __attribute__((packed, aligned(4096)));
-
-static struct sclp_readinfo_sccb __initdata early_readinfo_sccb;
-static int __initdata early_readinfo_sccb_valid;
-
-u64 sclp_facilities;
-
-void __init sclp_readinfo_early(void)
-{
- int ret;
- int i;
- struct sclp_readinfo_sccb *sccb;
- sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
- SCLP_CMDW_READ_SCP_INFO};
-
- /* Enable service signal subclass mask. */
- __ctl_set_bit(0, 9);
- sccb = &early_readinfo_sccb;
- for (i = 0; i < ARRAY_SIZE(commands); i++) {
- do {
- memset(sccb, 0, sizeof(*sccb));
- sccb->header.length = sizeof(*sccb);
- sccb->header.control_mask[2] = 0x80;
- ret = sclp_service_call(commands[i], sccb);
- } while (ret == -EBUSY);
-
- if (ret)
- break;
- __load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
- PSW_MASK_WAIT | PSW_DEFAULT_KEY);
- local_irq_disable();
- /*
- * Contents of the sccb might have changed
- * therefore a barrier is needed.
- */
- barrier();
- if (sccb->header.response_code == 0x10) {
- early_readinfo_sccb_valid = 1;
- break;
- }
- if (sccb->header.response_code != 0x1f0)
- break;
- }
- /* Disable service signal subclass mask again. */
- __ctl_clear_bit(0, 9);
-}
-
-void __init sclp_facilities_detect(void)
-{
- if (!early_readinfo_sccb_valid)
- return;
- sclp_facilities = early_readinfo_sccb.facilities;
-}
-
-unsigned long long __init sclp_memory_detect(void)
-{
- unsigned long long memsize;
- struct sclp_readinfo_sccb *sccb;
-
- if (!early_readinfo_sccb_valid)
- return 0;
- sccb = &early_readinfo_sccb;
- if (sccb->rnsize)
- memsize = sccb->rnsize << 20;
- else
- memsize = sccb->rnsize2 << 20;
- if (sccb->rnmax)
- memsize *= sccb->rnmax;
- else
- memsize *= sccb->rnmax2;
- return memsize;
-}
-
-/*
- * This function will be called after sclp_memory_detect(), which gets called
- * early from early.c code. Therefore the sccb should have valid contents.
- */
-void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
-{
- struct sclp_readinfo_sccb *sccb;
-
- if (!early_readinfo_sccb_valid)
- return;
- sccb = &early_readinfo_sccb;
- info->is_valid = 1;
- if (sccb->flags & 0x2)
- info->has_dump = 1;
- memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
-}
diff --git a/drivers/s390/char/sclp_rw.c b/drivers/s390/char/sclp_rw.c
index d6b06ab8118..ad7195d3de0 100644
--- a/drivers/s390/char/sclp_rw.c
+++ b/drivers/s390/char/sclp_rw.c
@@ -76,7 +76,7 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
}
/*
- * Return a pointer to the orignal page that has been used to create
+ * Return a pointer to the original page that has been used to create
* the buffer.
*/
void *
diff --git a/drivers/s390/char/tape_3590.c b/drivers/s390/char/tape_3590.c
index da25f8e2415..8246ef3ab09 100644
--- a/drivers/s390/char/tape_3590.c
+++ b/drivers/s390/char/tape_3590.c
@@ -1495,7 +1495,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
device->cdev->dev.bus_id);
return tape_3590_erp_basic(device, request, irb, -EPERM);
case 0x8013:
- PRINT_WARN("(%s): Another host has priviliged access to the "
+ PRINT_WARN("(%s): Another host has privileged access to the "
"tape device\n", device->cdev->dev.bus_id);
PRINT_WARN("(%s): To solve the problem unload the current "
"cartridge!\n", device->cdev->dev.bus_id);
diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c
index 2fae6338ee1..7ad8cf15764 100644
--- a/drivers/s390/char/tape_core.c
+++ b/drivers/s390/char/tape_core.c
@@ -37,7 +37,7 @@ static void tape_long_busy_timeout(unsigned long data);
* we can assign the devices to minor numbers of the same major
* The list is protected by the rwlock
*/
-static struct list_head tape_device_list = LIST_HEAD_INIT(tape_device_list);
+static LIST_HEAD(tape_device_list);
static DEFINE_RWLOCK(tape_device_lock);
/*
diff --git a/drivers/s390/char/tape_proc.c b/drivers/s390/char/tape_proc.c
index cea49f001f8..c9b96d51b28 100644
--- a/drivers/s390/char/tape_proc.c
+++ b/drivers/s390/char/tape_proc.c
@@ -97,7 +97,7 @@ static void tape_proc_stop(struct seq_file *m, void *v)
{
}
-static struct seq_operations tape_proc_seq = {
+static const struct seq_operations tape_proc_seq = {
.start = tape_proc_start,
.next = tape_proc_next,
.stop = tape_proc_stop,
diff --git a/drivers/s390/char/vmlogrdr.c b/drivers/s390/char/vmlogrdr.c
index e0c4c508e12..d364e0bfae1 100644
--- a/drivers/s390/char/vmlogrdr.c
+++ b/drivers/s390/char/vmlogrdr.c
@@ -683,7 +683,7 @@ static int vmlogrdr_register_driver(void)
/* Register with iucv driver */
ret = iucv_register(&vmlogrdr_iucv_handler, 1);
if (ret) {
- printk (KERN_ERR "vmlogrdr: failed to register with"
+ printk (KERN_ERR "vmlogrdr: failed to register with "
"iucv driver\n");
goto out;
}
diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c
index d70a6e65bf1..7689b500a10 100644
--- a/drivers/s390/char/vmur.c
+++ b/drivers/s390/char/vmur.c
@@ -759,7 +759,7 @@ static loff_t ur_llseek(struct file *file, loff_t offset, int whence)
return newpos;
}
-static struct file_operations ur_fops = {
+static const struct file_operations ur_fops = {
.owner = THIS_MODULE,
.open = ur_open,
.release = ur_release,
diff --git a/drivers/s390/char/zcore.c b/drivers/s390/char/zcore.c
index 7073daf7798..f523501e6e6 100644
--- a/drivers/s390/char/zcore.c
+++ b/drivers/s390/char/zcore.c
@@ -470,7 +470,7 @@ static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
return rc;
}
-static struct file_operations zcore_fops = {
+static const struct file_operations zcore_fops = {
.owner = THIS_MODULE,
.llseek = zcore_lseek,
.read = zcore_read,
diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c
index 5287631fbfc..b7a07a86629 100644
--- a/drivers/s390/cio/airq.c
+++ b/drivers/s390/cio/airq.c
@@ -1,12 +1,12 @@
/*
* drivers/s390/cio/airq.c
- * S/390 common I/O routines -- support for adapter interruptions
+ * Support for adapter interruptions
*
- * Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
- * IBM Corporation
- * Author(s): Ingo Adlung (adlung@de.ibm.com)
- * Cornelia Huck (cornelia.huck@de.ibm.com)
- * Arnd Bergmann (arndb@de.ibm.com)
+ * Copyright IBM Corp. 1999,2007
+ * Author(s): Ingo Adlung <adlung@de.ibm.com>
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Arnd Bergmann <arndb@de.ibm.com>
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
*/
#include <linux/init.h>
@@ -14,72 +14,131 @@
#include <linux/slab.h>
#include <linux/rcupdate.h>
+#include <asm/airq.h>
+
+#include "cio.h"
#include "cio_debug.h"
-#include "airq.h"
-static adapter_int_handler_t adapter_handler;
+#define NR_AIRQS 32
+#define NR_AIRQS_PER_WORD sizeof(unsigned long)
+#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD)
-/*
- * register for adapter interrupts
- *
- * With HiperSockets the zSeries architecture provides for
- * means of adapter interrups, pseudo I/O interrupts that are
- * not tied to an I/O subchannel, but to an adapter. However,
- * it doesn't disclose the info how to enable/disable them, but
- * to recognize them only. Perhaps we should consider them
- * being shared interrupts, and thus build a linked list
- * of adapter handlers ... to be evaluated ...
- */
-int
-s390_register_adapter_interrupt (adapter_int_handler_t handler)
-{
- int ret;
- char dbf_txt[15];
+union indicator_t {
+ unsigned long word[NR_AIRQ_WORDS];
+ unsigned char byte[NR_AIRQS];
+} __attribute__((packed));
- CIO_TRACE_EVENT (4, "rgaint");
+struct airq_t {
+ adapter_int_handler_t handler;
+ void *drv_data;
+};
- if (handler == NULL)
- ret = -EINVAL;
- else
- ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0);
- if (!ret)
- synchronize_sched(); /* Allow interrupts to complete. */
+static union indicator_t indicators;
+static struct airq_t *airqs[NR_AIRQS];
- sprintf (dbf_txt, "ret:%d", ret);
- CIO_TRACE_EVENT (4, dbf_txt);
+static int register_airq(struct airq_t *airq)
+{
+ int i;
- return ret;
+ for (i = 0; i < NR_AIRQS; i++)
+ if (!cmpxchg(&airqs[i], NULL, airq))
+ return i;
+ return -ENOMEM;
}
-int
-s390_unregister_adapter_interrupt (adapter_int_handler_t handler)
+/**
+ * s390_register_adapter_interrupt() - register adapter interrupt handler
+ * @handler: adapter handler to be registered
+ * @drv_data: driver data passed with each call to the handler
+ *
+ * Returns:
+ * Pointer to the indicator to be used on success
+ * ERR_PTR() if registration failed
+ */
+void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
+ void *drv_data)
{
+ struct airq_t *airq;
+ char dbf_txt[16];
int ret;
- char dbf_txt[15];
- CIO_TRACE_EVENT (4, "urgaint");
-
- if (handler == NULL)
- ret = -EINVAL;
- else {
- adapter_handler = NULL;
- synchronize_sched(); /* Allow interrupts to complete. */
- ret = 0;
+ airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
+ if (!airq) {
+ ret = -ENOMEM;
+ goto out;
}
- sprintf (dbf_txt, "ret:%d", ret);
- CIO_TRACE_EVENT (4, dbf_txt);
-
- return ret;
+ airq->handler = handler;
+ airq->drv_data = drv_data;
+ ret = register_airq(airq);
+ if (ret < 0)
+ kfree(airq);
+out:
+ snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
+ CIO_TRACE_EVENT(4, dbf_txt);
+ if (ret < 0)
+ return ERR_PTR(ret);
+ else
+ return &indicators.byte[ret];
}
+EXPORT_SYMBOL(s390_register_adapter_interrupt);
-void
-do_adapter_IO (void)
+/**
+ * s390_unregister_adapter_interrupt - unregister adapter interrupt handler
+ * @ind: indicator for which the handler is to be unregistered
+ */
+void s390_unregister_adapter_interrupt(void *ind)
{
- CIO_TRACE_EVENT (6, "doaio");
+ struct airq_t *airq;
+ char dbf_txt[16];
+ int i;
- if (adapter_handler)
- (*adapter_handler) ();
+ i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]);
+ snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
+ CIO_TRACE_EVENT(4, dbf_txt);
+ indicators.byte[i] = 0;
+ airq = xchg(&airqs[i], NULL);
+ /*
+ * Allow interrupts to complete. This will ensure that the airq handle
+ * is no longer referenced by any interrupt handler.
+ */
+ synchronize_sched();
+ kfree(airq);
}
+EXPORT_SYMBOL(s390_unregister_adapter_interrupt);
+
+#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
-EXPORT_SYMBOL (s390_register_adapter_interrupt);
-EXPORT_SYMBOL (s390_unregister_adapter_interrupt);
+void do_adapter_IO(void)
+{
+ int w;
+ int i;
+ unsigned long word;
+ struct airq_t *airq;
+
+ /*
+ * Access indicator array in word-sized chunks to minimize storage
+ * fetch operations.
+ */
+ for (w = 0; w < NR_AIRQ_WORDS; w++) {
+ word = indicators.word[w];
+ i = w * NR_AIRQS_PER_WORD;
+ /*
+ * Check bytes within word for active indicators.
+ */
+ while (word) {
+ if (word & INDICATOR_MASK) {
+ airq = airqs[i];
+ if (likely(airq))
+ airq->handler(&indicators.byte[i],
+ airq->drv_data);
+ else
+ /*
+ * Reset ill-behaved indicator.
+ */
+ indicators.byte[i] = 0;
+ }
+ word <<= 8;
+ i++;
+ }
+ }
+}
diff --git a/drivers/s390/cio/airq.h b/drivers/s390/cio/airq.h
deleted file mode 100644
index 7d6be3fdcd6..00000000000
--- a/drivers/s390/cio/airq.h
+++ /dev/null
@@ -1,10 +0,0 @@
-#ifndef S390_AINTERRUPT_H
-#define S390_AINTERRUPT_H
-
-typedef int (*adapter_int_handler_t)(void);
-
-extern int s390_register_adapter_interrupt(adapter_int_handler_t handler);
-extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler);
-extern void do_adapter_IO (void);
-
-#endif
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index bd5f16f80bf..e8597ec9224 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -348,7 +348,7 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
return user_len;
}
-static struct seq_operations cio_ignore_proc_seq_ops = {
+static const struct seq_operations cio_ignore_proc_seq_ops = {
.start = cio_ignore_proc_seq_start,
.stop = cio_ignore_proc_seq_stop,
.next = cio_ignore_proc_seq_next,
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index 5baa517c3b6..3964056a9a4 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -35,8 +35,8 @@ ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
struct ccwgroup_device *gdev;
struct ccwgroup_driver *gdrv;
- gdev = container_of(dev, struct ccwgroup_device, dev);
- gdrv = container_of(drv, struct ccwgroup_driver, driver);
+ gdev = to_ccwgroupdev(dev);
+ gdrv = to_ccwgroupdrv(drv);
if (gdev->creator_id == gdrv->driver_id)
return 1;
@@ -75,8 +75,10 @@ static void ccwgroup_ungroup_callback(struct device *dev)
struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
mutex_lock(&gdev->reg_mutex);
- __ccwgroup_remove_symlinks(gdev);
- device_unregister(dev);
+ if (device_is_registered(&gdev->dev)) {
+ __ccwgroup_remove_symlinks(gdev);
+ device_unregister(dev);
+ }
mutex_unlock(&gdev->reg_mutex);
}
@@ -111,7 +113,7 @@ ccwgroup_release (struct device *dev)
gdev = to_ccwgroupdev(dev);
for (i = 0; i < gdev->count; i++) {
- gdev->cdev[i]->dev.driver_data = NULL;
+ dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
put_device(&gdev->cdev[i]->dev);
}
kfree(gdev);
@@ -196,11 +198,11 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
goto error;
}
/* Don't allow a device to belong to more than one group. */
- if (gdev->cdev[i]->dev.driver_data) {
+ if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
rc = -EINVAL;
goto error;
}
- gdev->cdev[i]->dev.driver_data = gdev;
+ dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
}
gdev->creator_id = creator_id;
@@ -234,8 +236,8 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
error:
for (i = 0; i < argc; i++)
if (gdev->cdev[i]) {
- if (gdev->cdev[i]->dev.driver_data == gdev)
- gdev->cdev[i]->dev.driver_data = NULL;
+ if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
+ dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
put_device(&gdev->cdev[i]->dev);
}
mutex_unlock(&gdev->reg_mutex);
@@ -408,6 +410,7 @@ int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
/* register our new driver with the core */
cdriver->driver.bus = &ccwgroup_bus_type;
cdriver->driver.name = cdriver->name;
+ cdriver->driver.owner = cdriver->owner;
return driver_register(&cdriver->driver);
}
@@ -463,8 +466,8 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
{
struct ccwgroup_device *gdev;
- if (cdev->dev.driver_data) {
- gdev = (struct ccwgroup_device *)cdev->dev.driver_data;
+ gdev = dev_get_drvdata(&cdev->dev);
+ if (gdev) {
if (get_device(&gdev->dev)) {
mutex_lock(&gdev->reg_mutex);
if (device_is_registered(&gdev->dev))
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 597c0c76a2a..e7ba16a74ef 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -89,7 +89,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
/* Copy data */
ret = 0;
memset(ssd, 0, sizeof(struct chsc_ssd_info));
- if ((ssd_area->st != 0) && (ssd_area->st != 2))
+ if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
+ (ssd_area->st != SUBCHANNEL_TYPE_MSG))
goto out_free;
ssd->path_mask = ssd_area->path_mask;
ssd->fla_valid_mask = ssd_area->fla_valid_mask;
@@ -132,20 +133,16 @@ static void terminate_internal_io(struct subchannel *sch)
device_set_intretry(sch);
/* Call handler. */
if (sch->driver && sch->driver->termination)
- sch->driver->termination(&sch->dev);
+ sch->driver->termination(sch);
}
-static int
-s390_subchannel_remove_chpid(struct device *dev, void *data)
+static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
{
int j;
int mask;
- struct subchannel *sch;
- struct chp_id *chpid;
+ struct chp_id *chpid = data;
struct schib schib;
- sch = to_subchannel(dev);
- chpid = data;
for (j = 0; j < 8; j++) {
mask = 0x80 >> j;
if ((sch->schib.pmcw.pim & mask) &&
@@ -158,7 +155,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
spin_lock_irq(sch->lock);
stsch(sch->schid, &schib);
- if (!schib.pmcw.dnv)
+ if (!css_sch_is_valid(&schib))
goto out_unreg;
memcpy(&sch->schib, &schib, sizeof(struct schib));
/* Check for single path devices. */
@@ -172,12 +169,12 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
terminate_internal_io(sch);
/* Re-start path verification. */
if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
+ sch->driver->verify(sch);
}
} else {
/* trigger path verification. */
if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
+ sch->driver->verify(sch);
else if (sch->lpm == mask)
goto out_unreg;
}
@@ -201,12 +198,10 @@ void chsc_chp_offline(struct chp_id chpid)
if (chp_get_status(chpid) <= 0)
return;
- bus_for_each_dev(&css_bus_type, NULL, &chpid,
- s390_subchannel_remove_chpid);
+ for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
}
-static int
-s390_process_res_acc_new_sch(struct subchannel_id schid)
+static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
{
struct schib schib;
/*
@@ -252,18 +247,10 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
return 0;
}
-static int
-__s390_process_res_acc(struct subchannel_id schid, void *data)
+static int __s390_process_res_acc(struct subchannel *sch, void *data)
{
int chp_mask, old_lpm;
- struct res_acc_data *res_data;
- struct subchannel *sch;
-
- res_data = data;
- sch = get_subchannel_by_schid(schid);
- if (!sch)
- /* Check if a subchannel is newly available. */
- return s390_process_res_acc_new_sch(schid);
+ struct res_acc_data *res_data = data;
spin_lock_irq(sch->lock);
chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
@@ -279,10 +266,10 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
if (!old_lpm && sch->lpm)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
+ sch->driver->verify(sch);
out:
spin_unlock_irq(sch->lock);
- put_device(&sch->dev);
+
return 0;
}
@@ -305,7 +292,8 @@ static void s390_process_res_acc (struct res_acc_data *res_data)
* The more information we have (info), the less scanning
* will we have to do.
*/
- for_each_subchannel(__s390_process_res_acc, res_data);
+ for_each_subchannel_staged(__s390_process_res_acc,
+ s390_process_res_acc_new_sch, res_data);
}
static int
@@ -499,8 +487,7 @@ void chsc_process_crw(void)
} while (sei_area->flags & 0x80);
}
-static int
-__chp_add_new_sch(struct subchannel_id schid)
+static int __chp_add_new_sch(struct subchannel_id schid, void *data)
{
struct schib schib;
@@ -514,45 +501,37 @@ __chp_add_new_sch(struct subchannel_id schid)
}
-static int
-__chp_add(struct subchannel_id schid, void *data)
+static int __chp_add(struct subchannel *sch, void *data)
{
int i, mask;
- struct chp_id *chpid;
- struct subchannel *sch;
-
- chpid = data;
- sch = get_subchannel_by_schid(schid);
- if (!sch)
- /* Check if the subchannel is now available. */
- return __chp_add_new_sch(schid);
+ struct chp_id *chpid = data;
+
spin_lock_irq(sch->lock);
for (i=0; i<8; i++) {
mask = 0x80 >> i;
if ((sch->schib.pmcw.pim & mask) &&
- (sch->schib.pmcw.chpid[i] == chpid->id)) {
- if (stsch(sch->schid, &sch->schib) != 0) {
- /* Endgame. */
- spin_unlock_irq(sch->lock);
- return -ENXIO;
- }
+ (sch->schib.pmcw.chpid[i] == chpid->id))
break;
- }
}
if (i==8) {
spin_unlock_irq(sch->lock);
return 0;
}
+ if (stsch(sch->schid, &sch->schib)) {
+ spin_unlock_irq(sch->lock);
+ css_schedule_eval(sch->schid);
+ return 0;
+ }
sch->lpm = ((sch->schib.pmcw.pim &
sch->schib.pmcw.pam &
sch->schib.pmcw.pom)
| mask) & sch->opm;
if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
+ sch->driver->verify(sch);
spin_unlock_irq(sch->lock);
- put_device(&sch->dev);
+
return 0;
}
@@ -564,7 +543,8 @@ void chsc_chp_online(struct chp_id chpid)
CIO_TRACE_EVENT(2, dbf_txt);
if (chp_get_status(chpid) != 0)
- for_each_subchannel(__chp_add, &chpid);
+ for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
+ &chpid);
}
static void __s390_subchannel_vary_chpid(struct subchannel *sch,
@@ -589,7 +569,7 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch,
if (!old_lpm)
device_trigger_reprobe(sch);
else if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
+ sch->driver->verify(sch);
break;
}
sch->opm &= ~mask;
@@ -603,37 +583,29 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch,
terminate_internal_io(sch);
/* Re-start path verification. */
if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
+ sch->driver->verify(sch);
}
} else if (!sch->lpm) {
if (device_trigger_verify(sch) != 0)
css_schedule_eval(sch->schid);
} else if (sch->driver && sch->driver->verify)
- sch->driver->verify(&sch->dev);
+ sch->driver->verify(sch);
break;
}
spin_unlock_irqrestore(sch->lock, flags);
}
-static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
{
- struct subchannel *sch;
- struct chp_id *chpid;
-
- sch = to_subchannel(dev);
- chpid = data;
+ struct chp_id *chpid = data;
__s390_subchannel_vary_chpid(sch, *chpid, 0);
return 0;
}
-static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
{
- struct subchannel *sch;
- struct chp_id *chpid;
-
- sch = to_subchannel(dev);
- chpid = data;
+ struct chp_id *chpid = data;
__s390_subchannel_vary_chpid(sch, *chpid, 1);
return 0;
@@ -643,13 +615,7 @@ static int
__s390_vary_chpid_on(struct subchannel_id schid, void *data)
{
struct schib schib;
- struct subchannel *sch;
- sch = get_subchannel_by_schid(schid);
- if (sch) {
- put_device(&sch->dev);
- return 0;
- }
if (stsch_err(schid, &schib))
/* We're through */
return -ENXIO;
@@ -669,12 +635,13 @@ int chsc_chp_vary(struct chp_id chpid, int on)
* Redo PathVerification on the devices the chpid connects to
*/
- bus_for_each_dev(&css_bus_type, NULL, &chpid, on ?
- s390_subchannel_vary_chpid_on :
- s390_subchannel_vary_chpid_off);
if (on)
- /* Scan for new devices on varied on path. */
- for_each_subchannel(__s390_vary_chpid_on, NULL);
+ for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
+ __s390_vary_chpid_on, &chpid);
+ else
+ for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
+ NULL, &chpid);
+
return 0;
}
@@ -1075,7 +1042,7 @@ chsc_determine_css_characteristics(void)
scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
if (!scsc_area) {
- CIO_MSG_EVENT(0, "Was not able to determine available"
+ CIO_MSG_EVENT(0, "Was not able to determine available "
"CHSCs due to no memory.\n");
return -ENOMEM;
}
diff --git a/drivers/s390/cio/cio.c b/drivers/s390/cio/cio.c
index 46905345159..60590a12d52 100644
--- a/drivers/s390/cio/cio.c
+++ b/drivers/s390/cio/cio.c
@@ -23,11 +23,12 @@
#include <asm/reset.h>
#include <asm/ipl.h>
#include <asm/chpid.h>
-#include "airq.h"
+#include <asm/airq.h>
#include "cio.h"
#include "css.h"
#include "chsc.h"
#include "ioasm.h"
+#include "io_sch.h"
#include "blacklist.h"
#include "cio_debug.h"
#include "chp.h"
@@ -56,39 +57,37 @@ __setup ("cio_msg=", cio_setup);
/*
* Function: cio_debug_init
- * Initializes three debug logs (under /proc/s390dbf) for common I/O:
- * - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on
+ * Initializes three debug logs for common I/O:
+ * - cio_msg logs generic cio messages
* - cio_trace logs the calling of different functions
- * - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on
- * debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW
+ * - cio_crw logs machine check related cio messages
*/
-static int __init
-cio_debug_init (void)
+static int __init cio_debug_init(void)
{
- cio_debug_msg_id = debug_register ("cio_msg", 16, 4, 16*sizeof (long));
+ cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long));
if (!cio_debug_msg_id)
goto out_unregister;
- debug_register_view (cio_debug_msg_id, &debug_sprintf_view);
- debug_set_level (cio_debug_msg_id, 2);
- cio_debug_trace_id = debug_register ("cio_trace", 16, 4, 16);
+ debug_register_view(cio_debug_msg_id, &debug_sprintf_view);
+ debug_set_level(cio_debug_msg_id, 2);
+ cio_debug_trace_id = debug_register("cio_trace", 16, 1, 16);
if (!cio_debug_trace_id)
goto out_unregister;
- debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view);
- debug_set_level (cio_debug_trace_id, 2);
- cio_debug_crw_id = debug_register ("cio_crw", 4, 4, 16*sizeof (long));
+ debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view);
+ debug_set_level(cio_debug_trace_id, 2);
+ cio_debug_crw_id = debug_register("cio_crw", 16, 1, 16 * sizeof(long));
if (!cio_debug_crw_id)
goto out_unregister;
- debug_register_view (cio_debug_crw_id, &debug_sprintf_view);
- debug_set_level (cio_debug_crw_id, 2);
+ debug_register_view(cio_debug_crw_id, &debug_sprintf_view);
+ debug_set_level(cio_debug_crw_id, 4);
return 0;
out_unregister:
if (cio_debug_msg_id)
- debug_unregister (cio_debug_msg_id);
+ debug_unregister(cio_debug_msg_id);
if (cio_debug_trace_id)
- debug_unregister (cio_debug_trace_id);
+ debug_unregister(cio_debug_trace_id);
if (cio_debug_crw_id)
- debug_unregister (cio_debug_crw_id);
+ debug_unregister(cio_debug_crw_id);
printk(KERN_WARNING"cio: could not initialize debugging\n");
return -1;
}
@@ -147,7 +146,7 @@ cio_tpi(void)
spin_lock(sch->lock);
memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
if (sch->driver && sch->driver->irq)
- sch->driver->irq(&sch->dev);
+ sch->driver->irq(sch);
spin_unlock(sch->lock);
irq_exit ();
_local_bh_enable();
@@ -184,33 +183,35 @@ cio_start_key (struct subchannel *sch, /* subchannel structure */
{
char dbf_txt[15];
int ccode;
+ struct orb *orb;
- CIO_TRACE_EVENT (4, "stIO");
- CIO_TRACE_EVENT (4, sch->dev.bus_id);
+ CIO_TRACE_EVENT(4, "stIO");
+ CIO_TRACE_EVENT(4, sch->dev.bus_id);
+ orb = &to_io_private(sch)->orb;
/* sch is always under 2G. */
- sch->orb.intparm = (__u32)(unsigned long)sch;
- sch->orb.fmt = 1;
+ orb->intparm = (u32)(addr_t)sch;
+ orb->fmt = 1;
- sch->orb.pfch = sch->options.prefetch == 0;
- sch->orb.spnd = sch->options.suspend;
- sch->orb.ssic = sch->options.suspend && sch->options.inter;
- sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm;
+ orb->pfch = sch->options.prefetch == 0;
+ orb->spnd = sch->options.suspend;
+ orb->ssic = sch->options.suspend && sch->options.inter;
+ orb->lpm = (lpm != 0) ? lpm : sch->lpm;
#ifdef CONFIG_64BIT
/*
* for 64 bit we always support 64 bit IDAWs with 4k page size only
*/
- sch->orb.c64 = 1;
- sch->orb.i2k = 0;
+ orb->c64 = 1;
+ orb->i2k = 0;
#endif
- sch->orb.key = key >> 4;
+ orb->key = key >> 4;
/* issue "Start Subchannel" */
- sch->orb.cpa = (__u32) __pa (cpa);
- ccode = ssch (sch->schid, &sch->orb);
+ orb->cpa = (__u32) __pa(cpa);
+ ccode = ssch(sch->schid, orb);
/* process condition code */
- sprintf (dbf_txt, "ccode:%d", ccode);
- CIO_TRACE_EVENT (4, dbf_txt);
+ sprintf(dbf_txt, "ccode:%d", ccode);
+ CIO_TRACE_EVENT(4, dbf_txt);
switch (ccode) {
case 0:
@@ -405,8 +406,8 @@ cio_modify (struct subchannel *sch)
/*
* Enable subchannel.
*/
-int
-cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
+int cio_enable_subchannel(struct subchannel *sch, unsigned int isc,
+ u32 intparm)
{
char dbf_txt[15];
int ccode;
@@ -425,7 +426,7 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
for (retry = 5, ret = 0; retry > 0; retry--) {
sch->schib.pmcw.ena = 1;
sch->schib.pmcw.isc = isc;
- sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
+ sch->schib.pmcw.intparm = intparm;
ret = cio_modify(sch);
if (ret == -ENODEV)
break;
@@ -567,7 +568,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
*/
if (sch->st != 0) {
CIO_DEBUG(KERN_INFO, 0,
- "cio: Subchannel 0.%x.%04x reports "
+ "Subchannel 0.%x.%04x reports "
"non-I/O subchannel type %04X\n",
sch->schid.ssid, sch->schid.sch_no, sch->st);
/* We stop here for non-io subchannels. */
@@ -576,11 +577,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
}
/* Initialization for io subchannels. */
- if (!sch->schib.pmcw.dnv) {
- /* io subchannel but device number is invalid. */
+ if (!css_sch_is_valid(&sch->schib)) {
err = -ENODEV;
goto out;
}
+
/* Devno is valid. */
if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
/*
@@ -600,7 +601,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
sch->lpm = sch->schib.pmcw.pam & sch->opm;
CIO_DEBUG(KERN_INFO, 0,
- "cio: Detected device %04x on subchannel 0.%x.%04X"
+ "Detected device %04x on subchannel 0.%x.%04X"
" - PIM = %02X, PAM = %02X, POM = %02X\n",
sch->schib.pmcw.dev, sch->schid.ssid,
sch->schid.sch_no, sch->schib.pmcw.pim,
@@ -680,7 +681,7 @@ do_IRQ (struct pt_regs *regs)
sizeof (irb->scsw));
/* Call interrupt handler if there is one. */
if (sch->driver && sch->driver->irq)
- sch->driver->irq(&sch->dev);
+ sch->driver->irq(sch);
}
if (sch)
spin_unlock(sch->lock);
@@ -698,8 +699,14 @@ do_IRQ (struct pt_regs *regs)
#ifdef CONFIG_CCW_CONSOLE
static struct subchannel console_subchannel;
+static struct io_subchannel_private console_priv;
static int console_subchannel_in_use;
+void *cio_get_console_priv(void)
+{
+ return &console_priv;
+}
+
/*
* busy wait for the next interrupt on the console
*/
@@ -738,9 +745,9 @@ cio_test_for_console(struct subchannel_id schid, void *data)
{
if (stsch_err(schid, &console_subchannel.schib) != 0)
return -ENXIO;
- if (console_subchannel.schib.pmcw.dnv &&
- console_subchannel.schib.pmcw.dev ==
- console_devno) {
+ if ((console_subchannel.schib.pmcw.st == SUBCHANNEL_TYPE_IO) &&
+ console_subchannel.schib.pmcw.dnv &&
+ (console_subchannel.schib.pmcw.dev == console_devno)) {
console_irq = schid.sch_no;
return 1; /* found */
}
@@ -758,6 +765,7 @@ cio_get_console_sch_no(void)
/* VM provided us with the irq number of the console. */
schid.sch_no = console_irq;
if (stsch(schid, &console_subchannel.schib) != 0 ||
+ (console_subchannel.schib.pmcw.st != SUBCHANNEL_TYPE_IO) ||
!console_subchannel.schib.pmcw.dnv)
return -1;
console_devno = console_subchannel.schib.pmcw.dev;
@@ -804,7 +812,7 @@ cio_probe_console(void)
ctl_set_bit(6, 24);
console_subchannel.schib.pmcw.isc = 7;
console_subchannel.schib.pmcw.intparm =
- (__u32)(unsigned long)&console_subchannel;
+ (u32)(addr_t)&console_subchannel;
ret = cio_modify(&console_subchannel);
if (ret) {
console_subchannel_in_use = 0;
@@ -1022,7 +1030,7 @@ static int __reipl_subchannel_match(struct subchannel_id schid, void *data)
if (stsch_reset(schid, &schib))
return -ENXIO;
- if (schib.pmcw.dnv &&
+ if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv &&
(schib.pmcw.dev == match_id->devid.devno) &&
(schid.ssid == match_id->devid.ssid)) {
match_id->schid = schid;
@@ -1068,6 +1076,8 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
return -ENODEV;
if (stsch(schid, &schib))
return -ENODEV;
+ if (schib.pmcw.st != SUBCHANNEL_TYPE_IO)
+ return -ENODEV;
if (!schib.pmcw.dnv)
return -ENODEV;
iplinfo->devno = schib.pmcw.dev;
diff --git a/drivers/s390/cio/cio.h b/drivers/s390/cio/cio.h
index 7446c39951a..52afa4c784d 100644
--- a/drivers/s390/cio/cio.h
+++ b/drivers/s390/cio/cio.h
@@ -11,32 +11,32 @@
* path management control word
*/
struct pmcw {
- __u32 intparm; /* interruption parameter */
- __u32 qf : 1; /* qdio facility */
- __u32 res0 : 1; /* reserved zeros */
- __u32 isc : 3; /* interruption sublass */
- __u32 res5 : 3; /* reserved zeros */
- __u32 ena : 1; /* enabled */
- __u32 lm : 2; /* limit mode */
- __u32 mme : 2; /* measurement-mode enable */
- __u32 mp : 1; /* multipath mode */
- __u32 tf : 1; /* timing facility */
- __u32 dnv : 1; /* device number valid */
- __u32 dev : 16; /* device number */
- __u8 lpm; /* logical path mask */
- __u8 pnom; /* path not operational mask */
- __u8 lpum; /* last path used mask */
- __u8 pim; /* path installed mask */
- __u16 mbi; /* measurement-block index */
- __u8 pom; /* path operational mask */
- __u8 pam; /* path available mask */
- __u8 chpid[8]; /* CHPID 0-7 (if available) */
- __u32 unused1 : 8; /* reserved zeros */
- __u32 st : 3; /* subchannel type */
- __u32 unused2 : 18; /* reserved zeros */
- __u32 mbfc : 1; /* measurement block format control */
- __u32 xmwme : 1; /* extended measurement word mode enable */
- __u32 csense : 1; /* concurrent sense; can be enabled ...*/
+ u32 intparm; /* interruption parameter */
+ u32 qf : 1; /* qdio facility */
+ u32 res0 : 1; /* reserved zeros */
+ u32 isc : 3; /* interruption sublass */
+ u32 res5 : 3; /* reserved zeros */
+ u32 ena : 1; /* enabled */
+ u32 lm : 2; /* limit mode */
+ u32 mme : 2; /* measurement-mode enable */
+ u32 mp : 1; /* multipath mode */
+ u32 tf : 1; /* timing facility */
+ u32 dnv : 1; /* device number valid */
+ u32 dev : 16; /* device number */
+ u8 lpm; /* logical path mask */
+ u8 pnom; /* path not operational mask */
+ u8 lpum; /* last path used mask */
+ u8 pim; /* path installed mask */
+ u16 mbi; /* measurement-block index */
+ u8 pom; /* path operational mask */
+ u8 pam; /* path available mask */
+ u8 chpid[8]; /* CHPID 0-7 (if available) */
+ u32 unused1 : 8; /* reserved zeros */
+ u32 st : 3; /* subchannel type */
+ u32 unused2 : 18; /* reserved zeros */
+ u32 mbfc : 1; /* measurement block format control */
+ u32 xmwme : 1; /* extended measurement word mode enable */
+ u32 csense : 1; /* concurrent sense; can be enabled ...*/
/* ... per MSCH, however, if facility */
/* ... is not installed, this results */
/* ... in an operand exception. */
@@ -52,31 +52,6 @@ struct schib {
__u8 mda[4]; /* model dependent area */
} __attribute__ ((packed,aligned(4)));
-/*
- * operation request block
- */
-struct orb {
- __u32 intparm; /* interruption parameter */
- __u32 key : 4; /* flags, like key, suspend control, etc. */
- __u32 spnd : 1; /* suspend control */
- __u32 res1 : 1; /* reserved */
- __u32 mod : 1; /* modification control */
- __u32 sync : 1; /* synchronize control */
- __u32 fmt : 1; /* format control */
- __u32 pfch : 1; /* prefetch control */
- __u32 isic : 1; /* initial-status-interruption control */
- __u32 alcc : 1; /* address-limit-checking control */
- __u32 ssic : 1; /* suppress-suspended-interr. control */
- __u32 res2 : 1; /* reserved */
- __u32 c64 : 1; /* IDAW/QDIO 64 bit control */
- __u32 i2k : 1; /* IDAW 2/4kB block size control */
- __u32 lpm : 8; /* logical path mask */
- __u32 ils : 1; /* incorrect length */
- __u32 zero : 6; /* reserved zeros */
- __u32 orbx : 1; /* ORB extension control */
- __u32 cpa; /* channel program address */
-} __attribute__ ((packed,aligned(4)));
-
/* subchannel data structure used by I/O subroutines */
struct subchannel {
struct subchannel_id schid;
@@ -85,7 +60,7 @@ struct subchannel {
enum {
SUBCHANNEL_TYPE_IO = 0,
SUBCHANNEL_TYPE_CHSC = 1,
- SUBCHANNEL_TYPE_MESSAGE = 2,
+ SUBCHANNEL_TYPE_MSG = 2,
SUBCHANNEL_TYPE_ADM = 3,
} st; /* subchannel type */
@@ -99,11 +74,10 @@ struct subchannel {
__u8 lpm; /* logical path mask */
__u8 opm; /* operational path mask */
struct schib schib; /* subchannel information block */
- struct orb orb; /* operation request block */
- struct ccw1 sense_ccw; /* static ccw for sense command */
struct chsc_ssd_info ssd_info; /* subchannel description */
struct device dev; /* entry in device tree */
struct css_driver *driver;
+ void *private; /* private per subchannel type data */
} __attribute__ ((aligned(8)));
#define IO_INTERRUPT_TYPE 0 /* I/O interrupt type */
@@ -111,7 +85,7 @@ struct subchannel {
#define to_subchannel(n) container_of(n, struct subchannel, dev)
extern int cio_validate_subchannel (struct subchannel *, struct subchannel_id);
-extern int cio_enable_subchannel (struct subchannel *, unsigned int);
+extern int cio_enable_subchannel(struct subchannel *, unsigned int, u32);
extern int cio_disable_subchannel (struct subchannel *);
extern int cio_cancel (struct subchannel *);
extern int cio_clear (struct subchannel *);
@@ -125,6 +99,7 @@ extern int cio_get_options (struct subchannel *);
extern int cio_modify (struct subchannel *);
int cio_create_sch_lock(struct subchannel *);
+void do_adapter_IO(void);
/* Use with care. */
#ifdef CONFIG_CCW_CONSOLE
@@ -133,10 +108,12 @@ extern void cio_release_console(void);
extern int cio_is_console(struct subchannel_id);
extern struct subchannel *cio_get_console_subchannel(void);
extern spinlock_t * cio_get_console_lock(void);
+extern void *cio_get_console_priv(void);
#else
#define cio_is_console(schid) 0
#define cio_get_console_subchannel() NULL
-#define cio_get_console_lock() NULL;
+#define cio_get_console_lock() NULL
+#define cio_get_console_priv() NULL
#endif
extern int cio_show_msg;
diff --git a/drivers/s390/cio/cio_debug.h b/drivers/s390/cio/cio_debug.h
index c9bf8989930..d7429ef6c66 100644
--- a/drivers/s390/cio/cio_debug.h
+++ b/drivers/s390/cio/cio_debug.h
@@ -8,20 +8,19 @@ extern debug_info_t *cio_debug_msg_id;
extern debug_info_t *cio_debug_trace_id;
extern debug_info_t *cio_debug_crw_id;
-#define CIO_TRACE_EVENT(imp, txt) do { \
+#define CIO_TRACE_EVENT(imp, txt) do { \
debug_text_event(cio_debug_trace_id, imp, txt); \
} while (0)
-#define CIO_MSG_EVENT(imp, args...) do { \
- debug_sprintf_event(cio_debug_msg_id, imp , ##args); \
+#define CIO_MSG_EVENT(imp, args...) do { \
+ debug_sprintf_event(cio_debug_msg_id, imp , ##args); \
} while (0)
-#define CIO_CRW_EVENT(imp, args...) do { \
- debug_sprintf_event(cio_debug_crw_id, imp , ##args); \
+#define CIO_CRW_EVENT(imp, args...) do { \
+ debug_sprintf_event(cio_debug_crw_id, imp , ##args); \
} while (0)
-static inline void
-CIO_HEX_EVENT(int level, void *data, int length)
+static inline void CIO_HEX_EVENT(int level, void *data, int length)
{
if (unlikely(!cio_debug_trace_id))
return;
@@ -32,9 +31,10 @@ CIO_HEX_EVENT(int level, void *data, int length)
}
}
-#define CIO_DEBUG(printk_level,event_level,msg...) ({ \
- if (cio_show_msg) printk(printk_level msg); \
- CIO_MSG_EVENT (event_level, msg); \
-})
+#define CIO_DEBUG(printk_level, event_level, msg...) do { \
+ if (cio_show_msg) \
+ printk(printk_level "cio: " msg); \
+ CIO_MSG_EVENT(event_level, msg); \
+ } while (0)
#endif
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index c3df2cd009a..3b45bbe6cce 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
return ret;
}
+struct cb_data {
+ void *data;
+ struct idset *set;
+ int (*fn_known_sch)(struct subchannel *, void *);
+ int (*fn_unknown_sch)(struct subchannel_id, void *);
+};
+
+static int call_fn_known_sch(struct device *dev, void *data)
+{
+ struct subchannel *sch = to_subchannel(dev);
+ struct cb_data *cb = data;
+ int rc = 0;
+
+ idset_sch_del(cb->set, sch->schid);
+ if (cb->fn_known_sch)
+ rc = cb->fn_known_sch(sch, cb->data);
+ return rc;
+}
+
+static int call_fn_unknown_sch(struct subchannel_id schid, void *data)
+{
+ struct cb_data *cb = data;
+ int rc = 0;
+
+ if (idset_sch_contains(cb->set, schid))
+ rc = cb->fn_unknown_sch(schid, cb->data);
+ return rc;
+}
+
+int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
+ int (*fn_unknown)(struct subchannel_id,
+ void *), void *data)
+{
+ struct cb_data cb;
+ int rc;
+
+ cb.set = idset_sch_new();
+ if (!cb.set)
+ return -ENOMEM;
+ idset_fill(cb.set);
+ cb.data = data;
+ cb.fn_known_sch = fn_known;
+ cb.fn_unknown_sch = fn_unknown;
+ /* Process registered subchannels. */
+ rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch);
+ if (rc)
+ goto out;
+ /* Process unregistered subchannels. */
+ if (fn_unknown)
+ rc = for_each_subchannel(call_fn_unknown_sch, &cb);
+out:
+ idset_free(cb.set);
+
+ return rc;
+}
+
static struct subchannel *
css_alloc_subchannel(struct subchannel_id schid)
{
@@ -77,7 +133,7 @@ css_alloc_subchannel(struct subchannel_id schid)
* This is fine even on 64bit since the subchannel is always located
* under 2G.
*/
- sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
+ sch->schib.pmcw.intparm = (u32)(addr_t)sch;
ret = cio_modify(sch);
if (ret) {
kfree(sch->lock);
@@ -237,11 +293,25 @@ get_subchannel_by_schid(struct subchannel_id schid)
return dev ? to_subchannel(dev) : NULL;
}
+/**
+ * css_sch_is_valid() - check if a subchannel is valid
+ * @schib: subchannel information block for the subchannel
+ */
+int css_sch_is_valid(struct schib *schib)
+{
+ if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv)
+ return 0;
+ return 1;
+}
+EXPORT_SYMBOL_GPL(css_sch_is_valid);
+
static int css_get_subchannel_status(struct subchannel *sch)
{
struct schib schib;
- if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
+ if (stsch(sch->schid, &schib))
+ return CIO_GONE;
+ if (!css_sch_is_valid(&schib))
return CIO_GONE;
if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
return CIO_REVALIDATE;
@@ -293,7 +363,7 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
action = UNREGISTER;
if (sch->driver && sch->driver->notify) {
spin_unlock_irqrestore(sch->lock, flags);
- ret = sch->driver->notify(&sch->dev, event);
+ ret = sch->driver->notify(sch, event);
spin_lock_irqsave(sch->lock, flags);
if (ret)
action = NONE;
@@ -349,7 +419,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
/* Will be done on the slow path. */
return -EAGAIN;
}
- if (stsch_err(schid, &schib) || !schib.pmcw.dnv) {
+ if (stsch_err(schid, &schib) || !css_sch_is_valid(&schib)) {
/* Unusable - ignore. */
return 0;
}
@@ -388,20 +458,56 @@ static int __init slow_subchannel_init(void)
return 0;
}
-static void css_slow_path_func(struct work_struct *unused)
+static int slow_eval_known_fn(struct subchannel *sch, void *data)
{
- struct subchannel_id schid;
+ int eval;
+ int rc;
- CIO_TRACE_EVENT(4, "slowpath");
spin_lock_irq(&slow_subchannel_lock);
- init_subchannel_id(&schid);
- while (idset_sch_get_first(slow_subchannel_set, &schid)) {
- idset_sch_del(slow_subchannel_set, schid);
- spin_unlock_irq(&slow_subchannel_lock);
- css_evaluate_subchannel(schid, 1);
- spin_lock_irq(&slow_subchannel_lock);
+ eval = idset_sch_contains(slow_subchannel_set, sch->schid);
+ idset_sch_del(slow_subchannel_set, sch->schid);
+ spin_unlock_irq(&slow_subchannel_lock);
+ if (eval) {
+ rc = css_evaluate_known_subchannel(sch, 1);
+ if (rc == -EAGAIN)
+ css_schedule_eval(sch->schid);
}
+ return 0;
+}
+
+static int slow_eval_unknown_fn(struct subchannel_id schid, void *data)
+{
+ int eval;
+ int rc = 0;
+
+ spin_lock_irq(&slow_subchannel_lock);
+ eval = idset_sch_contains(slow_subchannel_set, schid);
+ idset_sch_del(slow_subchannel_set, schid);
spin_unlock_irq(&slow_subchannel_lock);
+ if (eval) {
+ rc = css_evaluate_new_subchannel(schid, 1);
+ switch (rc) {
+ case -EAGAIN:
+ css_schedule_eval(schid);
+ rc = 0;
+ break;
+ case -ENXIO:
+ case -ENOMEM:
+ case -EIO:
+ /* These should abort looping */
+ break;
+ default:
+ rc = 0;
+ }
+ }
+ return rc;
+}
+
+static void css_slow_path_func(struct work_struct *unused)
+{
+ CIO_TRACE_EVENT(4, "slowpath");
+ for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn,
+ NULL);
}
static DECLARE_WORK(slow_path_work, css_slow_path_func);
@@ -430,7 +536,6 @@ void css_schedule_eval_all(void)
/* Reprobe subchannel if unregistered. */
static int reprobe_subchannel(struct subchannel_id schid, void *data)
{
- struct subchannel *sch;
int ret;
CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n",
@@ -438,13 +543,6 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data)
if (need_reprobe)
return -EAGAIN;
- sch = get_subchannel_by_schid(schid);
- if (sch) {
- /* Already known. */
- put_device(&sch->dev);
- return 0;
- }
-
ret = css_probe_device(schid);
switch (ret) {
case 0:
@@ -472,7 +570,7 @@ static void reprobe_all(struct work_struct *unused)
/* Make sure initial subchannel scan is done. */
wait_event(ccw_device_init_wq,
atomic_read(&ccw_device_init_count) == 0);
- ret = for_each_subchannel(reprobe_subchannel, NULL);
+ ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL);
CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
need_reprobe);
@@ -787,8 +885,8 @@ int sch_is_pseudo_sch(struct subchannel *sch)
static int
css_bus_match (struct device *dev, struct device_driver *drv)
{
- struct subchannel *sch = container_of (dev, struct subchannel, dev);
- struct css_driver *driver = container_of (drv, struct css_driver, drv);
+ struct subchannel *sch = to_subchannel(dev);
+ struct css_driver *driver = to_cssdriver(drv);
if (sch->st == driver->subchannel_type)
return 1;
@@ -796,32 +894,36 @@ css_bus_match (struct device *dev, struct device_driver *drv)
return 0;
}
-static int
-css_probe (struct device *dev)
+static int css_probe(struct device *dev)
{
struct subchannel *sch;
+ int ret;
sch = to_subchannel(dev);
- sch->driver = container_of (dev->driver, struct css_driver, drv);
- return (sch->driver->probe ? sch->driver->probe(sch) : 0);
+ sch->driver = to_cssdriver(dev->driver);
+ ret = sch->driver->probe ? sch->driver->probe(sch) : 0;
+ if (ret)
+ sch->driver = NULL;
+ return ret;
}
-static int
-css_remove (struct device *dev)
+static int css_remove(struct device *dev)
{
struct subchannel *sch;
+ int ret;
sch = to_subchannel(dev);
- return (sch->driver->remove ? sch->driver->remove(sch) : 0);
+ ret = sch->driver->remove ? sch->driver->remove(sch) : 0;
+ sch->driver = NULL;
+ return ret;
}
-static void
-css_shutdown (struct device *dev)
+static void css_shutdown(struct device *dev)
{
struct subchannel *sch;
sch = to_subchannel(dev);
- if (sch->driver->shutdown)
+ if (sch->driver && sch->driver->shutdown)
sch->driver->shutdown(sch);
}
@@ -833,6 +935,34 @@ struct bus_type css_bus_type = {
.shutdown = css_shutdown,
};
+/**
+ * css_driver_register - register a css driver
+ * @cdrv: css driver to register
+ *
+ * This is mainly a wrapper around driver_register that sets name
+ * and bus_type in the embedded struct device_driver correctly.
+ */
+int css_driver_register(struct css_driver *cdrv)
+{
+ cdrv->drv.name = cdrv->name;
+ cdrv->drv.bus = &css_bus_type;
+ cdrv->drv.owner = cdrv->owner;
+ return driver_register(&cdrv->drv);
+}
+EXPORT_SYMBOL_GPL(css_driver_register);
+
+/**
+ * css_driver_unregister - unregister a css driver
+ * @cdrv: css driver to unregister
+ *
+ * This is a wrapper around driver_unregister.
+ */
+void css_driver_unregister(struct css_driver *cdrv)
+{
+ driver_unregister(&cdrv->drv);
+}
+EXPORT_SYMBOL_GPL(css_driver_unregister);
+
subsys_initcall(init_channel_subsystem);
MODULE_LICENSE("GPL");
diff --git a/drivers/s390/cio/css.h b/drivers/s390/cio/css.h
index 81215ef3243..b7055452355 100644
--- a/drivers/s390/cio/css.h
+++ b/drivers/s390/cio/css.h
@@ -58,64 +58,6 @@ struct pgid {
__u32 tod_high; /* high word TOD clock */
} __attribute__ ((packed));
-#define MAX_CIWS 8
-
-/*
- * sense-id response buffer layout
- */
-struct senseid {
- /* common part */
- __u8 reserved; /* always 0x'FF' */
- __u16 cu_type; /* control unit type */
- __u8 cu_model; /* control unit model */
- __u16 dev_type; /* device type */
- __u8 dev_model; /* device model */
- __u8 unused; /* padding byte */
- /* extended part */
- struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */
-} __attribute__ ((packed,aligned(4)));
-
-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 {
- 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 */
- } __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 */
- } __attribute__((packed)) flags;
- unsigned long intparm; /* user interruption parameter */
- struct qdio_irq *qdio_data;
- struct irb irb; /* device status */
- 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;
- wait_queue_head_t wait_q;
- struct timer_list timer;
- void *cmb; /* measurement information */
- struct list_head cmb_list; /* list of measured devices */
- u64 cmb_start_time; /* clock value of cmb reset */
- void *cmb_wait; /* deferred cmb enable/disable */
-};
-
/*
* A css driver handles all subchannels of one type.
* Currently, we only care about I/O subchannels (type 0), these
@@ -123,25 +65,35 @@ struct ccw_device_private {
*/
struct subchannel;
struct css_driver {
+ struct module *owner;
unsigned int subchannel_type;
struct device_driver drv;
- void (*irq)(struct device *);
- int (*notify)(struct device *, int);
- void (*verify)(struct device *);
- void (*termination)(struct device *);
+ void (*irq)(struct subchannel *);
+ int (*notify)(struct subchannel *, int);
+ void (*verify)(struct subchannel *);
+ void (*termination)(struct subchannel *);
int (*probe)(struct subchannel *);
int (*remove)(struct subchannel *);
void (*shutdown)(struct subchannel *);
+ const char *name;
};
+#define to_cssdriver(n) container_of(n, struct css_driver, drv)
+
/*
* all css_drivers have the css_bus_type
*/
extern struct bus_type css_bus_type;
+extern int css_driver_register(struct css_driver *);
+extern void css_driver_unregister(struct css_driver *);
+
extern void css_sch_device_unregister(struct subchannel *);
extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
extern int css_init_done;
+int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
+ int (*fn_unknown)(struct subchannel_id,
+ void *), void *data);
extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
extern void css_process_crw(int, int);
extern void css_reiterate_subchannels(void);
@@ -188,6 +140,8 @@ void css_schedule_eval(struct subchannel_id schid);
void css_schedule_eval_all(void);
int sch_is_pseudo_sch(struct subchannel *);
+struct schib;
+int css_sch_is_valid(struct schib *);
extern struct workqueue_struct *slow_path_wq;
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 74f6b539974..d35dc3f25d0 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -17,6 +17,7 @@
#include <linux/list.h>
#include <linux/device.h>
#include <linux/workqueue.h>
+#include <linux/timer.h>
#include <asm/ccwdev.h>
#include <asm/cio.h>
@@ -28,6 +29,12 @@
#include "css.h"
#include "device.h"
#include "ioasm.h"
+#include "io_sch.h"
+
+static struct timer_list recovery_timer;
+static spinlock_t recovery_lock;
+static int recovery_phase;
+static const unsigned long recovery_delay[] = { 3, 30, 300 };
/******************* bus type handling ***********************/
@@ -115,19 +122,18 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env)
struct bus_type ccw_bus_type;
-static int io_subchannel_probe (struct subchannel *);
-static int io_subchannel_remove (struct subchannel *);
-static int io_subchannel_notify(struct device *, int);
-static void io_subchannel_verify(struct device *);
-static void io_subchannel_ioterm(struct device *);
+static void io_subchannel_irq(struct subchannel *);
+static int io_subchannel_probe(struct subchannel *);
+static int io_subchannel_remove(struct subchannel *);
+static int io_subchannel_notify(struct subchannel *, int);
+static void io_subchannel_verify(struct subchannel *);
+static void io_subchannel_ioterm(struct subchannel *);
static void io_subchannel_shutdown(struct subchannel *);
static struct css_driver io_subchannel_driver = {
+ .owner = THIS_MODULE,
.subchannel_type = SUBCHANNEL_TYPE_IO,
- .drv = {
- .name = "io_subchannel",
- .bus = &css_bus_type,
- },
+ .name = "io_subchannel",
.irq = io_subchannel_irq,
.notify = io_subchannel_notify,
.verify = io_subchannel_verify,
@@ -142,6 +148,8 @@ struct workqueue_struct *ccw_device_notify_work;
wait_queue_head_t ccw_device_init_wq;
atomic_t ccw_device_init_count;
+static void recovery_func(unsigned long data);
+
static int __init
init_ccw_bus_type (void)
{
@@ -149,6 +157,7 @@ init_ccw_bus_type (void)
init_waitqueue_head(&ccw_device_init_wq);
atomic_set(&ccw_device_init_count, 0);
+ setup_timer(&recovery_timer, recovery_func, 0);
ccw_device_work = create_singlethread_workqueue("cio");
if (!ccw_device_work)
@@ -166,7 +175,8 @@ init_ccw_bus_type (void)
if ((ret = bus_register (&ccw_bus_type)))
goto out_err;
- if ((ret = driver_register(&io_subchannel_driver.drv)))
+ ret = css_driver_register(&io_subchannel_driver);
+ if (ret)
goto out_err;
wait_event(ccw_device_init_wq,
@@ -186,7 +196,7 @@ out_err:
static void __exit
cleanup_ccw_bus_type (void)
{
- driver_unregister(&io_subchannel_driver.drv);
+ css_driver_unregister(&io_subchannel_driver);
bus_unregister(&ccw_bus_type);
destroy_workqueue(ccw_device_notify_work);
destroy_workqueue(ccw_device_work);
@@ -773,7 +783,7 @@ static void sch_attach_device(struct subchannel *sch,
{
css_update_ssd_info(sch);
spin_lock_irq(sch->lock);
- sch->dev.driver_data = cdev;
+ sch_set_cdev(sch, cdev);
cdev->private->schid = sch->schid;
cdev->ccwlock = sch->lock;
device_trigger_reprobe(sch);
@@ -795,7 +805,7 @@ static void sch_attach_disconnected_device(struct subchannel *sch,
put_device(&other_sch->dev);
return;
}
- other_sch->dev.driver_data = NULL;
+ sch_set_cdev(other_sch, NULL);
/* No need to keep a subchannel without ccw device around. */
css_sch_device_unregister(other_sch);
put_device(&other_sch->dev);
@@ -831,12 +841,12 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
return;
}
spin_lock_irq(sch->lock);
- sch->dev.driver_data = cdev;
+ sch_set_cdev(sch, cdev);
spin_unlock_irq(sch->lock);
/* Start recognition for the new ccw device. */
if (io_subchannel_recog(cdev, sch)) {
spin_lock_irq(sch->lock);
- sch->dev.driver_data = NULL;
+ sch_set_cdev(sch, NULL);
spin_unlock_irq(sch->lock);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
@@ -940,7 +950,7 @@ io_subchannel_register(struct work_struct *work)
cdev->private->dev_id.devno, ret);
put_device(&cdev->dev);
spin_lock_irqsave(sch->lock, flags);
- sch->dev.driver_data = NULL;
+ sch_set_cdev(sch, NULL);
spin_unlock_irqrestore(sch->lock, flags);
kfree (cdev->private);
kfree (cdev);
@@ -1022,7 +1032,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
int rc;
struct ccw_device_private *priv;
- sch->dev.driver_data = cdev;
+ sch_set_cdev(sch, cdev);
sch->driver = &io_subchannel_driver;
cdev->ccwlock = sch->lock;
@@ -1082,7 +1092,7 @@ static void ccw_device_move_to_sch(struct work_struct *work)
}
if (former_parent) {
spin_lock_irq(former_parent->lock);
- former_parent->dev.driver_data = NULL;
+ sch_set_cdev(former_parent, NULL);
spin_unlock_irq(former_parent->lock);
css_sch_device_unregister(former_parent);
/* Reset intparm to zeroes. */
@@ -1096,6 +1106,18 @@ out:
put_device(&cdev->dev);
}
+static void io_subchannel_irq(struct subchannel *sch)
+{
+ struct ccw_device *cdev;
+
+ cdev = sch_get_cdev(sch);
+
+ CIO_TRACE_EVENT(3, "IRQ");
+ CIO_TRACE_EVENT(3, sch->dev.bus_id);
+ if (cdev)
+ dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
+}
+
static int
io_subchannel_probe (struct subchannel *sch)
{
@@ -1104,13 +1126,13 @@ io_subchannel_probe (struct subchannel *sch)
unsigned long flags;
struct ccw_dev_id dev_id;
- if (sch->dev.driver_data) {
+ cdev = sch_get_cdev(sch);
+ if (cdev) {
/*
* This subchannel already has an associated ccw_device.
* Register it and exit. This happens for all early
* device, e.g. the console.
*/
- cdev = sch->dev.driver_data;
cdev->dev.groups = ccwdev_attr_groups;
device_initialize(&cdev->dev);
ccw_device_register(cdev);
@@ -1132,6 +1154,11 @@ io_subchannel_probe (struct subchannel *sch)
*/
dev_id.devno = sch->schib.pmcw.dev;
dev_id.ssid = sch->schid.ssid;
+ /* Allocate I/O subchannel private data. */
+ sch->private = kzalloc(sizeof(struct io_subchannel_private),
+ GFP_KERNEL | GFP_DMA);
+ if (!sch->private)
+ return -ENOMEM;
cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
if (!cdev)
cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1149,16 +1176,18 @@ io_subchannel_probe (struct subchannel *sch)
return 0;
}
cdev = io_subchannel_create_ccwdev(sch);
- if (IS_ERR(cdev))
+ if (IS_ERR(cdev)) {
+ kfree(sch->private);
return PTR_ERR(cdev);
-
+ }
rc = io_subchannel_recog(cdev, sch);
if (rc) {
spin_lock_irqsave(sch->lock, flags);
- sch->dev.driver_data = NULL;
+ sch_set_cdev(sch, NULL);
spin_unlock_irqrestore(sch->lock, flags);
if (cdev->dev.release)
cdev->dev.release(&cdev->dev);
+ kfree(sch->private);
}
return rc;
@@ -1170,25 +1199,25 @@ io_subchannel_remove (struct subchannel *sch)
struct ccw_device *cdev;
unsigned long flags;
- if (!sch->dev.driver_data)
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
return 0;
- cdev = sch->dev.driver_data;
/* Set ccw device to not operational and drop reference. */
spin_lock_irqsave(cdev->ccwlock, flags);
- sch->dev.driver_data = NULL;
+ sch_set_cdev(sch, NULL);
cdev->private->state = DEV_STATE_NOT_OPER;
spin_unlock_irqrestore(cdev->ccwlock, flags);
ccw_device_unregister(cdev);
put_device(&cdev->dev);
+ kfree(sch->private);
return 0;
}
-static int
-io_subchannel_notify(struct device *dev, int event)
+static int io_subchannel_notify(struct subchannel *sch, int event)
{
struct ccw_device *cdev;
- cdev = dev->driver_data;
+ cdev = sch_get_cdev(sch);
if (!cdev)
return 0;
if (!cdev->drv)
@@ -1198,22 +1227,20 @@ io_subchannel_notify(struct device *dev, int event)
return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
}
-static void
-io_subchannel_verify(struct device *dev)
+static void io_subchannel_verify(struct subchannel *sch)
{
struct ccw_device *cdev;
- cdev = dev->driver_data;
+ cdev = sch_get_cdev(sch);
if (cdev)
dev_fsm_event(cdev, DEV_EVENT_VERIFY);
}
-static void
-io_subchannel_ioterm(struct device *dev)
+static void io_subchannel_ioterm(struct subchannel *sch)
{
struct ccw_device *cdev;
- cdev = dev->driver_data;
+ cdev = sch_get_cdev(sch);
if (!cdev)
return;
/* Internal I/O will be retried by the interrupt handler. */
@@ -1231,7 +1258,7 @@ io_subchannel_shutdown(struct subchannel *sch)
struct ccw_device *cdev;
int ret;
- cdev = sch->dev.driver_data;
+ cdev = sch_get_cdev(sch);
if (cio_is_console(sch->schid))
return;
@@ -1271,6 +1298,9 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
{
int rc;
+ /* Attach subchannel private data. */
+ sch->private = cio_get_console_priv();
+ memset(sch->private, 0, sizeof(struct io_subchannel_private));
/* Initialize the ccw_device structure. */
cdev->dev.parent= &sch->dev;
rc = io_subchannel_recog(cdev, sch);
@@ -1456,6 +1486,7 @@ int ccw_driver_register(struct ccw_driver *cdriver)
drv->bus = &ccw_bus_type;
drv->name = cdriver->name;
+ drv->owner = cdriver->owner;
return driver_register(drv);
}
@@ -1481,6 +1512,60 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev)
return sch->schid;
}
+static int recovery_check(struct device *dev, void *data)
+{
+ struct ccw_device *cdev = to_ccwdev(dev);
+ int *redo = data;
+
+ spin_lock_irq(cdev->ccwlock);
+ switch (cdev->private->state) {
+ case DEV_STATE_DISCONNECTED:
+ CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
+ cdev->private->dev_id.ssid,
+ cdev->private->dev_id.devno);
+ dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+ *redo = 1;
+ break;
+ case DEV_STATE_DISCONNECTED_SENSE_ID:
+ *redo = 1;
+ break;
+ }
+ spin_unlock_irq(cdev->ccwlock);
+
+ return 0;
+}
+
+static void recovery_func(unsigned long data)
+{
+ int redo = 0;
+
+ bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
+ if (redo) {
+ spin_lock_irq(&recovery_lock);
+ if (!timer_pending(&recovery_timer)) {
+ if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
+ recovery_phase++;
+ mod_timer(&recovery_timer, jiffies +
+ recovery_delay[recovery_phase] * HZ);
+ }
+ spin_unlock_irq(&recovery_lock);
+ } else
+ CIO_MSG_EVENT(2, "recovery: end\n");
+}
+
+void ccw_device_schedule_recovery(void)
+{
+ unsigned long flags;
+
+ CIO_MSG_EVENT(2, "recovery: schedule\n");
+ spin_lock_irqsave(&recovery_lock, flags);
+ if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
+ recovery_phase = 0;
+ mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
+ }
+ spin_unlock_irqrestore(&recovery_lock, flags);
+}
+
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(ccw_device_set_online);
EXPORT_SYMBOL(ccw_device_set_offline);
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 0d408960043..d40a2ffaa00 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -5,6 +5,8 @@
#include <asm/atomic.h>
#include <linux/wait.h>
+#include "io_sch.h"
+
/*
* states of the device statemachine
*/
@@ -74,7 +76,6 @@ extern struct workqueue_struct *ccw_device_notify_work;
extern wait_queue_head_t ccw_device_init_wq;
extern atomic_t ccw_device_init_count;
-void io_subchannel_irq (struct device *pdev);
void io_subchannel_recog_done(struct ccw_device *cdev);
int ccw_device_cancel_halt_clear(struct ccw_device *);
@@ -87,6 +88,8 @@ int ccw_device_recognition(struct ccw_device *);
int ccw_device_online(struct ccw_device *);
int ccw_device_offline(struct ccw_device *);
+void ccw_device_schedule_recovery(void);
+
/* Function prototypes for device status and basic sense stuff. */
void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index bfad421cda6..4b92c84fb43 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -25,14 +25,16 @@
#include "ioasm.h"
#include "chp.h"
+static int timeout_log_enabled;
+
int
device_is_online(struct subchannel *sch)
{
struct ccw_device *cdev;
- if (!sch->dev.driver_data)
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
return 0;
- cdev = sch->dev.driver_data;
return (cdev->private->state == DEV_STATE_ONLINE);
}
@@ -41,9 +43,9 @@ device_is_disconnected(struct subchannel *sch)
{
struct ccw_device *cdev;
- if (!sch->dev.driver_data)
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
return 0;
- cdev = sch->dev.driver_data;
return (cdev->private->state == DEV_STATE_DISCONNECTED ||
cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
}
@@ -53,19 +55,21 @@ device_set_disconnected(struct subchannel *sch)
{
struct ccw_device *cdev;
- if (!sch->dev.driver_data)
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
return;
- cdev = sch->dev.driver_data;
ccw_device_set_timeout(cdev, 0);
cdev->private->flags.fake_irb = 0;
cdev->private->state = DEV_STATE_DISCONNECTED;
+ if (cdev->online)
+ ccw_device_schedule_recovery();
}
void device_set_intretry(struct subchannel *sch)
{
struct ccw_device *cdev;
- cdev = sch->dev.driver_data;
+ cdev = sch_get_cdev(sch);
if (!cdev)
return;
cdev->private->flags.intretry = 1;
@@ -75,13 +79,62 @@ int device_trigger_verify(struct subchannel *sch)
{
struct ccw_device *cdev;
- cdev = sch->dev.driver_data;
+ cdev = sch_get_cdev(sch);
if (!cdev || !cdev->online)
return -EINVAL;
dev_fsm_event(cdev, DEV_EVENT_VERIFY);
return 0;
}
+static int __init ccw_timeout_log_setup(char *unused)
+{
+ timeout_log_enabled = 1;
+ return 1;
+}
+
+__setup("ccw_timeout_log", ccw_timeout_log_setup);
+
+static void ccw_timeout_log(struct ccw_device *cdev)
+{
+ struct schib schib;
+ struct subchannel *sch;
+ struct io_subchannel_private *private;
+ int cc;
+
+ sch = to_subchannel(cdev->dev.parent);
+ private = to_io_private(sch);
+ cc = stsch(sch->schid, &schib);
+
+ printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
+ "device information:\n", get_clock());
+ printk(KERN_WARNING "cio: orb:\n");
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ &private->orb, sizeof(private->orb), 0);
+ printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
+ printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
+ printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
+ "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
+
+ if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw ||
+ (void *)(addr_t)private->orb.cpa == cdev->private->iccws)
+ printk(KERN_WARNING "cio: last channel program (intern):\n");
+ else
+ printk(KERN_WARNING "cio: last channel program:\n");
+
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ (void *)(addr_t)private->orb.cpa,
+ sizeof(struct ccw1), 0);
+ printk(KERN_WARNING "cio: ccw device state: %d\n",
+ cdev->private->state);
+ printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
+ printk(KERN_WARNING "cio: schib:\n");
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ &schib, sizeof(schib), 0);
+ printk(KERN_WARNING "cio: ccw device flags:\n");
+ print_hex_dump(KERN_WARNING, "cio: ", DUMP_PREFIX_NONE, 16, 1,
+ &cdev->private->flags, sizeof(cdev->private->flags), 0);
+}
+
/*
* Timeout function. It just triggers a DEV_EVENT_TIMEOUT.
*/
@@ -92,6 +145,8 @@ ccw_device_timeout(unsigned long data)
cdev = (struct ccw_device *) data;
spin_lock_irq(cdev->ccwlock);
+ if (timeout_log_enabled)
+ ccw_timeout_log(cdev);
dev_fsm_event(cdev, DEV_EVENT_TIMEOUT);
spin_unlock_irq(cdev->ccwlock);
}
@@ -122,9 +177,9 @@ device_kill_pending_timer(struct subchannel *sch)
{
struct ccw_device *cdev;
- if (!sch->dev.driver_data)
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
return;
- cdev = sch->dev.driver_data;
ccw_device_set_timeout(cdev, 0);
}
@@ -268,7 +323,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
switch (state) {
case DEV_STATE_NOT_OPER:
CIO_DEBUG(KERN_WARNING, 2,
- "cio: SenseID : unknown device %04x on subchannel "
+ "SenseID : unknown device %04x on subchannel "
"0.%x.%04x\n", cdev->private->dev_id.devno,
sch->schid.ssid, sch->schid.sch_no);
break;
@@ -294,7 +349,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
}
/* Issue device info message. */
CIO_DEBUG(KERN_INFO, 2,
- "cio: SenseID : device 0.%x.%04x reports: "
+ "SenseID : device 0.%x.%04x reports: "
"CU Type/Mod = %04X/%02X, Dev Type/Mod = "
"%04X/%02X\n",
cdev->private->dev_id.ssid,
@@ -304,7 +359,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
break;
case DEV_STATE_BOXED:
CIO_DEBUG(KERN_WARNING, 2,
- "cio: SenseID : boxed device %04x on subchannel "
+ "SenseID : boxed device %04x on subchannel "
"0.%x.%04x\n", cdev->private->dev_id.devno,
sch->schid.ssid, sch->schid.sch_no);
break;
@@ -349,7 +404,7 @@ ccw_device_oper_notify(struct work_struct *work)
sch = to_subchannel(cdev->dev.parent);
if (sch->driver && sch->driver->notify) {
spin_unlock_irqrestore(cdev->ccwlock, flags);
- ret = sch->driver->notify(&sch->dev, CIO_OPER);
+ ret = sch->driver->notify(sch, CIO_OPER);
spin_lock_irqsave(cdev->ccwlock, flags);
} else
ret = 0;
@@ -389,7 +444,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
if (state == DEV_STATE_BOXED)
CIO_DEBUG(KERN_WARNING, 2,
- "cio: Boxed device %04x on subchannel %04x\n",
+ "Boxed device %04x on subchannel %04x\n",
cdev->private->dev_id.devno, sch->schid.sch_no);
if (cdev->private->flags.donotify) {
@@ -500,7 +555,8 @@ ccw_device_recognition(struct ccw_device *cdev)
(cdev->private->state != DEV_STATE_BOXED))
return -EINVAL;
sch = to_subchannel(cdev->dev.parent);
- ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+ ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+ (u32)(addr_t)sch);
if (ret != 0)
/* Couldn't enable the subchannel for i/o. Sick device. */
return ret;
@@ -587,9 +643,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
default:
/* Reset oper notify indication after verify error. */
cdev->private->flags.donotify = 0;
- if (cdev->online)
+ if (cdev->online) {
+ ccw_device_set_timeout(cdev, 0);
dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
- else
+ } else
ccw_device_done(cdev, DEV_STATE_NOT_OPER);
break;
}
@@ -610,7 +667,8 @@ ccw_device_online(struct ccw_device *cdev)
sch = to_subchannel(cdev->dev.parent);
if (css_init_done && !get_device(&cdev->dev))
return -ENODEV;
- ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+ ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+ (u32)(addr_t)sch);
if (ret != 0) {
/* Couldn't enable the subchannel for i/o. Sick device. */
if (ret == -ENODEV)
@@ -937,7 +995,7 @@ void device_kill_io(struct subchannel *sch)
int ret;
struct ccw_device *cdev;
- cdev = sch->dev.driver_data;
+ cdev = sch_get_cdev(sch);
ret = ccw_device_cancel_halt_clear(cdev);
if (ret == -EBUSY) {
ccw_device_set_timeout(cdev, 3*HZ);
@@ -990,7 +1048,8 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
struct subchannel *sch;
sch = to_subchannel(cdev->dev.parent);
- if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0)
+ if (cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+ (u32)(addr_t)sch) != 0)
/* Couldn't enable the subchannel for i/o. Sick device. */
return;
@@ -1006,9 +1065,9 @@ device_trigger_reprobe(struct subchannel *sch)
{
struct ccw_device *cdev;
- if (!sch->dev.driver_data)
+ cdev = sch_get_cdev(sch);
+ if (!cdev)
return;
- cdev = sch->dev.driver_data;
if (cdev->private->state != DEV_STATE_DISCONNECTED)
return;
@@ -1028,7 +1087,7 @@ device_trigger_reprobe(struct subchannel *sch)
sch->schib.pmcw.ena = 0;
if ((sch->lpm & (sch->lpm - 1)) != 0)
sch->schib.pmcw.mp = 1;
- sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
+ sch->schib.pmcw.intparm = (u32)(addr_t)sch;
/* 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) {
@@ -1223,21 +1282,4 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
},
};
-/*
- * io_subchannel_irq is called for "real" interrupts or for status
- * pending conditions on msch.
- */
-void
-io_subchannel_irq (struct device *pdev)
-{
- struct ccw_device *cdev;
-
- cdev = to_subchannel(pdev)->dev.driver_data;
-
- CIO_TRACE_EVENT (3, "IRQ");
- CIO_TRACE_EVENT (3, pdev->bus_id);
- if (cdev)
- dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
-}
-
EXPORT_SYMBOL_GPL(ccw_device_set_timeout);
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index 156f3f9786b..918b8b89cf9 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -24,6 +24,7 @@
#include "css.h"
#include "device.h"
#include "ioasm.h"
+#include "io_sch.h"
/*
* Input :
@@ -219,11 +220,13 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
return -EAGAIN;
}
if (irb->scsw.cc == 3) {
- if ((sch->orb.lpm &
- sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
+ u8 lpm;
+
+ lpm = to_io_private(sch)->orb.lpm;
+ if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x "
"on subchannel 0.%x.%04x is "
- "'not operational'\n", sch->orb.lpm,
+ "'not operational'\n", lpm,
cdev->private->dev_id.devno,
sch->schid.ssid, sch->schid.sch_no);
return -EACCES;
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 7fd2dadc329..49b58eb0fab 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -501,7 +501,7 @@ ccw_device_stlck(struct ccw_device *cdev)
return -ENOMEM;
}
spin_lock_irqsave(sch->lock, flags);
- ret = cio_enable_subchannel(sch, 3);
+ ret = cio_enable_subchannel(sch, 3, (u32)(addr_t)sch);
if (ret)
goto out_unlock;
/*
diff --git a/drivers/s390/cio/device_pgid.c b/drivers/s390/cio/device_pgid.c
index cb1879a9681..c52449a1f9f 100644
--- a/drivers/s390/cio/device_pgid.c
+++ b/drivers/s390/cio/device_pgid.c
@@ -22,6 +22,7 @@
#include "css.h"
#include "device.h"
#include "ioasm.h"
+#include "io_sch.h"
/*
* Helper function called from interrupt context to decide whether an
@@ -155,10 +156,13 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
return -EAGAIN;
}
if (irb->scsw.cc == 3) {
+ u8 lpm;
+
+ lpm = to_io_private(sch)->orb.lpm;
CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x,"
" lpm %02X, became 'not operational'\n",
cdev->private->dev_id.devno, sch->schid.ssid,
- sch->schid.sch_no, sch->orb.lpm);
+ sch->schid.sch_no, lpm);
return -EACCES;
}
i = 8 - ffs(cdev->private->imask);
diff --git a/drivers/s390/cio/device_status.c b/drivers/s390/cio/device_status.c
index aa96e675259..ebe0848cfe3 100644
--- a/drivers/s390/cio/device_status.c
+++ b/drivers/s390/cio/device_status.c
@@ -20,6 +20,7 @@
#include "css.h"
#include "device.h"
#include "ioasm.h"
+#include "io_sch.h"
/*
* Check for any kind of channel or interface control check but don't
@@ -310,6 +311,7 @@ int
ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
{
struct subchannel *sch;
+ struct ccw1 *sense_ccw;
sch = to_subchannel(cdev->dev.parent);
@@ -326,15 +328,16 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
/*
* We have ending status but no sense information. Do a basic sense.
*/
- sch->sense_ccw.cmd_code = CCW_CMD_BASIC_SENSE;
- sch->sense_ccw.cda = (__u32) __pa(cdev->private->irb.ecw);
- sch->sense_ccw.count = SENSE_MAX_COUNT;
- sch->sense_ccw.flags = CCW_FLAG_SLI;
+ sense_ccw = &to_io_private(sch)->sense_ccw;
+ sense_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
+ sense_ccw->cda = (__u32) __pa(cdev->private->irb.ecw);
+ sense_ccw->count = SENSE_MAX_COUNT;
+ sense_ccw->flags = CCW_FLAG_SLI;
/* Reset internal retry indication. */
cdev->private->flags.intretry = 0;
- return cio_start (sch, &sch->sense_ccw, 0xff);
+ return cio_start(sch, sense_ccw, 0xff);
}
/*
diff --git a/drivers/s390/cio/io_sch.h b/drivers/s390/cio/io_sch.h
new file mode 100644
index 00000000000..8c613160bfc
--- /dev/null
+++ b/drivers/s390/cio/io_sch.h
@@ -0,0 +1,163 @@
+#ifndef S390_IO_SCH_H
+#define S390_IO_SCH_H
+
+#include "schid.h"
+
+/*
+ * operation request block
+ */
+struct orb {
+ u32 intparm; /* interruption parameter */
+ u32 key : 4; /* flags, like key, suspend control, etc. */
+ u32 spnd : 1; /* suspend control */
+ u32 res1 : 1; /* reserved */
+ u32 mod : 1; /* modification control */
+ u32 sync : 1; /* synchronize control */
+ u32 fmt : 1; /* format control */
+ u32 pfch : 1; /* prefetch control */
+ u32 isic : 1; /* initial-status-interruption control */
+ u32 alcc : 1; /* address-limit-checking control */
+ u32 ssic : 1; /* suppress-suspended-interr. control */
+ u32 res2 : 1; /* reserved */
+ u32 c64 : 1; /* IDAW/QDIO 64 bit control */
+ u32 i2k : 1; /* IDAW 2/4kB block size control */
+ u32 lpm : 8; /* logical path mask */
+ u32 ils : 1; /* incorrect length */
+ u32 zero : 6; /* reserved zeros */
+ u32 orbx : 1; /* ORB extension control */
+ u32 cpa; /* channel program address */
+} __attribute__ ((packed, aligned(4)));
+
+struct io_subchannel_private {
+ struct orb orb; /* operation request block */
+ struct ccw1 sense_ccw; /* static ccw for sense command */
+} __attribute__ ((aligned(8)));
+
+#define to_io_private(n) ((struct io_subchannel_private *)n->private)
+#define sch_get_cdev(n) (dev_get_drvdata(&n->dev))
+#define sch_set_cdev(n, c) (dev_set_drvdata(&n->dev, c))
+
+#define MAX_CIWS 8
+
+/*
+ * sense-id response buffer layout
+ */
+struct senseid {
+ /* common part */
+ u8 reserved; /* always 0x'FF' */
+ u16 cu_type; /* control unit type */
+ u8 cu_model; /* control unit model */
+ u16 dev_type; /* device type */
+ u8 dev_model; /* device model */
+ u8 unused; /* padding byte */
+ /* extended part */
+ struct ciw ciw[MAX_CIWS]; /* variable # of CIWs */
+} __attribute__ ((packed, aligned(4)));
+
+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 {
+ 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 */
+ } __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 */
+ } __attribute__((packed)) flags;
+ unsigned long intparm; /* user interruption parameter */
+ struct qdio_irq *qdio_data;
+ struct irb irb; /* device status */
+ 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;
+ wait_queue_head_t wait_q;
+ struct timer_list timer;
+ void *cmb; /* measurement information */
+ struct list_head cmb_list; /* list of measured devices */
+ u64 cmb_start_time; /* clock value of cmb reset */
+ void *cmb_wait; /* deferred cmb enable/disable */
+};
+
+static inline int ssch(struct subchannel_id schid, volatile struct orb *addr)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " ssch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+ return ccode;
+}
+
+static inline int rsch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " rsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
+ return ccode;
+}
+
+static inline int csch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " csch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
+ return ccode;
+}
+
+static inline int hsch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " hsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
+ return ccode;
+}
+
+static inline int xsch(struct subchannel_id schid)
+{
+ register struct subchannel_id reg1 asm("1") = schid;
+ int ccode;
+
+ asm volatile(
+ " .insn rre,0xb2760000,%1,0\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
+ return ccode;
+}
+
+#endif
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 7153dd95908..652ea3625f9 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -109,72 +109,6 @@ static inline int tpi( volatile struct tpi_info *addr)
return ccode;
}
-static inline int ssch(struct subchannel_id schid,
- volatile struct orb *addr)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode;
-
- asm volatile(
- " ssch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
- return ccode;
-}
-
-static inline int rsch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode;
-
- asm volatile(
- " rsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
- return ccode;
-}
-
-static inline int csch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode;
-
- asm volatile(
- " csch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
- return ccode;
-}
-
-static inline int hsch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode;
-
- asm volatile(
- " hsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
- return ccode;
-}
-
-static inline int xsch(struct subchannel_id schid)
-{
- register struct subchannel_id reg1 asm ("1") = schid;
- int ccode;
-
- asm volatile(
- " .insn rre,0xb2760000,%1,0\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : "d" (reg1) : "cc");
- return ccode;
-}
-
static inline int chsc(void *chsc_area)
{
typedef struct { char _[4096]; } addr_type;
diff --git a/drivers/s390/cio/qdio.c b/drivers/s390/cio/qdio.c
index 40a3208c7cf..e2a781b6b21 100644
--- a/drivers/s390/cio/qdio.c
+++ b/drivers/s390/cio/qdio.c
@@ -48,11 +48,11 @@
#include <asm/debug.h>
#include <asm/s390_rdev.h>
#include <asm/qdio.h>
+#include <asm/airq.h>
#include "cio.h"
#include "css.h"
#include "device.h"
-#include "airq.h"
#include "qdio.h"
#include "ioasm.h"
#include "chsc.h"
@@ -96,7 +96,7 @@ static debug_info_t *qdio_dbf_slsb_in;
static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change
during a while loop */
static DEFINE_SPINLOCK(ttiq_list_lock);
-static int register_thinint_result;
+static void *tiqdio_ind;
static void tiqdio_tl(unsigned long);
static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0);
@@ -399,7 +399,7 @@ qdio_get_indicator(void)
{
int i;
- for (i=1;i<INDICATORS_PER_CACHELINE;i++)
+ for (i = 0; i < INDICATORS_PER_CACHELINE; i++)
if (!indicator_used[i]) {
indicator_used[i]=1;
return indicators+i;
@@ -1408,8 +1408,7 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set)
if (q->hydra_gives_outbound_pcis) {
if (!q->siga_sync_done_on_thinints) {
SYNC_MEMORY_ALL;
- } else if ((!q->siga_sync_done_on_outb_tis)&&
- (q->hydra_gives_outbound_pcis)) {
+ } else if (!q->siga_sync_done_on_outb_tis) {
SYNC_MEMORY_ALL_OUTB;
}
} else {
@@ -1911,8 +1910,7 @@ qdio_fill_thresholds(struct qdio_irq *irq_ptr,
}
}
-static int
-tiqdio_thinint_handler(void)
+static void tiqdio_thinint_handler(void *ind, void *drv_data)
{
QDIO_DBF_TEXT4(0,trace,"thin_int");
@@ -1925,7 +1923,6 @@ tiqdio_thinint_handler(void)
tiqdio_clear_global_summary();
tiqdio_inbound_checks();
- return 0;
}
static void
@@ -2445,7 +2442,7 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
real_addr_dev_st_chg_ind=0;
} else {
real_addr_local_summary_bit=
- virt_to_phys((volatile void *)indicators);
+ virt_to_phys((volatile void *)tiqdio_ind);
real_addr_dev_st_chg_ind=
virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
}
@@ -3740,23 +3737,25 @@ static void
tiqdio_register_thinints(void)
{
char dbf_text[20];
- register_thinint_result=
- s390_register_adapter_interrupt(&tiqdio_thinint_handler);
- if (register_thinint_result) {
- sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff));
+
+ tiqdio_ind =
+ s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL);
+ if (IS_ERR(tiqdio_ind)) {
+ sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind));
QDIO_DBF_TEXT0(0,setup,dbf_text);
QDIO_PRINT_ERR("failed to register adapter handler " \
- "(rc=%i).\nAdapter interrupts might " \
+ "(rc=%li).\nAdapter interrupts might " \
"not work. Continuing.\n",
- register_thinint_result);
+ PTR_ERR(tiqdio_ind));
+ tiqdio_ind = NULL;
}
}
static void
tiqdio_unregister_thinints(void)
{
- if (!register_thinint_result)
- s390_unregister_adapter_interrupt(&tiqdio_thinint_handler);
+ if (tiqdio_ind)
+ s390_unregister_adapter_interrupt(tiqdio_ind);
}
static int
@@ -3768,8 +3767,8 @@ qdio_get_qdio_memory(void)
for (i=1;i<INDICATORS_PER_CACHELINE;i++)
indicator_used[i]=0;
indicators = kzalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE),
- GFP_KERNEL);
- if (!indicators)
+ GFP_KERNEL);
+ if (!indicators)
return -ENOMEM;
return 0;
}
@@ -3780,7 +3779,6 @@ qdio_release_qdio_memory(void)
kfree(indicators);
}
-
static void
qdio_unregister_dbf_views(void)
{
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 6d7aad18f6f..37870e4e938 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -57,7 +57,7 @@
of the queue to 0 */
#define QDIO_ESTABLISH_TIMEOUT (1*HZ)
-#define QDIO_ACTIVATE_TIMEOUT ((5*HZ)>>10)
+#define QDIO_ACTIVATE_TIMEOUT (5*HZ)
#define QDIO_CLEANUP_CLEAR_TIMEOUT (20*HZ)
#define QDIO_CLEANUP_HALT_TIMEOUT (10*HZ)
#define QDIO_FORCE_CHECK_TIMEOUT (10*HZ)
diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c
index 3561982749e..c3076217871 100644
--- a/drivers/s390/net/claw.c
+++ b/drivers/s390/net/claw.c
@@ -2416,7 +2416,7 @@ init_ccw_bk(struct net_device *dev)
privptr->p_buff_pages_perwrite);
#endif
if (p_buff==NULL) {
- printk(KERN_INFO "%s:%s __get_free_pages"
+ printk(KERN_INFO "%s:%s __get_free_pages "
"for writes buf failed : get is for %d pages\n",
dev->name,
__FUNCTION__,
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index 0fd663b23d7..7bfe8d707a3 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1115,7 +1115,7 @@ list_modified:
rc = lcs_send_setipm(card, ipm);
spin_lock_irqsave(&card->ipm_lock, flags);
if (rc) {
- PRINT_INFO("Adding multicast address failed."
+ PRINT_INFO("Adding multicast address failed. "
"Table possibly full!\n");
/* store ipm in failed list -> will be added
* to ipm_list again, so a retry will be done
diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c
index d6e93f15440..f3d893cfe61 100644
--- a/drivers/s390/net/netiucv.c
+++ b/drivers/s390/net/netiucv.c
@@ -198,8 +198,7 @@ struct iucv_connection {
/**
* Linked list of all connection structs.
*/
-static struct list_head iucv_connection_list =
- LIST_HEAD_INIT(iucv_connection_list);
+static LIST_HEAD(iucv_connection_list);
static DEFINE_RWLOCK(iucv_connection_rwlock);
/**
diff --git a/drivers/s390/net/qeth_proc.c b/drivers/s390/net/qeth_proc.c
index f1ff165a5e0..46ecd03a597 100644
--- a/drivers/s390/net/qeth_proc.c
+++ b/drivers/s390/net/qeth_proc.c
@@ -146,7 +146,7 @@ qeth_procfile_seq_show(struct seq_file *s, void *it)
return 0;
}
-static struct seq_operations qeth_procfile_seq_ops = {
+static const struct seq_operations qeth_procfile_seq_ops = {
.start = qeth_procfile_seq_start,
.stop = qeth_procfile_seq_stop,
.next = qeth_procfile_seq_next,
@@ -264,7 +264,7 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
return 0;
}
-static struct seq_operations qeth_perf_procfile_seq_ops = {
+static const struct seq_operations qeth_perf_procfile_seq_ops = {
.start = qeth_procfile_seq_start,
.stop = qeth_procfile_seq_stop,
.next = qeth_procfile_seq_next,
diff --git a/drivers/s390/net/smsgiucv.c b/drivers/s390/net/smsgiucv.c
index 47bb47b4858..8735a415a11 100644
--- a/drivers/s390/net/smsgiucv.c
+++ b/drivers/s390/net/smsgiucv.c
@@ -42,7 +42,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
static struct iucv_path *smsg_path;
static DEFINE_SPINLOCK(smsg_list_lock);
-static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list);
+static LIST_HEAD(smsg_list);
static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]);
static void smsg_message_pending(struct iucv_path *, struct iucv_message *);
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 4f86c0e1296..2dc8110ebf7 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -1286,7 +1286,7 @@ zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
* note: no lock in subsequent strategy routines
* (this allows these routine to call schedule, e.g.
* kmalloc with such flags or qdio_initialize & friends)
- * Note: in case of timeout, the seperate strategies will fail
+ * Note: in case of timeout, the separate strategies will fail
* anyhow. No need for a special action. Even worse, a nameserver
* failure would not wake up waiting ports without the call.
*/
diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c
index fe57941ab55..e45f85f7c7e 100644
--- a/drivers/s390/scsi/zfcp_fsf.c
+++ b/drivers/s390/scsi/zfcp_fsf.c
@@ -502,7 +502,7 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req)
fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
break;
case FSF_SQ_NO_RECOM:
- ZFCP_LOG_NORMAL("bug: No recommendation could be given for a"
+ ZFCP_LOG_NORMAL("bug: No recommendation could be given for a "
"problem on the adapter %s "
"Stopping all operations on this adapter. ",
zfcp_get_busid_by_adapter(fsf_req->adapter));
@@ -813,7 +813,7 @@ zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req)
read_unlock_irqrestore(&zfcp_data.config_lock, flags);
if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) {
- ZFCP_LOG_NORMAL("bug: Reopen port indication received for"
+ ZFCP_LOG_NORMAL("bug: Reopen port indication received for "
"nonexisting port with d_id 0x%06x on "
"adapter %s. Ignored.\n",
status_buffer->d_id & ZFCP_DID_MASK,
@@ -2281,7 +2281,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
&lock_flags, &fsf_req);
if (retval) {
ZFCP_LOG_INFO("error: Out of resources. Could not create an "
- "exchange port data request for"
+ "exchange port data request for "
"the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
write_unlock_irqrestore(&adapter->request_queue.queue_lock,
@@ -2340,7 +2340,7 @@ zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
0, NULL, &lock_flags, &fsf_req);
if (retval) {
ZFCP_LOG_INFO("error: Out of resources. Could not create an "
- "exchange port data request for"
+ "exchange port data request for "
"the adapter %s.\n",
zfcp_get_busid_by_adapter(adapter));
write_unlock_irqrestore(&adapter->request_queue.queue_lock,
@@ -4725,7 +4725,7 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
/* allocate new FSF request */
fsf_req = zfcp_fsf_req_alloc(pool, req_flags);
if (unlikely(NULL == fsf_req)) {
- ZFCP_LOG_DEBUG("error: Could not put an FSF request into"
+ ZFCP_LOG_DEBUG("error: Could not put an FSF request into "
"the outbound (send) queue.\n");
ret = -ENOMEM;
goto failed_fsf_req;
diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c
index 51d92b196ee..22fdc17e0d0 100644
--- a/drivers/s390/scsi/zfcp_qdio.c
+++ b/drivers/s390/scsi/zfcp_qdio.c
@@ -529,7 +529,7 @@ zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req)
/**
- * zfcp_qdio_sbale_fill - set address and lenght in current SBALE
+ * zfcp_qdio_sbale_fill - set address and length in current SBALE
* on request_queue
*/
static void
diff --git a/include/asm-s390/airq.h b/include/asm-s390/airq.h
new file mode 100644
index 00000000000..41d028cb52a
--- /dev/null
+++ b/include/asm-s390/airq.h
@@ -0,0 +1,19 @@
+/*
+ * include/asm-s390/airq.h
+ *
+ * Copyright IBM Corp. 2002,2007
+ * Author(s): Ingo Adlung <adlung@de.ibm.com>
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
+ * Arnd Bergmann <arndb@de.ibm.com>
+ * Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_AIRQ_H
+#define _ASM_S390_AIRQ_H
+
+typedef void (*adapter_int_handler_t)(void *, void *);
+
+void *s390_register_adapter_interrupt(adapter_int_handler_t, void *);
+void s390_unregister_adapter_interrupt(void *);
+
+#endif /* _ASM_S390_AIRQ_H */
diff --git a/include/asm-s390/cio.h b/include/asm-s390/cio.h
index 2f08c16e44a..123b557c3ff 100644
--- a/include/asm-s390/cio.h
+++ b/include/asm-s390/cio.h
@@ -24,8 +24,8 @@
* @fmt: format
* @pfch: prefetch
* @isic: initial-status interruption control
- * @alcc: adress-limit checking control
- * @ssi: supress-suspended interruption
+ * @alcc: address-limit checking control
+ * @ssi: suppress-suspended interruption
* @zcc: zero condition code
* @ectl: extended control
* @pno: path not operational
diff --git a/include/asm-s390/dasd.h b/include/asm-s390/dasd.h
index 604f68fa6f5..3f002e13d02 100644
--- a/include/asm-s390/dasd.h
+++ b/include/asm-s390/dasd.h
@@ -105,7 +105,7 @@ typedef struct dasd_information_t {
} dasd_information_t;
/*
- * Read Subsystem Data - Perfomance Statistics
+ * Read Subsystem Data - Performance Statistics
*/
typedef struct dasd_rssd_perf_stats_t {
unsigned char invalid:1;
diff --git a/include/asm-s390/ipl.h b/include/asm-s390/ipl.h
index 2c40fd3a137..c1b2e50392b 100644
--- a/include/asm-s390/ipl.h
+++ b/include/asm-s390/ipl.h
@@ -83,6 +83,8 @@ extern u32 dump_prefix_page;
extern unsigned int zfcpdump_prefix_array[];
extern void do_reipl(void);
+extern void do_halt(void);
+extern void do_poff(void);
extern void ipl_save_parameters(void);
enum {
@@ -118,7 +120,7 @@ struct ipl_info
};
extern struct ipl_info ipl_info;
-extern void setup_ipl_info(void);
+extern void setup_ipl(void);
/*
* DIAG 308 support
@@ -141,6 +143,10 @@ enum diag308_opt {
DIAG308_IPL_OPT_DUMP = 0x20,
};
+enum diag308_flags {
+ DIAG308_FLAGS_LP_VALID = 0x80,
+};
+
enum diag308_rc {
DIAG308_RC_OK = 1,
};
diff --git a/include/asm-s390/mmu_context.h b/include/asm-s390/mmu_context.h
index 05b842126b9..a77d4ba3c8e 100644
--- a/include/asm-s390/mmu_context.h
+++ b/include/asm-s390/mmu_context.h
@@ -12,10 +12,15 @@
#include <asm/pgalloc.h>
#include <asm-generic/mm_hooks.h>
-/*
- * get a new mmu context.. S390 don't know about contexts.
- */
-#define init_new_context(tsk,mm) 0
+static inline int init_new_context(struct task_struct *tsk,
+ struct mm_struct *mm)
+{
+ mm->context = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
+#ifdef CONFIG_64BIT
+ mm->context |= _ASCE_TYPE_REGION3;
+#endif
+ return 0;
+}
#define destroy_context(mm) do { } while (0)
@@ -27,19 +32,11 @@
static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
{
- pgd_t *pgd = mm->pgd;
- unsigned long asce_bits;
-
- /* Calculate asce bits from the first pgd table entry. */
- asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
-#ifdef CONFIG_64BIT
- asce_bits |= _ASCE_TYPE_REGION3;
-#endif
- S390_lowcore.user_asce = asce_bits | __pa(pgd);
+ S390_lowcore.user_asce = mm->context | __pa(mm->pgd);
if (switch_amode) {
/* Load primary space page table origin. */
- pgd_t *shadow_pgd = get_shadow_table(pgd) ? : pgd;
- S390_lowcore.user_exec_asce = asce_bits | __pa(shadow_pgd);
+ pgd_t *shadow_pgd = get_shadow_table(mm->pgd) ? : mm->pgd;
+ S390_lowcore.user_exec_asce = mm->context | __pa(shadow_pgd);
asm volatile(LCTL_OPCODE" 1,1,%0\n"
: : "m" (S390_lowcore.user_exec_asce) );
} else
diff --git a/include/asm-s390/pgtable.h b/include/asm-s390/pgtable.h
index 1f530f8a628..79b9eab1a0c 100644
--- a/include/asm-s390/pgtable.h
+++ b/include/asm-s390/pgtable.h
@@ -104,41 +104,27 @@ extern char empty_zero_page[PAGE_SIZE];
#ifndef __ASSEMBLY__
/*
- * Just any arbitrary offset to the start of the vmalloc VM area: the
- * current 8MB value just means that there will be a 8MB "hole" after the
- * physical memory until the kernel virtual memory starts. That means that
- * any out-of-bounds memory accesses will hopefully be caught.
- * The vmalloc() routines leaves a hole of 4kB between each vmalloced
- * area for the same reason. ;)
- * vmalloc area starts at 4GB to prevent syscall table entry exchanging
- * from modules.
- */
-extern unsigned long vmalloc_end;
-
-#ifdef CONFIG_64BIT
-#define VMALLOC_ADDR (max(0x100000000UL, (unsigned long) high_memory))
-#else
-#define VMALLOC_ADDR ((unsigned long) high_memory)
-#endif
-#define VMALLOC_OFFSET (8*1024*1024)
-#define VMALLOC_START ((VMALLOC_ADDR + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
-#define VMALLOC_END vmalloc_end
-
-/*
- * We need some free virtual space to be able to do vmalloc.
- * VMALLOC_MIN_SIZE defines the minimum size of the vmalloc
- * area. On a machine with 2GB memory we make sure that we
- * have at least 128MB free space for vmalloc. On a machine
- * with 4TB we make sure we have at least 128GB.
+ * The vmalloc area will always be on the topmost area of the kernel
+ * mapping. We reserve 96MB (31bit) / 1GB (64bit) for vmalloc,
+ * which should be enough for any sane case.
+ * By putting vmalloc at the top, we maximise the gap between physical
+ * memory and vmalloc to catch misplaced memory accesses. As a side
+ * effect, this also makes sure that 64 bit module code cannot be used
+ * as system call address.
*/
#ifndef __s390x__
-#define VMALLOC_MIN_SIZE 0x8000000UL
-#define VMALLOC_END_INIT 0x80000000UL
+#define VMALLOC_START 0x78000000UL
+#define VMALLOC_END 0x7e000000UL
+#define VMEM_MAP_MAX 0x80000000UL
#else /* __s390x__ */
-#define VMALLOC_MIN_SIZE 0x2000000000UL
-#define VMALLOC_END_INIT 0x40000000000UL
+#define VMALLOC_START 0x3e000000000UL
+#define VMALLOC_END 0x3e040000000UL
+#define VMEM_MAP_MAX 0x40000000000UL
#endif /* __s390x__ */
+#define VMEM_MAP ((struct page *) VMALLOC_END)
+#define VMEM_MAP_SIZE ((VMALLOC_START / PAGE_SIZE) * sizeof(struct page))
+
/*
* A 31 bit pagetable entry of S390 has following format:
* | PFRA | | OS |
diff --git a/include/asm-s390/processor.h b/include/asm-s390/processor.h
index 21d40a19355..c86b982aef5 100644
--- a/include/asm-s390/processor.h
+++ b/include/asm-s390/processor.h
@@ -59,9 +59,6 @@ extern void s390_adjust_jiffies(void);
extern void print_cpu_info(struct cpuinfo_S390 *);
extern int get_cpu_capability(unsigned int *);
-/* Lazy FPU handling on uni-processor */
-extern struct task_struct *last_task_used_math;
-
/*
* User space process size: 2GB for 31 bit, 4TB for 64 bit.
*/
@@ -95,7 +92,6 @@ struct thread_struct {
unsigned long ksp; /* kernel stack pointer */
mm_segment_t mm_segment;
unsigned long prot_addr; /* address of protection-excep. */
- unsigned int error_code; /* error-code of last prog-excep. */
unsigned int trap_no;
per_struct per_info;
/* Used to give failing instruction back to user for ieee exceptions */
diff --git a/include/asm-s390/ptrace.h b/include/asm-s390/ptrace.h
index 332ee73688f..61f6952f2e3 100644
--- a/include/asm-s390/ptrace.h
+++ b/include/asm-s390/ptrace.h
@@ -465,6 +465,14 @@ struct user_regs_struct
#ifdef __KERNEL__
#define __ARCH_SYS_PTRACE 1
+/*
+ * These are defined as per linux/ptrace.h, which see.
+ */
+#define arch_has_single_step() (1)
+struct task_struct;
+extern void user_enable_single_step(struct task_struct *);
+extern void user_disable_single_step(struct task_struct *);
+
#define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
#define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN)
#define regs_return_value(regs)((regs)->gprs[2])
diff --git a/include/asm-s390/qdio.h b/include/asm-s390/qdio.h
index 74db1dc10a7..4b8ff55f680 100644
--- a/include/asm-s390/qdio.h
+++ b/include/asm-s390/qdio.h
@@ -184,7 +184,7 @@ struct qdr {
#endif /* QDIO_32_BIT */
unsigned long qiba; /* queue-information-block address */
unsigned int res8; /* reserved */
- unsigned int qkey : 4; /* queue-informatio-block key */
+ unsigned int qkey : 4; /* queue-information-block key */
unsigned int res9 : 28; /* reserved */
/* union _qd {*/ /* why this? */
struct qdesfmt0 qdf0[126];
diff --git a/include/asm-s390/rwsem.h b/include/asm-s390/rwsem.h
index 90f4eccaa29..9d2a1797180 100644
--- a/include/asm-s390/rwsem.h
+++ b/include/asm-s390/rwsem.h
@@ -91,8 +91,8 @@ struct rw_semaphore {
#endif
#define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \
- __RWSEM_DEP_MAP_INIT(name) }
+ { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait.lock), \
+ LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) }
#define DECLARE_RWSEM(name) \
struct rw_semaphore name = __RWSEM_INITIALIZER(name)
diff --git a/include/asm-s390/sclp.h b/include/asm-s390/sclp.h
index cb9faf1ea5c..b5f2843013a 100644
--- a/include/asm-s390/sclp.h
+++ b/include/asm-s390/sclp.h
@@ -27,7 +27,25 @@ struct sclp_ipl_info {
char loadparm[LOADPARM_LEN];
};
-void sclp_readinfo_early(void);
+struct sclp_cpu_entry {
+ u8 address;
+ u8 reserved0[13];
+ u8 type;
+ u8 reserved1;
+} __attribute__((packed));
+
+struct sclp_cpu_info {
+ unsigned int configured;
+ unsigned int standby;
+ unsigned int combined;
+ int has_cpu_type;
+ struct sclp_cpu_entry cpu[255];
+};
+
+int sclp_get_cpu_info(struct sclp_cpu_info *info);
+int sclp_cpu_configure(u8 cpu);
+int sclp_cpu_deconfigure(u8 cpu);
+void sclp_read_info_early(void);
void sclp_facilities_detect(void);
unsigned long long sclp_memory_detect(void);
int sclp_sdias_blk_count(void);
diff --git a/include/asm-s390/smp.h b/include/asm-s390/smp.h
index 07708c07701..c7b74326a52 100644
--- a/include/asm-s390/smp.h
+++ b/include/asm-s390/smp.h
@@ -35,8 +35,6 @@ extern void machine_restart_smp(char *);
extern void machine_halt_smp(void);
extern void machine_power_off_smp(void);
-extern void smp_setup_cpu_possible_map(void);
-
#define NO_PROC_ID 0xFF /* No processor magic marker */
/*
@@ -92,6 +90,8 @@ extern void __cpu_die (unsigned int cpu);
extern void cpu_die (void) __attribute__ ((noreturn));
extern int __cpu_up (unsigned int cpu);
+extern int smp_call_function_mask(cpumask_t mask, void (*func)(void *),
+ void *info, int wait);
#endif
#ifndef CONFIG_SMP
@@ -103,7 +103,6 @@ static inline void smp_send_stop(void)
#define hard_smp_processor_id() 0
#define smp_cpu_not_running(cpu) 1
-#define smp_setup_cpu_possible_map() do { } while (0)
#endif
extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];
diff --git a/include/asm-s390/spinlock.h b/include/asm-s390/spinlock.h
index 3fd43826fd0..df84ae96915 100644
--- a/include/asm-s390/spinlock.h
+++ b/include/asm-s390/spinlock.h
@@ -53,44 +53,48 @@ _raw_compare_and_swap(volatile unsigned int *lock,
*/
#define __raw_spin_is_locked(x) ((x)->owner_cpu != 0)
-#define __raw_spin_lock_flags(lock, flags) __raw_spin_lock(lock)
#define __raw_spin_unlock_wait(lock) \
do { while (__raw_spin_is_locked(lock)) \
_raw_spin_relax(lock); } while (0)
-extern void _raw_spin_lock_wait(raw_spinlock_t *, unsigned int pc);
-extern int _raw_spin_trylock_retry(raw_spinlock_t *, unsigned int pc);
+extern void _raw_spin_lock_wait(raw_spinlock_t *);
+extern void _raw_spin_lock_wait_flags(raw_spinlock_t *, unsigned long flags);
+extern int _raw_spin_trylock_retry(raw_spinlock_t *);
extern void _raw_spin_relax(raw_spinlock_t *lock);
static inline void __raw_spin_lock(raw_spinlock_t *lp)
{
- unsigned long pc = 1 | (unsigned long) __builtin_return_address(0);
int old;
old = _raw_compare_and_swap(&lp->owner_cpu, 0, ~smp_processor_id());
- if (likely(old == 0)) {
- lp->owner_pc = pc;
+ if (likely(old == 0))
return;
- }
- _raw_spin_lock_wait(lp, pc);
+ _raw_spin_lock_wait(lp);
+}
+
+static inline void __raw_spin_lock_flags(raw_spinlock_t *lp,
+ unsigned long flags)
+{
+ int old;
+
+ old = _raw_compare_and_swap(&lp->owner_cpu, 0, ~smp_processor_id());
+ if (likely(old == 0))
+ return;
+ _raw_spin_lock_wait_flags(lp, flags);
}
static inline int __raw_spin_trylock(raw_spinlock_t *lp)
{
- unsigned long pc = 1 | (unsigned long) __builtin_return_address(0);
int old;
old = _raw_compare_and_swap(&lp->owner_cpu, 0, ~smp_processor_id());
- if (likely(old == 0)) {
- lp->owner_pc = pc;
+ if (likely(old == 0))
return 1;
- }
- return _raw_spin_trylock_retry(lp, pc);
+ return _raw_spin_trylock_retry(lp);
}
static inline void __raw_spin_unlock(raw_spinlock_t *lp)
{
- lp->owner_pc = 0;
_raw_compare_and_swap(&lp->owner_cpu, lp->owner_cpu, 0);
}
diff --git a/include/asm-s390/spinlock_types.h b/include/asm-s390/spinlock_types.h
index b7ac13f7aa3..654abc40de0 100644
--- a/include/asm-s390/spinlock_types.h
+++ b/include/asm-s390/spinlock_types.h
@@ -7,7 +7,6 @@
typedef struct {
volatile unsigned int owner_cpu;
- volatile unsigned int owner_pc;
} __attribute__ ((aligned (4))) raw_spinlock_t;
#define __RAW_SPIN_LOCK_UNLOCKED { 0 }
diff --git a/include/asm-s390/tlbflush.h b/include/asm-s390/tlbflush.h
index a69bd2490d5..70fa5ae5818 100644
--- a/include/asm-s390/tlbflush.h
+++ b/include/asm-s390/tlbflush.h
@@ -42,11 +42,11 @@ static inline void __tlb_flush_global(void)
/*
* Flush all tlb entries of a page table on all cpus.
*/
-static inline void __tlb_flush_idte(pgd_t *pgd)
+static inline void __tlb_flush_idte(unsigned long asce)
{
asm volatile(
" .insn rrf,0xb98e0000,0,%0,%1,0"
- : : "a" (2048), "a" (__pa(pgd) & PAGE_MASK) : "cc" );
+ : : "a" (2048), "a" (asce) : "cc" );
}
static inline void __tlb_flush_mm(struct mm_struct * mm)
@@ -61,11 +61,11 @@ static inline void __tlb_flush_mm(struct mm_struct * mm)
* only ran on the local cpu.
*/
if (MACHINE_HAS_IDTE) {
- pgd_t *shadow_pgd = get_shadow_table(mm->pgd);
+ pgd_t *shadow = get_shadow_table(mm->pgd);
- if (shadow_pgd)
- __tlb_flush_idte(shadow_pgd);
- __tlb_flush_idte(mm->pgd);
+ if (shadow)
+ __tlb_flush_idte((unsigned long) shadow | mm->context);
+ __tlb_flush_idte((unsigned long) mm->pgd | mm->context);
return;
}
preempt_disable();
@@ -106,9 +106,23 @@ static inline void __tlb_flush_mm_cond(struct mm_struct * mm)
*/
#define flush_tlb() do { } while (0)
#define flush_tlb_all() do { } while (0)
-#define flush_tlb_mm(mm) __tlb_flush_mm_cond(mm)
#define flush_tlb_page(vma, addr) do { } while (0)
-#define flush_tlb_range(vma, start, end) __tlb_flush_mm_cond(mm)
-#define flush_tlb_kernel_range(start, end) __tlb_flush_mm(&init_mm)
+
+static inline void flush_tlb_mm(struct mm_struct *mm)
+{
+ __tlb_flush_mm_cond(mm);
+}
+
+static inline void flush_tlb_range(struct vm_area_struct *vma,
+ unsigned long start, unsigned long end)
+{
+ __tlb_flush_mm_cond(vma->vm_mm);
+}
+
+static inline void flush_tlb_kernel_range(unsigned long start,
+ unsigned long end)
+{
+ __tlb_flush_mm(&init_mm);
+}
#endif /* _S390_TLBFLUSH_H */
diff --git a/include/asm-s390/zcrypt.h b/include/asm-s390/zcrypt.h
index a5dada61775..f228f1b8687 100644
--- a/include/asm-s390/zcrypt.h
+++ b/include/asm-s390/zcrypt.h
@@ -117,7 +117,7 @@ struct CPRBX {
unsigned char padx004[16 - sizeof (char *)];
unsigned char * req_extb; /* request extension block 'addr'*/
unsigned char padx005[16 - sizeof (char *)];
- unsigned char * rpl_extb; /* reply extension block 'addres'*/
+ unsigned char * rpl_extb; /* reply extension block 'address'*/
unsigned short ccp_rtcode; /* server return code */
unsigned short ccp_rscode; /* server reason code */
unsigned int mac_data_len; /* Mac Data Length */
diff --git a/kernel/sysctl_check.c b/kernel/sysctl_check.c
index a68425a5cc1..d8a5558a47b 100644
--- a/kernel/sysctl_check.c
+++ b/kernel/sysctl_check.c
@@ -1,6 +1,5 @@
#include <linux/stat.h>
#include <linux/sysctl.h>
-#include "../arch/s390/appldata/appldata.h"
#include "../fs/xfs/linux-2.6/xfs_sysctl.h"
#include <linux/sunrpc/debug.h>
#include <linux/string.h>