diff --git a/[refs] b/[refs] index bd495f8d1b66..88c277937bb8 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: 0444fa78751260b38f0db3418e001bf86593f05f +refs/heads/master: 6ae8b1efcc83103f2e323c9486f56a8671ca1880 diff --git a/trunk/Documentation/DocBook/s390-drivers.tmpl b/trunk/Documentation/DocBook/s390-drivers.tmpl index 3d2f31b99dd9..254e769282a4 100644 --- a/trunk/Documentation/DocBook/s390-drivers.tmpl +++ b/trunk/Documentation/DocBook/s390-drivers.tmpl @@ -116,7 +116,6 @@ !Iinclude/asm-s390/ccwdev.h !Edrivers/s390/cio/device.c !Edrivers/s390/cio/device_ops.c -!Edrivers/s390/cio/airq.c The channel-measurement facility diff --git a/trunk/Documentation/cpu-hotplug.txt b/trunk/Documentation/cpu-hotplug.txt index ba0aacde94fb..fb94f5a71b68 100644 --- a/trunk/Documentation/cpu-hotplug.txt +++ b/trunk/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 +- x86_64, ia64, s390 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/trunk/Documentation/kernel-parameters.txt b/trunk/Documentation/kernel-parameters.txt index 880f882160e2..65de5ba7b74c 100644 --- a/trunk/Documentation/kernel-parameters.txt +++ b/trunk/Documentation/kernel-parameters.txt @@ -370,8 +370,7 @@ 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. - ccw_timeout_log [S390] - See Documentation/s390/CommonIO for details. + chandev= [HW,NET] Generic channel device initialisation checkreqprot [SELINUX] Set initial checkreqprot flag value. Format: { "0" | "1" } @@ -383,12 +382,6 @@ 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/trunk/Documentation/s390/CommonIO b/trunk/Documentation/s390/CommonIO index 8fbc0a852870..86320aa3fb0b 100644 --- a/trunk/Documentation/s390/CommonIO +++ b/trunk/Documentation/s390/CommonIO @@ -4,11 +4,6 @@ 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/trunk/arch/s390/Kconfig b/trunk/arch/s390/Kconfig index 6ef54d27fc00..1330061020ab 100644 --- a/trunk/arch/s390/Kconfig +++ b/trunk/arch/s390/Kconfig @@ -276,6 +276,9 @@ 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/trunk/arch/s390/crypto/Kconfig b/trunk/arch/s390/crypto/Kconfig new file mode 100644 index 000000000000..d1defbbfcd81 --- /dev/null +++ b/trunk/arch/s390/crypto/Kconfig @@ -0,0 +1,60 @@ +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/trunk/arch/s390/crypto/aes_s390.c b/trunk/arch/s390/crypto/aes_s390.c index a3f67f8b5427..46c97058ebe1 100644 --- a/trunk/arch/s390/crypto/aes_s390.c +++ b/trunk/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/trunk/arch/s390/crypto/prng.c b/trunk/arch/s390/crypto/prng.c index 0cfefddd8375..8eb3a1aedc22 100644 --- a/trunk/arch/s390/crypto/prng.c +++ b/trunk/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 length, we split it into chunks */ + /* nbytes can be arbitrary long, we spilt 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 const struct file_operations prng_fops = { +static struct file_operations prng_fops = { .owner = THIS_MODULE, .open = &prng_open, .release = NULL, diff --git a/trunk/arch/s390/kernel/Makefile b/trunk/arch/s390/kernel/Makefile index b3b650a93c7c..56cb71007cd9 100644 --- a/trunk/arch/s390/kernel/Makefile +++ b/trunk/arch/s390/kernel/Makefile @@ -31,3 +31,7 @@ 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/trunk/arch/s390/kernel/early.c b/trunk/arch/s390/kernel/early.c index 9f7b73b180f0..1b3af7dab816 100644 --- a/trunk/arch/s390/kernel/early.c +++ b/trunk/arch/s390/kernel/early.c @@ -276,7 +276,7 @@ void __init startup_init(void) create_kernel_nss(); sort_main_extable(); setup_lowcore_early(); - sclp_read_info_early(); + sclp_readinfo_early(); sclp_facilities_detect(); memsize = sclp_memory_detect(); #ifndef CONFIG_64BIT diff --git a/trunk/arch/s390/kernel/head64.S b/trunk/arch/s390/kernel/head64.S index 79dccd206a6e..a87b1976d409 100644 --- a/trunk/arch/s390/kernel/head64.S +++ b/trunk/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,2048 + lhi %r1,2094 lhi %r2,0 .long 0xb98e2001 oi 7(%r12),0x80 # set IDTE flag diff --git a/trunk/arch/s390/kernel/ipl.c b/trunk/arch/s390/kernel/ipl.c index db28cca81fef..b97694fa62ec 100644 --- a/trunk/arch/s390/kernel/ipl.c +++ b/trunk/arch/s390/kernel/ipl.c @@ -2,7 +2,7 @@ * arch/s390/kernel/ipl.c * ipl/reipl/dump support for Linux on s390. * - * Copyright IBM Corp. 2005,2007 + * Copyright (C) IBM Corp. 2005,2006 * Author(s): Michael Holzheu * Heiko Carstens * Volker Sameske @@ -31,43 +31,6 @@ #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) { @@ -91,6 +54,10 @@ 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) { @@ -132,6 +99,30 @@ 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; @@ -149,6 +140,8 @@ 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) @@ -212,8 +205,8 @@ static ssize_t sys_##_prefix##_##_name##_store(struct kobject *kobj, \ struct kobj_attribute *attr, \ const char *buf, size_t len) \ { \ - strncpy(_value, buf, sizeof(_value) - 1); \ - strstrip(_value); \ + if (sscanf(buf, _fmt_in, _value) != 1) \ + return -EINVAL; \ return len; \ } \ static struct kobj_attribute sys_##_prefix##_##_name##_attr = \ @@ -252,6 +245,33 @@ 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); @@ -408,74 +428,8 @@ 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 shutdown action: Reboot Linux on shutdown. + * reipl section */ /* FCP reipl device attributes */ @@ -595,9 +549,7 @@ static int reipl_set_type(enum ipl_type type) switch(type) { case IPL_TYPE_CCW: - if (diag308_set_works) - reipl_method = REIPL_METHOD_CCW_DIAG; - else if (MACHINE_IS_VM) + if (MACHINE_IS_VM) reipl_method = REIPL_METHOD_CCW_VM; else reipl_method = REIPL_METHOD_CCW_CIO; @@ -648,11 +600,143 @@ 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; -void reipl_run(struct shutdown_trigger *trigger) +/* + * 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) { struct ccw_dev_id devid; static char buf[100]; @@ -661,6 +745,8 @@ void reipl_run(struct shutdown_trigger *trigger) 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; @@ -701,21 +787,113 @@ void reipl_run(struct shutdown_trigger *trigger) default: break; } + signal_processor(smp_processor_id(), sigp_stop_and_store_status); } -static void __init reipl_probe(void) +static void do_dump(void) { - void *buffer; + struct ccw_dev_id devid; + static char buf[100]; - buffer = (void *) get_zeroed_page(GFP_KERNEL); - if (!buffer) + 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; - if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) - diag308_set_works = 1; - free_page((unsigned long)buffer); + } + printk(KERN_EMERG "Dump failed!\n"); } -static int __init reipl_nss_init(void) +/* 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) +{ + void *buffer; + + buffer = (void *) get_zeroed_page(GFP_KERNEL); + if (!buffer) + return; + if (diag308(DIAG308_STORE, buffer) == DIAG308_RC_OK) + diag308_set_works = 1; + free_page((unsigned long)buffer); +} + +static int __init reipl_nss_init(void) { int rc; @@ -745,7 +923,6 @@ 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, @@ -754,7 +931,8 @@ 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); - if (!MACHINE_IS_VM && !diag308_set_works) + /* FIXME: check for diag308_set_works when enabling diag ccw reipl */ + if (!MACHINE_IS_VM) 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; @@ -792,7 +970,7 @@ static int __init reipl_fcp_init(void) return 0; } -static int reipl_init(void) +static int __init reipl_init(void) { int rc; @@ -819,140 +997,6 @@ static int 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; @@ -998,14 +1042,31 @@ static int __init dump_fcp_init(void) return 0; } -static int dump_init(void) +#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) { 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.attr); + rc = sysfs_create_file(&dump_kset->kobj, &dump_type_attr); if (rc) { kset_unregister(dump_kset); return rc; @@ -1020,381 +1081,47 @@ static int dump_init(void) return 0; } -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) -{ - 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) +static int __init shutdown_actions_init(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; + int rc; -static void __init shutdown_triggers_init(void) -{ shutdown_actions_kset = kset_create_and_add("shutdown_actions", NULL, firmware_kobj); if (!shutdown_actions_kset) - 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; + return -ENOMEM; + rc = sysfs_create_file(&shutdown_actions_kset->kobj, &on_panic_attr); + if (rc) { + kset_unregister(shutdown_actions_kset); + return rc; } + atomic_notifier_chain_register(&panic_notifier_list, + &shutdown_on_panic_nb); + return 0; } static int __init s390_ipl_init(void) { - reipl_probe(); + int rc; + sclp_get_ipl_info(&sclp_ipl_info); - shutdown_actions_init(); - shutdown_triggers_init(); + 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; 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; @@ -1475,4 +1202,3 @@ void s390_reset_system(void) do_reset_calls(); } - diff --git a/trunk/arch/s390/kernel/process.c b/trunk/arch/s390/kernel/process.c index 0e7aca039307..29f7884b4ffa 100644 --- a/trunk/arch/s390/kernel/process.c +++ b/trunk/arch/s390/kernel/process.c @@ -36,7 +36,7 @@ #include #include #include -#include + #include #include #include @@ -182,15 +182,13 @@ void cpu_idle(void) void show_regs(struct pt_regs *regs) { - 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); + 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); + show_registers(regs); /* Show stack backtrace if pt_regs is from kernel mode */ if (!(regs->psw.mask & PSW_MASK_PSTATE)) diff --git a/trunk/arch/s390/kernel/ptrace.c b/trunk/arch/s390/kernel/ptrace.c index 6e036bae9875..1d81bf9488ae 100644 --- a/trunk/arch/s390/kernel/ptrace.c +++ b/trunk/arch/s390/kernel/ptrace.c @@ -86,13 +86,13 @@ FixPerRegisters(struct task_struct *task) per_info->control_regs.bits.storage_alt_space_ctl = 0; } -void user_enable_single_step(struct task_struct *task) +static void set_single_step(struct task_struct *task) { task->thread.per_info.single_step = 1; FixPerRegisters(task); } -void user_disable_single_step(struct task_struct *task) +static void clear_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. */ - user_disable_single_step(child); + clear_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. */ - user_disable_single_step(child); + clear_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. */ - user_disable_single_step(child); + clear_single_step(child); wake_up_process(child); return 0; @@ -675,7 +675,10 @@ 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; - user_enable_single_step(child); + if (data) + set_tsk_thread_flag(child, TIF_SINGLE_STEP); + else + set_single_step(child); /* give it a chance to run. */ wake_up_process(child); return 0; diff --git a/trunk/arch/s390/kernel/setup.c b/trunk/arch/s390/kernel/setup.c index 766c783bd7a7..577aa7dd660e 100644 --- a/trunk/arch/s390/kernel/setup.c +++ b/trunk/arch/s390/kernel/setup.c @@ -125,6 +125,75 @@ void __cpuinit cpu_init(void) enter_lazy_tlb(&init_mm, current); } +/* + * 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. */ @@ -239,6 +308,38 @@ 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. @@ -458,9 +559,7 @@ setup_resources(void) data_resource.start = (unsigned long) &_etext; data_resource.end = (unsigned long) &_edata - 1; - for (i = 0; i < MEMORY_CHUNKS; i++) { - if (!memory_chunk[i].size) - continue; + for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { res = alloc_bootmem_low(sizeof(struct resource)); res->flags = IORESOURCE_BUSY | IORESOURCE_MEM; switch (memory_chunk[i].type) { @@ -518,7 +617,7 @@ EXPORT_SYMBOL_GPL(real_memory_size); static void __init setup_memory_end(void) { unsigned long memory_size; - unsigned long max_mem; + unsigned long max_mem, max_phys; int i; #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE) @@ -526,31 +625,10 @@ 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(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; - } - } + max_mem = memory_end ? min(max_phys, memory_end) : max_phys; for (i = 0; i < MEMORY_CHUNKS; i++) { struct mem_chunk *chunk = &memory_chunk[i]; @@ -812,7 +890,7 @@ setup_arch(char **cmdline_p) parse_early_param(); - setup_ipl(); + setup_ipl_info(); setup_memory_end(); setup_addressing_mode(); setup_memory(); @@ -821,6 +899,7 @@ 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). @@ -841,7 +920,7 @@ setup_arch(char **cmdline_p) void __cpuinit print_cpu_info(struct cpuinfo_S390 *cpuinfo) { - printk(KERN_INFO "cpu %d " + printk("cpu %d " #ifdef CONFIG_SMP "phys_idx=%d " #endif @@ -917,7 +996,7 @@ static void *c_next(struct seq_file *m, void *v, loff_t *pos) static void c_stop(struct seq_file *m, void *v) { } -const struct seq_operations cpuinfo_op = { +struct seq_operations cpuinfo_op = { .start = c_start, .next = c_next, .stop = c_stop, diff --git a/trunk/arch/s390/kernel/signal.c b/trunk/arch/s390/kernel/signal.c index 4449bf32cbf1..d264671c1b71 100644 --- a/trunk/arch/s390/kernel/signal.c +++ b/trunk/arch/s390/kernel/signal.c @@ -471,7 +471,6 @@ 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, @@ -479,12 +478,15 @@ void do_signal(struct pt_regs *regs) siginfo_t *info, sigset_t *oldset, struct pt_regs *regs); - ret = handle_signal32(signr, &ka, &info, oldset, regs); + if (handle_signal32( + signr, &ka, &info, oldset, regs) == 0) { + if (test_thread_flag(TIF_RESTORE_SIGMASK)) + clear_thread_flag(TIF_RESTORE_SIGMASK); + } + return; } - else #endif - ret = handle_signal(signr, &ka, &info, oldset, regs); - if (!ret) { + if (handle_signal(signr, &ka, &info, oldset, regs) == 0) { /* * A signal was successfully delivered; the saved * sigmask will have been stored in the signal frame, @@ -493,14 +495,6 @@ 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/trunk/arch/s390/kernel/smp.c b/trunk/arch/s390/kernel/smp.c index aa37fa154512..264ea906db4c 100644 --- a/trunk/arch/s390/kernel/smp.c +++ b/trunk/arch/s390/kernel/smp.c @@ -42,7 +42,6 @@ #include #include #include -#include #include /* @@ -54,27 +53,11 @@ EXPORT_SYMBOL(lowcore_ptr); cpumask_t cpu_online_map = CPU_MASK_NONE; EXPORT_SYMBOL(cpu_online_map); -cpumask_t cpu_possible_map = CPU_MASK_ALL; +cpumask_t cpu_possible_map = CPU_MASK_NONE; 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); /* @@ -210,33 +193,6 @@ 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; @@ -260,6 +216,33 @@ 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. @@ -372,13 +355,6 @@ 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) /* @@ -399,10 +375,9 @@ 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] = 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) + 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) cpu_relax(); memcpy(zfcpdump_save_areas[cpu], (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE, @@ -422,155 +397,32 @@ static inline void smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) { } #endif /* CONFIG_ZFCPDUMP || CONFIG_ZFCPDUMP_MODULE */ -static int cpu_stopped(int cpu) -{ - __u32 status; - - /* 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) +/* + * Lets check how many CPUs we have. + */ +static unsigned int __init smp_count_cpus(void) { - unsigned int cpu, c_cpus, s_cpus; - struct sclp_cpu_info *info; - u16 boot_cpu_addr, cpu_addr; + unsigned int cpu, num_cpus; + __u16 boot_cpu_addr; - c_cpus = 1; - s_cpus = 0; + /* + * cpu 0 is the boot cpu. See smp_prepare_boot_cpu. + */ boot_cpu_addr = S390_lowcore.cpu_data.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) + current_thread_info()->cpu = 0; + num_cpus = 1; + for (cpu = 0; cpu <= 65535; cpu++) { + if ((__u16) cpu == boot_cpu_addr) continue; - __cpu_logical_map[CPU_INIT_NO] = cpu_addr; - if (!cpu_stopped(CPU_INIT_NO)) { - s_cpus++; + __cpu_logical_map[1] = (__u16) cpu; + if (signal_processor(1, sigp_sense) == sigp_not_operational) continue; - } - smp_get_save_area(c_cpus, cpu_addr); - c_cpus++; + smp_get_save_area(num_cpus, cpu); + 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(); + printk("Detected %d CPU's\n", (int) num_cpus); + printk("Boot cpu address %2X\n", boot_cpu_addr); + return num_cpus; } /* @@ -601,6 +453,8 @@ 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; @@ -616,82 +470,37 @@ static void __init smp_create_idle(unsigned int cpu) spin_lock_init(&(&per_cpu(s390_idle, cpu))->lock); } -static int __cpuinit smp_alloc_lowcore(int cpu) +static int cpu_stopped(int cpu) { - 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; - -#ifndef CONFIG_64BIT - if (MACHINE_HAS_IEEE) { - unsigned long save_area; + __u32 status; - save_area = get_zeroed_page(GFP_KERNEL); - if (!save_area) - goto out_save_area; - lowcore->extended_save_area_addr = (u32) save_area; + /* Check for stopped state */ + if (signal_processor_ps(&status, 0, cpu, sigp_sense) == + sigp_status_stored) { + if (status & 0x40) + return 1; } -#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; -} - -#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 */ /* Upping and downing of CPUs */ -int __cpuinit __cpu_up(unsigned int cpu) + +int __cpu_up(unsigned int cpu) { struct task_struct *idle; struct _lowcore *cpu_lowcore; struct stack_frame *sf; sigp_ccode ccode; + int curr_cpu; - if (smp_cpu_state[cpu] != CPU_STATE_CONFIGURED) - return -EIO; - if (smp_alloc_lowcore(cpu)) - return -ENOMEM; + 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; ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]), cpu, sigp_set_prefix); @@ -706,7 +515,6 @@ int __cpuinit __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)); @@ -720,8 +528,6 @@ int __cpuinit __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) @@ -732,20 +538,44 @@ int __cpuinit __cpu_up(unsigned int cpu) return 0; } -static int __init setup_possible_cpus(char *s) +static unsigned int __initdata additional_cpus; +static unsigned int __initdata possible_cpus; + +void __init smp_setup_cpu_possible_map(void) { - int pcpus, cpu; + 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); - pcpus = simple_strtoul(s, NULL, 0); - cpu_possible_map = cpumask_of_cpu(0); - for (cpu = 1; cpu < pcpus && cpu < NR_CPUS; cpu++) + for (cpu = 0; cpu < pos_cpus; cpu++) cpu_set(cpu, cpu_possible_map); - return 0; + + phy_cpus = min(phy_cpus, pos_cpus); + + for (cpu = 0; cpu < phy_cpus; cpu++) + cpu_set(cpu, cpu_present_map); } -early_param("possible_cpus", setup_possible_cpus); #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); + int __cpu_disable(void) { struct ec_creg_mask_parms cr_parms; @@ -782,8 +612,7 @@ void __cpu_die(unsigned int cpu) /* Wait until target cpu is down */ while (!smp_cpu_not_running(cpu)) cpu_relax(); - smp_free_lowcore(cpu); - printk(KERN_INFO "Processor %d spun down\n", cpu); + printk("Processor %d spun down\n", cpu); } void cpu_die(void) @@ -796,19 +625,49 @@ 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; - - smp_detect_cpus(); + int i; /* 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 */ @@ -824,17 +683,15 @@ 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; } /* @@ -848,79 +705,7 @@ int setup_profiling_timer(unsigned int multiplier) return 0; } -#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 DEFINE_PER_CPU(struct cpu, cpu_devices); static ssize_t show_capability(struct sys_device *dev, char *buf) { @@ -965,15 +750,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_online_attrs[] = { +static struct attribute *cpu_attrs[] = { &attr_capability.attr, &attr_idle_count.attr, &attr_idle_time_us.attr, NULL, }; -static struct attribute_group cpu_online_attr_group = { - .attrs = cpu_online_attrs, +static struct attribute_group cpu_attr_group = { + .attrs = cpu_attrs, }; static int __cpuinit smp_cpu_notify(struct notifier_block *self, @@ -993,12 +778,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_online_attr_group)) + if (sysfs_create_group(&s->kobj, &cpu_attr_group)) return NOTIFY_BAD; break; case CPU_DEAD: case CPU_DEAD_FROZEN: - sysfs_remove_group(&s->kobj, &cpu_online_attr_group); + sysfs_remove_group(&s->kobj, &cpu_attr_group); break; } return NOTIFY_OK; @@ -1008,62 +793,6 @@ 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; @@ -1071,14 +800,16 @@ static int __init topology_init(void) register_cpu_notifier(&smp_cpu_nb); -#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); + 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); if (rc) return rc; } diff --git a/trunk/arch/s390/kernel/traps.c b/trunk/arch/s390/kernel/traps.c index 52b8342c6bf2..8ed16a83fba7 100644 --- a/trunk/arch/s390/kernel/traps.c +++ b/trunk/arch/s390/kernel/traps.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -169,16 +168,9 @@ 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) @@ -266,14 +258,8 @@ 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] ", str, err & 0xffff, ++die_counter); -#ifdef CONFIG_PREEMPT - printk("PREEMPT "); -#endif -#ifdef CONFIG_SMP - printk("SMP"); -#endif - printk("\n"); + printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter); + print_modules(); notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV); show_regs(regs); bust_spinlocks(0); diff --git a/trunk/arch/s390/kernel/vmlinux.lds.S b/trunk/arch/s390/kernel/vmlinux.lds.S index 936159199346..849120e3e28a 100644 --- a/trunk/arch/s390/kernel/vmlinux.lds.S +++ b/trunk/arch/s390/kernel/vmlinux.lds.S @@ -17,12 +17,6 @@ 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; @@ -39,9 +33,6 @@ SECTIONS _etext = .; /* End of text section */ - NOTES :text :note - BUG_TABLE :text - RODATA #ifdef CONFIG_SHARED_KERNEL @@ -58,6 +49,9 @@ SECTIONS __stop___ex_table = .; } + NOTES + BUG_TABLE + .data : { /* Data */ DATA_DATA CONSTRUCTORS diff --git a/trunk/arch/s390/lib/spinlock.c b/trunk/arch/s390/lib/spinlock.c index e41f4008afc5..8d76403fcf89 100644 --- a/trunk/arch/s390/lib/spinlock.c +++ b/trunk/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) +void _raw_spin_lock_wait(raw_spinlock_t *lp, unsigned int pc) { int count = spin_retry; unsigned int cpu = ~smp_processor_id(); @@ -53,36 +53,15 @@ void _raw_spin_lock_wait(raw_spinlock_t *lp) } if (__raw_spin_is_locked(lp)) continue; - if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) + if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) { + lp->owner_pc = pc; return; - } -} -EXPORT_SYMBOL(_raw_spin_lock_wait); - -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); +EXPORT_SYMBOL(_raw_spin_lock_wait); -int _raw_spin_trylock_retry(raw_spinlock_t *lp) +int _raw_spin_trylock_retry(raw_spinlock_t *lp, unsigned int pc) { unsigned int cpu = ~smp_processor_id(); int count; @@ -90,8 +69,10 @@ int _raw_spin_trylock_retry(raw_spinlock_t *lp) 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) + if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) { + lp->owner_pc = pc; return 1; + } } return 0; } diff --git a/trunk/arch/s390/mm/extmem.c b/trunk/arch/s390/mm/extmem.c index 880b0ebf894b..394980b05e6f 100644 --- a/trunk/arch/s390/mm/extmem.c +++ b/trunk/arch/s390/mm/extmem.c @@ -83,7 +83,7 @@ struct dcss_segment { }; static DEFINE_MUTEX(dcss_lock); -static LIST_HEAD(dcss_list); +static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list); static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC", "EW/EN-MIXED" }; diff --git a/trunk/arch/s390/mm/vmem.c b/trunk/arch/s390/mm/vmem.c index 79d13a166a3d..fb9c5a85aa56 100644 --- a/trunk/arch/s390/mm/vmem.c +++ b/trunk/arch/s390/mm/vmem.c @@ -15,6 +15,10 @@ #include #include +unsigned long vmalloc_end; +EXPORT_SYMBOL(vmalloc_end); + +static struct page *vmem_map; static DEFINE_MUTEX(vmem_mutex); struct memory_segment { @@ -184,8 +188,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); @@ -236,10 +240,10 @@ static int vmem_add_mem(unsigned long start, unsigned long size) { int ret; - ret = vmem_add_mem_map(start, size); + ret = vmem_add_range(start, size); if (ret) return ret; - return vmem_add_range(start, size); + return vmem_add_mem_map(start, size); } /* @@ -250,7 +254,7 @@ static int insert_memory_segment(struct memory_segment *seg) { struct memory_segment *tmp; - if (seg->start + seg->size >= VMALLOC_START || + if (PFN_DOWN(seg->start + seg->size) > max_pfn || seg->start + seg->size < seg->start) return -ERANGE; @@ -353,15 +357,17 @@ int add_shared_memory(unsigned long start, unsigned long size) /* * 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; - BUILD_BUG_ON((unsigned long)VMEM_MAP + VMEM_MAP_SIZE > VMEM_MAP_MAX); - NODE_DATA(0)->node_mem_map = VMEM_MAP; + 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; + for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size); } @@ -376,7 +382,7 @@ static int __init vmem_convert_memory_chunk(void) int i; mutex_lock(&vmem_mutex); - for (i = 0; i < MEMORY_CHUNKS; i++) { + for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) { if (!memory_chunk[i].size) continue; seg = kzalloc(sizeof(*seg), GFP_KERNEL); diff --git a/trunk/drivers/crypto/Kconfig b/trunk/drivers/crypto/Kconfig index 6b658d84d521..8a70a9edabda 100644 --- a/trunk/drivers/crypto/Kconfig +++ b/trunk/drivers/crypto/Kconfig @@ -48,6 +48,8 @@ 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 @@ -81,67 +83,6 @@ 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/trunk/drivers/ide/pci/sl82c105.c b/trunk/drivers/ide/pci/sl82c105.c index 069f104fdcea..a85413467f93 100644 --- a/trunk/drivers/ide/pci/sl82c105.c +++ b/trunk/drivers/ide/pci/sl82c105.c @@ -13,6 +13,7 @@ * -- Benjamin Herrenschmidt (01/11/03) benh@kernel.crashing.org * * Copyright (C) 2006-2007 MontaVista Software, Inc. + * Copyright (C) 2007 Bartlomiej Zolnierkiewicz */ #include @@ -90,14 +91,8 @@ static void sl82c105_set_pio_mode(ide_drive_t *drive, const u8 pio) drive->drive_data &= 0xffff0000; drive->drive_data |= drv_ctrl; - if (!drive->using_dma) { - /* - * If we are actually using MW DMA, then we can not - * reprogram the interface drive control register. - */ - pci_write_config_word(dev, reg, drv_ctrl); - pci_read_config_word (dev, reg, &drv_ctrl); - } + pci_write_config_word(dev, reg, drv_ctrl); + pci_read_config_word (dev, reg, &drv_ctrl); printk(KERN_DEBUG "%s: selected %s (%dns) (%04X)\n", drive->name, ide_xfer_verbose(pio + XFER_PIO_0), @@ -123,17 +118,6 @@ static void sl82c105_set_dma_mode(ide_drive_t *drive, const u8 speed) */ drive->drive_data &= 0x0000ffff; drive->drive_data |= (unsigned long)drv_ctrl << 16; - - /* - * If we are already using DMA, we just reprogram - * the drive control register. - */ - if (drive->using_dma) { - struct pci_dev *dev = HWIF(drive)->pci_dev; - int reg = 0x44 + drive->dn * 4; - - pci_write_config_word(dev, reg, drv_ctrl); - } } /* @@ -201,6 +185,11 @@ static void sl82c105_dma_start(ide_drive_t *drive) { ide_hwif_t *hwif = HWIF(drive); struct pci_dev *dev = hwif->pci_dev; + int reg = 0x44 + drive->dn * 4; + + DBG(("%s(drive:%s)\n", __FUNCTION__, drive->name)); + + pci_write_config_word(dev, reg, drive->drive_data >> 16); sl82c105_reset_host(dev); ide_dma_start(drive); @@ -214,32 +203,19 @@ static void sl82c105_dma_timeout(ide_drive_t *drive) ide_dma_timeout(drive); } -static int sl82c105_ide_dma_on(ide_drive_t *drive) -{ - struct pci_dev *dev = HWIF(drive)->pci_dev; - int rc, reg = 0x44 + drive->dn * 4; - - DBG(("sl82c105_ide_dma_on(drive:%s)\n", drive->name)); - - rc = __ide_dma_on(drive); - if (rc == 0) { - pci_write_config_word(dev, reg, drive->drive_data >> 16); - - printk(KERN_INFO "%s: DMA enabled\n", drive->name); - } - return rc; -} - -static void sl82c105_dma_off_quietly(ide_drive_t *drive) +static int sl82c105_dma_end(ide_drive_t *drive) { struct pci_dev *dev = HWIF(drive)->pci_dev; int reg = 0x44 + drive->dn * 4; + int ret; + + DBG(("%s(drive:%s)\n", __FUNCTION__, drive->name)); - DBG(("sl82c105_dma_off_quietly(drive:%s)\n", drive->name)); + ret = __ide_dma_end(drive); pci_write_config_word(dev, reg, drive->drive_data); - ide_dma_off_quietly(drive); + return ret; } /* @@ -369,10 +345,9 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif) hwif->mwdma_mask = ATA_MWDMA2; - hwif->ide_dma_on = &sl82c105_ide_dma_on; - hwif->dma_off_quietly = &sl82c105_dma_off_quietly; hwif->dma_lost_irq = &sl82c105_dma_lost_irq; hwif->dma_start = &sl82c105_dma_start; + hwif->ide_dma_end = &sl82c105_dma_end; hwif->dma_timeout = &sl82c105_dma_timeout; if (hwif->mate) diff --git a/trunk/drivers/s390/block/Makefile b/trunk/drivers/s390/block/Makefile index 0a89e080b389..be9f22d52fd8 100644 --- a/trunk/drivers/s390/block/Makefile +++ b/trunk/drivers/s390/block/Makefile @@ -2,8 +2,8 @@ # S/390 block devices # -dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o -dasd_fba_mod-objs := dasd_fba.o +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_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/trunk/drivers/s390/block/dasd.c b/trunk/drivers/s390/block/dasd.c index 1db15f3e5d20..e6bfce690ca3 100644 --- a/trunk/drivers/s390/block/dasd.c +++ b/trunk/drivers/s390/block/dasd.c @@ -48,15 +48,13 @@ MODULE_LICENSE("GPL"); /* * SECTION: prototypes for static functions of dasd.c */ -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 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 void do_kick_device(struct work_struct *); -static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); /* * SECTION: Operations on the device structure. @@ -67,23 +65,26 @@ 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) + device = kzalloc(sizeof (struct dasd_device), GFP_ATOMIC); + if (device == NULL) 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) { + if (device->ccw_mem == NULL) { 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) { + if (device->erp_mem == NULL) { free_pages((unsigned long) device->ccw_mem, 1); kfree(device); return ERR_PTR(-ENOMEM); @@ -92,9 +93,10 @@ struct dasd_device *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); - atomic_set(&device->tasklet_scheduled, 0); + spin_lock_init(&device->request_queue_lock); + atomic_set (&device->tasklet_scheduled, 0); tasklet_init(&device->tasklet, - (void (*)(unsigned long)) dasd_device_tasklet, + (void (*)(unsigned long)) dasd_tasklet, (unsigned long) device); INIT_LIST_HEAD(&device->ccw_queue); init_timer(&device->timer); @@ -108,7 +110,8 @@ struct dasd_device *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); @@ -116,43 +119,11 @@ void dasd_free_device(struct dasd_device *device) kfree(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; @@ -162,13 +133,12 @@ static int dasd_state_new_to_known(struct dasd_device *device) */ dasd_get_device(device); - if (device->block) { - rc = dasd_alloc_queue(device->block); - if (rc) { - dasd_put_device(device); - return rc; - } + rc = dasd_alloc_queue(device); + if (rc) { + dasd_put_device(device); + return rc; } + device->state = DASD_STATE_KNOWN; return 0; } @@ -176,24 +146,21 @@ static int 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->uncheck_device) - device->discipline->uncheck_device(device); + if (device->discipline) 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; - if (device->block) - dasd_free_queue(device->block); + dasd_free_queue(device); /* Give up reference we took in dasd_state_new_to_known. */ dasd_put_device(device); @@ -203,19 +170,19 @@ static int 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. */ - if (device->block) { - rc = dasd_gendisk_alloc(device->block); - if (rc) - return rc; - } + rc = dasd_gendisk_alloc(device); + 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, 1, - 8 * sizeof(long)); + device->debug_area = debug_register(device->cdev->dev.bus_id, 1, 2, + 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"); @@ -227,17 +194,16 @@ static int 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; - if (device->block) { - dasd_gendisk_free(device->block); - dasd_block_clear_timer(device->block); - } - rc = dasd_flush_device_queue(device); + + dasd_gendisk_free(device); + rc = dasd_flush_ccw_queue(device, 1); if (rc) return rc; - dasd_device_clear_timer(device); + dasd_clear_timer(device); DBF_DEV_EVENT(DBF_EMERG, device, "%p debug area deleted", device); if (device->debug_area != NULL) { @@ -262,32 +228,26 @@ static int 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; - block = device->block; - /* make disk known with correct capacity */ - 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; + if (device->discipline->do_analysis != NULL) + rc = device->discipline->do_analysis(device); + if (rc) { + if (rc != -EAGAIN) + device->state = DASD_STATE_UNFMT; + return rc; } + /* 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; return rc; } @@ -296,31 +256,28 @@ static int 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; @@ -334,31 +291,17 @@ static int 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; - if (device->block) - dasd_schedule_block_bh(device->block); + dasd_schedule_bh(device); 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; } @@ -366,7 +309,8 @@ static int 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; @@ -401,7 +345,8 @@ static int 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; @@ -436,7 +381,8 @@ static int 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; @@ -463,15 +409,17 @@ static void 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_device_bh(device); + dasd_schedule_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. */ @@ -481,7 +429,8 @@ void 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) @@ -498,12 +447,14 @@ void 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) @@ -524,20 +475,20 @@ unsigned int dasd_profile_level = DASD_PROFILE_OFF; /* * Increments counter in global and local profiling structures. */ -#define dasd_profile_counter(value, counter, block) \ +#define dasd_profile_counter(value, counter, device) \ { \ int index; \ for (index = 0; index < 31 && value >> (2+index); index++); \ dasd_global_profile.counter[index]++; \ - block->profile.counter[index]++; \ + device->profile.counter[index]++; \ } /* * Add profiling information for cqr before execution. */ -static void dasd_profile_start(struct dasd_block *block, - struct dasd_ccw_req *cqr, - struct request *req) +static void +dasd_profile_start(struct dasd_device *device, struct dasd_ccw_req * cqr, + struct request *req) { struct list_head *l; unsigned int counter; @@ -547,19 +498,19 @@ static void dasd_profile_start(struct dasd_block *block, /* count the length of the chanq for statistics */ counter = 0; - list_for_each(l, &block->ccw_queue) + list_for_each(l, &device->ccw_queue) if (++counter >= 31) break; dasd_global_profile.dasd_io_nr_req[counter]++; - block->profile.dasd_io_nr_req[counter]++; + device->profile.dasd_io_nr_req[counter]++; } /* * Add profiling information for cqr after execution. */ -static void dasd_profile_end(struct dasd_block *block, - struct dasd_ccw_req *cqr, - struct request *req) +static void +dasd_profile_end(struct dasd_device *device, struct dasd_ccw_req * cqr, + struct request *req) { long strtime, irqtime, endtime, tottime; /* in microseconds */ long tottimeps, sectors; @@ -581,27 +532,27 @@ static void dasd_profile_end(struct dasd_block *block, 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 (!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; + 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; - 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); + 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); } #else -#define dasd_profile_start(block, cqr, req) do {} while (0) -#define dasd_profile_end(block, cqr, req) do {} while (0) +#define dasd_profile_start(device, cqr, req) do {} while (0) +#define dasd_profile_end(device, cqr, req) do {} while (0) #endif /* CONFIG_DASD_PROFILE */ /* @@ -611,9 +562,9 @@ static void dasd_profile_end(struct dasd_block *block, * 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; @@ -649,9 +600,9 @@ struct dasd_ccw_req *dasd_kmalloc_request(char *magic, int cplength, 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; @@ -698,7 +649,8 @@ struct dasd_ccw_req *dasd_smalloc_request(char *magic, int cplength, * 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; @@ -715,7 +667,8 @@ void 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; @@ -728,13 +681,14 @@ void 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->startdev; + device = cqr->device; if (strncmp((char *) &cqr->magic, device->discipline->ebcname, 4)) { DEV_MESSAGE(KERN_WARNING, device, " dasd_ccw_req 0x%08x magic doesn't match" @@ -752,7 +706,8 @@ static inline int 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; @@ -762,13 +717,13 @@ int dasd_term_IO(struct dasd_ccw_req *cqr) if (rc) return rc; retries = 0; - device = (struct dasd_device *) cqr->startdev; + device = (struct dasd_device *) cqr->device; 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_PENDING; + cqr->status = DASD_CQR_CLEAR; cqr->stopclk = get_clock(); cqr->starttime = 0; DBF_DEV_EVENT(DBF_DEBUG, device, @@ -798,7 +753,7 @@ int dasd_term_IO(struct dasd_ccw_req *cqr) } retries++; } - dasd_schedule_device_bh(device); + dasd_schedule_bh(device); return rc; } @@ -806,7 +761,8 @@ int 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; @@ -815,12 +771,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) rc = dasd_check_cqr(cqr); if (rc) return rc; - device = (struct dasd_device *) cqr->startdev; + device = (struct dasd_device *) cqr->device; 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_ERROR; + cqr->status = DASD_CQR_FAILED; return -EIO; } cqr->startclk = get_clock(); @@ -877,7 +833,8 @@ int 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_device_timeout(unsigned long ptr) +static void +dasd_timeout_device(unsigned long ptr) { unsigned long flags; struct dasd_device *device; @@ -887,13 +844,14 @@ static void dasd_device_timeout(unsigned long ptr) /* re-activate request queue */ device->stopped &= ~DASD_STOPPED_PENDING; spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - dasd_schedule_device_bh(device); + dasd_schedule_bh(device); } /* * Setup timeout for a device in jiffies. */ -void dasd_device_set_timer(struct dasd_device *device, int expires) +void +dasd_set_timer(struct dasd_device *device, int expires) { if (expires == 0) { if (timer_pending(&device->timer)) @@ -904,7 +862,7 @@ void dasd_device_set_timer(struct dasd_device *device, int expires) if (mod_timer(&device->timer, jiffies + expires)) return; } - device->timer.function = dasd_device_timeout; + device->timer.function = dasd_timeout_device; device->timer.data = (unsigned long) device; device->timer.expires = jiffies + expires; add_timer(&device->timer); @@ -913,14 +871,15 @@ void dasd_device_set_timer(struct dasd_device *device, int expires) /* * Clear timeout for a device. */ -void dasd_device_clear_timer(struct dasd_device *device) +void +dasd_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; @@ -934,7 +893,7 @@ static void dasd_handle_killed_request(struct ccw_device *cdev, return; } - device = (struct dasd_device *) cqr->startdev; + device = (struct dasd_device *) cqr->device; if (device == NULL || device != dasd_device_from_cdev_locked(cdev) || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { @@ -946,32 +905,46 @@ static void dasd_handle_killed_request(struct ccw_device *cdev, /* Schedule request to be retried. */ cqr->status = DASD_CQR_QUEUED; - dasd_device_clear_timer(device); - dasd_schedule_device_bh(device); + dasd_clear_timer(device); + dasd_schedule_bh(device); dasd_put_device(device); } -void dasd_generic_handle_state_change(struct dasd_device *device) +static void +dasd_handle_state_change_pending(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; - dasd_schedule_device_bh(device); - if (device->block) - dasd_schedule_block_bh(device->block); + + /* 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); } /* * 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)) { @@ -996,25 +969,29 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, cdev->dev.bus_id, ((irb->scsw.cstat<<8)|irb->scsw.dstat), (unsigned int) intparm); - /* 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; + /* 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) { device = dasd_device_from_cdev_locked(cdev); if (!IS_ERR(device)) { - dasd_device_clear_timer(device); - device->discipline->handle_unsolicited_interrupt(device, - irb); + dasd_handle_state_change_pending(device); dasd_put_device(device); } return; } - device = (struct dasd_device *) cqr->startdev; - if (!device || + 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 || strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) { MESSAGE(KERN_DEBUG, "invalid device in request: bus_id %s", cdev->dev.bus_id); @@ -1022,12 +999,12 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } /* Check for clear pending */ - if (cqr->status == DASD_CQR_CLEAR_PENDING && + if (cqr->status == DASD_CQR_CLEAR && irb->scsw.fctl & SCSW_FCTL_CLEAR_FUNC) { - cqr->status = DASD_CQR_CLEARED; - dasd_device_clear_timer(device); + cqr->status = DASD_CQR_QUEUED; + dasd_clear_timer(device); wake_up(&dasd_flush_wq); - dasd_schedule_device_bh(device); + dasd_schedule_bh(device); return; } @@ -1040,170 +1017,277 @@ void 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); - next = NULL; + + /* 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); expires = 0; - 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; + if (era == dasd_era_none) { + cqr->status = DASD_CQR_DONE; cqr->stopclk = now; /* Start first request on queue if possible -> fast_io. */ - if (cqr->devlist.next != &device->ccw_queue) { - next = list_entry(cqr->devlist.next, - struct dasd_ccw_req, devlist); + 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!"); + } } - } 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); } - /* 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 + switch (era) { + case dasd_era_fatal: + cqr->status = DASD_CQR_FAILED; + cqr->stopclk = now; + break; + case dasd_era_recover: cqr->status = DASD_CQR_ERROR; - } - 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!"); + break; + default: + BUG(); + } } if (expires != 0) - dasd_device_set_timer(device, expires); + dasd_set_timer(device, expires); else - dasd_device_clear_timer(device); - dasd_schedule_device_bh(device); + dasd_clear_timer(device); + dasd_schedule_bh(device); } /* - * 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. + * posts the buffer_cache about a finalized request */ -static void __dasd_device_recovery(struct dasd_device *device, - struct dasd_ccw_req *ref_cqr) +static inline void +dasd_end_request(struct request *req, int uptodate) { - struct list_head *l, *n; - struct dasd_ccw_req *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); +} - /* - * only requeue request that came from the dasd_block layer - */ - if (!ref_cqr->block) - return; +/* + * 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; - 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; - } - } -}; + 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); +} /* - * Remove those ccw requests from the queue that need to be returned - * to the upper layer. + * Process ccw request queue. */ -static void __dasd_device_process_ccw_queue(struct dasd_device *device, - struct list_head *final_queue) +static void +__dasd_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, devlist); - + cqr = list_entry(l, struct dasd_ccw_req, list); /* Stop list processing at the first non-final request. */ - if (cqr->status == DASD_CQR_QUEUED || - cqr->status == DASD_CQR_IN_IO || - cqr->status == DASD_CQR_CLEAR_PENDING) + if (cqr->status != DASD_CQR_DONE && + cqr->status != DASD_CQR_FAILED && + cqr->status != DASD_CQR_ERROR) break; + /* Process requests with DASD_CQR_ERROR */ if (cqr->status == DASD_CQR_ERROR) { - __dasd_device_recovery(device, cqr); + 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; } + + /* Process finished ERP request. */ + if (cqr->refers) { + __dasd_process_erp(device, cqr); + goto restart; + } + /* Rechain finished requests to final queue */ - list_move_tail(&cqr->devlist, final_queue); + cqr->endclk = get_clock(); + list_move_tail(&cqr->list, 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); +} + + /* - * the cqrs from the final queue are returned to the upper layer - * by setting a dasd_block state and calling the callback function + * Fetch requests from the block device queue. */ -static void __dasd_device_process_final_queue(struct dasd_device *device, - struct list_head *final_queue) +static void +__dasd_process_blk_queue(struct dasd_device * device) { - struct list_head *l, *n; + struct request_queue *queue; + struct request *req; struct dasd_ccw_req *cqr; + int nr_queued; - 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(); + 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 (cqr->block) - spin_unlock_bh(&cqr->block->queue_lock); - if (cqr->callback != NULL) - (cqr->callback)(cqr, cqr->callback_data); + 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; + } + 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++; } } - - /* * 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_device_check_expire(struct dasd_device *device) +static void +__dasd_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, devlist); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); 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 " @@ -1217,53 +1301,77 @@ static void __dasd_device_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_device_start_head(struct dasd_device *device) +static void +__dasd_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, devlist); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); if (cqr->status != DASD_CQR_QUEUED) return; - /* when device is stopped, return request to previous layer */ - if (device->stopped) { - cqr->status = DASD_CQR_CLEARED; - dasd_schedule_device_bh(device); + /* 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); 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_device_set_timer(device, cqr->expires); + dasd_set_timer(device, cqr->expires); else if (rc == -EACCES) { - dasd_schedule_device_bh(device); + dasd_schedule_bh(device); } else /* Hmpf, try again in 1/2 sec */ - dasd_device_set_timer(device, 50); + dasd_set_timer(device, 50); +} + +static inline int +_wait_for_clear(struct dasd_ccw_req *cqr) +{ + return (cqr->status == DASD_CQR_QUEUED); } /* - * 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. + * 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. */ -int dasd_flush_device_queue(struct dasd_device *device) +static int +dasd_flush_ccw_queue(struct dasd_device * device, int all) { - struct dasd_ccw_req *cqr, *n; - int rc; + struct dasd_ccw_req *cqr, *orig, *n; + int rc, i; + struct list_head flush_queue; INIT_LIST_HEAD(&flush_queue); spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = 0; - list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) { +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; + } /* Check status and move request to flush_queue */ switch (cqr->status) { case DASD_CQR_IN_IO: @@ -1279,60 +1387,90 @@ int dasd_flush_device_queue(struct dasd_device *device) } break; case DASD_CQR_QUEUED: + case DASD_CQR_ERROR: + /* set request to FAILED */ cqr->stopclk = get_clock(); - cqr->status = DASD_CQR_CLEARED; + cqr->status = DASD_CQR_FAILED; break; - default: /* no need to modify the others */ + default: /* do not touch the others */ break; } - list_move_tail(&cqr->devlist, &flush_queue); + /* 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; } + finished: spin_unlock_irq(get_ccwdev_lock(device->cdev)); - /* - * 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. + /* 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); + } + return rc; +} + +/* + * Acquire the device lock and process queues for the device. */ -static void dasd_device_tasklet(struct dasd_device *device) +static void +dasd_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_device_check_expire(device); - /* find final requests on ccw queue */ - __dasd_device_process_ccw_queue(device, &final_queue); + __dasd_check_expire(device); + /* Finish off requests on ccw queue */ + __dasd_process_ccw_queue(device, &final_queue); spin_unlock_irq(get_ccwdev_lock(device->cdev)); /* Now call the callback function of requests with final status */ - __dasd_device_process_final_queue(device, &final_queue); - spin_lock_irq(get_ccwdev_lock(device->cdev)); + 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); /* Now check if the head of the ccw queue needs to be started. */ - __dasd_device_start_head(device); - spin_unlock_irq(get_ccwdev_lock(device->cdev)); + __dasd_start_head(device); + spin_unlock(get_ccwdev_lock(device->cdev)); + spin_unlock_irq(&device->request_queue_lock); dasd_put_device(device); } /* * Schedules a call to dasd_tasklet over the device tasklet. */ -void dasd_schedule_device_bh(struct dasd_device *device) +void +dasd_schedule_bh(struct dasd_device * device) { /* Protect against rescheduling. */ if (atomic_cmpxchg (&device->tasklet_scheduled, 0, 1) != 0) @@ -1342,109 +1480,160 @@ void dasd_schedule_device_bh(struct dasd_device *device) } /* - * Queue a request to the head of the device ccw_queue. - * Start the I/O if possible. + * Queue a request to the head of the ccw_queue. Start the I/O if + * possible. */ -void dasd_add_request_head(struct dasd_ccw_req *cqr) +void +dasd_add_request_head(struct dasd_ccw_req *req) { struct dasd_device *device; unsigned long flags; - device = cqr->startdev; + device = req->device; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - cqr->status = DASD_CQR_QUEUED; - list_add(&cqr->devlist, &device->ccw_queue); + req->status = DASD_CQR_QUEUED; + req->device = device; + list_add(&req->list, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_device_bh(device); + dasd_schedule_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } /* - * Queue a request to the tail of the device ccw_queue. - * Start the I/O if possible. + * Queue a request to the tail of the ccw_queue. Start the I/O if + * possible. */ -void dasd_add_request_tail(struct dasd_ccw_req *cqr) +void +dasd_add_request_tail(struct dasd_ccw_req *req) { struct dasd_device *device; unsigned long flags; - device = cqr->startdev; + device = req->device; spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); - cqr->status = DASD_CQR_QUEUED; - list_add_tail(&cqr->devlist, &device->ccw_queue); + req->status = DASD_CQR_QUEUED; + req->device = device; + list_add_tail(&req->list, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_device_bh(device); + dasd_schedule_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } /* - * Wakeup helper for the 'sleep_on' functions. + * Wakeup callback. */ -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->startdev; + device = cqr->device; spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = ((cqr->status == DASD_CQR_DONE || - cqr->status == DASD_CQR_NEED_ERP || - cqr->status == DASD_CQR_TERMINATED) && - list_empty(&cqr->devlist)); + cqr->status == DASD_CQR_FAILED) && + list_empty(&cqr->list)); spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; } /* - * Queue a request to the tail of the device ccw_queue and wait for - * it's completion. + * Attempts to start a special ccw queue and waits for its 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->startdev; + device = cqr->device; + spin_lock_irq(get_ccwdev_lock(device->cdev)); init_waitqueue_head (&wait_q); cqr->callback = dasd_wakeup_cb; cqr->callback_data = (void *) &wait_q; - dasd_add_request_tail(cqr); + 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)); + wait_event(wait_q, _wait_for_wakeup(cqr)); /* Request status is either done or failed. */ - rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; + rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; return rc; } /* - * Queue a request to the tail of the device ccw_queue and wait - * interruptible for it's completion. + * Attempts to start a special ccw queue and wait interruptible + * for its 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; + int rc, finished; + + 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; - 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)); + 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)); } - rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO; return rc; } @@ -1454,23 +1643,25 @@ int 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, devlist); + cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, list); 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->startdev; + device = cqr->device; spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = _dasd_term_running_cqr(device); if (rc) { @@ -1482,17 +1673,17 @@ int 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->devlist, &device->ccw_queue); + list_add(&cqr->list, &device->ccw_queue); /* let the bh start the request to keep them in order */ - dasd_schedule_device_bh(device); + dasd_schedule_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_DONE) ? 0 : -EIO; + rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; return rc; } @@ -1501,14 +1692,11 @@ int 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->startdev; + struct dasd_device *device = cqr->device; unsigned long flags; int rc; @@ -1516,453 +1704,74 @@ int 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 cleared */ - cqr->status = DASD_CQR_CLEARED; + /* request was not started - just set to failed */ + cqr->status = DASD_CQR_FAILED; break; case DASD_CQR_IN_IO: /* request in IO - terminate IO and release again */ - 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; - } + 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; break; - default: /* already finished or clear pending - do nothing */ + case DASD_CQR_DONE: + case DASD_CQR_FAILED: + /* already finished - do nothing */ break; - } - 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)) + default: + DEV_MESSAGE(KERN_ALERT, device, + "invalid status %02x in request", + cqr->status); 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; } /* - * 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.) + * SECTION: Block device operations (request queue, partitions, open, release). */ /* * 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_block *block; + struct dasd_device *device; - block = queue->queuedata; - spin_lock(&block->queue_lock); + device = (struct dasd_device *) queue->queuedata; + spin_lock(get_ccwdev_lock(device->cdev)); /* Get new request from the block device request queue */ - __dasd_process_request_queue(block); + __dasd_process_blk_queue(device); /* Now check if the head of the ccw queue needs to be started. */ - __dasd_block_start_head(block); - spin_unlock(&block->queue_lock); + __dasd_start_head(device); + spin_unlock(get_ccwdev_lock(device->cdev)); } /* * Allocate and initialize request queue and default I/O scheduler. */ -static int dasd_alloc_queue(struct dasd_block *block) +static int +dasd_alloc_queue(struct dasd_device * device) { int rc; - block->request_queue = blk_init_queue(do_dasd_request, - &block->request_queue_lock); - if (block->request_queue == NULL) + device->request_queue = blk_init_queue(do_dasd_request, + &device->request_queue_lock); + if (device->request_queue == NULL) return -ENOMEM; - block->request_queue->queuedata = block; + device->request_queue->queuedata = device; - elevator_exit(block->request_queue->elevator); - rc = elevator_init(block->request_queue, "deadline"); + elevator_exit(device->request_queue->elevator); + rc = elevator_init(device->request_queue, "deadline"); if (rc) { - blk_cleanup_queue(block->request_queue); + blk_cleanup_queue(device->request_queue); return rc; } return 0; @@ -1971,76 +1780,79 @@ static int dasd_alloc_queue(struct dasd_block *block) /* * Allocate and initialize request queue. */ -static void dasd_setup_queue(struct dasd_block *block) +static void +dasd_setup_queue(struct dasd_device * device) { int max; - 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); + 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); } /* * Deactivate and free request queue. */ -static void dasd_free_queue(struct dasd_block *block) +static void +dasd_free_queue(struct dasd_device * device) { - if (block->request_queue) { - blk_cleanup_queue(block->request_queue); - block->request_queue = NULL; + if (device->request_queue) { + blk_cleanup_queue(device->request_queue); + device->request_queue = NULL; } } /* * Flush request on the request queue. */ -static void dasd_flush_request_queue(struct dasd_block *block) +static void +dasd_flush_request_queue(struct dasd_device * device) { struct request *req; - if (!block->request_queue) + if (!device->request_queue) return; - spin_lock_irq(&block->request_queue_lock); - while ((req = elv_next_request(block->request_queue))) { + spin_lock_irq(&device->request_queue_lock); + while ((req = elv_next_request(device->request_queue))) { blkdev_dequeue_request(req); dasd_end_request(req, 0); } - spin_unlock_irq(&block->request_queue_lock); + spin_unlock_irq(&device->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_block *block = disk->private_data; - struct dasd_device *base = block->base; + struct dasd_device *device = disk->private_data; int rc; - atomic_inc(&block->open_count); - if (test_bit(DASD_FLAG_OFFLINE, &base->flags)) { + atomic_inc(&device->open_count); + if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) { rc = -ENODEV; goto unlock; } - if (!try_module_get(base->discipline->owner)) { + if (!try_module_get(device->discipline->owner)) { rc = -EINVAL; goto unlock; } if (dasd_probeonly) { - DEV_MESSAGE(KERN_INFO, base, "%s", + DEV_MESSAGE(KERN_INFO, device, "%s", "No access to device due to probeonly mode"); rc = -EPERM; goto out; } - if (base->state <= DASD_STATE_BASIC) { - DBF_DEV_EVENT(DBF_ERR, base, " %s", + if (device->state <= DASD_STATE_BASIC) { + DBF_DEV_EVENT(DBF_ERR, device, " %s", " Cannot open unrecognized device"); rc = -ENODEV; goto out; @@ -2049,41 +1861,41 @@ static int dasd_open(struct inode *inp, struct file *filp) return 0; out: - module_put(base->discipline->owner); + module_put(device->discipline->owner); unlock: - atomic_dec(&block->open_count); + atomic_dec(&device->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_block *block = disk->private_data; + struct dasd_device *device = disk->private_data; - atomic_dec(&block->open_count); - module_put(block->base->discipline->owner); + atomic_dec(&device->open_count); + module_put(device->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_block *block; - struct dasd_device *base; + struct dasd_device *device; - block = bdev->bd_disk->private_data; - base = block->base; - if (!block) + device = bdev->bd_disk->private_data; + if (!device) return -ENODEV; - if (!base->discipline || - !base->discipline->fill_geometry) + if (!device->discipline || + !device->discipline->fill_geometry) return -EINVAL; - base->discipline->fill_geometry(block, geo); - geo->start = get_start_sect(bdev) >> block->s2b_shift; + device->discipline->fill_geometry(device, geo); + geo->start = get_start_sect(bdev) >> device->s2b_shift; return 0; } @@ -2097,9 +1909,6 @@ dasd_device_operations = { .getgeo = dasd_getgeo, }; -/******************************************************************************* - * end of block device operations - */ static void dasd_exit(void) @@ -2128,8 +1937,9 @@ 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; @@ -2159,20 +1969,19 @@ int 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; return code: %d\n", - cdev->dev.bus_id, ret); - return 0; + "dasd_generic_probe: could not initially online " + "ccw-device %s\n", cdev->dev.bus_id); + return ret; } /* * 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; @@ -2192,15 +2001,7 @@ void 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); } /* @@ -2208,8 +2009,10 @@ void 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; @@ -2245,7 +2048,6 @@ int 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 @@ -2265,8 +2067,6 @@ int 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", @@ -2281,10 +2081,10 @@ int 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); @@ -2301,39 +2101,30 @@ int dasd_generic_set_offline(struct ccw_device *cdev) * the blkdev_get in dasd_scan_partitions. We are only interested * in the other openers. */ - 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; - } + 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; } 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; @@ -2354,22 +2145,27 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) if (device->state < DASD_STATE_BASIC) break; /* Device is active. We want to keep it. */ - 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); + 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); ret = 1; break; case CIO_OPER: /* FIXME: add a sanity check. */ - device->stopped &= ~DASD_STOPPED_DC_WAIT; - dasd_schedule_device_bh(device); - if (device->block) - dasd_schedule_block_bh(device->block); + device->stopped &= ~(DASD_STOPPED_DC_WAIT|DASD_STOPPED_DC_EIO); + dasd_schedule_bh(device); ret = 1; break; } @@ -2399,8 +2195,7 @@ 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->startdev = device; - cqr->memdev = device; + cqr->device = device; cqr->expires = 10*HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); cqr->retries = 2; @@ -2422,12 +2217,13 @@ 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->memdev); + dasd_sfree_request(cqr, cqr->device); return ret; } EXPORT_SYMBOL_GPL(dasd_generic_read_dev_chars); -static int __init dasd_init(void) +static int __init +dasd_init(void) { int rc; @@ -2435,7 +2231,7 @@ static int __init 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, 1, 8 * sizeof(long)); + dasd_debug_area = debug_register("dasd", 1, 2, 8 * sizeof (long)); if (dasd_debug_area == NULL) { rc = -ENOMEM; goto failed; @@ -2481,18 +2277,15 @@ 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_device_clear_timer); -EXPORT_SYMBOL(dasd_block_clear_timer); +EXPORT_SYMBOL(dasd_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_device_bh); -EXPORT_SYMBOL(dasd_schedule_block_bh); +EXPORT_SYMBOL(dasd_schedule_bh); EXPORT_SYMBOL(dasd_set_target_state); -EXPORT_SYMBOL(dasd_device_set_timer); -EXPORT_SYMBOL(dasd_block_set_timer); +EXPORT_SYMBOL(dasd_set_timer); EXPORT_SYMBOL(dasd_sfree_request); EXPORT_SYMBOL(dasd_sleep_on); EXPORT_SYMBOL(dasd_sleep_on_immediatly); @@ -2506,7 +2299,4 @@ 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/trunk/drivers/s390/block/dasd_3370_erp.c b/trunk/drivers/s390/block/dasd_3370_erp.c new file mode 100644 index 000000000000..1ddab8991d92 --- /dev/null +++ b/trunk/drivers/s390/block/dasd_3370_erp.c @@ -0,0 +1,84 @@ +/* + * File...........: linux/drivers/s390/block/dasd_3370_erp.c + * Author(s)......: Holger Smolinski + * Bugreports.to..: + * (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/trunk/drivers/s390/block/dasd_3990_erp.c b/trunk/drivers/s390/block/dasd_3990_erp.c index c361ab69ec00..5b7385e430ea 100644 --- a/trunk/drivers/s390/block/dasd_3990_erp.c +++ b/trunk/drivers/s390/block/dasd_3990_erp.c @@ -24,6 +24,158 @@ struct DCTL_data { unsigned short res; /* reserved */ } __attribute__ ((packed)); +/* + ***************************************************************************** + * 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 @@ -54,7 +206,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->memdev); + dasd_free_erp_request(erp, erp->device); cqr->status = final_status; return cqr; @@ -72,17 +224,15 @@ static void dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires) { - struct dasd_device *device = erp->startdev; - unsigned long flags; + struct dasd_device *device = erp->device; 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; - spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); - erp->status = DASD_CQR_FILLED; - dasd_block_set_timer(device->block, expires); + erp->status = DASD_CQR_QUEUED; + + dasd_set_timer(device, expires); } /* @@ -101,7 +251,7 @@ static struct dasd_ccw_req * dasd_3990_erp_int_req(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; /* first time set initial retry counter and erp_function */ /* and retry once without blocking queue */ @@ -142,14 +292,11 @@ 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->startdev; + struct dasd_device *device = erp->device; __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); @@ -162,8 +309,9 @@ 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 submit the request again... */ - erp->status = DASD_CQR_FILLED; + /* reset status to queued to handle the request again... */ + if (erp->status > DASD_CQR_QUEUED) + erp->status = DASD_CQR_QUEUED; erp->retries = 1; } else { DEV_MESSAGE(KERN_ERR, device, @@ -172,7 +320,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) erp->irb.esw.esw0.sublog.lpum, opm); /* post request with permanent error */ - erp->status = DASD_CQR_FAILED; + if (erp->status > DASD_CQR_QUEUED) + erp->status = DASD_CQR_FAILED; } } /* end dasd_3990_erp_alternate_path */ @@ -195,14 +344,14 @@ static struct dasd_ccw_req * dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; 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), - device); + sizeof (struct DCTL_data), + erp->device); if (IS_ERR(dctl_cqr)) { DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate DCTL-CQR"); @@ -216,14 +365,13 @@ 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->startdev = device; - dctl_cqr->memdev = device; + dctl_cqr->device = erp->device; dctl_cqr->magic = erp->magic; dctl_cqr->expires = 5 * 60 * HZ; dctl_cqr->retries = 2; @@ -287,7 +435,7 @@ static struct dasd_ccw_req * dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; /* first time set initial retry counter and erp_function */ /* and retry once without waiting for state change pending */ @@ -324,7 +472,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_FILLED; + erp->status = DASD_CQR_QUEUED; } } @@ -382,7 +530,7 @@ static void dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; char msg_format = (sense[7] & 0xF0); char msg_no = (sense[7] & 0x0F); @@ -1009,7 +1157,7 @@ static struct dasd_ccw_req * dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->function = dasd_3990_erp_com_rej; @@ -1050,7 +1198,7 @@ static struct dasd_ccw_req * dasd_3990_erp_bus_out(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; /* first time set initial retry counter and erp_function */ /* and retry once without blocking queue */ @@ -1089,7 +1237,7 @@ static struct dasd_ccw_req * dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->function = dasd_3990_erp_equip_check; @@ -1131,6 +1279,7 @@ 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 */ @@ -1150,7 +1299,7 @@ static struct dasd_ccw_req * dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->function = dasd_3990_erp_data_check; @@ -1209,7 +1358,7 @@ static struct dasd_ccw_req * dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->function = dasd_3990_erp_overrun; @@ -1238,7 +1387,7 @@ static struct dasd_ccw_req * dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->function = dasd_3990_erp_inv_format; @@ -1254,7 +1403,8 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense) } else { DEV_MESSAGE(KERN_ERR, device, "%s", - "Invalid Track Format - Fatal error"); + "Invalid Track Format - Fatal error should have " + "been handled within the interrupt handler"); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); } @@ -1278,7 +1428,7 @@ static struct dasd_ccw_req * dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->startdev; + struct dasd_device *device = default_erp->device; DEV_MESSAGE(KERN_ERR, device, "%s", "End-of-Cylinder - must never happen"); @@ -1303,7 +1453,7 @@ static struct dasd_ccw_req * dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->function = dasd_3990_erp_env_data; @@ -1313,9 +1463,11 @@ 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->status = DASD_CQR_FILLED; + + erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO); } return erp; @@ -1338,10 +1490,11 @@ static struct dasd_ccw_req * dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense) { - struct dasd_device *device = default_erp->startdev; + struct dasd_device *device = default_erp->device; DEV_MESSAGE(KERN_ERR, device, "%s", - "No Record Found - Fatal error "); + "No Record Found - Fatal error should " + "have been handled within the interrupt handler"); return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED); @@ -1364,7 +1517,7 @@ static struct dasd_ccw_req * dasd_3990_erp_file_prot(struct dasd_ccw_req * erp) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected"); @@ -1372,43 +1525,6 @@ 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 * @@ -1507,7 +1623,7 @@ static struct dasd_ccw_req * dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->retries = 256; erp->function = dasd_3990_erp_action_10_32; @@ -1541,14 +1657,13 @@ 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->startdev; + struct dasd_device *device = default_erp->device; __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, *oldccw; + struct ccw1 *ccw; DEV_MESSAGE(KERN_DEBUG, device, "%s", "Write not finished because of unexpected condition"); @@ -1587,8 +1702,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"); @@ -1597,16 +1712,10 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense) /* use original DE */ DE_data = erp->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)); + 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)) { @@ -1639,7 +1748,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; @@ -1647,7 +1756,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; @@ -1661,8 +1770,7 @@ 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->startdev = device; - erp->memdev = device; + erp->device = device; erp->magic = default_erp->magic; erp->expires = 0; erp->retries = 256; @@ -1695,7 +1803,7 @@ static struct dasd_ccw_req * dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense) { - struct dasd_device *device = previous_erp->startdev; + struct dasd_device *device = previous_erp->device; __u32 cpa = 0; struct dasd_ccw_req *cqr; struct dasd_ccw_req *erp; @@ -1719,7 +1827,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_FILLED; + previous_erp->status = DASD_CQR_QUEUED; return previous_erp; } @@ -1742,7 +1850,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)) { @@ -1781,7 +1889,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_FILLED; + erp->status = DASD_CQR_QUEUED; return erp; @@ -1860,7 +1968,9 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) * try further actions. */ erp->lpm = 0; - erp->status = DASD_CQR_NEED_ERP; + + erp->status = DASD_CQR_ERROR; + } } @@ -1937,7 +2047,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->startdev; + struct dasd_device *device = erp->device; DEV_MESSAGE(KERN_ERR, device, "%s", "Set device to suspended duplex state should be " @@ -1971,26 +2081,28 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense) { if ((erp->function == dasd_3990_erp_compound_retry) && - (erp->status == DASD_CQR_NEED_ERP)) { + (erp->status == DASD_CQR_ERROR)) { dasd_3990_erp_compound_path(erp, sense); } if ((erp->function == dasd_3990_erp_compound_path) && - (erp->status == DASD_CQR_NEED_ERP)) { + (erp->status == DASD_CQR_ERROR)) { erp = dasd_3990_erp_compound_code(erp, sense); } if ((erp->function == dasd_3990_erp_compound_code) && - (erp->status == DASD_CQR_NEED_ERP)) { + (erp->status == DASD_CQR_ERROR)) { dasd_3990_erp_compound_config(erp, sense); } /* if no compound action ERP specified, the request failed */ - if (erp->status == DASD_CQR_NEED_ERP) + if (erp->status == DASD_CQR_ERROR) { + erp->status = DASD_CQR_FAILED; + } return erp; @@ -2015,7 +2127,7 @@ static struct dasd_ccw_req * dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; erp->function = dasd_3990_erp_inspect_32; @@ -2037,7 +2149,8 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense) case 0x01: /* fatal error */ DEV_MESSAGE(KERN_ERR, device, "%s", - "Retry not recommended - Fatal error"); + "Fatal error should have been " + "handled within the interrupt handler"); erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED); break; @@ -2140,11 +2253,6 @@ 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) { @@ -2179,13 +2287,13 @@ static struct dasd_ccw_req * dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) { - struct dasd_device *device = cqr->startdev; + struct dasd_device *device = cqr->device; struct ccw1 *ccw; /* allocate additional request block */ struct dasd_ccw_req *erp; - erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device); + erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, cqr->device); if (IS_ERR(erp)) { if (cqr->retries <= 0) { DEV_MESSAGE(KERN_ERR, device, "%s", @@ -2197,7 +2305,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr) "Unable to allocate ERP request " "(%i retries left)", cqr->retries); - dasd_block_set_timer(device->block, (HZ << 3)); + dasd_set_timer(device, (HZ << 3)); } return cqr; } @@ -2211,9 +2319,7 @@ 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->startdev = device; - erp->memdev = device; - erp->block = cqr->block; + erp->device = cqr->device; erp->magic = cqr->magic; erp->expires = 0; erp->retries = 256; @@ -2360,7 +2466,7 @@ static struct dasd_ccw_req * dasd_3990_erp_further_erp(struct dasd_ccw_req *erp) { - struct dasd_device *device = erp->startdev; + struct dasd_device *device = erp->device; char *sense = erp->irb.ecw; /* check for 24 byte sense ERP */ @@ -2451,7 +2557,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, struct dasd_ccw_req *erp) { - struct dasd_device *device = erp_head->startdev; + struct dasd_device *device = erp_head->device; struct dasd_ccw_req *erp_done = erp_head; /* finished req */ struct dasd_ccw_req *erp_free = NULL; /* req to be freed */ @@ -2463,13 +2569,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->blocklist); + list_del(&erp_done->list); erp_free = erp_done; erp_done = erp_done->refers; /* free the finished erp request */ - dasd_free_erp_request(erp_free, erp_free->memdev); + dasd_free_erp_request(erp_free, erp_free->device); } /* end while */ @@ -2497,7 +2603,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, erp->retries, erp); /* handle the request again... */ - erp->status = DASD_CQR_FILLED; + erp->status = DASD_CQR_QUEUED; } } else { @@ -2514,7 +2620,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head, * DASD_3990_ERP_ACTION * * DESCRIPTION - * control routine for 3990 erp actions. + * controll routine for 3990 erp actions. * Has to be called with the queue lock (namely the s390_irq_lock) acquired. * * PARAMETER @@ -2530,8 +2636,9 @@ 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->startdev; + struct dasd_device *device = cqr->device; struct dasd_ccw_req *temp_erp = NULL; if (device->features & DASD_FEATURE_ERPLOG) { @@ -2597,11 +2704,10 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr) } } - /* 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); + /* enqueue added ERP request */ + if (erp->status == DASD_CQR_FILLED) { + erp->status = DASD_CQR_QUEUED; + list_add(&erp->list, &device->ccw_queue); } return erp; diff --git a/trunk/drivers/s390/block/dasd_9336_erp.c b/trunk/drivers/s390/block/dasd_9336_erp.c new file mode 100644 index 000000000000..6e082688475a --- /dev/null +++ b/trunk/drivers/s390/block/dasd_9336_erp.c @@ -0,0 +1,41 @@ +/* + * File...........: linux/drivers/s390/block/dasd_9336_erp.c + * Author(s)......: Holger Smolinski + * Bugreports.to..: + * (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/trunk/drivers/s390/block/dasd_9343_erp.c b/trunk/drivers/s390/block/dasd_9343_erp.c new file mode 100644 index 000000000000..ddecb9808ed4 --- /dev/null +++ b/trunk/drivers/s390/block/dasd_9343_erp.c @@ -0,0 +1,21 @@ +/* + * File...........: linux/drivers/s390/block/dasd_9345_erp.c + * Author(s)......: Holger Smolinski + * Bugreports.to..: + * (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/trunk/drivers/s390/block/dasd_alias.c b/trunk/drivers/s390/block/dasd_alias.c deleted file mode 100644 index 3a40bee9d358..000000000000 --- a/trunk/drivers/s390/block/dasd_alias.c +++ /dev/null @@ -1,903 +0,0 @@ -/* - * PAV alias management for the DASD ECKD discipline - * - * Copyright IBM Corporation, 2007 - * Author(s): Stefan Weinhuber - */ - -#include -#include -#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/trunk/drivers/s390/block/dasd_devmap.c b/trunk/drivers/s390/block/dasd_devmap.c index f4fb40257348..0c67258fb9ec 100644 --- a/trunk/drivers/s390/block/dasd_devmap.c +++ b/trunk/drivers/s390/block/dasd_devmap.c @@ -48,6 +48,22 @@ struct dasd_devmap { struct dasd_uid uid; }; +/* + * 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: * : (0x)?[0-9a-fA-F]+ @@ -705,9 +721,8 @@ 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->block - && devmap->device->block->gdp) - set_disk_ro(devmap->device->block->gdp, val); + if (devmap->device && devmap->device->gdp) + set_disk_ro(devmap->device->gdp, val); spin_unlock(&dasd_devmap_lock); return count; } @@ -878,16 +893,12 @@ 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) || 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; + if (!IS_ERR(devmap)) + alias = devmap->uid.alias; else alias = 0; spin_unlock(&dasd_devmap_lock); + return sprintf(buf, alias ? "1\n" : "0\n"); } @@ -919,36 +930,19 @@ static ssize_t dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf) { struct dasd_devmap *devmap; - char uid_string[UID_STRLEN]; - char ua_string[3]; - struct dasd_uid *uid; + char uid[UID_STRLEN]; devmap = dasd_find_busid(dev->bus_id); spin_lock(&dasd_devmap_lock); - 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); + 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; spin_unlock(&dasd_devmap_lock); - return snprintf(buf, PAGE_SIZE, "%s\n", uid_string); + + return snprintf(buf, PAGE_SIZE, "%s\n", uid); } static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL); @@ -1046,16 +1040,39 @@ 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 0; + return (srv ? 1 : 0); } EXPORT_SYMBOL_GPL(dasd_set_uid); @@ -1121,6 +1138,9 @@ 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/trunk/drivers/s390/block/dasd_diag.c b/trunk/drivers/s390/block/dasd_diag.c index d91df38ee4f7..571320ab9e1a 100644 --- a/trunk/drivers/s390/block/dasd_diag.c +++ b/trunk/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->block->bp_block, 0, NULL); + rc = mdsk_init_io(device, device->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->startdev; + device = cqr->device; if (cqr->retries < 0) { DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p " "- no retry left)", cqr); - cqr->status = DASD_CQR_ERROR; + cqr->status = DASD_CQR_FAILED; 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_SUCCESS; + cqr->status = DASD_CQR_DONE; /* 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->startdev; + device = cqr->device; mdsk_term_io(device); - mdsk_init_io(device, device->block->bp_block, 0, NULL); - cqr->status = DASD_CQR_CLEAR_PENDING; + mdsk_init_io(device, device->bp_block, 0, NULL); + cqr->status = DASD_CQR_CLEAR; cqr->stopclk = get_clock(); - dasd_schedule_device_bh(device); + dasd_schedule_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->startdev; + device = (struct dasd_device *) cqr->device; 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_PENDING) { - cqr->status = DASD_CQR_CLEARED; - dasd_device_clear_timer(device); - dasd_schedule_device_bh(device); + if (cqr->status == DASD_CQR_CLEAR) { + cqr->status = DASD_CQR_QUEUED; + dasd_clear_timer(device); + dasd_schedule_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_SUCCESS; + cqr->status = DASD_CQR_DONE; /* 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, devlist); + struct dasd_ccw_req, list); 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_device_set_timer(device, expires); + dasd_set_timer(device, expires); else - dasd_device_clear_timer(device); - dasd_schedule_device_bh(device); + dasd_clear_timer(device); + dasd_schedule_bh(device); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); } @@ -309,7 +309,6 @@ 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; @@ -329,16 +328,6 @@ 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; @@ -420,14 +409,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; - block->blocks = (unsigned long) label->block_count; + device->blocks = (unsigned long) label->block_count; } else - block->blocks = end_block; - block->bp_block = bsize; - block->s2b_shift = 0; /* bits to shift 512 to get a block */ + device->blocks = end_block; + device->bp_block = bsize; + device->s2b_shift = 0; /* bits to shift 512 to get a block */ for (sb = 512; sb < bsize; sb = sb << 1) - block->s2b_shift++; - rc = mdsk_init_io(device, block->bp_block, 0, NULL); + device->s2b_shift++; + rc = mdsk_init_io(device, device->bp_block, 0, NULL); if (rc) { DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization " "failed (rc=%d)", rc); @@ -435,9 +424,9 @@ dasd_diag_check_device(struct dasd_device *device) } else { DEV_MESSAGE(KERN_INFO, device, "(%ld B/blk): %ldkB", - (unsigned long) block->bp_block, - (unsigned long) (block->blocks << - block->s2b_shift) >> 1); + (unsigned long) device->bp_block, + (unsigned long) (device->blocks << + device->s2b_shift) >> 1); } out: free_page((long) label); @@ -447,16 +436,22 @@ dasd_diag_check_device(struct dasd_device *device) /* Fill in virtual disk geometry for device. Return zero on success, non-zero * otherwise. */ static int -dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo) +dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) { - if (dasd_check_blocksize(block->bp_block) != 0) + if (dasd_check_blocksize(device->bp_block) != 0) return -EINVAL; - geo->cylinders = (block->blocks << block->s2b_shift) >> 10; + geo->cylinders = (device->blocks << device->s2b_shift) >> 10; geo->heads = 16; - geo->sectors = 128 >> block->s2b_shift; + geo->sectors = 128 >> device->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) { @@ -471,9 +466,8 @@ 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 *memdev, - struct dasd_block *block, - struct request *req) +static struct dasd_ccw_req * +dasd_diag_build_cp(struct dasd_device * device, struct request *req) { struct dasd_ccw_req *cqr; struct dasd_diag_req *dreq; @@ -492,17 +486,17 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, rw_cmd = MDSK_WRITE_REQ; else return ERR_PTR(-EINVAL); - blksize = block->bp_block; + blksize = device->bp_block; /* Calculate record id of first and last block. */ - first_rec = req->sector >> block->s2b_shift; - last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift; + first_rec = req->sector >> device->s2b_shift; + last_rec = (req->sector + req->nr_sectors - 1) >> device->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 >> (block->s2b_shift + 9); + count += bv->bv_len >> (device->s2b_shift + 9); } /* Paranoia. */ if (count != last_rec - first_rec + 1) @@ -511,7 +505,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, datasize = sizeof(struct dasd_diag_req) + count*sizeof(struct dasd_diag_bio); cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0, - datasize, memdev); + datasize, device); if (IS_ERR(cqr)) return cqr; @@ -535,9 +529,7 @@ static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev, cqr->buildclk = get_clock(); if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->startdev = memdev; - cqr->memdev = memdev; - cqr->block = block; + cqr->device = device; cqr->expires = DIAG_TIMEOUT; cqr->status = DASD_CQR_FILLED; return cqr; @@ -551,15 +543,10 @@ 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->memdev); + dasd_sfree_request(cqr, cqr->device); 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, @@ -596,7 +583,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, - .handle_terminated_request = dasd_diag_handle_terminated_request, + .examine_error = dasd_diag_examine_error, .erp_action = dasd_diag_erp_action, .erp_postaction = dasd_diag_erp_postaction, .build_cp = dasd_diag_build_cp, diff --git a/trunk/drivers/s390/block/dasd_eckd.c b/trunk/drivers/s390/block/dasd_eckd.c index 61f16937c1e0..44adf8496bda 100644 --- a/trunk/drivers/s390/block/dasd_eckd.c +++ b/trunk/drivers/s390/block/dasd_eckd.c @@ -52,6 +52,16 @@ 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[] = { @@ -178,7 +188,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; } @@ -198,7 +208,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: @@ -270,132 +280,6 @@ 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, @@ -416,7 +300,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) { @@ -557,15 +441,12 @@ 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; - 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; - } + 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; + return 0; } @@ -589,9 +470,7 @@ 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->startdev = device; - cqr->memdev = device; - cqr->block = NULL; + cqr->device = device; cqr->expires = 10*HZ; cqr->lpm = lpm; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); @@ -632,7 +511,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->memdev); + dasd_sfree_request(cqr, cqr->device); if (ret) goto out_error; @@ -678,19 +557,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){ @@ -707,104 +586,39 @@ 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 = 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; + 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; } /* @@ -815,28 +629,28 @@ static struct dasd_ccw_req *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->memdev); - 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->device); + return rc; } /* * Valide storage server of current device. */ -static int dasd_eckd_validate_server(struct dasd_device *device) +static int +dasd_eckd_validate_server(struct dasd_device *device, struct dasd_uid *uid) { int rc; - struct dasd_eckd_private *private; /* Currently PAV is the only reason to 'validate' server on LPAR */ if (dasd_nopav || MACHINE_IS_VM) @@ -845,11 +659,9 @@ static int dasd_eckd_validate_server(struct dasd_device *device) 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", - private->uid.vendor, private->uid.serial, - private->uid.ssid, rc); + uid->vendor, uid->serial, uid->ssid, rc); /* RE-Read Configuration Data */ return dasd_eckd_read_conf(device); } @@ -862,9 +674,9 @@ static int dasd_eckd_check_characteristics(struct dasd_device *device) { struct dasd_eckd_private *private; - struct dasd_block *block; + struct dasd_uid uid; void *rdc_data; - int is_known, rc; + int rc; private = (struct dasd_eckd_private *) device->private; if (private == NULL) { @@ -887,54 +699,27 @@ dasd_eckd_check_characteristics(struct dasd_device *device) /* Read Configuration Data */ rc = dasd_eckd_read_conf(device); if (rc) - goto out_err1; + return rc; /* Generate device unique id and register in devmap */ - rc = dasd_eckd_generate_uid(device, &private->uid); + rc = dasd_eckd_generate_uid(device, &uid); if (rc) - 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); + return rc; + rc = dasd_set_uid(device->cdev, &uid); + if (rc == 1) /* new server found */ + rc = dasd_eckd_validate_server(device, &uid); if (rc) - goto out_err3; + return rc; /* 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, @@ -944,24 +729,9 @@ 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) { @@ -985,7 +755,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, @@ -1013,9 +783,7 @@ dasd_eckd_analysis_ccw(struct dasd_device *device) ccw->count = 8; ccw->cda = (__u32)(addr_t) count_data; - cqr->block = NULL; - cqr->startdev = device; - cqr->memdev = device; + cqr->device = device; cqr->retries = 0; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -1035,7 +803,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->startdev; + device = init_cqr->device; private = (struct dasd_eckd_private *) device->private; private->init_cqr_status = init_cqr->status; dasd_sfree_request(init_cqr, device); @@ -1043,13 +811,13 @@ dasd_eckd_analysis_callback(struct dasd_ccw_req *init_cqr, void *data) } static int -dasd_eckd_start_analysis(struct dasd_block *block) +dasd_eckd_start_analysis(struct dasd_device *device) { struct dasd_eckd_private *private; struct dasd_ccw_req *init_cqr; - private = (struct dasd_eckd_private *) block->base->private; - init_cqr = dasd_eckd_analysis_ccw(block->base); + private = (struct dasd_eckd_private *) device->private; + init_cqr = dasd_eckd_analysis_ccw(device); if (IS_ERR(init_cqr)) return PTR_ERR(init_cqr); init_cqr->callback = dasd_eckd_analysis_callback; @@ -1060,15 +828,13 @@ dasd_eckd_start_analysis(struct dasd_block *block) } static int -dasd_eckd_end_analysis(struct dasd_block *block) +dasd_eckd_end_analysis(struct dasd_device *device) { - 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; @@ -1080,7 +846,7 @@ dasd_eckd_end_analysis(struct dasd_block *block) private->uses_cdl = 1; /* Calculate number of blocks/records per track. */ - blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); + blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); /* Check Track 0 for Compatible Disk Layout */ count_area = NULL; for (i = 0; i < 3; i++) { @@ -1110,65 +876,56 @@ dasd_eckd_end_analysis(struct dasd_block *block) if (count_area != NULL && count_area->kl == 0) { /* we found notthing violating our disk layout */ if (dasd_check_blocksize(count_area->dl) == 0) - block->bp_block = count_area->dl; + device->bp_block = count_area->dl; } - if (block->bp_block == 0) { + if (device->bp_block == 0) { DEV_MESSAGE(KERN_WARNING, device, "%s", "Volume has incompatible disk layout"); return -EMEDIUMTYPE; } - 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++; + 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++; - blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); - block->blocks = (private->rdc_data.no_cyl * + blk_per_trk = recs_per_track(&private->rdc_data, 0, device->bp_block); + device->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", - (block->bp_block >> 10), + (device->bp_block >> 10), ((private->rdc_data.no_cyl * private->rdc_data.trk_per_cyl * - blk_per_trk * (block->bp_block >> 9)) >> 1), - ((blk_per_trk * block->bp_block) >> 10), + blk_per_trk * (device->bp_block >> 9)) >> 1), + ((blk_per_trk * device->bp_block) >> 10), private->uses_cdl ? "compatible disk layout" : "linux disk layout"); return 0; } -static int dasd_eckd_do_analysis(struct dasd_block *block) +static int +dasd_eckd_do_analysis(struct dasd_device *device) { struct dasd_eckd_private *private; - private = (struct dasd_eckd_private *) block->base->private; + private = (struct dasd_eckd_private *) device->private; if (private->init_cqr_status < 0) - return dasd_eckd_start_analysis(block); + return dasd_eckd_start_analysis(device); else - return dasd_eckd_end_analysis(block); + return dasd_eckd_end_analysis(device); } -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_block *block, struct hd_geometry *geo) +dasd_eckd_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) { struct dasd_eckd_private *private; - private = (struct dasd_eckd_private *) block->base->private; - if (dasd_check_blocksize(block->bp_block) == 0) { + private = (struct dasd_eckd_private *) device->private; + if (dasd_check_blocksize(device->bp_block) == 0) { geo->sectors = recs_per_track(&private->rdc_data, - 0, block->bp_block); + 0, device->bp_block); } geo->cylinders = private->rdc_data.no_cyl; geo->heads = private->rdc_data.trk_per_cyl; @@ -1280,7 +1037,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->block->bp_block); + device->bp_block); data += sizeof(struct LO_eckd_data); break; case 0x04: /* Invalidate track. */ @@ -1353,28 +1110,43 @@ dasd_eckd_format_device(struct dasd_device * device, ccw++; } } - 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->device = device; + fcp->retries = 2; /* set retry counter to enable ERP */ fcp->buildclk = get_clock(); fcp->status = DASD_CQR_FILLED; return fcp; } -static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) +static dasd_era_t +dasd_eckd_examine_error(struct dasd_ccw_req * cqr, struct irb * irb) { - 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; + 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; } -}; +} static dasd_erp_fn_t dasd_eckd_erp_action(struct dasd_ccw_req * cqr) { - struct dasd_device *device = (struct dasd_device *) cqr->startdev; + struct dasd_device *device = (struct dasd_device *) cqr->device; struct ccw_device *cdev = device->cdev; switch (cdev->id.cu_type) { @@ -1396,37 +1168,8 @@ dasd_eckd_erp_postaction(struct dasd_ccw_req * cqr) return dasd_default_erp_postaction; } - -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) +static struct dasd_ccw_req * +dasd_eckd_build_cp(struct dasd_device * device, struct request *req) { struct dasd_eckd_private *private; unsigned long *idaws; @@ -1442,11 +1185,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, sector_t first_trk, last_trk; unsigned int first_offs, last_offs; unsigned char cmd, rcmd; - int use_prefix; - struct dasd_device *basedev; - basedev = block->base; - private = (struct dasd_eckd_private *) basedev->private; + private = (struct dasd_eckd_private *) device->private; if (rq_data_dir(req) == READ) cmd = DASD_ECKD_CCW_READ_MT; else if (rq_data_dir(req) == WRITE) @@ -1454,13 +1194,13 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, else return ERR_PTR(-EINVAL); /* Calculate number of blocks/records per track. */ - blksize = block->bp_block; + blksize = device->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 >> block->s2b_shift; + first_rec = first_trk = req->sector >> device->s2b_shift; first_offs = sector_div(first_trk, blk_per_trk); last_rec = last_trk = - (req->sector + req->nr_sectors - 1) >> block->s2b_shift; + (req->sector + req->nr_sectors - 1) >> device->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; @@ -1469,33 +1209,20 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, if (bv->bv_len & (blksize - 1)) /* Eckd can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (block->s2b_shift + 9); + count += bv->bv_len >> (device->s2b_shift + 9); #if defined(CONFIG_64BIT) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) - cidaw += bv->bv_len >> (block->s2b_shift + 9); + cidaw += bv->bv_len >> (device->s2b_shift + 9); #endif } /* Paranoia. */ if (count != last_rec - first_rec + 1) return ERR_PTR(-EINVAL); - - /* 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); - } + /* 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) @@ -1505,42 +1232,26 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, } /* Allocate the ccw request. */ cqr = dasd_smalloc_request(dasd_eckd_discipline.name, - cplength, datasize, startdev); + cplength, datasize, device); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; - /* 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)); + /* 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); } /* 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, basedev, blksize); + last_rec - recid + 1, cmd, device, blksize); } rq_for_each_segment(bv, req, iter) { dst = page_address(bv->bv_page) + bv->bv_offset; @@ -1570,7 +1281,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, ccw[-1].flags |= CCW_FLAG_CC; locate_record(ccw++, LO_data++, trkid, recoffs + 1, - 1, rcmd, basedev, count); + 1, rcmd, device, count); } /* Locate record for standard blocks ? */ if (private->uses_cdl && recid == 2*blk_per_trk) { @@ -1578,7 +1289,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, locate_record(ccw++, LO_data++, trkid, recoffs + 1, last_rec - recid + 1, - cmd, basedev, count); + cmd, device, count); } /* Read/write ccw. */ ccw[-1].flags |= CCW_FLAG_CC; @@ -1599,9 +1310,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, } if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->startdev = startdev; - cqr->memdev = startdev; - cqr->block = block; + cqr->device = device; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ cqr->lpm = private->path_data.ppm; cqr->retries = 256; @@ -1624,10 +1333,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->block->base->private; - blksize = cqr->block->bp_block; + private = (struct dasd_eckd_private *) cqr->device->private; + blksize = cqr->device->bp_block; blk_per_trk = recs_per_track(&private->rdc_data, 0, blksize); - recid = req->sector >> cqr->block->s2b_shift; + recid = req->sector >> cqr->device->s2b_shift; ccw = cqr->cpaddr; /* Skip over define extent & locate record. */ ccw++; @@ -1658,71 +1367,10 @@ dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) } out: status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->memdev); + dasd_sfree_request(cqr, cqr->device); 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) @@ -1736,9 +1384,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; } @@ -1771,8 +1419,7 @@ 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->startdev = device; - cqr->memdev = device; + cqr->device = 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 */ @@ -1782,7 +1429,7 @@ dasd_eckd_release(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->memdev); + dasd_sfree_request(cqr, cqr->device); return rc; } @@ -1812,8 +1459,7 @@ 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->startdev = device; - cqr->memdev = device; + cqr->device = 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 */ @@ -1823,7 +1469,7 @@ dasd_eckd_reserve(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->memdev); + dasd_sfree_request(cqr, cqr->device); return rc; } @@ -1852,8 +1498,7 @@ 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->startdev = device; - cqr->memdev = device; + cqr->device = 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 */ @@ -1863,7 +1508,7 @@ dasd_eckd_steal_lock(struct dasd_device *device) rc = dasd_sleep_on_immediatly(cqr); - dasd_sfree_request(cqr, cqr->memdev); + dasd_sfree_request(cqr, cqr->device); return rc; } @@ -1881,52 +1526,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->startdev = device; - cqr->memdev = device; + cqr->device = 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; /* Performance Statistics */ + prssdp->suborder = 0x01; /* Perfomance 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->memdev); + dasd_sfree_request(cqr, cqr->device); return rc; } @@ -1949,7 +1594,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; @@ -1982,10 +1627,8 @@ dasd_eckd_set_attrib(struct dasd_device *device, void __user *argp) } static int -dasd_eckd_ioctl(struct dasd_block *block, unsigned int cmd, void __user *argp) +dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp) { - struct dasd_device *device = block->base; - switch (cmd) { case BIODASDGATTR: return dasd_eckd_get_attrib(device, argp); @@ -2042,8 +1685,9 @@ 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; @@ -2099,40 +1743,37 @@ static void dasd_eckd_dump_sense(struct dasd_device *device, } 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); + /* 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"); - } - len += dasd_eckd_dump_ccw_range(from, last, page + len); - if (len > 0) - 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"); } + len += dasd_eckd_dump_ccw_range(from, last, page + len); + if (len > 0) + printk("%s", page); free_page((unsigned long) page); } @@ -2155,20 +1796,16 @@ 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, - .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt, - .build_cp = dasd_eckd_build_alias_cp, - .free_cp = dasd_eckd_free_alias_cp, + .build_cp = dasd_eckd_build_cp, + .free_cp = dasd_eckd_free_cp, .dump_sense = dasd_eckd_dump_sense, .fill_info = dasd_eckd_fill_info, .ioctl = dasd_eckd_ioctl, diff --git a/trunk/drivers/s390/block/dasd_eckd.h b/trunk/drivers/s390/block/dasd_eckd.h index fc2509c939bc..712ff1650134 100644 --- a/trunk/drivers/s390/block/dasd_eckd.h +++ b/trunk/drivers/s390/block/dasd_eckd.h @@ -39,8 +39,6 @@ #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 @@ -139,25 +137,6 @@ 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 { @@ -275,9 +254,7 @@ struct dasd_eckd_confdata { } __attribute__ ((packed)) ned; struct { unsigned char flags; /* byte 0 */ - unsigned char res1; /* byte 1 */ - __u16 format; /* byte 2-3 */ - unsigned char res2[4]; /* byte 4-7 */ + unsigned char res2[7]; /* byte 1- 7 */ unsigned char sua_flags; /* byte 8 */ __u8 base_unit_addr; /* byte 9 */ unsigned char res3[22]; /* byte 10-31 */ @@ -366,11 +343,6 @@ struct dasd_eckd_path { __u8 npm; }; -struct dasd_rssd_features { - char feature[256]; -} __attribute__((packed)); - - /* * Perform Subsystem Function - Prepare for Read Subsystem Data */ @@ -393,99 +365,4 @@ 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/trunk/drivers/s390/block/dasd_eer.c b/trunk/drivers/s390/block/dasd_eer.c index 6e53ab606e97..0c081a664ee8 100644 --- a/trunk/drivers/s390/block/dasd_eer.c +++ b/trunk/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_DONE) ? 0 : -EIO; + snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0; if (snss_rc) data_size = 0; else @@ -404,11 +404,10 @@ 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->devlist, &device->ccw_queue); - dasd_schedule_device_bh(device); + list_add(&cqr->list, &device->ccw_queue); + dasd_schedule_bh(device); } /* @@ -416,7 +415,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->startdev; + struct dasd_device *device = cqr->device; unsigned long flags; dasd_eer_write(device, cqr, DASD_EER_STATECHANGE); @@ -459,7 +458,7 @@ int dasd_eer_enable(struct dasd_device *device) if (!cqr) return -ENOMEM; - cqr->startdev = device; + cqr->device = device; cqr->retries = 255; cqr->expires = 10 * HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); diff --git a/trunk/drivers/s390/block/dasd_erp.c b/trunk/drivers/s390/block/dasd_erp.c index 8f10000851a3..caa5d91420f8 100644 --- a/trunk/drivers/s390/block/dasd_erp.c +++ b/trunk/drivers/s390/block/dasd_erp.c @@ -46,8 +46,6 @@ 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) { @@ -68,7 +66,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; @@ -83,11 +81,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->startdev; + device = cqr->device; /* just retry - there is nothing to save ... I got no sense data.... */ if (cqr->retries > 0) { @@ -95,12 +93,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_FILLED; + cqr->status = DASD_CQR_QUEUED; } 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 */ @@ -119,12 +117,15 @@ 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 */ @@ -132,10 +133,10 @@ struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr) struct dasd_ccw_req *refers; refers = cqr->refers; - /* remove the request from the block queue */ - list_del(&cqr->blocklist); + /* remove the request from the device queue */ + list_del(&cqr->list); /* free the finished erp request */ - dasd_free_erp_request(cqr, cqr->memdev); + dasd_free_erp_request(cqr, device); cqr = refers; } @@ -156,7 +157,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb) { struct dasd_device *device; - device = cqr->startdev; + device = cqr->device; /* dump sense data */ if (device->discipline && device->discipline->dump_sense) device->discipline->dump_sense(device, cqr, irb); diff --git a/trunk/drivers/s390/block/dasd_fba.c b/trunk/drivers/s390/block/dasd_fba.c index d13ea05089a7..1d95822e0b8e 100644 --- a/trunk/drivers/s390/block/dasd_fba.c +++ b/trunk/drivers/s390/block/dasd_fba.c @@ -117,7 +117,6 @@ 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; @@ -134,16 +133,6 @@ 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); @@ -166,37 +155,60 @@ dasd_fba_check_characteristics(struct dasd_device *device) return 0; } -static int dasd_fba_do_analysis(struct dasd_block *block) +static int +dasd_fba_do_analysis(struct dasd_device *device) { struct dasd_fba_private *private; int sb, rc; - private = (struct dasd_fba_private *) block->base->private; + private = (struct dasd_fba_private *) device->private; rc = dasd_check_blocksize(private->rdc_data.blk_size); if (rc) { - DEV_MESSAGE(KERN_INFO, block->base, "unknown blocksize %d", + DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d", private->rdc_data.blk_size); return rc; } - 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 */ + 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 */ for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1) - block->s2b_shift++; + device->s2b_shift++; return 0; } -static int dasd_fba_fill_geometry(struct dasd_block *block, - struct hd_geometry *geo) +static int +dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo) { - if (dasd_check_blocksize(block->bp_block) != 0) + if (dasd_check_blocksize(device->bp_block) != 0) return -EINVAL; - geo->cylinders = (block->blocks << block->s2b_shift) >> 10; + geo->cylinders = (device->blocks << device->s2b_shift) >> 10; geo->heads = 16; - geo->sectors = 128 >> block->s2b_shift; + geo->sectors = 128 >> device->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) { @@ -209,34 +221,13 @@ 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->startdev, "unknown ERP action %p", + DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p", cqr->function); return NULL; } -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) +static struct dasd_ccw_req * +dasd_fba_build_cp(struct dasd_device * device, struct request *req) { struct dasd_fba_private *private; unsigned long *idaws; @@ -251,17 +242,17 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, unsigned int blksize, off; unsigned char cmd; - private = (struct dasd_fba_private *) block->base->private; + private = (struct dasd_fba_private *) device->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 = block->bp_block; + blksize = device->bp_block; /* Calculate record id of first and last block. */ - first_rec = req->sector >> block->s2b_shift; - last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift; + first_rec = req->sector >> device->s2b_shift; + last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift; /* Check struct bio and count the number of blocks for the request. */ count = 0; cidaw = 0; @@ -269,7 +260,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, if (bv->bv_len & (blksize - 1)) /* Fba can only do full blocks. */ return ERR_PTR(-EINVAL); - count += bv->bv_len >> (block->s2b_shift + 9); + count += bv->bv_len >> (device->s2b_shift + 9); #if defined(CONFIG_64BIT) if (idal_is_needed (page_address(bv->bv_page), bv->bv_len)) cidaw += bv->bv_len / blksize; @@ -293,13 +284,13 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, } /* Allocate the ccw request. */ cqr = dasd_smalloc_request(dasd_fba_discipline.name, - cplength, datasize, memdev); + cplength, datasize, device); if (IS_ERR(cqr)) return cqr; ccw = cqr->cpaddr; /* First ccw is define extent. */ define_extent(ccw++, cqr->data, rq_data_dir(req), - block->bp_block, req->sector, req->nr_sectors); + device->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); @@ -335,7 +326,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, ccw[-1].flags |= CCW_FLAG_CC; } ccw->cmd_code = cmd; - ccw->count = block->bp_block; + ccw->count = device->bp_block; if (idal_is_needed(dst, blksize)) { ccw->cda = (__u32)(addr_t) idaws; ccw->flags = CCW_FLAG_IDA; @@ -351,9 +342,7 @@ static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, } if (req->cmd_flags & REQ_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); - cqr->startdev = memdev; - cqr->memdev = memdev; - cqr->block = block; + cqr->device = device; cqr->expires = 5 * 60 * HZ; /* 5 minutes */ cqr->retries = 32; cqr->buildclk = get_clock(); @@ -374,8 +363,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->block->base->private; - blksize = cqr->block->bp_block; + private = (struct dasd_fba_private *) cqr->device->private; + blksize = cqr->device->bp_block; ccw = cqr->cpaddr; /* Skip over define extent & locate record. */ ccw++; @@ -405,15 +394,10 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req) } out: status = cqr->status == DASD_CQR_DONE; - dasd_sfree_request(cqr, cqr->memdev); + dasd_sfree_request(cqr, cqr->device); 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) @@ -562,10 +546,9 @@ static struct dasd_discipline dasd_fba_discipline = { .fill_geometry = dasd_fba_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, - .handle_terminated_request = dasd_fba_handle_terminated_request, + .examine_error = dasd_fba_examine_error, .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/trunk/drivers/s390/block/dasd_genhd.c b/trunk/drivers/s390/block/dasd_genhd.c index aee6565aaf98..47ba4462708d 100644 --- a/trunk/drivers/s390/block/dasd_genhd.c +++ b/trunk/drivers/s390/block/dasd_genhd.c @@ -25,15 +25,14 @@ /* * Allocate and register gendisk structure for device. */ -int dasd_gendisk_alloc(struct dasd_block *block) +int +dasd_gendisk_alloc(struct dasd_device *device) { struct gendisk *gdp; - struct dasd_device *base; int len; /* Make sure the minor for this device exists. */ - base = block->base; - if (base->devindex >= DASD_PER_MAJOR) + if (device->devindex >= DASD_PER_MAJOR) return -EBUSY; gdp = alloc_disk(1 << DASD_PARTN_BITS); @@ -42,9 +41,9 @@ int dasd_gendisk_alloc(struct dasd_block *block) /* Initialize gendisk structure. */ gdp->major = DASD_MAJOR; - gdp->first_minor = base->devindex << DASD_PARTN_BITS; + gdp->first_minor = device->devindex << DASD_PARTN_BITS; gdp->fops = &dasd_device_operations; - gdp->driverfs_dev = &base->cdev->dev; + gdp->driverfs_dev = &device->cdev->dev; /* * Set device name. @@ -54,51 +53,53 @@ int dasd_gendisk_alloc(struct dasd_block *block) * dasdaaaa - dasdzzzz : 456976 devices, added up = 475252 */ len = sprintf(gdp->disk_name, "dasd"); - if (base->devindex > 25) { - if (base->devindex > 701) { - if (base->devindex > 18277) + if (device->devindex > 25) { + if (device->devindex > 701) { + if (device->devindex > 18277) len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((base->devindex-18278) + 'a'+(((device->devindex-18278) /17576)%26)); len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((base->devindex-702)/676)%26)); + 'a'+(((device->devindex-702)/676)%26)); } len += sprintf(gdp->disk_name + len, "%c", - 'a'+(((base->devindex-26)/26)%26)); + 'a'+(((device->devindex-26)/26)%26)); } - len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26)); + len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26)); - if (block->base->features & DASD_FEATURE_READONLY) + if (device->features & DASD_FEATURE_READONLY) set_disk_ro(gdp, 1); - gdp->private_data = block; - gdp->queue = block->request_queue; - block->gdp = gdp; - set_capacity(block->gdp, 0); - add_disk(block->gdp); + gdp->private_data = device; + gdp->queue = device->request_queue; + device->gdp = gdp; + set_capacity(device->gdp, 0); + add_disk(device->gdp); return 0; } /* * Unregister and free gendisk structure for device. */ -void dasd_gendisk_free(struct dasd_block *block) +void +dasd_gendisk_free(struct dasd_device *device) { - if (block->gdp) { - del_gendisk(block->gdp); - block->gdp->queue = NULL; - put_disk(block->gdp); - block->gdp = NULL; + if (device->gdp) { + del_gendisk(device->gdp); + device->gdp->queue = NULL; + put_disk(device->gdp); + device->gdp = NULL; } } /* * Trigger a partition detection. */ -int dasd_scan_partitions(struct dasd_block *block) +int +dasd_scan_partitions(struct dasd_device * device) { struct block_device *bdev; - bdev = bdget_disk(block->gdp, 0); + bdev = bdget_disk(device->gdp, 0); if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0) return -ENODEV; /* @@ -116,7 +117,7 @@ int dasd_scan_partitions(struct dasd_block *block) * is why the assignment to device->bdev is done AFTER * the BLKRRPART ioctl. */ - block->bdev = bdev; + device->bdev = bdev; return 0; } @@ -124,7 +125,8 @@ int dasd_scan_partitions(struct dasd_block *block) * 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_block *block) +void +dasd_destroy_partitions(struct dasd_device * device) { /* The two structs have 168/176 byte on 31/64 bit. */ struct blkpg_partition bpart; @@ -135,8 +137,8 @@ void dasd_destroy_partitions(struct dasd_block *block) * Get the bdev pointer from the device structure and clear * device->bdev to lower the offline open_count limit again. */ - bdev = block->bdev; - block->bdev = NULL; + bdev = device->bdev; + device->bdev = NULL; /* * See fs/partition/check.c:delete_partition @@ -147,16 +149,17 @@ void dasd_destroy_partitions(struct dasd_block *block) memset(&barg, 0, sizeof(struct blkpg_ioctl_arg)); barg.data = (void __force __user *) &bpart; barg.op = BLKPG_DEL_PARTITION; - for (bpart.pno = block->gdp->minors - 1; bpart.pno > 0; bpart.pno--) + for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--) ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg); - invalidate_partition(block->gdp, 0); + invalidate_partition(device->gdp, 0); /* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */ blkdev_put(bdev); - set_capacity(block->gdp, 0); + set_capacity(device->gdp, 0); } -int dasd_gendisk_init(void) +int +dasd_gendisk_init(void) { int rc; @@ -171,7 +174,8 @@ int dasd_gendisk_init(void) return 0; } -void dasd_gendisk_exit(void) +void +dasd_gendisk_exit(void) { unregister_blkdev(DASD_MAJOR, "dasd"); } diff --git a/trunk/drivers/s390/block/dasd_int.h b/trunk/drivers/s390/block/dasd_int.h index 44b2984dfbee..d427daeef511 100644 --- a/trunk/drivers/s390/block/dasd_int.h +++ b/trunk/drivers/s390/block/dasd_int.h @@ -64,7 +64,13 @@ * SECTION: Type definitions */ struct dasd_device; -struct dasd_block; + +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; /* BIT DEFINITIONS FOR SENSE DATA */ #define DASD_SENSE_BIT_0 0x80 @@ -145,22 +151,19 @@ do { \ struct dasd_ccw_req { unsigned int magic; /* Eye catcher */ - struct list_head devlist; /* for dasd_device request queue */ - struct list_head blocklist; /* for dasd_block request queue */ + struct list_head list; /* list_head for request queueing. */ /* Where to execute what... */ - 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 dasd_device *device; /* device the request is for */ 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 */ @@ -175,27 +178,20 @@ 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_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 */ - +#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 */ /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ @@ -218,71 +214,52 @@ 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. 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. - */ + /* + * 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. + */ 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. - */ + * 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 *); 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_block *, struct hd_geometry *); + int (*fill_geometry) (struct dasd_device *, struct hd_geometry *); int (*fill_info) (struct dasd_device *, struct dasd_information2_t *); - int (*ioctl) (struct dasd_block *, unsigned int, void __user *); + int (*ioctl) (struct dasd_device *, unsigned int, void __user *); }; extern struct dasd_discipline *dasd_diag_discipline_pointer; @@ -290,18 +267,12 @@ 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 type; + __u8 alias; char vendor[4]; char serial[15]; __u16 ssid; - __u8 real_unit_addr; - __u8 base_unit_addr; + __u8 unit_addr; }; /* @@ -322,9 +293,14 @@ struct dasd_uid { struct dasd_device { /* Block device stuff. */ - struct dasd_block *block; - + struct gendisk *gdp; + struct request_queue *request_queue; + spinlock_t request_queue_lock; + struct block_device *bdev; 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!) */ @@ -340,8 +316,9 @@ struct dasd_device { int state, target; int stopped; /* device (ccw_device_start) was stopped */ - /* reference count. */ + /* Open and reference count. */ atomic_t ref_count; + atomic_t open_count; /* ccw queue and memory for static ccw/erp buffers. */ struct list_head ccw_queue; @@ -360,45 +337,20 @@ 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_SU 16 /* summary unit check handling */ +#define DASD_STOPPED_DC_EIO 16 /* disconnected, return -EIO */ /* 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 */ @@ -537,9 +489,6 @@ 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 *); @@ -548,23 +497,18 @@ 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_device_bh(struct dasd_device *); -void dasd_schedule_block_bh(struct dasd_block *); +void dasd_schedule_bh(struct dasd_device *); 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_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 *); +void dasd_set_timer(struct dasd_device *, int); +void dasd_clear_timer(struct dasd_device *); 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); @@ -598,10 +542,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_block *); -void dasd_gendisk_free(struct dasd_block *); -int dasd_scan_partitions(struct dasd_block *); -void dasd_destroy_partitions(struct dasd_block *); +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 *); /* externals in dasd_ioctl.c */ int dasd_ioctl(struct inode *, struct file *, unsigned int, unsigned long); @@ -619,9 +563,20 @@ 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/trunk/drivers/s390/block/dasd_ioctl.c b/trunk/drivers/s390/block/dasd_ioctl.c index 91a64630cb0f..672eb0a3dd0b 100644 --- a/trunk/drivers/s390/block/dasd_ioctl.c +++ b/trunk/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_block *block = bdev->bd_disk->private_data; + struct dasd_device *device = bdev->bd_disk->private_data; if (!capable(CAP_SYS_ADMIN)) return -EACCES; - dasd_enable_device(block->base); + dasd_enable_device(device); /* Formatting the dasd device can change the capacity. */ mutex_lock(&bdev->bd_mutex); - i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9); + i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->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_block *block = bdev->bd_disk->private_data; + struct dasd_device *device = 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(block->base, DASD_STATE_BASIC); + dasd_set_target_state(device, 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_block *block) +static int +dasd_ioctl_quiesce(struct dasd_device *device) { unsigned long flags; - struct dasd_device *base; - base = block->base; if (!capable (CAP_SYS_ADMIN)) return -EACCES; - 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); + 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); return 0; } @@ -105,21 +105,22 @@ static int dasd_ioctl_quiesce(struct dasd_block *block) /* * Quiesce device. */ -static int dasd_ioctl_resume(struct dasd_block *block) +static int +dasd_ioctl_resume(struct dasd_device *device) { unsigned long flags; - struct dasd_device *base; - base = block->base; if (!capable (CAP_SYS_ADMIN)) return -EACCES; - 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); + 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); - dasd_schedule_block_bh(block); + dasd_schedule_bh (device); return 0; } @@ -129,23 +130,22 @@ static int dasd_ioctl_resume(struct dasd_block *block) * 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_block *block, struct format_data_t *fdata) +static int +dasd_format(struct dasd_device * device, struct format_data_t * fdata) { struct dasd_ccw_req *cqr; - struct dasd_device *base; int rc; - base = block->base; - if (base->discipline->format_device == NULL) + if (device->discipline->format_device == NULL) return -EPERM; - if (base->state != DASD_STATE_BASIC) { - DEV_MESSAGE(KERN_WARNING, base, "%s", + if (device->state != DASD_STATE_BASIC) { + DEV_MESSAGE(KERN_WARNING, device, "%s", "dasd_format: device is not disabled! "); return -EBUSY; } - DBF_DEV_EVENT(DBF_NOTICE, base, + DBF_DEV_EVENT(DBF_NOTICE, device, "formatting units %d to %d (%d B blocks) flags %d", fdata->start_unit, fdata->stop_unit, fdata->blksize, fdata->intensity); @@ -156,20 +156,20 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) * enabling the device later. */ if (fdata->start_unit == 0) { - struct block_device *bdev = bdget_disk(block->gdp, 0); + struct block_device *bdev = bdget_disk(device->gdp, 0); bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize); bdput(bdev); } while (fdata->start_unit <= fdata->stop_unit) { - cqr = base->discipline->format_device(base, fdata); + cqr = device->discipline->format_device(device, fdata); if (IS_ERR(cqr)) return PTR_ERR(cqr); rc = dasd_sleep_on_interruptible(cqr); - dasd_sfree_request(cqr, cqr->memdev); + dasd_sfree_request(cqr, cqr->device); if (rc) { if (rc != -ERESTARTSYS) - DEV_MESSAGE(KERN_ERR, base, + DEV_MESSAGE(KERN_ERR, device, " Formatting of unit %d failed " "with rc = %d", fdata->start_unit, rc); @@ -186,7 +186,7 @@ static int dasd_format(struct dasd_block *block, struct format_data_t *fdata) static int dasd_ioctl_format(struct block_device *bdev, void __user *argp) { - struct dasd_block *block = bdev->bd_disk->private_data; + struct dasd_device *device = bdev->bd_disk->private_data; struct format_data_t fdata; if (!capable(CAP_SYS_ADMIN)) @@ -194,47 +194,51 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp) if (!argp) return -EINVAL; - if (block->base->features & DASD_FEATURE_READONLY) + if (device->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, block->base, "%s", + DEV_MESSAGE(KERN_WARNING, device, "%s", "Cannot low-level format a partition"); return -EINVAL; } - return dasd_format(block, &fdata); + return dasd_format(device, &fdata); } #ifdef CONFIG_DASD_PROFILE /* * Reset device profile information */ -static int dasd_ioctl_reset_profile(struct dasd_block *block) +static int +dasd_ioctl_reset_profile(struct dasd_device *device) { - memset(&block->profile, 0, sizeof(struct dasd_profile_info_t)); + memset(&device->profile, 0, sizeof (struct dasd_profile_info_t)); return 0; } /* * Return device profile information */ -static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) +static int +dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) { if (dasd_profile_level == DASD_PROFILE_OFF) return -EIO; - if (copy_to_user(argp, &block->profile, - sizeof(struct dasd_profile_info_t))) + if (copy_to_user(argp, &device->profile, + sizeof (struct dasd_profile_info_t))) return -EFAULT; return 0; } #else -static int dasd_ioctl_reset_profile(struct dasd_block *block) +static int +dasd_ioctl_reset_profile(struct dasd_device *device) { return -ENOSYS; } -static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) +static int +dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp) { return -ENOSYS; } @@ -243,88 +247,87 @@ static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp) /* * Return dasd information. Used for BIODASDINFO and BIODASDINFO2. */ -static int dasd_ioctl_information(struct dasd_block *block, - unsigned int cmd, void __user *argp) +static int +dasd_ioctl_information(struct dasd_device *device, + 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; - base = block->base; - if (!base->discipline->fill_info) + if (!device->discipline->fill_info) return -EINVAL; dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL); if (dasd_info == NULL) return -ENOMEM; - rc = base->discipline->fill_info(base, dasd_info); + rc = device->discipline->fill_info(device, dasd_info); if (rc) { kfree(dasd_info); return rc; } - cdev = base->cdev; + cdev = device->cdev; ccw_device_get_id(cdev, &dev_id); dasd_info->devno = dev_id.devno; - dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev); + dasd_info->schid = _ccw_device_get_subchannel_number(device->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 = base->state; + dasd_info->status = device->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(&block->open_count); - if (!block->bdev) + dasd_info->open_count = atomic_read(&device->open_count); + if (!device->bdev) dasd_info->open_count++; /* * check if device is really formatted * LDL / CDL was returned by 'fill_info' */ - if ((base->state < DASD_STATE_READY) || - (dasd_check_blocksize(block->bp_block))) + if ((device->state < DASD_STATE_READY) || + (dasd_check_blocksize(device->bp_block))) dasd_info->format = DASD_FORMAT_NONE; dasd_info->features |= - ((base->features & DASD_FEATURE_READONLY) != 0); + ((device->features & DASD_FEATURE_READONLY) != 0); - if (base->discipline) - memcpy(dasd_info->type, base->discipline->name, 4); + if (device->discipline) + memcpy(dasd_info->type, device->discipline->name, 4); else memcpy(dasd_info->type, "none", 4); - if (block->request_queue->request_fn) { + if (device->request_queue->request_fn) { struct list_head *l; #ifdef DASD_EXTENDED_PROFILING { struct list_head *l; - spin_lock_irqsave(&block->lock, flags); - list_for_each(l, &block->request_queue->queue_head) + spin_lock_irqsave(&device->lock, flags); + list_for_each(l, &device->request_queue->queue_head) dasd_info->req_queue_len++; - spin_unlock_irqrestore(&block->lock, flags); + spin_unlock_irqrestore(&device->lock, flags); } #endif /* DASD_EXTENDED_PROFILING */ - spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags); - list_for_each(l, &base->ccw_queue) + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + list_for_each(l, &device->ccw_queue) dasd_info->chanq_len++; - spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), + spin_unlock_irqrestore(get_ccwdev_lock(device->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; @@ -336,7 +339,7 @@ static int dasd_ioctl_information(struct dasd_block *block, static int dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) { - struct dasd_block *block = bdev->bd_disk->private_data; + struct dasd_device *device = bdev->bd_disk->private_data; int intval; if (!capable(CAP_SYS_ADMIN)) @@ -348,10 +351,11 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp) return -EFAULT; set_disk_ro(bdev->bd_disk, intval); - return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval); + return dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval); } -static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, +static int +dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd, unsigned long arg) { struct cmbdata __user *argp = (void __user *) arg; @@ -359,7 +363,7 @@ static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd, struct cmbdata data; int ret; - ret = cmf_readall(block->base->cdev, &data); + ret = cmf_readall(device->cdev, &data); if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp)))) return -EFAULT; return ret; @@ -370,10 +374,10 @@ dasd_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct block_device *bdev = inode->i_bdev; - struct dasd_block *block = bdev->bd_disk->private_data; + struct dasd_device *device = bdev->bd_disk->private_data; void __user *argp = (void __user *)arg; - if (!block) + if (!device) return -ENODEV; if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) { @@ -387,33 +391,33 @@ dasd_ioctl(struct inode *inode, struct file *file, case BIODASDENABLE: return dasd_ioctl_enable(bdev); case BIODASDQUIESCE: - return dasd_ioctl_quiesce(block); + return dasd_ioctl_quiesce(device); case BIODASDRESUME: - return dasd_ioctl_resume(block); + return dasd_ioctl_resume(device); case BIODASDFMT: return dasd_ioctl_format(bdev, argp); case BIODASDINFO: - return dasd_ioctl_information(block, cmd, argp); + return dasd_ioctl_information(device, cmd, argp); case BIODASDINFO2: - return dasd_ioctl_information(block, cmd, argp); + return dasd_ioctl_information(device, cmd, argp); case BIODASDPRRD: - return dasd_ioctl_read_profile(block, argp); + return dasd_ioctl_read_profile(device, argp); case BIODASDPRRST: - return dasd_ioctl_reset_profile(block); + return dasd_ioctl_reset_profile(device); case BLKROSET: return dasd_ioctl_set_ro(bdev, argp); case DASDAPIVER: return dasd_ioctl_api_version(argp); case BIODASDCMFENABLE: - return enable_cmf(block->base->cdev); + return enable_cmf(device->cdev); case BIODASDCMFDISABLE: - return disable_cmf(block->base->cdev); + return disable_cmf(device->cdev); case BIODASDREADALLCMB: - return dasd_ioctl_readall_cmb(block, cmd, arg); + return dasd_ioctl_readall_cmb(device, cmd, arg); default: /* if the discipline has an ioctl method try it. */ - if (block->base->discipline->ioctl) { - int rval = block->base->discipline->ioctl(block, cmd, argp); + if (device->discipline->ioctl) { + int rval = device->discipline->ioctl(device, cmd, argp); if (rval != -ENOIOCTLCMD) return rval; } diff --git a/trunk/drivers/s390/block/dasd_proc.c b/trunk/drivers/s390/block/dasd_proc.c index 28a86f070048..ac7e8ef504cb 100644 --- a/trunk/drivers/s390/block/dasd_proc.c +++ b/trunk/drivers/s390/block/dasd_proc.c @@ -54,16 +54,11 @@ 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. */ @@ -72,14 +67,14 @@ dasd_devices_show(struct seq_file *m, void *v) else seq_printf(m, "(none)"); /* Print kdev. */ - if (block->gdp) + if (device->gdp) seq_printf(m, " at (%3d:%6d)", - block->gdp->major, block->gdp->first_minor); + device->gdp->major, device->gdp->first_minor); else seq_printf(m, " at (???:??????)"); /* Print device name. */ - if (block->gdp) - seq_printf(m, " is %-8s", block->gdp->disk_name); + if (device->gdp) + seq_printf(m, " is %-8s", device->gdp->disk_name); else seq_printf(m, " is ????????"); /* Print devices features. */ @@ -105,14 +100,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(block->bp_block)) + if (dasd_check_blocksize(device->bp_block)) seq_printf(m, "n/f "); else seq_printf(m, "at blocksize: %d, %ld blocks, %ld MB", - block->bp_block, block->blocks, - ((block->bp_block >> 9) * - block->blocks) >> 11); + device->bp_block, device->blocks, + ((device->bp_block >> 9) * + device->blocks) >> 11); break; default: seq_printf(m, "no stat"); @@ -142,7 +137,7 @@ static void dasd_devices_stop(struct seq_file *m, void *v) { } -static const struct seq_operations dasd_devices_seq_ops = { +static struct seq_operations dasd_devices_seq_ops = { .start = dasd_devices_start, .next = dasd_devices_next, .stop = dasd_devices_stop, diff --git a/trunk/drivers/s390/block/dcssblk.c b/trunk/drivers/s390/block/dcssblk.c index 7779bfce1c31..15a5789b7734 100644 --- a/trunk/drivers/s390/block/dcssblk.c +++ b/trunk/drivers/s390/block/dcssblk.c @@ -82,7 +82,7 @@ struct dcssblk_dev_info { struct request_queue *dcssblk_queue; }; -static LIST_HEAD(dcssblk_devices); +static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices); static struct rw_semaphore dcssblk_devices_sem; /* diff --git a/trunk/drivers/s390/char/Makefile b/trunk/drivers/s390/char/Makefile index 7e73e39a1741..130de19916f2 100644 --- a/trunk/drivers/s390/char/Makefile +++ b/trunk/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_cmd.o sclp_config.o sclp_cpi_sys.o + sclp_info.o sclp_config.o sclp_chp.o obj-$(CONFIG_TN3270) += raw3270.o obj-$(CONFIG_TN3270_CONSOLE) += con3270.o diff --git a/trunk/drivers/s390/char/monwriter.c b/trunk/drivers/s390/char/monwriter.c index a86c0534cd49..20442fbf9346 100644 --- a/trunk/drivers/s390/char/monwriter.c +++ b/trunk/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 "); diff --git a/trunk/drivers/s390/char/raw3270.c b/trunk/drivers/s390/char/raw3270.c index 0d98f1ff2edd..8d1c64a24dec 100644 --- a/trunk/drivers/s390/char/raw3270.c +++ b/trunk/drivers/s390/char/raw3270.c @@ -66,7 +66,7 @@ struct raw3270 { static DEFINE_MUTEX(raw3270_mutex); /* List of 3270 devices. */ -static LIST_HEAD(raw3270_devices); +static struct list_head raw3270_devices = LIST_HEAD_INIT(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 LIST_HEAD(raw3270_notifier); +static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier); int raw3270_register_notifier(void (*notifier)(int, int)) { diff --git a/trunk/drivers/s390/char/sclp.h b/trunk/drivers/s390/char/sclp.h index aa8186d18aee..c7318a125852 100644 --- a/trunk/drivers/s390/char/sclp.h +++ b/trunk/drivers/s390/char/sclp.h @@ -56,6 +56,8 @@ 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 @@ -81,8 +83,6 @@ 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/trunk/drivers/s390/char/sclp_chp.c b/trunk/drivers/s390/char/sclp_chp.c new file mode 100644 index 000000000000..c68f5e7e63a0 --- /dev/null +++ b/trunk/drivers/s390/char/sclp_chp.c @@ -0,0 +1,200 @@ +/* + * drivers/s390/char/sclp_chp.c + * + * Copyright IBM Corp. 2007 + * Author(s): Peter Oberparleiter + */ + +#include +#include +#include +#include +#include +#include + +#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/trunk/drivers/s390/char/sclp_cmd.c b/trunk/drivers/s390/char/sclp_cmd.c deleted file mode 100644 index b5c23396f8fe..000000000000 --- a/trunk/drivers/s390/char/sclp_cmd.c +++ /dev/null @@ -1,398 +0,0 @@ -/* - * drivers/s390/char/sclp_cmd.c - * - * Copyright IBM Corp. 2007 - * Author(s): Heiko Carstens , - * Peter Oberparleiter - */ - -#include -#include -#include -#include -#include -#include -#include -#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/trunk/drivers/s390/char/sclp_cpi.c b/trunk/drivers/s390/char/sclp_cpi.c index 5716487b8c9d..82a13d9fdfe4 100644 --- a/trunk/drivers/s390/char/sclp_cpi.c +++ b/trunk/drivers/s390/char/sclp_cpi.c @@ -1,41 +1,255 @@ /* - * drivers/s390/char/sclp_cpi.c - * SCLP control programm identification + * Author: Martin Peschke + * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation * - * Copyright IBM Corp. 2001, 2007 - * Author(s): Martin Peschke - * Michael Ernst + * SCLP Control-Program Identification. */ +#include #include #include #include -#include -#include "sclp_cpi_sys.h" +#include +#include +#include +#include +#include +#include +#include + +#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 +}; MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Identify this operating system instance " - "to the System z hardware"); -MODULE_AUTHOR("Martin Peschke , " - "Michael Ernst "); -static char *system_name = ""; -static char *sysplex_name = ""; +MODULE_AUTHOR( + "Martin Peschke, IBM Deutschland Entwicklung GmbH " + ""); + +MODULE_DESCRIPTION( + "identify this operating system instance to the S/390 " + "or zSeries hardware"); +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 __init cpi_module_init(void) +static int +cpi_check_parms(void) { - return sclp_cpi_set_data(system_name, sysplex_name, "LINUX", - LINUX_VERSION_CODE); + /* 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; } +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/trunk/drivers/s390/char/sclp_cpi_sys.c b/trunk/drivers/s390/char/sclp_cpi_sys.c deleted file mode 100644 index 41617032afdc..000000000000 --- a/trunk/drivers/s390/char/sclp_cpi_sys.c +++ /dev/null @@ -1,400 +0,0 @@ -/* - * drivers/s390/char/sclp_cpi_sys.c - * SCLP control program identification sysfs interface - * - * Copyright IBM Corp. 2001, 2007 - * Author(s): Martin Peschke - * Michael Ernst - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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/trunk/drivers/s390/char/sclp_cpi_sys.h b/trunk/drivers/s390/char/sclp_cpi_sys.h deleted file mode 100644 index deef3e6ff496..000000000000 --- a/trunk/drivers/s390/char/sclp_cpi_sys.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * drivers/s390/char/sclp_cpi_sys.h - * SCLP control program identification sysfs interface - * - * Copyright IBM Corp. 2007 - * Author(s): Michael Ernst - */ - -#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/trunk/drivers/s390/char/sclp_info.c b/trunk/drivers/s390/char/sclp_info.c new file mode 100644 index 000000000000..a1136e052750 --- /dev/null +++ b/trunk/drivers/s390/char/sclp_info.c @@ -0,0 +1,116 @@ +/* + * drivers/s390/char/sclp_info.c + * + * Copyright IBM Corp. 2007 + * Author(s): Heiko Carstens + */ + +#include +#include +#include +#include +#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/trunk/drivers/s390/char/sclp_rw.c b/trunk/drivers/s390/char/sclp_rw.c index ad7195d3de0c..d6b06ab81188 100644 --- a/trunk/drivers/s390/char/sclp_rw.c +++ b/trunk/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 original page that has been used to create + * Return a pointer to the orignal page that has been used to create * the buffer. */ void * diff --git a/trunk/drivers/s390/char/tape_3590.c b/trunk/drivers/s390/char/tape_3590.c index 8246ef3ab095..da25f8e24152 100644 --- a/trunk/drivers/s390/char/tape_3590.c +++ b/trunk/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 privileged access to the " + PRINT_WARN("(%s): Another host has priviliged 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/trunk/drivers/s390/char/tape_core.c b/trunk/drivers/s390/char/tape_core.c index 7ad8cf157641..2fae6338ee1c 100644 --- a/trunk/drivers/s390/char/tape_core.c +++ b/trunk/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 LIST_HEAD(tape_device_list); +static struct list_head tape_device_list = LIST_HEAD_INIT(tape_device_list); static DEFINE_RWLOCK(tape_device_lock); /* diff --git a/trunk/drivers/s390/char/tape_proc.c b/trunk/drivers/s390/char/tape_proc.c index c9b96d51b28f..cea49f001f89 100644 --- a/trunk/drivers/s390/char/tape_proc.c +++ b/trunk/drivers/s390/char/tape_proc.c @@ -97,7 +97,7 @@ static void tape_proc_stop(struct seq_file *m, void *v) { } -static const struct seq_operations tape_proc_seq = { +static struct seq_operations tape_proc_seq = { .start = tape_proc_start, .next = tape_proc_next, .stop = tape_proc_stop, diff --git a/trunk/drivers/s390/char/vmlogrdr.c b/trunk/drivers/s390/char/vmlogrdr.c index d364e0bfae12..e0c4c508e121 100644 --- a/trunk/drivers/s390/char/vmlogrdr.c +++ b/trunk/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/trunk/drivers/s390/char/vmur.c b/trunk/drivers/s390/char/vmur.c index 7689b500a104..d70a6e65bf14 100644 --- a/trunk/drivers/s390/char/vmur.c +++ b/trunk/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 const struct file_operations ur_fops = { +static struct file_operations ur_fops = { .owner = THIS_MODULE, .open = ur_open, .release = ur_release, diff --git a/trunk/drivers/s390/char/zcore.c b/trunk/drivers/s390/char/zcore.c index f523501e6e6c..7073daf77981 100644 --- a/trunk/drivers/s390/char/zcore.c +++ b/trunk/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 const struct file_operations zcore_fops = { +static struct file_operations zcore_fops = { .owner = THIS_MODULE, .llseek = zcore_lseek, .read = zcore_read, diff --git a/trunk/drivers/s390/cio/airq.c b/trunk/drivers/s390/cio/airq.c index b7a07a866291..5287631fbfc8 100644 --- a/trunk/drivers/s390/cio/airq.c +++ b/trunk/drivers/s390/cio/airq.c @@ -1,12 +1,12 @@ /* * drivers/s390/cio/airq.c - * Support for adapter interruptions + * S/390 common I/O routines -- support for adapter interruptions * - * Copyright IBM Corp. 1999,2007 - * Author(s): Ingo Adlung - * Cornelia Huck - * Arnd Bergmann - * Peter Oberparleiter + * 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) */ #include @@ -14,131 +14,72 @@ #include #include -#include - -#include "cio.h" #include "cio_debug.h" +#include "airq.h" -#define NR_AIRQS 32 -#define NR_AIRQS_PER_WORD sizeof(unsigned long) -#define NR_AIRQ_WORDS (NR_AIRQS / NR_AIRQS_PER_WORD) +static adapter_int_handler_t adapter_handler; -union indicator_t { - unsigned long word[NR_AIRQ_WORDS]; - unsigned char byte[NR_AIRQS]; -} __attribute__((packed)); +/* + * 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]; -struct airq_t { - adapter_int_handler_t handler; - void *drv_data; -}; + CIO_TRACE_EVENT (4, "rgaint"); -static union indicator_t indicators; -static struct airq_t *airqs[NR_AIRQS]; + if (handler == NULL) + ret = -EINVAL; + else + ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0); + if (!ret) + synchronize_sched(); /* Allow interrupts to complete. */ -static int register_airq(struct airq_t *airq) -{ - int i; + sprintf (dbf_txt, "ret:%d", ret); + CIO_TRACE_EVENT (4, dbf_txt); - for (i = 0; i < NR_AIRQS; i++) - if (!cmpxchg(&airqs[i], NULL, airq)) - return i; - return -ENOMEM; + return ret; } -/** - * 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) +int +s390_unregister_adapter_interrupt (adapter_int_handler_t handler) { - struct airq_t *airq; - char dbf_txt[16]; int ret; + char dbf_txt[15]; - airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL); - if (!airq) { - ret = -ENOMEM; - goto out; - } - 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); + CIO_TRACE_EVENT (4, "urgaint"); -/** - * 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) -{ - struct airq_t *airq; - char dbf_txt[16]; - int i; + if (handler == NULL) + ret = -EINVAL; + else { + adapter_handler = NULL; + synchronize_sched(); /* Allow interrupts to complete. */ + ret = 0; + } + sprintf (dbf_txt, "ret:%d", ret); + CIO_TRACE_EVENT (4, dbf_txt); - 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); + return ret; } -EXPORT_SYMBOL(s390_unregister_adapter_interrupt); - -#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8)) -void do_adapter_IO(void) +void +do_adapter_IO (void) { - int w; - int i; - unsigned long word; - struct airq_t *airq; + CIO_TRACE_EVENT (6, "doaio"); - /* - * 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++; - } - } + if (adapter_handler) + (*adapter_handler) (); } + +EXPORT_SYMBOL (s390_register_adapter_interrupt); +EXPORT_SYMBOL (s390_unregister_adapter_interrupt); diff --git a/trunk/drivers/s390/cio/airq.h b/trunk/drivers/s390/cio/airq.h new file mode 100644 index 000000000000..7d6be3fdcd66 --- /dev/null +++ b/trunk/drivers/s390/cio/airq.h @@ -0,0 +1,10 @@ +#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/trunk/drivers/s390/cio/blacklist.c b/trunk/drivers/s390/cio/blacklist.c index e8597ec92247..bd5f16f80bf8 100644 --- a/trunk/drivers/s390/cio/blacklist.c +++ b/trunk/drivers/s390/cio/blacklist.c @@ -348,7 +348,7 @@ cio_ignore_write(struct file *file, const char __user *user_buf, return user_len; } -static const struct seq_operations cio_ignore_proc_seq_ops = { +static 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/trunk/drivers/s390/cio/ccwgroup.c b/trunk/drivers/s390/cio/ccwgroup.c index 3964056a9a47..5baa517c3b66 100644 --- a/trunk/drivers/s390/cio/ccwgroup.c +++ b/trunk/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 = to_ccwgroupdev(dev); - gdrv = to_ccwgroupdrv(drv); + gdev = container_of(dev, struct ccwgroup_device, dev); + gdrv = container_of(drv, struct ccwgroup_driver, driver); if (gdev->creator_id == gdrv->driver_id) return 1; @@ -75,10 +75,8 @@ static void ccwgroup_ungroup_callback(struct device *dev) struct ccwgroup_device *gdev = to_ccwgroupdev(dev); mutex_lock(&gdev->reg_mutex); - if (device_is_registered(&gdev->dev)) { - __ccwgroup_remove_symlinks(gdev); - device_unregister(dev); - } + __ccwgroup_remove_symlinks(gdev); + device_unregister(dev); mutex_unlock(&gdev->reg_mutex); } @@ -113,7 +111,7 @@ ccwgroup_release (struct device *dev) gdev = to_ccwgroupdev(dev); for (i = 0; i < gdev->count; i++) { - dev_set_drvdata(&gdev->cdev[i]->dev, NULL); + gdev->cdev[i]->dev.driver_data = NULL; put_device(&gdev->cdev[i]->dev); } kfree(gdev); @@ -198,11 +196,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 (dev_get_drvdata(&gdev->cdev[i]->dev)) { + if (gdev->cdev[i]->dev.driver_data) { rc = -EINVAL; goto error; } - dev_set_drvdata(&gdev->cdev[i]->dev, gdev); + gdev->cdev[i]->dev.driver_data = gdev; } gdev->creator_id = creator_id; @@ -236,8 +234,8 @@ int ccwgroup_create(struct device *root, unsigned int creator_id, error: for (i = 0; i < argc; i++) if (gdev->cdev[i]) { - if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) - dev_set_drvdata(&gdev->cdev[i]->dev, NULL); + if (gdev->cdev[i]->dev.driver_data == gdev) + gdev->cdev[i]->dev.driver_data = NULL; put_device(&gdev->cdev[i]->dev); } mutex_unlock(&gdev->reg_mutex); @@ -410,7 +408,6 @@ 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); } @@ -466,8 +463,8 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) { struct ccwgroup_device *gdev; - gdev = dev_get_drvdata(&cdev->dev); - if (gdev) { + if (cdev->dev.driver_data) { + gdev = (struct ccwgroup_device *)cdev->dev.driver_data; if (get_device(&gdev->dev)) { mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) diff --git a/trunk/drivers/s390/cio/chsc.c b/trunk/drivers/s390/cio/chsc.c index e7ba16a74ef7..597c0c76a2ad 100644 --- a/trunk/drivers/s390/cio/chsc.c +++ b/trunk/drivers/s390/cio/chsc.c @@ -89,8 +89,7 @@ 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 != SUBCHANNEL_TYPE_IO) && - (ssd_area->st != SUBCHANNEL_TYPE_MSG)) + if ((ssd_area->st != 0) && (ssd_area->st != 2)) goto out_free; ssd->path_mask = ssd_area->path_mask; ssd->fla_valid_mask = ssd_area->fla_valid_mask; @@ -133,16 +132,20 @@ static void terminate_internal_io(struct subchannel *sch) device_set_intretry(sch); /* Call handler. */ if (sch->driver && sch->driver->termination) - sch->driver->termination(sch); + sch->driver->termination(&sch->dev); } -static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) +static int +s390_subchannel_remove_chpid(struct device *dev, void *data) { int j; int mask; - struct chp_id *chpid = data; + struct subchannel *sch; + struct chp_id *chpid; struct schib schib; + sch = to_subchannel(dev); + chpid = data; for (j = 0; j < 8; j++) { mask = 0x80 >> j; if ((sch->schib.pmcw.pim & mask) && @@ -155,7 +158,7 @@ static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) spin_lock_irq(sch->lock); stsch(sch->schid, &schib); - if (!css_sch_is_valid(&schib)) + if (!schib.pmcw.dnv) goto out_unreg; memcpy(&sch->schib, &schib, sizeof(struct schib)); /* Check for single path devices. */ @@ -169,12 +172,12 @@ static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data) terminate_internal_io(sch); /* Re-start path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); + sch->driver->verify(&sch->dev); } } else { /* trigger path verification. */ if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); + sch->driver->verify(&sch->dev); else if (sch->lpm == mask) goto out_unreg; } @@ -198,10 +201,12 @@ void chsc_chp_offline(struct chp_id chpid) if (chp_get_status(chpid) <= 0) return; - for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid); + bus_for_each_dev(&css_bus_type, NULL, &chpid, + s390_subchannel_remove_chpid); } -static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data) +static int +s390_process_res_acc_new_sch(struct subchannel_id schid) { struct schib schib; /* @@ -247,10 +252,18 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd, return 0; } -static int __s390_process_res_acc(struct subchannel *sch, void *data) +static int +__s390_process_res_acc(struct subchannel_id schid, void *data) { int chp_mask, old_lpm; - struct res_acc_data *res_data = data; + 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); spin_lock_irq(sch->lock); chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data); @@ -266,10 +279,10 @@ static int __s390_process_res_acc(struct subchannel *sch, void *data) if (!old_lpm && sch->lpm) device_trigger_reprobe(sch); else if (sch->driver && sch->driver->verify) - sch->driver->verify(sch); + sch->driver->verify(&sch->dev); out: spin_unlock_irq(sch->lock); - + put_device(&sch->dev); return 0; } @@ -292,8 +305,7 @@ 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_staged(__s390_process_res_acc, - s390_process_res_acc_new_sch, res_data); + for_each_subchannel(__s390_process_res_acc, res_data); } static int @@ -487,7 +499,8 @@ void chsc_process_crw(void) } while (sei_area->flags & 0x80); } -static int __chp_add_new_sch(struct subchannel_id schid, void *data) +static int +__chp_add_new_sch(struct subchannel_id schid) { struct schib schib; @@ -501,37 +514,45 @@ static int __chp_add_new_sch(struct subchannel_id schid, void *data) } -static int __chp_add(struct subchannel *sch, void *data) +static int +__chp_add(struct subchannel_id schid, void *data) { int i, mask; - struct chp_id *chpid = data; - + 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); 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)) + (sch->schib.pmcw.chpid[i] == chpid->id)) { + if (stsch(sch->schid, &sch->schib) != 0) { + /* Endgame. */ + spin_unlock_irq(sch->lock); + return -ENXIO; + } 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); + sch->driver->verify(&sch->dev); spin_unlock_irq(sch->lock); - + put_device(&sch->dev); return 0; } @@ -543,8 +564,7 @@ void chsc_chp_online(struct chp_id chpid) CIO_TRACE_EVENT(2, dbf_txt); if (chp_get_status(chpid) != 0) - for_each_subchannel_staged(__chp_add, __chp_add_new_sch, - &chpid); + for_each_subchannel(__chp_add, &chpid); } static void __s390_subchannel_vary_chpid(struct subchannel *sch, @@ -569,7 +589,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); + sch->driver->verify(&sch->dev); break; } sch->opm &= ~mask; @@ -583,29 +603,37 @@ 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); + sch->driver->verify(&sch->dev); } } 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); + sch->driver->verify(&sch->dev); break; } spin_unlock_irqrestore(sch->lock, flags); } -static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data) +static int s390_subchannel_vary_chpid_off(struct device *dev, void *data) { - struct chp_id *chpid = data; + struct subchannel *sch; + struct chp_id *chpid; + + sch = to_subchannel(dev); + chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 0); return 0; } -static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data) +static int s390_subchannel_vary_chpid_on(struct device *dev, void *data) { - struct chp_id *chpid = data; + struct subchannel *sch; + struct chp_id *chpid; + + sch = to_subchannel(dev); + chpid = data; __s390_subchannel_vary_chpid(sch, *chpid, 1); return 0; @@ -615,7 +643,13 @@ 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; @@ -635,13 +669,12 @@ 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) - 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); - + /* Scan for new devices on varied on path. */ + for_each_subchannel(__s390_vary_chpid_on, NULL); return 0; } @@ -1042,7 +1075,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/trunk/drivers/s390/cio/cio.c b/trunk/drivers/s390/cio/cio.c index 60590a12d529..46905345159e 100644 --- a/trunk/drivers/s390/cio/cio.c +++ b/trunk/drivers/s390/cio/cio.c @@ -23,12 +23,11 @@ #include #include #include -#include +#include "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" @@ -57,37 +56,39 @@ __setup ("cio_msg=", cio_setup); /* * Function: cio_debug_init - * Initializes three debug logs for common I/O: - * - cio_msg logs generic cio messages + * 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 * - cio_trace logs the calling of different functions - * - cio_crw logs machine check related cio messages + * - 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 */ -static int __init cio_debug_init(void) +static int __init +cio_debug_init (void) { - cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long)); + cio_debug_msg_id = debug_register ("cio_msg", 16, 4, 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, 1, 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, 4, 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", 16, 1, 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", 4, 4, 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, 4); + debug_register_view (cio_debug_crw_id, &debug_sprintf_view); + debug_set_level (cio_debug_crw_id, 2); 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; } @@ -146,7 +147,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); + sch->driver->irq(&sch->dev); spin_unlock(sch->lock); irq_exit (); _local_bh_enable(); @@ -183,35 +184,33 @@ 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. */ - orb->intparm = (u32)(addr_t)sch; - orb->fmt = 1; + sch->orb.intparm = (__u32)(unsigned long)sch; + sch->orb.fmt = 1; - 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; + 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; #ifdef CONFIG_64BIT /* * for 64 bit we always support 64 bit IDAWs with 4k page size only */ - orb->c64 = 1; - orb->i2k = 0; + sch->orb.c64 = 1; + sch->orb.i2k = 0; #endif - orb->key = key >> 4; + sch->orb.key = key >> 4; /* issue "Start Subchannel" */ - orb->cpa = (__u32) __pa(cpa); - ccode = ssch(sch->schid, orb); + sch->orb.cpa = (__u32) __pa (cpa); + ccode = ssch (sch->schid, &sch->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: @@ -406,8 +405,8 @@ cio_modify (struct subchannel *sch) /* * Enable subchannel. */ -int cio_enable_subchannel(struct subchannel *sch, unsigned int isc, - u32 intparm) +int +cio_enable_subchannel (struct subchannel *sch, unsigned int isc) { char dbf_txt[15]; int ccode; @@ -426,7 +425,7 @@ int 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 = intparm; + sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; ret = cio_modify(sch); if (ret == -ENODEV) break; @@ -568,7 +567,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) */ if (sch->st != 0) { CIO_DEBUG(KERN_INFO, 0, - "Subchannel 0.%x.%04x reports " + "cio: 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. */ @@ -577,11 +576,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) } /* Initialization for io subchannels. */ - if (!css_sch_is_valid(&sch->schib)) { + if (!sch->schib.pmcw.dnv) { + /* io subchannel but device number is invalid. */ err = -ENODEV; goto out; } - /* Devno is valid. */ if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) { /* @@ -601,7 +600,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid) sch->lpm = sch->schib.pmcw.pam & sch->opm; CIO_DEBUG(KERN_INFO, 0, - "Detected device %04x on subchannel 0.%x.%04X" + "cio: 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, @@ -681,7 +680,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); + sch->driver->irq(&sch->dev); } if (sch) spin_unlock(sch->lock); @@ -699,14 +698,8 @@ 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 */ @@ -745,9 +738,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.st == SUBCHANNEL_TYPE_IO) && - console_subchannel.schib.pmcw.dnv && - (console_subchannel.schib.pmcw.dev == console_devno)) { + if (console_subchannel.schib.pmcw.dnv && + console_subchannel.schib.pmcw.dev == + console_devno) { console_irq = schid.sch_no; return 1; /* found */ } @@ -765,7 +758,6 @@ 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; @@ -812,7 +804,7 @@ cio_probe_console(void) ctl_set_bit(6, 24); console_subchannel.schib.pmcw.isc = 7; console_subchannel.schib.pmcw.intparm = - (u32)(addr_t)&console_subchannel; + (__u32)(unsigned long)&console_subchannel; ret = cio_modify(&console_subchannel); if (ret) { console_subchannel_in_use = 0; @@ -1030,7 +1022,7 @@ static int __reipl_subchannel_match(struct subchannel_id schid, void *data) if (stsch_reset(schid, &schib)) return -ENXIO; - if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv && + if (schib.pmcw.dnv && (schib.pmcw.dev == match_id->devid.devno) && (schid.ssid == match_id->devid.ssid)) { match_id->schid = schid; @@ -1076,8 +1068,6 @@ 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/trunk/drivers/s390/cio/cio.h b/trunk/drivers/s390/cio/cio.h index 52afa4c784de..7446c39951a7 100644 --- a/trunk/drivers/s390/cio/cio.h +++ b/trunk/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,6 +52,31 @@ 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; @@ -60,7 +85,7 @@ struct subchannel { enum { SUBCHANNEL_TYPE_IO = 0, SUBCHANNEL_TYPE_CHSC = 1, - SUBCHANNEL_TYPE_MSG = 2, + SUBCHANNEL_TYPE_MESSAGE = 2, SUBCHANNEL_TYPE_ADM = 3, } st; /* subchannel type */ @@ -74,10 +99,11 @@ 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 */ @@ -85,7 +111,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, u32); +extern int cio_enable_subchannel (struct subchannel *, unsigned int); extern int cio_disable_subchannel (struct subchannel *); extern int cio_cancel (struct subchannel *); extern int cio_clear (struct subchannel *); @@ -99,7 +125,6 @@ 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 @@ -108,12 +133,10 @@ 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_priv() NULL +#define cio_get_console_lock() NULL; #endif extern int cio_show_msg; diff --git a/trunk/drivers/s390/cio/cio_debug.h b/trunk/drivers/s390/cio/cio_debug.h index d7429ef6c666..c9bf8989930f 100644 --- a/trunk/drivers/s390/cio/cio_debug.h +++ b/trunk/drivers/s390/cio/cio_debug.h @@ -8,19 +8,20 @@ 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; @@ -31,10 +32,9 @@ static inline void CIO_HEX_EVENT(int level, void *data, int length) } } -#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) +#define CIO_DEBUG(printk_level,event_level,msg...) ({ \ + if (cio_show_msg) printk(printk_level msg); \ + CIO_MSG_EVENT (event_level, msg); \ +}) #endif diff --git a/trunk/drivers/s390/cio/css.c b/trunk/drivers/s390/cio/css.c index 3b45bbe6cce0..c3df2cd009a4 100644 --- a/trunk/drivers/s390/cio/css.c +++ b/trunk/drivers/s390/cio/css.c @@ -51,62 +51,6 @@ 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) { @@ -133,7 +77,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)(addr_t)sch; + sch->schib.pmcw.intparm = (__u32)(unsigned long)sch; ret = cio_modify(sch); if (ret) { kfree(sch->lock); @@ -293,25 +237,11 @@ 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)) - return CIO_GONE; - if (!css_sch_is_valid(&schib)) + if (stsch(sch->schid, &schib) || !schib.pmcw.dnv) return CIO_GONE; if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev)) return CIO_REVALIDATE; @@ -363,7 +293,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, event); + ret = sch->driver->notify(&sch->dev, event); spin_lock_irqsave(sch->lock, flags); if (ret) action = NONE; @@ -419,7 +349,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) || !css_sch_is_valid(&schib)) { + if (stsch_err(schid, &schib) || !schib.pmcw.dnv) { /* Unusable - ignore. */ return 0; } @@ -458,56 +388,20 @@ static int __init slow_subchannel_init(void) return 0; } -static int slow_eval_known_fn(struct subchannel *sch, void *data) +static void css_slow_path_func(struct work_struct *unused) { - int eval; - int rc; + struct subchannel_id schid; + CIO_TRACE_EVENT(4, "slowpath"); 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); + 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); } - 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); @@ -536,6 +430,7 @@ 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", @@ -543,6 +438,13 @@ 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: @@ -570,7 +472,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_staged(NULL, reprobe_subchannel, NULL); + ret = for_each_subchannel(reprobe_subchannel, NULL); CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret, need_reprobe); @@ -885,8 +787,8 @@ int sch_is_pseudo_sch(struct subchannel *sch) static int css_bus_match (struct device *dev, struct device_driver *drv) { - struct subchannel *sch = to_subchannel(dev); - struct css_driver *driver = to_cssdriver(drv); + struct subchannel *sch = container_of (dev, struct subchannel, dev); + struct css_driver *driver = container_of (drv, struct css_driver, drv); if (sch->st == driver->subchannel_type) return 1; @@ -894,36 +796,32 @@ 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 = to_cssdriver(dev->driver); - ret = sch->driver->probe ? sch->driver->probe(sch) : 0; - if (ret) - sch->driver = NULL; - return ret; + sch->driver = container_of (dev->driver, struct css_driver, drv); + return (sch->driver->probe ? sch->driver->probe(sch) : 0); } -static int css_remove(struct device *dev) +static int +css_remove (struct device *dev) { struct subchannel *sch; - int ret; sch = to_subchannel(dev); - ret = sch->driver->remove ? sch->driver->remove(sch) : 0; - sch->driver = NULL; - return ret; + return (sch->driver->remove ? sch->driver->remove(sch) : 0); } -static void css_shutdown(struct device *dev) +static void +css_shutdown (struct device *dev) { struct subchannel *sch; sch = to_subchannel(dev); - if (sch->driver && sch->driver->shutdown) + if (sch->driver->shutdown) sch->driver->shutdown(sch); } @@ -935,34 +833,6 @@ 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/trunk/drivers/s390/cio/css.h b/trunk/drivers/s390/cio/css.h index b70554523552..81215ef32435 100644 --- a/trunk/drivers/s390/cio/css.h +++ b/trunk/drivers/s390/cio/css.h @@ -58,6 +58,64 @@ 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 @@ -65,35 +123,25 @@ struct pgid { */ struct subchannel; struct css_driver { - struct module *owner; unsigned int subchannel_type; struct device_driver drv; - void (*irq)(struct subchannel *); - int (*notify)(struct subchannel *, int); - void (*verify)(struct subchannel *); - void (*termination)(struct subchannel *); + void (*irq)(struct device *); + int (*notify)(struct device *, int); + void (*verify)(struct device *); + void (*termination)(struct device *); 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); @@ -140,8 +188,6 @@ 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/trunk/drivers/s390/cio/device.c b/trunk/drivers/s390/cio/device.c index d35dc3f25d06..74f6b539974a 100644 --- a/trunk/drivers/s390/cio/device.c +++ b/trunk/drivers/s390/cio/device.c @@ -17,7 +17,6 @@ #include #include #include -#include #include #include @@ -29,12 +28,6 @@ #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 ***********************/ @@ -122,18 +115,19 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env) struct bus_type ccw_bus_type; -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 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_shutdown(struct subchannel *); static struct css_driver io_subchannel_driver = { - .owner = THIS_MODULE, .subchannel_type = SUBCHANNEL_TYPE_IO, - .name = "io_subchannel", + .drv = { + .name = "io_subchannel", + .bus = &css_bus_type, + }, .irq = io_subchannel_irq, .notify = io_subchannel_notify, .verify = io_subchannel_verify, @@ -148,8 +142,6 @@ 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) { @@ -157,7 +149,6 @@ 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) @@ -175,8 +166,7 @@ init_ccw_bus_type (void) if ((ret = bus_register (&ccw_bus_type))) goto out_err; - ret = css_driver_register(&io_subchannel_driver); - if (ret) + if ((ret = driver_register(&io_subchannel_driver.drv))) goto out_err; wait_event(ccw_device_init_wq, @@ -196,7 +186,7 @@ init_ccw_bus_type (void) static void __exit cleanup_ccw_bus_type (void) { - css_driver_unregister(&io_subchannel_driver); + driver_unregister(&io_subchannel_driver.drv); bus_unregister(&ccw_bus_type); destroy_workqueue(ccw_device_notify_work); destroy_workqueue(ccw_device_work); @@ -783,7 +773,7 @@ static void sch_attach_device(struct subchannel *sch, { css_update_ssd_info(sch); spin_lock_irq(sch->lock); - sch_set_cdev(sch, cdev); + sch->dev.driver_data = cdev; cdev->private->schid = sch->schid; cdev->ccwlock = sch->lock; device_trigger_reprobe(sch); @@ -805,7 +795,7 @@ static void sch_attach_disconnected_device(struct subchannel *sch, put_device(&other_sch->dev); return; } - sch_set_cdev(other_sch, NULL); + other_sch->dev.driver_data = NULL; /* No need to keep a subchannel without ccw device around. */ css_sch_device_unregister(other_sch); put_device(&other_sch->dev); @@ -841,12 +831,12 @@ static void sch_create_and_recog_new_device(struct subchannel *sch) return; } spin_lock_irq(sch->lock); - sch_set_cdev(sch, cdev); + sch->dev.driver_data = 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_set_cdev(sch, NULL); + sch->dev.driver_data = NULL; spin_unlock_irq(sch->lock); if (cdev->dev.release) cdev->dev.release(&cdev->dev); @@ -950,7 +940,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_set_cdev(sch, NULL); + sch->dev.driver_data = NULL; spin_unlock_irqrestore(sch->lock, flags); kfree (cdev->private); kfree (cdev); @@ -1032,7 +1022,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch) int rc; struct ccw_device_private *priv; - sch_set_cdev(sch, cdev); + sch->dev.driver_data = cdev; sch->driver = &io_subchannel_driver; cdev->ccwlock = sch->lock; @@ -1092,7 +1082,7 @@ static void ccw_device_move_to_sch(struct work_struct *work) } if (former_parent) { spin_lock_irq(former_parent->lock); - sch_set_cdev(former_parent, NULL); + former_parent->dev.driver_data = NULL; spin_unlock_irq(former_parent->lock); css_sch_device_unregister(former_parent); /* Reset intparm to zeroes. */ @@ -1106,18 +1096,6 @@ static void ccw_device_move_to_sch(struct work_struct *work) 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) { @@ -1126,13 +1104,13 @@ io_subchannel_probe (struct subchannel *sch) unsigned long flags; struct ccw_dev_id dev_id; - cdev = sch_get_cdev(sch); - if (cdev) { + if (sch->dev.driver_data) { /* * 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); @@ -1154,11 +1132,6 @@ 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), @@ -1176,18 +1149,16 @@ io_subchannel_probe (struct subchannel *sch) return 0; } cdev = io_subchannel_create_ccwdev(sch); - if (IS_ERR(cdev)) { - kfree(sch->private); + if (IS_ERR(cdev)) return PTR_ERR(cdev); - } + rc = io_subchannel_recog(cdev, sch); if (rc) { spin_lock_irqsave(sch->lock, flags); - sch_set_cdev(sch, NULL); + sch->dev.driver_data = NULL; spin_unlock_irqrestore(sch->lock, flags); if (cdev->dev.release) cdev->dev.release(&cdev->dev); - kfree(sch->private); } return rc; @@ -1199,25 +1170,25 @@ io_subchannel_remove (struct subchannel *sch) struct ccw_device *cdev; unsigned long flags; - cdev = sch_get_cdev(sch); - if (!cdev) + if (!sch->dev.driver_data) return 0; + cdev = sch->dev.driver_data; /* Set ccw device to not operational and drop reference. */ spin_lock_irqsave(cdev->ccwlock, flags); - sch_set_cdev(sch, NULL); + sch->dev.driver_data = 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 subchannel *sch, int event) +static int +io_subchannel_notify(struct device *dev, int event) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); + cdev = dev->driver_data; if (!cdev) return 0; if (!cdev->drv) @@ -1227,20 +1198,22 @@ static int io_subchannel_notify(struct subchannel *sch, int event) return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0; } -static void io_subchannel_verify(struct subchannel *sch) +static void +io_subchannel_verify(struct device *dev) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); + cdev = dev->driver_data; if (cdev) dev_fsm_event(cdev, DEV_EVENT_VERIFY); } -static void io_subchannel_ioterm(struct subchannel *sch) +static void +io_subchannel_ioterm(struct device *dev) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); + cdev = dev->driver_data; if (!cdev) return; /* Internal I/O will be retried by the interrupt handler. */ @@ -1258,7 +1231,7 @@ io_subchannel_shutdown(struct subchannel *sch) struct ccw_device *cdev; int ret; - cdev = sch_get_cdev(sch); + cdev = sch->dev.driver_data; if (cio_is_console(sch->schid)) return; @@ -1298,9 +1271,6 @@ 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); @@ -1486,7 +1456,6 @@ 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); } @@ -1512,60 +1481,6 @@ 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/trunk/drivers/s390/cio/device.h b/trunk/drivers/s390/cio/device.h index d40a2ffaa000..0d4089600439 100644 --- a/trunk/drivers/s390/cio/device.h +++ b/trunk/drivers/s390/cio/device.h @@ -5,8 +5,6 @@ #include #include -#include "io_sch.h" - /* * states of the device statemachine */ @@ -76,6 +74,7 @@ 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 *); @@ -88,8 +87,6 @@ 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/trunk/drivers/s390/cio/device_fsm.c b/trunk/drivers/s390/cio/device_fsm.c index 4b92c84fb438..bfad421cda66 100644 --- a/trunk/drivers/s390/cio/device_fsm.c +++ b/trunk/drivers/s390/cio/device_fsm.c @@ -25,16 +25,14 @@ #include "ioasm.h" #include "chp.h" -static int timeout_log_enabled; - int device_is_online(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); - if (!cdev) + if (!sch->dev.driver_data) return 0; + cdev = sch->dev.driver_data; return (cdev->private->state == DEV_STATE_ONLINE); } @@ -43,9 +41,9 @@ device_is_disconnected(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); - if (!cdev) + if (!sch->dev.driver_data) return 0; + cdev = sch->dev.driver_data; return (cdev->private->state == DEV_STATE_DISCONNECTED || cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID); } @@ -55,21 +53,19 @@ device_set_disconnected(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); - if (!cdev) + if (!sch->dev.driver_data) 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_get_cdev(sch); + cdev = sch->dev.driver_data; if (!cdev) return; cdev->private->flags.intretry = 1; @@ -79,62 +75,13 @@ int device_trigger_verify(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); + cdev = sch->dev.driver_data; 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. */ @@ -145,8 +92,6 @@ 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); } @@ -177,9 +122,9 @@ device_kill_pending_timer(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); - if (!cdev) + if (!sch->dev.driver_data) return; + cdev = sch->dev.driver_data; ccw_device_set_timeout(cdev, 0); } @@ -323,7 +268,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) switch (state) { case DEV_STATE_NOT_OPER: CIO_DEBUG(KERN_WARNING, 2, - "SenseID : unknown device %04x on subchannel " + "cio: SenseID : unknown device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -349,7 +294,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) } /* Issue device info message. */ CIO_DEBUG(KERN_INFO, 2, - "SenseID : device 0.%x.%04x reports: " + "cio: SenseID : device 0.%x.%04x reports: " "CU Type/Mod = %04X/%02X, Dev Type/Mod = " "%04X/%02X\n", cdev->private->dev_id.ssid, @@ -359,7 +304,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state) break; case DEV_STATE_BOXED: CIO_DEBUG(KERN_WARNING, 2, - "SenseID : boxed device %04x on subchannel " + "cio: SenseID : boxed device %04x on subchannel " "0.%x.%04x\n", cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); break; @@ -404,7 +349,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, CIO_OPER); + ret = sch->driver->notify(&sch->dev, CIO_OPER); spin_lock_irqsave(cdev->ccwlock, flags); } else ret = 0; @@ -444,7 +389,7 @@ ccw_device_done(struct ccw_device *cdev, int state) if (state == DEV_STATE_BOXED) CIO_DEBUG(KERN_WARNING, 2, - "Boxed device %04x on subchannel %04x\n", + "cio: Boxed device %04x on subchannel %04x\n", cdev->private->dev_id.devno, sch->schid.sch_no); if (cdev->private->flags.donotify) { @@ -555,8 +500,7 @@ 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, - (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc); if (ret != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return ret; @@ -643,10 +587,9 @@ 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) { - ccw_device_set_timeout(cdev, 0); + if (cdev->online) dev_fsm_event(cdev, DEV_EVENT_NOTOPER); - } else + else ccw_device_done(cdev, DEV_STATE_NOT_OPER); break; } @@ -667,8 +610,7 @@ 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, - (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc); if (ret != 0) { /* Couldn't enable the subchannel for i/o. Sick device. */ if (ret == -ENODEV) @@ -995,7 +937,7 @@ void device_kill_io(struct subchannel *sch) int ret; struct ccw_device *cdev; - cdev = sch_get_cdev(sch); + cdev = sch->dev.driver_data; ret = ccw_device_cancel_halt_clear(cdev); if (ret == -EBUSY) { ccw_device_set_timeout(cdev, 3*HZ); @@ -1048,8 +990,7 @@ 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, - (u32)(addr_t)sch) != 0) + if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0) /* Couldn't enable the subchannel for i/o. Sick device. */ return; @@ -1065,9 +1006,9 @@ device_trigger_reprobe(struct subchannel *sch) { struct ccw_device *cdev; - cdev = sch_get_cdev(sch); - if (!cdev) + if (!sch->dev.driver_data) return; + cdev = sch->dev.driver_data; if (cdev->private->state != DEV_STATE_DISCONNECTED) return; @@ -1087,7 +1028,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)(addr_t)sch; + sch->schib.pmcw.intparm = (__u32)(unsigned long)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) { @@ -1282,4 +1223,21 @@ 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/trunk/drivers/s390/cio/device_id.c b/trunk/drivers/s390/cio/device_id.c index 918b8b89cf9a..156f3f9786b5 100644 --- a/trunk/drivers/s390/cio/device_id.c +++ b/trunk/drivers/s390/cio/device_id.c @@ -24,7 +24,6 @@ #include "css.h" #include "device.h" #include "ioasm.h" -#include "io_sch.h" /* * Input : @@ -220,13 +219,11 @@ ccw_device_check_sense_id(struct ccw_device *cdev) return -EAGAIN; } if (irb->scsw.cc == 3) { - u8 lpm; - - lpm = to_io_private(sch)->orb.lpm; - if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0) + if ((sch->orb.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", lpm, + "'not operational'\n", sch->orb.lpm, cdev->private->dev_id.devno, sch->schid.ssid, sch->schid.sch_no); return -EACCES; diff --git a/trunk/drivers/s390/cio/device_ops.c b/trunk/drivers/s390/cio/device_ops.c index 49b58eb0fab8..7fd2dadc3297 100644 --- a/trunk/drivers/s390/cio/device_ops.c +++ b/trunk/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, (u32)(addr_t)sch); + ret = cio_enable_subchannel(sch, 3); if (ret) goto out_unlock; /* diff --git a/trunk/drivers/s390/cio/device_pgid.c b/trunk/drivers/s390/cio/device_pgid.c index c52449a1f9fc..cb1879a96818 100644 --- a/trunk/drivers/s390/cio/device_pgid.c +++ b/trunk/drivers/s390/cio/device_pgid.c @@ -22,7 +22,6 @@ #include "css.h" #include "device.h" #include "ioasm.h" -#include "io_sch.h" /* * Helper function called from interrupt context to decide whether an @@ -156,13 +155,10 @@ __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, lpm); + sch->schid.sch_no, sch->orb.lpm); return -EACCES; } i = 8 - ffs(cdev->private->imask); diff --git a/trunk/drivers/s390/cio/device_status.c b/trunk/drivers/s390/cio/device_status.c index ebe0848cfe33..aa96e6752592 100644 --- a/trunk/drivers/s390/cio/device_status.c +++ b/trunk/drivers/s390/cio/device_status.c @@ -20,7 +20,6 @@ #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 @@ -311,7 +310,6 @@ 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); @@ -328,16 +326,15 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb) /* * We have ending status but no sense information. Do a basic sense. */ - 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; + 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; /* Reset internal retry indication. */ cdev->private->flags.intretry = 0; - return cio_start(sch, sense_ccw, 0xff); + return cio_start (sch, &sch->sense_ccw, 0xff); } /* diff --git a/trunk/drivers/s390/cio/io_sch.h b/trunk/drivers/s390/cio/io_sch.h deleted file mode 100644 index 8c613160bfce..000000000000 --- a/trunk/drivers/s390/cio/io_sch.h +++ /dev/null @@ -1,163 +0,0 @@ -#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/trunk/drivers/s390/cio/ioasm.h b/trunk/drivers/s390/cio/ioasm.h index 652ea3625f9d..7153dd959082 100644 --- a/trunk/drivers/s390/cio/ioasm.h +++ b/trunk/drivers/s390/cio/ioasm.h @@ -109,6 +109,72 @@ 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/trunk/drivers/s390/cio/qdio.c b/trunk/drivers/s390/cio/qdio.c index e2a781b6b21d..40a3208c7cf3 100644 --- a/trunk/drivers/s390/cio/qdio.c +++ b/trunk/drivers/s390/cio/qdio.c @@ -48,11 +48,11 @@ #include #include #include -#include #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 void *tiqdio_ind; +static int register_thinint_result; 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 = 0; i < INDICATORS_PER_CACHELINE; i++) + for (i=1;ihydra_gives_outbound_pcis) { if (!q->siga_sync_done_on_thinints) { SYNC_MEMORY_ALL; - } else if (!q->siga_sync_done_on_outb_tis) { + } else if ((!q->siga_sync_done_on_outb_tis)&& + (q->hydra_gives_outbound_pcis)) { SYNC_MEMORY_ALL_OUTB; } } else { @@ -1910,7 +1911,8 @@ qdio_fill_thresholds(struct qdio_irq *irq_ptr, } } -static void tiqdio_thinint_handler(void *ind, void *drv_data) +static int +tiqdio_thinint_handler(void) { QDIO_DBF_TEXT4(0,trace,"thin_int"); @@ -1923,6 +1925,7 @@ static void tiqdio_thinint_handler(void *ind, void *drv_data) tiqdio_clear_global_summary(); tiqdio_inbound_checks(); + return 0; } static void @@ -2442,7 +2445,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 *)tiqdio_ind); + virt_to_phys((volatile void *)indicators); real_addr_dev_st_chg_ind= virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind); } @@ -3737,25 +3740,23 @@ static void tiqdio_register_thinints(void) { char dbf_text[20]; - - tiqdio_ind = - s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL); - if (IS_ERR(tiqdio_ind)) { - sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind)); + register_thinint_result= + s390_register_adapter_interrupt(&tiqdio_thinint_handler); + if (register_thinint_result) { + sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff)); QDIO_DBF_TEXT0(0,setup,dbf_text); QDIO_PRINT_ERR("failed to register adapter handler " \ - "(rc=%li).\nAdapter interrupts might " \ + "(rc=%i).\nAdapter interrupts might " \ "not work. Continuing.\n", - PTR_ERR(tiqdio_ind)); - tiqdio_ind = NULL; + register_thinint_result); } } static void tiqdio_unregister_thinints(void) { - if (tiqdio_ind) - s390_unregister_adapter_interrupt(tiqdio_ind); + if (!register_thinint_result) + s390_unregister_adapter_interrupt(&tiqdio_thinint_handler); } static int @@ -3767,8 +3768,8 @@ qdio_get_qdio_memory(void) for (i=1;i>10) #define QDIO_CLEANUP_CLEAR_TIMEOUT (20*HZ) #define QDIO_CLEANUP_HALT_TIMEOUT (10*HZ) #define QDIO_FORCE_CHECK_TIMEOUT (10*HZ) diff --git a/trunk/drivers/s390/net/claw.c b/trunk/drivers/s390/net/claw.c index c3076217871e..3561982749e3 100644 --- a/trunk/drivers/s390/net/claw.c +++ b/trunk/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/trunk/drivers/s390/net/lcs.c b/trunk/drivers/s390/net/lcs.c index 7bfe8d707a34..0fd663b23d76 100644 --- a/trunk/drivers/s390/net/lcs.c +++ b/trunk/drivers/s390/net/lcs.c @@ -1115,7 +1115,7 @@ lcs_fix_multicast_list(struct lcs_card *card) 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/trunk/drivers/s390/net/netiucv.c b/trunk/drivers/s390/net/netiucv.c index f3d893cfe61d..d6e93f15440e 100644 --- a/trunk/drivers/s390/net/netiucv.c +++ b/trunk/drivers/s390/net/netiucv.c @@ -198,7 +198,8 @@ struct iucv_connection { /** * Linked list of all connection structs. */ -static LIST_HEAD(iucv_connection_list); +static struct list_head iucv_connection_list = + LIST_HEAD_INIT(iucv_connection_list); static DEFINE_RWLOCK(iucv_connection_rwlock); /** diff --git a/trunk/drivers/s390/net/qeth_proc.c b/trunk/drivers/s390/net/qeth_proc.c index 46ecd03a597e..f1ff165a5e05 100644 --- a/trunk/drivers/s390/net/qeth_proc.c +++ b/trunk/drivers/s390/net/qeth_proc.c @@ -146,7 +146,7 @@ qeth_procfile_seq_show(struct seq_file *s, void *it) return 0; } -static const struct seq_operations qeth_procfile_seq_ops = { +static 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 const struct seq_operations qeth_perf_procfile_seq_ops = { +static 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/trunk/drivers/s390/net/smsgiucv.c b/trunk/drivers/s390/net/smsgiucv.c index 8735a415a116..47bb47b48581 100644 --- a/trunk/drivers/s390/net/smsgiucv.c +++ b/trunk/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 LIST_HEAD(smsg_list); +static struct list_head smsg_list = LIST_HEAD_INIT(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/trunk/drivers/s390/scsi/zfcp_erp.c b/trunk/drivers/s390/scsi/zfcp_erp.c index 2dc8110ebf74..4f86c0e12961 100644 --- a/trunk/drivers/s390/scsi/zfcp_erp.c +++ b/trunk/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 separate strategies will fail + * Note: in case of timeout, the seperate 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/trunk/drivers/s390/scsi/zfcp_fsf.c b/trunk/drivers/s390/scsi/zfcp_fsf.c index e45f85f7c7ed..fe57941ab55d 100644 --- a/trunk/drivers/s390/scsi/zfcp_fsf.c +++ b/trunk/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/trunk/drivers/s390/scsi/zfcp_qdio.c b/trunk/drivers/s390/scsi/zfcp_qdio.c index 22fdc17e0d0e..51d92b196ee7 100644 --- a/trunk/drivers/s390/scsi/zfcp_qdio.c +++ b/trunk/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 length in current SBALE + * zfcp_qdio_sbale_fill - set address and lenght in current SBALE * on request_queue */ static void diff --git a/trunk/include/asm-s390/airq.h b/trunk/include/asm-s390/airq.h deleted file mode 100644 index 41d028cb52a4..000000000000 --- a/trunk/include/asm-s390/airq.h +++ /dev/null @@ -1,19 +0,0 @@ -/* - * include/asm-s390/airq.h - * - * Copyright IBM Corp. 2002,2007 - * Author(s): Ingo Adlung - * Cornelia Huck - * Arnd Bergmann - * Peter Oberparleiter - */ - -#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/trunk/include/asm-s390/cio.h b/trunk/include/asm-s390/cio.h index 123b557c3ff4..2f08c16e44ad 100644 --- a/trunk/include/asm-s390/cio.h +++ b/trunk/include/asm-s390/cio.h @@ -24,8 +24,8 @@ * @fmt: format * @pfch: prefetch * @isic: initial-status interruption control - * @alcc: address-limit checking control - * @ssi: suppress-suspended interruption + * @alcc: adress-limit checking control + * @ssi: supress-suspended interruption * @zcc: zero condition code * @ectl: extended control * @pno: path not operational diff --git a/trunk/include/asm-s390/dasd.h b/trunk/include/asm-s390/dasd.h index 3f002e13d024..604f68fa6f56 100644 --- a/trunk/include/asm-s390/dasd.h +++ b/trunk/include/asm-s390/dasd.h @@ -105,7 +105,7 @@ typedef struct dasd_information_t { } dasd_information_t; /* - * Read Subsystem Data - Performance Statistics + * Read Subsystem Data - Perfomance Statistics */ typedef struct dasd_rssd_perf_stats_t { unsigned char invalid:1; diff --git a/trunk/include/asm-s390/ipl.h b/trunk/include/asm-s390/ipl.h index c1b2e50392bb..2c40fd3a137f 100644 --- a/trunk/include/asm-s390/ipl.h +++ b/trunk/include/asm-s390/ipl.h @@ -83,8 +83,6 @@ 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 { @@ -120,7 +118,7 @@ struct ipl_info }; extern struct ipl_info ipl_info; -extern void setup_ipl(void); +extern void setup_ipl_info(void); /* * DIAG 308 support @@ -143,10 +141,6 @@ 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/trunk/include/asm-s390/mmu_context.h b/trunk/include/asm-s390/mmu_context.h index a77d4ba3c8eb..05b842126b99 100644 --- a/trunk/include/asm-s390/mmu_context.h +++ b/trunk/include/asm-s390/mmu_context.h @@ -12,15 +12,10 @@ #include #include -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; -} +/* + * get a new mmu context.. S390 don't know about contexts. + */ +#define init_new_context(tsk,mm) 0 #define destroy_context(mm) do { } while (0) @@ -32,11 +27,19 @@ static inline int init_new_context(struct task_struct *tsk, static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk) { - S390_lowcore.user_asce = mm->context | __pa(mm->pgd); + 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); if (switch_amode) { /* Load primary space page table origin. */ - pgd_t *shadow_pgd = get_shadow_table(mm->pgd) ? : mm->pgd; - S390_lowcore.user_exec_asce = mm->context | __pa(shadow_pgd); + pgd_t *shadow_pgd = get_shadow_table(pgd) ? : pgd; + S390_lowcore.user_exec_asce = asce_bits | __pa(shadow_pgd); asm volatile(LCTL_OPCODE" 1,1,%0\n" : : "m" (S390_lowcore.user_exec_asce) ); } else diff --git a/trunk/include/asm-s390/pgtable.h b/trunk/include/asm-s390/pgtable.h index 79b9eab1a0c7..1f530f8a6280 100644 --- a/trunk/include/asm-s390/pgtable.h +++ b/trunk/include/asm-s390/pgtable.h @@ -104,27 +104,41 @@ extern char empty_zero_page[PAGE_SIZE]; #ifndef __ASSEMBLY__ /* - * 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. + * 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. */ #ifndef __s390x__ -#define VMALLOC_START 0x78000000UL -#define VMALLOC_END 0x7e000000UL -#define VMEM_MAP_MAX 0x80000000UL +#define VMALLOC_MIN_SIZE 0x8000000UL +#define VMALLOC_END_INIT 0x80000000UL #else /* __s390x__ */ -#define VMALLOC_START 0x3e000000000UL -#define VMALLOC_END 0x3e040000000UL -#define VMEM_MAP_MAX 0x40000000000UL +#define VMALLOC_MIN_SIZE 0x2000000000UL +#define VMALLOC_END_INIT 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/trunk/include/asm-s390/processor.h b/trunk/include/asm-s390/processor.h index c86b982aef5a..21d40a19355e 100644 --- a/trunk/include/asm-s390/processor.h +++ b/trunk/include/asm-s390/processor.h @@ -59,6 +59,9 @@ 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. */ @@ -92,6 +95,7 @@ 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/trunk/include/asm-s390/ptrace.h b/trunk/include/asm-s390/ptrace.h index 61f6952f2e35..332ee73688fc 100644 --- a/trunk/include/asm-s390/ptrace.h +++ b/trunk/include/asm-s390/ptrace.h @@ -465,14 +465,6 @@ 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/trunk/include/asm-s390/qdio.h b/trunk/include/asm-s390/qdio.h index 4b8ff55f680e..74db1dc10a7d 100644 --- a/trunk/include/asm-s390/qdio.h +++ b/trunk/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-information-block key */ + unsigned int qkey : 4; /* queue-informatio-block key */ unsigned int res9 : 28; /* reserved */ /* union _qd {*/ /* why this? */ struct qdesfmt0 qdf0[126]; diff --git a/trunk/include/asm-s390/rwsem.h b/trunk/include/asm-s390/rwsem.h index 9d2a17971805..90f4eccaa290 100644 --- a/trunk/include/asm-s390/rwsem.h +++ b/trunk/include/asm-s390/rwsem.h @@ -91,8 +91,8 @@ struct rw_semaphore { #endif #define __RWSEM_INITIALIZER(name) \ - { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait.lock), \ - LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) } +{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, 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/trunk/include/asm-s390/sclp.h b/trunk/include/asm-s390/sclp.h index b5f2843013a3..cb9faf1ea5cf 100644 --- a/trunk/include/asm-s390/sclp.h +++ b/trunk/include/asm-s390/sclp.h @@ -27,25 +27,7 @@ struct sclp_ipl_info { char loadparm[LOADPARM_LEN]; }; -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_readinfo_early(void); void sclp_facilities_detect(void); unsigned long long sclp_memory_detect(void); int sclp_sdias_blk_count(void); diff --git a/trunk/include/asm-s390/smp.h b/trunk/include/asm-s390/smp.h index c7b74326a527..07708c07701e 100644 --- a/trunk/include/asm-s390/smp.h +++ b/trunk/include/asm-s390/smp.h @@ -35,6 +35,8 @@ 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 */ /* @@ -90,8 +92,6 @@ 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,6 +103,7 @@ 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/trunk/include/asm-s390/spinlock.h b/trunk/include/asm-s390/spinlock.h index df84ae96915f..3fd43826fd0b 100644 --- a/trunk/include/asm-s390/spinlock.h +++ b/trunk/include/asm-s390/spinlock.h @@ -53,48 +53,44 @@ _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 *); -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_lock_wait(raw_spinlock_t *, unsigned int pc); +extern int _raw_spin_trylock_retry(raw_spinlock_t *, unsigned int pc); 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)) + if (likely(old == 0)) { + lp->owner_pc = pc; return; - _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); + } + _raw_spin_lock_wait(lp, pc); } 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)) + if (likely(old == 0)) { + lp->owner_pc = pc; return 1; - return _raw_spin_trylock_retry(lp); + } + return _raw_spin_trylock_retry(lp, pc); } 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/trunk/include/asm-s390/spinlock_types.h b/trunk/include/asm-s390/spinlock_types.h index 654abc40de04..b7ac13f7aa37 100644 --- a/trunk/include/asm-s390/spinlock_types.h +++ b/trunk/include/asm-s390/spinlock_types.h @@ -7,6 +7,7 @@ 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/trunk/include/asm-s390/tlbflush.h b/trunk/include/asm-s390/tlbflush.h index 70fa5ae58180..a69bd2490d52 100644 --- a/trunk/include/asm-s390/tlbflush.h +++ b/trunk/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(unsigned long asce) +static inline void __tlb_flush_idte(pgd_t *pgd) { asm volatile( " .insn rrf,0xb98e0000,0,%0,%1,0" - : : "a" (2048), "a" (asce) : "cc" ); + : : "a" (2048), "a" (__pa(pgd) & PAGE_MASK) : "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 = get_shadow_table(mm->pgd); + pgd_t *shadow_pgd = get_shadow_table(mm->pgd); - if (shadow) - __tlb_flush_idte((unsigned long) shadow | mm->context); - __tlb_flush_idte((unsigned long) mm->pgd | mm->context); + if (shadow_pgd) + __tlb_flush_idte(shadow_pgd); + __tlb_flush_idte(mm->pgd); return; } preempt_disable(); @@ -106,23 +106,9 @@ 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) - -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); -} +#define flush_tlb_range(vma, start, end) __tlb_flush_mm_cond(mm) +#define flush_tlb_kernel_range(start, end) __tlb_flush_mm(&init_mm) #endif /* _S390_TLBFLUSH_H */ diff --git a/trunk/include/asm-s390/zcrypt.h b/trunk/include/asm-s390/zcrypt.h index f228f1b86877..a5dada617751 100644 --- a/trunk/include/asm-s390/zcrypt.h +++ b/trunk/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 'address'*/ + unsigned char * rpl_extb; /* reply extension block 'addres'*/ 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/trunk/kernel/sysctl_check.c b/trunk/kernel/sysctl_check.c index d8a5558a47b4..a68425a5cc1d 100644 --- a/trunk/kernel/sysctl_check.c +++ b/trunk/kernel/sysctl_check.c @@ -1,5 +1,6 @@ #include #include +#include "../arch/s390/appldata/appldata.h" #include "../fs/xfs/linux-2.6/xfs_sysctl.h" #include #include diff --git a/trunk/security/selinux/ss/services.c b/trunk/security/selinux/ss/services.c index 4bf715d4cf29..f83b19daed16 100644 --- a/trunk/security/selinux/ss/services.c +++ b/trunk/security/selinux/ss/services.c @@ -1744,9 +1744,6 @@ int security_genfs_sid(const char *fstype, struct ocontext *c; int rc = 0, cmp = 0; - while (path[0] == '/' && path[1] == '/') - path++; - POLICY_RDLOCK; for (genfs = policydb.genfs; genfs; genfs = genfs->next) {