From 53ec24b1e6c7118a127cf029a1519a2ce55268ec Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Wed, 5 Jan 2011 12:46:44 +0100 Subject: [S390] zcrypt: Fix check to look for facility bits 2 & 65 Fix the check for ap interupts to look for facility bits 2 and 65. Make sure that we only register interrupts for aps, if the machine has ap interrupt support. This patch is relevant only for the 2.6.37 stable series. Cc: stable@kernel.org Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 8fd8c62455e9..a1ba52a09602 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -154,7 +154,7 @@ static inline int ap_instructions_available(void) */ static int ap_interrupts_available(void) { - return test_facility(1) && test_facility(2); + return test_facility(2) && test_facility(65); } /** -- cgit v1.2.3 From 6f9a3c330652b0fdb65d89e94977a8e79fe730e7 Mon Sep 17 00:00:00 2001 From: Martin Schwidefsky Date: Wed, 5 Jan 2011 12:47:15 +0100 Subject: [S390] cleanup s390 Kconfig Make use of def_bool and def_tristate where possible and add sensible defaults to the config symbols where applicable. This shortens the defconfig file by another ~40 lines. Signed-off-by: Martin Schwidefsky --- arch/s390/Kconfig | 129 ++++++++++++++++++++------------------ arch/s390/Kconfig.debug | 6 +- arch/s390/defconfig | 152 +++++++++++++++++---------------------------- arch/s390/kvm/Kconfig | 7 ++- drivers/s390/block/Kconfig | 24 ++++--- drivers/s390/char/Kconfig | 69 ++++++++++++-------- drivers/s390/net/Kconfig | 51 ++++++++------- 7 files changed, 218 insertions(+), 220 deletions(-) (limited to 'drivers') diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index e0b98e71ff47..3243f7a52c72 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -1,13 +1,8 @@ -config SCHED_MC - def_bool y - depends on SMP - config MMU def_bool y config ZONE_DMA - def_bool y - depends on 64BIT + def_bool y if 64BIT config LOCKDEP_SUPPORT def_bool y @@ -25,12 +20,10 @@ config RWSEM_XCHGADD_ALGORITHM def_bool y config ARCH_HAS_ILOG2_U32 - bool - default n + def_bool n config ARCH_HAS_ILOG2_U64 - bool - default n + def_bool n config GENERIC_HWEIGHT def_bool y @@ -42,9 +35,7 @@ config GENERIC_CLOCKEVENTS def_bool y config GENERIC_BUG - bool - depends on BUG - default y + def_bool y if BUG config GENERIC_BUG_RELATIVE_POINTERS def_bool y @@ -59,13 +50,10 @@ config ARCH_DMA_ADDR_T_64BIT def_bool 64BIT config GENERIC_LOCKBREAK - bool - default y - depends on SMP && PREEMPT + def_bool y if SMP && PREEMPT config PGSTE - bool - default y if KVM + def_bool y if KVM config VIRT_CPU_ACCOUNTING def_bool y @@ -129,8 +117,7 @@ config S390 select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE config SCHED_OMIT_FRAME_POINTER - bool - default y + def_bool y source "init/Kconfig" @@ -143,20 +130,21 @@ comment "Processor type and features" source "kernel/time/Kconfig" config 64BIT - bool "64 bit kernel" + def_bool y + prompt "64 bit kernel" help Select this option if you have an IBM z/Architecture machine and want to use the 64 bit addressing mode. config 32BIT - bool - default y if !64BIT + def_bool y if !64BIT config KTIME_SCALAR def_bool 32BIT config SMP - bool "Symmetric multi-processing support" + def_bool y + prompt "Symmetric multi-processing support" ---help--- This enables support for systems with more than one CPU. If you have a system with only one CPU, like most personal computers, say N. If @@ -188,10 +176,10 @@ config NR_CPUS approximately sixteen kilobytes to the kernel image. config HOTPLUG_CPU - bool "Support for hot-pluggable CPUs" + def_bool y + prompt "Support for hot-pluggable CPUs" depends on SMP select HOTPLUG - default n help Say Y here to be able to turn CPUs off and on. CPUs can be controlled through /sys/devices/system/cpu/cpu#. @@ -207,14 +195,16 @@ config SCHED_MC increased overhead in some places. config SCHED_BOOK - bool "Book scheduler support" + def_bool y + prompt "Book scheduler support" depends on SMP && SCHED_MC help Book scheduler support improves the CPU scheduler's decision making when dealing with machines that have several books. config MATHEMU - bool "IEEE FPU emulation" + def_bool y + prompt "IEEE FPU emulation" depends on MARCH_G5 help This option is required for IEEE compliant floating point arithmetic @@ -222,7 +212,8 @@ config MATHEMU need this. config COMPAT - bool "Kernel support for 31 bit emulation" + def_bool y + prompt "Kernel support for 31 bit emulation" depends on 64BIT select COMPAT_BINFMT_ELF help @@ -232,16 +223,14 @@ config COMPAT executing 31 bit applications. It is safe to say "Y". config SYSVIPC_COMPAT - bool - depends on COMPAT && SYSVIPC - default y + def_bool y if COMPAT && SYSVIPC config AUDIT_ARCH - bool - default y + def_bool y config S390_EXEC_PROTECT - bool "Data execute protection" + def_bool y + prompt "Data execute protection" help This option allows to enable a buffer overflow protection for user space programs and it also selects the addressing mode option above. @@ -301,7 +290,8 @@ config MARCH_Z196 endchoice config PACK_STACK - bool "Pack kernel stack" + def_bool y + prompt "Pack kernel stack" help This option enables the compiler option -mkernel-backchain if it is available. If the option is available the compiler supports @@ -314,7 +304,8 @@ config PACK_STACK Say Y if you are unsure. config SMALL_STACK - bool "Use 8kb for kernel stack instead of 16kb" + def_bool n + prompt "Use 8kb for kernel stack instead of 16kb" depends on PACK_STACK && 64BIT && !LOCKDEP help If you say Y here and the compiler supports the -mkernel-backchain @@ -326,7 +317,8 @@ config SMALL_STACK Say N if you are unsure. config CHECK_STACK - bool "Detect kernel stack overflow" + def_bool y + prompt "Detect kernel stack overflow" help This option enables the compiler option -mstack-guard and -mstack-size if they are available. If the compiler supports them @@ -350,7 +342,8 @@ config STACK_GUARD 512 for 64 bit. config WARN_STACK - bool "Emit compiler warnings for function with broken stack usage" + def_bool n + prompt "Emit compiler warnings for function with broken stack usage" help This option enables the compiler options -mwarn-framesize and -mwarn-dynamicstack. If the compiler supports these options it @@ -385,24 +378,24 @@ config ARCH_SPARSEMEM_DEFAULT def_bool y config ARCH_SELECT_MEMORY_MODEL - def_bool y + def_bool y config ARCH_ENABLE_MEMORY_HOTPLUG - def_bool y - depends on SPARSEMEM + def_bool y if SPARSEMEM config ARCH_ENABLE_MEMORY_HOTREMOVE def_bool y config ARCH_HIBERNATION_POSSIBLE - def_bool y if 64BIT + def_bool y if 64BIT source "mm/Kconfig" comment "I/O subsystem configuration" config QDIO - tristate "QDIO support" + def_tristate y + prompt "QDIO support" ---help--- This driver provides the Queued Direct I/O base support for IBM System z. @@ -413,7 +406,8 @@ config QDIO If unsure, say Y. config CHSC_SCH - tristate "Support for CHSC subchannels" + def_tristate y + prompt "Support for CHSC subchannels" help This driver allows usage of CHSC subchannels. A CHSC subchannel is usually present on LPAR only. @@ -431,7 +425,8 @@ config CHSC_SCH comment "Misc" config IPL - bool "Builtin IPL record support" + def_bool y + prompt "Builtin IPL record support" help If you want to use the produced kernel to IPL directly from a device, you have to merge a bootsector specific to the device @@ -463,7 +458,8 @@ config FORCE_MAX_ZONEORDER default "9" config PFAULT - bool "Pseudo page fault support" + def_bool y + prompt "Pseudo page fault support" help Select this option, if you want to use PFAULT pseudo page fault handling under VM. If running native or in LPAR, this option @@ -475,7 +471,8 @@ config PFAULT this option. config SHARED_KERNEL - bool "VM shared kernel support" + def_bool y + prompt "VM shared kernel support" help Select this option, if you want to share the text segment of the Linux kernel between different VM guests. This reduces memory @@ -486,7 +483,8 @@ config SHARED_KERNEL doing and want to exploit this feature. config CMM - tristate "Cooperative memory management" + def_tristate n + prompt "Cooperative memory management" help Select this option, if you want to enable the kernel interface to reduce the memory size of the system. This is accomplished @@ -498,14 +496,16 @@ config CMM option. config CMM_IUCV - bool "IUCV special message interface to cooperative memory management" + def_bool y + prompt "IUCV special message interface to cooperative memory management" depends on CMM && (SMSGIUCV=y || CMM=SMSGIUCV) help Select this option to enable the special message interface to the cooperative memory management. config APPLDATA_BASE - bool "Linux - VM Monitor Stream, base infrastructure" + def_bool n + prompt "Linux - VM Monitor Stream, base infrastructure" depends on PROC_FS help This provides a kernel interface for creating and updating z/VM APPLDATA @@ -520,7 +520,8 @@ config APPLDATA_BASE The /proc entries can also be read from, showing the current settings. config APPLDATA_MEM - tristate "Monitor memory management statistics" + def_tristate m + prompt "Monitor memory management statistics" depends on APPLDATA_BASE && VM_EVENT_COUNTERS help This provides memory management related data to the Linux - VM Monitor @@ -536,7 +537,8 @@ config APPLDATA_MEM appldata_mem.o. config APPLDATA_OS - tristate "Monitor OS statistics" + def_tristate m + prompt "Monitor OS statistics" depends on APPLDATA_BASE help This provides OS related data to the Linux - VM Monitor Stream, like @@ -550,7 +552,8 @@ config APPLDATA_OS appldata_os.o. config APPLDATA_NET_SUM - tristate "Monitor overall network statistics" + def_tristate m + prompt "Monitor overall network statistics" depends on APPLDATA_BASE && NET help This provides network related data to the Linux - VM Monitor Stream, @@ -567,30 +570,32 @@ config APPLDATA_NET_SUM source kernel/Kconfig.hz config S390_HYPFS_FS - bool "s390 hypervisor file system support" + def_bool y + prompt "s390 hypervisor file system support" select SYS_HYPERVISOR - default y help This is a virtual file system intended to provide accounting information in an s390 hypervisor environment. config KEXEC - bool "kexec system call" + def_bool n + prompt "kexec system call" help kexec is a system call that implements the ability to shutdown your current kernel, and to start another kernel. It is like a reboot but is independent of hardware/microcode support. config ZFCPDUMP - bool "zfcpdump support" + def_bool n + prompt "zfcpdump support" select SMP - default n help Select this option if you want to build an zfcpdump enabled kernel. Refer to for more details on this. config S390_GUEST -bool "s390 guest support for KVM (EXPERIMENTAL)" + def_bool y + prompt "s390 guest support for KVM (EXPERIMENTAL)" depends on 64BIT && EXPERIMENTAL select VIRTIO select VIRTIO_RING @@ -602,9 +607,9 @@ bool "s390 guest support for KVM (EXPERIMENTAL)" the default console. config SECCOMP - bool "Enable seccomp to safely compute untrusted bytecode" + def_bool y + prompt "Enable seccomp to safely compute untrusted bytecode" depends on PROC_FS - default y help This kernel feature is useful for number crunching applications that may need to compute untrusted bytecode during their diff --git a/arch/s390/Kconfig.debug b/arch/s390/Kconfig.debug index 05221b13ffb1..2b380df95606 100644 --- a/arch/s390/Kconfig.debug +++ b/arch/s390/Kconfig.debug @@ -1,8 +1,7 @@ menu "Kernel hacking" config TRACE_IRQFLAGS_SUPPORT - bool - default y + def_bool y source "lib/Kconfig.debug" @@ -19,7 +18,8 @@ config STRICT_DEVMEM If you are unsure, say Y. config DEBUG_STRICT_USER_COPY_CHECKS - bool "Strict user copy size checks" + def_bool n + prompt "Strict user copy size checks" ---help--- Enabling this option turns a certain set of sanity checks for user copy operations into compile time warnings. diff --git a/arch/s390/defconfig b/arch/s390/defconfig index e40ac6ee6526..d79697157ac0 100644 --- a/arch/s390/defconfig +++ b/arch/s390/defconfig @@ -2,16 +2,12 @@ CONFIG_EXPERIMENTAL=y CONFIG_SYSVIPC=y CONFIG_POSIX_MQUEUE=y CONFIG_AUDIT=y +CONFIG_RCU_TRACE=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y -CONFIG_CGROUPS=y -CONFIG_CGROUP_NS=y -CONFIG_SYSFS_DEPRECATED_V2=y -CONFIG_UTS_NS=y -CONFIG_IPC_NS=y CONFIG_BLK_DEV_INITRD=y # CONFIG_CC_OPTIMIZE_FOR_SIZE is not set -# CONFIG_COMPAT_BRK is not set +CONFIG_PERF_EVENTS=y CONFIG_SLAB=y CONFIG_KPROBES=y CONFIG_MODULES=y @@ -20,24 +16,12 @@ CONFIG_MODVERSIONS=y CONFIG_DEFAULT_DEADLINE=y CONFIG_NO_HZ=y CONFIG_HIGH_RES_TIMERS=y -CONFIG_64BIT=y -CONFIG_SMP=y -CONFIG_NR_CPUS=32 -CONFIG_COMPAT=y -CONFIG_S390_EXEC_PROTECT=y -CONFIG_PACK_STACK=y -CONFIG_CHECK_STACK=y CONFIG_PREEMPT=y CONFIG_MEMORY_HOTPLUG=y CONFIG_MEMORY_HOTREMOVE=y -CONFIG_QDIO=y -CONFIG_CHSC_SCH=m -CONFIG_IPL=y CONFIG_BINFMT_MISC=m -CONFIG_PFAULT=y CONFIG_HZ_100=y CONFIG_KEXEC=y -CONFIG_S390_GUEST=y CONFIG_PM=y CONFIG_HIBERNATION=y CONFIG_PACKET=y @@ -46,16 +30,15 @@ CONFIG_NET_KEY=y CONFIG_AFIUCV=m CONFIG_INET=y CONFIG_IP_MULTICAST=y +# CONFIG_INET_LRO is not set CONFIG_IPV6=y -CONFIG_NETFILTER=y -CONFIG_NETFILTER_NETLINK_QUEUE=m -CONFIG_NETFILTER_NETLINK_LOG=m -CONFIG_NF_CONNTRACK=m -# CONFIG_NF_CT_PROTO_SCTP is not set +CONFIG_NET_SCTPPROBE=m +CONFIG_L2TP=m +CONFIG_L2TP_DEBUGFS=m +CONFIG_VLAN_8021Q=y CONFIG_NET_SCHED=y CONFIG_NET_SCH_CBQ=m CONFIG_NET_SCH_PRIO=m -CONFIG_NET_SCH_MULTIQ=y CONFIG_NET_SCH_RED=m CONFIG_NET_SCH_SFQ=m CONFIG_NET_SCH_TEQL=m @@ -69,28 +52,14 @@ CONFIG_NET_CLS_U32=m CONFIG_CLS_U32_MARK=y CONFIG_NET_CLS_RSVP=m CONFIG_NET_CLS_RSVP6=m -CONFIG_NET_CLS_FLOW=m CONFIG_NET_CLS_ACT=y CONFIG_NET_ACT_POLICE=y -CONFIG_NET_ACT_NAT=m -CONFIG_CAN=m -CONFIG_CAN_RAW=m -CONFIG_CAN_BCM=m -CONFIG_CAN_VCAN=m CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" # CONFIG_FIRMWARE_IN_KERNEL is not set CONFIG_BLK_DEV_LOOP=m CONFIG_BLK_DEV_NBD=m CONFIG_BLK_DEV_RAM=y -CONFIG_BLK_DEV_XIP=y -CONFIG_BLK_DEV_XPRAM=m -CONFIG_DASD=y -CONFIG_DASD_PROFILE=y -CONFIG_DASD_ECKD=y -CONFIG_DASD_FBA=y -CONFIG_DASD_DIAG=y -CONFIG_DASD_EER=y -CONFIG_VIRTIO_BLK=m +CONFIG_VIRTIO_BLK=y CONFIG_SCSI=y CONFIG_BLK_DEV_SD=y CONFIG_CHR_DEV_ST=y @@ -102,101 +71,92 @@ CONFIG_SCSI_CONSTANTS=y CONFIG_SCSI_LOGGING=y CONFIG_SCSI_SCAN_ASYNC=y CONFIG_ZFCP=y -CONFIG_SCSI_DH=m -CONFIG_SCSI_DH_RDAC=m -CONFIG_SCSI_DH_HP_SW=m -CONFIG_SCSI_DH_EMC=m -CONFIG_SCSI_DH_ALUA=m -CONFIG_SCSI_OSD_INITIATOR=m -CONFIG_SCSI_OSD_ULD=m -CONFIG_MD=y -CONFIG_BLK_DEV_MD=y -CONFIG_MD_LINEAR=m -CONFIG_MD_RAID0=m -CONFIG_MD_RAID1=m -CONFIG_MD_MULTIPATH=m -CONFIG_BLK_DEV_DM=y -CONFIG_DM_CRYPT=y -CONFIG_DM_SNAPSHOT=y -CONFIG_DM_MIRROR=y -CONFIG_DM_ZERO=y -CONFIG_DM_MULTIPATH=m +CONFIG_ZFCP_DIF=y CONFIG_NETDEVICES=y CONFIG_DUMMY=m CONFIG_BONDING=m CONFIG_EQUALIZER=m CONFIG_TUN=m -CONFIG_VETH=m CONFIG_NET_ETHERNET=y -CONFIG_LCS=m -CONFIG_CTCM=m -CONFIG_QETH=y -CONFIG_QETH_L2=y -CONFIG_QETH_L3=y -CONFIG_VIRTIO_NET=m -CONFIG_HW_RANDOM_VIRTIO=m +CONFIG_VIRTIO_NET=y CONFIG_RAW_DRIVER=m -CONFIG_TN3270=y -CONFIG_TN3270_TTY=y -CONFIG_TN3270_FS=m -CONFIG_TN3270_CONSOLE=y -CONFIG_TN3215=y -CONFIG_TN3215_CONSOLE=y -CONFIG_SCLP_TTY=y -CONFIG_SCLP_CONSOLE=y -CONFIG_SCLP_VT220_TTY=y -CONFIG_SCLP_VT220_CONSOLE=y -CONFIG_SCLP_CPI=m -CONFIG_SCLP_ASYNC=m -CONFIG_S390_TAPE=m -CONFIG_S390_TAPE_BLOCK=y -CONFIG_S390_TAPE_34XX=m -CONFIG_ACCESSIBILITY=y CONFIG_EXT2_FS=y CONFIG_EXT3_FS=y # CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y CONFIG_PROC_KCORE=y CONFIG_TMPFS=y CONFIG_TMPFS_POSIX_ACL=y -CONFIG_NFS_FS=y -CONFIG_NFS_V3=y -CONFIG_NFSD=y -CONFIG_NFSD_V3=y +# CONFIG_NETWORK_FILESYSTEMS is not set CONFIG_PARTITION_ADVANCED=y CONFIG_IBM_PARTITION=y CONFIG_DLM=m CONFIG_MAGIC_SYSRQ=y CONFIG_DEBUG_KERNEL=y -# CONFIG_SCHED_DEBUG is not set -CONFIG_DEBUG_SPINLOCK=y -CONFIG_DEBUG_MUTEXES=y +CONFIG_TIMER_STATS=y +CONFIG_PROVE_LOCKING=y +CONFIG_PROVE_RCU=y +CONFIG_LOCK_STAT=y +CONFIG_DEBUG_LOCKDEP=y CONFIG_DEBUG_SPINLOCK_SLEEP=y +CONFIG_DEBUG_LIST=y +CONFIG_DEBUG_NOTIFIERS=y # CONFIG_RCU_CPU_STALL_DETECTOR is not set -CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y +CONFIG_KPROBES_SANITY_TEST=y +CONFIG_CPU_NOTIFIER_ERROR_INJECT=m +CONFIG_LATENCYTOP=y CONFIG_SYSCTL_SYSCALL_CHECK=y -CONFIG_SAMPLES=y -CONFIG_CRYPTO_FIPS=y +CONFIG_DEBUG_PAGEALLOC=y +# CONFIG_FTRACE is not set +# CONFIG_STRICT_DEVMEM is not set +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_CRYPTD=m CONFIG_CRYPTO_AUTHENC=m +CONFIG_CRYPTO_TEST=m CONFIG_CRYPTO_CCM=m CONFIG_CRYPTO_GCM=m +CONFIG_CRYPTO_CBC=y CONFIG_CRYPTO_CTS=m CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_LRW=m CONFIG_CRYPTO_PCBC=m +CONFIG_CRYPTO_XTS=m +CONFIG_CRYPTO_XCBC=m CONFIG_CRYPTO_VMAC=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MICHAEL_MIC=m CONFIG_CRYPTO_RMD128=m CONFIG_CRYPTO_RMD160=m CONFIG_CRYPTO_RMD256=m CONFIG_CRYPTO_RMD320=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_TGR192=m +CONFIG_CRYPTO_WP512=m +CONFIG_CRYPTO_ANUBIS=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_BLOWFISH=m CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_DES=m CONFIG_CRYPTO_FCRYPT=m +CONFIG_CRYPTO_KHAZAD=m CONFIG_CRYPTO_SALSA20=m CONFIG_CRYPTO_SEED=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_DEFLATE=m CONFIG_CRYPTO_ZLIB=m CONFIG_CRYPTO_LZO=m CONFIG_ZCRYPT=m +CONFIG_CRYPTO_SHA1_S390=m +CONFIG_CRYPTO_SHA256_S390=m CONFIG_CRYPTO_SHA512_S390=m -CONFIG_CRC_T10DIF=y -CONFIG_CRC32=m +CONFIG_CRYPTO_DES_S390=m +CONFIG_CRYPTO_AES_S390=m CONFIG_CRC7=m -CONFIG_KVM=m -CONFIG_VIRTIO_BALLOON=m +CONFIG_VIRTIO_BALLOON=y diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig index a7251580891c..f66a1bdbb61d 100644 --- a/arch/s390/kvm/Kconfig +++ b/arch/s390/kvm/Kconfig @@ -4,8 +4,8 @@ source "virt/kvm/Kconfig" menuconfig VIRTUALIZATION - bool "Virtualization" - default y + def_bool y + prompt "Virtualization" ---help--- Say Y here to get to see options for using your Linux host to run other operating systems inside virtual machines (guests). @@ -16,7 +16,8 @@ menuconfig VIRTUALIZATION if VIRTUALIZATION config KVM - tristate "Kernel-based Virtual Machine (KVM) support" + def_tristate y + prompt "Kernel-based Virtual Machine (KVM) support" depends on HAVE_KVM && EXPERIMENTAL select PREEMPT_NOTIFIERS select ANON_INODES diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig index 07883197f474..8e477bb1f3f6 100644 --- a/drivers/s390/block/Kconfig +++ b/drivers/s390/block/Kconfig @@ -2,7 +2,8 @@ comment "S/390 block device drivers" depends on S390 && BLOCK config BLK_DEV_XPRAM - tristate "XPRAM disk support" + def_tristate m + prompt "XPRAM disk support" depends on S390 && BLOCK help Select this option if you want to use your expanded storage on S/390 @@ -12,13 +13,15 @@ config BLK_DEV_XPRAM xpram. If unsure, say "N". config DCSSBLK - tristate "DCSSBLK support" + def_tristate m + prompt "DCSSBLK support" depends on S390 && BLOCK help Support for dcss block device config DASD - tristate "Support for DASD devices" + def_tristate y + prompt "Support for DASD devices" depends on CCW && BLOCK select IOSCHED_DEADLINE help @@ -27,28 +30,32 @@ config DASD natively on a single image or an LPAR. config DASD_PROFILE - bool "Profiling support for dasd devices" + def_bool y + prompt "Profiling support for dasd devices" depends on DASD help Enable this option if you want to see profiling information in /proc/dasd/statistics. config DASD_ECKD - tristate "Support for ECKD Disks" + def_tristate y + prompt "Support for ECKD Disks" depends on DASD help ECKD devices are the most commonly used devices. You should enable this option unless you are very sure to have no ECKD device. config DASD_FBA - tristate "Support for FBA Disks" + def_tristate y + prompt "Support for FBA Disks" depends on DASD help Select this option to be able to access FBA devices. It is safe to say "Y". config DASD_DIAG - tristate "Support for DIAG access to Disks" + def_tristate y + prompt "Support for DIAG access to Disks" depends on DASD help Select this option if you want to use Diagnose250 command to access @@ -56,7 +63,8 @@ config DASD_DIAG say "N". config DASD_EER - bool "Extended error reporting (EER)" + def_bool y + prompt "Extended error reporting (EER)" depends on DASD help This driver provides a character device interface to the diff --git a/drivers/s390/char/Kconfig b/drivers/s390/char/Kconfig index 40834f18754c..dcee3c5c8954 100644 --- a/drivers/s390/char/Kconfig +++ b/drivers/s390/char/Kconfig @@ -2,76 +2,85 @@ comment "S/390 character device drivers" depends on S390 config TN3270 - tristate "Support for locally attached 3270 terminals" + def_tristate y + prompt "Support for locally attached 3270 terminals" depends on CCW help Include support for IBM 3270 terminals. config TN3270_TTY - tristate "Support for tty input/output on 3270 terminals" + def_tristate y + prompt "Support for tty input/output on 3270 terminals" depends on TN3270 help Include support for using an IBM 3270 terminal as a Linux tty. config TN3270_FS - tristate "Support for fullscreen applications on 3270 terminals" + def_tristate m + prompt "Support for fullscreen applications on 3270 terminals" depends on TN3270 help Include support for fullscreen applications on an IBM 3270 terminal. config TN3270_CONSOLE - bool "Support for console on 3270 terminal" + def_bool y + prompt "Support for console on 3270 terminal" depends on TN3270=y && TN3270_TTY=y help Include support for using an IBM 3270 terminal as a Linux system console. Available only if 3270 support is compiled in statically. config TN3215 - bool "Support for 3215 line mode terminal" + def_bool y + prompt "Support for 3215 line mode terminal" depends on CCW help Include support for IBM 3215 line-mode terminals. config TN3215_CONSOLE - bool "Support for console on 3215 line mode terminal" + def_bool y + prompt "Support for console on 3215 line mode terminal" depends on TN3215 help Include support for using an IBM 3215 line-mode terminal as a Linux system console. config CCW_CONSOLE - bool - depends on TN3215_CONSOLE || TN3270_CONSOLE - default y + def_bool y if TN3215_CONSOLE || TN3270_CONSOLE config SCLP_TTY - bool "Support for SCLP line mode terminal" + def_bool y + prompt "Support for SCLP line mode terminal" depends on S390 help Include support for IBM SCLP line-mode terminals. config SCLP_CONSOLE - bool "Support for console on SCLP line mode terminal" + def_bool y + prompt "Support for console on SCLP line mode terminal" depends on SCLP_TTY help Include support for using an IBM HWC line-mode terminal as the Linux system console. config SCLP_VT220_TTY - bool "Support for SCLP VT220-compatible terminal" + def_bool y + prompt "Support for SCLP VT220-compatible terminal" depends on S390 help Include support for an IBM SCLP VT220-compatible terminal. config SCLP_VT220_CONSOLE - bool "Support for console on SCLP VT220-compatible terminal" + def_bool y + prompt "Support for console on SCLP VT220-compatible terminal" depends on SCLP_VT220_TTY help Include support for using an IBM SCLP VT220-compatible terminal as a Linux system console. config SCLP_CPI - tristate "Control-Program Identification" + def_tristate m + prompt "Control-Program Identification" depends on S390 help This option enables the hardware console interface for system @@ -83,7 +92,8 @@ config SCLP_CPI need this feature and intend to run your kernel in LPAR. config SCLP_ASYNC - tristate "Support for Call Home via Asynchronous SCLP Records" + def_tristate m + prompt "Support for Call Home via Asynchronous SCLP Records" depends on S390 help This option enables the call home function, which is able to inform @@ -93,7 +103,8 @@ config SCLP_ASYNC need this feature and intend to run your kernel in LPAR. config S390_TAPE - tristate "S/390 tape device support" + def_tristate m + prompt "S/390 tape device support" depends on CCW help Select this option if you want to access channel-attached tape @@ -109,7 +120,8 @@ comment "S/390 tape interface support" depends on S390_TAPE config S390_TAPE_BLOCK - bool "Support for tape block devices" + def_bool y + prompt "Support for tape block devices" depends on S390_TAPE && BLOCK help Select this option if you want to access your channel-attached tape @@ -123,7 +135,8 @@ comment "S/390 tape hardware support" depends on S390_TAPE config S390_TAPE_34XX - tristate "Support for 3480/3490 tape hardware" + def_tristate m + prompt "Support for 3480/3490 tape hardware" depends on S390_TAPE help Select this option if you want to access IBM 3480/3490 magnetic @@ -131,7 +144,8 @@ config S390_TAPE_34XX It is safe to say "Y" here. config S390_TAPE_3590 - tristate "Support for 3590 tape hardware" + def_tristate m + prompt "Support for 3590 tape hardware" depends on S390_TAPE help Select this option if you want to access IBM 3590 magnetic @@ -139,7 +153,8 @@ config S390_TAPE_3590 It is safe to say "Y" here. config VMLOGRDR - tristate "Support for the z/VM recording system services (VM only)" + def_tristate m + prompt "Support for the z/VM recording system services (VM only)" depends on IUCV help Select this option if you want to be able to receive records collected @@ -148,29 +163,31 @@ config VMLOGRDR This driver depends on the IUCV support driver. config VMCP - bool "Support for the z/VM CP interface" + def_bool y + prompt "Support for the z/VM CP interface" depends on S390 help Select this option if you want to be able to interact with the control program on z/VM config MONREADER - tristate "API for reading z/VM monitor service records" + def_tristate m + prompt "API for reading z/VM monitor service records" depends on IUCV help Character device driver for reading z/VM monitor service records config MONWRITER - tristate "API for writing z/VM monitor service records" + def_tristate m + prompt "API for writing z/VM monitor service records" depends on S390 - default "m" help Character device driver for writing z/VM monitor service records config S390_VMUR - tristate "z/VM unit record device driver" + def_tristate m + prompt "z/VM unit record device driver" depends on S390 - default "m" help Character device driver for z/VM reader, puncher and printer. diff --git a/drivers/s390/net/Kconfig b/drivers/s390/net/Kconfig index 456b18743397..fa80ba1f0344 100644 --- a/drivers/s390/net/Kconfig +++ b/drivers/s390/net/Kconfig @@ -2,7 +2,8 @@ menu "S/390 network device drivers" depends on NETDEVICES && S390 config LCS - tristate "Lan Channel Station Interface" + def_tristate m + prompt "Lan Channel Station Interface" depends on CCW && NETDEVICES && (NET_ETHERNET || TR || FDDI) help Select this option if you want to use LCS networking on IBM System z. @@ -12,7 +13,8 @@ config LCS If you do not know what it is, it's safe to choose Y. config CTCM - tristate "CTC and MPC SNA device support" + def_tristate m + prompt "CTC and MPC SNA device support" depends on CCW && NETDEVICES help Select this option if you want to use channel-to-channel @@ -26,7 +28,8 @@ config CTCM If you do not need any channel-to-channel connection, choose N. config NETIUCV - tristate "IUCV network device support (VM only)" + def_tristate m + prompt "IUCV network device support (VM only)" depends on IUCV && NETDEVICES help Select this option if you want to use inter-user communication @@ -37,14 +40,16 @@ config NETIUCV The module name is netiucv. If unsure, choose Y. config SMSGIUCV - tristate "IUCV special message support (VM only)" + def_tristate m + prompt "IUCV special message support (VM only)" depends on IUCV help Select this option if you want to be able to receive SMSG messages from other VM guest systems. config SMSGIUCV_EVENT - tristate "Deliver IUCV special messages as uevents (VM only)" + def_tristate m + prompt "Deliver IUCV special messages as uevents (VM only)" depends on SMSGIUCV help Select this option to deliver CP special messages (SMSGs) as @@ -54,7 +59,8 @@ config SMSGIUCV_EVENT To compile as a module, choose M. The module name is "smsgiucv_app". config CLAW - tristate "CLAW device support" + def_tristate m + prompt "CLAW device support" depends on CCW && NETDEVICES help This driver supports channel attached CLAW devices. @@ -64,7 +70,8 @@ config CLAW To compile into the kernel, choose Y. config QETH - tristate "Gigabit Ethernet device support" + def_tristate y + prompt "Gigabit Ethernet device support" depends on CCW && NETDEVICES && IP_MULTICAST && QDIO help This driver supports the IBM System z OSA Express adapters @@ -78,25 +85,25 @@ config QETH The module name is qeth. config QETH_L2 - tristate "qeth layer 2 device support" - depends on QETH - help - Select this option to be able to run qeth devices in layer 2 mode. - To compile as a module, choose M. The module name is qeth_l2. - If unsure, choose y. + def_tristate y + prompt "qeth layer 2 device support" + depends on QETH + help + Select this option to be able to run qeth devices in layer 2 mode. + To compile as a module, choose M. The module name is qeth_l2. + If unsure, choose y. config QETH_L3 - tristate "qeth layer 3 device support" - depends on QETH - help - Select this option to be able to run qeth devices in layer 3 mode. - To compile as a module choose M. The module name is qeth_l3. - If unsure, choose Y. + def_tristate y + prompt "qeth layer 3 device support" + depends on QETH + help + Select this option to be able to run qeth devices in layer 3 mode. + To compile as a module choose M. The module name is qeth_l3. + If unsure, choose Y. config QETH_IPV6 - bool - depends on (QETH_L3 = IPV6) || (QETH_L3 && IPV6 = 'y') - default y + def_bool y if (QETH_L3 = IPV6) || (QETH_L3 && IPV6 = 'y') config CCWGROUP tristate -- cgit v1.2.3 From 052ff461c8427629aee887ccc27478fc7373237c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:28 +0100 Subject: [S390] irq: have detailed statistics for interrupt types Up to now /proc/interrupts only has statistics for external and i/o interrupts but doesn't split up them any further. This patch adds a line for every single interrupt source so that it is possible to easier tell what the machine is/was doing. Part of the output now looks like this; CPU0 CPU2 CPU4 EXT: 3898 4232 2305 I/O: 782 315 245 CLK: 1029 1964 727 [EXT] Clock Comparator IPI: 2868 2267 1577 [EXT] Signal Processor TMR: 0 0 0 [EXT] CPU Timer TAL: 0 0 0 [EXT] Timing Alert PFL: 0 0 0 [EXT] Pseudo Page Fault [...] NMI: 0 1 1 [NMI] Machine Checks Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 23 +++++++++++------------ arch/s390/kernel/irq.c | 30 ++++++++++++++++++++++++------ arch/s390/kernel/nmi.c | 3 ++- arch/s390/kernel/smp.c | 1 + arch/s390/kernel/time.c | 3 +++ arch/s390/kernel/vtime.c | 1 + arch/s390/mm/fault.c | 2 ++ drivers/s390/block/dasd_diag.c | 2 ++ drivers/s390/char/sclp.c | 6 +++++- drivers/s390/kvm/kvm_virtio.c | 3 +++ net/iucv/iucv.c | 2 ++ 11 files changed, 56 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 7da991a858f8..f65faf63ab3a 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -1,23 +1,22 @@ #ifndef _ASM_IRQ_H #define _ASM_IRQ_H -#ifdef __KERNEL__ #include -/* - * the definition of irqs has changed in 2.5.46: - * NR_IRQS is no longer the number of i/o - * interrupts (65536), but rather the number - * of interrupt classes (2). - * Only external and i/o interrupts make much sense here (CH). - */ - enum interruption_class { EXTERNAL_INTERRUPT, IO_INTERRUPT, - + EXTINT_CLK, + EXTINT_IPI, + EXTINT_TMR, + EXTINT_TLA, + EXTINT_PFL, + EXTINT_DSD, + EXTINT_VRT, + EXTINT_SCP, + EXTINT_IUC, + NMI_NMI, NR_IRQS, }; -#endif /* __KERNEL__ */ -#endif +#endif /* _ASM_IRQ_H */ diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 026a37a94fc9..9bd049b8f997 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -1,7 +1,5 @@ /* - * arch/s390/kernel/irq.c - * - * Copyright IBM Corp. 2004,2007 + * Copyright IBM Corp. 2004,2010 * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com), * Thomas Spatzier (tspat@de.ibm.com) * @@ -17,12 +15,31 @@ #include #include +struct irq_class { + char *name; + char *desc; +}; + +static const struct irq_class intrclass_names[] = { + {.name = "EXT" }, + {.name = "I/O" }, + {.name = "CLK", .desc = "[EXT] Clock Comparator" }, + {.name = "IPI", .desc = "[EXT] Signal Processor" }, + {.name = "TMR", .desc = "[EXT] CPU Timer" }, + {.name = "TAL", .desc = "[EXT] Timing Alert" }, + {.name = "PFL", .desc = "[EXT] Pseudo Page Fault" }, + {.name = "DSD", .desc = "[EXT] DASD Diag" }, + {.name = "VRT", .desc = "[EXT] Virtio" }, + {.name = "SCP", .desc = "[EXT] Service Call" }, + {.name = "IUC", .desc = "[EXT] IUCV" }, + {.name = "NMI", .desc = "[NMI] Machine Check" }, +}; + /* * show_interrupts is needed by /proc/interrupts. */ int show_interrupts(struct seq_file *p, void *v) { - static const char *intrclass_names[] = { "EXT", "I/O", }; int i = *(loff_t *) v, j; get_online_cpus(); @@ -34,15 +51,16 @@ int show_interrupts(struct seq_file *p, void *v) } if (i < NR_IRQS) { - seq_printf(p, "%s: ", intrclass_names[i]); + seq_printf(p, "%s: ", intrclass_names[i].name); #ifndef CONFIG_SMP seq_printf(p, "%10u ", kstat_irqs(i)); #else for_each_online_cpu(j) seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]); #endif + if (intrclass_names[i].desc) + seq_printf(p, " %s", intrclass_names[i].desc); seq_putc(p, '\n'); - } put_online_cpus(); return 0; diff --git a/arch/s390/kernel/nmi.c b/arch/s390/kernel/nmi.c index 1995c1712fc8..fab88431a06f 100644 --- a/arch/s390/kernel/nmi.c +++ b/arch/s390/kernel/nmi.c @@ -8,6 +8,7 @@ * Heiko Carstens , */ +#include #include #include #include @@ -255,7 +256,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs) nmi_enter(); s390_idle_check(regs, S390_lowcore.mcck_clock, S390_lowcore.mcck_enter_timer); - + kstat_cpu(smp_processor_id()).irqs[NMI_NMI]++; mci = (struct mci *) &S390_lowcore.mcck_interruption_code; mcck = &__get_cpu_var(cpu_mcck); umode = user_mode(regs); diff --git a/arch/s390/kernel/smp.c b/arch/s390/kernel/smp.c index 94cf510b8fe1..a9702df22f3a 100644 --- a/arch/s390/kernel/smp.c +++ b/arch/s390/kernel/smp.c @@ -161,6 +161,7 @@ static void do_ext_call_interrupt(unsigned int ext_int_code, { unsigned long bits; + kstat_cpu(smp_processor_id()).irqs[EXTINT_IPI]++; /* * handle bit signal external calls * diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 4c9d72d2e273..9e7b039458da 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -15,6 +15,7 @@ #define KMSG_COMPONENT "time" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include @@ -160,6 +161,7 @@ static void clock_comparator_interrupt(unsigned int ext_int_code, unsigned int param32, unsigned long param64) { + kstat_cpu(smp_processor_id()).irqs[EXTINT_CLK]++; if (S390_lowcore.clock_comparator == -1ULL) set_clock_comparator(S390_lowcore.clock_comparator); } @@ -170,6 +172,7 @@ static void stp_timing_alert(struct stp_irq_parm *); static void timing_alert_interrupt(unsigned int ext_int_code, unsigned int param32, unsigned long param64) { + kstat_cpu(smp_processor_id()).irqs[EXTINT_TLA]++; if (param32 & 0x00c40000) etr_timing_alert((struct etr_irq_parm *) ¶m32); if (param32 & 0x00038000) diff --git a/arch/s390/kernel/vtime.c b/arch/s390/kernel/vtime.c index 8636dd00e393..1ccdf4d8aa85 100644 --- a/arch/s390/kernel/vtime.c +++ b/arch/s390/kernel/vtime.c @@ -324,6 +324,7 @@ static void do_cpu_timer_interrupt(unsigned int ext_int_code, struct list_head cb_list; /* the callback queue */ __u64 elapsed, next; + kstat_cpu(smp_processor_id()).irqs[EXTINT_TMR]++; INIT_LIST_HEAD(&cb_list); vq = &__get_cpu_var(virt_cpu_timer); diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index fe5701e9efbf..839b16df72b3 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -10,6 +10,7 @@ * Copyright (C) 1995 Linus Torvalds */ +#include #include #include #include @@ -543,6 +544,7 @@ static void pfault_interrupt(unsigned int ext_int_code, struct task_struct *tsk; __u16 subcode; + kstat_cpu(smp_processor_id()).irqs[EXTINT_PFL]++; /* * Get the external interruption subcode & pfault * initial/completion signal bit. VM stores this diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index 266b34b55403..a3a5db58df18 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -10,6 +10,7 @@ #define KMSG_COMPONENT "dasd" +#include #include #include #include @@ -238,6 +239,7 @@ static void dasd_ext_handler(unsigned int ext_int_code, addr_t ip; int rc; + kstat_cpu(smp_processor_id()).irqs[EXTINT_DSD]++; switch (ext_int_code >> 24) { case DASD_DIAG_CODE_31BIT: ip = (addr_t) param32; diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index 35cc4686b99b..e65572e504ba 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -7,6 +7,7 @@ * Martin Schwidefsky */ +#include #include #include #include @@ -18,8 +19,9 @@ #include #include #include -#include #include +#include +#include #include "sclp.h" @@ -402,6 +404,7 @@ static void sclp_interrupt_handler(unsigned int ext_int_code, u32 finished_sccb; u32 evbuf_pending; + kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++; spin_lock(&sclp_lock); finished_sccb = param32 & 0xfffffff8; evbuf_pending = param32 & 0x3; @@ -824,6 +827,7 @@ static void sclp_check_handler(unsigned int ext_int_code, { u32 finished_sccb; + kstat_cpu(smp_processor_id()).irqs[EXTINT_SCP]++; finished_sccb = param32 & 0xfffffff8; /* Is this the interrupt we are waiting for? */ if (finished_sccb == 0) diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index 375aeeaf9ea5..414427d64a8f 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -10,6 +10,7 @@ * Author(s): Christian Borntraeger */ +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #define VIRTIO_SUBCODE_64 0x0D00 @@ -379,6 +381,7 @@ static void kvm_extint_handler(unsigned int ext_int_code, u16 subcode; u32 param; + kstat_cpu(smp_processor_id()).irqs[EXTINT_VRT]++; subcode = ext_int_code >> 16; if ((subcode & 0xff00) != VIRTIO_SUBCODE_64) return; diff --git a/net/iucv/iucv.c b/net/iucv/iucv.c index f7db676de77d..1ee5dab3cfae 100644 --- a/net/iucv/iucv.c +++ b/net/iucv/iucv.c @@ -36,6 +36,7 @@ #define KMSG_COMPONENT "iucv" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include @@ -1804,6 +1805,7 @@ static void iucv_external_interrupt(unsigned int ext_int_code, struct iucv_irq_data *p; struct iucv_irq_list *work; + kstat_cpu(smp_processor_id()).irqs[EXTINT_IUC]++; p = iucv_irq_data[smp_processor_id()]; if (p->ippathid >= iucv_max_pathid) { WARN_ON(p->ippathid >= iucv_max_pathid); -- cgit v1.2.3 From 30d77c3e1cbdff304b16ae02cb56baaa308e42fd Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:29 +0100 Subject: [S390] qdio: add qdio interrupts to interrupt statistics Count traditional qdio interrupts and adapter interrupts for qdio in the interrupt statistics. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 2 ++ arch/s390/kernel/irq.c | 2 ++ drivers/s390/cio/qdio_main.c | 2 ++ drivers/s390/cio/qdio_thinint.c | 2 ++ 4 files changed, 8 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index f65faf63ab3a..28cfe5905a00 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -15,6 +15,8 @@ enum interruption_class { EXTINT_VRT, EXTINT_SCP, EXTINT_IUC, + IOINT_QAI, + IOINT_QDI, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 9bd049b8f997..61d8098aa0b6 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -32,6 +32,8 @@ static const struct irq_class intrclass_names[] = { {.name = "VRT", .desc = "[EXT] Virtio" }, {.name = "SCP", .desc = "[EXT] Service Call" }, {.name = "IUC", .desc = "[EXT] IUCV" }, + {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" }, + {.name = "QDI", .desc = "[I/O] QDIO Interrupt" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 5fcfa7f9e9ef..194ea8c182b2 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -970,6 +971,7 @@ void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, return; } + kstat_cpu(smp_processor_id()).irqs[IOINT_QDI]++; if (irq_ptr->perf_stat_enabled) irq_ptr->perf_stat.qdio_int++; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 5d9c66627b6e..64b59a58a1cd 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -8,6 +8,7 @@ */ #include #include +#include #include #include #include @@ -127,6 +128,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data) struct qdio_q *q; last_ai_time = S390_lowcore.int_clock; + kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++; /* * SVS only when needed: issue SVS to benefit from iqdio interrupt -- cgit v1.2.3 From 3283942b71eb5023184b378230f5f0e3fbb40991 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:30 +0100 Subject: [S390] dasd: add support for irq statistics Add support for DASD I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/block/dasd.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 28cfe5905a00..082ef0cce287 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -17,6 +17,7 @@ enum interruption_class { EXTINT_IUC, IOINT_QAI, IOINT_QDI, + IOINT_DAS, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 61d8098aa0b6..313fe83a443a 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -34,6 +34,7 @@ static const struct irq_class intrclass_names[] = { {.name = "IUC", .desc = "[EXT] IUCV" }, {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" }, {.name = "QDI", .desc = "[I/O] QDIO Interrupt" }, + {.name = "DAS", .desc = "[I/O] DASD" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index fb613d70c2cb..faa7d425cb9c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -11,6 +11,7 @@ #define KMSG_COMPONENT "dasd" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include @@ -1076,6 +1077,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, unsigned long long now; int expires; + kstat_cpu(smp_processor_id()).irqs[IOINT_DAS]++; if (IS_ERR(irb)) { switch (PTR_ERR(irb)) { case -EIO: -- cgit v1.2.3 From 12fae5858cd97181c92472c9bb5f098a7eca2ffe Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:31 +0100 Subject: [S390] 3215: add support for irq statistics Add support for 3215 I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/char/con3215.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 082ef0cce287..d52533df6ac8 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -18,6 +18,7 @@ enum interruption_class { IOINT_QAI, IOINT_QDI, IOINT_DAS, + IOINT_C15, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 313fe83a443a..e7914e4adc48 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -35,6 +35,7 @@ static const struct irq_class intrclass_names[] = { {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" }, {.name = "QDI", .desc = "[I/O] QDIO Interrupt" }, {.name = "DAS", .desc = "[I/O] DASD" }, + {.name = "C15", .desc = "[I/O] 3215" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 59ec073724bf..3fb4335d491d 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -9,6 +9,7 @@ * Dan Morrison, IBM Corporation */ +#include #include #include #include @@ -361,6 +362,7 @@ static void raw3215_irq(struct ccw_device *cdev, unsigned long intparm, int cstat, dstat; int count; + kstat_cpu(smp_processor_id()).irqs[IOINT_C15]++; raw = dev_get_drvdata(&cdev->dev); req = (struct raw3215_req *) intparm; cstat = irb->scsw.cmd.cstat; -- cgit v1.2.3 From 3fe22f6bfd6f81aafd140d69578d3a2c39674664 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:32 +0100 Subject: [S390] 3270: add support for irq statistics Add support for 3270 I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/char/raw3270.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index d52533df6ac8..65e63c02b0f9 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -19,6 +19,7 @@ enum interruption_class { IOINT_QDI, IOINT_DAS, IOINT_C15, + IOINT_C70, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index e7914e4adc48..8c241416576f 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -36,6 +36,7 @@ static const struct irq_class intrclass_names[] = { {.name = "QDI", .desc = "[I/O] QDIO Interrupt" }, {.name = "DAS", .desc = "[I/O] DASD" }, {.name = "C15", .desc = "[I/O] 3215" }, + {.name = "C70", .desc = "[I/O] 3270" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c index 2a4c566456e7..96ba2fd1c8ad 100644 --- a/drivers/s390/char/raw3270.c +++ b/drivers/s390/char/raw3270.c @@ -7,6 +7,7 @@ * Copyright IBM Corp. 2003, 2009 */ +#include #include #include #include @@ -329,6 +330,7 @@ raw3270_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct raw3270_request *rq; int rc; + kstat_cpu(smp_processor_id()).irqs[IOINT_C70]++; rp = dev_get_drvdata(&cdev->dev); if (!rp) return; -- cgit v1.2.3 From b86651721f18f40319efe94ed3eac2d26682e5b9 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:33 +0100 Subject: [S390] tape: add support for irq statistics Add support for ccw based tape I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/char/tape_core.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 65e63c02b0f9..6986343a566f 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -20,6 +20,7 @@ enum interruption_class { IOINT_DAS, IOINT_C15, IOINT_C70, + IOINT_TAP, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 8c241416576f..5d7b6fbb3c13 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -37,6 +37,7 @@ static const struct irq_class intrclass_names[] = { {.name = "DAS", .desc = "[I/O] DASD" }, {.name = "C15", .desc = "[I/O] 3215" }, {.name = "C70", .desc = "[I/O] 3270" }, + {.name = "TAP", .desc = "[I/O] Tape" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/char/tape_core.c b/drivers/s390/char/tape_core.c index b3a3e8e8656e..7978a0adeaf3 100644 --- a/drivers/s390/char/tape_core.c +++ b/drivers/s390/char/tape_core.c @@ -14,6 +14,7 @@ #define KMSG_COMPONENT "tape" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include // for kernel parameters #include // for requesting modules @@ -1114,6 +1115,7 @@ __tape_do_irq (struct ccw_device *cdev, unsigned long intparm, struct irb *irb) struct tape_request *request; int rc; + kstat_cpu(smp_processor_id()).irqs[IOINT_TAP]++; device = dev_get_drvdata(&cdev->dev); if (device == NULL) { return; -- cgit v1.2.3 From f48198d592b0d680b9677bd69edd2290cd0c1f4f Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:34 +0100 Subject: [S390] vmur: add support for irq statistics Add support for VMUR I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/char/vmur.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 6986343a566f..6c9b55681eab 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -21,6 +21,7 @@ enum interruption_class { IOINT_C15, IOINT_C70, IOINT_TAP, + IOINT_VMR, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 5d7b6fbb3c13..ae726d1ae430 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -38,6 +38,7 @@ static const struct irq_class intrclass_names[] = { {.name = "C15", .desc = "[I/O] 3215" }, {.name = "C70", .desc = "[I/O] 3270" }, {.name = "TAP", .desc = "[I/O] Tape" }, + {.name = "VMR", .desc = "[I/O] Unit Record Devices" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/char/vmur.c b/drivers/s390/char/vmur.c index f7e4ae6bf15a..caef1757341d 100644 --- a/drivers/s390/char/vmur.c +++ b/drivers/s390/char/vmur.c @@ -11,6 +11,7 @@ #define KMSG_COMPONENT "vmur" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include @@ -302,6 +303,7 @@ static void ur_int_handler(struct ccw_device *cdev, unsigned long intparm, { struct urdev *urd; + kstat_cpu(smp_processor_id()).irqs[IOINT_VMR]++; TRACE("ur_int_handler: intparm=0x%lx cstat=%02x dstat=%02x res=%u\n", intparm, irb->scsw.cmd.cstat, irb->scsw.cmd.dstat, irb->scsw.cmd.count); -- cgit v1.2.3 From 096a61682e86090e4e74118ff6fa6858ca73aa58 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:35 +0100 Subject: [S390] lcs: add support for irq statistics Add support for LCS I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/net/lcs.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 6c9b55681eab..6a455b8ea18b 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -22,6 +22,7 @@ enum interruption_class { IOINT_C70, IOINT_TAP, IOINT_VMR, + IOINT_LCS, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index ae726d1ae430..4c6e071b7f92 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -39,6 +39,7 @@ static const struct irq_class intrclass_names[] = { {.name = "C70", .desc = "[I/O] 3270" }, {.name = "TAP", .desc = "[I/O] Tape" }, {.name = "VMR", .desc = "[I/O] Unit Record Devices" }, + {.name = "LCS", .desc = "[I/O] LCS" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c index 0f19d540b655..0bf708944314 100644 --- a/drivers/s390/net/lcs.c +++ b/drivers/s390/net/lcs.c @@ -26,6 +26,7 @@ #define KMSG_COMPONENT "lcs" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include @@ -1396,6 +1397,7 @@ lcs_irq(struct ccw_device *cdev, unsigned long intparm, struct irb *irb) int rc, index; int cstat, dstat; + kstat_cpu(smp_processor_id()).irqs[IOINT_LCS]++; if (lcs_check_irb_error(cdev, irb)) return; -- cgit v1.2.3 From 355eb4022b92349f70cd69ce5b9572c71c0be226 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:36 +0100 Subject: [S390] claw: add support for irq statistics Add support for CLAW I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/net/claw.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 6a455b8ea18b..2d5943c1c6a1 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -23,6 +23,7 @@ enum interruption_class { IOINT_TAP, IOINT_VMR, IOINT_LCS, + IOINT_CLW, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 4c6e071b7f92..406b539f61a9 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -40,6 +40,7 @@ static const struct irq_class intrclass_names[] = { {.name = "TAP", .desc = "[I/O] Tape" }, {.name = "VMR", .desc = "[I/O] Unit Record Devices" }, {.name = "LCS", .desc = "[I/O] LCS" }, + {.name = "CLW", .desc = "[I/O] CLAW" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/net/claw.c b/drivers/s390/net/claw.c index 8e4153d740f3..ce3a5c13ce0b 100644 --- a/drivers/s390/net/claw.c +++ b/drivers/s390/net/claw.c @@ -63,6 +63,7 @@ #define KMSG_COMPONENT "claw" +#include #include #include #include @@ -640,6 +641,7 @@ claw_irq_handler(struct ccw_device *cdev, struct claw_env *p_env; struct chbk *p_ch_r=NULL; + kstat_cpu(smp_processor_id()).irqs[IOINT_CLW]++; CLAW_DBF_TEXT(4, trace, "clawirq"); /* Bypass all 'unsolicited interrupts' */ privptr = dev_get_drvdata(&cdev->dev); -- cgit v1.2.3 From 85b81cdd0b038d580dedf6289df7de65826967d6 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:37 +0100 Subject: [S390] ctc: add support for irq statistics Add support for CTC I/O interrupt statistics in /proc/interrupts. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/net/ctcm_main.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 2d5943c1c6a1..8c79f9400a36 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -24,6 +24,7 @@ enum interruption_class { IOINT_VMR, IOINT_LCS, IOINT_CLW, + IOINT_CTC, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 406b539f61a9..57ed2b55c2a1 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -41,6 +41,7 @@ static const struct irq_class intrclass_names[] = { {.name = "VMR", .desc = "[I/O] Unit Record Devices" }, {.name = "LCS", .desc = "[I/O] LCS" }, {.name = "CLW", .desc = "[I/O] CLAW" }, + {.name = "CTC", .desc = "[I/O] CTC" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/net/ctcm_main.c b/drivers/s390/net/ctcm_main.c index 2c7d2d9be4d0..4c2845985927 100644 --- a/drivers/s390/net/ctcm_main.c +++ b/drivers/s390/net/ctcm_main.c @@ -24,6 +24,7 @@ #define KMSG_COMPONENT "ctcm" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include @@ -1204,6 +1205,7 @@ static void ctcm_irq_handler(struct ccw_device *cdev, int cstat; int dstat; + kstat_cpu(smp_processor_id()).irqs[IOINT_CTC]++; CTCM_DBF_TEXT_(TRACE, CTC_DBF_DEBUG, "Enter %s(%s)", CTCM_FUNTAIL, dev_name(&cdev->dev)); -- cgit v1.2.3 From 62d146ffe3adfed2747fc36138476c8417ce73a7 Mon Sep 17 00:00:00 2001 From: Holger Dengler Date: Wed, 5 Jan 2011 12:47:38 +0100 Subject: [S390] ap bus: add support for irq statistics Add support for AP Bus I/O interrupt statistics in /proc/interrupts. Signed-off-by: Holger Dengler Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/irq.h | 1 + arch/s390/kernel/irq.c | 1 + drivers/s390/crypto/ap_bus.c | 2 ++ 3 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/irq.h b/arch/s390/include/asm/irq.h index 8c79f9400a36..db14a311f1d2 100644 --- a/arch/s390/include/asm/irq.h +++ b/arch/s390/include/asm/irq.h @@ -25,6 +25,7 @@ enum interruption_class { IOINT_LCS, IOINT_CLW, IOINT_CTC, + IOINT_APB, NMI_NMI, NR_IRQS, }; diff --git a/arch/s390/kernel/irq.c b/arch/s390/kernel/irq.c index 57ed2b55c2a1..ea5099c9709c 100644 --- a/arch/s390/kernel/irq.c +++ b/arch/s390/kernel/irq.c @@ -42,6 +42,7 @@ static const struct irq_class intrclass_names[] = { {.name = "LCS", .desc = "[I/O] LCS" }, {.name = "CLW", .desc = "[I/O] CLAW" }, {.name = "CTC", .desc = "[I/O] CTC" }, + {.name = "APB", .desc = "[I/O] AP Bus" }, {.name = "NMI", .desc = "[NMI] Machine Check" }, }; diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index a1ba52a09602..4f37c45ee114 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -27,6 +27,7 @@ #define KMSG_COMPONENT "ap" #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt +#include #include #include #include @@ -1042,6 +1043,7 @@ out: static void ap_interrupt_handler(void *unused1, void *unused2) { + kstat_cpu(smp_processor_id()).irqs[IOINT_APB]++; tasklet_schedule(&ap_tasklet); } -- cgit v1.2.3 From 98b799800c3e2f855ef2d2c6263e84fa5d1420a0 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:47:40 +0100 Subject: [S390] sclp: use register_external_interrupt() Use register_external_interrupt() instead of register_early_external_interrupt(). The early variant is not necessary since kmalloc works already. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- drivers/s390/char/sclp.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c index e65572e504ba..b76c61f82485 100644 --- a/drivers/s390/char/sclp.c +++ b/drivers/s390/char/sclp.c @@ -27,9 +27,6 @@ #define SCLP_HEADER "sclp: " -/* Structure for register_early_external_interrupt. */ -static ext_int_info_t ext_int_info_hwc; - /* Lock to protect internal data consistency. */ static DEFINE_SPINLOCK(sclp_lock); @@ -870,8 +867,7 @@ sclp_check_interface(void) spin_lock_irqsave(&sclp_lock, flags); /* Prepare init mask command */ - rc = register_early_external_interrupt(0x2401, sclp_check_handler, - &ext_int_info_hwc); + rc = register_external_interrupt(0x2401, sclp_check_handler); if (rc) { spin_unlock_irqrestore(&sclp_lock, flags); return rc; @@ -904,8 +900,7 @@ sclp_check_interface(void) } else rc = -EBUSY; } - unregister_early_external_interrupt(0x2401, sclp_check_handler, - &ext_int_info_hwc); + unregister_external_interrupt(0x2401, sclp_check_handler); spin_unlock_irqrestore(&sclp_lock, flags); return rc; } @@ -1068,8 +1063,7 @@ sclp_init(void) if (rc) goto fail_init_state_uninitialized; /* Register interrupt handler */ - rc = register_early_external_interrupt(0x2401, sclp_interrupt_handler, - &ext_int_info_hwc); + rc = register_external_interrupt(0x2401, sclp_interrupt_handler); if (rc) goto fail_unregister_reboot_notifier; sclp_init_state = sclp_init_state_initialized; -- cgit v1.2.3 From b1f933da570576d1f290ea4dc9b896404cbd285d Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Wed, 5 Jan 2011 12:47:44 +0100 Subject: [S390] zcrypt: Introduce check for 4096 bit support. Implemented an asm in the ap bus and made it accessible for the card specific parts of the zcrypt driver. Thus when a cex3a is recognized a check can be performed to dermine whether the card supports 4096 bit RSA keys. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/ap_bus.c | 63 ++++++++++++++++++++++++++++++++++++++++++++ drivers/s390/crypto/ap_bus.h | 2 ++ 2 files changed, 65 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 4f37c45ee114..67302b944ab3 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -222,6 +222,69 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind) } #endif +static inline struct ap_queue_status __ap_4096_commands_available(ap_qid_t qid, + int *support) +{ + register unsigned long reg0 asm ("0") = 0UL | qid | (1UL << 23); + register struct ap_queue_status reg1 asm ("1"); + register unsigned long reg2 asm ("2") = 0UL; + + asm volatile( + ".long 0xb2af0000\n" + "0: la %1,0\n" + "1:\n" + EX_TABLE(0b, 1b) + : "+d" (reg0), "=d" (reg1), "=d" (reg2) + : + : "cc"); + + if (reg2 & 0x6000000000000000ULL) + *support = 1; + else + *support = 0; + + return reg1; +} + +/** + * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA + * support. + * @qid: The AP queue number + * + * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not. + */ +int ap_4096_commands_available(ap_qid_t qid) +{ + struct ap_queue_status status; + int i, support = 0; + status = __ap_4096_commands_available(qid, &support); + + for (i = 0; i < AP_MAX_RESET; i++) { + switch (status.response_code) { + case AP_RESPONSE_NORMAL: + return support; + case AP_RESPONSE_RESET_IN_PROGRESS: + case AP_RESPONSE_BUSY: + break; + case AP_RESPONSE_Q_NOT_AVAIL: + case AP_RESPONSE_DECONFIGURED: + case AP_RESPONSE_CHECKSTOPPED: + case AP_RESPONSE_INVALID_ADDRESS: + return 0; + case AP_RESPONSE_OTHERWISE_CHANGED: + break; + default: + break; + } + if (i < AP_MAX_RESET - 1) { + udelay(5); + status = __ap_4096_commands_available(qid, &support); + } + } + return support; +} +EXPORT_SYMBOL(ap_4096_commands_available); + /** * ap_queue_enable_interruption(): Enable interruption on an AP. * @qid: The AP queue number diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 4785d07cd447..08b9738285b4 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -196,4 +196,6 @@ void ap_flush_queue(struct ap_device *ap_dev); int ap_module_init(void); void ap_module_exit(void); +int ap_4096_commands_available(ap_qid_t qid); + #endif /* _AP_BUS_H_ */ -- cgit v1.2.3 From 3e309a66f52e042881f76cbfb9b6c2aa70163e02 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Wed, 5 Jan 2011 12:47:45 +0100 Subject: [S390] zcrypt: support for 4096 bit keys for cex3a Definitions for CEX3 card types are changed to support 4096 bit RSA keys. Also new structs for the accelerator mode are needed. Additionaly when checking the length of key parts, the case for bigger (4096 bit) keys is needed. Signed-off-by: Felix Beck Signed-off-by: Ralph Wuerthner Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_cex2a.c | 78 ++++++++++++++++++++++++++++++-------- drivers/s390/crypto/zcrypt_cex2a.h | 25 ++++++++++++ 2 files changed, 87 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index 9c409efa1ecf..def0901767de 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -41,7 +41,7 @@ #define CEX2A_MIN_MOD_SIZE 1 /* 8 bits */ #define CEX2A_MAX_MOD_SIZE 256 /* 2048 bits */ #define CEX3A_MIN_MOD_SIZE CEX2A_MIN_MOD_SIZE -#define CEX3A_MAX_MOD_SIZE CEX2A_MAX_MOD_SIZE +#define CEX3A_MAX_MOD_SIZE 512 /* 4096 bits */ #define CEX2A_SPEED_RATING 970 #define CEX3A_SPEED_RATING 900 /* Fixme: Needs finetuning */ @@ -49,8 +49,10 @@ #define CEX2A_MAX_MESSAGE_SIZE 0x390 /* sizeof(struct type50_crb2_msg) */ #define CEX2A_MAX_RESPONSE_SIZE 0x110 /* max outputdatalength + type80_hdr */ -#define CEX3A_MAX_MESSAGE_SIZE CEX2A_MAX_MESSAGE_SIZE -#define CEX3A_MAX_RESPONSE_SIZE CEX2A_MAX_RESPONSE_SIZE +#define CEX3A_MAX_RESPONSE_SIZE 0x210 /* 512 bit modulus + * (max outputdatalength) + + * type80_hdr*/ +#define CEX3A_MAX_MESSAGE_SIZE sizeof(struct type50_crb3_msg) #define CEX2A_CLEANUP_TIME (15*HZ) #define CEX3A_CLEANUP_TIME CEX2A_CLEANUP_TIME @@ -110,7 +112,7 @@ static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev, mod = meb1->modulus + sizeof(meb1->modulus) - mod_len; exp = meb1->exponent + sizeof(meb1->exponent) - mod_len; inp = meb1->message + sizeof(meb1->message) - mod_len; - } else { + } else if (mod_len <= 256) { struct type50_meb2_msg *meb2 = ap_msg->message; memset(meb2, 0, sizeof(*meb2)); ap_msg->length = sizeof(*meb2); @@ -120,6 +122,17 @@ static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_device *zdev, mod = meb2->modulus + sizeof(meb2->modulus) - mod_len; exp = meb2->exponent + sizeof(meb2->exponent) - mod_len; inp = meb2->message + sizeof(meb2->message) - mod_len; + } else { + /* mod_len > 256 = 4096 bit RSA Key */ + struct type50_meb3_msg *meb3 = ap_msg->message; + memset(meb3, 0, sizeof(*meb3)); + ap_msg->length = sizeof(*meb3); + meb3->header.msg_type_code = TYPE50_TYPE_CODE; + meb3->header.msg_len = sizeof(*meb3); + meb3->keyblock_type = TYPE50_MEB3_FMT; + mod = meb3->modulus + sizeof(meb3->modulus) - mod_len; + exp = meb3->exponent + sizeof(meb3->exponent) - mod_len; + inp = meb3->message + sizeof(meb3->message) - mod_len; } if (copy_from_user(mod, mex->n_modulus, mod_len) || @@ -142,7 +155,7 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, struct ap_message *ap_msg, struct ica_rsa_modexpo_crt *crt) { - int mod_len, short_len, long_len, long_offset; + int mod_len, short_len, long_len, long_offset, limit; unsigned char *p, *q, *dp, *dq, *u, *inp; mod_len = crt->inputdatalength; @@ -152,14 +165,20 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, /* * CEX2A cannot handle p, dp, or U > 128 bytes. * If we have one of these, we need to do extra checking. + * For CEX3A the limit is 256 bytes. */ - if (long_len > 128) { + if (zdev->max_mod_size == CEX3A_MAX_MOD_SIZE) + limit = 256; + else + limit = 128; + + if (long_len > limit) { /* * zcrypt_rsa_crt already checked for the leading * zeroes of np_prime, bp_key and u_mult_inc. */ - long_offset = long_len - 128; - long_len = 128; + long_offset = long_len - limit; + long_len = limit; } else long_offset = 0; @@ -180,7 +199,7 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, dq = crb1->dq + sizeof(crb1->dq) - short_len; u = crb1->u + sizeof(crb1->u) - long_len; inp = crb1->message + sizeof(crb1->message) - mod_len; - } else { + } else if (long_len <= 128) { struct type50_crb2_msg *crb2 = ap_msg->message; memset(crb2, 0, sizeof(*crb2)); ap_msg->length = sizeof(*crb2); @@ -193,6 +212,20 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, dq = crb2->dq + sizeof(crb2->dq) - short_len; u = crb2->u + sizeof(crb2->u) - long_len; inp = crb2->message + sizeof(crb2->message) - mod_len; + } else { + /* long_len >= 256 */ + struct type50_crb3_msg *crb3 = ap_msg->message; + memset(crb3, 0, sizeof(*crb3)); + ap_msg->length = sizeof(*crb3); + crb3->header.msg_type_code = TYPE50_TYPE_CODE; + crb3->header.msg_len = sizeof(*crb3); + crb3->keyblock_type = TYPE50_CRB3_FMT; + p = crb3->p + sizeof(crb3->p) - long_len; + q = crb3->q + sizeof(crb3->q) - short_len; + dp = crb3->dp + sizeof(crb3->dp) - long_len; + dq = crb3->dq + sizeof(crb3->dq) - short_len; + u = crb3->u + sizeof(crb3->u) - long_len; + inp = crb3->message + sizeof(crb3->message) - mod_len; } if (copy_from_user(p, crt->np_prime + long_offset, long_len) || @@ -203,7 +236,6 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_device *zdev, copy_from_user(inp, crt->inputdata, mod_len)) return -EFAULT; - return 0; } @@ -230,7 +262,10 @@ static int convert_type80(struct zcrypt_device *zdev, zdev->online = 0; return -EAGAIN; /* repeat the request on a different device. */ } - BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE); + if (zdev->user_space_type == ZCRYPT_CEX2A) + BUG_ON(t80h->len > CEX2A_MAX_RESPONSE_SIZE); + else + BUG_ON(t80h->len > CEX3A_MAX_RESPONSE_SIZE); data = reply->message + t80h->len - outputdatalength; if (copy_to_user(outputdata, data, outputdatalength)) return -EFAULT; @@ -282,7 +317,10 @@ static void zcrypt_cex2a_receive(struct ap_device *ap_dev, } t80h = reply->message; if (t80h->type == TYPE80_RSP_CODE) { - length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len); + if (ap_dev->device_type == AP_DEVICE_TYPE_CEX2A) + length = min(CEX2A_MAX_RESPONSE_SIZE, (int) t80h->len); + else + length = min(CEX3A_MAX_RESPONSE_SIZE, (int) t80h->len); memcpy(msg->message, reply->message, length); } else memcpy(msg->message, reply->message, sizeof error_reply); @@ -307,7 +345,10 @@ static long zcrypt_cex2a_modexpo(struct zcrypt_device *zdev, int rc; ap_init_message(&ap_msg); - ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (zdev->user_space_type == ZCRYPT_CEX2A) + ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + else + ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; ap_msg.psmid = (((unsigned long long) current->pid) << 32) + @@ -345,7 +386,10 @@ static long zcrypt_cex2a_modexpo_crt(struct zcrypt_device *zdev, int rc; ap_init_message(&ap_msg); - ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + if (zdev->user_space_type == ZCRYPT_CEX2A) + ap_msg.message = kmalloc(CEX2A_MAX_MESSAGE_SIZE, GFP_KERNEL); + else + ap_msg.message = kmalloc(CEX3A_MAX_MESSAGE_SIZE, GFP_KERNEL); if (!ap_msg.message) return -ENOMEM; ap_msg.psmid = (((unsigned long long) current->pid) << 32) + @@ -404,8 +448,10 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev) return -ENOMEM; zdev->user_space_type = ZCRYPT_CEX3A; zdev->type_string = "CEX3A"; - zdev->min_mod_size = CEX3A_MIN_MOD_SIZE; - zdev->max_mod_size = CEX3A_MAX_MOD_SIZE; + zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; + zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; + if (ap_4096_commands_available(ap_dev->qid)) + zdev->max_mod_size = CEX3A_MAX_MOD_SIZE; zdev->short_crt = 1; zdev->speed_rating = CEX3A_SPEED_RATING; break; diff --git a/drivers/s390/crypto/zcrypt_cex2a.h b/drivers/s390/crypto/zcrypt_cex2a.h index 8f69d1dacab8..0350665810cf 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.h +++ b/drivers/s390/crypto/zcrypt_cex2a.h @@ -51,8 +51,10 @@ struct type50_hdr { #define TYPE50_MEB1_FMT 0x0001 #define TYPE50_MEB2_FMT 0x0002 +#define TYPE50_MEB3_FMT 0x0003 #define TYPE50_CRB1_FMT 0x0011 #define TYPE50_CRB2_FMT 0x0012 +#define TYPE50_CRB3_FMT 0x0013 /* Mod-Exp, with a small modulus */ struct type50_meb1_msg { @@ -74,6 +76,16 @@ struct type50_meb2_msg { unsigned char message[256]; } __attribute__((packed)); +/* Mod-Exp, with a larger modulus */ +struct type50_meb3_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0003 */ + unsigned char reserved[6]; + unsigned char exponent[512]; + unsigned char modulus[512]; + unsigned char message[512]; +} __attribute__((packed)); + /* CRT, with a small modulus */ struct type50_crb1_msg { struct type50_hdr header; @@ -100,6 +112,19 @@ struct type50_crb2_msg { unsigned char message[256]; } __attribute__((packed)); +/* CRT, with a larger modulus */ +struct type50_crb3_msg { + struct type50_hdr header; + unsigned short keyblock_type; /* 0x0013 */ + unsigned char reserved[6]; + unsigned char p[256]; + unsigned char q[256]; + unsigned char dp[256]; + unsigned char dq[256]; + unsigned char u[256]; + unsigned char message[512]; +} __attribute__((packed)); + /** * The type 80 response family is associated with a CEX2A card. * -- cgit v1.2.3 From 2ade1fab026b4a103f0105ec4b47654fc2f729c7 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Wed, 5 Jan 2011 12:47:46 +0100 Subject: [S390] zcrypt: support for 4096 bit keys for cex3c Definitions for CEX3 card types are changed to support 4096 bit RSA keys in the coprocessor. Signed-off-by: Felix Beck Signed-off-by: Ralph Wuerthner Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_pcixcc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index 510fab4577d4..fc8eb9dce841 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -45,12 +45,12 @@ #define PCIXCC_MIN_MOD_SIZE_OLD 64 /* 512 bits */ #define PCIXCC_MAX_MOD_SIZE 256 /* 2048 bits */ #define CEX3C_MIN_MOD_SIZE PCIXCC_MIN_MOD_SIZE -#define CEX3C_MAX_MOD_SIZE PCIXCC_MAX_MOD_SIZE +#define CEX3C_MAX_MOD_SIZE 512 /* 4096 bits */ #define PCIXCC_MCL2_SPEED_RATING 7870 #define PCIXCC_MCL3_SPEED_RATING 7870 #define CEX2C_SPEED_RATING 7000 -#define CEX3C_SPEED_RATING 6500 /* FIXME: needs finetuning */ +#define CEX3C_SPEED_RATING 6500 #define PCIXCC_MAX_ICA_MESSAGE_SIZE 0x77c /* max size type6 v2 crt message */ #define PCIXCC_MAX_ICA_RESPONSE_SIZE 0x77c /* max size type86 v2 reply */ -- cgit v1.2.3 From c2567f8ffa2704f6f2f81013e9a590deca5a865f Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Wed, 5 Jan 2011 12:47:47 +0100 Subject: [S390] zcrypt: cope with cca restriction of cex3 The cca on the crypto adapter has a restriction in the size of the exponent if a key with a modulus bigger than 2048 bit is used. Thus in that case we have to avoid that the crypto device driver thinks the adapter is defect and sets it offline. Therfore a new member for the zcrypt_device struct called max_exp_bit_length is introduced. This will be set the first time the cca returns the error code function not implemented. If this is done with an adapter twice it will return -EINVAL. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_api.h | 1 + drivers/s390/crypto/zcrypt_cex2a.c | 6 +++++- drivers/s390/crypto/zcrypt_pcica.c | 1 + drivers/s390/crypto/zcrypt_pcicc.c | 1 + drivers/s390/crypto/zcrypt_pcixcc.c | 13 +++++++++++++ 5 files changed, 21 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index 8e7ffbf2466c..88ebd114735b 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -109,6 +109,7 @@ struct zcrypt_device { int request_count; /* # current requests. */ struct ap_message reply; /* Per-device reply structure. */ + int max_exp_bit_length; }; struct zcrypt_device *zcrypt_device_alloc(size_t); diff --git a/drivers/s390/crypto/zcrypt_cex2a.c b/drivers/s390/crypto/zcrypt_cex2a.c index def0901767de..2176d00b395e 100644 --- a/drivers/s390/crypto/zcrypt_cex2a.c +++ b/drivers/s390/crypto/zcrypt_cex2a.c @@ -441,6 +441,7 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev) zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; zdev->short_crt = 1; zdev->speed_rating = CEX2A_SPEED_RATING; + zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE; break; case AP_DEVICE_TYPE_CEX3A: zdev = zcrypt_device_alloc(CEX3A_MAX_RESPONSE_SIZE); @@ -450,8 +451,11 @@ static int zcrypt_cex2a_probe(struct ap_device *ap_dev) zdev->type_string = "CEX3A"; zdev->min_mod_size = CEX2A_MIN_MOD_SIZE; zdev->max_mod_size = CEX2A_MAX_MOD_SIZE; - if (ap_4096_commands_available(ap_dev->qid)) + zdev->max_exp_bit_length = CEX2A_MAX_MOD_SIZE; + if (ap_4096_commands_available(ap_dev->qid)) { zdev->max_mod_size = CEX3A_MAX_MOD_SIZE; + zdev->max_exp_bit_length = CEX3A_MAX_MOD_SIZE; + } zdev->short_crt = 1; zdev->speed_rating = CEX3A_SPEED_RATING; break; diff --git a/drivers/s390/crypto/zcrypt_pcica.c b/drivers/s390/crypto/zcrypt_pcica.c index 09e934b295a0..1afb69c75fea 100644 --- a/drivers/s390/crypto/zcrypt_pcica.c +++ b/drivers/s390/crypto/zcrypt_pcica.c @@ -373,6 +373,7 @@ static int zcrypt_pcica_probe(struct ap_device *ap_dev) zdev->min_mod_size = PCICA_MIN_MOD_SIZE; zdev->max_mod_size = PCICA_MAX_MOD_SIZE; zdev->speed_rating = PCICA_SPEED_RATING; + zdev->max_exp_bit_length = PCICA_MAX_MOD_SIZE; ap_dev->reply = &zdev->reply; ap_dev->private = zdev; rc = zcrypt_device_register(zdev); diff --git a/drivers/s390/crypto/zcrypt_pcicc.c b/drivers/s390/crypto/zcrypt_pcicc.c index 9dec5c77cff4..aa4c050a5694 100644 --- a/drivers/s390/crypto/zcrypt_pcicc.c +++ b/drivers/s390/crypto/zcrypt_pcicc.c @@ -579,6 +579,7 @@ static int zcrypt_pcicc_probe(struct ap_device *ap_dev) zdev->min_mod_size = PCICC_MIN_MOD_SIZE; zdev->max_mod_size = PCICC_MAX_MOD_SIZE; zdev->speed_rating = PCICC_SPEED_RATING; + zdev->max_exp_bit_length = PCICC_MAX_MOD_SIZE; ap_dev->reply = &zdev->reply; ap_dev->private = zdev; rc = zcrypt_device_register(zdev); diff --git a/drivers/s390/crypto/zcrypt_pcixcc.c b/drivers/s390/crypto/zcrypt_pcixcc.c index fc8eb9dce841..4f85eb725f4f 100644 --- a/drivers/s390/crypto/zcrypt_pcixcc.c +++ b/drivers/s390/crypto/zcrypt_pcixcc.c @@ -567,6 +567,15 @@ static int convert_response_ica(struct zcrypt_device *zdev, case TYPE88_RSP_CODE: return convert_error(zdev, reply); case TYPE86_RSP_CODE: + if (msg->cprbx.ccp_rtcode && + (msg->cprbx.ccp_rscode == 0x14f) && + (outputdatalength > 256)) { + if (zdev->max_exp_bit_length <= 17) { + zdev->max_exp_bit_length = 17; + return -EAGAIN; + } else + return -EINVAL; + } if (msg->hdr.reply_code) return convert_error(zdev, reply); if (msg->cprbx.cprb_ver_id == 0x02) @@ -1052,11 +1061,13 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->speed_rating = PCIXCC_MCL2_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE_OLD; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE; } else { zdev->type_string = "PCIXCC_MCL3"; zdev->speed_rating = PCIXCC_MCL3_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE; } break; case AP_DEVICE_TYPE_CEX2C: @@ -1065,6 +1076,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->speed_rating = CEX2C_SPEED_RATING; zdev->min_mod_size = PCIXCC_MIN_MOD_SIZE; zdev->max_mod_size = PCIXCC_MAX_MOD_SIZE; + zdev->max_exp_bit_length = PCIXCC_MAX_MOD_SIZE; break; case AP_DEVICE_TYPE_CEX3C: zdev->user_space_type = ZCRYPT_CEX3C; @@ -1072,6 +1084,7 @@ static int zcrypt_pcixcc_probe(struct ap_device *ap_dev) zdev->speed_rating = CEX3C_SPEED_RATING; zdev->min_mod_size = CEX3C_MIN_MOD_SIZE; zdev->max_mod_size = CEX3C_MAX_MOD_SIZE; + zdev->max_exp_bit_length = CEX3C_MAX_MOD_SIZE; break; default: goto out_free; -- cgit v1.2.3 From 078f8ecaa30718694d1e13d9f415b7ce75b3c968 Mon Sep 17 00:00:00 2001 From: Felix Beck Date: Wed, 5 Jan 2011 12:47:48 +0100 Subject: [S390] Handling of 4096 bit RSA keys in CRT format. Also process 4096 bit RSA keys in CRT format. Handle them like the smaller keys and take care of the zero padding. Signed-off-by: Felix Beck Signed-off-by: Martin Schwidefsky --- drivers/s390/crypto/zcrypt_api.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 7fca9c10ffcf..8e65447f76b7 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -396,8 +396,15 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt) if (copied == 0) { unsigned int len; spin_unlock_bh(&zcrypt_device_lock); - /* len is max 256 / 2 - 120 = 8 */ - len = crt->inputdatalength / 2 - 120; + /* len is max 256 / 2 - 120 = 8 + * For bigger device just assume len of leading + * 0s is 8 as stated in the requirements for + * ica_rsa_modexpo_crt struct in zcrypt.h. + */ + if (crt->inputdatalength <= 256) + len = crt->inputdatalength / 2 - 120; + else + len = 8; if (len > sizeof(z1)) return -EFAULT; z1 = z2 = z3 = 0; @@ -405,6 +412,7 @@ static long zcrypt_rsa_crt(struct ica_rsa_modexpo_crt *crt) copy_from_user(&z2, crt->bp_key, len) || copy_from_user(&z3, crt->u_mult_inv, len)) return -EFAULT; + z1 = z2 = z3 = 0; copied = 1; /* * We have to restart device lookup - -- cgit v1.2.3 From 4f325184f2d4c1f2258873b2a333005dc4dfcbc0 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:49 +0100 Subject: [S390] qdio: prevent race for shared indicators If the shared indicator is used the following race leads to an inbound stall: Device CPU0 CPU1 ======================================================== non-shared DSCI =>1 ALSI => 1 Thin INT ALSI => 0 non-shared DSCI tasklets scheduled shared DSCI => 1 ALSI => 1 shared DSCI => 0 ALSI ? -> set Thin INT ALSI => 0 ALSI was set, shared DSCI => 1 After that no more interrupts occur because the DSCI is still set. Fix that race by only resetting the shared DSCI if it was actually set so the tasklets for all shared devices are scheduled and will run after the interrupt. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio.h | 4 +-- drivers/s390/cio/qdio_main.c | 4 +-- drivers/s390/cio/qdio_thinint.c | 54 +++++++---------------------------------- 3 files changed, 13 insertions(+), 49 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 0f4ef8769a3d..9b6ea3ca3ece 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -423,9 +423,9 @@ struct indicator_t { extern struct indicator_t *q_indicators; -static inline int shared_ind(struct qdio_irq *irq_ptr) +static inline int shared_ind(u32 *dsci) { - return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; + return dsci == &q_indicators[TIQDIO_SHARED_IND].ind; } /* prototypes for thin interrupt */ diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 194ea8c182b2..6621de94f3ad 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -1552,7 +1552,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr) WARN_ON(queue_irqs_enabled(q)); - if (!shared_ind(q->irq_ptr)) + if (!shared_ind(q->irq_ptr->dsci)) xchg(q->irq_ptr->dsci, 0); qdio_stop_polling(q); @@ -1562,7 +1562,7 @@ int qdio_start_irq(struct ccw_device *cdev, int nr) * We need to check again to not lose initiative after * resetting the ACK state. */ - if (!shared_ind(q->irq_ptr) && *q->irq_ptr->dsci) + if (!shared_ind(q->irq_ptr->dsci) && *q->irq_ptr->dsci) goto rescan; if (!qdio_inbound_q_done(q)) goto rescan; diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 64b59a58a1cd..5c4e741d8221 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -36,22 +36,8 @@ static u8 *tiqdio_alsi; struct indicator_t *q_indicators; -static int css_qdio_omit_svs; - static u64 last_ai_time; -static inline unsigned long do_clear_global_summary(void) -{ - register unsigned long __fn asm("1") = 3; - register unsigned long __tmp asm("2"); - register unsigned long __time asm("3"); - - asm volatile( - " .insn rre,0xb2650000,2,0" - : "+d" (__fn), "=d" (__tmp), "=d" (__time)); - return __time; -} - /* returns addr for the device state change indicator */ static u32 *get_indicator(void) { @@ -84,10 +70,6 @@ void tiqdio_add_input_queues(struct qdio_irq *irq_ptr) struct qdio_q *q; int i; - /* No TDD facility? If we must use SIGA-s we can also omit SVS. */ - if (!css_qdio_omit_svs && irq_ptr->siga_flag.sync) - css_qdio_omit_svs = 1; - mutex_lock(&tiq_list_lock); for_each_input_queue(irq_ptr, q, i) list_add_rcu(&q->entry, &tiq_list); @@ -113,9 +95,9 @@ void tiqdio_remove_input_queues(struct qdio_irq *irq_ptr) } } -static inline int shared_ind_used(void) +static inline u32 shared_ind_set(void) { - return atomic_read(&q_indicators[TIQDIO_SHARED_IND].count); + return q_indicators[TIQDIO_SHARED_IND].ind; } /** @@ -125,22 +107,12 @@ static inline int shared_ind_used(void) */ static void tiqdio_thinint_handler(void *alsi, void *data) { + u32 si_used = shared_ind_set(); struct qdio_q *q; last_ai_time = S390_lowcore.int_clock; kstat_cpu(smp_processor_id()).irqs[IOINT_QAI]++; - /* - * SVS only when needed: issue SVS to benefit from iqdio interrupt - * avoidance (SVS clears adapter interrupt suppression overwrite). - */ - if (!css_qdio_omit_svs) - do_clear_global_summary(); - - /* reset local summary indicator */ - if (shared_ind_used()) - xchg(tiqdio_alsi, 0); - /* protect tiq_list entries, only changed in activate or shutdown */ rcu_read_lock(); @@ -148,7 +120,10 @@ static void tiqdio_thinint_handler(void *alsi, void *data) list_for_each_entry_rcu(q, &tiq_list, entry) { /* only process queues from changed sets */ - if (!*q->irq_ptr->dsci) + if (unlikely(shared_ind(q->irq_ptr->dsci))) { + if (!si_used) + continue; + } else if (!*q->irq_ptr->dsci) continue; if (q->u.in.queue_start_poll) { @@ -164,7 +139,7 @@ static void tiqdio_thinint_handler(void *alsi, void *data) q->irq_ptr->int_parm); } else { /* only clear it if the indicator is non-shared */ - if (!shared_ind(q->irq_ptr)) + if (!shared_ind(q->irq_ptr->dsci)) xchg(q->irq_ptr->dsci, 0); /* * Call inbound processing but not directly @@ -180,13 +155,8 @@ static void tiqdio_thinint_handler(void *alsi, void *data) * If the shared indicator was used clear it now after all queues * were processed. */ - if (shared_ind_used()) { + if (si_used && shared_ind_set()) xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); - - /* prevent racing */ - if (*tiqdio_alsi) - xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 1 << 7); - } } static int set_subchannel_ind(struct qdio_irq *irq_ptr, int reset) @@ -271,12 +241,6 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr) { if (!is_thinint_irq(irq_ptr)) return 0; - - /* Check for aif time delay disablement. If installed, - * omit SVS even under LPAR - */ - if (css_general_characteristics.aif_tdd) - css_qdio_omit_svs = 1; return set_subchannel_ind(irq_ptr, 0); } -- cgit v1.2.3 From 3d6c76ff32bb9b2ebf6e859855d315eb42e3df50 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:50 +0100 Subject: [S390] qdio: outbound tasklet scan threshold Introduce a scan treshold for the qdio outbound queues. By setting the threshold the driver can tell qdio after how much used SBALs qdio should schedule the outbound tasklet that scans the queue for finished SBALs. The threshold is specific by the drivers because a Hipersockets device is much faster in utilizing outbound buffers than a ZFCP or OSA device. The default values after how many used SBALs the tasklet should run are: OSA: > 31 SBALs Hipersockets: > 7 SBALs zfcp: > 55 SBALs Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/qdio.h | 1 + drivers/s390/cio/qdio.h | 2 ++ drivers/s390/cio/qdio_main.c | 8 +++++++- drivers/s390/cio/qdio_setup.c | 1 + drivers/s390/net/qeth_core_main.c | 2 ++ drivers/s390/scsi/zfcp_qdio.c | 2 ++ 6 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/qdio.h b/arch/s390/include/asm/qdio.h index 46e96bc1f5a1..350e7ee5952d 100644 --- a/arch/s390/include/asm/qdio.h +++ b/arch/s390/include/asm/qdio.h @@ -361,6 +361,7 @@ struct qdio_initialize { qdio_handler_t *input_handler; qdio_handler_t *output_handler; void (*queue_start_poll) (struct ccw_device *, int, unsigned long); + int scan_threshold; unsigned long int_parm; void **input_sbal_addr_array; void **output_sbal_addr_array; diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 9b6ea3ca3ece..a77aa9109cfd 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -249,6 +249,8 @@ struct qdio_output_q { int use_enh_siga; /* timer to check for more outbound work */ struct timer_list timer; + /* used SBALs before tasklet schedule */ + int scan_threshold; }; /* diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 6621de94f3ad..4c0109900c74 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -1492,7 +1492,13 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, qperf_inc(q, fast_requeue); out: - tasklet_schedule(&q->tasklet); + /* in case of SIGA errors we must process the error immediately */ + if (used >= q->u.out.scan_threshold || rc) + tasklet_schedule(&q->tasklet); + else + /* free the SBALs in case of no further traffic */ + if (!timer_pending(&q->u.out.timer)) + mod_timer(&q->u.out.timer, jiffies + HZ); return rc; } diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index a13cf7ec64b2..635f35dc8466 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -178,6 +178,7 @@ static void setup_queues(struct qdio_irq *irq_ptr, setup_queues_misc(q, irq_ptr, qdio_init->output_handler, i); q->is_input_q = 0; + q->u.out.scan_threshold = qdio_init->scan_threshold; setup_storage_lists(q, irq_ptr, output_sbal_array, i); output_sbal_array += QDIO_MAX_BUFFERS_PER_Q; diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index e6b2df0e73f5..f65320babf71 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -3831,6 +3831,8 @@ static int qeth_qdio_establish(struct qeth_card *card) init_data.int_parm = (unsigned long) card; init_data.input_sbal_addr_array = (void **) in_sbal_ptrs; init_data.output_sbal_addr_array = (void **) out_sbal_ptrs; + init_data.scan_threshold = + (card->info.type == QETH_CARD_TYPE_IQD) ? 8 : 32; if (atomic_cmpxchg(&card->qdio.state, QETH_QDIO_ALLOCATED, QETH_QDIO_ESTABLISHED) == QETH_QDIO_ALLOCATED) { diff --git a/drivers/s390/scsi/zfcp_qdio.c b/drivers/s390/scsi/zfcp_qdio.c index a0554beb4179..5ae40ef586a8 100644 --- a/drivers/s390/scsi/zfcp_qdio.c +++ b/drivers/s390/scsi/zfcp_qdio.c @@ -292,6 +292,8 @@ static void zfcp_qdio_setup_init_data(struct qdio_initialize *id, id->int_parm = (unsigned long) qdio; id->input_sbal_addr_array = (void **) (qdio->res_q); id->output_sbal_addr_array = (void **) (qdio->req_q); + id->scan_threshold = + QDIO_MAX_BUFFERS_PER_Q - ZFCP_QDIO_MAX_SBALS_PER_REQ * 2; } /** -- cgit v1.2.3 From 0195843bfda90a215f3b72c9aac2fd0bc9244b67 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:51 +0100 Subject: [S390] qdio: outbound queue full counter Add a counter for outbound queue full events to the qdio statistics. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio.h | 1 + drivers/s390/cio/qdio_debug.c | 1 + drivers/s390/cio/qdio_main.c | 3 +++ 3 files changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index a77aa9109cfd..40ca0b9241e5 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -202,6 +202,7 @@ struct qdio_dev_perf_stat { unsigned int inbound_queue_full; unsigned int outbound_call; unsigned int outbound_handler; + unsigned int outbound_queue_full; unsigned int fast_requeue; unsigned int target_full; unsigned int eqbs; diff --git a/drivers/s390/cio/qdio_debug.c b/drivers/s390/cio/qdio_debug.c index 28868e7471a5..f8b03a636e49 100644 --- a/drivers/s390/cio/qdio_debug.c +++ b/drivers/s390/cio/qdio_debug.c @@ -151,6 +151,7 @@ static char *qperf_names[] = { "Inbound queue full", "Outbound calls", "Outbound handler", + "Outbound queue full", "Outbound fast_requeue", "Outbound target_full", "QEBSM eqbs", diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 4c0109900c74..af86875bede4 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -1447,6 +1447,9 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, used = atomic_add_return(count, &q->nr_buf_used); BUG_ON(used > QDIO_MAX_BUFFERS_PER_Q); + if (used == QDIO_MAX_BUFFERS_PER_Q) + qperf_inc(q, outbound_queue_full); + if (callflags & QDIO_FLAG_PCI_OUT) { q->u.out.pci_out_enabled = 1; qperf_inc(q, pci_request_int); -- cgit v1.2.3 From 958c0ba403cb6a693b54be2389f9ef53377fa259 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:52 +0100 Subject: [S390] qdio: use proper QEBSM operand for SIGA-R and SIGA-S If QIOASSIST is enabled for a qdio device the SIGA instruction requires a modified function code. This function code modifier was missing for SIGA-R and SIGA-S which can lead to a kernel panic caused by an operand exception. Cc: stable@kernel.org Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio.h | 6 ++++++ drivers/s390/cio/qdio_main.c | 43 ++++++++++++++++++++++++++++--------------- 2 files changed, 34 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 40ca0b9241e5..0a42da4beafa 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -91,6 +91,12 @@ enum qdio_irq_states { #define AC1_SC_QEBSM_AVAILABLE 0x02 /* available for subchannel */ #define AC1_SC_QEBSM_ENABLED 0x01 /* enabled for subchannel */ +/* SIGA flags */ +#define QDIO_SIGA_WRITE 0x00 +#define QDIO_SIGA_READ 0x01 +#define QDIO_SIGA_SYNC 0x02 +#define QDIO_SIGA_QEBSM_FLAG 0x80 + #ifdef CONFIG_64BIT static inline int do_sqbs(u64 token, unsigned char state, int queue, int *start, int *count) diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index af86875bede4..99823477d57e 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -30,11 +30,12 @@ MODULE_AUTHOR("Utz Bacher ,"\ MODULE_DESCRIPTION("QDIO base support"); MODULE_LICENSE("GPL"); -static inline int do_siga_sync(struct subchannel_id schid, - unsigned int out_mask, unsigned int in_mask) +static inline int do_siga_sync(unsigned long schid, + unsigned int out_mask, unsigned int in_mask, + unsigned int fc) { - register unsigned long __fc asm ("0") = 2; - register struct subchannel_id __schid asm ("1") = schid; + register unsigned long __fc asm ("0") = fc; + register unsigned long __schid asm ("1") = schid; register unsigned long out asm ("2") = out_mask; register unsigned long in asm ("3") = in_mask; int cc; @@ -48,10 +49,11 @@ static inline int do_siga_sync(struct subchannel_id schid, return cc; } -static inline int do_siga_input(struct subchannel_id schid, unsigned int mask) +static inline int do_siga_input(unsigned long schid, unsigned int mask, + unsigned int fc) { - register unsigned long __fc asm ("0") = 1; - register struct subchannel_id __schid asm ("1") = schid; + register unsigned long __fc asm ("0") = fc; + register unsigned long __schid asm ("1") = schid; register unsigned long __mask asm ("2") = mask; int cc; @@ -280,6 +282,8 @@ void qdio_init_buf_states(struct qdio_irq *irq_ptr) static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, unsigned int input) { + unsigned long schid = *((u32 *) &q->irq_ptr->schid); + unsigned int fc = QDIO_SIGA_SYNC; int cc; if (!need_siga_sync(q)) @@ -288,7 +292,12 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); qperf_inc(q, siga_sync); - cc = do_siga_sync(q->irq_ptr->schid, output, input); + if (is_qebsm(q)) { + schid = q->irq_ptr->sch_token; + fc |= QDIO_SIGA_QEBSM_FLAG; + } + + cc = do_siga_sync(schid, output, input, fc); if (cc) DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); return cc; @@ -314,8 +323,8 @@ static inline int qdio_siga_sync_all(struct qdio_q *q) static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) { - unsigned long schid; - unsigned int fc = 0; + unsigned long schid = *((u32 *) &q->irq_ptr->schid); + unsigned int fc = QDIO_SIGA_WRITE; u64 start_time = 0; int cc; @@ -324,11 +333,8 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) if (is_qebsm(q)) { schid = q->irq_ptr->sch_token; - fc |= 0x80; + fc |= QDIO_SIGA_QEBSM_FLAG; } - else - schid = *((u32 *)&q->irq_ptr->schid); - again: cc = do_siga_output(schid, q->mask, busy_bit, fc); @@ -348,12 +354,19 @@ again: static inline int qdio_siga_input(struct qdio_q *q) { + unsigned long schid = *((u32 *) &q->irq_ptr->schid); + unsigned int fc = QDIO_SIGA_READ; int cc; DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-r:%1d", q->nr); qperf_inc(q, siga_read); - cc = do_siga_input(q->irq_ptr->schid, q->mask); + if (is_qebsm(q)) { + schid = q->irq_ptr->sch_token; + fc |= QDIO_SIGA_QEBSM_FLAG; + } + + cc = do_siga_input(schid, q->mask, fc); if (cc) DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); return cc; -- cgit v1.2.3 From 110da31709023de61735f2d8a3e52c20c23bb570 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:53 +0100 Subject: [S390] qdio: remove enhanced SIGA HiperSocket devices only use one SBAL per qdio call without the enhanced SIGA feature. Since that feature is currently not used remove it from the qdio code so the compiler can generate better code for the HiperSocket outbound path. While at it mark the SIGA error conditions as unlikely. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio.h | 2 -- drivers/s390/cio/qdio_main.c | 57 ++++++++++++-------------------------------- 2 files changed, 15 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 0a42da4beafa..1b40a92fec14 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -252,8 +252,6 @@ struct qdio_input_q { struct qdio_output_q { /* PCIs are enabled for the queue */ int pci_out_enabled; - /* IQDIO: output multiple buffers (enhanced SIGA) */ - int use_enh_siga; /* timer to check for more outbound work */ struct timer_list timer; /* used SBALs before tasklet schedule */ diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 99823477d57e..8a722f208325 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -298,7 +298,7 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, } cc = do_siga_sync(schid, output, input, fc); - if (cc) + if (unlikely(cc)) DBF_ERROR("%4x SIGA-S:%2d", SCH_NO(q), cc); return cc; } @@ -328,9 +328,6 @@ static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) u64 start_time = 0; int cc; - if (q->u.out.use_enh_siga) - fc = 3; - if (is_qebsm(q)) { schid = q->irq_ptr->sch_token; fc |= QDIO_SIGA_QEBSM_FLAG; @@ -339,7 +336,7 @@ again: cc = do_siga_output(schid, q->mask, busy_bit, fc); /* hipersocket busy condition */ - if (*busy_bit) { + if (unlikely(*busy_bit)) { WARN_ON(queue_type(q) != QDIO_IQDIO_QFMT || cc != 2); if (!start_time) { @@ -367,7 +364,7 @@ static inline int qdio_siga_input(struct qdio_q *q) } cc = do_siga_input(schid, q->mask, fc); - if (cc) + if (unlikely(cc)) DBF_ERROR("%4x SIGA-R:%2d", SCH_NO(q), cc); return cc; } @@ -1288,7 +1285,6 @@ int qdio_establish(struct qdio_initialize *init_data) } qdio_setup_ssqd_info(irq_ptr); - DBF_EVENT("qDmmwc:%2x", irq_ptr->ssqd_desc.mmwc); DBF_EVENT("qib ac:%4x", irq_ptr->qib.ac); /* qebsm is now setup if available, initialize buffer states */ @@ -1466,48 +1462,25 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, if (callflags & QDIO_FLAG_PCI_OUT) { q->u.out.pci_out_enabled = 1; qperf_inc(q, pci_request_int); - } - else + } else q->u.out.pci_out_enabled = 0; if (queue_type(q) == QDIO_IQDIO_QFMT) { - if (multicast_outbound(q)) + /* One SIGA-W per buffer required for unicast HiperSockets. */ + WARN_ON_ONCE(count > 1 && !multicast_outbound(q)); + + rc = qdio_kick_outbound_q(q); + } else if (unlikely(need_siga_sync(q))) { + rc = qdio_siga_sync_q(q); + } else { + /* try to fast requeue buffers */ + get_buf_state(q, prev_buf(bufnr), &state, 0); + if (state != SLSB_CU_OUTPUT_PRIMED) rc = qdio_kick_outbound_q(q); else - if ((q->irq_ptr->ssqd_desc.mmwc > 1) && - (count > 1) && - (count <= q->irq_ptr->ssqd_desc.mmwc)) { - /* exploit enhanced SIGA */ - q->u.out.use_enh_siga = 1; - rc = qdio_kick_outbound_q(q); - } else { - /* - * One siga-w per buffer required for unicast - * HiperSockets. - */ - q->u.out.use_enh_siga = 0; - while (count--) { - rc = qdio_kick_outbound_q(q); - if (rc) - goto out; - } - } - goto out; + qperf_inc(q, fast_requeue); } - if (need_siga_sync(q)) { - qdio_siga_sync_q(q); - goto out; - } - - /* try to fast requeue buffers */ - get_buf_state(q, prev_buf(bufnr), &state, 0); - if (state != SLSB_CU_OUTPUT_PRIMED) - rc = qdio_kick_outbound_q(q); - else - qperf_inc(q, fast_requeue); - -out: /* in case of SIGA errors we must process the error immediately */ if (used >= q->u.out.scan_threshold || rc) tasklet_schedule(&q->tasklet); -- cgit v1.2.3 From 90adac58d1a4daf3560739ff5b76497d5ece16c4 Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:54 +0100 Subject: [S390] qdio: cleanup SIGA sync Simplify the SIGA sync code and add unlikely annotations. In polling mode SBALs may be accessed without interrupt, so call SIGA sync before every scan. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/qdio.h | 16 +++++------ drivers/s390/cio/qdio_main.c | 64 ++++++++++++++++++++----------------------- drivers/s390/cio/qdio_setup.c | 19 +++++-------- 3 files changed, 44 insertions(+), 55 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h index 1b40a92fec14..7bc643f3f5ab 100644 --- a/drivers/s390/cio/qdio.h +++ b/drivers/s390/cio/qdio.h @@ -148,10 +148,9 @@ struct siga_flag { u8 input:1; u8 output:1; u8 sync:1; - u8 no_sync_ti:1; - u8 no_sync_out_ti:1; - u8 no_sync_out_pci:1; - u8:2; + u8 sync_after_ai:1; + u8 sync_out_after_pci:1; + u8:3; } __attribute__ ((packed)); struct chsc_ssqd_area { @@ -390,12 +389,13 @@ static inline int multicast_outbound(struct qdio_q *q) (q->irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) #define is_qebsm(q) (q->irq_ptr->sch_token != 0) -#define need_siga_sync_thinint(q) (!q->irq_ptr->siga_flag.no_sync_ti) -#define need_siga_sync_out_thinint(q) (!q->irq_ptr->siga_flag.no_sync_out_ti) #define need_siga_in(q) (q->irq_ptr->siga_flag.input) #define need_siga_out(q) (q->irq_ptr->siga_flag.output) -#define need_siga_sync(q) (q->irq_ptr->siga_flag.sync) -#define siga_syncs_out_pci(q) (q->irq_ptr->siga_flag.no_sync_out_pci) +#define need_siga_sync(q) (unlikely(q->irq_ptr->siga_flag.sync)) +#define need_siga_sync_after_ai(q) \ + (unlikely(q->irq_ptr->siga_flag.sync_after_ai)) +#define need_siga_sync_out_after_pci(q) \ + (unlikely(q->irq_ptr->siga_flag.sync_out_after_pci)) #define for_each_input_queue(irq_ptr, q, i) \ for (i = 0, q = irq_ptr->input_qs[0]; \ diff --git a/drivers/s390/cio/qdio_main.c b/drivers/s390/cio/qdio_main.c index 8a722f208325..e9fff2b9bce2 100644 --- a/drivers/s390/cio/qdio_main.c +++ b/drivers/s390/cio/qdio_main.c @@ -286,9 +286,6 @@ static inline int qdio_siga_sync(struct qdio_q *q, unsigned int output, unsigned int fc = QDIO_SIGA_SYNC; int cc; - if (!need_siga_sync(q)) - return 0; - DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "siga-s:%1d", q->nr); qperf_inc(q, siga_sync); @@ -311,16 +308,6 @@ static inline int qdio_siga_sync_q(struct qdio_q *q) return qdio_siga_sync(q, q->mask, 0); } -static inline int qdio_siga_sync_out(struct qdio_q *q) -{ - return qdio_siga_sync(q, ~0U, 0); -} - -static inline int qdio_siga_sync_all(struct qdio_q *q) -{ - return qdio_siga_sync(q, ~0U, ~0U); -} - static int qdio_siga_output(struct qdio_q *q, unsigned int *busy_bit) { unsigned long schid = *((u32 *) &q->irq_ptr->schid); @@ -369,21 +356,23 @@ static inline int qdio_siga_input(struct qdio_q *q) return cc; } -static inline void qdio_sync_after_thinint(struct qdio_q *q) +#define qdio_siga_sync_out(q) qdio_siga_sync(q, ~0U, 0) +#define qdio_siga_sync_all(q) qdio_siga_sync(q, ~0U, ~0U) + +static inline void qdio_sync_queues(struct qdio_q *q) { - if (pci_out_supported(q)) { - if (need_siga_sync_thinint(q)) - qdio_siga_sync_all(q); - else if (need_siga_sync_out_thinint(q)) - qdio_siga_sync_out(q); - } else + /* PCI capable outbound queues will also be scanned so sync them too */ + if (pci_out_supported(q)) + qdio_siga_sync_all(q); + else qdio_siga_sync_q(q); } int debug_get_buf_state(struct qdio_q *q, unsigned int bufnr, unsigned char *state) { - qdio_siga_sync_q(q); + if (need_siga_sync(q)) + qdio_siga_sync_q(q); return get_buf_states(q, bufnr, state, 1, 0); } @@ -560,7 +549,8 @@ static inline int qdio_inbound_q_done(struct qdio_q *q) if (!atomic_read(&q->nr_buf_used)) return 1; - qdio_siga_sync_q(q); + if (need_siga_sync(q)) + qdio_siga_sync_q(q); get_buf_state(q, q->first_to_check, &state, 0); if (state == SLSB_P_INPUT_PRIMED || state == SLSB_P_INPUT_ERROR) @@ -655,9 +645,12 @@ static int get_outbound_buffer_frontier(struct qdio_q *q) int count, stop; unsigned char state; - if (((queue_type(q) != QDIO_IQDIO_QFMT) && !pci_out_supported(q)) || - (queue_type(q) == QDIO_IQDIO_QFMT && multicast_outbound(q))) - qdio_siga_sync_q(q); + if (need_siga_sync(q)) + if (((queue_type(q) != QDIO_IQDIO_QFMT) && + !pci_out_supported(q)) || + (queue_type(q) == QDIO_IQDIO_QFMT && + multicast_outbound(q))) + qdio_siga_sync_q(q); /* * Don't check 128 buffers, as otherwise qdio_inbound_q_moved @@ -829,7 +822,8 @@ static inline void qdio_check_outbound_after_thinint(struct qdio_q *q) static void __tiqdio_inbound_processing(struct qdio_q *q) { qperf_inc(q, tasklet_inbound); - qdio_sync_after_thinint(q); + if (need_siga_sync(q) && need_siga_sync_after_ai(q)) + qdio_sync_queues(q); /* * The interrupt could be caused by a PCI request. Check the @@ -909,16 +903,14 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr) tasklet_schedule(&q->tasklet); } - if (!(irq_ptr->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED)) + if (!pci_out_supported(q)) return; for_each_output_queue(irq_ptr, q, i) { if (qdio_outbound_q_done(q)) continue; - - if (!siga_syncs_out_pci(q)) + if (need_siga_sync(q) && need_siga_sync_out_after_pci(q)) qdio_siga_sync_q(q); - tasklet_schedule(&q->tasklet); } } @@ -1470,7 +1462,7 @@ static int handle_outbound(struct qdio_q *q, unsigned int callflags, WARN_ON_ONCE(count > 1 && !multicast_outbound(q)); rc = qdio_kick_outbound_q(q); - } else if (unlikely(need_siga_sync(q))) { + } else if (need_siga_sync(q)) { rc = qdio_siga_sync_q(q); } else { /* try to fast requeue buffers */ @@ -1597,12 +1589,14 @@ int qdio_get_next_buffers(struct ccw_device *cdev, int nr, int *bufnr, q = irq_ptr->input_qs[nr]; WARN_ON(queue_irqs_enabled(q)); - qdio_sync_after_thinint(q); - /* - * The interrupt could be caused by a PCI request. Check the - * PCI capable outbound queues. + * Cannot rely on automatic sync after interrupt since queues may + * also be examined without interrupt. */ + if (need_siga_sync(q)) + qdio_sync_queues(q); + + /* check the PCI capable outbound queues. */ qdio_check_outbound_after_thinint(q); if (!qdio_inbound_q_moved(q)) diff --git a/drivers/s390/cio/qdio_setup.c b/drivers/s390/cio/qdio_setup.c index 635f35dc8466..89107d0938c4 100644 --- a/drivers/s390/cio/qdio_setup.c +++ b/drivers/s390/cio/qdio_setup.c @@ -197,14 +197,10 @@ static void process_ac_flags(struct qdio_irq *irq_ptr, unsigned char qdioac) irq_ptr->siga_flag.output = 1; if (qdioac & AC1_SIGA_SYNC_NEEDED) irq_ptr->siga_flag.sync = 1; - if (qdioac & AC1_AUTOMATIC_SYNC_ON_THININT) - irq_ptr->siga_flag.no_sync_ti = 1; - if (qdioac & AC1_AUTOMATIC_SYNC_ON_OUT_PCI) - irq_ptr->siga_flag.no_sync_out_pci = 1; - - if (irq_ptr->siga_flag.no_sync_out_pci && - irq_ptr->siga_flag.no_sync_ti) - irq_ptr->siga_flag.no_sync_out_ti = 1; + if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_THININT)) + irq_ptr->siga_flag.sync_after_ai = 1; + if (!(qdioac & AC1_AUTOMATIC_SYNC_ON_OUT_PCI)) + irq_ptr->siga_flag.sync_out_after_pci = 1; } static void check_and_setup_qebsm(struct qdio_irq *irq_ptr, @@ -452,7 +448,7 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, char s[80]; snprintf(s, 80, "qdio: %s %s on SC %x using " - "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s%s\n", + "AI:%d QEBSM:%d PCI:%d TDD:%d SIGA:%s%s%s%s%s\n", dev_name(&cdev->dev), (irq_ptr->qib.qfmt == QDIO_QETH_QFMT) ? "OSA" : ((irq_ptr->qib.qfmt == QDIO_ZFCP_QFMT) ? "ZFCP" : "HS"), @@ -464,9 +460,8 @@ void qdio_print_subchannel_info(struct qdio_irq *irq_ptr, (irq_ptr->siga_flag.input) ? "R" : " ", (irq_ptr->siga_flag.output) ? "W" : " ", (irq_ptr->siga_flag.sync) ? "S" : " ", - (!irq_ptr->siga_flag.no_sync_ti) ? "A" : " ", - (!irq_ptr->siga_flag.no_sync_out_ti) ? "O" : " ", - (!irq_ptr->siga_flag.no_sync_out_pci) ? "P" : " "); + (irq_ptr->siga_flag.sync_after_ai) ? "A" : " ", + (irq_ptr->siga_flag.sync_out_after_pci) ? "P" : " "); printk(KERN_INFO "%s", s); } -- cgit v1.2.3 From ce322ccd53f2505cf8b0ed204631d6ac054ac66a Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Wed, 5 Jan 2011 12:47:56 +0100 Subject: [S390] cio: obtain mdc value per channel path Add support to accumulate the number of 64K-bytes blocks all paths to a device at least support for a transport command. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/ccwdev.h | 2 ++ drivers/s390/cio/chsc.c | 19 +++++++++++++++++++ drivers/s390/cio/chsc.h | 18 ++++++++++++++++++ drivers/s390/cio/device_ops.c | 40 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+) (limited to 'drivers') diff --git a/arch/s390/include/asm/ccwdev.h b/arch/s390/include/asm/ccwdev.h index e8501115eca8..ff6f62e0ec3e 100644 --- a/arch/s390/include/asm/ccwdev.h +++ b/arch/s390/include/asm/ccwdev.h @@ -204,6 +204,8 @@ int ccw_device_tm_start_timeout(struct ccw_device *, struct tcw *, unsigned long, u8, int); int ccw_device_tm_intrg(struct ccw_device *cdev); +int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask); + extern int ccw_device_set_online(struct ccw_device *cdev); extern int ccw_device_set_offline(struct ccw_device *cdev); diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c index 1aaddea673e0..0689fcf23a11 100644 --- a/drivers/s390/cio/chsc.c +++ b/drivers/s390/cio/chsc.c @@ -695,6 +695,25 @@ out: return ret; } +int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, + struct channel_path_desc_fmt1 *desc) +{ + struct chsc_response_struct *chsc_resp; + struct chsc_scpd *scpd_area; + int ret; + + spin_lock_irq(&chsc_page_lock); + scpd_area = chsc_page; + ret = chsc_determine_channel_path_desc(chpid, 0, 0, 1, 0, scpd_area); + if (ret) + goto out; + chsc_resp = (void *)&scpd_area->response; + memcpy(desc, &chsc_resp->data, sizeof(*desc)); +out: + spin_unlock_irq(&chsc_page_lock); + return ret; +} + static void chsc_initialize_cmg_chars(struct channel_path *chp, u8 cmcv, struct cmg_chars *chars) diff --git a/drivers/s390/cio/chsc.h b/drivers/s390/cio/chsc.h index 6693f5e3176f..3f15b2aaeaea 100644 --- a/drivers/s390/cio/chsc.h +++ b/drivers/s390/cio/chsc.h @@ -35,6 +35,22 @@ struct channel_path_desc { u8 chpp; } __attribute__ ((packed)); +struct channel_path_desc_fmt1 { + u8 flags; + u8 lsn; + u8 desc; + u8 chpid; + u32:24; + u8 chpp; + u32 unused[3]; + u16 mdc; + u16:13; + u8 r:1; + u8 s:1; + u8 f:1; + u32 zeros[2]; +} __attribute__ ((packed)); + struct channel_path; struct css_chsc_char { @@ -92,6 +108,8 @@ int chsc_determine_channel_path_desc(struct chp_id chpid, int fmt, int rfmt, int c, int m, void *page); int chsc_determine_base_channel_path_desc(struct chp_id chpid, struct channel_path_desc *desc); +int chsc_determine_fmt1_channel_path_desc(struct chp_id chpid, + struct channel_path_desc_fmt1 *desc); void chsc_chp_online(struct chp_id chpid); void chsc_chp_offline(struct chp_id chpid); int chsc_get_channel_measurement_chars(struct channel_path *chp); diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c index 6da84543dfe9..651976b54af8 100644 --- a/drivers/s390/cio/device_ops.c +++ b/drivers/s390/cio/device_ops.c @@ -686,6 +686,46 @@ int ccw_device_tm_start_timeout(struct ccw_device *cdev, struct tcw *tcw, } EXPORT_SYMBOL(ccw_device_tm_start_timeout); +/** + * ccw_device_get_mdc - accumulate max data count + * @cdev: ccw device for which the max data count is accumulated + * @mask: mask of paths to use + * + * Return the number of 64K-bytes blocks all paths at least support + * for a transport command. Return values <= 0 indicate failures. + */ +int ccw_device_get_mdc(struct ccw_device *cdev, u8 mask) +{ + struct subchannel *sch = to_subchannel(cdev->dev.parent); + struct channel_path_desc_fmt1 desc; + struct chp_id chpid; + int mdc = 0, ret, i; + + /* Adjust requested path mask to excluded varied off paths. */ + if (mask) + mask &= sch->lpm; + else + mask = sch->lpm; + + chp_id_init(&chpid); + for (i = 0; i < 8; i++) { + if (!(mask & (0x80 >> i))) + continue; + chpid.id = sch->schib.pmcw.chpid[i]; + ret = chsc_determine_fmt1_channel_path_desc(chpid, &desc); + if (ret) + return ret; + if (!desc.f) + return 0; + if (!desc.r) + mdc = 1; + mdc = mdc ? min(mdc, (int)desc.mdc) : desc.mdc; + } + + return mdc; +} +EXPORT_SYMBOL(ccw_device_get_mdc); + /** * ccw_device_tm_intrg - perform interrogate function * @cdev: ccw device on which to perform the interrogate function -- cgit v1.2.3 From 4bc4e965d3e86897e4c7c487a477ccdf13db5b82 Mon Sep 17 00:00:00 2001 From: Sebastian Ott Date: Wed, 5 Jan 2011 12:47:58 +0100 Subject: [S390] css: update subchannel descriptor Update the subchannel descriptor if we receive a "Installed parameters modified" crw. Signed-off-by: Sebastian Ott Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/css.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c index 825951b6b83f..24d8e97355b9 100644 --- a/drivers/s390/cio/css.c +++ b/drivers/s390/cio/css.c @@ -618,6 +618,7 @@ EXPORT_SYMBOL_GPL(css_schedule_reprobe); static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) { struct subchannel_id mchk_schid; + struct subchannel *sch; if (overflow) { css_schedule_eval_all(); @@ -637,6 +638,13 @@ static void css_process_crw(struct crw *crw0, struct crw *crw1, int overflow) if (crw1) mchk_schid.ssid = (crw1->rsid >> 4) & 3; + if (crw0->erc == CRW_ERC_PMOD) { + sch = get_subchannel_by_schid(mchk_schid); + if (sch) { + css_update_ssd_info(sch); + put_device(&sch->dev); + } + } /* * Since we are always presented with IPI in the CRW, we have to * use stsch() to find out if the subchannel in question has come -- cgit v1.2.3 From aa3a41d009d433dd9775b356b2d70551816f1f3c Mon Sep 17 00:00:00 2001 From: Jan Glauber Date: Wed, 5 Jan 2011 12:47:59 +0100 Subject: [S390] qeth: buffer count imbalance The used buffers counter is not incremented in case of an error so the counter can become negative. Increment the used buffers counter before checking for errors. Signed-off-by: Jan Glauber Signed-off-by: Martin Schwidefsky --- drivers/s390/net/qeth_core_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/net/qeth_core_main.c b/drivers/s390/net/qeth_core_main.c index f65320babf71..29f848bfc12f 100644 --- a/drivers/s390/net/qeth_core_main.c +++ b/drivers/s390/net/qeth_core_main.c @@ -2840,6 +2840,7 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, queue->card->perf_stats.outbound_do_qdio_time += qeth_get_micros() - queue->card->perf_stats.outbound_do_qdio_start_time; + atomic_add(count, &queue->used_buffers); if (rc) { queue->card->stats.tx_errors += count; /* ignore temporary SIGA errors without busy condition */ @@ -2853,7 +2854,6 @@ static void qeth_flush_buffers(struct qeth_qdio_out_q *queue, int index, qeth_schedule_recovery(queue->card); return; } - atomic_add(count, &queue->used_buffers); if (queue->card->options.performance_stats) queue->card->perf_stats.bufs_sent += count; } -- cgit v1.2.3 From 9062014cb60194630272709da82d5879d563865e Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Wed, 5 Jan 2011 12:48:01 +0100 Subject: [S390] cio: reduce memory consumption of itcw structures Any list of indirect data adresses (TIDAL) used by a TCW must not cross a page boundary. The original itcw implementation complies with this restriction by allocating allmost twice as much memory as actually needed, so that in any case there is enough room for the full TIDAL, either above or below the page boundary. This patch implements an alternative method, by using a TTIC TIDAW to connect TIDAL parts below and above a page boundary. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/itcw.c | 62 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 52 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/itcw.c b/drivers/s390/cio/itcw.c index a0ae29564774..358ee16d10a2 100644 --- a/drivers/s390/cio/itcw.c +++ b/drivers/s390/cio/itcw.c @@ -93,6 +93,7 @@ EXPORT_SYMBOL(itcw_get_tcw); size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) { size_t len; + int cross_count; /* Main data. */ len = sizeof(struct itcw); @@ -105,12 +106,27 @@ size_t itcw_calc_size(int intrg, int max_tidaws, int intrg_max_tidaws) /* TSB */ sizeof(struct tsb) + /* TIDAL */ intrg_max_tidaws * sizeof(struct tidaw); } + /* Maximum required alignment padding. */ len += /* Initial TCW */ 63 + /* Interrogate TCCB */ 7; - /* Maximum padding for structures that may not cross 4k boundary. */ - if ((max_tidaws > 0) || (intrg_max_tidaws > 0)) - len += max(max_tidaws, intrg_max_tidaws) * - sizeof(struct tidaw) - 1; + + /* TIDAW lists may not cross a 4k boundary. To cross a + * boundary we need to add a TTIC TIDAW. We need to reserve + * one additional TIDAW for a TTIC that we may need to add due + * to the placement of the data chunk in memory, and a further + * TIDAW for each page boundary that the TIDAW list may cross + * due to it's own size. + */ + if (max_tidaws) { + cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + len += cross_count * sizeof(struct tidaw); + } + if (intrg_max_tidaws) { + cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + len += cross_count * sizeof(struct tidaw); + } return len; } EXPORT_SYMBOL(itcw_calc_size); @@ -165,6 +181,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, void *chunk; addr_t start; addr_t end; + int cross_count; /* Check for 2G limit. */ start = (addr_t) buffer; @@ -177,8 +194,17 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, if (IS_ERR(chunk)) return chunk; itcw = chunk; - itcw->max_tidaws = max_tidaws; - itcw->intrg_max_tidaws = intrg_max_tidaws; + /* allow for TTIC tidaws that may be needed to cross a page boundary */ + cross_count = 0; + if (max_tidaws) + cross_count = 1 + ((max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + itcw->max_tidaws = max_tidaws + cross_count; + cross_count = 0; + if (intrg_max_tidaws) + cross_count = 1 + ((intrg_max_tidaws * sizeof(struct tidaw) - 1) + >> PAGE_SHIFT); + itcw->intrg_max_tidaws = intrg_max_tidaws + cross_count; /* Main TCW. */ chunk = fit_chunk(&start, end, sizeof(struct tcw), 64, 0); if (IS_ERR(chunk)) @@ -198,7 +224,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, /* Data TIDAL. */ if (max_tidaws > 0) { chunk = fit_chunk(&start, end, sizeof(struct tidaw) * - max_tidaws, 16, 1); + itcw->max_tidaws, 16, 0); if (IS_ERR(chunk)) return chunk; tcw_set_data(itcw->tcw, chunk, 1); @@ -206,7 +232,7 @@ struct itcw *itcw_init(void *buffer, size_t size, int op, int intrg, /* Interrogate data TIDAL. */ if (intrg && (intrg_max_tidaws > 0)) { chunk = fit_chunk(&start, end, sizeof(struct tidaw) * - intrg_max_tidaws, 16, 1); + itcw->intrg_max_tidaws, 16, 0); if (IS_ERR(chunk)) return chunk; tcw_set_data(itcw->intrg_tcw, chunk, 1); @@ -283,13 +309,29 @@ EXPORT_SYMBOL(itcw_add_dcw); * the new tidaw on success or -%ENOSPC if the new tidaw would exceed the * available space. * - * Note: the tidaw-list is assumed to be contiguous with no ttics. The - * last-tidaw flag for the last tidaw in the list will be set by itcw_finalize. + * Note: TTIC tidaws are automatically added when needed, so explicitly calling + * this interface with the TTIC flag is not supported. The last-tidaw flag + * for the last tidaw in the list will be set by itcw_finalize. */ struct tidaw *itcw_add_tidaw(struct itcw *itcw, u8 flags, void *addr, u32 count) { + struct tidaw *following; + if (itcw->num_tidaws >= itcw->max_tidaws) return ERR_PTR(-ENOSPC); + /* + * Is the tidaw, which follows the one we are about to fill, on the next + * page? Then we have to insert a TTIC tidaw first, that points to the + * tidaw on the new page. + */ + following = ((struct tidaw *) tcw_get_data(itcw->tcw)) + + itcw->num_tidaws + 1; + if (itcw->num_tidaws && !((unsigned long) following & ~PAGE_MASK)) { + tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, + TIDAW_FLAGS_TTIC, following, 0); + if (itcw->num_tidaws >= itcw->max_tidaws) + return ERR_PTR(-ENOSPC); + } return tcw_add_tidaw(itcw->tcw, itcw->num_tidaws++, flags, addr, count); } EXPORT_SYMBOL(itcw_add_tidaw); -- cgit v1.2.3 From ef19298b406f93af4bb249f0776deb8366e97532 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Wed, 5 Jan 2011 12:48:02 +0100 Subject: [S390] dasd: add High Performance FICON multitrack support Some storage systems support multitrack High Performance FICON requests, which read or write data to more than one track. This patch enables the DASD device driver to generate multitrack High Performance FICON requests. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 4 -- drivers/s390/block/dasd_eckd.c | 158 ++++++++++++++++++++++++++++++----------- drivers/s390/block/dasd_eckd.h | 6 ++ 3 files changed, 122 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index faa7d425cb9c..605f96f154a5 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -745,10 +745,6 @@ struct dasd_ccw_req *dasd_smalloc_request(int magic, int cplength, char *data; int size; - /* Sanity checks */ - BUG_ON(datasize > PAGE_SIZE || - (cplength*sizeof(struct ccw1)) > PAGE_SIZE); - size = (sizeof(struct dasd_ccw_req) + 7L) & -8L; if (cplength > 0) size += cplength * sizeof(struct ccw1); diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index bf61274af3bb..549443af121c 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -1105,6 +1105,37 @@ static void dasd_eckd_validate_server(struct dasd_device *device) "returned rc=%d", private->uid.ssid, rc); } +static u32 get_fcx_max_data(struct dasd_device *device) +{ +#if defined(CONFIG_64BIT) + int tpm, mdc; + int fcx_in_css, fcx_in_gneq, fcx_in_features; + struct dasd_eckd_private *private; + + if (dasd_nofcx) + return 0; + /* is transport mode supported? */ + private = (struct dasd_eckd_private *) device->private; + fcx_in_css = css_general_characteristics.fcx; + fcx_in_gneq = private->gneq->reserved2[7] & 0x04; + fcx_in_features = private->features.feature[40] & 0x80; + tpm = fcx_in_css && fcx_in_gneq && fcx_in_features; + + if (!tpm) + return 0; + + mdc = ccw_device_get_mdc(device->cdev, 0); + if (mdc < 0) { + dev_warn(&device->cdev->dev, "Detecting the maximum supported" + " data size for zHPF requests failed\n"); + return 0; + } else + return mdc * FCX_MAX_DATA_FACTOR; +#else + return 0; +#endif +} + /* * Check device characteristics. * If the device is accessible using ECKD discipline, the device is enabled. @@ -1223,6 +1254,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device) else private->real_cyl = private->rdc_data.no_cyl; + private->fcx_max_data = get_fcx_max_data(device); + readonly = dasd_device_is_ro(device); if (readonly) set_bit(DASD_FLAG_DEVICE_RO, &device->flags); @@ -2326,6 +2359,12 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( struct tidaw *last_tidaw = NULL; int itcw_op; size_t itcw_size; + u8 tidaw_flags; + unsigned int seg_len, part_len, len_to_track_end; + unsigned char new_track; + sector_t recid, trkid; + unsigned int offs; + unsigned int count, count_to_trk_end; basedev = block->base; private = (struct dasd_eckd_private *) basedev->private; @@ -2341,12 +2380,16 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( /* trackbased I/O needs address all memory via TIDAWs, * not just for 64 bit addresses. This allows us to map * each segment directly to one tidaw. + * In the case of write requests, additional tidaws may + * be needed when a segment crosses a track boundary. */ trkcount = last_trk - first_trk + 1; ctidaw = 0; rq_for_each_segment(bv, req, iter) { ++ctidaw; } + if (rq_data_dir(req) == WRITE) + ctidaw += (last_trk - first_trk); /* Allocate the ccw request. */ itcw_size = itcw_calc_size(0, ctidaw, 0); @@ -2354,15 +2397,6 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( if (IS_ERR(cqr)) return cqr; - cqr->cpmode = 1; - cqr->startdev = startdev; - cqr->memdev = startdev; - cqr->block = block; - cqr->expires = 100*HZ; - cqr->buildclk = get_clock(); - cqr->status = DASD_CQR_FILLED; - cqr->retries = 10; - /* transfer length factor: how many bytes to read from the last track */ if (first_trk == last_trk) tlf = last_offs - first_offs + 1; @@ -2371,8 +2405,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( tlf *= blksize; itcw = itcw_init(cqr->data, itcw_size, itcw_op, 0, ctidaw, 0); + if (IS_ERR(itcw)) { + dasd_sfree_request(cqr, startdev); + return ERR_PTR(-EINVAL); + } cqr->cpaddr = itcw_get_tcw(itcw); - if (prepare_itcw(itcw, first_trk, last_trk, cmd, basedev, startdev, first_offs + 1, @@ -2385,26 +2422,64 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( dasd_sfree_request(cqr, startdev); return ERR_PTR(-EAGAIN); } - /* * A tidaw can address 4k of memory, but must not cross page boundaries * We can let the block layer handle this by setting * blk_queue_segment_boundary to page boundaries and * blk_max_segment_size to page size when setting up the request queue. + * For write requests, a TIDAW must not cross track boundaries, because + * we have to set the CBC flag on the last tidaw for each track. */ - rq_for_each_segment(bv, req, iter) { - dst = page_address(bv->bv_page) + bv->bv_offset; - last_tidaw = itcw_add_tidaw(itcw, 0x00, dst, bv->bv_len); - if (IS_ERR(last_tidaw)) - return (struct dasd_ccw_req *)last_tidaw; + if (rq_data_dir(req) == WRITE) { + new_track = 1; + recid = first_rec; + rq_for_each_segment(bv, req, iter) { + dst = page_address(bv->bv_page) + bv->bv_offset; + seg_len = bv->bv_len; + while (seg_len) { + if (new_track) { + trkid = recid; + offs = sector_div(trkid, blk_per_trk); + count_to_trk_end = blk_per_trk - offs; + count = min((last_rec - recid + 1), + (sector_t)count_to_trk_end); + len_to_track_end = count * blksize; + recid += count; + new_track = 0; + } + part_len = min(seg_len, len_to_track_end); + seg_len -= part_len; + len_to_track_end -= part_len; + /* We need to end the tidaw at track end */ + if (!len_to_track_end) { + new_track = 1; + tidaw_flags = TIDAW_FLAGS_INSERT_CBC; + } else + tidaw_flags = 0; + last_tidaw = itcw_add_tidaw(itcw, tidaw_flags, + dst, part_len); + if (IS_ERR(last_tidaw)) + return ERR_PTR(-EINVAL); + dst += part_len; + } + } + } else { + rq_for_each_segment(bv, req, iter) { + dst = page_address(bv->bv_page) + bv->bv_offset; + last_tidaw = itcw_add_tidaw(itcw, 0x00, + dst, bv->bv_len); + if (IS_ERR(last_tidaw)) + return ERR_PTR(-EINVAL); + } } - - last_tidaw->flags |= 0x80; + last_tidaw->flags |= TIDAW_FLAGS_LAST; + last_tidaw->flags &= ~TIDAW_FLAGS_INSERT_CBC; itcw_finalize(itcw); if (blk_noretry_request(req) || block->base->features & DASD_FEATURE_FAILFAST) set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); + cqr->cpmode = 1; cqr->startdev = startdev; cqr->memdev = startdev; cqr->block = block; @@ -2420,11 +2495,9 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, struct dasd_block *block, struct request *req) { - int tpm, cmdrtd, cmdwtd; + int cmdrtd, cmdwtd; int use_prefix; -#if defined(CONFIG_64BIT) - int fcx_in_css, fcx_in_gneq, fcx_in_features; -#endif + int fcx_multitrack; struct dasd_eckd_private *private; struct dasd_device *basedev; sector_t first_rec, last_rec; @@ -2432,6 +2505,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, unsigned int first_offs, last_offs; unsigned int blk_per_trk, blksize; int cdlspecial; + unsigned int data_size; struct dasd_ccw_req *cqr; basedev = block->base; @@ -2450,15 +2524,11 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, last_offs = sector_div(last_trk, blk_per_trk); cdlspecial = (private->uses_cdl && first_rec < 2*blk_per_trk); - /* is transport mode supported? */ -#if defined(CONFIG_64BIT) - fcx_in_css = css_general_characteristics.fcx; - fcx_in_gneq = private->gneq->reserved2[7] & 0x04; - fcx_in_features = private->features.feature[40] & 0x80; - tpm = fcx_in_css && fcx_in_gneq && fcx_in_features; -#else - tpm = 0; -#endif + fcx_multitrack = private->features.feature[40] & 0x20; + data_size = blk_rq_bytes(req); + /* tpm write request add CBC data on each track boundary */ + if (rq_data_dir(req) == WRITE) + data_size += (last_trk - first_trk) * 4; /* is read track data and write track data in command mode supported? */ cmdrtd = private->features.feature[9] & 0x20; @@ -2468,13 +2538,15 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, cqr = NULL; if (cdlspecial || dasd_page_cache) { /* do nothing, just fall through to the cmd mode single case */ - } else if (!dasd_nofcx && tpm && (first_trk == last_trk)) { + } else if ((data_size <= private->fcx_max_data) + && (fcx_multitrack || (first_trk == last_trk))) { cqr = dasd_eckd_build_cp_tpm_track(startdev, block, req, first_rec, last_rec, first_trk, last_trk, first_offs, last_offs, blk_per_trk, blksize); - if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) + if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && + (PTR_ERR(cqr) != -ENOMEM)) cqr = NULL; } else if (use_prefix && (((rq_data_dir(req) == READ) && cmdrtd) || @@ -2484,7 +2556,8 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, first_trk, last_trk, first_offs, last_offs, blk_per_trk, blksize); - if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) + if (IS_ERR(cqr) && (PTR_ERR(cqr) != -EAGAIN) && + (PTR_ERR(cqr) != -ENOMEM)) cqr = NULL; } if (!cqr) @@ -3279,10 +3352,8 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, { char *page; int len, sl, sct, residual; - struct tsb *tsb; - u8 *sense; - + u8 *sense, *rcq; page = (char *) get_zeroed_page(GFP_ATOMIC); if (page == NULL) { @@ -3348,12 +3419,15 @@ static void dasd_eckd_dump_sense_tcw(struct dasd_device *device, case 2: /* ts_ddpc */ len += sprintf(page + len, KERN_ERR PRINTK_HEADER " tsb->tsa.ddpc.rc %d\n", tsb->tsa.ddpc.rc); - len += sprintf(page + len, KERN_ERR PRINTK_HEADER - " tsb->tsa.ddpc.rcq: "); - for (sl = 0; sl < 16; sl++) { + for (sl = 0; sl < 2; sl++) { + len += sprintf(page + len, + KERN_ERR PRINTK_HEADER + " tsb->tsa.ddpc.rcq %2d-%2d: ", + (8 * sl), ((8 * sl) + 7)); + rcq = tsb->tsa.ddpc.rcq; for (sct = 0; sct < 8; sct++) { len += sprintf(page + len, " %02x", - tsb->tsa.ddpc.rcq[sl]); + rcq[8 * sl + sct]); } len += sprintf(page + len, "\n"); } @@ -3573,7 +3647,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .owner = THIS_MODULE, .name = "ECKD", .ebcname = "ECKD", - .max_blocks = 240, + .max_blocks = 190, .check_device = dasd_eckd_check_characteristics, .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 12097c24f2f5..2150aed541be 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -57,6 +57,10 @@ */ #define LV_COMPAT_CYL 0xFFFE + +#define FCX_MAX_DATA_FACTOR 65536 + + /***************************************************************************** * SECTION: Type Definitions ****************************************************************************/ @@ -455,6 +459,8 @@ struct dasd_eckd_private { struct alias_pav_group *pavgroup; struct alias_lcu *lcu; int count; + + u32 fcx_max_data; }; -- cgit v1.2.3 From a4d26c6aeceea330ee5e0fb6b017d57e3b252d29 Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Wed, 5 Jan 2011 12:48:03 +0100 Subject: [S390] dasd: do path verification for paths added at runtime When a new path is added at runtime, the CIO layer will call the drivers path_event callback. The DASD device driver uses this callback to trigger a path verification for the new path. The driver will use only those paths for I/O, which have been successfully verified. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 202 +++++++++++++++++++++------- drivers/s390/block/dasd_3990_erp.c | 16 +-- drivers/s390/block/dasd_devmap.c | 1 + drivers/s390/block/dasd_diag.c | 1 + drivers/s390/block/dasd_eckd.c | 261 +++++++++++++++++++++++++++++++------ drivers/s390/block/dasd_eckd.h | 9 +- drivers/s390/block/dasd_erp.c | 3 +- drivers/s390/block/dasd_fba.c | 3 + drivers/s390/block/dasd_int.h | 22 ++++ 9 files changed, 418 insertions(+), 100 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 605f96f154a5..8f2067bc88c0 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -913,6 +913,11 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) cqr->startclk = get_clock(); cqr->starttime = jiffies; cqr->retries--; + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { + cqr->lpm &= device->path_data.opm; + if (!cqr->lpm) + cqr->lpm = device->path_data.opm; + } if (cqr->cpmode == 1) { rc = ccw_device_tm_start(device->cdev, cqr->cpaddr, (long) cqr, cqr->lpm); @@ -925,35 +930,53 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) cqr->status = DASD_CQR_IN_IO; break; case -EBUSY: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: device busy, retry later"); break; case -ETIMEDOUT: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: request timeout, retry later"); break; case -EACCES: - /* -EACCES indicates that the request used only a - * subset of the available pathes and all these - * pathes are gone. - * Do a retry with all available pathes. + /* -EACCES indicates that the request used only a subset of the + * available paths and all these paths are gone. If the lpm of + * this request was only a subset of the opm (e.g. the ppm) then + * we just do a retry with all available paths. + * If we already use the full opm, something is amiss, and we + * need a full path verification. */ - cqr->lpm = LPM_ANYPATH; - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", - "start_IO: selected pathes gone," - " retry on all pathes"); + if (test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) { + DBF_DEV_EVENT(DBF_WARNING, device, + "start_IO: selected paths gone (%x)", + cqr->lpm); + } else if (cqr->lpm != device->path_data.opm) { + cqr->lpm = device->path_data.opm; + DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + "start_IO: selected paths gone," + " retry on all paths"); + } else { + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "start_IO: all paths in opm gone," + " do path verification"); + dasd_generic_last_path_gone(device); + device->path_data.opm = 0; + device->path_data.ppm = 0; + device->path_data.npm = 0; + device->path_data.tbvpm = + ccw_device_get_path_mask(device->cdev); + } break; case -ENODEV: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: -ENODEV device gone, retry"); break; case -EIO: - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: -EIO device gone, retry"); break; case -EINVAL: /* most likely caused in power management context */ - DBF_DEV_EVENT(DBF_DEBUG, device, "%s", + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "start_IO: -EINVAL device currently " "not accessible"); break; @@ -1175,12 +1198,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, */ if (!test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags) && cqr->retries > 0) { - if (cqr->lpm == LPM_ANYPATH) + if (cqr->lpm == device->path_data.opm) DBF_DEV_EVENT(DBF_DEBUG, device, "default ERP in fastpath " "(%i retries left)", cqr->retries); - cqr->lpm = LPM_ANYPATH; + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) + cqr->lpm = device->path_data.opm; cqr->status = DASD_CQR_QUEUED; next = cqr; } else @@ -1364,8 +1388,14 @@ static void __dasd_device_start_head(struct dasd_device *device) cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); if (cqr->status != DASD_CQR_QUEUED) return; - /* when device is stopped, return request to previous layer */ - if (device->stopped) { + /* when device is stopped, return request to previous layer + * exception: only the disconnect or unresumed bits are set and the + * cqr is a path verification request + */ + if (device->stopped && + !(!(device->stopped & ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM)) + && test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))) { + cqr->intrc = -EAGAIN; cqr->status = DASD_CQR_CLEARED; dasd_schedule_device_bh(device); return; @@ -1381,6 +1411,23 @@ static void __dasd_device_start_head(struct dasd_device *device) dasd_device_set_timer(device, 50); } +static void __dasd_device_check_path_events(struct dasd_device *device) +{ + int rc; + + if (device->path_data.tbvpm) { + if (device->stopped & ~(DASD_STOPPED_DC_WAIT | + DASD_UNRESUMED_PM)) + return; + rc = device->discipline->verify_path( + device, device->path_data.tbvpm); + if (rc) + dasd_device_set_timer(device, 50); + else + device->path_data.tbvpm = 0; + } +}; + /* * Go through all request on the dasd_device request queue, * terminate them on the cdev if necessary, and return them to the @@ -1455,6 +1502,7 @@ static void dasd_device_tasklet(struct dasd_device *device) __dasd_device_check_expire(device); /* find final requests on ccw queue */ __dasd_device_process_ccw_queue(device, &final_queue); + __dasd_device_check_path_events(device); 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); @@ -2586,10 +2634,53 @@ int dasd_generic_set_offline(struct ccw_device *cdev) return 0; } +int dasd_generic_last_path_gone(struct dasd_device *device) +{ + struct dasd_ccw_req *cqr; + + dev_warn(&device->cdev->dev, "No operational channel path is left " + "for the device\n"); + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "last path gone"); + /* First of all call extended error reporting. */ + dasd_eer_write(device, NULL, DASD_EER_NOPATH); + + if (device->state < DASD_STATE_BASIC) + return 0; + /* 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_CLEAR_PENDING)) { + cqr->status = DASD_CQR_QUEUED; + cqr->retries++; + } + dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT); + dasd_device_clear_timer(device); + dasd_schedule_device_bh(device); + return 1; +} +EXPORT_SYMBOL_GPL(dasd_generic_last_path_gone); + +int dasd_generic_path_operational(struct dasd_device *device) +{ + dev_info(&device->cdev->dev, "A channel path to the device has become " + "operational\n"); + DBF_DEV_EVENT(DBF_WARNING, device, "%s", "path operational"); + dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT); + if (device->stopped & DASD_UNRESUMED_PM) { + dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM); + dasd_restore_device(device); + return 1; + } + dasd_schedule_device_bh(device); + if (device->block) + dasd_schedule_block_bh(device->block); + return 1; +} +EXPORT_SYMBOL_GPL(dasd_generic_path_operational); + int dasd_generic_notify(struct ccw_device *cdev, int event) { struct dasd_device *device; - struct dasd_ccw_req *cqr; int ret; device = dasd_device_from_cdev_locked(cdev); @@ -2600,41 +2691,64 @@ int dasd_generic_notify(struct ccw_device *cdev, int event) case CIO_GONE: case CIO_BOXED: case CIO_NO_PATH: - /* First of all call extended error reporting. */ - dasd_eer_write(device, NULL, DASD_EER_NOPATH); - - 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++; - } - dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT); - dasd_device_clear_timer(device); - dasd_schedule_device_bh(device); - ret = 1; + device->path_data.opm = 0; + device->path_data.ppm = 0; + device->path_data.npm = 0; + ret = dasd_generic_last_path_gone(device); break; case CIO_OPER: - /* FIXME: add a sanity check. */ - dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT); - if (device->stopped & DASD_UNRESUMED_PM) { - dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM); - dasd_restore_device(device); - ret = 1; - break; - } - dasd_schedule_device_bh(device); - if (device->block) - dasd_schedule_block_bh(device->block); ret = 1; + if (device->path_data.opm) + ret = dasd_generic_path_operational(device); break; } dasd_put_device(device); return ret; } +void dasd_generic_path_event(struct ccw_device *cdev, int *path_event) +{ + int chp; + __u8 oldopm, eventlpm; + struct dasd_device *device; + + device = dasd_device_from_cdev_locked(cdev); + if (IS_ERR(device)) + return; + for (chp = 0; chp < 8; chp++) { + eventlpm = 0x80 >> chp; + if (path_event[chp] & PE_PATH_GONE) { + oldopm = device->path_data.opm; + device->path_data.opm &= ~eventlpm; + device->path_data.ppm &= ~eventlpm; + device->path_data.npm &= ~eventlpm; + if (oldopm && !device->path_data.opm) + dasd_generic_last_path_gone(device); + } + if (path_event[chp] & PE_PATH_AVAILABLE) { + device->path_data.opm &= ~eventlpm; + device->path_data.ppm &= ~eventlpm; + device->path_data.npm &= ~eventlpm; + device->path_data.tbvpm |= eventlpm; + dasd_schedule_device_bh(device); + } + } + dasd_put_device(device); +} +EXPORT_SYMBOL_GPL(dasd_generic_path_event); + +int dasd_generic_verify_path(struct dasd_device *device, __u8 lpm) +{ + if (!device->path_data.opm && lpm) { + device->path_data.opm = lpm; + dasd_generic_path_operational(device); + } else + device->path_data.opm |= lpm; + return 0; +} +EXPORT_SYMBOL_GPL(dasd_generic_verify_path); + + int dasd_generic_pm_freeze(struct ccw_device *cdev) { struct dasd_ccw_req *cqr, *n; diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c index 968c76cf7127..1654a24817be 100644 --- a/drivers/s390/block/dasd_3990_erp.c +++ b/drivers/s390/block/dasd_3990_erp.c @@ -152,9 +152,9 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp) 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); + erp->lpm = device->path_data.opm & + ~(erp->irb.esw.esw0.sublog.lpum); else erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum); @@ -270,10 +270,11 @@ static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp) { erp->function = dasd_3990_erp_action_1; dasd_3990_erp_alternate_path(erp); - if (erp->status == DASD_CQR_FAILED) { + if (erp->status == DASD_CQR_FAILED && + !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) { erp->status = DASD_CQR_FILLED; erp->retries = 10; - erp->lpm = LPM_ANYPATH; + erp->lpm = erp->startdev->path_data.opm; erp->function = dasd_3990_erp_action_1_sec; } return erp; @@ -1907,15 +1908,14 @@ dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense) static void dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense) { - if (sense[25] & DASD_SENSE_BIT_3) { dasd_3990_erp_alternate_path(erp); - if (erp->status == DASD_CQR_FAILED) { + if (erp->status == DASD_CQR_FAILED && + !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) { /* reset the lpm and the status to be able to * try further actions. */ - - erp->lpm = 0; + erp->lpm = erp->startdev->path_data.opm; erp->status = DASD_CQR_NEED_ERP; } } diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 8d41f3ed38d7..0001df8ad3e6 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -639,6 +639,7 @@ dasd_put_device_wake(struct dasd_device *device) { wake_up(&dasd_delete_wq); } +EXPORT_SYMBOL_GPL(dasd_put_device_wake); /* * Return dasd_device structure associated with cdev. diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index a3a5db58df18..29143eda9dd9 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -619,6 +619,7 @@ static struct dasd_discipline dasd_diag_discipline = { .ebcname = "DIAG", .max_blocks = DIAG_MAX_BLOCKS, .check_device = dasd_diag_check_device, + .verify_path = dasd_generic_verify_path, .fill_geometry = dasd_diag_fill_geometry, .start_IO = dasd_start_diag, .term_IO = dasd_diag_term_IO, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 549443af121c..a1ebf5722ae5 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -90,6 +90,18 @@ static struct { } *dasd_reserve_req; static DEFINE_MUTEX(dasd_reserve_mutex); +/* definitions for the path verification worker */ +struct path_verification_work_data { + struct work_struct worker; + struct dasd_device *device; + struct dasd_ccw_req cqr; + struct ccw1 ccw; + __u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE]; + int isglobal; + __u8 tbvpm; +}; +static struct path_verification_work_data *path_verification_worker; +static DEFINE_MUTEX(dasd_path_verification_mutex); /* initial attempt at a probe function. this can be simplified once * the other detection code is gone */ @@ -755,26 +767,27 @@ static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid) return -EINVAL; } -static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, - void *rcd_buffer, - struct ciw *ciw, __u8 lpm) +static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device, + struct dasd_ccw_req *cqr, + __u8 *rcd_buffer, + __u8 lpm) { - struct dasd_ccw_req *cqr; struct ccw1 *ccw; - - cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, ciw->count, - device); - - if (IS_ERR(cqr)) { - DBF_DEV_EVENT(DBF_WARNING, device, "%s", - "Could not allocate RCD request"); - return cqr; - } + /* + * buffer has to start with EBCDIC "V1.0" to show + * support for virtual device SNEQ + */ + rcd_buffer[0] = 0xE5; + rcd_buffer[1] = 0xF1; + rcd_buffer[2] = 0x4B; + rcd_buffer[3] = 0xF0; ccw = cqr->cpaddr; - ccw->cmd_code = ciw->cmd; + ccw->cmd_code = DASD_ECKD_CCW_RCD; + ccw->flags = 0; ccw->cda = (__u32)(addr_t)rcd_buffer; - ccw->count = ciw->count; + ccw->count = DASD_ECKD_RCD_DATA_SIZE; + cqr->magic = DASD_ECKD_MAGIC; cqr->startdev = device; cqr->memdev = device; @@ -784,7 +797,29 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device, cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; - return cqr; + set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags); +} + +static int dasd_eckd_read_conf_immediately(struct dasd_device *device, + struct dasd_ccw_req *cqr, + __u8 *rcd_buffer, + __u8 lpm) +{ + struct ciw *ciw; + int rc; + /* + * sanity check: scan for RCD command in extended SenseID data + * some devices do not support RCD + */ + ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); + if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) + return -EOPNOTSUPP; + + dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm); + clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + cqr->retries = 5; + rc = dasd_sleep_on_immediatly(cqr); + return rc; } static int dasd_eckd_read_conf_lpm(struct dasd_device *device, @@ -797,32 +832,29 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, struct dasd_ccw_req *cqr; /* - * scan for RCD command in extended SenseID data + * sanity check: scan for RCD command in extended SenseID data + * some devices do not support RCD */ ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD); - if (!ciw || ciw->cmd == 0) { + if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) { ret = -EOPNOTSUPP; goto out_error; } - rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA); + rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA); if (!rcd_buf) { ret = -ENOMEM; goto out_error; } - - /* - * buffer has to start with EBCDIC "V1.0" to show - * support for virtual device SNEQ - */ - rcd_buf[0] = 0xE5; - rcd_buf[1] = 0xF1; - rcd_buf[2] = 0x4B; - rcd_buf[3] = 0xF0; - cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm); + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, + 0, /* use rcd_buf as data ara */ + device); if (IS_ERR(cqr)) { - ret = PTR_ERR(cqr); + DBF_DEV_EVENT(DBF_WARNING, device, "%s", + "Could not allocate RCD request"); + ret = -ENOMEM; goto out_error; } + dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm); ret = dasd_sleep_on(cqr); /* * on success we update the user input parms @@ -831,7 +863,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device, if (ret) goto out_error; - *rcd_buffer_size = ciw->count; + *rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE; *rcd_buffer = rcd_buf; return 0; out_error: @@ -901,18 +933,18 @@ static int dasd_eckd_read_conf(struct dasd_device *device) void *conf_data; int conf_len, conf_data_saved; int rc; - __u8 lpm; + __u8 lpm, opm; struct dasd_eckd_private *private; - struct dasd_eckd_path *path_data; + struct dasd_path *path_data; private = (struct dasd_eckd_private *) device->private; - path_data = (struct dasd_eckd_path *) &private->path_data; - path_data->opm = ccw_device_get_path_mask(device->cdev); + path_data = &device->path_data; + opm = ccw_device_get_path_mask(device->cdev); lpm = 0x80; conf_data_saved = 0; /* get configuration data per operational path */ for (lpm = 0x80; lpm; lpm>>= 1) { - if (lpm & path_data->opm){ + if (lpm & opm) { rc = dasd_eckd_read_conf_lpm(device, &conf_data, &conf_len, lpm); if (rc && rc != -EOPNOTSUPP) { /* -EOPNOTSUPP is ok */ @@ -925,6 +957,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device) DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", "No configuration data " "retrieved"); + /* no further analysis possible */ + path_data->opm |= lpm; continue; /* no error */ } /* save first valid configuration data */ @@ -948,6 +982,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device) path_data->ppm |= lpm; break; } + path_data->opm |= lpm; if (conf_data != private->conf_data) kfree(conf_data); } @@ -955,6 +990,140 @@ static int dasd_eckd_read_conf(struct dasd_device *device) return 0; } +static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm) +{ + struct dasd_eckd_private *private; + int mdc; + u32 fcx_max_data; + + private = (struct dasd_eckd_private *) device->private; + if (private->fcx_max_data) { + mdc = ccw_device_get_mdc(device->cdev, lpm); + if ((mdc < 0)) { + dev_warn(&device->cdev->dev, + "Detecting the maximum data size for zHPF " + "requests failed (rc=%d) for a new path %x\n", + mdc, lpm); + return mdc; + } + fcx_max_data = mdc * FCX_MAX_DATA_FACTOR; + if (fcx_max_data < private->fcx_max_data) { + dev_warn(&device->cdev->dev, + "The maximum data size for zHPF requests %u " + "on a new path %x is below the active maximum " + "%u\n", fcx_max_data, lpm, + private->fcx_max_data); + return -EACCES; + } + } + return 0; +} + +static void do_path_verification_work(struct work_struct *work) +{ + struct path_verification_work_data *data; + struct dasd_device *device; + __u8 lpm, opm, npm, ppm, epm; + unsigned long flags; + int rc; + + data = container_of(work, struct path_verification_work_data, worker); + device = data->device; + + opm = 0; + npm = 0; + ppm = 0; + epm = 0; + for (lpm = 0x80; lpm; lpm >>= 1) { + if (lpm & data->tbvpm) { + memset(data->rcd_buffer, 0, sizeof(data->rcd_buffer)); + memset(&data->cqr, 0, sizeof(data->cqr)); + data->cqr.cpaddr = &data->ccw; + rc = dasd_eckd_read_conf_immediately(device, &data->cqr, + data->rcd_buffer, + lpm); + if (!rc) { + switch (dasd_eckd_path_access(data->rcd_buffer, + DASD_ECKD_RCD_DATA_SIZE)) { + case 0x02: + npm |= lpm; + break; + case 0x03: + ppm |= lpm; + break; + } + opm |= lpm; + } else if (rc == -EOPNOTSUPP) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "path verification: No configuration " + "data retrieved"); + opm |= lpm; + } else if (rc == -EAGAIN) { + DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s", + "path verification: device is stopped," + " try again later"); + epm |= lpm; + } else { + dev_warn(&device->cdev->dev, + "Reading device feature codes failed " + "(rc=%d) for new path %x\n", rc, lpm); + continue; + } + if (verify_fcx_max_data(device, lpm)) { + opm &= ~lpm; + npm &= ~lpm; + ppm &= ~lpm; + } + } + } + /* + * There is a small chance that a path is lost again between + * above path verification and the following modification of + * the device opm mask. We could avoid that race here by using + * yet another path mask, but we rather deal with this unlikely + * situation in dasd_start_IO. + */ + spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); + if (!device->path_data.opm && opm) { + device->path_data.opm = opm; + dasd_generic_path_operational(device); + } else + device->path_data.opm |= opm; + device->path_data.npm |= npm; + device->path_data.ppm |= ppm; + device->path_data.tbvpm |= epm; + spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); + + dasd_put_device(device); + if (data->isglobal) + mutex_unlock(&dasd_path_verification_mutex); + else + kfree(data); +} + +static int dasd_eckd_verify_path(struct dasd_device *device, __u8 lpm) +{ + struct path_verification_work_data *data; + + data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA); + if (!data) { + if (mutex_trylock(&dasd_path_verification_mutex)) { + data = path_verification_worker; + data->isglobal = 1; + } else + return -ENOMEM; + } else { + memset(data, 0, sizeof(*data)); + data->isglobal = 0; + } + INIT_WORK(&data->worker, do_path_verification_work); + dasd_get_device(device); + data->device = device; + data->tbvpm = lpm; + schedule_work(&data->worker); + return 0; +} + static int dasd_eckd_read_features(struct dasd_device *device) { struct dasd_psf_prssd_data *prssdp; @@ -1749,6 +1918,7 @@ static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr) if (cqr->block && (cqr->startdev != cqr->block->base)) { dasd_eckd_reset_ccw_to_base_io(cqr); cqr->startdev = cqr->block->base; + cqr->lpm = cqr->block->base->path_data.opm; } }; @@ -2017,7 +2187,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( cqr->memdev = startdev; cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ - cqr->lpm = private->path_data.ppm; + cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -2194,7 +2364,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track( cqr->memdev = startdev; cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ - cqr->lpm = private->path_data.ppm; + cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -2484,7 +2654,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track( cqr->memdev = startdev; cqr->block = block; cqr->expires = startdev->default_expires * HZ; /* default 5 minutes */ - cqr->lpm = private->path_data.ppm; + cqr->lpm = startdev->path_data.ppm; cqr->retries = 256; cqr->buildclk = get_clock(); cqr->status = DASD_CQR_FILLED; @@ -3624,6 +3794,7 @@ static struct ccw_driver dasd_eckd_driver = { .set_offline = dasd_generic_set_offline, .set_online = dasd_eckd_set_online, .notify = dasd_generic_notify, + .path_event = dasd_generic_path_event, .freeze = dasd_generic_pm_freeze, .thaw = dasd_generic_restore_device, .restore = dasd_generic_restore_device, @@ -3651,6 +3822,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .check_device = dasd_eckd_check_characteristics, .uncheck_device = dasd_eckd_uncheck_device, .do_analysis = dasd_eckd_do_analysis, + .verify_path = dasd_eckd_verify_path, .ready_to_online = dasd_eckd_ready_to_online, .online_to_ready = dasd_eckd_online_to_ready, .fill_geometry = dasd_eckd_fill_geometry, @@ -3683,11 +3855,19 @@ dasd_eckd_init(void) GFP_KERNEL | GFP_DMA); if (!dasd_reserve_req) return -ENOMEM; + path_verification_worker = kmalloc(sizeof(*path_verification_worker), + GFP_KERNEL | GFP_DMA); + if (!path_verification_worker) { + kfree(dasd_reserve_req); + return -ENOMEM; + } ret = ccw_driver_register(&dasd_eckd_driver); if (!ret) wait_for_device_probe(); - else + else { + kfree(path_verification_worker); kfree(dasd_reserve_req); + } return ret; } @@ -3695,6 +3875,7 @@ static void __exit dasd_eckd_cleanup(void) { ccw_driver_unregister(&dasd_eckd_driver); + kfree(path_verification_worker); kfree(dasd_reserve_req); } diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 2150aed541be..5051f374cbcb 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -45,6 +45,7 @@ #define DASD_ECKD_CCW_PFX 0xE7 #define DASD_ECKD_CCW_PFX_READ 0xEA #define DASD_ECKD_CCW_RSCK 0xF9 +#define DASD_ECKD_CCW_RCD 0xFA /* * Perform Subsystem Function / Sub-Orders @@ -59,6 +60,7 @@ #define FCX_MAX_DATA_FACTOR 65536 +#define DASD_ECKD_RCD_DATA_SIZE 256 /***************************************************************************** @@ -335,12 +337,6 @@ struct dasd_gneq { __u8 reserved2[22]; } __attribute__ ((packed)); -struct dasd_eckd_path { - __u8 opm; - __u8 ppm; - __u8 npm; -}; - struct dasd_rssd_features { char feature[256]; } __attribute__((packed)); @@ -446,7 +442,6 @@ struct dasd_eckd_private { struct vd_sneq *vdsneq; struct dasd_gneq *gneq; - struct dasd_eckd_path path_data; struct eckd_count count_area[5]; int init_cqr_status; int uses_cdl; diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c index 7656384a811d..0eafe2e421e7 100644 --- a/drivers/s390/block/dasd_erp.c +++ b/drivers/s390/block/dasd_erp.c @@ -96,7 +96,8 @@ dasd_default_erp_action(struct dasd_ccw_req *cqr) DBF_DEV_EVENT(DBF_DEBUG, device, "default ERP called (%i retries left)", cqr->retries); - cqr->lpm = LPM_ANYPATH; + if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) + cqr->lpm = device->path_data.opm; cqr->status = DASD_CQR_FILLED; } else { pr_err("%s: default ERP has run out of retries and failed\n", diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index bec5486e0e6d..86bacda2c5f6 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -73,6 +73,7 @@ static struct ccw_driver dasd_fba_driver = { .set_offline = dasd_generic_set_offline, .set_online = dasd_fba_set_online, .notify = dasd_generic_notify, + .path_event = dasd_generic_path_event, .freeze = dasd_generic_pm_freeze, .thaw = dasd_generic_restore_device, .restore = dasd_generic_restore_device, @@ -164,6 +165,7 @@ dasd_fba_check_characteristics(struct dasd_device *device) } device->default_expires = DASD_EXPIRES; + device->path_data.opm = LPM_ANYPATH; readonly = dasd_device_is_ro(device); if (readonly) @@ -596,6 +598,7 @@ static struct dasd_discipline dasd_fba_discipline = { .max_blocks = 96, .check_device = dasd_fba_check_characteristics, .do_analysis = dasd_fba_do_analysis, + .verify_path = dasd_generic_verify_path, .fill_geometry = dasd_fba_fill_geometry, .start_IO = dasd_start_IO, .term_IO = dasd_term_IO, diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 500678d7116c..ba038ef57606 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -231,6 +231,7 @@ struct dasd_ccw_req { /* per dasd_ccw_req flags */ #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ #define DASD_CQR_FLAGS_FAILFAST 1 /* FAILFAST */ +#define DASD_CQR_VERIFY_PATH 2 /* path verification request */ /* Signature for error recovery functions. */ typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *); @@ -286,6 +287,14 @@ struct dasd_discipline { */ int (*do_analysis) (struct dasd_block *); + /* + * This function is called, when new paths become available. + * Disciplins may use this callback to do necessary setup work, + * e.g. verify that new path is compatible with the current + * configuration. + */ + int (*verify_path)(struct dasd_device *, __u8); + /* * Last things to do when a device is set online, and first things * when it is set offline. @@ -362,6 +371,13 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer; #define DASD_EER_STATECHANGE 3 #define DASD_EER_PPRCSUSPEND 4 +struct dasd_path { + __u8 opm; + __u8 tbvpm; + __u8 ppm; + __u8 npm; +}; + struct dasd_device { /* Block device stuff. */ struct dasd_block *block; @@ -377,6 +393,7 @@ struct dasd_device { struct dasd_discipline *discipline; struct dasd_discipline *base_discipline; char *private; + struct dasd_path path_data; /* Device state and target state. */ int state, target; @@ -620,10 +637,15 @@ 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); +int dasd_generic_last_path_gone(struct dasd_device *); +int dasd_generic_path_operational(struct dasd_device *); + void dasd_generic_handle_state_change(struct dasd_device *); int dasd_generic_pm_freeze(struct ccw_device *); int dasd_generic_restore_device(struct ccw_device *); enum uc_todo dasd_generic_uc_handler(struct ccw_device *, struct irb *); +void dasd_generic_path_event(struct ccw_device *, int *); +int dasd_generic_verify_path(struct dasd_device *, __u8); int dasd_generic_read_dev_chars(struct dasd_device *, int, void *, int); char *dasd_get_sense(struct irb *); -- cgit v1.2.3 From 5a27e60dec59a95bd7f8ae9a19ae2ede4f76395b Mon Sep 17 00:00:00 2001 From: Stefan Weinhuber Date: Wed, 5 Jan 2011 12:48:04 +0100 Subject: [S390] dasd: Improve handling of stolen DASD reservation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a DASD device has been reserved by a Linux system, and later this reservation is ‘stolen’ by a second system by means of an unconditional reserve, then the first system receives a notification about this fact. With this patch such an event can be either ignored, as before, or it can be used to let the device fail all I/O request, so that the device will not block anymore. Signed-off-by: Stefan Weinhuber Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/dasd.h | 1 + drivers/s390/block/dasd.c | 75 +++++++++++++++++------------- drivers/s390/block/dasd_devmap.c | 99 ++++++++++++++++++++++++++++++++++++++++ drivers/s390/block/dasd_eckd.c | 51 ++++++++++++--------- drivers/s390/block/dasd_eer.c | 1 + drivers/s390/block/dasd_fba.c | 18 ++------ drivers/s390/block/dasd_int.h | 13 ++++-- 7 files changed, 189 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/dasd.h b/arch/s390/include/asm/dasd.h index b604a9186f8e..47fcdada5d25 100644 --- a/arch/s390/include/asm/dasd.h +++ b/arch/s390/include/asm/dasd.h @@ -80,6 +80,7 @@ typedef struct dasd_information2_t { #define DASD_FEATURE_INITIAL_ONLINE 0x04 #define DASD_FEATURE_ERPLOG 0x08 #define DASD_FEATURE_FAILFAST 0x10 +#define DASD_FEATURE_FAILONSLCK 0x20 #define DASD_PARTN_BITS 2 diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 8f2067bc88c0..f16afe74464f 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -902,6 +902,16 @@ int dasd_start_IO(struct dasd_ccw_req *cqr) return rc; } device = (struct dasd_device *) cqr->startdev; + if (((cqr->block && + test_bit(DASD_FLAG_LOCK_STOLEN, &cqr->block->base->flags)) || + test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags)) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + DBF_DEV_EVENT(DBF_DEBUG, device, "start_IO: return request %p " + "because of stolen lock", cqr); + cqr->status = DASD_CQR_ERROR; + cqr->intrc = -EPERM; + return -EPERM; + } if (cqr->retries < 0) { /* internal error 14 - start_IO run out of retries */ sprintf(errorstring, "14 %p", cqr); @@ -1115,16 +1125,11 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, } now = get_clock(); - - /* check for unsolicited interrupts */ cqr = (struct dasd_ccw_req *) intparm; - if (!cqr || ((scsw_cc(&irb->scsw) == 1) && - (scsw_fctl(&irb->scsw) & SCSW_FCTL_START_FUNC) && - ((scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND) || - (scsw_stctl(&irb->scsw) == (SCSW_STCTL_STATUS_PEND | - SCSW_STCTL_ALERT_STATUS))))) { - if (cqr && cqr->status == DASD_CQR_IN_IO) - cqr->status = DASD_CQR_QUEUED; + /* check for conditions that should be handled immediately */ + if (!cqr || + !(scsw_dstat(&irb->scsw) == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) && + scsw_cstat(&irb->scsw) == 0)) { if (cqr) memcpy(&cqr->irb, irb, sizeof(*irb)); device = dasd_device_from_cdev_locked(cdev); @@ -1135,17 +1140,14 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, dasd_put_device(device); return; } - device->discipline->dump_sense_dbf(device, irb, - "unsolicited"); - if ((device->features & DASD_FEATURE_ERPLOG)) - device->discipline->dump_sense(device, cqr, - irb); - dasd_device_clear_timer(device); - device->discipline->handle_unsolicited_interrupt(device, - irb); + device->discipline->dump_sense_dbf(device, irb, "int"); + if (device->features & DASD_FEATURE_ERPLOG) + device->discipline->dump_sense(device, cqr, irb); + device->discipline->check_for_device_change(device, cqr, irb); dasd_put_device(device); - return; } + if (!cqr) + return; device = (struct dasd_device *) cqr->startdev; if (!device || @@ -1185,13 +1187,6 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, struct dasd_ccw_req, devlist); } } else { /* error */ - memcpy(&cqr->irb, irb, sizeof(struct irb)); - /* log sense for every failed I/O to s390 debugfeature */ - dasd_log_sense_dbf(cqr, irb); - if (device->features & DASD_FEATURE_ERPLOG) { - dasd_log_sense(cqr, irb); - } - /* * If we don't want complex ERP for this request, then just * reset this and retry it in the fastpath @@ -1232,13 +1227,13 @@ enum uc_todo dasd_generic_uc_handler(struct ccw_device *cdev, struct irb *irb) goto out; if (test_bit(DASD_FLAG_OFFLINE, &device->flags) || device->state != device->target || - !device->discipline->handle_unsolicited_interrupt){ + !device->discipline->check_for_device_change){ dasd_put_device(device); goto out; } - - dasd_device_clear_timer(device); - device->discipline->handle_unsolicited_interrupt(device, irb); + if (device->discipline->dump_sense_dbf) + device->discipline->dump_sense_dbf(device, irb, "uc"); + device->discipline->check_for_device_change(device, NULL, irb); dasd_put_device(device); out: return UC_TODO_RETRY; @@ -1659,7 +1654,12 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) continue; if (cqr->status != DASD_CQR_FILLED) /* could be failed */ continue; - + if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + continue; + } /* Non-temporary stop condition will trigger fail fast */ if (device->stopped & ~DASD_STOPPED_PENDING && test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && @@ -1667,7 +1667,6 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible) cqr->status = DASD_CQR_FAILED; continue; } - /* Don't try to start requests if device is stopped */ if (interruptible) { rc = wait_event_interruptible( @@ -1752,13 +1751,18 @@ int dasd_sleep_on_immediatly(struct dasd_ccw_req *cqr) int rc; device = cqr->startdev; + if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + return -EIO; + } spin_lock_irq(get_ccwdev_lock(device->cdev)); rc = _dasd_term_running_cqr(device); if (rc) { spin_unlock_irq(get_ccwdev_lock(device->cdev)); return rc; } - cqr->callback = dasd_wakeup_cb; cqr->callback_data = DASD_SLEEPON_START_TAG; cqr->status = DASD_CQR_QUEUED; @@ -2062,6 +2066,13 @@ static void __dasd_block_start_head(struct dasd_block *block) list_for_each_entry(cqr, &block->ccw_queue, blocklist) { if (cqr->status != DASD_CQR_FILLED) continue; + if (test_bit(DASD_FLAG_LOCK_STOLEN, &block->base->flags) && + !test_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags)) { + cqr->status = DASD_CQR_FAILED; + cqr->intrc = -EPERM; + dasd_schedule_block_bh(block); + continue; + } /* Non-temporary stop condition will trigger fail fast */ if (block->base->stopped & ~DASD_STOPPED_PENDING && test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) && diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 0001df8ad3e6..47fc88692494 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -1127,6 +1127,103 @@ dasd_expires_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store); +static ssize_t dasd_reservation_policy_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dasd_devmap *devmap; + int rc = 0; + + devmap = dasd_find_busid(dev_name(dev)); + if (IS_ERR(devmap)) { + rc = snprintf(buf, PAGE_SIZE, "ignore\n"); + } else { + spin_lock(&dasd_devmap_lock); + if (devmap->features & DASD_FEATURE_FAILONSLCK) + rc = snprintf(buf, PAGE_SIZE, "fail\n"); + else + rc = snprintf(buf, PAGE_SIZE, "ignore\n"); + spin_unlock(&dasd_devmap_lock); + } + return rc; +} + +static ssize_t dasd_reservation_policy_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + int rc; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + rc = 0; + spin_lock(&dasd_devmap_lock); + if (sysfs_streq("ignore", buf)) + devmap->features &= ~DASD_FEATURE_FAILONSLCK; + else if (sysfs_streq("fail", buf)) + devmap->features |= DASD_FEATURE_FAILONSLCK; + else + rc = -EINVAL; + if (devmap->device) + devmap->device->features = devmap->features; + spin_unlock(&dasd_devmap_lock); + if (rc) + return rc; + else + return count; +} + +static DEVICE_ATTR(reservation_policy, 0644, + dasd_reservation_policy_show, dasd_reservation_policy_store); + +static ssize_t dasd_reservation_state_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct dasd_device *device; + int rc = 0; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return snprintf(buf, PAGE_SIZE, "none\n"); + + if (test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) + rc = snprintf(buf, PAGE_SIZE, "reserved\n"); + else if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags)) + rc = snprintf(buf, PAGE_SIZE, "lost\n"); + else + rc = snprintf(buf, PAGE_SIZE, "none\n"); + dasd_put_device(device); + return rc; +} + +static ssize_t dasd_reservation_state_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_device *device; + int rc = 0; + + device = dasd_device_from_cdev(to_ccwdev(dev)); + if (IS_ERR(device)) + return -ENODEV; + if (sysfs_streq("reset", buf)) + clear_bit(DASD_FLAG_LOCK_STOLEN, &device->flags); + else + rc = -EINVAL; + dasd_put_device(device); + + if (rc) + return rc; + else + return count; +} + +static DEVICE_ATTR(last_known_reservation_state, 0644, + dasd_reservation_state_show, dasd_reservation_state_store); + static struct attribute * dasd_attrs[] = { &dev_attr_readonly.attr, &dev_attr_discipline.attr, @@ -1139,6 +1236,8 @@ static struct attribute * dasd_attrs[] = { &dev_attr_erplog.attr, &dev_attr_failfast.attr, &dev_attr_expires.attr, + &dev_attr_reservation_policy.attr, + &dev_attr_last_known_reservation_state.attr, NULL, }; diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index a1ebf5722ae5..46eafce3a0a6 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -817,6 +817,7 @@ static int dasd_eckd_read_conf_immediately(struct dasd_device *device, dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm); clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); cqr->retries = 5; rc = dasd_sleep_on_immediatly(cqr); return rc; @@ -1947,9 +1948,9 @@ 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) +static void dasd_eckd_check_for_device_change(struct dasd_device *device, + struct dasd_ccw_req *cqr, + struct irb *irb) { char mask; char *sense = NULL; @@ -1973,40 +1974,41 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device, /* schedule worker to reload device */ dasd_reload_device(device); } - dasd_generic_handle_state_change(device); return; } - /* summary unit check */ sense = dasd_get_sense(irb); - if (sense && (sense[7] == 0x0D) && + if (!sense) + return; + + /* summary unit check */ + if ((sense[7] == 0x0D) && (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) { dasd_alias_handle_summary_unit_check(device, irb); return; } /* service information message SIM */ - if (sense && !(sense[27] & DASD_SENSE_BIT_0) && + if (!cqr && !(sense[27] & DASD_SENSE_BIT_0) && ((sense[6] & DASD_SIM_SENSE) == DASD_SIM_SENSE)) { dasd_3990_erp_handle_sim(device, sense); - dasd_schedule_device_bh(device); return; } - if ((scsw_cc(&irb->scsw) == 1) && !sense && - (scsw_fctl(&irb->scsw) == SCSW_FCTL_START_FUNC) && - (scsw_actl(&irb->scsw) == SCSW_ACTL_START_PEND) && - (scsw_stctl(&irb->scsw) == SCSW_STCTL_STATUS_PEND)) { - /* fake irb do nothing, they are handled elsewhere */ - dasd_schedule_device_bh(device); - return; + /* loss of device reservation is handled via base devices only + * as alias devices may be used with several bases + */ + if (device->block && (sense[7] == 0x3F) && + (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK) && + test_bit(DASD_FLAG_IS_RESERVED, &device->flags)) { + if (device->features & DASD_FEATURE_FAILONSLCK) + set_bit(DASD_FLAG_LOCK_STOLEN, &device->flags); + clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); + dev_err(&device->cdev->dev, + "The device reservation was lost\n"); } - - dasd_schedule_device_bh(device); - return; -}; - +} static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single( struct dasd_device *startdev, @@ -2931,6 +2933,8 @@ dasd_eckd_release(struct dasd_device *device) cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); + if (!rc) + clear_bit(DASD_FLAG_IS_RESERVED, &device->flags); if (useglobal) mutex_unlock(&dasd_reserve_mutex); @@ -2984,6 +2988,8 @@ dasd_eckd_reserve(struct dasd_device *device) cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); + if (!rc) + set_bit(DASD_FLAG_IS_RESERVED, &device->flags); if (useglobal) mutex_unlock(&dasd_reserve_mutex); @@ -3036,6 +3042,8 @@ dasd_eckd_steal_lock(struct dasd_device *device) cqr->status = DASD_CQR_FILLED; rc = dasd_sleep_on_immediatly(cqr); + if (!rc) + set_bit(DASD_FLAG_IS_RESERVED, &device->flags); if (useglobal) mutex_unlock(&dasd_reserve_mutex); @@ -3088,6 +3096,7 @@ static int dasd_eckd_snid(struct dasd_device *device, cqr->memdev = device; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); + set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); cqr->retries = 5; cqr->expires = 10 * HZ; cqr->buildclk = get_clock(); @@ -3832,7 +3841,7 @@ static struct dasd_discipline dasd_eckd_discipline = { .format_device = dasd_eckd_format_device, .erp_action = dasd_eckd_erp_action, .erp_postaction = dasd_eckd_erp_postaction, - .handle_unsolicited_interrupt = dasd_eckd_handle_unsolicited_interrupt, + .check_for_device_change = dasd_eckd_check_for_device_change, .build_cp = dasd_eckd_build_alias_cp, .free_cp = dasd_eckd_free_alias_cp, .dump_sense = dasd_eckd_dump_sense, diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c index 83b4615a3b62..77f778b7b070 100644 --- a/drivers/s390/block/dasd_eer.c +++ b/drivers/s390/block/dasd_eer.c @@ -473,6 +473,7 @@ int dasd_eer_enable(struct dasd_device *device) cqr->retries = 255; cqr->expires = 10 * HZ; clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags); + set_bit(DASD_CQR_ALLOW_SLOCK, &cqr->flags); ccw = cqr->cpaddr; ccw->cmd_code = DASD_ECKD_CCW_SNSS; diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index 86bacda2c5f6..be89b3a893da 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -233,24 +233,16 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr) return NULL; } -static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device, - struct irb *irb) +static void dasd_fba_check_for_device_change(struct dasd_device *device, + struct dasd_ccw_req *cqr, + 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.cmd.dstat & mask) == mask) { + if ((irb->scsw.cmd.dstat & mask) == mask) dasd_generic_handle_state_change(device); - return; - } - - /* check for unsolicited interrupts */ - DBF_DEV_EVENT(DBF_WARNING, device, "%s", - "unsolicited interrupt received"); - device->discipline->dump_sense_dbf(device, irb, "unsolicited"); - dasd_schedule_device_bh(device); - return; }; static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev, @@ -605,7 +597,7 @@ static struct dasd_discipline dasd_fba_discipline = { .handle_terminated_request = dasd_fba_handle_terminated_request, .erp_action = dasd_fba_erp_action, .erp_postaction = dasd_fba_erp_postaction, - .handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt, + .check_for_device_change = dasd_fba_check_for_device_change, .build_cp = dasd_fba_build_cp, .free_cp = dasd_fba_free_cp, .dump_sense = dasd_fba_dump_sense, diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index ba038ef57606..df9f6999411d 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h @@ -232,6 +232,10 @@ struct dasd_ccw_req { #define DASD_CQR_FLAGS_USE_ERP 0 /* use ERP for this request */ #define DASD_CQR_FLAGS_FAILFAST 1 /* FAILFAST */ #define DASD_CQR_VERIFY_PATH 2 /* path verification request */ +#define DASD_CQR_ALLOW_SLOCK 3 /* Try this request even when lock was + * stolen. Should not be combined with + * DASD_CQR_FLAGS_USE_ERP + */ /* Signature for error recovery functions. */ typedef struct dasd_ccw_req *(*dasd_erp_fn_t) (struct dasd_ccw_req *); @@ -334,9 +338,9 @@ struct dasd_discipline { void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *, struct irb *); void (*dump_sense_dbf) (struct dasd_device *, struct irb *, char *); - - void (*handle_unsolicited_interrupt) (struct dasd_device *, - struct irb *); + void (*check_for_device_change) (struct dasd_device *, + struct dasd_ccw_req *, + struct irb *); /* i/o control functions. */ int (*fill_geometry) (struct dasd_block *, struct hd_geometry *); @@ -473,6 +477,9 @@ struct dasd_block { * confuse this with the user specified * read-only feature. */ +#define DASD_FLAG_IS_RESERVED 7 /* The device is reserved */ +#define DASD_FLAG_LOCK_STOLEN 8 /* The device lock was stolen */ + void dasd_put_device_wake(struct dasd_device *); -- cgit v1.2.3 From 6f272b9cec285a9610a2acf101f694bc58bed37e Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 5 Jan 2011 12:48:05 +0100 Subject: [S390] dasd: Prevent deadlock during suspend/resume. The freeze callback may set a stop bit so that a worker thread could not start I/O. The discipline specific freeze function waits for the worker to be completed. Set the stop_bit after the discipline specific freeze function has returned and no worker is running. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index f16afe74464f..82d9ce36bd0b 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -2769,6 +2769,10 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) if (IS_ERR(device)) return PTR_ERR(device); + + if (device->discipline->freeze) + rc = device->discipline->freeze(device); + /* disallow new I/O */ dasd_device_set_stop_bits(device, DASD_STOPPED_PM); /* clear active requests */ @@ -2805,9 +2809,6 @@ int dasd_generic_pm_freeze(struct ccw_device *cdev) list_splice_tail(&freeze_queue, &device->ccw_queue); spin_unlock_irq(get_ccwdev_lock(cdev)); - if (device->discipline->freeze) - rc = device->discipline->freeze(device); - dasd_put_device(device); return rc; } -- cgit v1.2.3 From e4dbb0f2b5dd6a836d0e5c60aa5f573e0bbcf76a Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 5 Jan 2011 12:48:06 +0100 Subject: [S390] dasd: Add support for raw ECKD access. Normal I/O operations through the DASD device driver give only access to the data fields of an ECKD device even for track based I/O. This patch extends the DASD device driver to give access to whole ECKD tracks including count, key and data fields. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- arch/s390/include/asm/dasd.h | 2 + drivers/s390/block/dasd.c | 23 ++++- drivers/s390/block/dasd_devmap.c | 55 ++++++++++- drivers/s390/block/dasd_eckd.c | 194 ++++++++++++++++++++++++++++++++++++++- drivers/s390/block/dasd_eckd.h | 2 + 5 files changed, 268 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/arch/s390/include/asm/dasd.h b/arch/s390/include/asm/dasd.h index 47fcdada5d25..0be28efe5b66 100644 --- a/arch/s390/include/asm/dasd.h +++ b/arch/s390/include/asm/dasd.h @@ -73,6 +73,7 @@ typedef struct dasd_information2_t { * 0x02: use diag discipline (diag) * 0x04: set the device initially online (internal use only) * 0x08: enable ERP related logging + * 0x20: give access to raw eckd data */ #define DASD_FEATURE_DEFAULT 0x00 #define DASD_FEATURE_READONLY 0x01 @@ -81,6 +82,7 @@ typedef struct dasd_information2_t { #define DASD_FEATURE_ERPLOG 0x08 #define DASD_FEATURE_FAILFAST 0x10 #define DASD_FEATURE_FAILONSLCK 0x20 +#define DASD_FEATURE_USERAW 0x40 #define DASD_PARTN_BITS 2 diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 82d9ce36bd0b..4e266f43332d 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -369,6 +369,11 @@ dasd_state_ready_to_online(struct dasd_device * device) device->state = DASD_STATE_ONLINE; if (device->block) { dasd_schedule_block_bh(device->block); + if ((device->features & DASD_FEATURE_USERAW)) { + disk = device->block->gdp; + kobject_uevent(&disk_to_dev(disk)->kobj, KOBJ_CHANGE); + return 0; + } disk = device->block->bdev->bd_disk; disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); while ((part = disk_part_iter_next(&piter))) @@ -394,7 +399,7 @@ static int dasd_state_online_to_ready(struct dasd_device *device) return rc; } device->state = DASD_STATE_READY; - if (device->block) { + if (device->block && !(device->features & DASD_FEATURE_USERAW)) { disk = device->block->bdev->bd_disk; disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0); while ((part = disk_part_iter_next(&piter))) @@ -2258,8 +2263,20 @@ static void dasd_setup_queue(struct dasd_block *block) { int max; - blk_queue_logical_block_size(block->request_queue, block->bp_block); - max = block->base->discipline->max_blocks << block->s2b_shift; + if (block->base->features & DASD_FEATURE_USERAW) { + /* + * the max_blocks value for raw_track access is 256 + * it is higher than the native ECKD value because we + * only need one ccw per track + * so the max_hw_sectors are + * 2048 x 512B = 1024kB = 16 tracks + */ + max = 2048; + } else { + max = block->base->discipline->max_blocks << block->s2b_shift; + } + blk_queue_logical_block_size(block->request_queue, + block->bp_block); blk_queue_max_hw_sectors(block->request_queue, max); blk_queue_max_segments(block->request_queue, -1L); /* with page sized segments we can translate each segement into diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index 47fc88692494..cb6a67bc89ff 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c @@ -208,6 +208,8 @@ dasd_feature_list(char *str, char **endp) features |= DASD_FEATURE_READONLY; else if (len == 4 && !strncmp(str, "diag", 4)) features |= DASD_FEATURE_USEDIAG; + else if (len == 3 && !strncmp(str, "raw", 3)) + features |= DASD_FEATURE_USERAW; else if (len == 6 && !strncmp(str, "erplog", 6)) features |= DASD_FEATURE_ERPLOG; else if (len == 8 && !strncmp(str, "failfast", 8)) @@ -857,7 +859,7 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr, spin_lock(&dasd_devmap_lock); /* Changing diag discipline flag is only allowed in offline state. */ rc = count; - if (!devmap->device) { + if (!devmap->device && !(devmap->features & DASD_FEATURE_USERAW)) { if (val) devmap->features |= DASD_FEATURE_USEDIAG; else @@ -870,6 +872,56 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr, static DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store); +/* + * use_raw controls whether the driver should give access to raw eckd data or + * operate in standard mode + */ +static ssize_t +dasd_use_raw_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct dasd_devmap *devmap; + int use_raw; + + devmap = dasd_find_busid(dev_name(dev)); + if (!IS_ERR(devmap)) + use_raw = (devmap->features & DASD_FEATURE_USERAW) != 0; + else + use_raw = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USERAW) != 0; + return sprintf(buf, use_raw ? "1\n" : "0\n"); +} + +static ssize_t +dasd_use_raw_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct dasd_devmap *devmap; + ssize_t rc; + unsigned long val; + + devmap = dasd_devmap_from_cdev(to_ccwdev(dev)); + if (IS_ERR(devmap)) + return PTR_ERR(devmap); + + if ((strict_strtoul(buf, 10, &val) != 0) || val > 1) + return -EINVAL; + + spin_lock(&dasd_devmap_lock); + /* Changing diag discipline flag is only allowed in offline state. */ + rc = count; + if (!devmap->device && !(devmap->features & DASD_FEATURE_USEDIAG)) { + if (val) + devmap->features |= DASD_FEATURE_USERAW; + else + devmap->features &= ~DASD_FEATURE_USERAW; + } else + rc = -EPERM; + spin_unlock(&dasd_devmap_lock); + return rc; +} + +static DEVICE_ATTR(raw_track_access, 0644, dasd_use_raw_show, + dasd_use_raw_store); + static ssize_t dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1232,6 +1284,7 @@ static struct attribute * dasd_attrs[] = { &dev_attr_vendor.attr, &dev_attr_uid.attr, &dev_attr_use_diag.attr, + &dev_attr_raw_track_access.attr, &dev_attr_eer_enabled.attr, &dev_attr_erplog.attr, &dev_attr_failfast.attr, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 46eafce3a0a6..318672d05563 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -54,6 +54,15 @@ #define ECKD_F7(i) (i->factor7) #define ECKD_F8(i) (i->factor8) +/* + * raw track access always map to 64k in memory + * so it maps to 16 blocks of 4k per track + */ +#define DASD_RAW_BLOCK_PER_TRACK 16 +#define DASD_RAW_BLOCKSIZE 4096 +/* 64k are 128 x 512 byte sectors */ +#define DASD_RAW_SECTORS_PER_TRACK 128 + MODULE_LICENSE("GPL"); static struct dasd_discipline dasd_eckd_discipline; @@ -385,6 +394,23 @@ static void fill_LRE_data(struct LRE_eckd_data *data, unsigned int trk, data->length = reclen; data->operation.operation = 0x03; break; + case DASD_ECKD_CCW_WRITE_FULL_TRACK: + data->operation.orientation = 0x0; + data->operation.operation = 0x3F; + data->extended_operation = 0x11; + data->length = 0; + data->extended_parameter_length = 0x02; + if (data->count > 8) { + data->extended_parameter[0] = 0xFF; + data->extended_parameter[1] = 0xFF; + data->extended_parameter[1] <<= (16 - count); + } else { + data->extended_parameter[0] = 0xFF; + data->extended_parameter[0] <<= (8 - count); + data->extended_parameter[1] = 0x00; + } + data->sector = 0xFF; + break; case DASD_ECKD_CCW_WRITE_TRACK_DATA: data->auxiliary.length_valid = 0x1; data->length = reclen; /* not tlf, as one might think */ @@ -408,6 +434,12 @@ static void fill_LRE_data(struct LRE_eckd_data *data, unsigned int trk, case DASD_ECKD_CCW_READ_COUNT: data->operation.operation = 0x06; break; + case DASD_ECKD_CCW_READ_TRACK: + data->operation.orientation = 0x1; + data->operation.operation = 0x0C; + data->extended_parameter_length = 0; + data->sector = 0xFF; + break; case DASD_ECKD_CCW_READ_TRACK_DATA: data->auxiliary.length_valid = 0x1; data->length = tlf; @@ -451,10 +483,16 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, ccw->cmd_code = DASD_ECKD_CCW_PFX; ccw->flags = 0; - ccw->count = sizeof(*pfxdata); - ccw->cda = (__u32) __pa(pfxdata); + if (cmd == DASD_ECKD_CCW_WRITE_FULL_TRACK) { + ccw->count = sizeof(*pfxdata) + 2; + ccw->cda = (__u32) __pa(pfxdata); + memset(pfxdata, 0, sizeof(*pfxdata) + 2); + } else { + ccw->count = sizeof(*pfxdata); + ccw->cda = (__u32) __pa(pfxdata); + memset(pfxdata, 0, sizeof(*pfxdata)); + } - memset(pfxdata, 0, sizeof(*pfxdata)); /* prefix data */ if (format > 1) { DBF_DEV_EVENT(DBF_ERR, basedev, @@ -488,6 +526,7 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, dedata->mask.perm = 0x1; dedata->attributes.operation = basepriv->attrib.operation; break; + case DASD_ECKD_CCW_READ_TRACK: case DASD_ECKD_CCW_READ_TRACK_DATA: dedata->mask.perm = 0x1; dedata->attributes.operation = basepriv->attrib.operation; @@ -514,6 +553,11 @@ static int prefix_LRE(struct ccw1 *ccw, struct PFX_eckd_data *pfxdata, dedata->attributes.operation = DASD_BYPASS_CACHE; rc = check_XRC_on_prefix(pfxdata, basedev); break; + case DASD_ECKD_CCW_WRITE_FULL_TRACK: + dedata->mask.perm = 0x03; + dedata->attributes.operation = basepriv->attrib.operation; + dedata->blk_size = 0; + break; case DASD_ECKD_CCW_WRITE_TRACK_DATA: dedata->mask.perm = 0x02; dedata->attributes.operation = basepriv->attrib.operation; @@ -1607,6 +1651,13 @@ static int dasd_eckd_end_analysis(struct dasd_block *block) dasd_sfree_request(init_cqr, device); } + if (device->features & DASD_FEATURE_USERAW) { + block->bp_block = DASD_RAW_BLOCKSIZE; + blk_per_trk = DASD_RAW_BLOCK_PER_TRACK; + block->s2b_shift = 3; + goto raw; + } + if (status == INIT_CQR_UNFORMATTED) { dev_warn(&device->cdev->dev, "The DASD is not formatted\n"); return -EMEDIUMTYPE; @@ -1644,6 +1695,7 @@ static int dasd_eckd_end_analysis(struct dasd_block *block) dev_warn(&device->cdev->dev, "Track 0 has no records following the VTOC\n"); } + if (count_area != NULL && count_area->kl == 0) { /* we found notthing violating our disk layout */ if (dasd_check_blocksize(count_area->dl) == 0) @@ -1659,6 +1711,8 @@ static int dasd_eckd_end_analysis(struct dasd_block *block) block->s2b_shift++; blk_per_trk = recs_per_track(&private->rdc_data, 0, block->bp_block); + +raw: block->blocks = (private->real_cyl * private->rdc_data.trk_per_cyl * blk_per_trk); @@ -2741,6 +2795,135 @@ static struct dasd_ccw_req *dasd_eckd_build_cp(struct dasd_device *startdev, return cqr; } +static struct dasd_ccw_req *dasd_raw_build_cp(struct dasd_device *startdev, + struct dasd_block *block, + struct request *req) +{ + struct dasd_eckd_private *private; + unsigned long *idaws; + struct dasd_device *basedev; + struct dasd_ccw_req *cqr; + struct ccw1 *ccw; + struct req_iterator iter; + struct bio_vec *bv; + char *dst; + unsigned char cmd; + unsigned int trkcount; + unsigned int seg_len, len_to_track_end; + unsigned int first_offs; + unsigned int cidaw, cplength, datasize; + sector_t first_trk, last_trk; + unsigned int pfx_datasize; + + /* + * raw track access needs to be mutiple of 64k and on 64k boundary + */ + if ((blk_rq_pos(req) % DASD_RAW_SECTORS_PER_TRACK) != 0) { + cqr = ERR_PTR(-EINVAL); + goto out; + } + if (((blk_rq_pos(req) + blk_rq_sectors(req)) % + DASD_RAW_SECTORS_PER_TRACK) != 0) { + cqr = ERR_PTR(-EINVAL); + goto out; + } + + first_trk = blk_rq_pos(req) / DASD_RAW_SECTORS_PER_TRACK; + last_trk = (blk_rq_pos(req) + blk_rq_sectors(req) - 1) / + DASD_RAW_SECTORS_PER_TRACK; + trkcount = last_trk - first_trk + 1; + first_offs = 0; + basedev = block->base; + private = (struct dasd_eckd_private *) basedev->private; + + if (rq_data_dir(req) == READ) + cmd = DASD_ECKD_CCW_READ_TRACK; + else if (rq_data_dir(req) == WRITE) + cmd = DASD_ECKD_CCW_WRITE_FULL_TRACK; + else { + cqr = ERR_PTR(-EINVAL); + goto out; + } + + /* + * Raw track based I/O needs IDAWs for each page, + * and not just for 64 bit addresses. + */ + cidaw = trkcount * DASD_RAW_BLOCK_PER_TRACK; + + /* 1x prefix + one read/write ccw per track */ + cplength = 1 + trkcount; + + /* + * struct PFX_eckd_data has up to 2 byte as extended parameter + * this is needed for write full track and has to be mentioned + * seperately + * add 8 instead of 2 to keep 8 byte boundary + */ + pfx_datasize = sizeof(struct PFX_eckd_data) + 8; + + datasize = pfx_datasize + cidaw * sizeof(unsigned long long); + + /* Allocate the ccw request. */ + cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, cplength, + datasize, startdev); + if (IS_ERR(cqr)) + goto out; + ccw = cqr->cpaddr; + + if (prefix_LRE(ccw++, cqr->data, first_trk, last_trk, cmd, + basedev, startdev, 1 /* format */, first_offs + 1, + trkcount, 0, 0) == -EAGAIN) { + /* Clock not in sync and XRC is enabled. + * Try again later. + */ + dasd_sfree_request(cqr, startdev); + cqr = ERR_PTR(-EAGAIN); + goto out; + } + + idaws = (unsigned long *)(cqr->data + pfx_datasize); + + len_to_track_end = 0; + + rq_for_each_segment(bv, req, iter) { + dst = page_address(bv->bv_page) + bv->bv_offset; + seg_len = bv->bv_len; + if (!len_to_track_end) { + ccw[-1].flags |= CCW_FLAG_CC; + ccw->cmd_code = cmd; + /* maximum 3390 track size */ + ccw->count = 57326; + /* 64k map to one track */ + len_to_track_end = 65536; + ccw->cda = (__u32)(addr_t)idaws; + ccw->flags |= CCW_FLAG_IDA; + ccw->flags |= CCW_FLAG_SLI; + ccw++; + } + len_to_track_end -= seg_len; + idaws = idal_create_words(idaws, dst, seg_len); + } + + if (blk_noretry_request(req) || + block->base->features & DASD_FEATURE_FAILFAST) + set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags); + cqr->startdev = startdev; + cqr->memdev = startdev; + cqr->block = block; + cqr->expires = startdev->default_expires * HZ; + cqr->lpm = startdev->path_data.ppm; + cqr->retries = 256; + cqr->buildclk = get_clock(); + cqr->status = DASD_CQR_FILLED; + + if (IS_ERR(cqr) && PTR_ERR(cqr) != -EAGAIN) + cqr = NULL; +out: + return cqr; +} + + static int dasd_eckd_free_cp(struct dasd_ccw_req *cqr, struct request *req) { @@ -2845,7 +3028,10 @@ static struct dasd_ccw_req *dasd_eckd_build_alias_cp(struct dasd_device *base, spin_lock_irqsave(get_ccwdev_lock(startdev->cdev), flags); private->count++; - cqr = dasd_eckd_build_cp(startdev, block, req); + if ((base->features & DASD_FEATURE_USERAW)) + cqr = dasd_raw_build_cp(startdev, block, req); + else + cqr = dasd_eckd_build_cp(startdev, block, req); if (IS_ERR(cqr)) private->count--; spin_unlock_irqrestore(get_ccwdev_lock(startdev->cdev), flags); diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h index 5051f374cbcb..4a688a873a77 100644 --- a/drivers/s390/block/dasd_eckd.h +++ b/drivers/s390/block/dasd_eckd.h @@ -37,11 +37,13 @@ #define DASD_ECKD_CCW_WRITE_KD_MT 0x8d #define DASD_ECKD_CCW_READ_KD_MT 0x8e #define DASD_ECKD_CCW_RELEASE 0x94 +#define DASD_ECKD_CCW_WRITE_FULL_TRACK 0x95 #define DASD_ECKD_CCW_READ_CKD_MT 0x9e #define DASD_ECKD_CCW_WRITE_CKD_MT 0x9d #define DASD_ECKD_CCW_WRITE_TRACK_DATA 0xA5 #define DASD_ECKD_CCW_READ_TRACK_DATA 0xA6 #define DASD_ECKD_CCW_RESERVE 0xB4 +#define DASD_ECKD_CCW_READ_TRACK 0xDE #define DASD_ECKD_CCW_PFX 0xE7 #define DASD_ECKD_CCW_PFX_READ 0xEA #define DASD_ECKD_CCW_RSCK 0xF9 -- cgit v1.2.3 From 09a8e7adcf960bd6a7204f3f3b377a89ce22efbf Mon Sep 17 00:00:00 2001 From: Stefan Haberland Date: Wed, 5 Jan 2011 12:48:07 +0100 Subject: [S390] dasd: Correct retry counter for terminated I/O. In case the DASD driver needs to term a running I/O the retry counter is decreased twice. Remove the unnecessary retry counter decrease in das_term_IO. Signed-off-by: Stefan Haberland Signed-off-by: Martin Schwidefsky --- drivers/s390/block/dasd.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 4e266f43332d..794bfd962266 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -855,7 +855,6 @@ int dasd_term_IO(struct dasd_ccw_req *cqr) rc = ccw_device_clear(device->cdev, (long) cqr); switch (rc) { case 0: /* termination successful */ - cqr->retries--; cqr->status = DASD_CQR_CLEAR_PENDING; cqr->stopclk = get_clock(); cqr->starttime = 0; -- cgit v1.2.3 From c03017544e3b2e60aa3c8ae451fac01595f1bf11 Mon Sep 17 00:00:00 2001 From: Peter Oberparleiter Date: Wed, 5 Jan 2011 12:48:13 +0100 Subject: [S390] cio: fix ccwgroup unregistration race condition A race condition exists in the ccwgroup device unregistration code which can cause a kernel panic due to a use-after-free bug. This race condition might be triggered when all ccw devices associated with a ccwgroup device are removed at the same time (e.g. because the corresponding channel path becomes no longer available). Fix this race condition by clearing the references from the associated ccw devices to the ccw group device during unregistration of the ccw group device. Signed-off-by: Peter Oberparleiter Signed-off-by: Martin Schwidefsky --- drivers/s390/cio/ccwgroup.c | 78 +++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 38 deletions(-) (limited to 'drivers') diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 97b25d68e3e7..2864581d8ecb 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -66,6 +66,27 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) } +/* + * Remove references from ccw devices to ccw group device and from + * ccw group device to ccw devices. + */ +static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev) +{ + struct ccw_device *cdev; + int i; + + for (i = 0; i < gdev->count; i++) { + cdev = gdev->cdev[i]; + if (!cdev) + continue; + spin_lock_irq(cdev->ccwlock); + dev_set_drvdata(&cdev->dev, NULL); + spin_unlock_irq(cdev->ccwlock); + gdev->cdev[i] = NULL; + put_device(&cdev->dev); + } +} + /* * Provide an 'ungroup' attribute so the user can remove group devices no * longer needed or accidentially created. Saves memory :) @@ -78,6 +99,7 @@ static void ccwgroup_ungroup_callback(struct device *dev) if (device_is_registered(&gdev->dev)) { __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + __ccwgroup_remove_cdev_refs(gdev); } mutex_unlock(&gdev->reg_mutex); } @@ -116,21 +138,7 @@ static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store); static void ccwgroup_release (struct device *dev) { - struct ccwgroup_device *gdev; - int i; - - gdev = to_ccwgroupdev(dev); - - for (i = 0; i < gdev->count; i++) { - if (gdev->cdev[i]) { - spin_lock_irq(gdev->cdev[i]->ccwlock); - if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev) - dev_set_drvdata(&gdev->cdev[i]->dev, NULL); - spin_unlock_irq(gdev->cdev[i]->ccwlock); - put_device(&gdev->cdev[i]->dev); - } - } - kfree(gdev); + kfree(to_ccwgroupdev(dev)); } static int @@ -639,6 +647,7 @@ void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver) mutex_lock(&gdev->reg_mutex); __ccwgroup_remove_symlinks(gdev); device_unregister(dev); + __ccwgroup_remove_cdev_refs(gdev); mutex_unlock(&gdev->reg_mutex); put_device(dev); } @@ -660,25 +669,6 @@ int ccwgroup_probe_ccwdev(struct ccw_device *cdev) return 0; } -static struct ccwgroup_device * -__ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev) -{ - struct ccwgroup_device *gdev; - - gdev = dev_get_drvdata(&cdev->dev); - if (gdev) { - if (get_device(&gdev->dev)) { - mutex_lock(&gdev->reg_mutex); - if (device_is_registered(&gdev->dev)) - return gdev; - mutex_unlock(&gdev->reg_mutex); - put_device(&gdev->dev); - } - return NULL; - } - return NULL; -} - /** * ccwgroup_remove_ccwdev() - remove function for slave devices * @cdev: ccw device to be removed @@ -694,13 +684,25 @@ void ccwgroup_remove_ccwdev(struct ccw_device *cdev) /* Ignore offlining errors, device is gone anyway. */ ccw_device_set_offline(cdev); /* If one of its devices is gone, the whole group is done for. */ - gdev = __ccwgroup_get_gdev_by_cdev(cdev); - if (gdev) { + spin_lock_irq(cdev->ccwlock); + gdev = dev_get_drvdata(&cdev->dev); + if (!gdev) { + spin_unlock_irq(cdev->ccwlock); + return; + } + /* Get ccwgroup device reference for local processing. */ + get_device(&gdev->dev); + spin_unlock_irq(cdev->ccwlock); + /* Unregister group device. */ + mutex_lock(&gdev->reg_mutex); + if (device_is_registered(&gdev->dev)) { __ccwgroup_remove_symlinks(gdev); device_unregister(&gdev->dev); - mutex_unlock(&gdev->reg_mutex); - put_device(&gdev->dev); + __ccwgroup_remove_cdev_refs(gdev); } + mutex_unlock(&gdev->reg_mutex); + /* Release ccwgroup device reference for local processing. */ + put_device(&gdev->dev); } MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 8e1023016cf17152972b98bce6c144834a4916d5 Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Wed, 5 Jan 2011 12:48:18 +0100 Subject: [S390] prevent unneccesary loops_per_jiffy recalculation When the seqfile /proc/cpuinfo gets accesses for each possible cpu loops_per_jiffy gets recalculated. However its value is only needed on first access. In addition loops_per_jiffy should be recalculated when the machine reports a capability change. Signed-off-by: Heiko Carstens Signed-off-by: Martin Schwidefsky --- arch/s390/kernel/processor.c | 2 +- drivers/s390/char/sclp_config.c | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/arch/s390/kernel/processor.c b/arch/s390/kernel/processor.c index 753623bde8ec..311e9d712888 100644 --- a/arch/s390/kernel/processor.c +++ b/arch/s390/kernel/processor.c @@ -46,8 +46,8 @@ static int show_cpuinfo(struct seq_file *m, void *v) unsigned long n = (unsigned long) v - 1; int i; - s390_adjust_jiffies(); if (!n) { + s390_adjust_jiffies(); seq_printf(m, "vendor_id : IBM/S390\n" "# processors : %i\n" "bogomips per cpu: %lu.%02lu\n", diff --git a/drivers/s390/char/sclp_config.c b/drivers/s390/char/sclp_config.c index b497afe061cc..16e232a99fb7 100644 --- a/drivers/s390/char/sclp_config.c +++ b/drivers/s390/char/sclp_config.c @@ -33,6 +33,7 @@ static void sclp_cpu_capability_notify(struct work_struct *work) int cpu; struct sys_device *sysdev; + s390_adjust_jiffies(); pr_warning("cpu capability changed.\n"); get_online_cpus(); for_each_online_cpu(cpu) { -- cgit v1.2.3