Browse Source

Merge branch 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6

* 'for-linus' of git://git390.osdl.marist.edu/pub/scm/linux-2.6: (56 commits)
  [S390] replace lock_cpu_hotplug with get_online_cpus
  [S390] usage of s390dbf: shrink number of debug areas to use.
  [S390] constify function pointer tables.
  [S390] do local_irq_restore while spinning in spin_lock_irqsave.
  [S390] add smp_call_function_mask
  [S390] dasd: fix loop in request expiration handling
  [S390] Unused field / extern declaration in processor.h
  [S390] Remove TOPDIR from Makefile
  [S390] dasd: add hyper PAV support to DASD device driver, part 1
  [S390] single-step cleanup
  [S390] Move NOTES and BUG_TABLE.
  [S390] drivers/s390/: Spelling fixes
  [S390] include/asm-s390/: Spelling fixes
  [S390] arch/s390/: Spelling fixes
  [S390] Use diag308 subcodes 3 and 6 for reboot and dump when possible.
  [S390] vmemmap: allocate struct pages before 1:1 mapping
  [S390] Initialize sclp_ipl_info
  [S390] Allocate and free cpu lowcores and stacks when needed/possible.
  [S390] use LIST_HEAD instead of LIST_HEAD_INIT
  [S390] Load disabled wait psw instead of stopping cpu on halt.
  ...
Linus Torvalds 17 years ago
parent
commit
0444fa7875
100 changed files with 5481 additions and 3245 deletions
  1. 1 0
      Documentation/DocBook/s390-drivers.tmpl
  2. 1 1
      Documentation/cpu-hotplug.txt
  3. 8 1
      Documentation/kernel-parameters.txt
  4. 5 0
      Documentation/s390/CommonIO
  5. 0 3
      arch/s390/Kconfig
  6. 0 60
      arch/s390/crypto/Kconfig
  7. 1 1
      arch/s390/crypto/aes_s390.c
  8. 2 2
      arch/s390/crypto/prng.c
  9. 0 4
      arch/s390/kernel/Makefile
  10. 1 1
      arch/s390/kernel/early.c
  11. 1 1
      arch/s390/kernel/head64.S
  12. 618 345
      arch/s390/kernel/ipl.c
  13. 10 8
      arch/s390/kernel/process.c
  14. 6 9
      arch/s390/kernel/ptrace.c
  15. 30 109
      arch/s390/kernel/setup.c
  16. 13 7
      arch/s390/kernel/signal.c
  17. 422 153
      arch/s390/kernel/smp.c
  18. 17 3
      arch/s390/kernel/traps.c
  19. 9 3
      arch/s390/kernel/vmlinux.lds.S
  20. 27 8
      arch/s390/lib/spinlock.c
  21. 1 1
      arch/s390/mm/extmem.c
  22. 10 16
      arch/s390/mm/vmem.c
  23. 61 2
      drivers/crypto/Kconfig
  24. 2 2
      drivers/s390/block/Makefile
  25. 316 400
      drivers/s390/block/dasd.c
  26. 0 84
      drivers/s390/block/dasd_3370_erp.c
  27. 127 233
      drivers/s390/block/dasd_3990_erp.c
  28. 0 41
      drivers/s390/block/dasd_9336_erp.c
  29. 0 21
      drivers/s390/block/dasd_9343_erp.c
  30. 903 0
      drivers/s390/block/dasd_alias.c
  31. 37 57
      drivers/s390/block/dasd_devmap.c
  32. 60 47
      drivers/s390/block/dasd_diag.c
  33. 510 158
      drivers/s390/block/dasd_eckd.c
  34. 124 1
      drivers/s390/block/dasd_eckd.h
  35. 6 5
      drivers/s390/block/dasd_eer.c
  36. 12 13
      drivers/s390/block/dasd_erp.c
  37. 68 51
      drivers/s390/block/dasd_fba.c
  38. 36 40
      drivers/s390/block/dasd_genhd.c
  39. 127 82
      drivers/s390/block/dasd_int.h
  40. 84 88
      drivers/s390/block/dasd_ioctl.c
  41. 14 9
      drivers/s390/block/dasd_proc.c
  42. 1 1
      drivers/s390/block/dcssblk.c
  43. 1 1
      drivers/s390/char/Makefile
  44. 1 1
      drivers/s390/char/monwriter.c
  45. 2 2
      drivers/s390/char/raw3270.c
  46. 2 2
      drivers/s390/char/sclp.h
  47. 0 200
      drivers/s390/char/sclp_chp.c
  48. 398 0
      drivers/s390/char/sclp_cmd.c
  49. 16 230
      drivers/s390/char/sclp_cpi.c
  50. 400 0
      drivers/s390/char/sclp_cpi_sys.c
  51. 15 0
      drivers/s390/char/sclp_cpi_sys.h
  52. 0 116
      drivers/s390/char/sclp_info.c
  53. 1 1
      drivers/s390/char/sclp_rw.c
  54. 1 1
      drivers/s390/char/tape_3590.c
  55. 1 1
      drivers/s390/char/tape_core.c
  56. 1 1
      drivers/s390/char/tape_proc.c
  57. 1 1
      drivers/s390/char/vmlogrdr.c
  58. 1 1
      drivers/s390/char/vmur.c
  59. 1 1
      drivers/s390/char/zcore.c
  60. 115 56
      drivers/s390/cio/airq.c
  61. 0 10
      drivers/s390/cio/airq.h
  62. 1 1
      drivers/s390/cio/blacklist.c
  63. 14 11
      drivers/s390/cio/ccwgroup.c
  64. 44 77
      drivers/s390/cio/chsc.c
  65. 58 48
      drivers/s390/cio/cio.c
  66. 33 56
      drivers/s390/cio/cio.h
  67. 11 11
      drivers/s390/cio/cio_debug.h
  68. 164 34
      drivers/s390/cio/css.c
  69. 16 62
      drivers/s390/cio/css.h
  70. 121 36
      drivers/s390/cio/device.c
  71. 4 1
      drivers/s390/cio/device.h
  72. 83 41
      drivers/s390/cio/device_fsm.c
  73. 6 3
      drivers/s390/cio/device_id.c
  74. 1 1
      drivers/s390/cio/device_ops.c
  75. 5 1
      drivers/s390/cio/device_pgid.c
  76. 8 5
      drivers/s390/cio/device_status.c
  77. 163 0
      drivers/s390/cio/io_sch.h
  78. 0 66
      drivers/s390/cio/ioasm.h
  79. 18 20
      drivers/s390/cio/qdio.c
  80. 1 1
      drivers/s390/cio/qdio.h
  81. 1 1
      drivers/s390/net/claw.c
  82. 1 1
      drivers/s390/net/lcs.c
  83. 1 2
      drivers/s390/net/netiucv.c
  84. 2 2
      drivers/s390/net/qeth_proc.c
  85. 1 1
      drivers/s390/net/smsgiucv.c
  86. 1 1
      drivers/s390/scsi/zfcp_erp.c
  87. 5 5
      drivers/s390/scsi/zfcp_fsf.c
  88. 1 1
      drivers/s390/scsi/zfcp_qdio.c
  89. 19 0
      include/asm-s390/airq.h
  90. 2 2
      include/asm-s390/cio.h
  91. 1 1
      include/asm-s390/dasd.h
  92. 7 1
      include/asm-s390/ipl.h
  93. 12 15
      include/asm-s390/mmu_context.h
  94. 16 30
      include/asm-s390/pgtable.h
  95. 0 4
      include/asm-s390/processor.h
  96. 8 0
      include/asm-s390/ptrace.h
  97. 1 1
      include/asm-s390/qdio.h
  98. 2 2
      include/asm-s390/rwsem.h
  99. 19 1
      include/asm-s390/sclp.h
  100. 2 3
      include/asm-s390/smp.h

+ 1 - 0
Documentation/DocBook/s390-drivers.tmpl

@@ -116,6 +116,7 @@
 !Iinclude/asm-s390/ccwdev.h
 !Edrivers/s390/cio/device.c
 !Edrivers/s390/cio/device_ops.c
+!Edrivers/s390/cio/airq.c
     </sect1>
     <sect1 id="cmf">
      <title>The channel-measurement facility</title>

+ 1 - 1
Documentation/cpu-hotplug.txt

@@ -50,7 +50,7 @@ additional_cpus=n (*)	Use this to limit hotpluggable cpus. This option sets
   			cpu_possible_map = cpu_present_map + additional_cpus
 
 (*) Option valid only for following architectures
-- x86_64, ia64, s390
+- x86_64, ia64
 
 ia64 and x86_64 use the number of disabled local apics in ACPI tables MADT
 to determine the number of potentially hot-pluggable cpus. The implementation

+ 8 - 1
Documentation/kernel-parameters.txt

@@ -370,7 +370,8 @@ and is between 256 and 4096 characters. It is defined in the file
 			configured.  Potentially dangerous and should only be
 			used if you are entirely sure of the consequences.
 
-	chandev=	[HW,NET] Generic channel device initialisation
+	ccw_timeout_log [S390]
+			See Documentation/s390/CommonIO for details.
 
 	checkreqprot	[SELINUX] Set initial checkreqprot flag value.
 			Format: { "0" | "1" }
@@ -382,6 +383,12 @@ and is between 256 and 4096 characters. It is defined in the file
 			Value can be changed at runtime via
 				/selinux/checkreqprot.
 
+	cio_ignore=	[S390]
+			See Documentation/s390/CommonIO for details.
+
+	cio_msg=	[S390]
+			See Documentation/s390/CommonIO for details.
+
 	clock=		[BUGS=X86-32, HW] gettimeofday clocksource override.
 			[Deprecated]
 			Forces specified clocksource (if available) to be used

+ 5 - 0
Documentation/s390/CommonIO

@@ -4,6 +4,11 @@ S/390 common I/O-Layer - command line parameters, procfs and debugfs entries
 Command line parameters
 -----------------------
 
+* ccw_timeout_log
+
+  Enable logging of debug information in case of ccw device timeouts.
+
+
 * cio_msg = yes | no
   
   Determines whether information on found devices and sensed device 

+ 0 - 3
arch/s390/Kconfig

@@ -276,9 +276,6 @@ source "kernel/Kconfig.preempt"
 
 source "mm/Kconfig"
 
-config HOLES_IN_ZONE
-	def_bool y
-
 comment "I/O subsystem configuration"
 
 config MACHCHK_WARNING

+ 0 - 60
arch/s390/crypto/Kconfig

@@ -1,60 +0,0 @@
-config CRYPTO_SHA1_S390
-	tristate "SHA1 digest algorithm"
-	depends on S390
-	select CRYPTO_ALGAPI
-	help
-	  This is the s390 hardware accelerated implementation of the
-	  SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
-
-config CRYPTO_SHA256_S390
-	tristate "SHA256 digest algorithm"
-	depends on S390
-	select CRYPTO_ALGAPI
-	help
-	  This is the s390 hardware accelerated implementation of the
-	  SHA256 secure hash standard (DFIPS 180-2).
-
-	  This version of SHA implements a 256 bit hash with 128 bits of
-	  security against collision attacks.
-
-config CRYPTO_DES_S390
-	tristate "DES and Triple DES cipher algorithms"
-	depends on S390
-	select CRYPTO_ALGAPI
-	select CRYPTO_BLKCIPHER
-	help
-	  This us the s390 hardware accelerated implementation of the
-	  DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3).
-
-config CRYPTO_AES_S390
-	tristate "AES cipher algorithms"
-	depends on S390
-	select CRYPTO_ALGAPI
-	select CRYPTO_BLKCIPHER
-	help
-	  This is the s390 hardware accelerated implementation of the
-	  AES cipher algorithms (FIPS-197). AES uses the Rijndael
-	  algorithm.
-
-	  Rijndael appears to be consistently a very good performer in
-	  both hardware and software across a wide range of computing
-	  environments regardless of its use in feedback or non-feedback
-	  modes. Its key setup time is excellent, and its key agility is
-	  good. Rijndael's very low memory requirements make it very well
-	  suited for restricted-space environments, in which it also
-	  demonstrates excellent performance. Rijndael's operations are
-	  among the easiest to defend against power and timing attacks.
-
-	  On s390 the System z9-109 currently only supports the key size
-	  of 128 bit.
-
-config S390_PRNG
-	tristate "Pseudo random number generator device driver"
-	depends on S390
-	default "m"
-	help
-	  Select this option if you want to use the s390 pseudo random number
-	  generator. The PRNG is part of the cryptographic processor functions
-	  and uses triple-DES to generate secure random numbers like the
-	  ANSI X9.17 standard. The PRNG is usable via the char device
-	  /dev/prandom.

+ 1 - 1
arch/s390/crypto/aes_s390.c

@@ -516,7 +516,7 @@ static int __init aes_init(void)
 	/* z9 109 and z9 BC/EC only support 128 bit key length */
 	if (keylen_flag == AES_KEYLEN_128)
 		printk(KERN_INFO
-		       "aes_s390: hardware acceleration only available for"
+		       "aes_s390: hardware acceleration only available for "
 		       "128 bit keys\n");
 
 	ret = crypto_register_alg(&aes_alg);

+ 2 - 2
arch/s390/crypto/prng.c

@@ -90,7 +90,7 @@ static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes,
 	int ret = 0;
 	int tmp;
 
-	/* nbytes can be arbitrary long, we spilt it into chunks */
+	/* nbytes can be arbitrary length, we split it into chunks */
 	while (nbytes) {
 		/* same as in extract_entropy_user in random.c */
 		if (need_resched()) {
@@ -146,7 +146,7 @@ static ssize_t prng_read(struct file *file, char __user *ubuf, size_t nbytes,
 	return ret;
 }
 
-static struct file_operations prng_fops = {
+static const struct file_operations prng_fops = {
 	.owner		= THIS_MODULE,
 	.open		= &prng_open,
 	.release	= NULL,

+ 0 - 4
arch/s390/kernel/Makefile

@@ -31,7 +31,3 @@ S390_KEXEC_OBJS := machine_kexec.o crash.o
 S390_KEXEC_OBJS += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o)
 obj-$(CONFIG_KEXEC) += $(S390_KEXEC_OBJS)
 
-#
-# This is just to get the dependencies...
-#
-binfmt_elf32.o:	$(TOPDIR)/fs/binfmt_elf.c

+ 1 - 1
arch/s390/kernel/early.c

@@ -276,7 +276,7 @@ void __init startup_init(void)
 	create_kernel_nss();
 	sort_main_extable();
 	setup_lowcore_early();
-	sclp_readinfo_early();
+	sclp_read_info_early();
 	sclp_facilities_detect();
 	memsize = sclp_memory_detect();
 #ifndef CONFIG_64BIT

+ 1 - 1
arch/s390/kernel/head64.S

@@ -157,7 +157,7 @@ startup_continue:
 	.long	0xb2b10000		# store facility list
 	tm	0xc8,0x08		# check bit for clearing-by-ASCE
 	bno	0f-.LPG1(%r13)
-	lhi	%r1,2094
+	lhi	%r1,2048
 	lhi	%r2,0
 	.long	0xb98e2001
 	oi	7(%r12),0x80		# set IDTE flag

File diff suppressed because it is too large
+ 618 - 345
arch/s390/kernel/ipl.c


+ 10 - 8
arch/s390/kernel/process.c

@@ -36,7 +36,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/notifier.h>
-
+#include <linux/utsname.h>
 #include <asm/uaccess.h>
 #include <asm/pgtable.h>
 #include <asm/system.h>
@@ -182,13 +182,15 @@ void cpu_idle(void)
 
 void show_regs(struct pt_regs *regs)
 {
-	struct task_struct *tsk = current;
-
-        printk("CPU:    %d    %s\n", task_thread_info(tsk)->cpu, print_tainted());
-        printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
-	       current->comm, task_pid_nr(current), (void *) tsk,
-	       (void *) tsk->thread.ksp);
-
+	print_modules();
+	printk("CPU: %d %s %s %.*s\n",
+	       task_thread_info(current)->cpu, print_tainted(),
+	       init_utsname()->release,
+	       (int)strcspn(init_utsname()->version, " "),
+	       init_utsname()->version);
+	printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
+	       current->comm, current->pid, current,
+	       (void *) current->thread.ksp);
 	show_registers(regs);
 	/* Show stack backtrace if pt_regs is from kernel mode */
 	if (!(regs->psw.mask & PSW_MASK_PSTATE))

+ 6 - 9
arch/s390/kernel/ptrace.c

@@ -86,13 +86,13 @@ FixPerRegisters(struct task_struct *task)
 		per_info->control_regs.bits.storage_alt_space_ctl = 0;
 }
 
-static void set_single_step(struct task_struct *task)
+void user_enable_single_step(struct task_struct *task)
 {
 	task->thread.per_info.single_step = 1;
 	FixPerRegisters(task);
 }
 
-static void clear_single_step(struct task_struct *task)
+void user_disable_single_step(struct task_struct *task)
 {
 	task->thread.per_info.single_step = 0;
 	FixPerRegisters(task);
@@ -107,7 +107,7 @@ void
 ptrace_disable(struct task_struct *child)
 {
 	/* make sure the single step bit is not set. */
-	clear_single_step(child);
+	user_disable_single_step(child);
 }
 
 #ifndef CONFIG_64BIT
@@ -651,7 +651,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
 			clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		child->exit_code = data;
 		/* make sure the single step bit is not set. */
-		clear_single_step(child);
+		user_disable_single_step(child);
 		wake_up_process(child);
 		return 0;
 
@@ -665,7 +665,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
 			return 0;
 		child->exit_code = SIGKILL;
 		/* make sure the single step bit is not set. */
-		clear_single_step(child);
+		user_disable_single_step(child);
 		wake_up_process(child);
 		return 0;
 
@@ -675,10 +675,7 @@ do_ptrace(struct task_struct *child, long request, long addr, long data)
 			return -EIO;
 		clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
 		child->exit_code = data;
-		if (data)
-			set_tsk_thread_flag(child, TIF_SINGLE_STEP);
-		else
-			set_single_step(child);
+		user_enable_single_step(child);
 		/* give it a chance to run. */
 		wake_up_process(child);
 		return 0;

+ 30 - 109
arch/s390/kernel/setup.c

@@ -125,75 +125,6 @@ void __cpuinit cpu_init(void)
         enter_lazy_tlb(&init_mm, current);
 }
 
-/*
- * VM halt and poweroff setup routines
- */
-char vmhalt_cmd[128] = "";
-char vmpoff_cmd[128] = "";
-static char vmpanic_cmd[128] = "";
-
-static void strncpy_skip_quote(char *dst, char *src, int n)
-{
-        int sx, dx;
-
-        dx = 0;
-        for (sx = 0; src[sx] != 0; sx++) {
-                if (src[sx] == '"') continue;
-                dst[dx++] = src[sx];
-                if (dx >= n) break;
-        }
-}
-
-static int __init vmhalt_setup(char *str)
-{
-        strncpy_skip_quote(vmhalt_cmd, str, 127);
-        vmhalt_cmd[127] = 0;
-        return 1;
-}
-
-__setup("vmhalt=", vmhalt_setup);
-
-static int __init vmpoff_setup(char *str)
-{
-        strncpy_skip_quote(vmpoff_cmd, str, 127);
-        vmpoff_cmd[127] = 0;
-        return 1;
-}
-
-__setup("vmpoff=", vmpoff_setup);
-
-static int vmpanic_notify(struct notifier_block *self, unsigned long event,
-			  void *data)
-{
-	if (MACHINE_IS_VM && strlen(vmpanic_cmd) > 0)
-		cpcmd(vmpanic_cmd, NULL, 0, NULL);
-
-	return NOTIFY_OK;
-}
-
-#define PANIC_PRI_VMPANIC	0
-
-static struct notifier_block vmpanic_nb = {
-	.notifier_call = vmpanic_notify,
-	.priority = PANIC_PRI_VMPANIC
-};
-
-static int __init vmpanic_setup(char *str)
-{
-	static int register_done __initdata = 0;
-
-	strncpy_skip_quote(vmpanic_cmd, str, 127);
-	vmpanic_cmd[127] = 0;
-	if (!register_done) {
-		register_done = 1;
-		atomic_notifier_chain_register(&panic_notifier_list,
-					       &vmpanic_nb);
-	}
-	return 1;
-}
-
-__setup("vmpanic=", vmpanic_setup);
-
 /*
  * condev= and conmode= setup parameter.
  */
@@ -308,38 +239,6 @@ static void __init setup_zfcpdump(unsigned int console_devno)
 static inline void setup_zfcpdump(unsigned int console_devno) {}
 #endif /* CONFIG_ZFCPDUMP */
 
-#ifdef CONFIG_SMP
-void (*_machine_restart)(char *command) = machine_restart_smp;
-void (*_machine_halt)(void) = machine_halt_smp;
-void (*_machine_power_off)(void) = machine_power_off_smp;
-#else
-/*
- * Reboot, halt and power_off routines for non SMP.
- */
-static void do_machine_restart_nonsmp(char * __unused)
-{
-	do_reipl();
-}
-
-static void do_machine_halt_nonsmp(void)
-{
-        if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
-		__cpcmd(vmhalt_cmd, NULL, 0, NULL);
-        signal_processor(smp_processor_id(), sigp_stop_and_store_status);
-}
-
-static void do_machine_power_off_nonsmp(void)
-{
-        if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
-		__cpcmd(vmpoff_cmd, NULL, 0, NULL);
-        signal_processor(smp_processor_id(), sigp_stop_and_store_status);
-}
-
-void (*_machine_restart)(char *command) = do_machine_restart_nonsmp;
-void (*_machine_halt)(void) = do_machine_halt_nonsmp;
-void (*_machine_power_off)(void) = do_machine_power_off_nonsmp;
-#endif
-
  /*
  * Reboot, halt and power_off stubs. They just call _machine_restart,
  * _machine_halt or _machine_power_off. 
@@ -559,7 +458,9 @@ setup_resources(void)
 	data_resource.start = (unsigned long) &_etext;
 	data_resource.end = (unsigned long) &_edata - 1;
 
-	for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
+	for (i = 0; i < MEMORY_CHUNKS; i++) {
+		if (!memory_chunk[i].size)
+			continue;
 		res = alloc_bootmem_low(sizeof(struct resource));
 		res->flags = IORESOURCE_BUSY | IORESOURCE_MEM;
 		switch (memory_chunk[i].type) {
@@ -617,7 +518,7 @@ EXPORT_SYMBOL_GPL(real_memory_size);
 static void __init setup_memory_end(void)
 {
 	unsigned long memory_size;
-	unsigned long max_mem, max_phys;
+	unsigned long max_mem;
 	int i;
 
 #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
@@ -625,10 +526,31 @@ static void __init setup_memory_end(void)
 		memory_end = ZFCPDUMP_HSA_SIZE;
 #endif
 	memory_size = 0;
-	max_phys = VMALLOC_END_INIT - VMALLOC_MIN_SIZE;
 	memory_end &= PAGE_MASK;
 
-	max_mem = memory_end ? min(max_phys, memory_end) : max_phys;
+	max_mem = memory_end ? min(VMALLOC_START, memory_end) : VMALLOC_START;
+	memory_end = min(max_mem, memory_end);
+
+	/*
+	 * Make sure all chunks are MAX_ORDER aligned so we don't need the
+	 * extra checks that HOLES_IN_ZONE would require.
+	 */
+	for (i = 0; i < MEMORY_CHUNKS; i++) {
+		unsigned long start, end;
+		struct mem_chunk *chunk;
+		unsigned long align;
+
+		chunk = &memory_chunk[i];
+		align = 1UL << (MAX_ORDER + PAGE_SHIFT - 1);
+		start = (chunk->addr + align - 1) & ~(align - 1);
+		end = (chunk->addr + chunk->size) & ~(align - 1);
+		if (start >= end)
+			memset(chunk, 0, sizeof(*chunk));
+		else {
+			chunk->addr = start;
+			chunk->size = end - start;
+		}
+	}
 
 	for (i = 0; i < MEMORY_CHUNKS; i++) {
 		struct mem_chunk *chunk = &memory_chunk[i];
@@ -890,7 +812,7 @@ setup_arch(char **cmdline_p)
 
 	parse_early_param();
 
-	setup_ipl_info();
+	setup_ipl();
 	setup_memory_end();
 	setup_addressing_mode();
 	setup_memory();
@@ -899,7 +821,6 @@ setup_arch(char **cmdline_p)
 
         cpu_init();
         __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
-	smp_setup_cpu_possible_map();
 
 	/*
 	 * Setup capabilities (ELF_HWCAP & ELF_PLATFORM).
@@ -920,7 +841,7 @@ setup_arch(char **cmdline_p)
 
 void __cpuinit print_cpu_info(struct cpuinfo_S390 *cpuinfo)
 {
-   printk("cpu %d "
+   printk(KERN_INFO "cpu %d "
 #ifdef CONFIG_SMP
            "phys_idx=%d "
 #endif
@@ -996,7 +917,7 @@ static void *c_next(struct seq_file *m, void *v, loff_t *pos)
 static void c_stop(struct seq_file *m, void *v)
 {
 }
-struct seq_operations cpuinfo_op = {
+const struct seq_operations cpuinfo_op = {
 	.start	= c_start,
 	.next	= c_next,
 	.stop	= c_stop,

+ 13 - 7
arch/s390/kernel/signal.c

@@ -471,6 +471,7 @@ void do_signal(struct pt_regs *regs)
 
 	if (signr > 0) {
 		/* Whee!  Actually deliver the signal.  */
+		int ret;
 #ifdef CONFIG_COMPAT
 		if (test_thread_flag(TIF_31BIT)) {
 			extern int handle_signal32(unsigned long sig,
@@ -478,15 +479,12 @@ void do_signal(struct pt_regs *regs)
 						   siginfo_t *info,
 						   sigset_t *oldset,
 						   struct pt_regs *regs);
-			if (handle_signal32(
-				    signr, &ka, &info, oldset, regs) == 0) {
-				if (test_thread_flag(TIF_RESTORE_SIGMASK))
-					clear_thread_flag(TIF_RESTORE_SIGMASK);
-			}
-			return;
+			ret = handle_signal32(signr, &ka, &info, oldset, regs);
 	        }
+		else
 #endif
-		if (handle_signal(signr, &ka, &info, oldset, regs) == 0) {
+			ret = handle_signal(signr, &ka, &info, oldset, regs);
+		if (!ret) {
 			/*
 			 * A signal was successfully delivered; the saved
 			 * sigmask will have been stored in the signal frame,
@@ -495,6 +493,14 @@ void do_signal(struct pt_regs *regs)
 			 */
 			if (test_thread_flag(TIF_RESTORE_SIGMASK))
 				clear_thread_flag(TIF_RESTORE_SIGMASK);
+
+			/*
+			 * If we would have taken a single-step trap
+			 * for a normal instruction, act like we took
+			 * one for the handler setup.
+			 */
+			if (current->thread.per_info.single_step)
+				set_thread_flag(TIF_SINGLE_STEP);
 		}
 		return;
 	}

+ 422 - 153
arch/s390/kernel/smp.c

@@ -42,6 +42,7 @@
 #include <asm/tlbflush.h>
 #include <asm/timer.h>
 #include <asm/lowcore.h>
+#include <asm/sclp.h>
 #include <asm/cpu.h>
 
 /*
@@ -53,11 +54,27 @@ EXPORT_SYMBOL(lowcore_ptr);
 cpumask_t cpu_online_map = CPU_MASK_NONE;
 EXPORT_SYMBOL(cpu_online_map);
 
-cpumask_t cpu_possible_map = CPU_MASK_NONE;
+cpumask_t cpu_possible_map = CPU_MASK_ALL;
 EXPORT_SYMBOL(cpu_possible_map);
 
 static struct task_struct *current_set[NR_CPUS];
 
+static u8 smp_cpu_type;
+static int smp_use_sigp_detection;
+
+enum s390_cpu_state {
+	CPU_STATE_STANDBY,
+	CPU_STATE_CONFIGURED,
+};
+
+#ifdef CONFIG_HOTPLUG_CPU
+static DEFINE_MUTEX(smp_cpu_state_mutex);
+#endif
+static int smp_cpu_state[NR_CPUS];
+
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
+DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
+
 static void smp_ext_bitcall(int, ec_bit_sig);
 
 /*
@@ -193,6 +210,33 @@ int smp_call_function_single(int cpu, void (*func) (void *info), void *info,
 }
 EXPORT_SYMBOL(smp_call_function_single);
 
+/**
+ * smp_call_function_mask(): Run a function on a set of other CPUs.
+ * @mask: The set of cpus to run on.  Must not include the current cpu.
+ * @func: The function to run. This must be fast and non-blocking.
+ * @info: An arbitrary pointer to pass to the function.
+ * @wait: If true, wait (atomically) until function has completed on other CPUs.
+ *
+ * Returns 0 on success, else a negative status code.
+ *
+ * If @wait is true, then returns once @func has returned; otherwise
+ * it returns just before the target cpu calls @func.
+ *
+ * You must not call this function with disabled interrupts or from a
+ * hardware interrupt handler or from a bottom half handler.
+ */
+int
+smp_call_function_mask(cpumask_t mask,
+			void (*func)(void *), void *info,
+			int wait)
+{
+	preempt_disable();
+	__smp_call_function_map(func, info, 0, wait, mask);
+	preempt_enable();
+	return 0;
+}
+EXPORT_SYMBOL(smp_call_function_mask);
+
 void smp_send_stop(void)
 {
 	int cpu, rc;
@@ -216,33 +260,6 @@ void smp_send_stop(void)
 	}
 }
 
-/*
- * Reboot, halt and power_off routines for SMP.
- */
-void machine_restart_smp(char *__unused)
-{
-	smp_send_stop();
-	do_reipl();
-}
-
-void machine_halt_smp(void)
-{
-	smp_send_stop();
-	if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
-		__cpcmd(vmhalt_cmd, NULL, 0, NULL);
-	signal_processor(smp_processor_id(), sigp_stop_and_store_status);
-	for (;;);
-}
-
-void machine_power_off_smp(void)
-{
-	smp_send_stop();
-	if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
-		__cpcmd(vmpoff_cmd, NULL, 0, NULL);
-	signal_processor(smp_processor_id(), sigp_stop_and_store_status);
-	for (;;);
-}
-
 /*
  * This is the main routine where commands issued by other
  * cpus are handled.
@@ -355,6 +372,13 @@ void smp_ctl_clear_bit(int cr, int bit)
 }
 EXPORT_SYMBOL(smp_ctl_clear_bit);
 
+/*
+ * In early ipl state a temp. logically cpu number is needed, so the sigp
+ * functions can be used to sense other cpus. Since NR_CPUS is >= 2 on
+ * CONFIG_SMP and the ipl cpu is logical cpu 0, it must be 1.
+ */
+#define CPU_INIT_NO	1
+
 #if defined(CONFIG_ZFCPDUMP) || defined(CONFIG_ZFCPDUMP_MODULE)
 
 /*
@@ -375,9 +399,10 @@ static void __init smp_get_save_area(unsigned int cpu, unsigned int phy_cpu)
 		       "kernel was compiled with NR_CPUS=%i\n", cpu, NR_CPUS);
 		return;
 	}
-	zfcpdump_save_areas[cpu] = alloc_bootmem(sizeof(union save_area));
-	__cpu_logical_map[1] = (__u16) phy_cpu;
-	while (signal_processor(1, sigp_stop_and_store_status) == sigp_busy)
+	zfcpdump_save_areas[cpu] = kmalloc(sizeof(union save_area), GFP_KERNEL);
+	__cpu_logical_map[CPU_INIT_NO] = (__u16) phy_cpu;
+	while (signal_processor(CPU_INIT_NO, sigp_stop_and_store_status) ==
+	       sigp_busy)
 		cpu_relax();
 	memcpy(zfcpdump_save_areas[cpu],
 	       (void *)(unsigned long) store_prefix() + SAVE_AREA_BASE,
@@ -397,32 +422,155 @@ static inline void smp_get_save_area(unsigned int cpu, unsigned int phy_cpu) { }
 
 #endif /* CONFIG_ZFCPDUMP || CONFIG_ZFCPDUMP_MODULE */
 
-/*
- * Lets check how many CPUs we have.
- */
-static unsigned int __init smp_count_cpus(void)
+static int cpu_stopped(int cpu)
 {
-	unsigned int cpu, num_cpus;
-	__u16 boot_cpu_addr;
+	__u32 status;
 
-	/*
-	 * cpu 0 is the boot cpu. See smp_prepare_boot_cpu.
-	 */
+	/* Check for stopped state */
+	if (signal_processor_ps(&status, 0, cpu, sigp_sense) ==
+	    sigp_status_stored) {
+		if (status & 0x40)
+			return 1;
+	}
+	return 0;
+}
+
+static int cpu_known(int cpu_id)
+{
+	int cpu;
+
+	for_each_present_cpu(cpu) {
+		if (__cpu_logical_map[cpu] == cpu_id)
+			return 1;
+	}
+	return 0;
+}
+
+static int smp_rescan_cpus_sigp(cpumask_t avail)
+{
+	int cpu_id, logical_cpu;
+
+	logical_cpu = first_cpu(avail);
+	if (logical_cpu == NR_CPUS)
+		return 0;
+	for (cpu_id = 0; cpu_id <= 65535; cpu_id++) {
+		if (cpu_known(cpu_id))
+			continue;
+		__cpu_logical_map[logical_cpu] = cpu_id;
+		if (!cpu_stopped(logical_cpu))
+			continue;
+		cpu_set(logical_cpu, cpu_present_map);
+		smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED;
+		logical_cpu = next_cpu(logical_cpu, avail);
+		if (logical_cpu == NR_CPUS)
+			break;
+	}
+	return 0;
+}
+
+static int smp_rescan_cpus_sclp(cpumask_t avail)
+{
+	struct sclp_cpu_info *info;
+	int cpu_id, logical_cpu, cpu;
+	int rc;
+
+	logical_cpu = first_cpu(avail);
+	if (logical_cpu == NR_CPUS)
+		return 0;
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+	rc = sclp_get_cpu_info(info);
+	if (rc)
+		goto out;
+	for (cpu = 0; cpu < info->combined; cpu++) {
+		if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type)
+			continue;
+		cpu_id = info->cpu[cpu].address;
+		if (cpu_known(cpu_id))
+			continue;
+		__cpu_logical_map[logical_cpu] = cpu_id;
+		cpu_set(logical_cpu, cpu_present_map);
+		if (cpu >= info->configured)
+			smp_cpu_state[logical_cpu] = CPU_STATE_STANDBY;
+		else
+			smp_cpu_state[logical_cpu] = CPU_STATE_CONFIGURED;
+		logical_cpu = next_cpu(logical_cpu, avail);
+		if (logical_cpu == NR_CPUS)
+			break;
+	}
+out:
+	kfree(info);
+	return rc;
+}
+
+static int smp_rescan_cpus(void)
+{
+	cpumask_t avail;
+
+	cpus_xor(avail, cpu_possible_map, cpu_present_map);
+	if (smp_use_sigp_detection)
+		return smp_rescan_cpus_sigp(avail);
+	else
+		return smp_rescan_cpus_sclp(avail);
+}
+
+static void __init smp_detect_cpus(void)
+{
+	unsigned int cpu, c_cpus, s_cpus;
+	struct sclp_cpu_info *info;
+	u16 boot_cpu_addr, cpu_addr;
+
+	c_cpus = 1;
+	s_cpus = 0;
 	boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
-	current_thread_info()->cpu = 0;
-	num_cpus = 1;
-	for (cpu = 0; cpu <= 65535; cpu++) {
-		if ((__u16) cpu == boot_cpu_addr)
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		panic("smp_detect_cpus failed to allocate memory\n");
+	/* Use sigp detection algorithm if sclp doesn't work. */
+	if (sclp_get_cpu_info(info)) {
+		smp_use_sigp_detection = 1;
+		for (cpu = 0; cpu <= 65535; cpu++) {
+			if (cpu == boot_cpu_addr)
+				continue;
+			__cpu_logical_map[CPU_INIT_NO] = cpu;
+			if (!cpu_stopped(CPU_INIT_NO))
+				continue;
+			smp_get_save_area(c_cpus, cpu);
+			c_cpus++;
+		}
+		goto out;
+	}
+
+	if (info->has_cpu_type) {
+		for (cpu = 0; cpu < info->combined; cpu++) {
+			if (info->cpu[cpu].address == boot_cpu_addr) {
+				smp_cpu_type = info->cpu[cpu].type;
+				break;
+			}
+		}
+	}
+
+	for (cpu = 0; cpu < info->combined; cpu++) {
+		if (info->has_cpu_type && info->cpu[cpu].type != smp_cpu_type)
+			continue;
+		cpu_addr = info->cpu[cpu].address;
+		if (cpu_addr == boot_cpu_addr)
 			continue;
-		__cpu_logical_map[1] = (__u16) cpu;
-		if (signal_processor(1, sigp_sense) == sigp_not_operational)
+		__cpu_logical_map[CPU_INIT_NO] = cpu_addr;
+		if (!cpu_stopped(CPU_INIT_NO)) {
+			s_cpus++;
 			continue;
-		smp_get_save_area(num_cpus, cpu);
-		num_cpus++;
+		}
+		smp_get_save_area(c_cpus, cpu_addr);
+		c_cpus++;
 	}
-	printk("Detected %d CPU's\n", (int) num_cpus);
-	printk("Boot cpu address %2X\n", boot_cpu_addr);
-	return num_cpus;
+out:
+	kfree(info);
+	printk(KERN_INFO "CPUs: %d configured, %d standby\n", c_cpus, s_cpus);
+	get_online_cpus();
+	smp_rescan_cpus();
+	put_online_cpus();
 }
 
 /*
@@ -453,8 +601,6 @@ int __cpuinit start_secondary(void *cpuvoid)
 	return 0;
 }
 
-DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
-
 static void __init smp_create_idle(unsigned int cpu)
 {
 	struct task_struct *p;
@@ -470,37 +616,82 @@ static void __init smp_create_idle(unsigned int cpu)
 	spin_lock_init(&(&per_cpu(s390_idle, cpu))->lock);
 }
 
-static int cpu_stopped(int cpu)
+static int __cpuinit smp_alloc_lowcore(int cpu)
 {
-	__u32 status;
+	unsigned long async_stack, panic_stack;
+	struct _lowcore *lowcore;
+	int lc_order;
+
+	lc_order = sizeof(long) == 8 ? 1 : 0;
+	lowcore = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, lc_order);
+	if (!lowcore)
+		return -ENOMEM;
+	async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
+	if (!async_stack)
+		goto out_async_stack;
+	panic_stack = __get_free_page(GFP_KERNEL);
+	if (!panic_stack)
+		goto out_panic_stack;
+
+	*lowcore = S390_lowcore;
+	lowcore->async_stack = async_stack + ASYNC_SIZE;
+	lowcore->panic_stack = panic_stack + PAGE_SIZE;
 
-	/* Check for stopped state */
-	if (signal_processor_ps(&status, 0, cpu, sigp_sense) ==
-	    sigp_status_stored) {
-		if (status & 0x40)
-			return 1;
+#ifndef CONFIG_64BIT
+	if (MACHINE_HAS_IEEE) {
+		unsigned long save_area;
+
+		save_area = get_zeroed_page(GFP_KERNEL);
+		if (!save_area)
+			goto out_save_area;
+		lowcore->extended_save_area_addr = (u32) save_area;
 	}
+#endif
+	lowcore_ptr[cpu] = lowcore;
 	return 0;
+
+#ifndef CONFIG_64BIT
+out_save_area:
+	free_page(panic_stack);
+#endif
+out_panic_stack:
+	free_pages(async_stack, ASYNC_ORDER);
+out_async_stack:
+	free_pages((unsigned long) lowcore, lc_order);
+	return -ENOMEM;
 }
 
-/* Upping and downing of CPUs */
+#ifdef CONFIG_HOTPLUG_CPU
+static void smp_free_lowcore(int cpu)
+{
+	struct _lowcore *lowcore;
+	int lc_order;
+
+	lc_order = sizeof(long) == 8 ? 1 : 0;
+	lowcore = lowcore_ptr[cpu];
+#ifndef CONFIG_64BIT
+	if (MACHINE_HAS_IEEE)
+		free_page((unsigned long) lowcore->extended_save_area_addr);
+#endif
+	free_page(lowcore->panic_stack - PAGE_SIZE);
+	free_pages(lowcore->async_stack - ASYNC_SIZE, ASYNC_ORDER);
+	free_pages((unsigned long) lowcore, lc_order);
+	lowcore_ptr[cpu] = NULL;
+}
+#endif /* CONFIG_HOTPLUG_CPU */
 
-int __cpu_up(unsigned int cpu)
+/* Upping and downing of CPUs */
+int __cpuinit __cpu_up(unsigned int cpu)
 {
 	struct task_struct *idle;
 	struct _lowcore *cpu_lowcore;
 	struct stack_frame *sf;
 	sigp_ccode ccode;
-	int curr_cpu;
 
-	for (curr_cpu = 0; curr_cpu <= 65535; curr_cpu++) {
-		__cpu_logical_map[cpu] = (__u16) curr_cpu;
-		if (cpu_stopped(cpu))
-			break;
-	}
-
-	if (!cpu_stopped(cpu))
-		return -ENODEV;
+	if (smp_cpu_state[cpu] != CPU_STATE_CONFIGURED)
+		return -EIO;
+	if (smp_alloc_lowcore(cpu))
+		return -ENOMEM;
 
 	ccode = signal_processor_p((__u32)(unsigned long)(lowcore_ptr[cpu]),
 				   cpu, sigp_set_prefix);
@@ -515,6 +706,7 @@ int __cpu_up(unsigned int cpu)
 	cpu_lowcore = lowcore_ptr[cpu];
 	cpu_lowcore->kernel_stack = (unsigned long)
 		task_stack_page(idle) + THREAD_SIZE;
+	cpu_lowcore->thread_info = (unsigned long) task_thread_info(idle);
 	sf = (struct stack_frame *) (cpu_lowcore->kernel_stack
 				     - sizeof(struct pt_regs)
 				     - sizeof(struct stack_frame));
@@ -528,6 +720,8 @@ int __cpu_up(unsigned int cpu)
 	cpu_lowcore->percpu_offset = __per_cpu_offset[cpu];
 	cpu_lowcore->current_task = (unsigned long) idle;
 	cpu_lowcore->cpu_data.cpu_nr = cpu;
+	cpu_lowcore->softirq_pending = 0;
+	cpu_lowcore->ext_call_fast = 0;
 	eieio();
 
 	while (signal_processor(cpu, sigp_restart) == sigp_busy)
@@ -538,44 +732,20 @@ int __cpu_up(unsigned int cpu)
 	return 0;
 }
 
-static unsigned int __initdata additional_cpus;
-static unsigned int __initdata possible_cpus;
-
-void __init smp_setup_cpu_possible_map(void)
+static int __init setup_possible_cpus(char *s)
 {
-	unsigned int phy_cpus, pos_cpus, cpu;
-
-	phy_cpus = smp_count_cpus();
-	pos_cpus = min(phy_cpus + additional_cpus, (unsigned int) NR_CPUS);
-
-	if (possible_cpus)
-		pos_cpus = min(possible_cpus, (unsigned int) NR_CPUS);
+	int pcpus, cpu;
 
-	for (cpu = 0; cpu < pos_cpus; cpu++)
+	pcpus = simple_strtoul(s, NULL, 0);
+	cpu_possible_map = cpumask_of_cpu(0);
+	for (cpu = 1; cpu < pcpus && cpu < NR_CPUS; cpu++)
 		cpu_set(cpu, cpu_possible_map);
-
-	phy_cpus = min(phy_cpus, pos_cpus);
-
-	for (cpu = 0; cpu < phy_cpus; cpu++)
-		cpu_set(cpu, cpu_present_map);
-}
-
-#ifdef CONFIG_HOTPLUG_CPU
-
-static int __init setup_additional_cpus(char *s)
-{
-	additional_cpus = simple_strtoul(s, NULL, 0);
-	return 0;
-}
-early_param("additional_cpus", setup_additional_cpus);
-
-static int __init setup_possible_cpus(char *s)
-{
-	possible_cpus = simple_strtoul(s, NULL, 0);
 	return 0;
 }
 early_param("possible_cpus", setup_possible_cpus);
 
+#ifdef CONFIG_HOTPLUG_CPU
+
 int __cpu_disable(void)
 {
 	struct ec_creg_mask_parms cr_parms;
@@ -612,7 +782,8 @@ void __cpu_die(unsigned int cpu)
 	/* Wait until target cpu is down */
 	while (!smp_cpu_not_running(cpu))
 		cpu_relax();
-	printk("Processor %d spun down\n", cpu);
+	smp_free_lowcore(cpu);
+	printk(KERN_INFO "Processor %d spun down\n", cpu);
 }
 
 void cpu_die(void)
@@ -625,49 +796,19 @@ void cpu_die(void)
 
 #endif /* CONFIG_HOTPLUG_CPU */
 
-/*
- *	Cycle through the processors and setup structures.
- */
-
 void __init smp_prepare_cpus(unsigned int max_cpus)
 {
-	unsigned long stack;
 	unsigned int cpu;
-	int i;
+
+	smp_detect_cpus();
 
 	/* request the 0x1201 emergency signal external interrupt */
 	if (register_external_interrupt(0x1201, do_ext_call_interrupt) != 0)
 		panic("Couldn't request external interrupt 0x1201");
 	memset(lowcore_ptr, 0, sizeof(lowcore_ptr));
-	/*
-	 *  Initialize prefix pages and stacks for all possible cpus
-	 */
 	print_cpu_info(&S390_lowcore.cpu_data);
+	smp_alloc_lowcore(smp_processor_id());
 
-	for_each_possible_cpu(i) {
-		lowcore_ptr[i] = (struct _lowcore *)
-			__get_free_pages(GFP_KERNEL | GFP_DMA,
-					 sizeof(void*) == 8 ? 1 : 0);
-		stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
-		if (!lowcore_ptr[i] || !stack)
-			panic("smp_boot_cpus failed to allocate memory\n");
-
-		*(lowcore_ptr[i]) = S390_lowcore;
-		lowcore_ptr[i]->async_stack = stack + ASYNC_SIZE;
-		stack = __get_free_pages(GFP_KERNEL, 0);
-		if (!stack)
-			panic("smp_boot_cpus failed to allocate memory\n");
-		lowcore_ptr[i]->panic_stack = stack + PAGE_SIZE;
-#ifndef CONFIG_64BIT
-		if (MACHINE_HAS_IEEE) {
-			lowcore_ptr[i]->extended_save_area_addr =
-				(__u32) __get_free_pages(GFP_KERNEL, 0);
-			if (!lowcore_ptr[i]->extended_save_area_addr)
-				panic("smp_boot_cpus failed to "
-				      "allocate memory\n");
-		}
-#endif
-	}
 #ifndef CONFIG_64BIT
 	if (MACHINE_HAS_IEEE)
 		ctl_set_bit(14, 29); /* enable extended save area */
@@ -683,15 +824,17 @@ void __init smp_prepare_boot_cpu(void)
 {
 	BUG_ON(smp_processor_id() != 0);
 
+	current_thread_info()->cpu = 0;
+	cpu_set(0, cpu_present_map);
 	cpu_set(0, cpu_online_map);
 	S390_lowcore.percpu_offset = __per_cpu_offset[0];
 	current_set[0] = current;
+	smp_cpu_state[0] = CPU_STATE_CONFIGURED;
 	spin_lock_init(&(&__get_cpu_var(s390_idle))->lock);
 }
 
 void __init smp_cpus_done(unsigned int max_cpus)
 {
-	cpu_present_map = cpu_possible_map;
 }
 
 /*
@@ -705,7 +848,79 @@ int setup_profiling_timer(unsigned int multiplier)
 	return 0;
 }
 
-static DEFINE_PER_CPU(struct cpu, cpu_devices);
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t cpu_configure_show(struct sys_device *dev, char *buf)
+{
+	ssize_t count;
+
+	mutex_lock(&smp_cpu_state_mutex);
+	count = sprintf(buf, "%d\n", smp_cpu_state[dev->id]);
+	mutex_unlock(&smp_cpu_state_mutex);
+	return count;
+}
+
+static ssize_t cpu_configure_store(struct sys_device *dev, const char *buf,
+				   size_t count)
+{
+	int cpu = dev->id;
+	int val, rc;
+	char delim;
+
+	if (sscanf(buf, "%d %c", &val, &delim) != 1)
+		return -EINVAL;
+	if (val != 0 && val != 1)
+		return -EINVAL;
+
+	mutex_lock(&smp_cpu_state_mutex);
+	get_online_cpus();
+	rc = -EBUSY;
+	if (cpu_online(cpu))
+		goto out;
+	rc = 0;
+	switch (val) {
+	case 0:
+		if (smp_cpu_state[cpu] == CPU_STATE_CONFIGURED) {
+			rc = sclp_cpu_deconfigure(__cpu_logical_map[cpu]);
+			if (!rc)
+				smp_cpu_state[cpu] = CPU_STATE_STANDBY;
+		}
+		break;
+	case 1:
+		if (smp_cpu_state[cpu] == CPU_STATE_STANDBY) {
+			rc = sclp_cpu_configure(__cpu_logical_map[cpu]);
+			if (!rc)
+				smp_cpu_state[cpu] = CPU_STATE_CONFIGURED;
+		}
+		break;
+	default:
+		break;
+	}
+out:
+	put_online_cpus();
+	mutex_unlock(&smp_cpu_state_mutex);
+	return rc ? rc : count;
+}
+static SYSDEV_ATTR(configure, 0644, cpu_configure_show, cpu_configure_store);
+#endif /* CONFIG_HOTPLUG_CPU */
+
+static ssize_t show_cpu_address(struct sys_device *dev, char *buf)
+{
+	return sprintf(buf, "%d\n", __cpu_logical_map[dev->id]);
+}
+static SYSDEV_ATTR(address, 0444, show_cpu_address, NULL);
+
+
+static struct attribute *cpu_common_attrs[] = {
+#ifdef CONFIG_HOTPLUG_CPU
+	&attr_configure.attr,
+#endif
+	&attr_address.attr,
+	NULL,
+};
+
+static struct attribute_group cpu_common_attr_group = {
+	.attrs = cpu_common_attrs,
+};
 
 static ssize_t show_capability(struct sys_device *dev, char *buf)
 {
@@ -750,15 +965,15 @@ static ssize_t show_idle_time(struct sys_device *dev, char *buf)
 }
 static SYSDEV_ATTR(idle_time_us, 0444, show_idle_time, NULL);
 
-static struct attribute *cpu_attrs[] = {
+static struct attribute *cpu_online_attrs[] = {
 	&attr_capability.attr,
 	&attr_idle_count.attr,
 	&attr_idle_time_us.attr,
 	NULL,
 };
 
-static struct attribute_group cpu_attr_group = {
-	.attrs = cpu_attrs,
+static struct attribute_group cpu_online_attr_group = {
+	.attrs = cpu_online_attrs,
 };
 
 static int __cpuinit smp_cpu_notify(struct notifier_block *self,
@@ -778,12 +993,12 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self,
 		idle->idle_time = 0;
 		idle->idle_count = 0;
 		spin_unlock_irq(&idle->lock);
-		if (sysfs_create_group(&s->kobj, &cpu_attr_group))
+		if (sysfs_create_group(&s->kobj, &cpu_online_attr_group))
 			return NOTIFY_BAD;
 		break;
 	case CPU_DEAD:
 	case CPU_DEAD_FROZEN:
-		sysfs_remove_group(&s->kobj, &cpu_attr_group);
+		sysfs_remove_group(&s->kobj, &cpu_online_attr_group);
 		break;
 	}
 	return NOTIFY_OK;
@@ -793,6 +1008,62 @@ static struct notifier_block __cpuinitdata smp_cpu_nb = {
 	.notifier_call = smp_cpu_notify,
 };
 
+static int smp_add_present_cpu(int cpu)
+{
+	struct cpu *c = &per_cpu(cpu_devices, cpu);
+	struct sys_device *s = &c->sysdev;
+	int rc;
+
+	c->hotpluggable = 1;
+	rc = register_cpu(c, cpu);
+	if (rc)
+		goto out;
+	rc = sysfs_create_group(&s->kobj, &cpu_common_attr_group);
+	if (rc)
+		goto out_cpu;
+	if (!cpu_online(cpu))
+		goto out;
+	rc = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
+	if (!rc)
+		return 0;
+	sysfs_remove_group(&s->kobj, &cpu_common_attr_group);
+out_cpu:
+#ifdef CONFIG_HOTPLUG_CPU
+	unregister_cpu(c);
+#endif
+out:
+	return rc;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static ssize_t rescan_store(struct sys_device *dev, const char *buf,
+			    size_t count)
+{
+	cpumask_t newcpus;
+	int cpu;
+	int rc;
+
+	mutex_lock(&smp_cpu_state_mutex);
+	get_online_cpus();
+	newcpus = cpu_present_map;
+	rc = smp_rescan_cpus();
+	if (rc)
+		goto out;
+	cpus_andnot(newcpus, cpu_present_map, newcpus);
+	for_each_cpu_mask(cpu, newcpus) {
+		rc = smp_add_present_cpu(cpu);
+		if (rc)
+			cpu_clear(cpu, cpu_present_map);
+	}
+	rc = 0;
+out:
+	put_online_cpus();
+	mutex_unlock(&smp_cpu_state_mutex);
+	return rc ? rc : count;
+}
+static SYSDEV_ATTR(rescan, 0200, NULL, rescan_store);
+#endif /* CONFIG_HOTPLUG_CPU */
+
 static int __init topology_init(void)
 {
 	int cpu;
@@ -800,16 +1071,14 @@ static int __init topology_init(void)
 
 	register_cpu_notifier(&smp_cpu_nb);
 
-	for_each_possible_cpu(cpu) {
-		struct cpu *c = &per_cpu(cpu_devices, cpu);
-		struct sys_device *s = &c->sysdev;
-
-		c->hotpluggable = 1;
-		register_cpu(c, cpu);
-		if (!cpu_online(cpu))
-			continue;
-		s = &c->sysdev;
-		rc = sysfs_create_group(&s->kobj, &cpu_attr_group);
+#ifdef CONFIG_HOTPLUG_CPU
+	rc = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
+			       &attr_rescan.attr);
+	if (rc)
+		return rc;
+#endif
+	for_each_present_cpu(cpu) {
+		rc = smp_add_present_cpu(cpu);
 		if (rc)
 			return rc;
 	}

+ 17 - 3
arch/s390/kernel/traps.c

@@ -31,6 +31,7 @@
 #include <linux/reboot.h>
 #include <linux/kprobes.h>
 #include <linux/bug.h>
+#include <linux/utsname.h>
 #include <asm/system.h>
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -168,9 +169,16 @@ void show_stack(struct task_struct *task, unsigned long *sp)
  */
 void dump_stack(void)
 {
+	printk("CPU: %d %s %s %.*s\n",
+	       task_thread_info(current)->cpu, print_tainted(),
+	       init_utsname()->release,
+	       (int)strcspn(init_utsname()->version, " "),
+	       init_utsname()->version);
+	printk("Process %s (pid: %d, task: %p, ksp: %p)\n",
+	       current->comm, current->pid, current,
+	       (void *) current->thread.ksp);
 	show_stack(NULL, NULL);
 }
-
 EXPORT_SYMBOL(dump_stack);
 
 static inline int mask_bits(struct pt_regs *regs, unsigned long bits)
@@ -258,8 +266,14 @@ void die(const char * str, struct pt_regs * regs, long err)
 	console_verbose();
 	spin_lock_irq(&die_lock);
 	bust_spinlocks(1);
-	printk("%s: %04lx [#%d]\n", str, err & 0xffff, ++die_counter);
-	print_modules();
+	printk("%s: %04lx [#%d] ", str, err & 0xffff, ++die_counter);
+#ifdef CONFIG_PREEMPT
+	printk("PREEMPT ");
+#endif
+#ifdef CONFIG_SMP
+	printk("SMP");
+#endif
+	printk("\n");
 	notify_die(DIE_OOPS, str, regs, err, current->thread.trap_no, SIGSEGV);
 	show_regs(regs);
 	bust_spinlocks(0);

+ 9 - 3
arch/s390/kernel/vmlinux.lds.S

@@ -17,6 +17,12 @@ ENTRY(_start)
 jiffies = jiffies_64;
 #endif
 
+PHDRS {
+	text PT_LOAD FLAGS(5);	/* R_E */
+	data PT_LOAD FLAGS(7);	/* RWE */
+	note PT_NOTE FLAGS(0);	/* ___ */
+}
+
 SECTIONS
 {
 	. = 0x00000000;
@@ -33,6 +39,9 @@ SECTIONS
 
 	_etext = .;		/* End of text section */
 
+	NOTES :text :note
+	BUG_TABLE :text
+
 	RODATA
 
 #ifdef CONFIG_SHARED_KERNEL
@@ -49,9 +58,6 @@ SECTIONS
 		__stop___ex_table = .;
 	}
 
-	NOTES
-	BUG_TABLE
-
 	.data : {		/* Data */
 		DATA_DATA
 		CONSTRUCTORS

+ 27 - 8
arch/s390/lib/spinlock.c

@@ -39,7 +39,7 @@ static inline void _raw_yield_cpu(int cpu)
 		_raw_yield();
 }
 
-void _raw_spin_lock_wait(raw_spinlock_t *lp, unsigned int pc)
+void _raw_spin_lock_wait(raw_spinlock_t *lp)
 {
 	int count = spin_retry;
 	unsigned int cpu = ~smp_processor_id();
@@ -53,15 +53,36 @@ void _raw_spin_lock_wait(raw_spinlock_t *lp, unsigned int pc)
 		}
 		if (__raw_spin_is_locked(lp))
 			continue;
-		if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) {
-			lp->owner_pc = pc;
+		if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0)
 			return;
-		}
 	}
 }
 EXPORT_SYMBOL(_raw_spin_lock_wait);
 
-int _raw_spin_trylock_retry(raw_spinlock_t *lp, unsigned int pc)
+void _raw_spin_lock_wait_flags(raw_spinlock_t *lp, unsigned long flags)
+{
+	int count = spin_retry;
+	unsigned int cpu = ~smp_processor_id();
+
+	local_irq_restore(flags);
+	while (1) {
+		if (count-- <= 0) {
+			unsigned int owner = lp->owner_cpu;
+			if (owner != 0)
+				_raw_yield_cpu(~owner);
+			count = spin_retry;
+		}
+		if (__raw_spin_is_locked(lp))
+			continue;
+		local_irq_disable();
+		if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0)
+			return;
+		local_irq_restore(flags);
+	}
+}
+EXPORT_SYMBOL(_raw_spin_lock_wait_flags);
+
+int _raw_spin_trylock_retry(raw_spinlock_t *lp)
 {
 	unsigned int cpu = ~smp_processor_id();
 	int count;
@@ -69,10 +90,8 @@ int _raw_spin_trylock_retry(raw_spinlock_t *lp, unsigned int pc)
 	for (count = spin_retry; count > 0; count--) {
 		if (__raw_spin_is_locked(lp))
 			continue;
-		if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0) {
-			lp->owner_pc = pc;
+		if (_raw_compare_and_swap(&lp->owner_cpu, 0, cpu) == 0)
 			return 1;
-		}
 	}
 	return 0;
 }

+ 1 - 1
arch/s390/mm/extmem.c

@@ -83,7 +83,7 @@ struct dcss_segment {
 };
 
 static DEFINE_MUTEX(dcss_lock);
-static struct list_head dcss_list = LIST_HEAD_INIT(dcss_list);
+static LIST_HEAD(dcss_list);
 static char *segtype_string[] = { "SW", "EW", "SR", "ER", "SN", "EN", "SC",
 					"EW/EN-MIXED" };
 

+ 10 - 16
arch/s390/mm/vmem.c

@@ -15,10 +15,6 @@
 #include <asm/setup.h>
 #include <asm/tlbflush.h>
 
-unsigned long vmalloc_end;
-EXPORT_SYMBOL(vmalloc_end);
-
-static struct page *vmem_map;
 static DEFINE_MUTEX(vmem_mutex);
 
 struct memory_segment {
@@ -188,8 +184,8 @@ static int vmem_add_mem_map(unsigned long start, unsigned long size)
 	pte_t  pte;
 	int ret = -ENOMEM;
 
-	map_start = vmem_map + PFN_DOWN(start);
-	map_end	= vmem_map + PFN_DOWN(start + size);
+	map_start = VMEM_MAP + PFN_DOWN(start);
+	map_end	= VMEM_MAP + PFN_DOWN(start + size);
 
 	start_addr = (unsigned long) map_start & PAGE_MASK;
 	end_addr = PFN_ALIGN((unsigned long) map_end);
@@ -240,10 +236,10 @@ static int vmem_add_mem(unsigned long start, unsigned long size)
 {
 	int ret;
 
-	ret = vmem_add_range(start, size);
+	ret = vmem_add_mem_map(start, size);
 	if (ret)
 		return ret;
-	return vmem_add_mem_map(start, size);
+	return vmem_add_range(start, size);
 }
 
 /*
@@ -254,7 +250,7 @@ static int insert_memory_segment(struct memory_segment *seg)
 {
 	struct memory_segment *tmp;
 
-	if (PFN_DOWN(seg->start + seg->size) > max_pfn ||
+	if (seg->start + seg->size >= VMALLOC_START ||
 	    seg->start + seg->size < seg->start)
 		return -ERANGE;
 
@@ -357,17 +353,15 @@ out:
 
 /*
  * map whole physical memory to virtual memory (identity mapping)
+ * we reserve enough space in the vmalloc area for vmemmap to hotplug
+ * additional memory segments.
  */
 void __init vmem_map_init(void)
 {
-	unsigned long map_size;
 	int i;
 
-	map_size = ALIGN(max_low_pfn, MAX_ORDER_NR_PAGES) * sizeof(struct page);
-	vmalloc_end = PFN_ALIGN(VMALLOC_END_INIT) - PFN_ALIGN(map_size);
-	vmem_map = (struct page *) vmalloc_end;
-	NODE_DATA(0)->node_mem_map = vmem_map;
-
+	BUILD_BUG_ON((unsigned long)VMEM_MAP + VMEM_MAP_SIZE > VMEM_MAP_MAX);
+	NODE_DATA(0)->node_mem_map = VMEM_MAP;
 	for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++)
 		vmem_add_mem(memory_chunk[i].addr, memory_chunk[i].size);
 }
@@ -382,7 +376,7 @@ static int __init vmem_convert_memory_chunk(void)
 	int i;
 
 	mutex_lock(&vmem_mutex);
-	for (i = 0; i < MEMORY_CHUNKS && memory_chunk[i].size > 0; i++) {
+	for (i = 0; i < MEMORY_CHUNKS; i++) {
 		if (!memory_chunk[i].size)
 			continue;
 		seg = kzalloc(sizeof(*seg), GFP_KERNEL);

+ 61 - 2
drivers/crypto/Kconfig

@@ -48,8 +48,6 @@ config CRYPTO_DEV_PADLOCK_SHA
 	  If unsure say M. The compiled module will be
 	  called padlock-sha.ko
 
-source "arch/s390/crypto/Kconfig"
-
 config CRYPTO_DEV_GEODE
 	tristate "Support for the Geode LX AES engine"
 	depends on X86_32 && PCI
@@ -83,6 +81,67 @@ config ZCRYPT_MONOLITHIC
 	  that contains all parts of the crypto device driver (ap bus,
 	  request router and all the card drivers).
 
+config CRYPTO_SHA1_S390
+	tristate "SHA1 digest algorithm"
+	depends on S390
+	select CRYPTO_ALGAPI
+	help
+	  This is the s390 hardware accelerated implementation of the
+	  SHA-1 secure hash standard (FIPS 180-1/DFIPS 180-2).
+
+config CRYPTO_SHA256_S390
+	tristate "SHA256 digest algorithm"
+	depends on S390
+	select CRYPTO_ALGAPI
+	help
+	  This is the s390 hardware accelerated implementation of the
+	  SHA256 secure hash standard (DFIPS 180-2).
+
+	  This version of SHA implements a 256 bit hash with 128 bits of
+	  security against collision attacks.
+
+config CRYPTO_DES_S390
+	tristate "DES and Triple DES cipher algorithms"
+	depends on S390
+	select CRYPTO_ALGAPI
+	select CRYPTO_BLKCIPHER
+	help
+	  This us the s390 hardware accelerated implementation of the
+	  DES cipher algorithm (FIPS 46-2), and Triple DES EDE (FIPS 46-3).
+
+config CRYPTO_AES_S390
+	tristate "AES cipher algorithms"
+	depends on S390
+	select CRYPTO_ALGAPI
+	select CRYPTO_BLKCIPHER
+	help
+	  This is the s390 hardware accelerated implementation of the
+	  AES cipher algorithms (FIPS-197). AES uses the Rijndael
+	  algorithm.
+
+	  Rijndael appears to be consistently a very good performer in
+	  both hardware and software across a wide range of computing
+	  environments regardless of its use in feedback or non-feedback
+	  modes. Its key setup time is excellent, and its key agility is
+	  good. Rijndael's very low memory requirements make it very well
+	  suited for restricted-space environments, in which it also
+	  demonstrates excellent performance. Rijndael's operations are
+	  among the easiest to defend against power and timing attacks.
+
+	  On s390 the System z9-109 currently only supports the key size
+	  of 128 bit.
+
+config S390_PRNG
+	tristate "Pseudo random number generator device driver"
+	depends on S390
+	default "m"
+	help
+	  Select this option if you want to use the s390 pseudo random number
+	  generator. The PRNG is part of the cryptographic processor functions
+	  and uses triple-DES to generate secure random numbers like the
+	  ANSI X9.17 standard. The PRNG is usable via the char device
+	  /dev/prandom.
+
 config CRYPTO_DEV_HIFN_795X
 	tristate "Driver HIFN 795x crypto accelerator chips"
 	select CRYPTO_DES

+ 2 - 2
drivers/s390/block/Makefile

@@ -2,8 +2,8 @@
 # S/390 block devices
 #
 
-dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_9343_erp.o
-dasd_fba_mod-objs  := dasd_fba.o dasd_3370_erp.o dasd_9336_erp.o
+dasd_eckd_mod-objs := dasd_eckd.o dasd_3990_erp.o dasd_alias.o
+dasd_fba_mod-objs  := dasd_fba.o
 dasd_diag_mod-objs := dasd_diag.o
 dasd_mod-objs      := dasd.o dasd_ioctl.o dasd_proc.o dasd_devmap.o \
 			dasd_genhd.o dasd_erp.o

File diff suppressed because it is too large
+ 316 - 400
drivers/s390/block/dasd.c


+ 0 - 84
drivers/s390/block/dasd_3370_erp.c

@@ -1,84 +0,0 @@
-/*
- * File...........: linux/drivers/s390/block/dasd_3370_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(3370)"
-
-#include "dasd_int.h"
-
-
-/*
- * DASD_3370_ERP_EXAMINE
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recover error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   The logic is based on the 'IBM 3880 Storage Control Reference' manual
- *   'Chapter 7. 3370 Sense Data'.
- *
- * RETURN VALUES
- *   dasd_era_none	no error
- *   dasd_era_fatal	for all fatal (unrecoverable errors)
- *   dasd_era_recover	for all others.
- */
-dasd_era_t
-dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-	char *sense = irb->ecw;
-
-	/* check for successful execution first */
-	if (irb->scsw.cstat == 0x00 &&
-	    irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-		return dasd_era_none;
-	if (sense[0] & 0x80) {	/* CMD reject */
-		return dasd_era_fatal;
-	}
-	if (sense[0] & 0x40) {	/* Drive offline */
-		return dasd_era_recover;
-	}
-	if (sense[0] & 0x20) {	/* Bus out parity */
-		return dasd_era_recover;
-	}
-	if (sense[0] & 0x10) {	/* equipment check */
-		if (sense[1] & 0x80) {
-			return dasd_era_fatal;
-		}
-		return dasd_era_recover;
-	}
-	if (sense[0] & 0x08) {	/* data check */
-		if (sense[1] & 0x80) {
-			return dasd_era_fatal;
-		}
-		return dasd_era_recover;
-	}
-	if (sense[0] & 0x04) {	/* overrun */
-		if (sense[1] & 0x80) {
-			return dasd_era_fatal;
-		}
-		return dasd_era_recover;
-	}
-	if (sense[1] & 0x40) {	/* invalid blocksize */
-		return dasd_era_fatal;
-	}
-	if (sense[1] & 0x04) {	/* file protected */
-		return dasd_era_recover;
-	}
-	if (sense[1] & 0x01) {	/* operation incomplete */
-		return dasd_era_recover;
-	}
-	if (sense[2] & 0x80) {	/* check data erroor */
-		return dasd_era_recover;
-	}
-	if (sense[2] & 0x10) {	/* Env. data present */
-		return dasd_era_recover;
-	}
-	/* examine the 24 byte sense data */
-	return dasd_era_recover;
-
-}				/* END dasd_3370_erp_examine */

+ 127 - 233
drivers/s390/block/dasd_3990_erp.c

@@ -24,158 +24,6 @@ struct DCTL_data {
 	unsigned short res;	   /* reserved */
 } __attribute__ ((packed));
 
-/*
- *****************************************************************************
- * SECTION ERP EXAMINATION
- *****************************************************************************
- */
-
-/*
- * DASD_3990_ERP_EXAMINE_24
- *
- * DESCRIPTION
- *   Checks only for fatal (unrecoverable) error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   Each bit configuration leading to an action code 2 (Exit with
- *   programming error or unusual condition indication)
- *   are handled as fatal errors.
- *
- *   All other configurations are handled as recoverable errors.
- *
- * RETURN VALUES
- *   dasd_era_fatal	for all fatal (unrecoverable errors)
- *   dasd_era_recover	for all others.
- */
-static dasd_era_t
-dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense)
-{
-
-	struct dasd_device *device = cqr->device;
-
-	/* check for 'Command Reject' */
-	if ((sense[0] & SNS0_CMD_REJECT) &&
-	    (!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
-
-		DEV_MESSAGE(KERN_ERR, device, "%s",
-			    "EXAMINE 24: Command Reject detected - "
-			    "fatal error");
-
-		return dasd_era_fatal;
-	}
-
-	/* check for 'Invalid Track Format' */
-	if ((sense[1] & SNS1_INV_TRACK_FORMAT) &&
-	    (!(sense[2] & SNS2_ENV_DATA_PRESENT))) {
-
-		DEV_MESSAGE(KERN_ERR, device, "%s",
-			    "EXAMINE 24: Invalid Track Format detected "
-			    "- fatal error");
-
-		return dasd_era_fatal;
-	}
-
-	/* check for 'No Record Found' */
-	if (sense[1] & SNS1_NO_REC_FOUND) {
-
-                /* FIXME: fatal error ?!? */
-		DEV_MESSAGE(KERN_ERR, device,
-			    "EXAMINE 24: No Record Found detected %s",
-                            device->state <= DASD_STATE_BASIC ?
-			    " " : "- fatal error");
-
-		return dasd_era_fatal;
-	}
-
-	/* return recoverable for all others */
-	return dasd_era_recover;
-}				/* END dasd_3990_erp_examine_24 */
-
-/*
- * DASD_3990_ERP_EXAMINE_32
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recoverable error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- * RETURN VALUES
- *   dasd_era_none	no error
- *   dasd_era_fatal	for all fatal (unrecoverable errors)
- *   dasd_era_recover	for recoverable others.
- */
-static dasd_era_t
-dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
-{
-
-	struct dasd_device *device = cqr->device;
-
-	switch (sense[25]) {
-	case 0x00:
-		return dasd_era_none;
-
-	case 0x01:
-		DEV_MESSAGE(KERN_ERR, device, "%s", "EXAMINE 32: fatal error");
-
-		return dasd_era_fatal;
-
-	default:
-
-		return dasd_era_recover;
-	}
-
-}				/* end dasd_3990_erp_examine_32 */
-
-/*
- * DASD_3990_ERP_EXAMINE
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recover error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   The logic is based on the 'IBM 3990 Storage Control  Reference' manual
- *   'Chapter 7. Error Recovery Procedures'.
- *
- * RETURN VALUES
- *   dasd_era_none	no error
- *   dasd_era_fatal	for all fatal (unrecoverable errors)
- *   dasd_era_recover	for all others.
- */
-dasd_era_t
-dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-
-	char *sense = irb->ecw;
-	dasd_era_t era = dasd_era_recover;
-	struct dasd_device *device = cqr->device;
-
-	/* check for successful execution first */
-	if (irb->scsw.cstat == 0x00 &&
-	    irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-		return dasd_era_none;
-
-	/* distinguish between 24 and 32 byte sense data */
-	if (sense[27] & DASD_SENSE_BIT_0) {
-
-		era = dasd_3990_erp_examine_24(cqr, sense);
-
-	} else {
-
-		era = dasd_3990_erp_examine_32(cqr, sense);
-
-	}
-
-	/* log the erp chain if fatal error occurred */
-	if ((era == dasd_era_fatal) && (device->state >= DASD_STATE_READY)) {
-		dasd_log_sense(cqr, irb);
-	}
-
-	return era;
-
-}				/* END dasd_3990_erp_examine */
-
 /*
  *****************************************************************************
  * SECTION ERP HANDLING
@@ -206,7 +54,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
 {
 	struct dasd_ccw_req *cqr = erp->refers;
 
-	dasd_free_erp_request(erp, erp->device);
+	dasd_free_erp_request(erp, erp->memdev);
 	cqr->status = final_status;
 	return cqr;
 
@@ -224,15 +72,17 @@ static void
 dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
+	unsigned long flags;
 
 	DEV_MESSAGE(KERN_INFO, device,
 		    "blocking request queue for %is", expires/HZ);
 
+	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
 	device->stopped |= DASD_STOPPED_PENDING;
-	erp->status = DASD_CQR_QUEUED;
-
-	dasd_set_timer(device, expires);
+	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+	erp->status = DASD_CQR_FILLED;
+	dasd_block_set_timer(device->block, expires);
 }
 
 /*
@@ -251,7 +101,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	/* first time set initial retry counter and erp_function */
 	/* and retry once without blocking queue		 */
@@ -292,11 +142,14 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
 static void
 dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
 {
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 	__u8 opm;
+	unsigned long flags;
 
 	/* try alternate valid path */
+	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
 	opm = ccw_device_get_path_mask(device->cdev);
+	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
 	//FIXME: start with get_opm ?
 	if (erp->lpm == 0)
 		erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum);
@@ -309,9 +162,8 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
 			    "try alternate lpm=%x (lpum=%x / opm=%x)",
 			    erp->lpm, erp->irb.esw.esw0.sublog.lpum, opm);
 
-		/* reset status to queued to handle the request again... */
-		if (erp->status > DASD_CQR_QUEUED)
-			erp->status = DASD_CQR_QUEUED;
+		/* reset status to submit the request again... */
+		erp->status = DASD_CQR_FILLED;
 		erp->retries = 1;
 	} else {
 		DEV_MESSAGE(KERN_ERR, device,
@@ -320,8 +172,7 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
 			    erp->irb.esw.esw0.sublog.lpum, opm);
 
 		/* post request with permanent error */
-		if (erp->status > DASD_CQR_QUEUED)
-			erp->status = DASD_CQR_FAILED;
+		erp->status = DASD_CQR_FAILED;
 	}
 }				/* end dasd_3990_erp_alternate_path */
 
@@ -344,14 +195,14 @@ static struct dasd_ccw_req *
 dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 	struct DCTL_data *DCTL_data;
 	struct ccw1 *ccw;
 	struct dasd_ccw_req *dctl_cqr;
 
 	dctl_cqr = dasd_alloc_erp_request((char *) &erp->magic, 1,
-					  sizeof (struct DCTL_data),
-					  erp->device);
+					  sizeof(struct DCTL_data),
+					  device);
 	if (IS_ERR(dctl_cqr)) {
 		DEV_MESSAGE(KERN_ERR, device, "%s",
 			    "Unable to allocate DCTL-CQR");
@@ -365,13 +216,14 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
 	DCTL_data->modifier = modifier;
 
 	ccw = dctl_cqr->cpaddr;
-	memset(ccw, 0, sizeof (struct ccw1));
+	memset(ccw, 0, sizeof(struct ccw1));
 	ccw->cmd_code = CCW_CMD_DCTL;
 	ccw->count = 4;
 	ccw->cda = (__u32)(addr_t) DCTL_data;
 	dctl_cqr->function = dasd_3990_erp_DCTL;
 	dctl_cqr->refers = erp;
-	dctl_cqr->device = erp->device;
+	dctl_cqr->startdev = device;
+	dctl_cqr->memdev = device;
 	dctl_cqr->magic = erp->magic;
 	dctl_cqr->expires = 5 * 60 * HZ;
 	dctl_cqr->retries = 2;
@@ -435,7 +287,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	/* first time set initial retry counter and erp_function    */
 	/* and retry once without waiting for state change pending  */
@@ -472,7 +324,7 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
 				     "redriving request immediately, "
 				     "%d retries left",
 				     erp->retries);
-			erp->status = DASD_CQR_QUEUED;
+			erp->status = DASD_CQR_FILLED;
 		}
 	}
 
@@ -530,7 +382,7 @@ static void
 dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 	char msg_format = (sense[7] & 0xF0);
 	char msg_no = (sense[7] & 0x0F);
 
@@ -1157,7 +1009,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->function = dasd_3990_erp_com_rej;
 
@@ -1198,7 +1050,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_bus_out(struct dasd_ccw_req * erp)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	/* first time set initial retry counter and erp_function */
 	/* and retry once without blocking queue		 */
@@ -1237,7 +1089,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->function = dasd_3990_erp_equip_check;
 
@@ -1279,7 +1131,6 @@ dasd_3990_erp_equip_check(struct dasd_ccw_req * erp, char *sense)
 
 		erp = dasd_3990_erp_action_5(erp);
 	}
-
 	return erp;
 
 }				/* end dasd_3990_erp_equip_check */
@@ -1299,7 +1150,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_data_check(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->function = dasd_3990_erp_data_check;
 
@@ -1358,7 +1209,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_overrun(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->function = dasd_3990_erp_overrun;
 
@@ -1387,7 +1238,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->function = dasd_3990_erp_inv_format;
 
@@ -1403,8 +1254,7 @@ dasd_3990_erp_inv_format(struct dasd_ccw_req * erp, char *sense)
 
 	} else {
 		DEV_MESSAGE(KERN_ERR, device, "%s",
-			    "Invalid Track Format - Fatal error should have "
-			    "been handled within the interrupt handler");
+			    "Invalid Track Format - Fatal error");
 
 		erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
 	}
@@ -1428,7 +1278,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_EOC(struct dasd_ccw_req * default_erp, char *sense)
 {
 
-	struct dasd_device *device = default_erp->device;
+	struct dasd_device *device = default_erp->startdev;
 
 	DEV_MESSAGE(KERN_ERR, device, "%s",
 		    "End-of-Cylinder - must never happen");
@@ -1453,7 +1303,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->function = dasd_3990_erp_env_data;
 
@@ -1463,11 +1313,9 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
 
 	/* don't retry on disabled interface */
 	if (sense[7] != 0x0F) {
-
 		erp = dasd_3990_erp_action_4(erp, sense);
 	} else {
-
-		erp = dasd_3990_erp_cleanup(erp, DASD_CQR_IN_IO);
+		erp->status = DASD_CQR_FILLED;
 	}
 
 	return erp;
@@ -1490,11 +1338,10 @@ static struct dasd_ccw_req *
 dasd_3990_erp_no_rec(struct dasd_ccw_req * default_erp, char *sense)
 {
 
-	struct dasd_device *device = default_erp->device;
+	struct dasd_device *device = default_erp->startdev;
 
 	DEV_MESSAGE(KERN_ERR, device, "%s",
-		    "No Record Found - Fatal error should "
-		    "have been handled within the interrupt handler");
+		    "No Record Found - Fatal error ");
 
 	return dasd_3990_erp_cleanup(default_erp, DASD_CQR_FAILED);
 
@@ -1517,7 +1364,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	DEV_MESSAGE(KERN_ERR, device, "%s", "File Protected");
 
@@ -1525,6 +1372,43 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
 
 }				/* end dasd_3990_erp_file_prot */
 
+/*
+ * DASD_3990_ERP_INSPECT_ALIAS
+ *
+ * DESCRIPTION
+ *   Checks if the original request was started on an alias device.
+ *   If yes, it modifies the original and the erp request so that
+ *   the erp request can be started on a base device.
+ *
+ * PARAMETER
+ *   erp		pointer to the currently created default ERP
+ *
+ * RETURN VALUES
+ *   erp		pointer to the modified ERP, or NULL
+ */
+
+static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
+						struct dasd_ccw_req *erp)
+{
+	struct dasd_ccw_req *cqr = erp->refers;
+
+	if (cqr->block &&
+	    (cqr->block->base != cqr->startdev)) {
+		if (cqr->startdev->features & DASD_FEATURE_ERPLOG) {
+			DEV_MESSAGE(KERN_ERR, cqr->startdev,
+				    "ERP on alias device for request %p,"
+				    " recover on base device %s", cqr,
+				    cqr->block->base->cdev->dev.bus_id);
+		}
+		dasd_eckd_reset_ccw_to_base_io(cqr);
+		erp->startdev = cqr->block->base;
+		erp->function = dasd_3990_erp_inspect_alias;
+		return erp;
+	} else
+		return NULL;
+}
+
+
 /*
  * DASD_3990_ERP_INSPECT_24
  *
@@ -1623,7 +1507,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->retries = 256;
 	erp->function = dasd_3990_erp_action_10_32;
@@ -1657,13 +1541,14 @@ static struct dasd_ccw_req *
 dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
 {
 
-	struct dasd_device *device = default_erp->device;
+	struct dasd_device *device = default_erp->startdev;
 	__u32 cpa = 0;
 	struct dasd_ccw_req *cqr;
 	struct dasd_ccw_req *erp;
 	struct DE_eckd_data *DE_data;
+	struct PFX_eckd_data *PFX_data;
 	char *LO_data;		/* LO_eckd_data_t */
-	struct ccw1 *ccw;
+	struct ccw1 *ccw, *oldccw;
 
 	DEV_MESSAGE(KERN_DEBUG, device, "%s",
 		    "Write not finished because of unexpected condition");
@@ -1702,8 +1587,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
 	/* Build new ERP request including DE/LO */
 	erp = dasd_alloc_erp_request((char *) &cqr->magic,
 				     2 + 1,/* DE/LO + TIC */
-				     sizeof (struct DE_eckd_data) +
-				     sizeof (struct LO_eckd_data), device);
+				     sizeof(struct DE_eckd_data) +
+				     sizeof(struct LO_eckd_data), device);
 
 	if (IS_ERR(erp)) {
 		DEV_MESSAGE(KERN_ERR, device, "%s", "Unable to allocate ERP");
@@ -1712,10 +1597,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
 
 	/* use original DE */
 	DE_data = erp->data;
-	memcpy(DE_data, cqr->data, sizeof (struct DE_eckd_data));
+	oldccw = cqr->cpaddr;
+	if (oldccw->cmd_code == DASD_ECKD_CCW_PFX) {
+		PFX_data = cqr->data;
+		memcpy(DE_data, &PFX_data->define_extend,
+		       sizeof(struct DE_eckd_data));
+	} else
+		memcpy(DE_data, cqr->data, sizeof(struct DE_eckd_data));
 
 	/* create LO */
-	LO_data = erp->data + sizeof (struct DE_eckd_data);
+	LO_data = erp->data + sizeof(struct DE_eckd_data);
 
 	if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) {
 
@@ -1748,7 +1639,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
 
 	/* create DE ccw */
 	ccw = erp->cpaddr;
-	memset(ccw, 0, sizeof (struct ccw1));
+	memset(ccw, 0, sizeof(struct ccw1));
 	ccw->cmd_code = DASD_ECKD_CCW_DEFINE_EXTENT;
 	ccw->flags = CCW_FLAG_CC;
 	ccw->count = 16;
@@ -1756,7 +1647,7 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
 
 	/* create LO ccw */
 	ccw++;
-	memset(ccw, 0, sizeof (struct ccw1));
+	memset(ccw, 0, sizeof(struct ccw1));
 	ccw->cmd_code = DASD_ECKD_CCW_LOCATE_RECORD;
 	ccw->flags = CCW_FLAG_CC;
 	ccw->count = 16;
@@ -1770,7 +1661,8 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
 	/* fill erp related fields */
 	erp->function = dasd_3990_erp_action_1B_32;
 	erp->refers = default_erp->refers;
-	erp->device = device;
+	erp->startdev = device;
+	erp->memdev = device;
 	erp->magic = default_erp->magic;
 	erp->expires = 0;
 	erp->retries = 256;
@@ -1803,7 +1695,7 @@ static struct dasd_ccw_req *
 dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
 {
 
-	struct dasd_device *device = previous_erp->device;
+	struct dasd_device *device = previous_erp->startdev;
 	__u32 cpa = 0;
 	struct dasd_ccw_req *cqr;
 	struct dasd_ccw_req *erp;
@@ -1827,7 +1719,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
 		DEV_MESSAGE(KERN_DEBUG, device, "%s",
 			    "Imprecise ending is set - just retry");
 
-		previous_erp->status = DASD_CQR_QUEUED;
+		previous_erp->status = DASD_CQR_FILLED;
 
 		return previous_erp;
 	}
@@ -1850,7 +1742,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
 	erp = previous_erp;
 
 	/* update the LO with the new returned sense data  */
-	LO_data = erp->data + sizeof (struct DE_eckd_data);
+	LO_data = erp->data + sizeof(struct DE_eckd_data);
 
 	if ((sense[3] == 0x01) && (LO_data[1] & 0x01)) {
 
@@ -1889,7 +1781,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
 	ccw++;			/* addr of TIC ccw */
 	ccw->cda = cpa;
 
-	erp->status = DASD_CQR_QUEUED;
+	erp->status = DASD_CQR_FILLED;
 
 	return erp;
 
@@ -1968,9 +1860,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
 			 * try further actions. */
 
 			erp->lpm = 0;
-
-			erp->status = DASD_CQR_ERROR;
-
+			erp->status = DASD_CQR_NEED_ERP;
 		}
 	}
 
@@ -2047,7 +1937,7 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense)
 	if ((sense[25] & DASD_SENSE_BIT_1) && (sense[26] & DASD_SENSE_BIT_2)) {
 
 		/* set to suspended duplex state then restart */
-		struct dasd_device *device = erp->device;
+		struct dasd_device *device = erp->startdev;
 
 		DEV_MESSAGE(KERN_ERR, device, "%s",
 			    "Set device to suspended duplex state should be "
@@ -2081,28 +1971,26 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense)
 {
 
 	if ((erp->function == dasd_3990_erp_compound_retry) &&
-	    (erp->status == DASD_CQR_ERROR)) {
+	    (erp->status == DASD_CQR_NEED_ERP)) {
 
 		dasd_3990_erp_compound_path(erp, sense);
 	}
 
 	if ((erp->function == dasd_3990_erp_compound_path) &&
-	    (erp->status == DASD_CQR_ERROR)) {
+	    (erp->status == DASD_CQR_NEED_ERP)) {
 
 		erp = dasd_3990_erp_compound_code(erp, sense);
 	}
 
 	if ((erp->function == dasd_3990_erp_compound_code) &&
-	    (erp->status == DASD_CQR_ERROR)) {
+	    (erp->status == DASD_CQR_NEED_ERP)) {
 
 		dasd_3990_erp_compound_config(erp, sense);
 	}
 
 	/* if no compound action ERP specified, the request failed */
-	if (erp->status == DASD_CQR_ERROR) {
-
+	if (erp->status == DASD_CQR_NEED_ERP)
 		erp->status = DASD_CQR_FAILED;
-	}
 
 	return erp;
 
@@ -2127,7 +2015,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 
 	erp->function = dasd_3990_erp_inspect_32;
 
@@ -2149,8 +2037,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
 
 		case 0x01:	/* fatal error */
 			DEV_MESSAGE(KERN_ERR, device, "%s",
-				    "Fatal error should have been "
-				    "handled within the interrupt handler");
+				    "Retry not recommended - Fatal error");
 
 			erp = dasd_3990_erp_cleanup(erp, DASD_CQR_FAILED);
 			break;
@@ -2253,6 +2140,11 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
 	/* already set up new ERP !			      */
 	char *sense = erp->refers->irb.ecw;
 
+	/* if this problem occured on an alias retry on base */
+	erp_new = dasd_3990_erp_inspect_alias(erp);
+	if (erp_new)
+		return erp_new;
+
 	/* distinguish between 24 and 32 byte sense data */
 	if (sense[27] & DASD_SENSE_BIT_0) {
 
@@ -2287,13 +2179,13 @@ static struct dasd_ccw_req *
 dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
 {
 
-	struct dasd_device *device = cqr->device;
+	struct dasd_device *device = cqr->startdev;
 	struct ccw1 *ccw;
 
 	/* allocate additional request block */
 	struct dasd_ccw_req *erp;
 
-	erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, cqr->device);
+	erp = dasd_alloc_erp_request((char *) &cqr->magic, 2, 0, device);
 	if (IS_ERR(erp)) {
                 if (cqr->retries <= 0) {
 		        DEV_MESSAGE(KERN_ERR, device, "%s",
@@ -2305,7 +2197,7 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
                                      "Unable to allocate ERP request "
 				     "(%i retries left)",
                                      cqr->retries);
-			dasd_set_timer(device, (HZ << 3));
+			dasd_block_set_timer(device->block, (HZ << 3));
                 }
 		return cqr;
 	}
@@ -2319,7 +2211,9 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
 	ccw->cda      = (long)(cqr->cpaddr);
 	erp->function = dasd_3990_erp_add_erp;
 	erp->refers   = cqr;
-	erp->device   = cqr->device;
+	erp->startdev = device;
+	erp->memdev   = device;
+	erp->block    = cqr->block;
 	erp->magic    = cqr->magic;
 	erp->expires  = 0;
 	erp->retries  = 256;
@@ -2466,7 +2360,7 @@ static struct dasd_ccw_req *
 dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
 {
 
-	struct dasd_device *device = erp->device;
+	struct dasd_device *device = erp->startdev;
 	char *sense = erp->irb.ecw;
 
 	/* check for 24 byte sense ERP */
@@ -2557,7 +2451,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
 			       struct dasd_ccw_req *erp)
 {
 
-	struct dasd_device *device = erp_head->device;
+	struct dasd_device *device = erp_head->startdev;
 	struct dasd_ccw_req *erp_done = erp_head;	/* finished req */
 	struct dasd_ccw_req *erp_free = NULL;	/* req to be freed */
 
@@ -2569,13 +2463,13 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
 			      "original request was lost\n");
 
 		/* remove the request from the device queue */
-		list_del(&erp_done->list);
+		list_del(&erp_done->blocklist);
 
 		erp_free = erp_done;
 		erp_done = erp_done->refers;
 
 		/* free the finished erp request */
-		dasd_free_erp_request(erp_free, erp_free->device);
+		dasd_free_erp_request(erp_free, erp_free->memdev);
 
 	}			/* end while */
 
@@ -2603,7 +2497,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
 				    erp->retries, erp);
 
 			/* handle the request again... */
-			erp->status = DASD_CQR_QUEUED;
+			erp->status = DASD_CQR_FILLED;
 		}
 
 	} else {
@@ -2620,7 +2514,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
  * DASD_3990_ERP_ACTION
  *
  * DESCRIPTION
- *   controll routine for 3990 erp actions.
+ *   control routine for 3990 erp actions.
  *   Has to be called with the queue lock (namely the s390_irq_lock) acquired.
  *
  * PARAMETER
@@ -2636,9 +2530,8 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
 struct dasd_ccw_req *
 dasd_3990_erp_action(struct dasd_ccw_req * cqr)
 {
-
 	struct dasd_ccw_req *erp = NULL;
-	struct dasd_device *device = cqr->device;
+	struct dasd_device *device = cqr->startdev;
 	struct dasd_ccw_req *temp_erp = NULL;
 
 	if (device->features & DASD_FEATURE_ERPLOG) {
@@ -2704,10 +2597,11 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
 		}
 	}
 
-	/* enqueue added ERP request */
-	if (erp->status == DASD_CQR_FILLED) {
-		erp->status = DASD_CQR_QUEUED;
-		list_add(&erp->list, &device->ccw_queue);
+	/* enqueue ERP request if it's a new one */
+	if (list_empty(&erp->blocklist)) {
+		cqr->status = DASD_CQR_IN_ERP;
+		/* add erp request before the cqr */
+		list_add_tail(&erp->blocklist, &cqr->blocklist);
 	}
 
 	return erp;

+ 0 - 41
drivers/s390/block/dasd_9336_erp.c

@@ -1,41 +0,0 @@
-/*
- * File...........: linux/drivers/s390/block/dasd_9336_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(9336)"
-
-#include "dasd_int.h"
-
-
-/*
- * DASD_9336_ERP_EXAMINE
- *
- * DESCRIPTION
- *   Checks only for fatal/no/recover error.
- *   A detailed examination of the sense data is done later outside
- *   the interrupt handler.
- *
- *   The logic is based on the 'IBM 3880 Storage Control Reference' manual
- *   'Chapter 7. 9336 Sense Data'.
- *
- * RETURN VALUES
- *   dasd_era_none	no error
- *   dasd_era_fatal	for all fatal (unrecoverable errors)
- *   dasd_era_recover	for all others.
- */
-dasd_era_t
-dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-	/* check for successful execution first */
-	if (irb->scsw.cstat == 0x00 &&
-	    irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-		return dasd_era_none;
-
-	/* examine the 24 byte sense data */
-	return dasd_era_recover;
-
-}				/* END dasd_9336_erp_examine */

+ 0 - 21
drivers/s390/block/dasd_9343_erp.c

@@ -1,21 +0,0 @@
-/*
- * File...........: linux/drivers/s390/block/dasd_9345_erp.c
- * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Bugreports.to..: <Linux390@de.ibm.com>
- * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000
- *
- */
-
-#define PRINTK_HEADER "dasd_erp(9343)"
-
-#include "dasd_int.h"
-
-dasd_era_t
-dasd_9343_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-	if (irb->scsw.cstat == 0x00 &&
-	    irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-		return dasd_era_none;
-
-	return dasd_era_recover;
-}

+ 903 - 0
drivers/s390/block/dasd_alias.c

@@ -0,0 +1,903 @@
+/*
+ * PAV alias management for the DASD ECKD discipline
+ *
+ * Copyright IBM Corporation, 2007
+ * Author(s): Stefan Weinhuber <wein@de.ibm.com>
+ */
+
+#include <linux/list.h>
+#include <asm/ebcdic.h>
+#include "dasd_int.h"
+#include "dasd_eckd.h"
+
+#ifdef PRINTK_HEADER
+#undef PRINTK_HEADER
+#endif				/* PRINTK_HEADER */
+#define PRINTK_HEADER "dasd(eckd):"
+
+
+/*
+ * General concept of alias management:
+ * - PAV and DASD alias management is specific to the eckd discipline.
+ * - A device is connected to an lcu as long as the device exists.
+ *   dasd_alias_make_device_known_to_lcu will be called wenn the
+ *   device is checked by the eckd discipline and
+ *   dasd_alias_disconnect_device_from_lcu will be called
+ *   before the device is deleted.
+ * - The dasd_alias_add_device / dasd_alias_remove_device
+ *   functions mark the point when a device is 'ready for service'.
+ * - A summary unit check is a rare occasion, but it is mandatory to
+ *   support it. It requires some complex recovery actions before the
+ *   devices can be used again (see dasd_alias_handle_summary_unit_check).
+ * - dasd_alias_get_start_dev will find an alias device that can be used
+ *   instead of the base device and does some (very simple) load balancing.
+ *   This is the function that gets called for each I/O, so when improving
+ *   something, this function should get faster or better, the rest has just
+ *   to be correct.
+ */
+
+
+static void summary_unit_check_handling_work(struct work_struct *);
+static void lcu_update_work(struct work_struct *);
+static int _schedule_lcu_update(struct alias_lcu *, struct dasd_device *);
+
+static struct alias_root aliastree = {
+	.serverlist = LIST_HEAD_INIT(aliastree.serverlist),
+	.lock = __SPIN_LOCK_UNLOCKED(aliastree.lock),
+};
+
+static struct alias_server *_find_server(struct dasd_uid *uid)
+{
+	struct alias_server *pos;
+	list_for_each_entry(pos, &aliastree.serverlist, server) {
+		if (!strncmp(pos->uid.vendor, uid->vendor,
+			     sizeof(uid->vendor))
+		    && !strncmp(pos->uid.serial, uid->serial,
+				sizeof(uid->serial)))
+			return pos;
+	};
+	return NULL;
+}
+
+static struct alias_lcu *_find_lcu(struct alias_server *server,
+				   struct dasd_uid *uid)
+{
+	struct alias_lcu *pos;
+	list_for_each_entry(pos, &server->lculist, lcu) {
+		if (pos->uid.ssid == uid->ssid)
+			return pos;
+	};
+	return NULL;
+}
+
+static struct alias_pav_group *_find_group(struct alias_lcu *lcu,
+					   struct dasd_uid *uid)
+{
+	struct alias_pav_group *pos;
+	__u8 search_unit_addr;
+
+	/* for hyper pav there is only one group */
+	if (lcu->pav == HYPER_PAV) {
+		if (list_empty(&lcu->grouplist))
+			return NULL;
+		else
+			return list_first_entry(&lcu->grouplist,
+						struct alias_pav_group, group);
+	}
+
+	/* for base pav we have to find the group that matches the base */
+	if (uid->type == UA_BASE_DEVICE)
+		search_unit_addr = uid->real_unit_addr;
+	else
+		search_unit_addr = uid->base_unit_addr;
+	list_for_each_entry(pos, &lcu->grouplist, group) {
+		if (pos->uid.base_unit_addr == search_unit_addr)
+			return pos;
+	};
+	return NULL;
+}
+
+static struct alias_server *_allocate_server(struct dasd_uid *uid)
+{
+	struct alias_server *server;
+
+	server = kzalloc(sizeof(*server), GFP_KERNEL);
+	if (!server)
+		return ERR_PTR(-ENOMEM);
+	memcpy(server->uid.vendor, uid->vendor, sizeof(uid->vendor));
+	memcpy(server->uid.serial, uid->serial, sizeof(uid->serial));
+	INIT_LIST_HEAD(&server->server);
+	INIT_LIST_HEAD(&server->lculist);
+	return server;
+}
+
+static void _free_server(struct alias_server *server)
+{
+	kfree(server);
+}
+
+static struct alias_lcu *_allocate_lcu(struct dasd_uid *uid)
+{
+	struct alias_lcu *lcu;
+
+	lcu = kzalloc(sizeof(*lcu), GFP_KERNEL);
+	if (!lcu)
+		return ERR_PTR(-ENOMEM);
+	lcu->uac = kzalloc(sizeof(*(lcu->uac)), GFP_KERNEL | GFP_DMA);
+	if (!lcu->uac)
+		goto out_err1;
+	lcu->rsu_cqr = kzalloc(sizeof(*lcu->rsu_cqr), GFP_KERNEL | GFP_DMA);
+	if (!lcu->rsu_cqr)
+		goto out_err2;
+	lcu->rsu_cqr->cpaddr = kzalloc(sizeof(struct ccw1),
+				       GFP_KERNEL | GFP_DMA);
+	if (!lcu->rsu_cqr->cpaddr)
+		goto out_err3;
+	lcu->rsu_cqr->data = kzalloc(16, GFP_KERNEL | GFP_DMA);
+	if (!lcu->rsu_cqr->data)
+		goto out_err4;
+
+	memcpy(lcu->uid.vendor, uid->vendor, sizeof(uid->vendor));
+	memcpy(lcu->uid.serial, uid->serial, sizeof(uid->serial));
+	lcu->uid.ssid = uid->ssid;
+	lcu->pav = NO_PAV;
+	lcu->flags = NEED_UAC_UPDATE | UPDATE_PENDING;
+	INIT_LIST_HEAD(&lcu->lcu);
+	INIT_LIST_HEAD(&lcu->inactive_devices);
+	INIT_LIST_HEAD(&lcu->active_devices);
+	INIT_LIST_HEAD(&lcu->grouplist);
+	INIT_WORK(&lcu->suc_data.worker, summary_unit_check_handling_work);
+	INIT_DELAYED_WORK(&lcu->ruac_data.dwork, lcu_update_work);
+	spin_lock_init(&lcu->lock);
+	return lcu;
+
+out_err4:
+	kfree(lcu->rsu_cqr->cpaddr);
+out_err3:
+	kfree(lcu->rsu_cqr);
+out_err2:
+	kfree(lcu->uac);
+out_err1:
+	kfree(lcu);
+	return ERR_PTR(-ENOMEM);
+}
+
+static void _free_lcu(struct alias_lcu *lcu)
+{
+	kfree(lcu->rsu_cqr->data);
+	kfree(lcu->rsu_cqr->cpaddr);
+	kfree(lcu->rsu_cqr);
+	kfree(lcu->uac);
+	kfree(lcu);
+}
+
+/*
+ * This is the function that will allocate all the server and lcu data,
+ * so this function must be called first for a new device.
+ * If the return value is 1, the lcu was already known before, if it
+ * is 0, this is a new lcu.
+ * Negative return code indicates that something went wrong (e.g. -ENOMEM)
+ */
+int dasd_alias_make_device_known_to_lcu(struct dasd_device *device)
+{
+	struct dasd_eckd_private *private;
+	unsigned long flags;
+	struct alias_server *server, *newserver;
+	struct alias_lcu *lcu, *newlcu;
+	int is_lcu_known;
+	struct dasd_uid *uid;
+
+	private = (struct dasd_eckd_private *) device->private;
+	uid = &private->uid;
+	spin_lock_irqsave(&aliastree.lock, flags);
+	is_lcu_known = 1;
+	server = _find_server(uid);
+	if (!server) {
+		spin_unlock_irqrestore(&aliastree.lock, flags);
+		newserver = _allocate_server(uid);
+		if (IS_ERR(newserver))
+			return PTR_ERR(newserver);
+		spin_lock_irqsave(&aliastree.lock, flags);
+		server = _find_server(uid);
+		if (!server) {
+			list_add(&newserver->server, &aliastree.serverlist);
+			server = newserver;
+			is_lcu_known = 0;
+		} else {
+			/* someone was faster */
+			_free_server(newserver);
+		}
+	}
+
+	lcu = _find_lcu(server, uid);
+	if (!lcu) {
+		spin_unlock_irqrestore(&aliastree.lock, flags);
+		newlcu = _allocate_lcu(uid);
+		if (IS_ERR(newlcu))
+			return PTR_ERR(lcu);
+		spin_lock_irqsave(&aliastree.lock, flags);
+		lcu = _find_lcu(server, uid);
+		if (!lcu) {
+			list_add(&newlcu->lcu, &server->lculist);
+			lcu = newlcu;
+			is_lcu_known = 0;
+		} else {
+			/* someone was faster */
+			_free_lcu(newlcu);
+		}
+		is_lcu_known = 0;
+	}
+	spin_lock(&lcu->lock);
+	list_add(&device->alias_list, &lcu->inactive_devices);
+	private->lcu = lcu;
+	spin_unlock(&lcu->lock);
+	spin_unlock_irqrestore(&aliastree.lock, flags);
+
+	return is_lcu_known;
+}
+
+/*
+ * This function removes a device from the scope of alias management.
+ * The complicated part is to make sure that it is not in use by
+ * any of the workers. If necessary cancel the work.
+ */
+void dasd_alias_disconnect_device_from_lcu(struct dasd_device *device)
+{
+	struct dasd_eckd_private *private;
+	unsigned long flags;
+	struct alias_lcu *lcu;
+	struct alias_server *server;
+	int was_pending;
+
+	private = (struct dasd_eckd_private *) device->private;
+	lcu = private->lcu;
+	spin_lock_irqsave(&lcu->lock, flags);
+	list_del_init(&device->alias_list);
+	/* make sure that the workers don't use this device */
+	if (device == lcu->suc_data.device) {
+		spin_unlock_irqrestore(&lcu->lock, flags);
+		cancel_work_sync(&lcu->suc_data.worker);
+		spin_lock_irqsave(&lcu->lock, flags);
+		if (device == lcu->suc_data.device)
+			lcu->suc_data.device = NULL;
+	}
+	was_pending = 0;
+	if (device == lcu->ruac_data.device) {
+		spin_unlock_irqrestore(&lcu->lock, flags);
+		was_pending = 1;
+		cancel_delayed_work_sync(&lcu->ruac_data.dwork);
+		spin_lock_irqsave(&lcu->lock, flags);
+		if (device == lcu->ruac_data.device)
+			lcu->ruac_data.device = NULL;
+	}
+	private->lcu = NULL;
+	spin_unlock_irqrestore(&lcu->lock, flags);
+
+	spin_lock_irqsave(&aliastree.lock, flags);
+	spin_lock(&lcu->lock);
+	if (list_empty(&lcu->grouplist) &&
+	    list_empty(&lcu->active_devices) &&
+	    list_empty(&lcu->inactive_devices)) {
+		list_del(&lcu->lcu);
+		spin_unlock(&lcu->lock);
+		_free_lcu(lcu);
+		lcu = NULL;
+	} else {
+		if (was_pending)
+			_schedule_lcu_update(lcu, NULL);
+		spin_unlock(&lcu->lock);
+	}
+	server = _find_server(&private->uid);
+	if (server && list_empty(&server->lculist)) {
+		list_del(&server->server);
+		_free_server(server);
+	}
+	spin_unlock_irqrestore(&aliastree.lock, flags);
+}
+
+/*
+ * This function assumes that the unit address configuration stored
+ * in the lcu is up to date and will update the device uid before
+ * adding it to a pav group.
+ */
+static int _add_device_to_lcu(struct alias_lcu *lcu,
+			      struct dasd_device *device)
+{
+
+	struct dasd_eckd_private *private;
+	struct alias_pav_group *group;
+	struct dasd_uid *uid;
+
+	private = (struct dasd_eckd_private *) device->private;
+	uid = &private->uid;
+	uid->type = lcu->uac->unit[uid->real_unit_addr].ua_type;
+	uid->base_unit_addr = lcu->uac->unit[uid->real_unit_addr].base_ua;
+	dasd_set_uid(device->cdev, &private->uid);
+
+	/* if we have no PAV anyway, we don't need to bother with PAV groups */
+	if (lcu->pav == NO_PAV) {
+		list_move(&device->alias_list, &lcu->active_devices);
+		return 0;
+	}
+
+	group = _find_group(lcu, uid);
+	if (!group) {
+		group = kzalloc(sizeof(*group), GFP_ATOMIC);
+		if (!group)
+			return -ENOMEM;
+		memcpy(group->uid.vendor, uid->vendor, sizeof(uid->vendor));
+		memcpy(group->uid.serial, uid->serial, sizeof(uid->serial));
+		group->uid.ssid = uid->ssid;
+		if (uid->type == UA_BASE_DEVICE)
+			group->uid.base_unit_addr = uid->real_unit_addr;
+		else
+			group->uid.base_unit_addr = uid->base_unit_addr;
+		INIT_LIST_HEAD(&group->group);
+		INIT_LIST_HEAD(&group->baselist);
+		INIT_LIST_HEAD(&group->aliaslist);
+		list_add(&group->group, &lcu->grouplist);
+	}
+	if (uid->type == UA_BASE_DEVICE)
+		list_move(&device->alias_list, &group->baselist);
+	else
+		list_move(&device->alias_list, &group->aliaslist);
+	private->pavgroup = group;
+	return 0;
+};
+
+static void _remove_device_from_lcu(struct alias_lcu *lcu,
+				    struct dasd_device *device)
+{
+	struct dasd_eckd_private *private;
+	struct alias_pav_group *group;
+
+	private = (struct dasd_eckd_private *) device->private;
+	list_move(&device->alias_list, &lcu->inactive_devices);
+	group = private->pavgroup;
+	if (!group)
+		return;
+	private->pavgroup = NULL;
+	if (list_empty(&group->baselist) && list_empty(&group->aliaslist)) {
+		list_del(&group->group);
+		kfree(group);
+		return;
+	}
+	if (group->next == device)
+		group->next = NULL;
+};
+
+static int read_unit_address_configuration(struct dasd_device *device,
+					   struct alias_lcu *lcu)
+{
+	struct dasd_psf_prssd_data *prssdp;
+	struct dasd_ccw_req *cqr;
+	struct ccw1 *ccw;
+	int rc;
+	unsigned long flags;
+
+	cqr = dasd_kmalloc_request("ECKD",
+				   1 /* PSF */	+ 1 /* RSSD */ ,
+				   (sizeof(struct dasd_psf_prssd_data)),
+				   device);
+	if (IS_ERR(cqr))
+		return PTR_ERR(cqr);
+	cqr->startdev = device;
+	cqr->memdev = device;
+	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+	cqr->retries = 10;
+	cqr->expires = 20 * HZ;
+
+	/* Prepare for Read Subsystem Data */
+	prssdp = (struct dasd_psf_prssd_data *) cqr->data;
+	memset(prssdp, 0, sizeof(struct dasd_psf_prssd_data));
+	prssdp->order = PSF_ORDER_PRSSD;
+	prssdp->suborder = 0x0e;	/* Read unit address configuration */
+	/* all other bytes of prssdp must be zero */
+
+	ccw = cqr->cpaddr;
+	ccw->cmd_code = DASD_ECKD_CCW_PSF;
+	ccw->count = sizeof(struct dasd_psf_prssd_data);
+	ccw->flags |= CCW_FLAG_CC;
+	ccw->cda = (__u32)(addr_t) prssdp;
+
+	/* Read Subsystem Data - feature codes */
+	memset(lcu->uac, 0, sizeof(*(lcu->uac)));
+
+	ccw++;
+	ccw->cmd_code = DASD_ECKD_CCW_RSSD;
+	ccw->count = sizeof(*(lcu->uac));
+	ccw->cda = (__u32)(addr_t) lcu->uac;
+
+	cqr->buildclk = get_clock();
+	cqr->status = DASD_CQR_FILLED;
+
+	/* need to unset flag here to detect race with summary unit check */
+	spin_lock_irqsave(&lcu->lock, flags);
+	lcu->flags &= ~NEED_UAC_UPDATE;
+	spin_unlock_irqrestore(&lcu->lock, flags);
+
+	do {
+		rc = dasd_sleep_on(cqr);
+	} while (rc && (cqr->retries > 0));
+	if (rc) {
+		spin_lock_irqsave(&lcu->lock, flags);
+		lcu->flags |= NEED_UAC_UPDATE;
+		spin_unlock_irqrestore(&lcu->lock, flags);
+	}
+	dasd_kfree_request(cqr, cqr->memdev);
+	return rc;
+}
+
+static int _lcu_update(struct dasd_device *refdev, struct alias_lcu *lcu)
+{
+	unsigned long flags;
+	struct alias_pav_group *pavgroup, *tempgroup;
+	struct dasd_device *device, *tempdev;
+	int i, rc;
+	struct dasd_eckd_private *private;
+
+	spin_lock_irqsave(&lcu->lock, flags);
+	list_for_each_entry_safe(pavgroup, tempgroup, &lcu->grouplist, group) {
+		list_for_each_entry_safe(device, tempdev, &pavgroup->baselist,
+					 alias_list) {
+			list_move(&device->alias_list, &lcu->active_devices);
+			private = (struct dasd_eckd_private *) device->private;
+			private->pavgroup = NULL;
+		}
+		list_for_each_entry_safe(device, tempdev, &pavgroup->aliaslist,
+					 alias_list) {
+			list_move(&device->alias_list, &lcu->active_devices);
+			private = (struct dasd_eckd_private *) device->private;
+			private->pavgroup = NULL;
+		}
+		list_del(&pavgroup->group);
+		kfree(pavgroup);
+	}
+	spin_unlock_irqrestore(&lcu->lock, flags);
+
+	rc = read_unit_address_configuration(refdev, lcu);
+	if (rc)
+		return rc;
+
+	spin_lock_irqsave(&lcu->lock, flags);
+	lcu->pav = NO_PAV;
+	for (i = 0; i < MAX_DEVICES_PER_LCU; ++i) {
+		switch (lcu->uac->unit[i].ua_type) {
+		case UA_BASE_PAV_ALIAS:
+			lcu->pav = BASE_PAV;
+			break;
+		case UA_HYPER_PAV_ALIAS:
+			lcu->pav = HYPER_PAV;
+			break;
+		}
+		if (lcu->pav != NO_PAV)
+			break;
+	}
+
+	list_for_each_entry_safe(device, tempdev, &lcu->active_devices,
+				 alias_list) {
+		_add_device_to_lcu(lcu, device);
+	}
+	spin_unlock_irqrestore(&lcu->lock, flags);
+	return 0;
+}
+
+static void lcu_update_work(struct work_struct *work)
+{
+	struct alias_lcu *lcu;
+	struct read_uac_work_data *ruac_data;
+	struct dasd_device *device;
+	unsigned long flags;
+	int rc;
+
+	ruac_data = container_of(work, struct read_uac_work_data, dwork.work);
+	lcu = container_of(ruac_data, struct alias_lcu, ruac_data);
+	device = ruac_data->device;
+	rc = _lcu_update(device, lcu);
+	/*
+	 * Need to check flags again, as there could have been another
+	 * prepare_update or a new device a new device while we were still
+	 * processing the data
+	 */
+	spin_lock_irqsave(&lcu->lock, flags);
+	if (rc || (lcu->flags & NEED_UAC_UPDATE)) {
+		DEV_MESSAGE(KERN_WARNING, device, "could not update"
+			    " alias data in lcu (rc = %d), retry later", rc);
+		schedule_delayed_work(&lcu->ruac_data.dwork, 30*HZ);
+	} else {
+		lcu->ruac_data.device = NULL;
+		lcu->flags &= ~UPDATE_PENDING;
+	}
+	spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+static int _schedule_lcu_update(struct alias_lcu *lcu,
+				struct dasd_device *device)
+{
+	struct dasd_device *usedev = NULL;
+	struct alias_pav_group *group;
+
+	lcu->flags |= NEED_UAC_UPDATE;
+	if (lcu->ruac_data.device) {
+		/* already scheduled or running */
+		return 0;
+	}
+	if (device && !list_empty(&device->alias_list))
+		usedev = device;
+
+	if (!usedev && !list_empty(&lcu->grouplist)) {
+		group = list_first_entry(&lcu->grouplist,
+					 struct alias_pav_group, group);
+		if (!list_empty(&group->baselist))
+			usedev = list_first_entry(&group->baselist,
+						  struct dasd_device,
+						  alias_list);
+		else if (!list_empty(&group->aliaslist))
+			usedev = list_first_entry(&group->aliaslist,
+						  struct dasd_device,
+						  alias_list);
+	}
+	if (!usedev && !list_empty(&lcu->active_devices)) {
+		usedev = list_first_entry(&lcu->active_devices,
+					  struct dasd_device, alias_list);
+	}
+	/*
+	 * if we haven't found a proper device yet, give up for now, the next
+	 * device that will be set active will trigger an lcu update
+	 */
+	if (!usedev)
+		return -EINVAL;
+	lcu->ruac_data.device = usedev;
+	schedule_delayed_work(&lcu->ruac_data.dwork, 0);
+	return 0;
+}
+
+int dasd_alias_add_device(struct dasd_device *device)
+{
+	struct dasd_eckd_private *private;
+	struct alias_lcu *lcu;
+	unsigned long flags;
+	int rc;
+
+	private = (struct dasd_eckd_private *) device->private;
+	lcu = private->lcu;
+	rc = 0;
+	spin_lock_irqsave(&lcu->lock, flags);
+	if (!(lcu->flags & UPDATE_PENDING)) {
+		rc = _add_device_to_lcu(lcu, device);
+		if (rc)
+			lcu->flags |= UPDATE_PENDING;
+	}
+	if (lcu->flags & UPDATE_PENDING) {
+		list_move(&device->alias_list, &lcu->active_devices);
+		_schedule_lcu_update(lcu, device);
+	}
+	spin_unlock_irqrestore(&lcu->lock, flags);
+	return rc;
+}
+
+int dasd_alias_remove_device(struct dasd_device *device)
+{
+	struct dasd_eckd_private *private;
+	struct alias_lcu *lcu;
+	unsigned long flags;
+
+	private = (struct dasd_eckd_private *) device->private;
+	lcu = private->lcu;
+	spin_lock_irqsave(&lcu->lock, flags);
+	_remove_device_from_lcu(lcu, device);
+	spin_unlock_irqrestore(&lcu->lock, flags);
+	return 0;
+}
+
+struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *base_device)
+{
+
+	struct dasd_device *alias_device;
+	struct alias_pav_group *group;
+	struct alias_lcu *lcu;
+	struct dasd_eckd_private *private, *alias_priv;
+	unsigned long flags;
+
+	private = (struct dasd_eckd_private *) base_device->private;
+	group = private->pavgroup;
+	lcu = private->lcu;
+	if (!group || !lcu)
+		return NULL;
+	if (lcu->pav == NO_PAV ||
+	    lcu->flags & (NEED_UAC_UPDATE | UPDATE_PENDING))
+		return NULL;
+
+	spin_lock_irqsave(&lcu->lock, flags);
+	alias_device = group->next;
+	if (!alias_device) {
+		if (list_empty(&group->aliaslist)) {
+			spin_unlock_irqrestore(&lcu->lock, flags);
+			return NULL;
+		} else {
+			alias_device = list_first_entry(&group->aliaslist,
+							struct dasd_device,
+							alias_list);
+		}
+	}
+	if (list_is_last(&alias_device->alias_list, &group->aliaslist))
+		group->next = list_first_entry(&group->aliaslist,
+					       struct dasd_device, alias_list);
+	else
+		group->next = list_first_entry(&alias_device->alias_list,
+					       struct dasd_device, alias_list);
+	spin_unlock_irqrestore(&lcu->lock, flags);
+	alias_priv = (struct dasd_eckd_private *) alias_device->private;
+	if ((alias_priv->count < private->count) && !alias_device->stopped)
+		return alias_device;
+	else
+		return NULL;
+}
+
+/*
+ * Summary unit check handling depends on the way alias devices
+ * are handled so it is done here rather then in dasd_eckd.c
+ */
+static int reset_summary_unit_check(struct alias_lcu *lcu,
+				    struct dasd_device *device,
+				    char reason)
+{
+	struct dasd_ccw_req *cqr;
+	int rc = 0;
+
+	cqr = lcu->rsu_cqr;
+	strncpy((char *) &cqr->magic, "ECKD", 4);
+	ASCEBC((char *) &cqr->magic, 4);
+	cqr->cpaddr->cmd_code = DASD_ECKD_CCW_RSCK;
+	cqr->cpaddr->flags = 0 ;
+	cqr->cpaddr->count = 16;
+	cqr->cpaddr->cda = (__u32)(addr_t) cqr->data;
+	((char *)cqr->data)[0] = reason;
+
+	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
+	cqr->retries = 255;	/* set retry counter to enable basic ERP */
+	cqr->startdev = device;
+	cqr->memdev = device;
+	cqr->block = NULL;
+	cqr->expires = 5 * HZ;
+	cqr->buildclk = get_clock();
+	cqr->status = DASD_CQR_FILLED;
+
+	rc = dasd_sleep_on_immediatly(cqr);
+	return rc;
+}
+
+static void _restart_all_base_devices_on_lcu(struct alias_lcu *lcu)
+{
+	struct alias_pav_group *pavgroup;
+	struct dasd_device *device;
+	struct dasd_eckd_private *private;
+
+	/* active and inactive list can contain alias as well as base devices */
+	list_for_each_entry(device, &lcu->active_devices, alias_list) {
+		private = (struct dasd_eckd_private *) device->private;
+		if (private->uid.type != UA_BASE_DEVICE)
+			continue;
+		dasd_schedule_block_bh(device->block);
+		dasd_schedule_device_bh(device);
+	}
+	list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
+		private = (struct dasd_eckd_private *) device->private;
+		if (private->uid.type != UA_BASE_DEVICE)
+			continue;
+		dasd_schedule_block_bh(device->block);
+		dasd_schedule_device_bh(device);
+	}
+	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+		list_for_each_entry(device, &pavgroup->baselist, alias_list) {
+			dasd_schedule_block_bh(device->block);
+			dasd_schedule_device_bh(device);
+		}
+	}
+}
+
+static void flush_all_alias_devices_on_lcu(struct alias_lcu *lcu)
+{
+	struct alias_pav_group *pavgroup;
+	struct dasd_device *device, *temp;
+	struct dasd_eckd_private *private;
+	int rc;
+	unsigned long flags;
+	LIST_HEAD(active);
+
+	/*
+	 * Problem here ist that dasd_flush_device_queue may wait
+	 * for termination of a request to complete. We can't keep
+	 * the lcu lock during that time, so we must assume that
+	 * the lists may have changed.
+	 * Idea: first gather all active alias devices in a separate list,
+	 * then flush the first element of this list unlocked, and afterwards
+	 * check if it is still on the list before moving it to the
+	 * active_devices list.
+	 */
+
+	spin_lock_irqsave(&lcu->lock, flags);
+	list_for_each_entry_safe(device, temp, &lcu->active_devices,
+				 alias_list) {
+		private = (struct dasd_eckd_private *) device->private;
+		if (private->uid.type == UA_BASE_DEVICE)
+			continue;
+		list_move(&device->alias_list, &active);
+	}
+
+	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+		list_splice_init(&pavgroup->aliaslist, &active);
+	}
+	while (!list_empty(&active)) {
+		device = list_first_entry(&active, struct dasd_device,
+					  alias_list);
+		spin_unlock_irqrestore(&lcu->lock, flags);
+		rc = dasd_flush_device_queue(device);
+		spin_lock_irqsave(&lcu->lock, flags);
+		/*
+		 * only move device around if it wasn't moved away while we
+		 * were waiting for the flush
+		 */
+		if (device == list_first_entry(&active,
+					       struct dasd_device, alias_list))
+			list_move(&device->alias_list, &lcu->active_devices);
+	}
+	spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+/*
+ * This function is called in interrupt context, so the
+ * cdev lock for device is already locked!
+ */
+static void _stop_all_devices_on_lcu(struct alias_lcu *lcu,
+				     struct dasd_device *device)
+{
+	struct alias_pav_group *pavgroup;
+	struct dasd_device *pos;
+
+	list_for_each_entry(pos, &lcu->active_devices, alias_list) {
+		if (pos != device)
+			spin_lock(get_ccwdev_lock(pos->cdev));
+		pos->stopped |= DASD_STOPPED_SU;
+		if (pos != device)
+			spin_unlock(get_ccwdev_lock(pos->cdev));
+	}
+	list_for_each_entry(pos, &lcu->inactive_devices, alias_list) {
+		if (pos != device)
+			spin_lock(get_ccwdev_lock(pos->cdev));
+		pos->stopped |= DASD_STOPPED_SU;
+		if (pos != device)
+			spin_unlock(get_ccwdev_lock(pos->cdev));
+	}
+	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+		list_for_each_entry(pos, &pavgroup->baselist, alias_list) {
+			if (pos != device)
+				spin_lock(get_ccwdev_lock(pos->cdev));
+			pos->stopped |= DASD_STOPPED_SU;
+			if (pos != device)
+				spin_unlock(get_ccwdev_lock(pos->cdev));
+		}
+		list_for_each_entry(pos, &pavgroup->aliaslist, alias_list) {
+			if (pos != device)
+				spin_lock(get_ccwdev_lock(pos->cdev));
+			pos->stopped |= DASD_STOPPED_SU;
+			if (pos != device)
+				spin_unlock(get_ccwdev_lock(pos->cdev));
+		}
+	}
+}
+
+static void _unstop_all_devices_on_lcu(struct alias_lcu *lcu)
+{
+	struct alias_pav_group *pavgroup;
+	struct dasd_device *device;
+	unsigned long flags;
+
+	list_for_each_entry(device, &lcu->active_devices, alias_list) {
+		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+		device->stopped &= ~DASD_STOPPED_SU;
+		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+	}
+
+	list_for_each_entry(device, &lcu->inactive_devices, alias_list) {
+		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+		device->stopped &= ~DASD_STOPPED_SU;
+		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+	}
+
+	list_for_each_entry(pavgroup, &lcu->grouplist, group) {
+		list_for_each_entry(device, &pavgroup->baselist, alias_list) {
+			spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+			device->stopped &= ~DASD_STOPPED_SU;
+			spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+					       flags);
+		}
+		list_for_each_entry(device, &pavgroup->aliaslist, alias_list) {
+			spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+			device->stopped &= ~DASD_STOPPED_SU;
+			spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+					       flags);
+		}
+	}
+}
+
+static void summary_unit_check_handling_work(struct work_struct *work)
+{
+	struct alias_lcu *lcu;
+	struct summary_unit_check_work_data *suc_data;
+	unsigned long flags;
+	struct dasd_device *device;
+
+	suc_data = container_of(work, struct summary_unit_check_work_data,
+				worker);
+	lcu = container_of(suc_data, struct alias_lcu, suc_data);
+	device = suc_data->device;
+
+	/* 1. flush alias devices */
+	flush_all_alias_devices_on_lcu(lcu);
+
+	/* 2. reset summary unit check */
+	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+	device->stopped &= ~(DASD_STOPPED_SU | DASD_STOPPED_PENDING);
+	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+	reset_summary_unit_check(lcu, device, suc_data->reason);
+
+	spin_lock_irqsave(&lcu->lock, flags);
+	_unstop_all_devices_on_lcu(lcu);
+	_restart_all_base_devices_on_lcu(lcu);
+	/* 3. read new alias configuration */
+	_schedule_lcu_update(lcu, device);
+	lcu->suc_data.device = NULL;
+	spin_unlock_irqrestore(&lcu->lock, flags);
+}
+
+/*
+ * note: this will be called from int handler context (cdev locked)
+ */
+void dasd_alias_handle_summary_unit_check(struct dasd_device *device,
+					  struct irb *irb)
+{
+	struct alias_lcu *lcu;
+	char reason;
+	struct dasd_eckd_private *private;
+
+	private = (struct dasd_eckd_private *) device->private;
+
+	reason = irb->ecw[8];
+	DEV_MESSAGE(KERN_WARNING, device, "%s %x",
+		    "eckd handle summary unit check: reason", reason);
+
+	lcu = private->lcu;
+	if (!lcu) {
+		DEV_MESSAGE(KERN_WARNING, device, "%s",
+			    "device not ready to handle summary"
+			    " unit check (no lcu structure)");
+		return;
+	}
+	spin_lock(&lcu->lock);
+	_stop_all_devices_on_lcu(lcu, device);
+	/* prepare for lcu_update */
+	private->lcu->flags |= NEED_UAC_UPDATE | UPDATE_PENDING;
+	/* If this device is about to be removed just return and wait for
+	 * the next interrupt on a different device
+	 */
+	if (list_empty(&device->alias_list)) {
+		DEV_MESSAGE(KERN_WARNING, device, "%s",
+			    "device is in offline processing,"
+			    " don't do summary unit check handling");
+		spin_unlock(&lcu->lock);
+		return;
+	}
+	if (lcu->suc_data.device) {
+		/* already scheduled or running */
+		DEV_MESSAGE(KERN_WARNING, device, "%s",
+			    "previous instance of summary unit check worker"
+			    " still pending");
+		spin_unlock(&lcu->lock);
+		return ;
+	}
+	lcu->suc_data.reason = reason;
+	lcu->suc_data.device = device;
+	spin_unlock(&lcu->lock);
+	schedule_work(&lcu->suc_data.worker);
+};

+ 37 - 57
drivers/s390/block/dasd_devmap.c

@@ -48,22 +48,6 @@ struct dasd_devmap {
 	struct dasd_uid uid;
 };
 
-/*
- * dasd_server_ssid_map contains a globally unique storage server subsystem ID.
- * dasd_server_ssid_list contains the list of all subsystem IDs accessed by
- * the DASD device driver.
- */
-struct dasd_server_ssid_map {
-	struct list_head list;
-	struct system_id {
-		char vendor[4];
-		char serial[15];
-		__u16 ssid;
-	} sid;
-};
-
-static struct list_head dasd_server_ssid_list;
-
 /*
  * Parameter parsing functions for dasd= parameter. The syntax is:
  *   <devno>		: (0x)?[0-9a-fA-F]+
@@ -721,8 +705,9 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
 		devmap->features &= ~DASD_FEATURE_READONLY;
 	if (devmap->device)
 		devmap->device->features = devmap->features;
-	if (devmap->device && devmap->device->gdp)
-		set_disk_ro(devmap->device->gdp, val);
+	if (devmap->device && devmap->device->block
+	    && devmap->device->block->gdp)
+		set_disk_ro(devmap->device->block->gdp, val);
 	spin_unlock(&dasd_devmap_lock);
 	return count;
 }
@@ -893,12 +878,16 @@ dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
 
 	devmap = dasd_find_busid(dev->bus_id);
 	spin_lock(&dasd_devmap_lock);
-	if (!IS_ERR(devmap))
-		alias = devmap->uid.alias;
+	if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+		spin_unlock(&dasd_devmap_lock);
+		return sprintf(buf, "0\n");
+	}
+	if (devmap->uid.type == UA_BASE_PAV_ALIAS ||
+	    devmap->uid.type == UA_HYPER_PAV_ALIAS)
+		alias = 1;
 	else
 		alias = 0;
 	spin_unlock(&dasd_devmap_lock);
-
 	return sprintf(buf, alias ? "1\n" : "0\n");
 }
 
@@ -930,19 +919,36 @@ static ssize_t
 dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
 	struct dasd_devmap *devmap;
-	char uid[UID_STRLEN];
+	char uid_string[UID_STRLEN];
+	char ua_string[3];
+	struct dasd_uid *uid;
 
 	devmap = dasd_find_busid(dev->bus_id);
 	spin_lock(&dasd_devmap_lock);
-	if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
-		snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x",
-			 devmap->uid.vendor, devmap->uid.serial,
-			 devmap->uid.ssid, devmap->uid.unit_addr);
-	else
-		uid[0] = 0;
+	if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+		spin_unlock(&dasd_devmap_lock);
+		return sprintf(buf, "\n");
+	}
+	uid = &devmap->uid;
+	switch (uid->type) {
+	case UA_BASE_DEVICE:
+		sprintf(ua_string, "%02x", uid->real_unit_addr);
+		break;
+	case UA_BASE_PAV_ALIAS:
+		sprintf(ua_string, "%02x", uid->base_unit_addr);
+		break;
+	case UA_HYPER_PAV_ALIAS:
+		sprintf(ua_string, "xx");
+		break;
+	default:
+		/* should not happen, treat like base device */
+		sprintf(ua_string, "%02x", uid->real_unit_addr);
+		break;
+	}
+	snprintf(uid_string, sizeof(uid_string), "%s.%s.%04x.%s",
+		 uid->vendor, uid->serial, uid->ssid, ua_string);
 	spin_unlock(&dasd_devmap_lock);
-
-	return snprintf(buf, PAGE_SIZE, "%s\n", uid);
+	return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
 }
 
 static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
@@ -1040,39 +1046,16 @@ int
 dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
 {
 	struct dasd_devmap *devmap;
-	struct dasd_server_ssid_map *srv, *tmp;
 
 	devmap = dasd_find_busid(cdev->dev.bus_id);
 	if (IS_ERR(devmap))
 		return PTR_ERR(devmap);
 
-	/* generate entry for server_ssid_map */
-	srv = (struct dasd_server_ssid_map *)
-		kzalloc(sizeof(struct dasd_server_ssid_map), GFP_KERNEL);
-	if (!srv)
-		return -ENOMEM;
-	strncpy(srv->sid.vendor, uid->vendor, sizeof(srv->sid.vendor) - 1);
-	strncpy(srv->sid.serial, uid->serial, sizeof(srv->sid.serial) - 1);
-	srv->sid.ssid = uid->ssid;
-
-	/* server is already contained ? */
 	spin_lock(&dasd_devmap_lock);
 	devmap->uid = *uid;
-	list_for_each_entry(tmp, &dasd_server_ssid_list, list) {
-		if (!memcmp(&srv->sid, &tmp->sid,
-			    sizeof(struct system_id))) {
-			kfree(srv);
-			srv = NULL;
-			break;
-		}
-	}
-
-	/* add servermap to serverlist */
-	if (srv)
-		list_add(&srv->list, &dasd_server_ssid_list);
 	spin_unlock(&dasd_devmap_lock);
 
-	return (srv ? 1 : 0);
+	return 0;
 }
 EXPORT_SYMBOL_GPL(dasd_set_uid);
 
@@ -1138,9 +1121,6 @@ dasd_devmap_init(void)
 	dasd_max_devindex = 0;
 	for (i = 0; i < 256; i++)
 		INIT_LIST_HEAD(&dasd_hashlists[i]);
-
-	/* Initialize servermap structure. */
-	INIT_LIST_HEAD(&dasd_server_ssid_list);
 	return 0;
 }
 

+ 60 - 47
drivers/s390/block/dasd_diag.c

@@ -142,7 +142,7 @@ dasd_diag_erp(struct dasd_device *device)
 	int rc;
 
 	mdsk_term_io(device);
-	rc = mdsk_init_io(device, device->bp_block, 0, NULL);
+	rc = mdsk_init_io(device, device->block->bp_block, 0, NULL);
 	if (rc)
 		DEV_MESSAGE(KERN_WARNING, device, "DIAG ERP unsuccessful, "
 			    "rc=%d", rc);
@@ -158,11 +158,11 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
 	struct dasd_diag_req *dreq;
 	int rc;
 
-	device = cqr->device;
+	device = cqr->startdev;
 	if (cqr->retries < 0) {
 		DEV_MESSAGE(KERN_WARNING, device, "DIAG start_IO: request %p "
 			    "- no retry left)", cqr);
-		cqr->status = DASD_CQR_FAILED;
+		cqr->status = DASD_CQR_ERROR;
 		return -EIO;
 	}
 	private = (struct dasd_diag_private *) device->private;
@@ -184,7 +184,7 @@ dasd_start_diag(struct dasd_ccw_req * cqr)
 	switch (rc) {
 	case 0: /* Synchronous I/O finished successfully */
 		cqr->stopclk = get_clock();
-		cqr->status = DASD_CQR_DONE;
+		cqr->status = DASD_CQR_SUCCESS;
 		/* Indicate to calling function that only a dasd_schedule_bh()
 		   and no timer is needed */
                 rc = -EACCES;
@@ -209,12 +209,12 @@ dasd_diag_term_IO(struct dasd_ccw_req * cqr)
 {
 	struct dasd_device *device;
 
-	device = cqr->device;
+	device = cqr->startdev;
 	mdsk_term_io(device);
-	mdsk_init_io(device, device->bp_block, 0, NULL);
-	cqr->status = DASD_CQR_CLEAR;
+	mdsk_init_io(device, device->block->bp_block, 0, NULL);
+	cqr->status = DASD_CQR_CLEAR_PENDING;
 	cqr->stopclk = get_clock();
-	dasd_schedule_bh(device);
+	dasd_schedule_device_bh(device);
 	return 0;
 }
 
@@ -247,7 +247,7 @@ dasd_ext_handler(__u16 code)
 		return;
 	}
 	cqr = (struct dasd_ccw_req *) ip;
-	device = (struct dasd_device *) cqr->device;
+	device = (struct dasd_device *) cqr->startdev;
 	if (strncmp(device->discipline->ebcname, (char *) &cqr->magic, 4)) {
 		DEV_MESSAGE(KERN_WARNING, device,
 			    " magic number of dasd_ccw_req 0x%08X doesn't"
@@ -260,10 +260,10 @@ dasd_ext_handler(__u16 code)
 	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
 
 	/* Check for a pending clear operation */
-	if (cqr->status == DASD_CQR_CLEAR) {
-		cqr->status = DASD_CQR_QUEUED;
-		dasd_clear_timer(device);
-		dasd_schedule_bh(device);
+	if (cqr->status == DASD_CQR_CLEAR_PENDING) {
+		cqr->status = DASD_CQR_CLEARED;
+		dasd_device_clear_timer(device);
+		dasd_schedule_device_bh(device);
 		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
 		return;
 	}
@@ -272,11 +272,11 @@ dasd_ext_handler(__u16 code)
 
 	expires = 0;
 	if (status == 0) {
-		cqr->status = DASD_CQR_DONE;
+		cqr->status = DASD_CQR_SUCCESS;
 		/* Start first request on queue if possible -> fast_io. */
 		if (!list_empty(&device->ccw_queue)) {
 			next = list_entry(device->ccw_queue.next,
-					  struct dasd_ccw_req, list);
+					  struct dasd_ccw_req, devlist);
 			if (next->status == DASD_CQR_QUEUED) {
 				rc = dasd_start_diag(next);
 				if (rc == 0)
@@ -296,10 +296,10 @@ dasd_ext_handler(__u16 code)
 	}
 
 	if (expires != 0)
-		dasd_set_timer(device, expires);
+		dasd_device_set_timer(device, expires);
 	else
-		dasd_clear_timer(device);
-	dasd_schedule_bh(device);
+		dasd_device_clear_timer(device);
+	dasd_schedule_device_bh(device);
 
 	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
 }
@@ -309,6 +309,7 @@ dasd_ext_handler(__u16 code)
 static int
 dasd_diag_check_device(struct dasd_device *device)
 {
+	struct dasd_block *block;
 	struct dasd_diag_private *private;
 	struct dasd_diag_characteristics *rdc_data;
 	struct dasd_diag_bio bio;
@@ -328,6 +329,16 @@ dasd_diag_check_device(struct dasd_device *device)
 		ccw_device_get_id(device->cdev, &private->dev_id);
 		device->private = (void *) private;
 	}
+	block = dasd_alloc_block();
+	if (IS_ERR(block)) {
+		DEV_MESSAGE(KERN_WARNING, device, "%s",
+			    "could not allocate dasd block structure");
+		kfree(device->private);
+		return PTR_ERR(block);
+	}
+	device->block = block;
+	block->base = device;
+
 	/* Read Device Characteristics */
 	rdc_data = (void *) &(private->rdc_data);
 	rdc_data->dev_nr = private->dev_id.devno;
@@ -409,14 +420,14 @@ dasd_diag_check_device(struct dasd_device *device)
 		  sizeof(DASD_DIAG_CMS1)) == 0) {
 		/* get formatted blocksize from label block */
 		bsize = (unsigned int) label->block_size;
-		device->blocks = (unsigned long) label->block_count;
+		block->blocks = (unsigned long) label->block_count;
 	} else
-		device->blocks = end_block;
-	device->bp_block = bsize;
-	device->s2b_shift = 0;	/* bits to shift 512 to get a block */
+		block->blocks = end_block;
+	block->bp_block = bsize;
+	block->s2b_shift = 0;	/* bits to shift 512 to get a block */
 	for (sb = 512; sb < bsize; sb = sb << 1)
-		device->s2b_shift++;
-	rc = mdsk_init_io(device, device->bp_block, 0, NULL);
+		block->s2b_shift++;
+	rc = mdsk_init_io(device, block->bp_block, 0, NULL);
 	if (rc) {
 		DEV_MESSAGE(KERN_WARNING, device, "DIAG initialization "
 			"failed (rc=%d)", rc);
@@ -424,9 +435,9 @@ dasd_diag_check_device(struct dasd_device *device)
 	} else {
 		DEV_MESSAGE(KERN_INFO, device,
 			    "(%ld B/blk): %ldkB",
-			    (unsigned long) device->bp_block,
-			    (unsigned long) (device->blocks <<
-				device->s2b_shift) >> 1);
+			    (unsigned long) block->bp_block,
+			    (unsigned long) (block->blocks <<
+				block->s2b_shift) >> 1);
 	}
 out:
 	free_page((long) label);
@@ -436,22 +447,16 @@ out:
 /* Fill in virtual disk geometry for device. Return zero on success, non-zero
  * otherwise. */
 static int
-dasd_diag_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+dasd_diag_fill_geometry(struct dasd_block *block, struct hd_geometry *geo)
 {
-	if (dasd_check_blocksize(device->bp_block) != 0)
+	if (dasd_check_blocksize(block->bp_block) != 0)
 		return -EINVAL;
-	geo->cylinders = (device->blocks << device->s2b_shift) >> 10;
+	geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
 	geo->heads = 16;
-	geo->sectors = 128 >> device->s2b_shift;
+	geo->sectors = 128 >> block->s2b_shift;
 	return 0;
 }
 
-static dasd_era_t
-dasd_diag_examine_error(struct dasd_ccw_req * cqr, struct irb * stat)
-{
-	return dasd_era_fatal;
-}
-
 static dasd_erp_fn_t
 dasd_diag_erp_action(struct dasd_ccw_req * cqr)
 {
@@ -466,8 +471,9 @@ dasd_diag_erp_postaction(struct dasd_ccw_req * cqr)
 
 /* Create DASD request from block device request. Return pointer to new
  * request on success, ERR_PTR otherwise. */
-static struct dasd_ccw_req *
-dasd_diag_build_cp(struct dasd_device * device, struct request *req)
+static struct dasd_ccw_req *dasd_diag_build_cp(struct dasd_device *memdev,
+					       struct dasd_block *block,
+					       struct request *req)
 {
 	struct dasd_ccw_req *cqr;
 	struct dasd_diag_req *dreq;
@@ -486,17 +492,17 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
 		rw_cmd = MDSK_WRITE_REQ;
 	else
 		return ERR_PTR(-EINVAL);
-	blksize = device->bp_block;
+	blksize = block->bp_block;
 	/* Calculate record id of first and last block. */
-	first_rec = req->sector >> device->s2b_shift;
-	last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+	first_rec = req->sector >> block->s2b_shift;
+	last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
 	/* Check struct bio and count the number of blocks for the request. */
 	count = 0;
 	rq_for_each_segment(bv, req, iter) {
 		if (bv->bv_len & (blksize - 1))
 			/* Fba can only do full blocks. */
 			return ERR_PTR(-EINVAL);
-		count += bv->bv_len >> (device->s2b_shift + 9);
+		count += bv->bv_len >> (block->s2b_shift + 9);
 	}
 	/* Paranoia. */
 	if (count != last_rec - first_rec + 1)
@@ -505,7 +511,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
 	datasize = sizeof(struct dasd_diag_req) +
 		count*sizeof(struct dasd_diag_bio);
 	cqr = dasd_smalloc_request(dasd_diag_discipline.name, 0,
-				   datasize, device);
+				   datasize, memdev);
 	if (IS_ERR(cqr))
 		return cqr;
 
@@ -529,7 +535,9 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
 	cqr->buildclk = get_clock();
 	if (req->cmd_flags & REQ_FAILFAST)
 		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-	cqr->device = device;
+	cqr->startdev = memdev;
+	cqr->memdev = memdev;
+	cqr->block = block;
 	cqr->expires = DIAG_TIMEOUT;
 	cqr->status = DASD_CQR_FILLED;
 	return cqr;
@@ -543,10 +551,15 @@ dasd_diag_free_cp(struct dasd_ccw_req *cqr, struct request *req)
 	int status;
 
 	status = cqr->status == DASD_CQR_DONE;
-	dasd_sfree_request(cqr, cqr->device);
+	dasd_sfree_request(cqr, cqr->memdev);
 	return status;
 }
 
+static void dasd_diag_handle_terminated_request(struct dasd_ccw_req *cqr)
+{
+	cqr->status = DASD_CQR_FILLED;
+};
+
 /* Fill in IOCTL data for device. */
 static int
 dasd_diag_fill_info(struct dasd_device * device,
@@ -583,7 +596,7 @@ static struct dasd_discipline dasd_diag_discipline = {
 	.fill_geometry = dasd_diag_fill_geometry,
 	.start_IO = dasd_start_diag,
 	.term_IO = dasd_diag_term_IO,
-	.examine_error = dasd_diag_examine_error,
+	.handle_terminated_request = dasd_diag_handle_terminated_request,
 	.erp_action = dasd_diag_erp_action,
 	.erp_postaction = dasd_diag_erp_postaction,
 	.build_cp = dasd_diag_build_cp,

File diff suppressed because it is too large
+ 510 - 158
drivers/s390/block/dasd_eckd.c


+ 124 - 1
drivers/s390/block/dasd_eckd.h

@@ -39,6 +39,8 @@
 #define DASD_ECKD_CCW_READ_CKD_MT	 0x9e
 #define DASD_ECKD_CCW_WRITE_CKD_MT	 0x9d
 #define DASD_ECKD_CCW_RESERVE		 0xB4
+#define DASD_ECKD_CCW_PFX		 0xE7
+#define DASD_ECKD_CCW_RSCK		 0xF9
 
 /*
  * Perform Subsystem Function / Sub-Orders
@@ -137,6 +139,25 @@ struct LO_eckd_data {
 	__u16 length;
 } __attribute__ ((packed));
 
+/* Prefix data for format 0x00 and 0x01 */
+struct PFX_eckd_data {
+	unsigned char format;
+	struct {
+		unsigned char define_extend:1;
+		unsigned char time_stamp:1;
+		unsigned char verify_base:1;
+		unsigned char hyper_pav:1;
+		unsigned char reserved:4;
+	} __attribute__ ((packed)) validity;
+	__u8 base_address;
+	__u8 aux;
+	__u8 base_lss;
+	__u8 reserved[7];
+	struct DE_eckd_data define_extend;
+	struct LO_eckd_data locate_record;
+	__u8 LO_extended_data[4];
+} __attribute__ ((packed));
+
 struct dasd_eckd_characteristics {
 	__u16 cu_type;
 	struct {
@@ -254,7 +275,9 @@ struct dasd_eckd_confdata {
 		} __attribute__ ((packed)) ned;
 		struct {
 			unsigned char flags;            /* byte  0    */
-			unsigned char res2[7];          /* byte  1- 7 */
+			unsigned char res1;		/* byte  1    */
+			__u16 format;			/* byte  2-3  */
+			unsigned char res2[4];		/* byte  4-7  */
 			unsigned char sua_flags;	/* byte  8    */
 			__u8 base_unit_addr;            /* byte  9    */
 			unsigned char res3[22];	        /* byte 10-31 */
@@ -343,6 +366,11 @@ struct dasd_eckd_path {
 	__u8 npm;
 };
 
+struct dasd_rssd_features {
+	char feature[256];
+} __attribute__((packed));
+
+
 /*
  * Perform Subsystem Function - Prepare for Read Subsystem Data
  */
@@ -365,4 +393,99 @@ struct dasd_psf_ssc_data {
 	unsigned char reserved[59];
 } __attribute__((packed));
 
+
+/*
+ * some structures and definitions for alias handling
+ */
+struct dasd_unit_address_configuration {
+	struct {
+		char ua_type;
+		char base_ua;
+	} unit[256];
+} __attribute__((packed));
+
+
+#define MAX_DEVICES_PER_LCU 256
+
+/* flags on the LCU  */
+#define NEED_UAC_UPDATE  0x01
+#define UPDATE_PENDING	0x02
+
+enum pavtype {NO_PAV, BASE_PAV, HYPER_PAV};
+
+
+struct alias_root {
+	struct list_head serverlist;
+	spinlock_t lock;
+};
+
+struct alias_server {
+	struct list_head server;
+	struct dasd_uid uid;
+	struct list_head lculist;
+};
+
+struct summary_unit_check_work_data {
+	char reason;
+	struct dasd_device *device;
+	struct work_struct worker;
+};
+
+struct read_uac_work_data {
+	struct dasd_device *device;
+	struct delayed_work dwork;
+};
+
+struct alias_lcu {
+	struct list_head lcu;
+	struct dasd_uid uid;
+	enum pavtype pav;
+	char flags;
+	spinlock_t lock;
+	struct list_head grouplist;
+	struct list_head active_devices;
+	struct list_head inactive_devices;
+	struct dasd_unit_address_configuration *uac;
+	struct summary_unit_check_work_data suc_data;
+	struct read_uac_work_data ruac_data;
+	struct dasd_ccw_req *rsu_cqr;
+};
+
+struct alias_pav_group {
+	struct list_head group;
+	struct dasd_uid uid;
+	struct alias_lcu *lcu;
+	struct list_head baselist;
+	struct list_head aliaslist;
+	struct dasd_device *next;
+};
+
+
+struct dasd_eckd_private {
+	struct dasd_eckd_characteristics rdc_data;
+	struct dasd_eckd_confdata conf_data;
+	struct dasd_eckd_path path_data;
+	struct eckd_count count_area[5];
+	int init_cqr_status;
+	int uses_cdl;
+	struct attrib_data_t attrib;	/* e.g. cache operations */
+	struct dasd_rssd_features features;
+
+	/* alias managemnet */
+	struct dasd_uid uid;
+	struct alias_pav_group *pavgroup;
+	struct alias_lcu *lcu;
+	int count;
+};
+
+
+
+int dasd_alias_make_device_known_to_lcu(struct dasd_device *);
+void dasd_alias_disconnect_device_from_lcu(struct dasd_device *);
+int dasd_alias_add_device(struct dasd_device *);
+int dasd_alias_remove_device(struct dasd_device *);
+struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *);
+void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
+void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
+
 #endif				/* DASD_ECKD_H */

+ 6 - 5
drivers/s390/block/dasd_eer.c

@@ -336,7 +336,7 @@ static void dasd_eer_write_snss_trigger(struct dasd_device *device,
 	unsigned long flags;
 	struct eerbuffer *eerb;
 
-	snss_rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
+	snss_rc = (cqr->status == DASD_CQR_DONE) ? 0 : -EIO;
 	if (snss_rc)
 		data_size = 0;
 	else
@@ -404,10 +404,11 @@ void dasd_eer_snss(struct dasd_device *device)
 		set_bit(DASD_FLAG_EER_SNSS, &device->flags);
 		return;
 	}
+	/* cdev is already locked, can't use dasd_add_request_head */
 	clear_bit(DASD_FLAG_EER_SNSS, &device->flags);
 	cqr->status = DASD_CQR_QUEUED;
-	list_add(&cqr->list, &device->ccw_queue);
-	dasd_schedule_bh(device);
+	list_add(&cqr->devlist, &device->ccw_queue);
+	dasd_schedule_device_bh(device);
 }
 
 /*
@@ -415,7 +416,7 @@ void dasd_eer_snss(struct dasd_device *device)
  */
 static void dasd_eer_snss_cb(struct dasd_ccw_req *cqr, void *data)
 {
-        struct dasd_device *device = cqr->device;
+	struct dasd_device *device = cqr->startdev;
 	unsigned long flags;
 
 	dasd_eer_write(device, cqr, DASD_EER_STATECHANGE);
@@ -458,7 +459,7 @@ int dasd_eer_enable(struct dasd_device *device)
 	if (!cqr)
 		return -ENOMEM;
 
-	cqr->device = device;
+	cqr->startdev = device;
 	cqr->retries = 255;
 	cqr->expires = 10 * HZ;
 	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);

+ 12 - 13
drivers/s390/block/dasd_erp.c

@@ -46,6 +46,8 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
 	if (cqr == NULL)
 		return ERR_PTR(-ENOMEM);
 	memset(cqr, 0, sizeof(struct dasd_ccw_req));
+	INIT_LIST_HEAD(&cqr->devlist);
+	INIT_LIST_HEAD(&cqr->blocklist);
 	data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L);
 	cqr->cpaddr = NULL;
 	if (cplength > 0) {
@@ -66,7 +68,7 @@ dasd_alloc_erp_request(char *magic, int cplength, int datasize,
 }
 
 void
-dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
+dasd_free_erp_request(struct dasd_ccw_req *cqr, struct dasd_device * device)
 {
 	unsigned long flags;
 
@@ -81,11 +83,11 @@ dasd_free_erp_request(struct dasd_ccw_req * cqr, struct dasd_device * device)
  * dasd_default_erp_action just retries the current cqr
  */
 struct dasd_ccw_req *
-dasd_default_erp_action(struct dasd_ccw_req * cqr)
+dasd_default_erp_action(struct dasd_ccw_req *cqr)
 {
 	struct dasd_device *device;
 
-	device = cqr->device;
+	device = cqr->startdev;
 
         /* just retry - there is nothing to save ... I got no sense data.... */
         if (cqr->retries > 0) {
@@ -93,12 +95,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
                              "default ERP called (%i retries left)",
                              cqr->retries);
 		cqr->lpm    = LPM_ANYPATH;
-		cqr->status = DASD_CQR_QUEUED;
+		cqr->status = DASD_CQR_FILLED;
         } else {
                 DEV_MESSAGE (KERN_WARNING, device, "%s",
 			     "default ERP called (NO retry left)");
 		cqr->status = DASD_CQR_FAILED;
-		cqr->stopclk = get_clock ();
+		cqr->stopclk = get_clock();
         }
         return cqr;
 }				/* end dasd_default_erp_action */
@@ -117,15 +119,12 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
  * RETURN VALUES
  *   cqr		pointer to the original CQR
  */
-struct dasd_ccw_req *
-dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
+struct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
 {
-	struct dasd_device *device;
 	int success;
 
 	BUG_ON(cqr->refers == NULL || cqr->function == NULL);
 
-	device = cqr->device;
 	success = cqr->status == DASD_CQR_DONE;
 
 	/* free all ERPs - but NOT the original cqr */
@@ -133,10 +132,10 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
 		struct dasd_ccw_req *refers;
 
 		refers = cqr->refers;
-		/* remove the request from the device queue */
-		list_del(&cqr->list);
+		/* remove the request from the block queue */
+		list_del(&cqr->blocklist);
 		/* free the finished erp request */
-		dasd_free_erp_request(cqr, device);
+		dasd_free_erp_request(cqr, cqr->memdev);
 		cqr = refers;
 	}
 
@@ -157,7 +156,7 @@ dasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb)
 {
 	struct dasd_device *device;
 
-	device = cqr->device;
+	device = cqr->startdev;
 	/* dump sense data */
 	if (device->discipline && device->discipline->dump_sense)
 		device->discipline->dump_sense(device, cqr, irb);

+ 68 - 51
drivers/s390/block/dasd_fba.c

@@ -117,6 +117,7 @@ locate_record(struct ccw1 * ccw, struct LO_fba_data *data, int rw,
 static int
 dasd_fba_check_characteristics(struct dasd_device *device)
 {
+	struct dasd_block *block;
 	struct dasd_fba_private *private;
 	struct ccw_device *cdev = device->cdev;
 	void *rdc_data;
@@ -133,6 +134,16 @@ dasd_fba_check_characteristics(struct dasd_device *device)
 		}
 		device->private = (void *) private;
 	}
+	block = dasd_alloc_block();
+	if (IS_ERR(block)) {
+		DEV_MESSAGE(KERN_WARNING, device, "%s",
+			    "could not allocate dasd block structure");
+		kfree(device->private);
+		return PTR_ERR(block);
+	}
+	device->block = block;
+	block->base = device;
+
 	/* Read Device Characteristics */
 	rdc_data = (void *) &(private->rdc_data);
 	rc = dasd_generic_read_dev_chars(device, "FBA ", &rdc_data, 32);
@@ -155,60 +166,37 @@ dasd_fba_check_characteristics(struct dasd_device *device)
 	return 0;
 }
 
-static int
-dasd_fba_do_analysis(struct dasd_device *device)
+static int dasd_fba_do_analysis(struct dasd_block *block)
 {
 	struct dasd_fba_private *private;
 	int sb, rc;
 
-	private = (struct dasd_fba_private *) device->private;
+	private = (struct dasd_fba_private *) block->base->private;
 	rc = dasd_check_blocksize(private->rdc_data.blk_size);
 	if (rc) {
-		DEV_MESSAGE(KERN_INFO, device, "unknown blocksize %d",
+		DEV_MESSAGE(KERN_INFO, block->base, "unknown blocksize %d",
 			    private->rdc_data.blk_size);
 		return rc;
 	}
-	device->blocks = private->rdc_data.blk_bdsa;
-	device->bp_block = private->rdc_data.blk_size;
-	device->s2b_shift = 0;	/* bits to shift 512 to get a block */
+	block->blocks = private->rdc_data.blk_bdsa;
+	block->bp_block = private->rdc_data.blk_size;
+	block->s2b_shift = 0;	/* bits to shift 512 to get a block */
 	for (sb = 512; sb < private->rdc_data.blk_size; sb = sb << 1)
-		device->s2b_shift++;
+		block->s2b_shift++;
 	return 0;
 }
 
-static int
-dasd_fba_fill_geometry(struct dasd_device *device, struct hd_geometry *geo)
+static int dasd_fba_fill_geometry(struct dasd_block *block,
+				  struct hd_geometry *geo)
 {
-	if (dasd_check_blocksize(device->bp_block) != 0)
+	if (dasd_check_blocksize(block->bp_block) != 0)
 		return -EINVAL;
-	geo->cylinders = (device->blocks << device->s2b_shift) >> 10;
+	geo->cylinders = (block->blocks << block->s2b_shift) >> 10;
 	geo->heads = 16;
-	geo->sectors = 128 >> device->s2b_shift;
+	geo->sectors = 128 >> block->s2b_shift;
 	return 0;
 }
 
-static dasd_era_t
-dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
-{
-	struct dasd_device *device;
-	struct ccw_device *cdev;
-
-	device = (struct dasd_device *) cqr->device;
-	if (irb->scsw.cstat == 0x00 &&
-	    irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
-		return dasd_era_none;
-
-	cdev = device->cdev;
-	switch (cdev->id.dev_type) {
-	case 0x3370:
-		return dasd_3370_erp_examine(cqr, irb);
-	case 0x9336:
-		return dasd_9336_erp_examine(cqr, irb);
-	default:
-		return dasd_era_recover;
-	}
-}
-
 static dasd_erp_fn_t
 dasd_fba_erp_action(struct dasd_ccw_req * cqr)
 {
@@ -221,13 +209,34 @@ dasd_fba_erp_postaction(struct dasd_ccw_req * cqr)
 	if (cqr->function == dasd_default_erp_action)
 		return dasd_default_erp_postaction;
 
-	DEV_MESSAGE(KERN_WARNING, cqr->device, "unknown ERP action %p",
+	DEV_MESSAGE(KERN_WARNING, cqr->startdev, "unknown ERP action %p",
 		    cqr->function);
 	return NULL;
 }
 
-static struct dasd_ccw_req *
-dasd_fba_build_cp(struct dasd_device * device, struct request *req)
+static void dasd_fba_handle_unsolicited_interrupt(struct dasd_device *device,
+						   struct irb *irb)
+{
+	char mask;
+
+	/* first of all check for state change pending interrupt */
+	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
+	if ((irb->scsw.dstat & mask) == mask) {
+		dasd_generic_handle_state_change(device);
+		return;
+	}
+
+	/* check for unsolicited interrupts */
+	DEV_MESSAGE(KERN_DEBUG, device, "%s",
+		    "unsolicited interrupt received");
+	device->discipline->dump_sense(device, NULL, irb);
+	dasd_schedule_device_bh(device);
+	return;
+};
+
+static struct dasd_ccw_req *dasd_fba_build_cp(struct dasd_device * memdev,
+					      struct dasd_block *block,
+					      struct request *req)
 {
 	struct dasd_fba_private *private;
 	unsigned long *idaws;
@@ -242,17 +251,17 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
 	unsigned int blksize, off;
 	unsigned char cmd;
 
-	private = (struct dasd_fba_private *) device->private;
+	private = (struct dasd_fba_private *) block->base->private;
 	if (rq_data_dir(req) == READ) {
 		cmd = DASD_FBA_CCW_READ;
 	} else if (rq_data_dir(req) == WRITE) {
 		cmd = DASD_FBA_CCW_WRITE;
 	} else
 		return ERR_PTR(-EINVAL);
-	blksize = device->bp_block;
+	blksize = block->bp_block;
 	/* Calculate record id of first and last block. */
-	first_rec = req->sector >> device->s2b_shift;
-	last_rec = (req->sector + req->nr_sectors - 1) >> device->s2b_shift;
+	first_rec = req->sector >> block->s2b_shift;
+	last_rec = (req->sector + req->nr_sectors - 1) >> block->s2b_shift;
 	/* Check struct bio and count the number of blocks for the request. */
 	count = 0;
 	cidaw = 0;
@@ -260,7 +269,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
 		if (bv->bv_len & (blksize - 1))
 			/* Fba can only do full blocks. */
 			return ERR_PTR(-EINVAL);
-		count += bv->bv_len >> (device->s2b_shift + 9);
+		count += bv->bv_len >> (block->s2b_shift + 9);
 #if defined(CONFIG_64BIT)
 		if (idal_is_needed (page_address(bv->bv_page), bv->bv_len))
 			cidaw += bv->bv_len / blksize;
@@ -284,13 +293,13 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
 	}
 	/* Allocate the ccw request. */
 	cqr = dasd_smalloc_request(dasd_fba_discipline.name,
-				   cplength, datasize, device);
+				   cplength, datasize, memdev);
 	if (IS_ERR(cqr))
 		return cqr;
 	ccw = cqr->cpaddr;
 	/* First ccw is define extent. */
 	define_extent(ccw++, cqr->data, rq_data_dir(req),
-		      device->bp_block, req->sector, req->nr_sectors);
+		      block->bp_block, req->sector, req->nr_sectors);
 	/* Build locate_record + read/write ccws. */
 	idaws = (unsigned long *) (cqr->data + sizeof(struct DE_fba_data));
 	LO_data = (struct LO_fba_data *) (idaws + cidaw);
@@ -326,7 +335,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
 					ccw[-1].flags |= CCW_FLAG_CC;
 			}
 			ccw->cmd_code = cmd;
-			ccw->count = device->bp_block;
+			ccw->count = block->bp_block;
 			if (idal_is_needed(dst, blksize)) {
 				ccw->cda = (__u32)(addr_t) idaws;
 				ccw->flags = CCW_FLAG_IDA;
@@ -342,7 +351,9 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
 	}
 	if (req->cmd_flags & REQ_FAILFAST)
 		set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
-	cqr->device = device;
+	cqr->startdev = memdev;
+	cqr->memdev = memdev;
+	cqr->block = block;
 	cqr->expires = 5 * 60 * HZ;	/* 5 minutes */
 	cqr->retries = 32;
 	cqr->buildclk = get_clock();
@@ -363,8 +374,8 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)
 
 	if (!dasd_page_cache)
 		goto out;
-	private = (struct dasd_fba_private *) cqr->device->private;
-	blksize = cqr->device->bp_block;
+	private = (struct dasd_fba_private *) cqr->block->base->private;
+	blksize = cqr->block->bp_block;
 	ccw = cqr->cpaddr;
 	/* Skip over define extent & locate record. */
 	ccw++;
@@ -394,10 +405,15 @@ dasd_fba_free_cp(struct dasd_ccw_req *cqr, struct request *req)
 	}
 out:
 	status = cqr->status == DASD_CQR_DONE;
-	dasd_sfree_request(cqr, cqr->device);
+	dasd_sfree_request(cqr, cqr->memdev);
 	return status;
 }
 
+static void dasd_fba_handle_terminated_request(struct dasd_ccw_req *cqr)
+{
+	cqr->status = DASD_CQR_FILLED;
+};
+
 static int
 dasd_fba_fill_info(struct dasd_device * device,
 		   struct dasd_information2_t * info)
@@ -546,9 +562,10 @@ static struct dasd_discipline dasd_fba_discipline = {
 	.fill_geometry = dasd_fba_fill_geometry,
 	.start_IO = dasd_start_IO,
 	.term_IO = dasd_term_IO,
-	.examine_error = dasd_fba_examine_error,
+	.handle_terminated_request = dasd_fba_handle_terminated_request,
 	.erp_action = dasd_fba_erp_action,
 	.erp_postaction = dasd_fba_erp_postaction,
+	.handle_unsolicited_interrupt = dasd_fba_handle_unsolicited_interrupt,
 	.build_cp = dasd_fba_build_cp,
 	.free_cp = dasd_fba_free_cp,
 	.dump_sense = dasd_fba_dump_sense,

+ 36 - 40
drivers/s390/block/dasd_genhd.c

@@ -25,14 +25,15 @@
 /*
  * Allocate and register gendisk structure for device.
  */
-int
-dasd_gendisk_alloc(struct dasd_device *device)
+int dasd_gendisk_alloc(struct dasd_block *block)
 {
 	struct gendisk *gdp;
+	struct dasd_device *base;
 	int len;
 
 	/* Make sure the minor for this device exists. */
-	if (device->devindex >= DASD_PER_MAJOR)
+	base = block->base;
+	if (base->devindex >= DASD_PER_MAJOR)
 		return -EBUSY;
 
 	gdp = alloc_disk(1 << DASD_PARTN_BITS);
@@ -41,9 +42,9 @@ dasd_gendisk_alloc(struct dasd_device *device)
 
 	/* Initialize gendisk structure. */
 	gdp->major = DASD_MAJOR;
-	gdp->first_minor = device->devindex << DASD_PARTN_BITS;
+	gdp->first_minor = base->devindex << DASD_PARTN_BITS;
 	gdp->fops = &dasd_device_operations;
-	gdp->driverfs_dev = &device->cdev->dev;
+	gdp->driverfs_dev = &base->cdev->dev;
 
 	/*
 	 * Set device name.
@@ -53,53 +54,51 @@ dasd_gendisk_alloc(struct dasd_device *device)
 	 *   dasdaaaa - dasdzzzz : 456976 devices, added up = 475252
 	 */
 	len = sprintf(gdp->disk_name, "dasd");
-	if (device->devindex > 25) {
-	        if (device->devindex > 701) {
-		        if (device->devindex > 18277)
+	if (base->devindex > 25) {
+		if (base->devindex > 701) {
+			if (base->devindex > 18277)
 			        len += sprintf(gdp->disk_name + len, "%c",
-					       'a'+(((device->devindex-18278)
+					       'a'+(((base->devindex-18278)
 						     /17576)%26));
 			len += sprintf(gdp->disk_name + len, "%c",
-				       'a'+(((device->devindex-702)/676)%26));
+				       'a'+(((base->devindex-702)/676)%26));
 		}
 		len += sprintf(gdp->disk_name + len, "%c",
-			       'a'+(((device->devindex-26)/26)%26));
+			       'a'+(((base->devindex-26)/26)%26));
 	}
-	len += sprintf(gdp->disk_name + len, "%c", 'a'+(device->devindex%26));
+	len += sprintf(gdp->disk_name + len, "%c", 'a'+(base->devindex%26));
 
-	if (device->features & DASD_FEATURE_READONLY)
+	if (block->base->features & DASD_FEATURE_READONLY)
 		set_disk_ro(gdp, 1);
-	gdp->private_data = device;
-	gdp->queue = device->request_queue;
-	device->gdp = gdp;
-	set_capacity(device->gdp, 0);
-	add_disk(device->gdp);
+	gdp->private_data = block;
+	gdp->queue = block->request_queue;
+	block->gdp = gdp;
+	set_capacity(block->gdp, 0);
+	add_disk(block->gdp);
 	return 0;
 }
 
 /*
  * Unregister and free gendisk structure for device.
  */
-void
-dasd_gendisk_free(struct dasd_device *device)
+void dasd_gendisk_free(struct dasd_block *block)
 {
-	if (device->gdp) {
-		del_gendisk(device->gdp);
-		device->gdp->queue = NULL;
-		put_disk(device->gdp);
-		device->gdp = NULL;
+	if (block->gdp) {
+		del_gendisk(block->gdp);
+		block->gdp->queue = NULL;
+		put_disk(block->gdp);
+		block->gdp = NULL;
 	}
 }
 
 /*
  * Trigger a partition detection.
  */
-int
-dasd_scan_partitions(struct dasd_device * device)
+int dasd_scan_partitions(struct dasd_block *block)
 {
 	struct block_device *bdev;
 
-	bdev = bdget_disk(device->gdp, 0);
+	bdev = bdget_disk(block->gdp, 0);
 	if (!bdev || blkdev_get(bdev, FMODE_READ, 1) < 0)
 		return -ENODEV;
 	/*
@@ -117,7 +116,7 @@ dasd_scan_partitions(struct dasd_device * device)
 	 * is why the assignment to device->bdev is done AFTER
 	 * the BLKRRPART ioctl.
 	 */
-	device->bdev = bdev;
+	block->bdev = bdev;
 	return 0;
 }
 
@@ -125,8 +124,7 @@ dasd_scan_partitions(struct dasd_device * device)
  * Remove all inodes in the system for a device, delete the
  * partitions and make device unusable by setting its size to zero.
  */
-void
-dasd_destroy_partitions(struct dasd_device * device)
+void dasd_destroy_partitions(struct dasd_block *block)
 {
 	/* The two structs have 168/176 byte on 31/64 bit. */
 	struct blkpg_partition bpart;
@@ -137,8 +135,8 @@ dasd_destroy_partitions(struct dasd_device * device)
 	 * Get the bdev pointer from the device structure and clear
 	 * device->bdev to lower the offline open_count limit again.
 	 */
-	bdev = device->bdev;
-	device->bdev = NULL;
+	bdev = block->bdev;
+	block->bdev = NULL;
 
 	/*
 	 * See fs/partition/check.c:delete_partition
@@ -149,17 +147,16 @@ dasd_destroy_partitions(struct dasd_device * device)
 	memset(&barg, 0, sizeof(struct blkpg_ioctl_arg));
 	barg.data = (void __force __user *) &bpart;
 	barg.op = BLKPG_DEL_PARTITION;
-	for (bpart.pno = device->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
+	for (bpart.pno = block->gdp->minors - 1; bpart.pno > 0; bpart.pno--)
 		ioctl_by_bdev(bdev, BLKPG, (unsigned long) &barg);
 
-	invalidate_partition(device->gdp, 0);
+	invalidate_partition(block->gdp, 0);
 	/* Matching blkdev_put to the blkdev_get in dasd_scan_partitions. */
 	blkdev_put(bdev);
-	set_capacity(device->gdp, 0);
+	set_capacity(block->gdp, 0);
 }
 
-int
-dasd_gendisk_init(void)
+int dasd_gendisk_init(void)
 {
 	int rc;
 
@@ -174,8 +171,7 @@ dasd_gendisk_init(void)
 	return 0;
 }
 
-void
-dasd_gendisk_exit(void)
+void dasd_gendisk_exit(void)
 {
 	unregister_blkdev(DASD_MAJOR, "dasd");
 }

+ 127 - 82
drivers/s390/block/dasd_int.h

@@ -64,13 +64,7 @@
  * SECTION: Type definitions
  */
 struct dasd_device;
-
-typedef enum {
-	dasd_era_fatal = -1,	/* no chance to recover		     */
-	dasd_era_none = 0,	/* don't recover, everything alright */
-	dasd_era_msg = 1,	/* don't recover, just report...     */
-	dasd_era_recover = 2	/* recovery action recommended	     */
-} dasd_era_t;
+struct dasd_block;
 
 /* BIT DEFINITIONS FOR SENSE DATA */
 #define DASD_SENSE_BIT_0 0x80
@@ -151,19 +145,22 @@ do { \
 
 struct dasd_ccw_req {
 	unsigned int magic;		/* Eye catcher */
-        struct list_head list;		/* list_head for request queueing. */
+	struct list_head devlist;	/* for dasd_device request queue */
+	struct list_head blocklist;	/* for dasd_block request queue */
 
 	/* Where to execute what... */
-	struct dasd_device *device;	/* device the request is for */
+	struct dasd_block *block;	/* the originating block device */
+	struct dasd_device *memdev;	/* the device used to allocate this */
+	struct dasd_device *startdev;	/* device the request is started on */
 	struct ccw1 *cpaddr;		/* address of channel program */
-	char status;	        	/* status of this request */
+	char status;			/* status of this request */
 	short retries;			/* A retry counter */
 	unsigned long flags;        	/* flags of this request */
 
 	/* ... and how */
 	unsigned long starttime;	/* jiffies time of request start */
 	int expires;			/* expiration period in jiffies */
-	char lpm;               	/* logical path mask */
+	char lpm;			/* logical path mask */
 	void *data;			/* pointer to data area */
 
 	/* these are important for recovering erroneous requests          */
@@ -178,20 +175,27 @@ struct dasd_ccw_req {
 	unsigned long long endclk;	/* TOD-clock of request termination */
 
         /* Callback that is called after reaching final status. */
-        void (*callback)(struct dasd_ccw_req *, void *data);
-        void *callback_data;
+	void (*callback)(struct dasd_ccw_req *, void *data);
+	void *callback_data;
 };
 
 /*
  * dasd_ccw_req -> status can be:
  */
-#define DASD_CQR_FILLED   0x00	/* request is ready to be processed */
-#define DASD_CQR_QUEUED   0x01	/* request is queued to be processed */
-#define DASD_CQR_IN_IO    0x02	/* request is currently in IO */
-#define DASD_CQR_DONE     0x03	/* request is completed successfully */
-#define DASD_CQR_ERROR    0x04	/* request is completed with error */
-#define DASD_CQR_FAILED   0x05	/* request is finally failed */
-#define DASD_CQR_CLEAR    0x06	/* request is clear pending */
+#define DASD_CQR_FILLED 	0x00	/* request is ready to be processed */
+#define DASD_CQR_DONE		0x01	/* request is completed successfully */
+#define DASD_CQR_NEED_ERP	0x02	/* request needs recovery action */
+#define DASD_CQR_IN_ERP 	0x03	/* request is in recovery */
+#define DASD_CQR_FAILED 	0x04	/* request is finally failed */
+#define DASD_CQR_TERMINATED	0x05	/* request was stopped by driver */
+
+#define DASD_CQR_QUEUED 	0x80	/* request is queued to be processed */
+#define DASD_CQR_IN_IO		0x81	/* request is currently in IO */
+#define DASD_CQR_ERROR		0x82	/* request is completed with error */
+#define DASD_CQR_CLEAR_PENDING	0x83	/* request is clear pending */
+#define DASD_CQR_CLEARED	0x84	/* request was cleared */
+#define DASD_CQR_SUCCESS	0x85	/* request was successfull */
+
 
 /* per dasd_ccw_req flags */
 #define DASD_CQR_FLAGS_USE_ERP   0	/* use ERP for this request */
@@ -214,52 +218,71 @@ struct dasd_discipline {
 
 	struct list_head list;	/* used for list of disciplines */
 
-        /*
-         * Device recognition functions. check_device is used to verify
-         * the sense data and the information returned by read device
-         * characteristics. It returns 0 if the discipline can be used
-         * for the device in question.
-         * do_analysis is used in the step from device state "basic" to
-         * state "accept". It returns 0 if the device can be made ready,
-         * it returns -EMEDIUMTYPE if the device can't be made ready or
-         * -EAGAIN if do_analysis started a ccw that needs to complete
-         * before the analysis may be repeated.
-         */
-        int (*check_device)(struct dasd_device *);
-	int (*do_analysis) (struct dasd_device *);
-
-        /*
-         * Device operation functions. build_cp creates a ccw chain for
-         * a block device request, start_io starts the request and
-         * term_IO cancels it (e.g. in case of a timeout). format_device
-         * returns a ccw chain to be used to format the device.
-         */
+	/*
+	 * Device recognition functions. check_device is used to verify
+	 * the sense data and the information returned by read device
+	 * characteristics. It returns 0 if the discipline can be used
+	 * for the device in question. uncheck_device is called during
+	 * device shutdown to deregister a device from its discipline.
+	 */
+	int (*check_device) (struct dasd_device *);
+	void (*uncheck_device) (struct dasd_device *);
+
+	/*
+	 * do_analysis is used in the step from device state "basic" to
+	 * state "accept". It returns 0 if the device can be made ready,
+	 * it returns -EMEDIUMTYPE if the device can't be made ready or
+	 * -EAGAIN if do_analysis started a ccw that needs to complete
+	 * before the analysis may be repeated.
+	 */
+	int (*do_analysis) (struct dasd_block *);
+
+	/*
+	 * Last things to do when a device is set online, and first things
+	 * when it is set offline.
+	 */
+	int (*ready_to_online) (struct dasd_device *);
+	int (*online_to_ready) (struct dasd_device *);
+
+	/*
+	 * Device operation functions. build_cp creates a ccw chain for
+	 * a block device request, start_io starts the request and
+	 * term_IO cancels it (e.g. in case of a timeout). format_device
+	 * returns a ccw chain to be used to format the device.
+	 * handle_terminated_request allows to examine a cqr and prepare
+	 * it for retry.
+	 */
 	struct dasd_ccw_req *(*build_cp) (struct dasd_device *,
+					  struct dasd_block *,
 					  struct request *);
 	int (*start_IO) (struct dasd_ccw_req *);
 	int (*term_IO) (struct dasd_ccw_req *);
+	void (*handle_terminated_request) (struct dasd_ccw_req *);
 	struct dasd_ccw_req *(*format_device) (struct dasd_device *,
 					       struct format_data_t *);
 	int (*free_cp) (struct dasd_ccw_req *, struct request *);
-        /*
-         * Error recovery functions. examine_error() returns a value that
-         * indicates what to do for an error condition. If examine_error()
+
+	/*
+	 * Error recovery functions. examine_error() returns a value that
+	 * indicates what to do for an error condition. If examine_error()
 	 * returns 'dasd_era_recover' erp_action() is called to create a
-         * special error recovery ccw. erp_postaction() is called after
-         * an error recovery ccw has finished its execution. dump_sense
-         * is called for every error condition to print the sense data
-         * to the console.
-         */
-	dasd_era_t(*examine_error) (struct dasd_ccw_req *, struct irb *);
+	 * special error recovery ccw. erp_postaction() is called after
+	 * an error recovery ccw has finished its execution. dump_sense
+	 * is called for every error condition to print the sense data
+	 * to the console.
+	 */
 	dasd_erp_fn_t(*erp_action) (struct dasd_ccw_req *);
 	dasd_erp_fn_t(*erp_postaction) (struct dasd_ccw_req *);
 	void (*dump_sense) (struct dasd_device *, struct dasd_ccw_req *,
 			    struct irb *);
 
+	void (*handle_unsolicited_interrupt) (struct dasd_device *,
+					      struct irb *);
+
         /* i/o control functions. */
-	int (*fill_geometry) (struct dasd_device *, struct hd_geometry *);
+	int (*fill_geometry) (struct dasd_block *, struct hd_geometry *);
 	int (*fill_info) (struct dasd_device *, struct dasd_information2_t *);
-	int (*ioctl) (struct dasd_device *, unsigned int, void __user *);
+	int (*ioctl) (struct dasd_block *, unsigned int, void __user *);
 };
 
 extern struct dasd_discipline *dasd_diag_discipline_pointer;
@@ -267,12 +290,18 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer;
 /*
  * Unique identifier for dasd device.
  */
+#define UA_NOT_CONFIGURED  0x00
+#define UA_BASE_DEVICE	   0x01
+#define UA_BASE_PAV_ALIAS  0x02
+#define UA_HYPER_PAV_ALIAS 0x03
+
 struct dasd_uid {
-	__u8 alias;
+	__u8 type;
 	char vendor[4];
 	char serial[15];
 	__u16 ssid;
-	__u8 unit_addr;
+	__u8 real_unit_addr;
+	__u8 base_unit_addr;
 };
 
 /*
@@ -293,14 +322,9 @@ struct dasd_uid {
 
 struct dasd_device {
 	/* Block device stuff. */
-	struct gendisk *gdp;
-	struct request_queue *request_queue;
-	spinlock_t request_queue_lock;
-	struct block_device *bdev;
+	struct dasd_block *block;
+
         unsigned int devindex;
-	unsigned long blocks;	   /* size of volume in blocks */
-	unsigned int bp_block;	   /* bytes per block */
-	unsigned int s2b_shift;	   /* log2 (bp_block/512) */
 	unsigned long flags;	   /* per device flags */
 	unsigned short features;   /* copy of devmap-features (read-only!) */
 
@@ -316,9 +340,8 @@ struct dasd_device {
 	int state, target;
 	int stopped;		/* device (ccw_device_start) was stopped */
 
-	/* Open and reference count. */
+	/* reference count. */
         atomic_t ref_count;
-	atomic_t open_count;
 
 	/* ccw queue and memory for static ccw/erp buffers. */
 	struct list_head ccw_queue;
@@ -337,20 +360,45 @@ struct dasd_device {
 
 	struct ccw_device *cdev;
 
+	/* hook for alias management */
+	struct list_head alias_list;
+};
+
+struct dasd_block {
+	/* Block device stuff. */
+	struct gendisk *gdp;
+	struct request_queue *request_queue;
+	spinlock_t request_queue_lock;
+	struct block_device *bdev;
+	atomic_t open_count;
+
+	unsigned long blocks;	   /* size of volume in blocks */
+	unsigned int bp_block;	   /* bytes per block */
+	unsigned int s2b_shift;	   /* log2 (bp_block/512) */
+
+	struct dasd_device *base;
+	struct list_head ccw_queue;
+	spinlock_t queue_lock;
+
+	atomic_t tasklet_scheduled;
+	struct tasklet_struct tasklet;
+	struct timer_list timer;
+
 #ifdef CONFIG_DASD_PROFILE
 	struct dasd_profile_info_t profile;
 #endif
 };
 
+
+
 /* reasons why device (ccw_device_start) was stopped */
 #define DASD_STOPPED_NOT_ACC 1         /* not accessible */
 #define DASD_STOPPED_QUIESCE 2         /* Quiesced */
 #define DASD_STOPPED_PENDING 4         /* long busy */
 #define DASD_STOPPED_DC_WAIT 8         /* disconnected, wait */
-#define DASD_STOPPED_DC_EIO  16        /* disconnected, return -EIO */
+#define DASD_STOPPED_SU      16        /* summary unit check handling */
 
 /* per device flags */
-#define DASD_FLAG_DSC_ERROR	2	/* return -EIO when disconnected */
 #define DASD_FLAG_OFFLINE	3	/* device is in offline processing */
 #define DASD_FLAG_EER_SNSS	4	/* A SNSS is required */
 #define DASD_FLAG_EER_IN_USE	5	/* A SNSS request is running */
@@ -489,6 +537,9 @@ dasd_kmalloc_set_cda(struct ccw1 *ccw, void *cda, struct dasd_device *device)
 struct dasd_device *dasd_alloc_device(void);
 void dasd_free_device(struct dasd_device *);
 
+struct dasd_block *dasd_alloc_block(void);
+void dasd_free_block(struct dasd_block *);
+
 void dasd_enable_device(struct dasd_device *);
 void dasd_set_target_state(struct dasd_device *, int);
 void dasd_kick_device(struct dasd_device *);
@@ -497,18 +548,23 @@ void dasd_add_request_head(struct dasd_ccw_req *);
 void dasd_add_request_tail(struct dasd_ccw_req *);
 int  dasd_start_IO(struct dasd_ccw_req *);
 int  dasd_term_IO(struct dasd_ccw_req *);
-void dasd_schedule_bh(struct dasd_device *);
+void dasd_schedule_device_bh(struct dasd_device *);
+void dasd_schedule_block_bh(struct dasd_block *);
 int  dasd_sleep_on(struct dasd_ccw_req *);
 int  dasd_sleep_on_immediatly(struct dasd_ccw_req *);
 int  dasd_sleep_on_interruptible(struct dasd_ccw_req *);
-void dasd_set_timer(struct dasd_device *, int);
-void dasd_clear_timer(struct dasd_device *);
+void dasd_device_set_timer(struct dasd_device *, int);
+void dasd_device_clear_timer(struct dasd_device *);
+void dasd_block_set_timer(struct dasd_block *, int);
+void dasd_block_clear_timer(struct dasd_block *);
 int  dasd_cancel_req(struct dasd_ccw_req *);
+int dasd_flush_device_queue(struct dasd_device *);
 int dasd_generic_probe (struct ccw_device *, struct dasd_discipline *);
 void dasd_generic_remove (struct ccw_device *cdev);
 int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
 int dasd_generic_set_offline (struct ccw_device *cdev);
 int dasd_generic_notify(struct ccw_device *, int);
+void dasd_generic_handle_state_change(struct dasd_device *);
 
 int dasd_generic_read_dev_chars(struct dasd_device *, char *, void **, int);
 
@@ -542,10 +598,10 @@ int dasd_busid_known(char *);
 /* externals in dasd_gendisk.c */
 int  dasd_gendisk_init(void);
 void dasd_gendisk_exit(void);
-int dasd_gendisk_alloc(struct dasd_device *);
-void dasd_gendisk_free(struct dasd_device *);
-int dasd_scan_partitions(struct dasd_device *);
-void dasd_destroy_partitions(struct dasd_device *);
+int dasd_gendisk_alloc(struct dasd_block *);
+void dasd_gendisk_free(struct dasd_block *);
+int dasd_scan_partitions(struct dasd_block *);
+void dasd_destroy_partitions(struct dasd_block *);
 
 /* externals in dasd_ioctl.c */
 int  dasd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
@@ -563,20 +619,9 @@ struct dasd_ccw_req *dasd_alloc_erp_request(char *, int, int,
 void dasd_free_erp_request(struct dasd_ccw_req *, struct dasd_device *);
 void dasd_log_sense(struct dasd_ccw_req *, struct irb *);
 
-/* externals in dasd_3370_erp.c */
-dasd_era_t dasd_3370_erp_examine(struct dasd_ccw_req *, struct irb *);
-
 /* externals in dasd_3990_erp.c */
-dasd_era_t dasd_3990_erp_examine(struct dasd_ccw_req *, struct irb *);
 struct dasd_ccw_req *dasd_3990_erp_action(struct dasd_ccw_req *);
 
-/* externals in dasd_9336_erp.c */
-dasd_era_t dasd_9336_erp_examine(struct dasd_ccw_req *, struct irb *);
-
-/* externals in dasd_9336_erp.c */
-dasd_era_t dasd_9343_erp_examine(struct dasd_ccw_req *, struct irb *);
-struct dasd_ccw_req *dasd_9343_erp_action(struct dasd_ccw_req *);
-
 /* externals in dasd_eer.c */
 #ifdef CONFIG_DASD_EER
 int dasd_eer_init(void);

+ 84 - 88
drivers/s390/block/dasd_ioctl.c

@@ -38,15 +38,15 @@ dasd_ioctl_api_version(void __user *argp)
 static int
 dasd_ioctl_enable(struct block_device *bdev)
 {
-	struct dasd_device *device = bdev->bd_disk->private_data;
+	struct dasd_block *block = bdev->bd_disk->private_data;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
 
-	dasd_enable_device(device);
+	dasd_enable_device(block->base);
 	/* Formatting the dasd device can change the capacity. */
 	mutex_lock(&bdev->bd_mutex);
-	i_size_write(bdev->bd_inode, (loff_t)get_capacity(device->gdp) << 9);
+	i_size_write(bdev->bd_inode, (loff_t)get_capacity(block->gdp) << 9);
 	mutex_unlock(&bdev->bd_mutex);
 	return 0;
 }
@@ -58,7 +58,7 @@ dasd_ioctl_enable(struct block_device *bdev)
 static int
 dasd_ioctl_disable(struct block_device *bdev)
 {
-	struct dasd_device *device = bdev->bd_disk->private_data;
+	struct dasd_block *block = bdev->bd_disk->private_data;
 
 	if (!capable(CAP_SYS_ADMIN))
 		return -EACCES;
@@ -71,7 +71,7 @@ dasd_ioctl_disable(struct block_device *bdev)
 	 * using the BIODASDFMT ioctl. Therefore the correct state for the
 	 * device is DASD_STATE_BASIC that allows to do basic i/o.
 	 */
-	dasd_set_target_state(device, DASD_STATE_BASIC);
+	dasd_set_target_state(block->base, DASD_STATE_BASIC);
 	/*
 	 * Set i_size to zero, since read, write, etc. check against this
 	 * value.
@@ -85,19 +85,19 @@ dasd_ioctl_disable(struct block_device *bdev)
 /*
  * Quiesce device.
  */
-static int
-dasd_ioctl_quiesce(struct dasd_device *device)
+static int dasd_ioctl_quiesce(struct dasd_block *block)
 {
 	unsigned long flags;
+	struct dasd_device *base;
 
+	base = block->base;
 	if (!capable (CAP_SYS_ADMIN))
 		return -EACCES;
 
-	DEV_MESSAGE (KERN_DEBUG, device, "%s",
-		     "Quiesce IO on device");
-	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-	device->stopped |= DASD_STOPPED_QUIESCE;
-	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+	DEV_MESSAGE(KERN_DEBUG, base, "%s", "Quiesce IO on device");
+	spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+	base->stopped |= DASD_STOPPED_QUIESCE;
+	spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
 	return 0;
 }
 
@@ -105,22 +105,21 @@ dasd_ioctl_quiesce(struct dasd_device *device)
 /*
  * Quiesce device.
  */
-static int
-dasd_ioctl_resume(struct dasd_device *device)
+static int dasd_ioctl_resume(struct dasd_block *block)
 {
 	unsigned long flags;
+	struct dasd_device *base;
 
+	base = block->base;
 	if (!capable (CAP_SYS_ADMIN))
 		return -EACCES;
 
-	DEV_MESSAGE (KERN_DEBUG, device, "%s",
-		     "resume IO on device");
-
-	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-	device->stopped &= ~DASD_STOPPED_QUIESCE;
-	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+	DEV_MESSAGE(KERN_DEBUG, base, "%s", "resume IO on device");
+	spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+	base->stopped &= ~DASD_STOPPED_QUIESCE;
+	spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
 
-	dasd_schedule_bh (device);
+	dasd_schedule_block_bh(block);
 	return 0;
 }
 
@@ -130,22 +129,23 @@ dasd_ioctl_resume(struct dasd_device *device)
  * commands to format a single unit of the device. In terms of the ECKD
  * devices this means CCWs are generated to format a single track.
  */
-static int
-dasd_format(struct dasd_device * device, struct format_data_t * fdata)
+static int dasd_format(struct dasd_block *block, struct format_data_t *fdata)
 {
 	struct dasd_ccw_req *cqr;
+	struct dasd_device *base;
 	int rc;
 
-	if (device->discipline->format_device == NULL)
+	base = block->base;
+	if (base->discipline->format_device == NULL)
 		return -EPERM;
 
-	if (device->state != DASD_STATE_BASIC) {
-		DEV_MESSAGE(KERN_WARNING, device, "%s",
+	if (base->state != DASD_STATE_BASIC) {
+		DEV_MESSAGE(KERN_WARNING, base, "%s",
 			    "dasd_format: device is not disabled! ");
 		return -EBUSY;
 	}
 
-	DBF_DEV_EVENT(DBF_NOTICE, device,
+	DBF_DEV_EVENT(DBF_NOTICE, base,
 		      "formatting units %d to %d (%d B blocks) flags %d",
 		      fdata->start_unit,
 		      fdata->stop_unit, fdata->blksize, fdata->intensity);
@@ -156,20 +156,20 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata)
 	 * enabling the device later.
 	 */
 	if (fdata->start_unit == 0) {
-		struct block_device *bdev = bdget_disk(device->gdp, 0);
+		struct block_device *bdev = bdget_disk(block->gdp, 0);
 		bdev->bd_inode->i_blkbits = blksize_bits(fdata->blksize);
 		bdput(bdev);
 	}
 
 	while (fdata->start_unit <= fdata->stop_unit) {
-		cqr = device->discipline->format_device(device, fdata);
+		cqr = base->discipline->format_device(base, fdata);
 		if (IS_ERR(cqr))
 			return PTR_ERR(cqr);
 		rc = dasd_sleep_on_interruptible(cqr);
-		dasd_sfree_request(cqr, cqr->device);
+		dasd_sfree_request(cqr, cqr->memdev);
 		if (rc) {
 			if (rc != -ERESTARTSYS)
-				DEV_MESSAGE(KERN_ERR, device,
+				DEV_MESSAGE(KERN_ERR, base,
 					    " Formatting of unit %d failed "
 					    "with rc = %d",
 					    fdata->start_unit, rc);
@@ -186,7 +186,7 @@ dasd_format(struct dasd_device * device, struct format_data_t * fdata)
 static int
 dasd_ioctl_format(struct block_device *bdev, void __user *argp)
 {
-	struct dasd_device *device = bdev->bd_disk->private_data;
+	struct dasd_block *block = bdev->bd_disk->private_data;
 	struct format_data_t fdata;
 
 	if (!capable(CAP_SYS_ADMIN))
@@ -194,51 +194,47 @@ dasd_ioctl_format(struct block_device *bdev, void __user *argp)
 	if (!argp)
 		return -EINVAL;
 
-	if (device->features & DASD_FEATURE_READONLY)
+	if (block->base->features & DASD_FEATURE_READONLY)
 		return -EROFS;
 	if (copy_from_user(&fdata, argp, sizeof(struct format_data_t)))
 		return -EFAULT;
 	if (bdev != bdev->bd_contains) {
-		DEV_MESSAGE(KERN_WARNING, device, "%s",
+		DEV_MESSAGE(KERN_WARNING, block->base, "%s",
 			    "Cannot low-level format a partition");
 		return -EINVAL;
 	}
-	return dasd_format(device, &fdata);
+	return dasd_format(block, &fdata);
 }
 
 #ifdef CONFIG_DASD_PROFILE
 /*
  * Reset device profile information
  */
-static int
-dasd_ioctl_reset_profile(struct dasd_device *device)
+static int dasd_ioctl_reset_profile(struct dasd_block *block)
 {
-	memset(&device->profile, 0, sizeof (struct dasd_profile_info_t));
+	memset(&block->profile, 0, sizeof(struct dasd_profile_info_t));
 	return 0;
 }
 
 /*
  * Return device profile information
  */
-static int
-dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
+static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
 {
 	if (dasd_profile_level == DASD_PROFILE_OFF)
 		return -EIO;
-	if (copy_to_user(argp, &device->profile,
-			 sizeof (struct dasd_profile_info_t)))
+	if (copy_to_user(argp, &block->profile,
+			 sizeof(struct dasd_profile_info_t)))
 		return -EFAULT;
 	return 0;
 }
 #else
-static int
-dasd_ioctl_reset_profile(struct dasd_device *device)
+static int dasd_ioctl_reset_profile(struct dasd_block *block)
 {
 	return -ENOSYS;
 }
 
-static int
-dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
+static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
 {
 	return -ENOSYS;
 }
@@ -247,87 +243,88 @@ dasd_ioctl_read_profile(struct dasd_device *device, void __user *argp)
 /*
  * Return dasd information. Used for BIODASDINFO and BIODASDINFO2.
  */
-static int
-dasd_ioctl_information(struct dasd_device *device,
-		unsigned int cmd, void __user *argp)
+static int dasd_ioctl_information(struct dasd_block *block,
+				  unsigned int cmd, void __user *argp)
 {
 	struct dasd_information2_t *dasd_info;
 	unsigned long flags;
 	int rc;
+	struct dasd_device *base;
 	struct ccw_device *cdev;
 	struct ccw_dev_id dev_id;
 
-	if (!device->discipline->fill_info)
+	base = block->base;
+	if (!base->discipline->fill_info)
 		return -EINVAL;
 
 	dasd_info = kzalloc(sizeof(struct dasd_information2_t), GFP_KERNEL);
 	if (dasd_info == NULL)
 		return -ENOMEM;
 
-	rc = device->discipline->fill_info(device, dasd_info);
+	rc = base->discipline->fill_info(base, dasd_info);
 	if (rc) {
 		kfree(dasd_info);
 		return rc;
 	}
 
-	cdev = device->cdev;
+	cdev = base->cdev;
 	ccw_device_get_id(cdev, &dev_id);
 
 	dasd_info->devno = dev_id.devno;
-	dasd_info->schid = _ccw_device_get_subchannel_number(device->cdev);
+	dasd_info->schid = _ccw_device_get_subchannel_number(base->cdev);
 	dasd_info->cu_type = cdev->id.cu_type;
 	dasd_info->cu_model = cdev->id.cu_model;
 	dasd_info->dev_type = cdev->id.dev_type;
 	dasd_info->dev_model = cdev->id.dev_model;
-	dasd_info->status = device->state;
+	dasd_info->status = base->state;
 	/*
 	 * The open_count is increased for every opener, that includes
 	 * the blkdev_get in dasd_scan_partitions.
 	 * This must be hidden from user-space.
 	 */
-	dasd_info->open_count = atomic_read(&device->open_count);
-	if (!device->bdev)
+	dasd_info->open_count = atomic_read(&block->open_count);
+	if (!block->bdev)
 		dasd_info->open_count++;
 
 	/*
 	 * check if device is really formatted
 	 * LDL / CDL was returned by 'fill_info'
 	 */
-	if ((device->state < DASD_STATE_READY) ||
-	    (dasd_check_blocksize(device->bp_block)))
+	if ((base->state < DASD_STATE_READY) ||
+	    (dasd_check_blocksize(block->bp_block)))
 		dasd_info->format = DASD_FORMAT_NONE;
 
 	dasd_info->features |=
-		((device->features & DASD_FEATURE_READONLY) != 0);
+		((base->features & DASD_FEATURE_READONLY) != 0);
 
-	if (device->discipline)
-		memcpy(dasd_info->type, device->discipline->name, 4);
+	if (base->discipline)
+		memcpy(dasd_info->type, base->discipline->name, 4);
 	else
 		memcpy(dasd_info->type, "none", 4);
 
-	if (device->request_queue->request_fn) {
+	if (block->request_queue->request_fn) {
 		struct list_head *l;
 #ifdef DASD_EXTENDED_PROFILING
 		{
 			struct list_head *l;
-			spin_lock_irqsave(&device->lock, flags);
-			list_for_each(l, &device->request_queue->queue_head)
+			spin_lock_irqsave(&block->lock, flags);
+			list_for_each(l, &block->request_queue->queue_head)
 				dasd_info->req_queue_len++;
-			spin_unlock_irqrestore(&device->lock, flags);
+			spin_unlock_irqrestore(&block->lock, flags);
 		}
 #endif				/* DASD_EXTENDED_PROFILING */
-		spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
-		list_for_each(l, &device->ccw_queue)
+		spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
+		list_for_each(l, &base->ccw_queue)
 			dasd_info->chanq_len++;
-		spin_unlock_irqrestore(get_ccwdev_lock(device->cdev),
+		spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
 				       flags);
 	}
 
 	rc = 0;
 	if (copy_to_user(argp, dasd_info,
 			 ((cmd == (unsigned int) BIODASDINFO2) ?
-			  sizeof (struct dasd_information2_t) :
-			  sizeof (struct dasd_information_t))))
+			  sizeof(struct dasd_information2_t) :
+			  sizeof(struct dasd_information_t))))
 		rc = -EFAULT;
 	kfree(dasd_info);
 	return rc;
@@ -339,7 +336,7 @@ dasd_ioctl_information(struct dasd_device *device,
 static int
 dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
 {
-	struct dasd_device *device =  bdev->bd_disk->private_data;
+	struct dasd_block *block =  bdev->bd_disk->private_data;
 	int intval;
 
 	if (!capable(CAP_SYS_ADMIN))
@@ -351,11 +348,10 @@ dasd_ioctl_set_ro(struct block_device *bdev, void __user *argp)
 		return -EFAULT;
 
 	set_disk_ro(bdev->bd_disk, intval);
-	return dasd_set_feature(device->cdev, DASD_FEATURE_READONLY, intval);
+	return dasd_set_feature(block->base->cdev, DASD_FEATURE_READONLY, intval);
 }
 
-static int
-dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
+static int dasd_ioctl_readall_cmb(struct dasd_block *block, unsigned int cmd,
 		unsigned long arg)
 {
 	struct cmbdata __user *argp = (void __user *) arg;
@@ -363,7 +359,7 @@ dasd_ioctl_readall_cmb(struct dasd_device *device, unsigned int cmd,
 	struct cmbdata data;
 	int ret;
 
-	ret = cmf_readall(device->cdev, &data);
+	ret = cmf_readall(block->base->cdev, &data);
 	if (!ret && copy_to_user(argp, &data, min(size, sizeof(*argp))))
 		return -EFAULT;
 	return ret;
@@ -374,10 +370,10 @@ dasd_ioctl(struct inode *inode, struct file *file,
 	   unsigned int cmd, unsigned long arg)
 {
 	struct block_device *bdev = inode->i_bdev;
-	struct dasd_device *device = bdev->bd_disk->private_data;
+	struct dasd_block *block = bdev->bd_disk->private_data;
 	void __user *argp = (void __user *)arg;
 
-	if (!device)
+	if (!block)
                 return -ENODEV;
 
 	if ((_IOC_DIR(cmd) != _IOC_NONE) && !arg) {
@@ -391,33 +387,33 @@ dasd_ioctl(struct inode *inode, struct file *file,
 	case BIODASDENABLE:
 		return dasd_ioctl_enable(bdev);
 	case BIODASDQUIESCE:
-		return dasd_ioctl_quiesce(device);
+		return dasd_ioctl_quiesce(block);
 	case BIODASDRESUME:
-		return dasd_ioctl_resume(device);
+		return dasd_ioctl_resume(block);
 	case BIODASDFMT:
 		return dasd_ioctl_format(bdev, argp);
 	case BIODASDINFO:
-		return dasd_ioctl_information(device, cmd, argp);
+		return dasd_ioctl_information(block, cmd, argp);
 	case BIODASDINFO2:
-		return dasd_ioctl_information(device, cmd, argp);
+		return dasd_ioctl_information(block, cmd, argp);
 	case BIODASDPRRD:
-		return dasd_ioctl_read_profile(device, argp);
+		return dasd_ioctl_read_profile(block, argp);
 	case BIODASDPRRST:
-		return dasd_ioctl_reset_profile(device);
+		return dasd_ioctl_reset_profile(block);
 	case BLKROSET:
 		return dasd_ioctl_set_ro(bdev, argp);
 	case DASDAPIVER:
 		return dasd_ioctl_api_version(argp);
 	case BIODASDCMFENABLE:
-		return enable_cmf(device->cdev);
+		return enable_cmf(block->base->cdev);
 	case BIODASDCMFDISABLE:
-		return disable_cmf(device->cdev);
+		return disable_cmf(block->base->cdev);
 	case BIODASDREADALLCMB:
-		return dasd_ioctl_readall_cmb(device, cmd, arg);
+		return dasd_ioctl_readall_cmb(block, cmd, arg);
 	default:
 		/* if the discipline has an ioctl method try it. */
-		if (device->discipline->ioctl) {
-			int rval = device->discipline->ioctl(device, cmd, argp);
+		if (block->base->discipline->ioctl) {
+			int rval = block->base->discipline->ioctl(block, cmd, argp);
 			if (rval != -ENOIOCTLCMD)
 				return rval;
 		}

+ 14 - 9
drivers/s390/block/dasd_proc.c

@@ -54,11 +54,16 @@ static int
 dasd_devices_show(struct seq_file *m, void *v)
 {
 	struct dasd_device *device;
+	struct dasd_block *block;
 	char *substr;
 
 	device = dasd_device_from_devindex((unsigned long) v - 1);
 	if (IS_ERR(device))
 		return 0;
+	if (device->block)
+		block = device->block;
+	else
+		return 0;
 	/* Print device number. */
 	seq_printf(m, "%s", device->cdev->dev.bus_id);
 	/* Print discipline string. */
@@ -67,14 +72,14 @@ dasd_devices_show(struct seq_file *m, void *v)
 	else
 		seq_printf(m, "(none)");
 	/* Print kdev. */
-	if (device->gdp)
+	if (block->gdp)
 		seq_printf(m, " at (%3d:%6d)",
-			   device->gdp->major, device->gdp->first_minor);
+			   block->gdp->major, block->gdp->first_minor);
 	else
 		seq_printf(m, "  at (???:??????)");
 	/* Print device name. */
-	if (device->gdp)
-		seq_printf(m, " is %-8s", device->gdp->disk_name);
+	if (block->gdp)
+		seq_printf(m, " is %-8s", block->gdp->disk_name);
 	else
 		seq_printf(m, " is ????????");
 	/* Print devices features. */
@@ -100,14 +105,14 @@ dasd_devices_show(struct seq_file *m, void *v)
 	case DASD_STATE_READY:
 	case DASD_STATE_ONLINE:
 		seq_printf(m, "active ");
-		if (dasd_check_blocksize(device->bp_block))
+		if (dasd_check_blocksize(block->bp_block))
 			seq_printf(m, "n/f	 ");
 		else
 			seq_printf(m,
 				   "at blocksize: %d, %ld blocks, %ld MB",
-				   device->bp_block, device->blocks,
-				   ((device->bp_block >> 9) *
-				    device->blocks) >> 11);
+				   block->bp_block, block->blocks,
+				   ((block->bp_block >> 9) *
+				    block->blocks) >> 11);
 		break;
 	default:
 		seq_printf(m, "no stat");
@@ -137,7 +142,7 @@ static void dasd_devices_stop(struct seq_file *m, void *v)
 {
 }
 
-static struct seq_operations dasd_devices_seq_ops = {
+static const struct seq_operations dasd_devices_seq_ops = {
 	.start		= dasd_devices_start,
 	.next		= dasd_devices_next,
 	.stop		= dasd_devices_stop,

+ 1 - 1
drivers/s390/block/dcssblk.c

@@ -82,7 +82,7 @@ struct dcssblk_dev_info {
 	struct request_queue *dcssblk_queue;
 };
 
-static struct list_head dcssblk_devices = LIST_HEAD_INIT(dcssblk_devices);
+static LIST_HEAD(dcssblk_devices);
 static struct rw_semaphore dcssblk_devices_sem;
 
 /*

+ 1 - 1
drivers/s390/char/Makefile

@@ -3,7 +3,7 @@
 #
 
 obj-y += ctrlchar.o keyboard.o defkeymap.o sclp.o sclp_rw.o sclp_quiesce.o \
-	 sclp_info.o sclp_config.o sclp_chp.o
+	 sclp_cmd.o sclp_config.o sclp_cpi_sys.o
 
 obj-$(CONFIG_TN3270) += raw3270.o
 obj-$(CONFIG_TN3270_CONSOLE) += con3270.o

+ 1 - 1
drivers/s390/char/monwriter.c

@@ -295,7 +295,7 @@ module_init(mon_init);
 module_exit(mon_exit);
 
 module_param_named(max_bufs, mon_max_bufs, int, 0644);
-MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers"
+MODULE_PARM_DESC(max_bufs, "Maximum number of sample monitor data buffers "
 		 "that can be active at one time");
 
 MODULE_AUTHOR("Melissa Howland <Melissa.Howland@us.ibm.com>");

+ 2 - 2
drivers/s390/char/raw3270.c

@@ -66,7 +66,7 @@ struct raw3270 {
 static DEFINE_MUTEX(raw3270_mutex);
 
 /* List of 3270 devices. */
-static struct list_head raw3270_devices = LIST_HEAD_INIT(raw3270_devices);
+static LIST_HEAD(raw3270_devices);
 
 /*
  * Flag to indicate if the driver has been registered. Some operations
@@ -1210,7 +1210,7 @@ struct raw3270_notifier {
 	void (*notifier)(int, int);
 };
 
-static struct list_head raw3270_notifier = LIST_HEAD_INIT(raw3270_notifier);
+static LIST_HEAD(raw3270_notifier);
 
 int raw3270_register_notifier(void (*notifier)(int, int))
 {

+ 2 - 2
drivers/s390/char/sclp.h

@@ -56,8 +56,6 @@ typedef unsigned int sclp_cmdw_t;
 #define SCLP_CMDW_READ_EVENT_DATA	0x00770005
 #define SCLP_CMDW_WRITE_EVENT_DATA	0x00760005
 #define SCLP_CMDW_WRITE_EVENT_MASK	0x00780005
-#define SCLP_CMDW_READ_SCP_INFO		0x00020001
-#define SCLP_CMDW_READ_SCP_INFO_FORCED	0x00120001
 
 #define GDS_ID_MDSMU		0x1310
 #define GDS_ID_MDSROUTEINFO	0x1311
@@ -83,6 +81,8 @@ extern u64 sclp_facilities;
 
 #define SCLP_HAS_CHP_INFO	(sclp_facilities & 0x8000000000000000ULL)
 #define SCLP_HAS_CHP_RECONFIG	(sclp_facilities & 0x2000000000000000ULL)
+#define SCLP_HAS_CPU_INFO	(sclp_facilities & 0x0800000000000000ULL)
+#define SCLP_HAS_CPU_RECONFIG	(sclp_facilities & 0x0400000000000000ULL)
 
 struct gds_subvector {
 	u8	length;

+ 0 - 200
drivers/s390/char/sclp_chp.c

@@ -1,200 +0,0 @@
-/*
- *  drivers/s390/char/sclp_chp.c
- *
- *    Copyright IBM Corp. 2007
- *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
- */
-
-#include <linux/types.h>
-#include <linux/gfp.h>
-#include <linux/errno.h>
-#include <linux/completion.h>
-#include <asm/sclp.h>
-#include <asm/chpid.h>
-
-#include "sclp.h"
-
-#define TAG	"sclp_chp: "
-
-#define SCLP_CMDW_CONFIGURE_CHANNEL_PATH	0x000f0001
-#define SCLP_CMDW_DECONFIGURE_CHANNEL_PATH	0x000e0001
-#define SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION	0x00030001
-
-static inline sclp_cmdw_t get_configure_cmdw(struct chp_id chpid)
-{
-	return SCLP_CMDW_CONFIGURE_CHANNEL_PATH | chpid.id << 8;
-}
-
-static inline sclp_cmdw_t get_deconfigure_cmdw(struct chp_id chpid)
-{
-	return SCLP_CMDW_DECONFIGURE_CHANNEL_PATH | chpid.id << 8;
-}
-
-static void chp_callback(struct sclp_req *req, void *data)
-{
-	struct completion *completion = data;
-
-	complete(completion);
-}
-
-struct chp_cfg_sccb {
-	struct sccb_header header;
-	u8 ccm;
-	u8 reserved[6];
-	u8 cssid;
-} __attribute__((packed));
-
-struct chp_cfg_data {
-	struct chp_cfg_sccb sccb;
-	struct sclp_req req;
-	struct completion completion;
-} __attribute__((packed));
-
-static int do_configure(sclp_cmdw_t cmd)
-{
-	struct chp_cfg_data *data;
-	int rc;
-
-	if (!SCLP_HAS_CHP_RECONFIG)
-		return -EOPNOTSUPP;
-	/* Prepare sccb. */
-	data = (struct chp_cfg_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
-	if (!data)
-		return -ENOMEM;
-	data->sccb.header.length = sizeof(struct chp_cfg_sccb);
-	data->req.command = cmd;
-	data->req.sccb = &(data->sccb);
-	data->req.status = SCLP_REQ_FILLED;
-	data->req.callback = chp_callback;
-	data->req.callback_data = &(data->completion);
-	init_completion(&data->completion);
-
-	/* Perform sclp request. */
-	rc = sclp_add_request(&(data->req));
-	if (rc)
-		goto out;
-	wait_for_completion(&data->completion);
-
-	/* Check response .*/
-	if (data->req.status != SCLP_REQ_DONE) {
-		printk(KERN_WARNING TAG "configure channel-path request failed "
-		       "(status=0x%02x)\n", data->req.status);
-		rc = -EIO;
-		goto out;
-	}
-	switch (data->sccb.header.response_code) {
-	case 0x0020:
-	case 0x0120:
-	case 0x0440:
-	case 0x0450:
-		break;
-	default:
-		printk(KERN_WARNING TAG "configure channel-path failed "
-		       "(cmd=0x%08x, response=0x%04x)\n", cmd,
-		       data->sccb.header.response_code);
-		rc = -EIO;
-		break;
-	}
-out:
-	free_page((unsigned long) data);
-
-	return rc;
-}
-
-/**
- * sclp_chp_configure - perform configure channel-path sclp command
- * @chpid: channel-path ID
- *
- * Perform configure channel-path command sclp command for specified chpid.
- * Return 0 after command successfully finished, non-zero otherwise.
- */
-int sclp_chp_configure(struct chp_id chpid)
-{
-	return do_configure(get_configure_cmdw(chpid));
-}
-
-/**
- * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
- * @chpid: channel-path ID
- *
- * Perform deconfigure channel-path command sclp command for specified chpid
- * and wait for completion. On success return 0. Return non-zero otherwise.
- */
-int sclp_chp_deconfigure(struct chp_id chpid)
-{
-	return do_configure(get_deconfigure_cmdw(chpid));
-}
-
-struct chp_info_sccb {
-	struct sccb_header header;
-	u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
-	u8 standby[SCLP_CHP_INFO_MASK_SIZE];
-	u8 configured[SCLP_CHP_INFO_MASK_SIZE];
-	u8 ccm;
-	u8 reserved[6];
-	u8 cssid;
-} __attribute__((packed));
-
-struct chp_info_data {
-	struct chp_info_sccb sccb;
-	struct sclp_req req;
-	struct completion completion;
-} __attribute__((packed));
-
-/**
- * sclp_chp_read_info - perform read channel-path information sclp command
- * @info: resulting channel-path information data
- *
- * Perform read channel-path information sclp command and wait for completion.
- * On success, store channel-path information in @info and return 0. Return
- * non-zero otherwise.
- */
-int sclp_chp_read_info(struct sclp_chp_info *info)
-{
-	struct chp_info_data *data;
-	int rc;
-
-	if (!SCLP_HAS_CHP_INFO)
-		return -EOPNOTSUPP;
-	/* Prepare sccb. */
-	data = (struct chp_info_data *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
-	if (!data)
-		return -ENOMEM;
-	data->sccb.header.length = sizeof(struct chp_info_sccb);
-	data->req.command = SCLP_CMDW_READ_CHANNEL_PATH_INFORMATION;
-	data->req.sccb = &(data->sccb);
-	data->req.status = SCLP_REQ_FILLED;
-	data->req.callback = chp_callback;
-	data->req.callback_data = &(data->completion);
-	init_completion(&data->completion);
-
-	/* Perform sclp request. */
-	rc = sclp_add_request(&(data->req));
-	if (rc)
-		goto out;
-	wait_for_completion(&data->completion);
-
-	/* Check response .*/
-	if (data->req.status != SCLP_REQ_DONE) {
-		printk(KERN_WARNING TAG "read channel-path info request failed "
-		       "(status=0x%02x)\n", data->req.status);
-		rc = -EIO;
-		goto out;
-	}
-	if (data->sccb.header.response_code != 0x0010) {
-		printk(KERN_WARNING TAG "read channel-path info failed "
-		       "(response=0x%04x)\n", data->sccb.header.response_code);
-		rc = -EIO;
-		goto out;
-	}
-	memcpy(info->recognized, data->sccb.recognized,
-	       SCLP_CHP_INFO_MASK_SIZE);
-	memcpy(info->standby, data->sccb.standby,
-	       SCLP_CHP_INFO_MASK_SIZE);
-	memcpy(info->configured, data->sccb.configured,
-	       SCLP_CHP_INFO_MASK_SIZE);
-out:
-	free_page((unsigned long) data);
-
-	return rc;
-}

+ 398 - 0
drivers/s390/char/sclp_cmd.c

@@ -0,0 +1,398 @@
+/*
+ *  drivers/s390/char/sclp_cmd.c
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>,
+ *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <asm/chpid.h>
+#include <asm/sclp.h>
+#include "sclp.h"
+
+#define TAG	"sclp_cmd: "
+
+#define SCLP_CMDW_READ_SCP_INFO		0x00020001
+#define SCLP_CMDW_READ_SCP_INFO_FORCED	0x00120001
+
+struct read_info_sccb {
+	struct	sccb_header header;	/* 0-7 */
+	u16	rnmax;			/* 8-9 */
+	u8	rnsize;			/* 10 */
+	u8	_reserved0[24 - 11];	/* 11-15 */
+	u8	loadparm[8];		/* 24-31 */
+	u8	_reserved1[48 - 32];	/* 32-47 */
+	u64	facilities;		/* 48-55 */
+	u8	_reserved2[84 - 56];	/* 56-83 */
+	u8	fac84;			/* 84 */
+	u8	_reserved3[91 - 85];	/* 85-90 */
+	u8	flags;			/* 91 */
+	u8	_reserved4[100 - 92];	/* 92-99 */
+	u32	rnsize2;		/* 100-103 */
+	u64	rnmax2;			/* 104-111 */
+	u8	_reserved5[4096 - 112];	/* 112-4095 */
+} __attribute__((packed, aligned(PAGE_SIZE)));
+
+static struct read_info_sccb __initdata early_read_info_sccb;
+static int __initdata early_read_info_sccb_valid;
+
+u64 sclp_facilities;
+static u8 sclp_fac84;
+
+static int __init sclp_cmd_sync_early(sclp_cmdw_t cmd, void *sccb)
+{
+	int rc;
+
+	__ctl_set_bit(0, 9);
+	rc = sclp_service_call(cmd, sccb);
+	if (rc)
+		goto out;
+	__load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
+			PSW_MASK_WAIT | PSW_DEFAULT_KEY);
+	local_irq_disable();
+out:
+	/* Contents of the sccb might have changed. */
+	barrier();
+	__ctl_clear_bit(0, 9);
+	return rc;
+}
+
+void __init sclp_read_info_early(void)
+{
+	int rc;
+	int i;
+	struct read_info_sccb *sccb;
+	sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
+				  SCLP_CMDW_READ_SCP_INFO};
+
+	sccb = &early_read_info_sccb;
+	for (i = 0; i < ARRAY_SIZE(commands); i++) {
+		do {
+			memset(sccb, 0, sizeof(*sccb));
+			sccb->header.length = sizeof(*sccb);
+			sccb->header.control_mask[2] = 0x80;
+			rc = sclp_cmd_sync_early(commands[i], sccb);
+		} while (rc == -EBUSY);
+
+		if (rc)
+			break;
+		if (sccb->header.response_code == 0x10) {
+			early_read_info_sccb_valid = 1;
+			break;
+		}
+		if (sccb->header.response_code != 0x1f0)
+			break;
+	}
+}
+
+void __init sclp_facilities_detect(void)
+{
+	if (!early_read_info_sccb_valid)
+		return;
+	sclp_facilities = early_read_info_sccb.facilities;
+	sclp_fac84 = early_read_info_sccb.fac84;
+}
+
+unsigned long long __init sclp_memory_detect(void)
+{
+	unsigned long long memsize;
+	struct read_info_sccb *sccb;
+
+	if (!early_read_info_sccb_valid)
+		return 0;
+	sccb = &early_read_info_sccb;
+	if (sccb->rnsize)
+		memsize = sccb->rnsize << 20;
+	else
+		memsize = sccb->rnsize2 << 20;
+	if (sccb->rnmax)
+		memsize *= sccb->rnmax;
+	else
+		memsize *= sccb->rnmax2;
+	return memsize;
+}
+
+/*
+ * This function will be called after sclp_memory_detect(), which gets called
+ * early from early.c code. Therefore the sccb should have valid contents.
+ */
+void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
+{
+	struct read_info_sccb *sccb;
+
+	if (!early_read_info_sccb_valid)
+		return;
+	sccb = &early_read_info_sccb;
+	info->is_valid = 1;
+	if (sccb->flags & 0x2)
+		info->has_dump = 1;
+	memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
+}
+
+static void sclp_sync_callback(struct sclp_req *req, void *data)
+{
+	struct completion *completion = data;
+
+	complete(completion);
+}
+
+static int do_sync_request(sclp_cmdw_t cmd, void *sccb)
+{
+	struct completion completion;
+	struct sclp_req *request;
+	int rc;
+
+	request = kzalloc(sizeof(*request), GFP_KERNEL);
+	if (!request)
+		return -ENOMEM;
+	request->command = cmd;
+	request->sccb = sccb;
+	request->status = SCLP_REQ_FILLED;
+	request->callback = sclp_sync_callback;
+	request->callback_data = &completion;
+	init_completion(&completion);
+
+	/* Perform sclp request. */
+	rc = sclp_add_request(request);
+	if (rc)
+		goto out;
+	wait_for_completion(&completion);
+
+	/* Check response. */
+	if (request->status != SCLP_REQ_DONE) {
+		printk(KERN_WARNING TAG "sync request failed "
+		       "(cmd=0x%08x, status=0x%02x)\n", cmd, request->status);
+		rc = -EIO;
+	}
+out:
+	kfree(request);
+	return rc;
+}
+
+/*
+ * CPU configuration related functions.
+ */
+
+#define SCLP_CMDW_READ_CPU_INFO		0x00010001
+#define SCLP_CMDW_CONFIGURE_CPU		0x00110001
+#define SCLP_CMDW_DECONFIGURE_CPU	0x00100001
+
+struct read_cpu_info_sccb {
+	struct	sccb_header header;
+	u16	nr_configured;
+	u16	offset_configured;
+	u16	nr_standby;
+	u16	offset_standby;
+	u8	reserved[4096 - 16];
+} __attribute__((packed, aligned(PAGE_SIZE)));
+
+static void sclp_fill_cpu_info(struct sclp_cpu_info *info,
+			       struct read_cpu_info_sccb *sccb)
+{
+	char *page = (char *) sccb;
+
+	memset(info, 0, sizeof(*info));
+	info->configured = sccb->nr_configured;
+	info->standby = sccb->nr_standby;
+	info->combined = sccb->nr_configured + sccb->nr_standby;
+	info->has_cpu_type = sclp_fac84 & 0x1;
+	memcpy(&info->cpu, page + sccb->offset_configured,
+	       info->combined * sizeof(struct sclp_cpu_entry));
+}
+
+int sclp_get_cpu_info(struct sclp_cpu_info *info)
+{
+	int rc;
+	struct read_cpu_info_sccb *sccb;
+
+	if (!SCLP_HAS_CPU_INFO)
+		return -EOPNOTSUPP;
+	sccb = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sccb)
+		return -ENOMEM;
+	sccb->header.length = sizeof(*sccb);
+	rc = do_sync_request(SCLP_CMDW_READ_CPU_INFO, sccb);
+	if (rc)
+		goto out;
+	if (sccb->header.response_code != 0x0010) {
+		printk(KERN_WARNING TAG "readcpuinfo failed "
+		       "(response=0x%04x)\n", sccb->header.response_code);
+		rc = -EIO;
+		goto out;
+	}
+	sclp_fill_cpu_info(info, sccb);
+out:
+	free_page((unsigned long) sccb);
+	return rc;
+}
+
+struct cpu_configure_sccb {
+	struct sccb_header header;
+} __attribute__((packed, aligned(8)));
+
+static int do_cpu_configure(sclp_cmdw_t cmd)
+{
+	struct cpu_configure_sccb *sccb;
+	int rc;
+
+	if (!SCLP_HAS_CPU_RECONFIG)
+		return -EOPNOTSUPP;
+	/*
+	 * This is not going to cross a page boundary since we force
+	 * kmalloc to have a minimum alignment of 8 bytes on s390.
+	 */
+	sccb = kzalloc(sizeof(*sccb), GFP_KERNEL | GFP_DMA);
+	if (!sccb)
+		return -ENOMEM;
+	sccb->header.length = sizeof(*sccb);
+	rc = do_sync_request(cmd, sccb);
+	if (rc)
+		goto out;
+	switch (sccb->header.response_code) {
+	case 0x0020:
+	case 0x0120:
+		break;
+	default:
+		printk(KERN_WARNING TAG "configure cpu failed (cmd=0x%08x, "
+		       "response=0x%04x)\n", cmd, sccb->header.response_code);
+		rc = -EIO;
+		break;
+	}
+out:
+	kfree(sccb);
+	return rc;
+}
+
+int sclp_cpu_configure(u8 cpu)
+{
+	return do_cpu_configure(SCLP_CMDW_CONFIGURE_CPU | cpu << 8);
+}
+
+int sclp_cpu_deconfigure(u8 cpu)
+{
+	return do_cpu_configure(SCLP_CMDW_DECONFIGURE_CPU | cpu << 8);
+}
+
+/*
+ * Channel path configuration related functions.
+ */
+
+#define SCLP_CMDW_CONFIGURE_CHPATH		0x000f0001
+#define SCLP_CMDW_DECONFIGURE_CHPATH		0x000e0001
+#define SCLP_CMDW_READ_CHPATH_INFORMATION	0x00030001
+
+struct chp_cfg_sccb {
+	struct sccb_header header;
+	u8 ccm;
+	u8 reserved[6];
+	u8 cssid;
+} __attribute__((packed));
+
+static int do_chp_configure(sclp_cmdw_t cmd)
+{
+	struct chp_cfg_sccb *sccb;
+	int rc;
+
+	if (!SCLP_HAS_CHP_RECONFIG)
+		return -EOPNOTSUPP;
+	/* Prepare sccb. */
+	sccb = (struct chp_cfg_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sccb)
+		return -ENOMEM;
+	sccb->header.length = sizeof(*sccb);
+	rc = do_sync_request(cmd, sccb);
+	if (rc)
+		goto out;
+	switch (sccb->header.response_code) {
+	case 0x0020:
+	case 0x0120:
+	case 0x0440:
+	case 0x0450:
+		break;
+	default:
+		printk(KERN_WARNING TAG "configure channel-path failed "
+		       "(cmd=0x%08x, response=0x%04x)\n", cmd,
+		       sccb->header.response_code);
+		rc = -EIO;
+		break;
+	}
+out:
+	free_page((unsigned long) sccb);
+	return rc;
+}
+
+/**
+ * sclp_chp_configure - perform configure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform configure channel-path command sclp command for specified chpid.
+ * Return 0 after command successfully finished, non-zero otherwise.
+ */
+int sclp_chp_configure(struct chp_id chpid)
+{
+	return do_chp_configure(SCLP_CMDW_CONFIGURE_CHPATH | chpid.id << 8);
+}
+
+/**
+ * sclp_chp_deconfigure - perform deconfigure channel-path sclp command
+ * @chpid: channel-path ID
+ *
+ * Perform deconfigure channel-path command sclp command for specified chpid
+ * and wait for completion. On success return 0. Return non-zero otherwise.
+ */
+int sclp_chp_deconfigure(struct chp_id chpid)
+{
+	return do_chp_configure(SCLP_CMDW_DECONFIGURE_CHPATH | chpid.id << 8);
+}
+
+struct chp_info_sccb {
+	struct sccb_header header;
+	u8 recognized[SCLP_CHP_INFO_MASK_SIZE];
+	u8 standby[SCLP_CHP_INFO_MASK_SIZE];
+	u8 configured[SCLP_CHP_INFO_MASK_SIZE];
+	u8 ccm;
+	u8 reserved[6];
+	u8 cssid;
+} __attribute__((packed));
+
+/**
+ * sclp_chp_read_info - perform read channel-path information sclp command
+ * @info: resulting channel-path information data
+ *
+ * Perform read channel-path information sclp command and wait for completion.
+ * On success, store channel-path information in @info and return 0. Return
+ * non-zero otherwise.
+ */
+int sclp_chp_read_info(struct sclp_chp_info *info)
+{
+	struct chp_info_sccb *sccb;
+	int rc;
+
+	if (!SCLP_HAS_CHP_INFO)
+		return -EOPNOTSUPP;
+	/* Prepare sccb. */
+	sccb = (struct chp_info_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sccb)
+		return -ENOMEM;
+	sccb->header.length = sizeof(*sccb);
+	rc = do_sync_request(SCLP_CMDW_READ_CHPATH_INFORMATION, sccb);
+	if (rc)
+		goto out;
+	if (sccb->header.response_code != 0x0010) {
+		printk(KERN_WARNING TAG "read channel-path info failed "
+		       "(response=0x%04x)\n", sccb->header.response_code);
+		rc = -EIO;
+		goto out;
+	}
+	memcpy(info->recognized, sccb->recognized, SCLP_CHP_INFO_MASK_SIZE);
+	memcpy(info->standby, sccb->standby, SCLP_CHP_INFO_MASK_SIZE);
+	memcpy(info->configured, sccb->configured, SCLP_CHP_INFO_MASK_SIZE);
+out:
+	free_page((unsigned long) sccb);
+	return rc;
+}

+ 16 - 230
drivers/s390/char/sclp_cpi.c

@@ -1,255 +1,41 @@
 /*
- * Author: Martin Peschke <mpeschke@de.ibm.com>
- * Copyright (C) 2001 IBM Entwicklung GmbH, IBM Corporation
+ *  drivers/s390/char/sclp_cpi.c
+ *    SCLP control programm identification
  *
- * SCLP Control-Program Identification.
+ *    Copyright IBM Corp. 2001, 2007
+ *    Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ *		 Michael Ernst <mernst@de.ibm.com>
  */
 
-#include <linux/version.h>
 #include <linux/kmod.h>
 #include <linux/module.h>
 #include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/string.h>
-#include <linux/err.h>
-#include <linux/slab.h>
-#include <asm/ebcdic.h>
-#include <asm/semaphore.h>
-
-#include "sclp.h"
-#include "sclp_rw.h"
-
-#define CPI_LENGTH_SYSTEM_TYPE	8
-#define CPI_LENGTH_SYSTEM_NAME	8
-#define CPI_LENGTH_SYSPLEX_NAME	8
-
-struct cpi_evbuf {
-	struct evbuf_header header;
-	u8	id_format;
-	u8	reserved0;
-	u8	system_type[CPI_LENGTH_SYSTEM_TYPE];
-	u64	reserved1;
-	u8	system_name[CPI_LENGTH_SYSTEM_NAME];
-	u64	reserved2;
-	u64	system_level;
-	u64	reserved3;
-	u8	sysplex_name[CPI_LENGTH_SYSPLEX_NAME];
-	u8	reserved4[16];
-} __attribute__((packed));
-
-struct cpi_sccb {
-	struct sccb_header header;
-	struct cpi_evbuf cpi_evbuf;
-} __attribute__((packed));
-
-/* Event type structure for write message and write priority message */
-static struct sclp_register sclp_cpi_event =
-{
-	.send_mask = EVTYP_CTLPROGIDENT_MASK
-};
+#include <linux/version.h>
+#include "sclp_cpi_sys.h"
 
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Identify this operating system instance "
+		   "to the System z hardware");
+MODULE_AUTHOR("Martin Peschke <mpeschke@de.ibm.com>, "
+	      "Michael Ernst <mernst@de.ibm.com>");
 
-MODULE_AUTHOR(
-	"Martin Peschke, IBM Deutschland Entwicklung GmbH "
-	"<mpeschke@de.ibm.com>");
-
-MODULE_DESCRIPTION(
-	"identify this operating system instance to the S/390 "
-	"or zSeries hardware");
+static char *system_name = "";
+static char *sysplex_name = "";
 
-static char *system_name = NULL;
 module_param(system_name, charp, 0);
 MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");
-
-static char *sysplex_name = NULL;
-#ifdef ALLOW_SYSPLEX_NAME
 module_param(sysplex_name, charp, 0);
 MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
-#endif
-
-/* use default value for this field (as well as for system level) */
-static char *system_type = "LINUX";
 
-static int
-cpi_check_parms(void)
+static int __init cpi_module_init(void)
 {
-	/* reject if no system type specified */
-	if (!system_type) {
-		printk("cpi: bug: no system type specified\n");
-		return -EINVAL;
-	}
-
-	/* reject if system type larger than 8 characters */
-	if (strlen(system_type) > CPI_LENGTH_SYSTEM_NAME) {
-		printk("cpi: bug: system type has length of %li characters - "
-		       "only %i characters supported\n",
-		       strlen(system_type), CPI_LENGTH_SYSTEM_TYPE);
-		return -EINVAL;
-	}
-
-	/* reject if no system name specified */
-	if (!system_name) {
-		printk("cpi: no system name specified\n");
-		return -EINVAL;
-	}
-
-	/* reject if system name larger than 8 characters */
-	if (strlen(system_name) > CPI_LENGTH_SYSTEM_NAME) {
-		printk("cpi: system name has length of %li characters - "
-		       "only %i characters supported\n",
-		       strlen(system_name), CPI_LENGTH_SYSTEM_NAME);
-		return -EINVAL;
-	}
-
-	/* reject if specified sysplex name larger than 8 characters */
-	if (sysplex_name && strlen(sysplex_name) > CPI_LENGTH_SYSPLEX_NAME) {
-		printk("cpi: sysplex name has length of %li characters"
-		       " - only %i characters supported\n",
-		       strlen(sysplex_name), CPI_LENGTH_SYSPLEX_NAME);
-		return -EINVAL;
-	}
-	return 0;
+	return sclp_cpi_set_data(system_name, sysplex_name, "LINUX",
+				 LINUX_VERSION_CODE);
 }
 
-static void
-cpi_callback(struct sclp_req *req, void *data)
-{
-	struct semaphore *sem;
-
-	sem = (struct semaphore *) data;
-	up(sem);
-}
-
-static struct sclp_req *
-cpi_prepare_req(void)
-{
-	struct sclp_req *req;
-	struct cpi_sccb *sccb;
-	struct cpi_evbuf *evb;
-
-	req = kmalloc(sizeof(struct sclp_req), GFP_KERNEL);
-	if (req == NULL)
-		return ERR_PTR(-ENOMEM);
-	sccb = (struct cpi_sccb *) __get_free_page(GFP_KERNEL | GFP_DMA);
-	if (sccb == NULL) {
-		kfree(req);
-		return ERR_PTR(-ENOMEM);
-	}
-	memset(sccb, 0, sizeof(struct cpi_sccb));
-
-	/* setup SCCB for Control-Program Identification */
-	sccb->header.length = sizeof(struct cpi_sccb);
-	sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
-	sccb->cpi_evbuf.header.type = 0x0B;
-	evb = &sccb->cpi_evbuf;
-
-	/* set system type */
-	memset(evb->system_type, ' ', CPI_LENGTH_SYSTEM_TYPE);
-	memcpy(evb->system_type, system_type, strlen(system_type));
-	sclp_ascebc_str(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
-	EBC_TOUPPER(evb->system_type, CPI_LENGTH_SYSTEM_TYPE);
-
-	/* set system name */
-	memset(evb->system_name, ' ', CPI_LENGTH_SYSTEM_NAME);
-	memcpy(evb->system_name, system_name, strlen(system_name));
-	sclp_ascebc_str(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
-	EBC_TOUPPER(evb->system_name, CPI_LENGTH_SYSTEM_NAME);
-
-	/* set system level */
-	evb->system_level = LINUX_VERSION_CODE;
-
-	/* set sysplex name */
-	if (sysplex_name) {
-		memset(evb->sysplex_name, ' ', CPI_LENGTH_SYSPLEX_NAME);
-		memcpy(evb->sysplex_name, sysplex_name, strlen(sysplex_name));
-		sclp_ascebc_str(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
-		EBC_TOUPPER(evb->sysplex_name, CPI_LENGTH_SYSPLEX_NAME);
-	}
-
-	/* prepare request data structure presented to SCLP driver */
-	req->command = SCLP_CMDW_WRITE_EVENT_DATA;
-	req->sccb = sccb;
-	req->status = SCLP_REQ_FILLED;
-	req->callback = cpi_callback;
-	return req;
-}
-
-static void
-cpi_free_req(struct sclp_req *req)
-{
-	free_page((unsigned long) req->sccb);
-	kfree(req);
-}
-
-static int __init
-cpi_module_init(void)
-{
-	struct semaphore sem;
-	struct sclp_req *req;
-	int rc;
-
-	rc = cpi_check_parms();
-	if (rc)
-		return rc;
-
-	rc = sclp_register(&sclp_cpi_event);
-	if (rc) {
-		/* could not register sclp event. Die. */
-		printk(KERN_WARNING "cpi: could not register to hardware "
-		       "console.\n");
-		return -EINVAL;
-	}
-	if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) {
-		printk(KERN_WARNING "cpi: no control program identification "
-		       "support\n");
-		sclp_unregister(&sclp_cpi_event);
-		return -EOPNOTSUPP;
-	}
-
-	req = cpi_prepare_req();
-	if (IS_ERR(req)) {
-		printk(KERN_WARNING "cpi: couldn't allocate request\n");
-		sclp_unregister(&sclp_cpi_event);
-		return PTR_ERR(req);
-	}
-
-	/* Prepare semaphore */
-	sema_init(&sem, 0);
-	req->callback_data = &sem;
-	/* Add request to sclp queue */
-	rc = sclp_add_request(req);
-	if (rc) {
-		printk(KERN_WARNING "cpi: could not start request\n");
-		cpi_free_req(req);
-		sclp_unregister(&sclp_cpi_event);
-		return rc;
-	}
-	/* make "insmod" sleep until callback arrives */
-	down(&sem);
-
-	rc = ((struct cpi_sccb *) req->sccb)->header.response_code;
-	if (rc != 0x0020) {
-		printk(KERN_WARNING "cpi: failed with response code 0x%x\n",
-		       rc);
-		rc = -ECOMM;
-	} else
-		rc = 0;
-
-	cpi_free_req(req);
-	sclp_unregister(&sclp_cpi_event);
-
-	return rc;
-}
-
-
 static void __exit cpi_module_exit(void)
 {
 }
 
-
-/* declare driver module init/cleanup functions */
 module_init(cpi_module_init);
 module_exit(cpi_module_exit);
-

+ 400 - 0
drivers/s390/char/sclp_cpi_sys.c

@@ -0,0 +1,400 @@
+/*
+ *  drivers/s390/char/sclp_cpi_sys.c
+ *    SCLP control program identification sysfs interface
+ *
+ *    Copyright IBM Corp. 2001, 2007
+ *    Author(s): Martin Peschke <mpeschke@de.ibm.com>
+ *		 Michael Ernst <mernst@de.ibm.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/stat.h>
+#include <linux/device.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+#include <linux/kmod.h>
+#include <linux/timer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/completion.h>
+#include <asm/ebcdic.h>
+#include <asm/sclp.h>
+#include "sclp.h"
+#include "sclp_rw.h"
+#include "sclp_cpi_sys.h"
+
+#define CPI_LENGTH_NAME 8
+#define CPI_LENGTH_LEVEL 16
+
+struct cpi_evbuf {
+	struct evbuf_header header;
+	u8	id_format;
+	u8	reserved0;
+	u8	system_type[CPI_LENGTH_NAME];
+	u64	reserved1;
+	u8	system_name[CPI_LENGTH_NAME];
+	u64	reserved2;
+	u64	system_level;
+	u64	reserved3;
+	u8	sysplex_name[CPI_LENGTH_NAME];
+	u8	reserved4[16];
+} __attribute__((packed));
+
+struct cpi_sccb {
+	struct sccb_header header;
+	struct cpi_evbuf cpi_evbuf;
+} __attribute__((packed));
+
+static struct sclp_register sclp_cpi_event = {
+	.send_mask = EVTYP_CTLPROGIDENT_MASK,
+};
+
+static char system_name[CPI_LENGTH_NAME + 1];
+static char sysplex_name[CPI_LENGTH_NAME + 1];
+static char system_type[CPI_LENGTH_NAME + 1];
+static u64 system_level;
+
+static void set_data(char *field, char *data)
+{
+	memset(field, ' ', CPI_LENGTH_NAME);
+	memcpy(field, data, strlen(data));
+	sclp_ascebc_str(field, CPI_LENGTH_NAME);
+}
+
+static void cpi_callback(struct sclp_req *req, void *data)
+{
+	struct completion *completion = data;
+
+	complete(completion);
+}
+
+static struct sclp_req *cpi_prepare_req(void)
+{
+	struct sclp_req *req;
+	struct cpi_sccb *sccb;
+	struct cpi_evbuf *evb;
+
+	req = kzalloc(sizeof(struct sclp_req), GFP_KERNEL);
+	if (!req)
+		return ERR_PTR(-ENOMEM);
+	sccb = (struct cpi_sccb *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
+	if (!sccb) {
+		kfree(req);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	/* setup SCCB for Control-Program Identification */
+	sccb->header.length = sizeof(struct cpi_sccb);
+	sccb->cpi_evbuf.header.length = sizeof(struct cpi_evbuf);
+	sccb->cpi_evbuf.header.type = 0x0b;
+	evb = &sccb->cpi_evbuf;
+
+	/* set system type */
+	set_data(evb->system_type, system_type);
+
+	/* set system name */
+	set_data(evb->system_name, system_name);
+
+	/* set sytem level */
+	evb->system_level = system_level;
+
+	/* set sysplex name */
+	set_data(evb->sysplex_name, sysplex_name);
+
+	/* prepare request data structure presented to SCLP driver */
+	req->command = SCLP_CMDW_WRITE_EVENT_DATA;
+	req->sccb = sccb;
+	req->status = SCLP_REQ_FILLED;
+	req->callback = cpi_callback;
+	return req;
+}
+
+static void cpi_free_req(struct sclp_req *req)
+{
+	free_page((unsigned long) req->sccb);
+	kfree(req);
+}
+
+static int cpi_req(void)
+{
+	struct completion completion;
+	struct sclp_req *req;
+	int rc;
+	int response;
+
+	rc = sclp_register(&sclp_cpi_event);
+	if (rc) {
+		printk(KERN_WARNING "cpi: could not register "
+			"to hardware console.\n");
+		goto out;
+	}
+	if (!(sclp_cpi_event.sclp_send_mask & EVTYP_CTLPROGIDENT_MASK)) {
+		printk(KERN_WARNING "cpi: no control program "
+			"identification support\n");
+		rc = -EOPNOTSUPP;
+		goto out_unregister;
+	}
+
+	req = cpi_prepare_req();
+	if (IS_ERR(req)) {
+		printk(KERN_WARNING "cpi: could not allocate request\n");
+		rc = PTR_ERR(req);
+		goto out_unregister;
+	}
+
+	init_completion(&completion);
+	req->callback_data = &completion;
+
+	/* Add request to sclp queue */
+	rc = sclp_add_request(req);
+	if (rc) {
+		printk(KERN_WARNING "cpi: could not start request\n");
+		goto out_free_req;
+	}
+
+	wait_for_completion(&completion);
+
+	if (req->status != SCLP_REQ_DONE) {
+		printk(KERN_WARNING "cpi: request failed (status=0x%02x)\n",
+			req->status);
+		rc = -EIO;
+		goto out_free_req;
+	}
+
+	response = ((struct cpi_sccb *) req->sccb)->header.response_code;
+	if (response != 0x0020) {
+		printk(KERN_WARNING "cpi: failed with "
+			"response code 0x%x\n", response);
+		rc = -EIO;
+	}
+
+out_free_req:
+	cpi_free_req(req);
+
+out_unregister:
+	sclp_unregister(&sclp_cpi_event);
+
+out:
+	return rc;
+}
+
+static int check_string(const char *attr, const char *str)
+{
+	size_t len;
+	size_t i;
+
+	len = strlen(str);
+
+	if ((len > 0) && (str[len - 1] == '\n'))
+		len--;
+
+	if (len > CPI_LENGTH_NAME)
+		return -EINVAL;
+
+	for (i = 0; i < len ; i++) {
+		if (isalpha(str[i]) || isdigit(str[i]) ||
+		    strchr("$@# ", str[i]))
+			continue;
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void set_string(char *attr, const char *value)
+{
+	size_t len;
+	size_t i;
+
+	len = strlen(value);
+
+	if ((len > 0) && (value[len - 1] == '\n'))
+		len--;
+
+	for (i = 0; i < CPI_LENGTH_NAME; i++) {
+		if (i < len)
+			attr[i] = toupper(value[i]);
+		else
+			attr[i] = ' ';
+	}
+}
+
+static ssize_t system_name_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%s\n", system_name);
+}
+
+static ssize_t system_name_store(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 const char *buf,
+	size_t len)
+{
+	int rc;
+
+	rc = check_string("system_name", buf);
+	if (rc)
+		return rc;
+
+	set_string(system_name, buf);
+
+	return len;
+}
+
+static struct kobj_attribute system_name_attr =
+	__ATTR(system_name, 0644, system_name_show, system_name_store);
+
+static ssize_t sysplex_name_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%s\n", sysplex_name);
+}
+
+static ssize_t sysplex_name_store(struct kobject *kobj,
+				  struct kobj_attribute *attr,
+				  const char *buf,
+	size_t len)
+{
+	int rc;
+
+	rc = check_string("sysplex_name", buf);
+	if (rc)
+		return rc;
+
+	set_string(sysplex_name, buf);
+
+	return len;
+}
+
+static struct kobj_attribute sysplex_name_attr =
+	__ATTR(sysplex_name, 0644, sysplex_name_show, sysplex_name_store);
+
+static ssize_t system_type_show(struct kobject *kobj,
+				struct kobj_attribute *attr, char *page)
+{
+	return snprintf(page, PAGE_SIZE, "%s\n", system_type);
+}
+
+static ssize_t system_type_store(struct kobject *kobj,
+				 struct kobj_attribute *attr,
+				 const char *buf,
+	size_t len)
+{
+	int rc;
+
+	rc = check_string("system_type", buf);
+	if (rc)
+		return rc;
+
+	set_string(system_type, buf);
+
+	return len;
+}
+
+static struct kobj_attribute system_type_attr =
+	__ATTR(system_type, 0644, system_type_show, system_type_store);
+
+static ssize_t system_level_show(struct kobject *kobj,
+				 struct kobj_attribute *attr, char *page)
+{
+	unsigned long long level = system_level;
+
+	return snprintf(page, PAGE_SIZE, "%#018llx\n", level);
+}
+
+static ssize_t system_level_store(struct kobject *kobj,
+				  struct kobj_attribute *attr,
+				  const char *buf,
+	size_t len)
+{
+	unsigned long long level;
+	char *endp;
+
+	level = simple_strtoull(buf, &endp, 16);
+
+	if (endp == buf)
+		return -EINVAL;
+	if (*endp == '\n')
+		endp++;
+	if (*endp)
+		return -EINVAL;
+
+	system_level = level;
+
+	return len;
+}
+
+static struct kobj_attribute system_level_attr =
+	__ATTR(system_level, 0644, system_level_show, system_level_store);
+
+static ssize_t set_store(struct kobject *kobj,
+			 struct kobj_attribute *attr,
+			 const char *buf, size_t len)
+{
+	int rc;
+
+	rc = cpi_req();
+	if (rc)
+		return rc;
+
+	return len;
+}
+
+static struct kobj_attribute set_attr = __ATTR(set, 0200, NULL, set_store);
+
+static struct attribute *cpi_attrs[] = {
+	&system_name_attr.attr,
+	&sysplex_name_attr.attr,
+	&system_type_attr.attr,
+	&system_level_attr.attr,
+	&set_attr.attr,
+	NULL,
+};
+
+static struct attribute_group cpi_attr_group = {
+	.attrs = cpi_attrs,
+};
+
+static struct kset *cpi_kset;
+
+int sclp_cpi_set_data(const char *system, const char *sysplex, const char *type,
+		      const u64 level)
+{
+	int rc;
+
+	rc = check_string("system_name", system);
+	if (rc)
+		return rc;
+	rc = check_string("sysplex_name", sysplex);
+	if (rc)
+		return rc;
+	rc = check_string("system_type", type);
+	if (rc)
+		return rc;
+
+	set_string(system_name, system);
+	set_string(sysplex_name, sysplex);
+	set_string(system_type, type);
+	system_level = level;
+
+	return cpi_req();
+}
+EXPORT_SYMBOL(sclp_cpi_set_data);
+
+static int __init cpi_init(void)
+{
+	int rc;
+
+	cpi_kset = kset_create_and_add("cpi", NULL, firmware_kobj);
+	if (!cpi_kset)
+		return -ENOMEM;
+
+	rc = sysfs_create_group(&cpi_kset->kobj, &cpi_attr_group);
+	if (rc)
+		kset_unregister(cpi_kset);
+
+	return rc;
+}
+
+__initcall(cpi_init);

+ 15 - 0
drivers/s390/char/sclp_cpi_sys.h

@@ -0,0 +1,15 @@
+/*
+ *  drivers/s390/char/sclp_cpi_sys.h
+ *    SCLP control program identification sysfs interface
+ *
+ *    Copyright IBM Corp. 2007
+ *    Author(s): Michael Ernst <mernst@de.ibm.com>
+ */
+
+#ifndef __SCLP_CPI_SYS_H__
+#define __SCLP_CPI_SYS_H__
+
+int sclp_cpi_set_data(const char *system, const char *sysplex,
+		      const char *type, u64 level);
+
+#endif	 /* __SCLP_CPI_SYS_H__ */

+ 0 - 116
drivers/s390/char/sclp_info.c

@@ -1,116 +0,0 @@
-/*
- *  drivers/s390/char/sclp_info.c
- *
- *    Copyright IBM Corp. 2007
- *    Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
- */
-
-#include <linux/init.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <asm/sclp.h>
-#include "sclp.h"
-
-struct sclp_readinfo_sccb {
-	struct	sccb_header header;	/* 0-7 */
-	u16	rnmax;			/* 8-9 */
-	u8	rnsize;			/* 10 */
-	u8	_reserved0[24 - 11];	/* 11-23 */
-	u8	loadparm[8];		/* 24-31 */
-	u8	_reserved1[48 - 32];	/* 32-47 */
-	u64	facilities;		/* 48-55 */
-	u8	_reserved2[91 - 56];	/* 56-90 */
-	u8	flags;			/* 91 */
-	u8	_reserved3[100 - 92];	/* 92-99 */
-	u32	rnsize2;		/* 100-103 */
-	u64	rnmax2;			/* 104-111 */
-	u8	_reserved4[4096 - 112];	/* 112-4095 */
-} __attribute__((packed, aligned(4096)));
-
-static struct sclp_readinfo_sccb __initdata early_readinfo_sccb;
-static int __initdata early_readinfo_sccb_valid;
-
-u64 sclp_facilities;
-
-void __init sclp_readinfo_early(void)
-{
-	int ret;
-	int i;
-	struct sclp_readinfo_sccb *sccb;
-	sclp_cmdw_t commands[] = {SCLP_CMDW_READ_SCP_INFO_FORCED,
-				  SCLP_CMDW_READ_SCP_INFO};
-
-	/* Enable service signal subclass mask. */
-	__ctl_set_bit(0, 9);
-	sccb = &early_readinfo_sccb;
-	for (i = 0; i < ARRAY_SIZE(commands); i++) {
-		do {
-			memset(sccb, 0, sizeof(*sccb));
-			sccb->header.length = sizeof(*sccb);
-			sccb->header.control_mask[2] = 0x80;
-			ret = sclp_service_call(commands[i], sccb);
-		} while (ret == -EBUSY);
-
-		if (ret)
-			break;
-		__load_psw_mask(PSW_BASE_BITS | PSW_MASK_EXT |
-				PSW_MASK_WAIT | PSW_DEFAULT_KEY);
-		local_irq_disable();
-		/*
-		 * Contents of the sccb might have changed
-		 * therefore a barrier is needed.
-		 */
-		barrier();
-		if (sccb->header.response_code == 0x10) {
-			early_readinfo_sccb_valid = 1;
-			break;
-		}
-		if (sccb->header.response_code != 0x1f0)
-			break;
-	}
-	/* Disable service signal subclass mask again. */
-	__ctl_clear_bit(0, 9);
-}
-
-void __init sclp_facilities_detect(void)
-{
-	if (!early_readinfo_sccb_valid)
-		return;
-	sclp_facilities = early_readinfo_sccb.facilities;
-}
-
-unsigned long long __init sclp_memory_detect(void)
-{
-	unsigned long long memsize;
-	struct sclp_readinfo_sccb *sccb;
-
-	if (!early_readinfo_sccb_valid)
-		return 0;
-	sccb = &early_readinfo_sccb;
-	if (sccb->rnsize)
-		memsize = sccb->rnsize << 20;
-	else
-		memsize = sccb->rnsize2 << 20;
-	if (sccb->rnmax)
-		memsize *= sccb->rnmax;
-	else
-		memsize *= sccb->rnmax2;
-	return memsize;
-}
-
-/*
- * This function will be called after sclp_memory_detect(), which gets called
- * early from early.c code. Therefore the sccb should have valid contents.
- */
-void __init sclp_get_ipl_info(struct sclp_ipl_info *info)
-{
-	struct sclp_readinfo_sccb *sccb;
-
-	if (!early_readinfo_sccb_valid)
-		return;
-	sccb = &early_readinfo_sccb;
-	info->is_valid = 1;
-	if (sccb->flags & 0x2)
-		info->has_dump = 1;
-	memcpy(&info->loadparm, &sccb->loadparm, LOADPARM_LEN);
-}

+ 1 - 1
drivers/s390/char/sclp_rw.c

@@ -76,7 +76,7 @@ sclp_make_buffer(void *page, unsigned short columns, unsigned short htab)
 }
 
 /*
- * Return a pointer to the orignal page that has been used to create
+ * Return a pointer to the original page that has been used to create
  * the buffer.
  */
 void *

+ 1 - 1
drivers/s390/char/tape_3590.c

@@ -1495,7 +1495,7 @@ tape_3590_unit_check(struct tape_device *device, struct tape_request *request,
 			   device->cdev->dev.bus_id);
 		return tape_3590_erp_basic(device, request, irb, -EPERM);
 	case 0x8013:
-		PRINT_WARN("(%s): Another host has priviliged access to the "
+		PRINT_WARN("(%s): Another host has privileged access to the "
 			   "tape device\n", device->cdev->dev.bus_id);
 		PRINT_WARN("(%s): To solve the problem unload the current "
 			   "cartridge!\n", device->cdev->dev.bus_id);

+ 1 - 1
drivers/s390/char/tape_core.c

@@ -37,7 +37,7 @@ static void tape_long_busy_timeout(unsigned long data);
  * we can assign the devices to minor numbers of the same major
  * The list is protected by the rwlock
  */
-static struct list_head tape_device_list = LIST_HEAD_INIT(tape_device_list);
+static LIST_HEAD(tape_device_list);
 static DEFINE_RWLOCK(tape_device_lock);
 
 /*

+ 1 - 1
drivers/s390/char/tape_proc.c

@@ -97,7 +97,7 @@ static void tape_proc_stop(struct seq_file *m, void *v)
 {
 }
 
-static struct seq_operations tape_proc_seq = {
+static const struct seq_operations tape_proc_seq = {
 	.start		= tape_proc_start,
 	.next		= tape_proc_next,
 	.stop		= tape_proc_stop,

+ 1 - 1
drivers/s390/char/vmlogrdr.c

@@ -683,7 +683,7 @@ static int vmlogrdr_register_driver(void)
 	/* Register with iucv driver */
 	ret = iucv_register(&vmlogrdr_iucv_handler, 1);
 	if (ret) {
-		printk (KERN_ERR "vmlogrdr: failed to register with"
+		printk (KERN_ERR "vmlogrdr: failed to register with "
 			"iucv driver\n");
 		goto out;
 	}

+ 1 - 1
drivers/s390/char/vmur.c

@@ -759,7 +759,7 @@ static loff_t ur_llseek(struct file *file, loff_t offset, int whence)
 	return newpos;
 }
 
-static struct file_operations ur_fops = {
+static const struct file_operations ur_fops = {
 	.owner	 = THIS_MODULE,
 	.open	 = ur_open,
 	.release = ur_release,

+ 1 - 1
drivers/s390/char/zcore.c

@@ -470,7 +470,7 @@ static loff_t zcore_lseek(struct file *file, loff_t offset, int orig)
 	return rc;
 }
 
-static struct file_operations zcore_fops = {
+static const struct file_operations zcore_fops = {
 	.owner		= THIS_MODULE,
 	.llseek		= zcore_lseek,
 	.read		= zcore_read,

+ 115 - 56
drivers/s390/cio/airq.c

@@ -1,12 +1,12 @@
 /*
  *  drivers/s390/cio/airq.c
- *   S/390 common I/O routines -- support for adapter interruptions
+ *    Support for adapter interruptions
  *
- *    Copyright (C) 1999-2002 IBM Deutschland Entwicklung GmbH,
- *			      IBM Corporation
- *    Author(s): Ingo Adlung (adlung@de.ibm.com)
- *		 Cornelia Huck (cornelia.huck@de.ibm.com)
- *		 Arnd Bergmann (arndb@de.ibm.com)
+ *    Copyright IBM Corp. 1999,2007
+ *    Author(s): Ingo Adlung <adlung@de.ibm.com>
+ *		 Cornelia Huck <cornelia.huck@de.ibm.com>
+ *		 Arnd Bergmann <arndb@de.ibm.com>
+ *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
  */
 
 #include <linux/init.h>
@@ -14,72 +14,131 @@
 #include <linux/slab.h>
 #include <linux/rcupdate.h>
 
+#include <asm/airq.h>
+
+#include "cio.h"
 #include "cio_debug.h"
-#include "airq.h"
 
-static adapter_int_handler_t adapter_handler;
+#define NR_AIRQS		32
+#define NR_AIRQS_PER_WORD	sizeof(unsigned long)
+#define NR_AIRQ_WORDS		(NR_AIRQS / NR_AIRQS_PER_WORD)
 
-/*
- * register for adapter interrupts
- *
- * With HiperSockets the zSeries architecture provides for
- *  means of adapter interrups, pseudo I/O interrupts that are
- *  not tied to an I/O subchannel, but to an adapter. However,
- *  it doesn't disclose the info how to enable/disable them, but
- *  to recognize them only. Perhaps we should consider them
- *  being shared interrupts, and thus build a linked list
- *  of adapter handlers ... to be evaluated ...
- */
-int
-s390_register_adapter_interrupt (adapter_int_handler_t handler)
-{
-	int ret;
-	char dbf_txt[15];
+union indicator_t {
+	unsigned long word[NR_AIRQ_WORDS];
+	unsigned char byte[NR_AIRQS];
+} __attribute__((packed));
 
-	CIO_TRACE_EVENT (4, "rgaint");
+struct airq_t {
+	adapter_int_handler_t handler;
+	void *drv_data;
+};
 
-	if (handler == NULL)
-		ret = -EINVAL;
-	else
-		ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0);
-	if (!ret)
-		synchronize_sched();  /* Allow interrupts to complete. */
+static union indicator_t indicators;
+static struct airq_t *airqs[NR_AIRQS];
 
-	sprintf (dbf_txt, "ret:%d", ret);
-	CIO_TRACE_EVENT (4, dbf_txt);
+static int register_airq(struct airq_t *airq)
+{
+	int i;
 
-	return ret;
+	for (i = 0; i < NR_AIRQS; i++)
+		if (!cmpxchg(&airqs[i], NULL, airq))
+			return i;
+	return -ENOMEM;
 }
 
-int
-s390_unregister_adapter_interrupt (adapter_int_handler_t handler)
+/**
+ * s390_register_adapter_interrupt() - register adapter interrupt handler
+ * @handler: adapter handler to be registered
+ * @drv_data: driver data passed with each call to the handler
+ *
+ * Returns:
+ *  Pointer to the indicator to be used on success
+ *  ERR_PTR() if registration failed
+ */
+void *s390_register_adapter_interrupt(adapter_int_handler_t handler,
+				      void *drv_data)
 {
+	struct airq_t *airq;
+	char dbf_txt[16];
 	int ret;
-	char dbf_txt[15];
 
-	CIO_TRACE_EVENT (4, "urgaint");
-
-	if (handler == NULL)
-		ret = -EINVAL;
-	else {
-		adapter_handler = NULL;
-		synchronize_sched();  /* Allow interrupts to complete. */
-		ret = 0;
+	airq = kmalloc(sizeof(struct airq_t), GFP_KERNEL);
+	if (!airq) {
+		ret = -ENOMEM;
+		goto out;
 	}
-	sprintf (dbf_txt, "ret:%d", ret);
-	CIO_TRACE_EVENT (4, dbf_txt);
-
-	return ret;
+	airq->handler = handler;
+	airq->drv_data = drv_data;
+	ret = register_airq(airq);
+	if (ret < 0)
+		kfree(airq);
+out:
+	snprintf(dbf_txt, sizeof(dbf_txt), "rairq:%d", ret);
+	CIO_TRACE_EVENT(4, dbf_txt);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	else
+		return &indicators.byte[ret];
 }
+EXPORT_SYMBOL(s390_register_adapter_interrupt);
 
-void
-do_adapter_IO (void)
+/**
+ * s390_unregister_adapter_interrupt - unregister adapter interrupt handler
+ * @ind: indicator for which the handler is to be unregistered
+ */
+void s390_unregister_adapter_interrupt(void *ind)
 {
-	CIO_TRACE_EVENT (6, "doaio");
+	struct airq_t *airq;
+	char dbf_txt[16];
+	int i;
 
-	if (adapter_handler)
-		(*adapter_handler) ();
+	i = (int) ((addr_t) ind) - ((addr_t) &indicators.byte[0]);
+	snprintf(dbf_txt, sizeof(dbf_txt), "urairq:%d", i);
+	CIO_TRACE_EVENT(4, dbf_txt);
+	indicators.byte[i] = 0;
+	airq = xchg(&airqs[i], NULL);
+	/*
+	 * Allow interrupts to complete. This will ensure that the airq handle
+	 * is no longer referenced by any interrupt handler.
+	 */
+	synchronize_sched();
+	kfree(airq);
 }
+EXPORT_SYMBOL(s390_unregister_adapter_interrupt);
+
+#define INDICATOR_MASK	(0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
 
-EXPORT_SYMBOL (s390_register_adapter_interrupt);
-EXPORT_SYMBOL (s390_unregister_adapter_interrupt);
+void do_adapter_IO(void)
+{
+	int w;
+	int i;
+	unsigned long word;
+	struct airq_t *airq;
+
+	/*
+	 * Access indicator array in word-sized chunks to minimize storage
+	 * fetch operations.
+	 */
+	for (w = 0; w < NR_AIRQ_WORDS; w++) {
+		word = indicators.word[w];
+		i = w * NR_AIRQS_PER_WORD;
+		/*
+		 * Check bytes within word for active indicators.
+		 */
+		while (word) {
+			if (word & INDICATOR_MASK) {
+				airq = airqs[i];
+				if (likely(airq))
+					airq->handler(&indicators.byte[i],
+						      airq->drv_data);
+				else
+					/*
+					 * Reset ill-behaved indicator.
+					 */
+					indicators.byte[i] = 0;
+			}
+			word <<= 8;
+			i++;
+		}
+	}
+}

+ 0 - 10
drivers/s390/cio/airq.h

@@ -1,10 +0,0 @@
-#ifndef S390_AINTERRUPT_H
-#define S390_AINTERRUPT_H
-
-typedef	int (*adapter_int_handler_t)(void);
-
-extern int s390_register_adapter_interrupt(adapter_int_handler_t handler);
-extern int s390_unregister_adapter_interrupt(adapter_int_handler_t handler);
-extern void do_adapter_IO (void);
-
-#endif

+ 1 - 1
drivers/s390/cio/blacklist.c

@@ -348,7 +348,7 @@ cio_ignore_write(struct file *file, const char __user *user_buf,
 	return user_len;
 }
 
-static struct seq_operations cio_ignore_proc_seq_ops = {
+static const struct seq_operations cio_ignore_proc_seq_ops = {
 	.start = cio_ignore_proc_seq_start,
 	.stop  = cio_ignore_proc_seq_stop,
 	.next  = cio_ignore_proc_seq_next,

+ 14 - 11
drivers/s390/cio/ccwgroup.c

@@ -35,8 +35,8 @@ ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
 	struct ccwgroup_device *gdev;
 	struct ccwgroup_driver *gdrv;
 
-	gdev = container_of(dev, struct ccwgroup_device, dev);
-	gdrv = container_of(drv, struct ccwgroup_driver, driver);
+	gdev = to_ccwgroupdev(dev);
+	gdrv = to_ccwgroupdrv(drv);
 
 	if (gdev->creator_id == gdrv->driver_id)
 		return 1;
@@ -75,8 +75,10 @@ static void ccwgroup_ungroup_callback(struct device *dev)
 	struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
 
 	mutex_lock(&gdev->reg_mutex);
-	__ccwgroup_remove_symlinks(gdev);
-	device_unregister(dev);
+	if (device_is_registered(&gdev->dev)) {
+		__ccwgroup_remove_symlinks(gdev);
+		device_unregister(dev);
+	}
 	mutex_unlock(&gdev->reg_mutex);
 }
 
@@ -111,7 +113,7 @@ ccwgroup_release (struct device *dev)
 	gdev = to_ccwgroupdev(dev);
 
 	for (i = 0; i < gdev->count; i++) {
-		gdev->cdev[i]->dev.driver_data = NULL;
+		dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
 		put_device(&gdev->cdev[i]->dev);
 	}
 	kfree(gdev);
@@ -196,11 +198,11 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
 			goto error;
 		}
 		/* Don't allow a device to belong to more than one group. */
-		if (gdev->cdev[i]->dev.driver_data) {
+		if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
 			rc = -EINVAL;
 			goto error;
 		}
-		gdev->cdev[i]->dev.driver_data = gdev;
+		dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
 	}
 
 	gdev->creator_id = creator_id;
@@ -234,8 +236,8 @@ int ccwgroup_create(struct device *root, unsigned int creator_id,
 error:
 	for (i = 0; i < argc; i++)
 		if (gdev->cdev[i]) {
-			if (gdev->cdev[i]->dev.driver_data == gdev)
-				gdev->cdev[i]->dev.driver_data = NULL;
+			if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
+				dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
 			put_device(&gdev->cdev[i]->dev);
 		}
 	mutex_unlock(&gdev->reg_mutex);
@@ -408,6 +410,7 @@ int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
 	/* register our new driver with the core */
 	cdriver->driver.bus = &ccwgroup_bus_type;
 	cdriver->driver.name = cdriver->name;
+	cdriver->driver.owner = cdriver->owner;
 
 	return driver_register(&cdriver->driver);
 }
@@ -463,8 +466,8 @@ __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
 {
 	struct ccwgroup_device *gdev;
 
-	if (cdev->dev.driver_data) {
-		gdev = (struct ccwgroup_device *)cdev->dev.driver_data;
+	gdev = dev_get_drvdata(&cdev->dev);
+	if (gdev) {
 		if (get_device(&gdev->dev)) {
 			mutex_lock(&gdev->reg_mutex);
 			if (device_is_registered(&gdev->dev))

+ 44 - 77
drivers/s390/cio/chsc.c

@@ -89,7 +89,8 @@ int chsc_get_ssd_info(struct subchannel_id schid, struct chsc_ssd_info *ssd)
 	/* Copy data */
 	ret = 0;
 	memset(ssd, 0, sizeof(struct chsc_ssd_info));
-	if ((ssd_area->st != 0) && (ssd_area->st != 2))
+	if ((ssd_area->st != SUBCHANNEL_TYPE_IO) &&
+	    (ssd_area->st != SUBCHANNEL_TYPE_MSG))
 		goto out_free;
 	ssd->path_mask = ssd_area->path_mask;
 	ssd->fla_valid_mask = ssd_area->fla_valid_mask;
@@ -132,20 +133,16 @@ static void terminate_internal_io(struct subchannel *sch)
 	device_set_intretry(sch);
 	/* Call handler. */
 	if (sch->driver && sch->driver->termination)
-		sch->driver->termination(&sch->dev);
+		sch->driver->termination(sch);
 }
 
-static int
-s390_subchannel_remove_chpid(struct device *dev, void *data)
+static int s390_subchannel_remove_chpid(struct subchannel *sch, void *data)
 {
 	int j;
 	int mask;
-	struct subchannel *sch;
-	struct chp_id *chpid;
+	struct chp_id *chpid = data;
 	struct schib schib;
 
-	sch = to_subchannel(dev);
-	chpid = data;
 	for (j = 0; j < 8; j++) {
 		mask = 0x80 >> j;
 		if ((sch->schib.pmcw.pim & mask) &&
@@ -158,7 +155,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
 	spin_lock_irq(sch->lock);
 
 	stsch(sch->schid, &schib);
-	if (!schib.pmcw.dnv)
+	if (!css_sch_is_valid(&schib))
 		goto out_unreg;
 	memcpy(&sch->schib, &schib, sizeof(struct schib));
 	/* Check for single path devices. */
@@ -172,12 +169,12 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
 			terminate_internal_io(sch);
 			/* Re-start path verification. */
 			if (sch->driver && sch->driver->verify)
-				sch->driver->verify(&sch->dev);
+				sch->driver->verify(sch);
 		}
 	} else {
 		/* trigger path verification. */
 		if (sch->driver && sch->driver->verify)
-			sch->driver->verify(&sch->dev);
+			sch->driver->verify(sch);
 		else if (sch->lpm == mask)
 			goto out_unreg;
 	}
@@ -201,12 +198,10 @@ void chsc_chp_offline(struct chp_id chpid)
 
 	if (chp_get_status(chpid) <= 0)
 		return;
-	bus_for_each_dev(&css_bus_type, NULL, &chpid,
-			 s390_subchannel_remove_chpid);
+	for_each_subchannel_staged(s390_subchannel_remove_chpid, NULL, &chpid);
 }
 
-static int
-s390_process_res_acc_new_sch(struct subchannel_id schid)
+static int s390_process_res_acc_new_sch(struct subchannel_id schid, void *data)
 {
 	struct schib schib;
 	/*
@@ -252,18 +247,10 @@ static int get_res_chpid_mask(struct chsc_ssd_info *ssd,
 	return 0;
 }
 
-static int
-__s390_process_res_acc(struct subchannel_id schid, void *data)
+static int __s390_process_res_acc(struct subchannel *sch, void *data)
 {
 	int chp_mask, old_lpm;
-	struct res_acc_data *res_data;
-	struct subchannel *sch;
-
-	res_data = data;
-	sch = get_subchannel_by_schid(schid);
-	if (!sch)
-		/* Check if a subchannel is newly available. */
-		return s390_process_res_acc_new_sch(schid);
+	struct res_acc_data *res_data = data;
 
 	spin_lock_irq(sch->lock);
 	chp_mask = get_res_chpid_mask(&sch->ssd_info, res_data);
@@ -279,10 +266,10 @@ __s390_process_res_acc(struct subchannel_id schid, void *data)
 	if (!old_lpm && sch->lpm)
 		device_trigger_reprobe(sch);
 	else if (sch->driver && sch->driver->verify)
-		sch->driver->verify(&sch->dev);
+		sch->driver->verify(sch);
 out:
 	spin_unlock_irq(sch->lock);
-	put_device(&sch->dev);
+
 	return 0;
 }
 
@@ -305,7 +292,8 @@ static void s390_process_res_acc (struct res_acc_data *res_data)
 	 * The more information we have (info), the less scanning
 	 * will we have to do.
 	 */
-	for_each_subchannel(__s390_process_res_acc, res_data);
+	for_each_subchannel_staged(__s390_process_res_acc,
+				   s390_process_res_acc_new_sch, res_data);
 }
 
 static int
@@ -499,8 +487,7 @@ void chsc_process_crw(void)
 	} while (sei_area->flags & 0x80);
 }
 
-static int
-__chp_add_new_sch(struct subchannel_id schid)
+static int __chp_add_new_sch(struct subchannel_id schid, void *data)
 {
 	struct schib schib;
 
@@ -514,45 +501,37 @@ __chp_add_new_sch(struct subchannel_id schid)
 }
 
 
-static int
-__chp_add(struct subchannel_id schid, void *data)
+static int __chp_add(struct subchannel *sch, void *data)
 {
 	int i, mask;
-	struct chp_id *chpid;
-	struct subchannel *sch;
-
-	chpid = data;
-	sch = get_subchannel_by_schid(schid);
-	if (!sch)
-		/* Check if the subchannel is now available. */
-		return __chp_add_new_sch(schid);
+	struct chp_id *chpid = data;
+
 	spin_lock_irq(sch->lock);
 	for (i=0; i<8; i++) {
 		mask = 0x80 >> i;
 		if ((sch->schib.pmcw.pim & mask) &&
-		    (sch->schib.pmcw.chpid[i] == chpid->id)) {
-			if (stsch(sch->schid, &sch->schib) != 0) {
-				/* Endgame. */
-				spin_unlock_irq(sch->lock);
-				return -ENXIO;
-			}
+		    (sch->schib.pmcw.chpid[i] == chpid->id))
 			break;
-		}
 	}
 	if (i==8) {
 		spin_unlock_irq(sch->lock);
 		return 0;
 	}
+	if (stsch(sch->schid, &sch->schib)) {
+		spin_unlock_irq(sch->lock);
+		css_schedule_eval(sch->schid);
+		return 0;
+	}
 	sch->lpm = ((sch->schib.pmcw.pim &
 		     sch->schib.pmcw.pam &
 		     sch->schib.pmcw.pom)
 		    | mask) & sch->opm;
 
 	if (sch->driver && sch->driver->verify)
-		sch->driver->verify(&sch->dev);
+		sch->driver->verify(sch);
 
 	spin_unlock_irq(sch->lock);
-	put_device(&sch->dev);
+
 	return 0;
 }
 
@@ -564,7 +543,8 @@ void chsc_chp_online(struct chp_id chpid)
 	CIO_TRACE_EVENT(2, dbf_txt);
 
 	if (chp_get_status(chpid) != 0)
-		for_each_subchannel(__chp_add, &chpid);
+		for_each_subchannel_staged(__chp_add, __chp_add_new_sch,
+					   &chpid);
 }
 
 static void __s390_subchannel_vary_chpid(struct subchannel *sch,
@@ -589,7 +569,7 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch,
 			if (!old_lpm)
 				device_trigger_reprobe(sch);
 			else if (sch->driver && sch->driver->verify)
-				sch->driver->verify(&sch->dev);
+				sch->driver->verify(sch);
 			break;
 		}
 		sch->opm &= ~mask;
@@ -603,37 +583,29 @@ static void __s390_subchannel_vary_chpid(struct subchannel *sch,
 				terminate_internal_io(sch);
 				/* Re-start path verification. */
 				if (sch->driver && sch->driver->verify)
-					sch->driver->verify(&sch->dev);
+					sch->driver->verify(sch);
 			}
 		} else if (!sch->lpm) {
 			if (device_trigger_verify(sch) != 0)
 				css_schedule_eval(sch->schid);
 		} else if (sch->driver && sch->driver->verify)
-			sch->driver->verify(&sch->dev);
+			sch->driver->verify(sch);
 		break;
 	}
 	spin_unlock_irqrestore(sch->lock, flags);
 }
 
-static int s390_subchannel_vary_chpid_off(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_off(struct subchannel *sch, void *data)
 {
-	struct subchannel *sch;
-	struct chp_id *chpid;
-
-	sch = to_subchannel(dev);
-	chpid = data;
+	struct chp_id *chpid = data;
 
 	__s390_subchannel_vary_chpid(sch, *chpid, 0);
 	return 0;
 }
 
-static int s390_subchannel_vary_chpid_on(struct device *dev, void *data)
+static int s390_subchannel_vary_chpid_on(struct subchannel *sch, void *data)
 {
-	struct subchannel *sch;
-	struct chp_id *chpid;
-
-	sch = to_subchannel(dev);
-	chpid = data;
+	struct chp_id *chpid = data;
 
 	__s390_subchannel_vary_chpid(sch, *chpid, 1);
 	return 0;
@@ -643,13 +615,7 @@ static int
 __s390_vary_chpid_on(struct subchannel_id schid, void *data)
 {
 	struct schib schib;
-	struct subchannel *sch;
 
-	sch = get_subchannel_by_schid(schid);
-	if (sch) {
-		put_device(&sch->dev);
-		return 0;
-	}
 	if (stsch_err(schid, &schib))
 		/* We're through */
 		return -ENXIO;
@@ -669,12 +635,13 @@ int chsc_chp_vary(struct chp_id chpid, int on)
 	 * Redo PathVerification on the devices the chpid connects to
 	 */
 
-	bus_for_each_dev(&css_bus_type, NULL, &chpid, on ?
-			 s390_subchannel_vary_chpid_on :
-			 s390_subchannel_vary_chpid_off);
 	if (on)
-		/* Scan for new devices on varied on path. */
-		for_each_subchannel(__s390_vary_chpid_on, NULL);
+		for_each_subchannel_staged(s390_subchannel_vary_chpid_on,
+					   __s390_vary_chpid_on, &chpid);
+	else
+		for_each_subchannel_staged(s390_subchannel_vary_chpid_off,
+					   NULL, &chpid);
+
 	return 0;
 }
 
@@ -1075,7 +1042,7 @@ chsc_determine_css_characteristics(void)
 
 	scsc_area = (void *)get_zeroed_page(GFP_KERNEL | GFP_DMA);
 	if (!scsc_area) {
-		CIO_MSG_EVENT(0, "Was not able to determine available"
+		CIO_MSG_EVENT(0, "Was not able to determine available "
 			      "CHSCs due to no memory.\n");
 		return -ENOMEM;
 	}

+ 58 - 48
drivers/s390/cio/cio.c

@@ -23,11 +23,12 @@
 #include <asm/reset.h>
 #include <asm/ipl.h>
 #include <asm/chpid.h>
-#include "airq.h"
+#include <asm/airq.h>
 #include "cio.h"
 #include "css.h"
 #include "chsc.h"
 #include "ioasm.h"
+#include "io_sch.h"
 #include "blacklist.h"
 #include "cio_debug.h"
 #include "chp.h"
@@ -56,39 +57,37 @@ __setup ("cio_msg=", cio_setup);
 
 /*
  * Function: cio_debug_init
- * Initializes three debug logs (under /proc/s390dbf) for common I/O:
- * - cio_msg logs the messages which are printk'ed when CONFIG_DEBUG_IO is on
+ * Initializes three debug logs for common I/O:
+ * - cio_msg logs generic cio messages
  * - cio_trace logs the calling of different functions
- * - cio_crw logs the messages which are printk'ed when CONFIG_DEBUG_CRW is on
- * debug levels depend on CONFIG_DEBUG_IO resp. CONFIG_DEBUG_CRW
+ * - cio_crw logs machine check related cio messages
  */
-static int __init
-cio_debug_init (void)
+static int __init cio_debug_init(void)
 {
-	cio_debug_msg_id = debug_register ("cio_msg", 16, 4, 16*sizeof (long));
+	cio_debug_msg_id = debug_register("cio_msg", 16, 1, 16 * sizeof(long));
 	if (!cio_debug_msg_id)
 		goto out_unregister;
-	debug_register_view (cio_debug_msg_id, &debug_sprintf_view);
-	debug_set_level (cio_debug_msg_id, 2);
-	cio_debug_trace_id = debug_register ("cio_trace", 16, 4, 16);
+	debug_register_view(cio_debug_msg_id, &debug_sprintf_view);
+	debug_set_level(cio_debug_msg_id, 2);
+	cio_debug_trace_id = debug_register("cio_trace", 16, 1, 16);
 	if (!cio_debug_trace_id)
 		goto out_unregister;
-	debug_register_view (cio_debug_trace_id, &debug_hex_ascii_view);
-	debug_set_level (cio_debug_trace_id, 2);
-	cio_debug_crw_id = debug_register ("cio_crw", 4, 4, 16*sizeof (long));
+	debug_register_view(cio_debug_trace_id, &debug_hex_ascii_view);
+	debug_set_level(cio_debug_trace_id, 2);
+	cio_debug_crw_id = debug_register("cio_crw", 16, 1, 16 * sizeof(long));
 	if (!cio_debug_crw_id)
 		goto out_unregister;
-	debug_register_view (cio_debug_crw_id, &debug_sprintf_view);
-	debug_set_level (cio_debug_crw_id, 2);
+	debug_register_view(cio_debug_crw_id, &debug_sprintf_view);
+	debug_set_level(cio_debug_crw_id, 4);
 	return 0;
 
 out_unregister:
 	if (cio_debug_msg_id)
-		debug_unregister (cio_debug_msg_id);
+		debug_unregister(cio_debug_msg_id);
 	if (cio_debug_trace_id)
-		debug_unregister (cio_debug_trace_id);
+		debug_unregister(cio_debug_trace_id);
 	if (cio_debug_crw_id)
-		debug_unregister (cio_debug_crw_id);
+		debug_unregister(cio_debug_crw_id);
 	printk(KERN_WARNING"cio: could not initialize debugging\n");
 	return -1;
 }
@@ -147,7 +146,7 @@ cio_tpi(void)
 	spin_lock(sch->lock);
 	memcpy (&sch->schib.scsw, &irb->scsw, sizeof (struct scsw));
 	if (sch->driver && sch->driver->irq)
-		sch->driver->irq(&sch->dev);
+		sch->driver->irq(sch);
 	spin_unlock(sch->lock);
 	irq_exit ();
 	_local_bh_enable();
@@ -184,33 +183,35 @@ cio_start_key (struct subchannel *sch,	/* subchannel structure */
 {
 	char dbf_txt[15];
 	int ccode;
+	struct orb *orb;
 
-	CIO_TRACE_EVENT (4, "stIO");
-	CIO_TRACE_EVENT (4, sch->dev.bus_id);
+	CIO_TRACE_EVENT(4, "stIO");
+	CIO_TRACE_EVENT(4, sch->dev.bus_id);
 
+	orb = &to_io_private(sch)->orb;
 	/* sch is always under 2G. */
-	sch->orb.intparm = (__u32)(unsigned long)sch;
-	sch->orb.fmt = 1;
+	orb->intparm = (u32)(addr_t)sch;
+	orb->fmt = 1;
 
-	sch->orb.pfch = sch->options.prefetch == 0;
-	sch->orb.spnd = sch->options.suspend;
-	sch->orb.ssic = sch->options.suspend && sch->options.inter;
-	sch->orb.lpm = (lpm != 0) ? lpm : sch->lpm;
+	orb->pfch = sch->options.prefetch == 0;
+	orb->spnd = sch->options.suspend;
+	orb->ssic = sch->options.suspend && sch->options.inter;
+	orb->lpm = (lpm != 0) ? lpm : sch->lpm;
 #ifdef CONFIG_64BIT
 	/*
 	 * for 64 bit we always support 64 bit IDAWs with 4k page size only
 	 */
-	sch->orb.c64 = 1;
-	sch->orb.i2k = 0;
+	orb->c64 = 1;
+	orb->i2k = 0;
 #endif
-	sch->orb.key = key >> 4;
+	orb->key = key >> 4;
 	/* issue "Start Subchannel" */
-	sch->orb.cpa = (__u32) __pa (cpa);
-	ccode = ssch (sch->schid, &sch->orb);
+	orb->cpa = (__u32) __pa(cpa);
+	ccode = ssch(sch->schid, orb);
 
 	/* process condition code */
-	sprintf (dbf_txt, "ccode:%d", ccode);
-	CIO_TRACE_EVENT (4, dbf_txt);
+	sprintf(dbf_txt, "ccode:%d", ccode);
+	CIO_TRACE_EVENT(4, dbf_txt);
 
 	switch (ccode) {
 	case 0:
@@ -405,8 +406,8 @@ cio_modify (struct subchannel *sch)
 /*
  * Enable subchannel.
  */
-int
-cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
+int cio_enable_subchannel(struct subchannel *sch, unsigned int isc,
+			  u32 intparm)
 {
 	char dbf_txt[15];
 	int ccode;
@@ -425,7 +426,7 @@ cio_enable_subchannel (struct subchannel *sch, unsigned int isc)
 	for (retry = 5, ret = 0; retry > 0; retry--) {
 		sch->schib.pmcw.ena = 1;
 		sch->schib.pmcw.isc = isc;
-		sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
+		sch->schib.pmcw.intparm = intparm;
 		ret = cio_modify(sch);
 		if (ret == -ENODEV)
 			break;
@@ -567,7 +568,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
 	 */
 	if (sch->st != 0) {
 		CIO_DEBUG(KERN_INFO, 0,
-			  "cio: Subchannel 0.%x.%04x reports "
+			  "Subchannel 0.%x.%04x reports "
 			  "non-I/O subchannel type %04X\n",
 			  sch->schid.ssid, sch->schid.sch_no, sch->st);
 		/* We stop here for non-io subchannels. */
@@ -576,11 +577,11 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
 	}
 
 	/* Initialization for io subchannels. */
-	if (!sch->schib.pmcw.dnv) {
-		/* io subchannel but device number is invalid. */
+	if (!css_sch_is_valid(&sch->schib)) {
 		err = -ENODEV;
 		goto out;
 	}
+
 	/* Devno is valid. */
 	if (is_blacklisted (sch->schid.ssid, sch->schib.pmcw.dev)) {
 		/*
@@ -600,7 +601,7 @@ cio_validate_subchannel (struct subchannel *sch, struct subchannel_id schid)
 	sch->lpm = sch->schib.pmcw.pam & sch->opm;
 
 	CIO_DEBUG(KERN_INFO, 0,
-		  "cio: Detected device %04x on subchannel 0.%x.%04X"
+		  "Detected device %04x on subchannel 0.%x.%04X"
 		  " - PIM = %02X, PAM = %02X, POM = %02X\n",
 		  sch->schib.pmcw.dev, sch->schid.ssid,
 		  sch->schid.sch_no, sch->schib.pmcw.pim,
@@ -680,7 +681,7 @@ do_IRQ (struct pt_regs *regs)
 				sizeof (irb->scsw));
 			/* Call interrupt handler if there is one. */
 			if (sch->driver && sch->driver->irq)
-				sch->driver->irq(&sch->dev);
+				sch->driver->irq(sch);
 		}
 		if (sch)
 			spin_unlock(sch->lock);
@@ -698,8 +699,14 @@ do_IRQ (struct pt_regs *regs)
 
 #ifdef CONFIG_CCW_CONSOLE
 static struct subchannel console_subchannel;
+static struct io_subchannel_private console_priv;
 static int console_subchannel_in_use;
 
+void *cio_get_console_priv(void)
+{
+	return &console_priv;
+}
+
 /*
  * busy wait for the next interrupt on the console
  */
@@ -738,9 +745,9 @@ cio_test_for_console(struct subchannel_id schid, void *data)
 {
 	if (stsch_err(schid, &console_subchannel.schib) != 0)
 		return -ENXIO;
-	if (console_subchannel.schib.pmcw.dnv &&
-	    console_subchannel.schib.pmcw.dev ==
-	    console_devno) {
+	if ((console_subchannel.schib.pmcw.st == SUBCHANNEL_TYPE_IO) &&
+	    console_subchannel.schib.pmcw.dnv &&
+	    (console_subchannel.schib.pmcw.dev == console_devno)) {
 		console_irq = schid.sch_no;
 		return 1; /* found */
 	}
@@ -758,6 +765,7 @@ cio_get_console_sch_no(void)
 		/* VM provided us with the irq number of the console. */
 		schid.sch_no = console_irq;
 		if (stsch(schid, &console_subchannel.schib) != 0 ||
+		    (console_subchannel.schib.pmcw.st != SUBCHANNEL_TYPE_IO) ||
 		    !console_subchannel.schib.pmcw.dnv)
 			return -1;
 		console_devno = console_subchannel.schib.pmcw.dev;
@@ -804,7 +812,7 @@ cio_probe_console(void)
 	ctl_set_bit(6, 24);
 	console_subchannel.schib.pmcw.isc = 7;
 	console_subchannel.schib.pmcw.intparm =
-		(__u32)(unsigned long)&console_subchannel;
+		(u32)(addr_t)&console_subchannel;
 	ret = cio_modify(&console_subchannel);
 	if (ret) {
 		console_subchannel_in_use = 0;
@@ -1022,7 +1030,7 @@ static int __reipl_subchannel_match(struct subchannel_id schid, void *data)
 
 	if (stsch_reset(schid, &schib))
 		return -ENXIO;
-	if (schib.pmcw.dnv &&
+	if ((schib.pmcw.st == SUBCHANNEL_TYPE_IO) && schib.pmcw.dnv &&
 	    (schib.pmcw.dev == match_id->devid.devno) &&
 	    (schid.ssid == match_id->devid.ssid)) {
 		match_id->schid = schid;
@@ -1068,6 +1076,8 @@ int __init cio_get_iplinfo(struct cio_iplinfo *iplinfo)
 		return -ENODEV;
 	if (stsch(schid, &schib))
 		return -ENODEV;
+	if (schib.pmcw.st != SUBCHANNEL_TYPE_IO)
+		return -ENODEV;
 	if (!schib.pmcw.dnv)
 		return -ENODEV;
 	iplinfo->devno = schib.pmcw.dev;

+ 33 - 56
drivers/s390/cio/cio.h

@@ -11,32 +11,32 @@
  * path management control word
  */
 struct pmcw {
-	__u32 intparm;		/* interruption parameter */
-	__u32 qf   : 1;		/* qdio facility */
-	__u32 res0 : 1;		/* reserved zeros */
-	__u32 isc  : 3;		/* interruption sublass */
-	__u32 res5 : 3;		/* reserved zeros */
-	__u32 ena  : 1;		/* enabled */
-	__u32 lm   : 2;		/* limit mode */
-	__u32 mme  : 2;		/* measurement-mode enable */
-	__u32 mp   : 1;		/* multipath mode */
-	__u32 tf   : 1;		/* timing facility */
-	__u32 dnv  : 1;		/* device number valid */
-	__u32 dev  : 16;	/* device number */
-	__u8  lpm;		/* logical path mask */
-	__u8  pnom;		/* path not operational mask */
-	__u8  lpum;		/* last path used mask */
-	__u8  pim;		/* path installed mask */
-	__u16 mbi;		/* measurement-block index */
-	__u8  pom;		/* path operational mask */
-	__u8  pam;		/* path available mask */
-	__u8  chpid[8];		/* CHPID 0-7 (if available) */
-	__u32 unused1 : 8;	/* reserved zeros */
-	__u32 st      : 3;	/* subchannel type */
-	__u32 unused2 : 18;	/* reserved zeros */
-	__u32 mbfc    : 1;      /* measurement block format control */
-	__u32 xmwme   : 1;      /* extended measurement word mode enable */
-	__u32 csense  : 1;	/* concurrent sense; can be enabled ...*/
+	u32 intparm;		/* interruption parameter */
+	u32 qf	 : 1;		/* qdio facility */
+	u32 res0 : 1;		/* reserved zeros */
+	u32 isc  : 3;		/* interruption sublass */
+	u32 res5 : 3;		/* reserved zeros */
+	u32 ena  : 1;		/* enabled */
+	u32 lm	 : 2;		/* limit mode */
+	u32 mme  : 2;		/* measurement-mode enable */
+	u32 mp	 : 1;		/* multipath mode */
+	u32 tf	 : 1;		/* timing facility */
+	u32 dnv  : 1;		/* device number valid */
+	u32 dev  : 16;		/* device number */
+	u8  lpm;		/* logical path mask */
+	u8  pnom;		/* path not operational mask */
+	u8  lpum;		/* last path used mask */
+	u8  pim;		/* path installed mask */
+	u16 mbi;		/* measurement-block index */
+	u8  pom;		/* path operational mask */
+	u8  pam;		/* path available mask */
+	u8  chpid[8];		/* CHPID 0-7 (if available) */
+	u32 unused1 : 8;	/* reserved zeros */
+	u32 st	    : 3;	/* subchannel type */
+	u32 unused2 : 18;	/* reserved zeros */
+	u32 mbfc    : 1;	/* measurement block format control */
+	u32 xmwme   : 1;	/* extended measurement word mode enable */
+	u32 csense  : 1;	/* concurrent sense; can be enabled ...*/
 				/*  ... per MSCH, however, if facility */
 				/*  ... is not installed, this results */
 				/*  ... in an operand exception.       */
@@ -52,31 +52,6 @@ struct schib {
 	__u8 mda[4];		 /* model dependent area */
 } __attribute__ ((packed,aligned(4)));
 
-/*
- * operation request block
- */
-struct orb {
-	__u32 intparm;		/* interruption parameter */
-	__u32 key  : 4; 	/* flags, like key, suspend control, etc. */
-	__u32 spnd : 1; 	/* suspend control */
-	__u32 res1 : 1; 	/* reserved */
-	__u32 mod  : 1; 	/* modification control */
-	__u32 sync : 1; 	/* synchronize control */
-	__u32 fmt  : 1; 	/* format control */
-	__u32 pfch : 1; 	/* prefetch control */
-	__u32 isic : 1; 	/* initial-status-interruption control */
-	__u32 alcc : 1; 	/* address-limit-checking control */
-	__u32 ssic : 1; 	/* suppress-suspended-interr. control */
-	__u32 res2 : 1; 	/* reserved */
-	__u32 c64  : 1; 	/* IDAW/QDIO 64 bit control  */
-	__u32 i2k  : 1; 	/* IDAW 2/4kB block size control */
-	__u32 lpm  : 8; 	/* logical path mask */
-	__u32 ils  : 1; 	/* incorrect length */
-	__u32 zero : 6; 	/* reserved zeros */
-	__u32 orbx : 1; 	/* ORB extension control */
-	__u32 cpa;		/* channel program address */
-}  __attribute__ ((packed,aligned(4)));
-
 /* subchannel data structure used by I/O subroutines */
 struct subchannel {
 	struct subchannel_id schid;
@@ -85,7 +60,7 @@ struct subchannel {
 	enum {
 		SUBCHANNEL_TYPE_IO = 0,
 		SUBCHANNEL_TYPE_CHSC = 1,
-		SUBCHANNEL_TYPE_MESSAGE = 2,
+		SUBCHANNEL_TYPE_MSG = 2,
 		SUBCHANNEL_TYPE_ADM = 3,
 	} st;			/* subchannel type */
 
@@ -99,11 +74,10 @@ struct subchannel {
 	__u8 lpm;		/* logical path mask */
 	__u8 opm;               /* operational path mask */
 	struct schib schib;	/* subchannel information block */
-	struct orb orb;		/* operation request block */
-	struct ccw1 sense_ccw;	/* static ccw for sense command */
 	struct chsc_ssd_info ssd_info;	/* subchannel description */
 	struct device dev;	/* entry in device tree */
 	struct css_driver *driver;
+	void *private; /* private per subchannel type data */
 } __attribute__ ((aligned(8)));
 
 #define IO_INTERRUPT_TYPE	   0 /* I/O interrupt type */
@@ -111,7 +85,7 @@ struct subchannel {
 #define to_subchannel(n) container_of(n, struct subchannel, dev)
 
 extern int cio_validate_subchannel (struct subchannel *, struct subchannel_id);
-extern int cio_enable_subchannel (struct subchannel *, unsigned int);
+extern int cio_enable_subchannel(struct subchannel *, unsigned int, u32);
 extern int cio_disable_subchannel (struct subchannel *);
 extern int cio_cancel (struct subchannel *);
 extern int cio_clear (struct subchannel *);
@@ -125,6 +99,7 @@ extern int cio_get_options (struct subchannel *);
 extern int cio_modify (struct subchannel *);
 
 int cio_create_sch_lock(struct subchannel *);
+void do_adapter_IO(void);
 
 /* Use with care. */
 #ifdef CONFIG_CCW_CONSOLE
@@ -133,10 +108,12 @@ extern void cio_release_console(void);
 extern int cio_is_console(struct subchannel_id);
 extern struct subchannel *cio_get_console_subchannel(void);
 extern spinlock_t * cio_get_console_lock(void);
+extern void *cio_get_console_priv(void);
 #else
 #define cio_is_console(schid) 0
 #define cio_get_console_subchannel() NULL
-#define cio_get_console_lock() NULL;
+#define cio_get_console_lock() NULL
+#define cio_get_console_priv() NULL
 #endif
 
 extern int cio_show_msg;

+ 11 - 11
drivers/s390/cio/cio_debug.h

@@ -8,20 +8,19 @@ extern debug_info_t *cio_debug_msg_id;
 extern debug_info_t *cio_debug_trace_id;
 extern debug_info_t *cio_debug_crw_id;
 
-#define CIO_TRACE_EVENT(imp, txt) do { \
+#define CIO_TRACE_EVENT(imp, txt) do {				\
 		debug_text_event(cio_debug_trace_id, imp, txt); \
 	} while (0)
 
-#define CIO_MSG_EVENT(imp, args...) do { \
-		debug_sprintf_event(cio_debug_msg_id, imp , ##args); \
+#define CIO_MSG_EVENT(imp, args...) do {				\
+		debug_sprintf_event(cio_debug_msg_id, imp , ##args);	\
 	} while (0)
 
-#define CIO_CRW_EVENT(imp, args...) do { \
-		debug_sprintf_event(cio_debug_crw_id, imp , ##args); \
+#define CIO_CRW_EVENT(imp, args...) do {				\
+		debug_sprintf_event(cio_debug_crw_id, imp , ##args);	\
 	} while (0)
 
-static inline void
-CIO_HEX_EVENT(int level, void *data, int length)
+static inline void CIO_HEX_EVENT(int level, void *data, int length)
 {
 	if (unlikely(!cio_debug_trace_id))
 		return;
@@ -32,9 +31,10 @@ CIO_HEX_EVENT(int level, void *data, int length)
 	}
 }
 
-#define CIO_DEBUG(printk_level,event_level,msg...) ({ \
-	if (cio_show_msg) printk(printk_level msg); \
-	CIO_MSG_EVENT (event_level, msg); \
-})
+#define CIO_DEBUG(printk_level, event_level, msg...) do {	\
+		if (cio_show_msg)				\
+			printk(printk_level "cio: " msg);	\
+		CIO_MSG_EVENT(event_level, msg);		\
+	} while (0)
 
 #endif

+ 164 - 34
drivers/s390/cio/css.c

@@ -51,6 +51,62 @@ for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *data)
 	return ret;
 }
 
+struct cb_data {
+	void *data;
+	struct idset *set;
+	int (*fn_known_sch)(struct subchannel *, void *);
+	int (*fn_unknown_sch)(struct subchannel_id, void *);
+};
+
+static int call_fn_known_sch(struct device *dev, void *data)
+{
+	struct subchannel *sch = to_subchannel(dev);
+	struct cb_data *cb = data;
+	int rc = 0;
+
+	idset_sch_del(cb->set, sch->schid);
+	if (cb->fn_known_sch)
+		rc = cb->fn_known_sch(sch, cb->data);
+	return rc;
+}
+
+static int call_fn_unknown_sch(struct subchannel_id schid, void *data)
+{
+	struct cb_data *cb = data;
+	int rc = 0;
+
+	if (idset_sch_contains(cb->set, schid))
+		rc = cb->fn_unknown_sch(schid, cb->data);
+	return rc;
+}
+
+int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
+			       int (*fn_unknown)(struct subchannel_id,
+			       void *), void *data)
+{
+	struct cb_data cb;
+	int rc;
+
+	cb.set = idset_sch_new();
+	if (!cb.set)
+		return -ENOMEM;
+	idset_fill(cb.set);
+	cb.data = data;
+	cb.fn_known_sch = fn_known;
+	cb.fn_unknown_sch = fn_unknown;
+	/* Process registered subchannels. */
+	rc = bus_for_each_dev(&css_bus_type, NULL, &cb, call_fn_known_sch);
+	if (rc)
+		goto out;
+	/* Process unregistered subchannels. */
+	if (fn_unknown)
+		rc = for_each_subchannel(call_fn_unknown_sch, &cb);
+out:
+	idset_free(cb.set);
+
+	return rc;
+}
+
 static struct subchannel *
 css_alloc_subchannel(struct subchannel_id schid)
 {
@@ -77,7 +133,7 @@ css_alloc_subchannel(struct subchannel_id schid)
 	 * This is fine even on 64bit since the subchannel is always located
 	 * under 2G.
 	 */
-	sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
+	sch->schib.pmcw.intparm = (u32)(addr_t)sch;
 	ret = cio_modify(sch);
 	if (ret) {
 		kfree(sch->lock);
@@ -237,11 +293,25 @@ get_subchannel_by_schid(struct subchannel_id schid)
 	return dev ? to_subchannel(dev) : NULL;
 }
 
+/**
+ * css_sch_is_valid() - check if a subchannel is valid
+ * @schib: subchannel information block for the subchannel
+ */
+int css_sch_is_valid(struct schib *schib)
+{
+	if ((schib->pmcw.st == SUBCHANNEL_TYPE_IO) && !schib->pmcw.dnv)
+		return 0;
+	return 1;
+}
+EXPORT_SYMBOL_GPL(css_sch_is_valid);
+
 static int css_get_subchannel_status(struct subchannel *sch)
 {
 	struct schib schib;
 
-	if (stsch(sch->schid, &schib) || !schib.pmcw.dnv)
+	if (stsch(sch->schid, &schib))
+		return CIO_GONE;
+	if (!css_sch_is_valid(&schib))
 		return CIO_GONE;
 	if (sch->schib.pmcw.dnv && (schib.pmcw.dev != sch->schib.pmcw.dev))
 		return CIO_REVALIDATE;
@@ -293,7 +363,7 @@ static int css_evaluate_known_subchannel(struct subchannel *sch, int slow)
 		action = UNREGISTER;
 		if (sch->driver && sch->driver->notify) {
 			spin_unlock_irqrestore(sch->lock, flags);
-			ret = sch->driver->notify(&sch->dev, event);
+			ret = sch->driver->notify(sch, event);
 			spin_lock_irqsave(sch->lock, flags);
 			if (ret)
 				action = NONE;
@@ -349,7 +419,7 @@ static int css_evaluate_new_subchannel(struct subchannel_id schid, int slow)
 		/* Will be done on the slow path. */
 		return -EAGAIN;
 	}
-	if (stsch_err(schid, &schib) || !schib.pmcw.dnv) {
+	if (stsch_err(schid, &schib) || !css_sch_is_valid(&schib)) {
 		/* Unusable - ignore. */
 		return 0;
 	}
@@ -388,20 +458,56 @@ static int __init slow_subchannel_init(void)
 	return 0;
 }
 
-static void css_slow_path_func(struct work_struct *unused)
+static int slow_eval_known_fn(struct subchannel *sch, void *data)
 {
-	struct subchannel_id schid;
+	int eval;
+	int rc;
 
-	CIO_TRACE_EVENT(4, "slowpath");
 	spin_lock_irq(&slow_subchannel_lock);
-	init_subchannel_id(&schid);
-	while (idset_sch_get_first(slow_subchannel_set, &schid)) {
-		idset_sch_del(slow_subchannel_set, schid);
-		spin_unlock_irq(&slow_subchannel_lock);
-		css_evaluate_subchannel(schid, 1);
-		spin_lock_irq(&slow_subchannel_lock);
+	eval = idset_sch_contains(slow_subchannel_set, sch->schid);
+	idset_sch_del(slow_subchannel_set, sch->schid);
+	spin_unlock_irq(&slow_subchannel_lock);
+	if (eval) {
+		rc = css_evaluate_known_subchannel(sch, 1);
+		if (rc == -EAGAIN)
+			css_schedule_eval(sch->schid);
 	}
+	return 0;
+}
+
+static int slow_eval_unknown_fn(struct subchannel_id schid, void *data)
+{
+	int eval;
+	int rc = 0;
+
+	spin_lock_irq(&slow_subchannel_lock);
+	eval = idset_sch_contains(slow_subchannel_set, schid);
+	idset_sch_del(slow_subchannel_set, schid);
 	spin_unlock_irq(&slow_subchannel_lock);
+	if (eval) {
+		rc = css_evaluate_new_subchannel(schid, 1);
+		switch (rc) {
+		case -EAGAIN:
+			css_schedule_eval(schid);
+			rc = 0;
+			break;
+		case -ENXIO:
+		case -ENOMEM:
+		case -EIO:
+			/* These should abort looping */
+			break;
+		default:
+			rc = 0;
+		}
+	}
+	return rc;
+}
+
+static void css_slow_path_func(struct work_struct *unused)
+{
+	CIO_TRACE_EVENT(4, "slowpath");
+	for_each_subchannel_staged(slow_eval_known_fn, slow_eval_unknown_fn,
+				   NULL);
 }
 
 static DECLARE_WORK(slow_path_work, css_slow_path_func);
@@ -430,7 +536,6 @@ void css_schedule_eval_all(void)
 /* Reprobe subchannel if unregistered. */
 static int reprobe_subchannel(struct subchannel_id schid, void *data)
 {
-	struct subchannel *sch;
 	int ret;
 
 	CIO_MSG_EVENT(6, "cio: reprobe 0.%x.%04x\n",
@@ -438,13 +543,6 @@ static int reprobe_subchannel(struct subchannel_id schid, void *data)
 	if (need_reprobe)
 		return -EAGAIN;
 
-	sch = get_subchannel_by_schid(schid);
-	if (sch) {
-		/* Already known. */
-		put_device(&sch->dev);
-		return 0;
-	}
-
 	ret = css_probe_device(schid);
 	switch (ret) {
 	case 0:
@@ -472,7 +570,7 @@ static void reprobe_all(struct work_struct *unused)
 	/* Make sure initial subchannel scan is done. */
 	wait_event(ccw_device_init_wq,
 		   atomic_read(&ccw_device_init_count) == 0);
-	ret = for_each_subchannel(reprobe_subchannel, NULL);
+	ret = for_each_subchannel_staged(NULL, reprobe_subchannel, NULL);
 
 	CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
 		      need_reprobe);
@@ -787,8 +885,8 @@ int sch_is_pseudo_sch(struct subchannel *sch)
 static int
 css_bus_match (struct device *dev, struct device_driver *drv)
 {
-	struct subchannel *sch = container_of (dev, struct subchannel, dev);
-	struct css_driver *driver = container_of (drv, struct css_driver, drv);
+	struct subchannel *sch = to_subchannel(dev);
+	struct css_driver *driver = to_cssdriver(drv);
 
 	if (sch->st == driver->subchannel_type)
 		return 1;
@@ -796,32 +894,36 @@ css_bus_match (struct device *dev, struct device_driver *drv)
 	return 0;
 }
 
-static int
-css_probe (struct device *dev)
+static int css_probe(struct device *dev)
 {
 	struct subchannel *sch;
+	int ret;
 
 	sch = to_subchannel(dev);
-	sch->driver = container_of (dev->driver, struct css_driver, drv);
-	return (sch->driver->probe ? sch->driver->probe(sch) : 0);
+	sch->driver = to_cssdriver(dev->driver);
+	ret = sch->driver->probe ? sch->driver->probe(sch) : 0;
+	if (ret)
+		sch->driver = NULL;
+	return ret;
 }
 
-static int
-css_remove (struct device *dev)
+static int css_remove(struct device *dev)
 {
 	struct subchannel *sch;
+	int ret;
 
 	sch = to_subchannel(dev);
-	return (sch->driver->remove ? sch->driver->remove(sch) : 0);
+	ret = sch->driver->remove ? sch->driver->remove(sch) : 0;
+	sch->driver = NULL;
+	return ret;
 }
 
-static void
-css_shutdown (struct device *dev)
+static void css_shutdown(struct device *dev)
 {
 	struct subchannel *sch;
 
 	sch = to_subchannel(dev);
-	if (sch->driver->shutdown)
+	if (sch->driver && sch->driver->shutdown)
 		sch->driver->shutdown(sch);
 }
 
@@ -833,6 +935,34 @@ struct bus_type css_bus_type = {
 	.shutdown = css_shutdown,
 };
 
+/**
+ * css_driver_register - register a css driver
+ * @cdrv: css driver to register
+ *
+ * This is mainly a wrapper around driver_register that sets name
+ * and bus_type in the embedded struct device_driver correctly.
+ */
+int css_driver_register(struct css_driver *cdrv)
+{
+	cdrv->drv.name = cdrv->name;
+	cdrv->drv.bus = &css_bus_type;
+	cdrv->drv.owner = cdrv->owner;
+	return driver_register(&cdrv->drv);
+}
+EXPORT_SYMBOL_GPL(css_driver_register);
+
+/**
+ * css_driver_unregister - unregister a css driver
+ * @cdrv: css driver to unregister
+ *
+ * This is a wrapper around driver_unregister.
+ */
+void css_driver_unregister(struct css_driver *cdrv)
+{
+	driver_unregister(&cdrv->drv);
+}
+EXPORT_SYMBOL_GPL(css_driver_unregister);
+
 subsys_initcall(init_channel_subsystem);
 
 MODULE_LICENSE("GPL");

+ 16 - 62
drivers/s390/cio/css.h

@@ -58,64 +58,6 @@ struct pgid {
 	__u32 tod_high;		/* high word TOD clock */
 } __attribute__ ((packed));
 
-#define MAX_CIWS 8
-
-/*
- * sense-id response buffer layout
- */
-struct senseid {
-	/* common part */
-	__u8  reserved;     	/* always 0x'FF' */
-	__u16 cu_type;	     	/* control unit type */
-	__u8  cu_model;     	/* control unit model */
-	__u16 dev_type;     	/* device type */
-	__u8  dev_model;    	/* device model */
-	__u8  unused;	     	/* padding byte */
-	/* extended part */
-	struct ciw ciw[MAX_CIWS];	/* variable # of CIWs */
-}  __attribute__ ((packed,aligned(4)));
-
-struct ccw_device_private {
-	struct ccw_device *cdev;
-	struct subchannel *sch;
-	int state;		/* device state */
-	atomic_t onoff;
-	unsigned long registered;
-	struct ccw_dev_id dev_id;	/* device id */
-	struct subchannel_id schid;	/* subchannel number */
-	__u8 imask;		/* lpm mask for SNID/SID/SPGID */
-	int iretry;		/* retry counter SNID/SID/SPGID */
-	struct {
-		unsigned int fast:1;	/* post with "channel end" */
-		unsigned int repall:1;	/* report every interrupt status */
-		unsigned int pgroup:1;  /* do path grouping */
-		unsigned int force:1;   /* allow forced online */
-	} __attribute__ ((packed)) options;
-	struct {
-		unsigned int pgid_single:1; /* use single path for Set PGID */
-		unsigned int esid:1;        /* Ext. SenseID supported by HW */
-		unsigned int dosense:1;	    /* delayed SENSE required */
-		unsigned int doverify:1;    /* delayed path verification */
-		unsigned int donotify:1;    /* call notify function */
-		unsigned int recog_done:1;  /* dev. recog. complete */
-		unsigned int fake_irb:1;    /* deliver faked irb */
-		unsigned int intretry:1;    /* retry internal operation */
-	} __attribute__((packed)) flags;
-	unsigned long intparm;	/* user interruption parameter */
-	struct qdio_irq *qdio_data;
-	struct irb irb;		/* device status */
-	struct senseid senseid;	/* SenseID info */
-	struct pgid pgid[8];	/* path group IDs per chpid*/
-	struct ccw1 iccws[2];	/* ccws for SNID/SID/SPGID commands */
-	struct work_struct kick_work;
-	wait_queue_head_t wait_q;
-	struct timer_list timer;
-	void *cmb;			/* measurement information */
-	struct list_head cmb_list;	/* list of measured devices */
-	u64 cmb_start_time;		/* clock value of cmb reset */
-	void *cmb_wait;			/* deferred cmb enable/disable */
-};
-
 /*
  * A css driver handles all subchannels of one type.
  * Currently, we only care about I/O subchannels (type 0), these
@@ -123,25 +65,35 @@ struct ccw_device_private {
  */
 struct subchannel;
 struct css_driver {
+	struct module *owner;
 	unsigned int subchannel_type;
 	struct device_driver drv;
-	void (*irq)(struct device *);
-	int (*notify)(struct device *, int);
-	void (*verify)(struct device *);
-	void (*termination)(struct device *);
+	void (*irq)(struct subchannel *);
+	int (*notify)(struct subchannel *, int);
+	void (*verify)(struct subchannel *);
+	void (*termination)(struct subchannel *);
 	int (*probe)(struct subchannel *);
 	int (*remove)(struct subchannel *);
 	void (*shutdown)(struct subchannel *);
+	const char *name;
 };
 
+#define to_cssdriver(n) container_of(n, struct css_driver, drv)
+
 /*
  * all css_drivers have the css_bus_type
  */
 extern struct bus_type css_bus_type;
 
+extern int css_driver_register(struct css_driver *);
+extern void css_driver_unregister(struct css_driver *);
+
 extern void css_sch_device_unregister(struct subchannel *);
 extern struct subchannel * get_subchannel_by_schid(struct subchannel_id);
 extern int css_init_done;
+int for_each_subchannel_staged(int (*fn_known)(struct subchannel *, void *),
+			       int (*fn_unknown)(struct subchannel_id,
+			       void *), void *data);
 extern int for_each_subchannel(int(*fn)(struct subchannel_id, void *), void *);
 extern void css_process_crw(int, int);
 extern void css_reiterate_subchannels(void);
@@ -188,6 +140,8 @@ void css_schedule_eval(struct subchannel_id schid);
 void css_schedule_eval_all(void);
 
 int sch_is_pseudo_sch(struct subchannel *);
+struct schib;
+int css_sch_is_valid(struct schib *);
 
 extern struct workqueue_struct *slow_path_wq;
 

+ 121 - 36
drivers/s390/cio/device.c

@@ -17,6 +17,7 @@
 #include <linux/list.h>
 #include <linux/device.h>
 #include <linux/workqueue.h>
+#include <linux/timer.h>
 
 #include <asm/ccwdev.h>
 #include <asm/cio.h>
@@ -28,6 +29,12 @@
 #include "css.h"
 #include "device.h"
 #include "ioasm.h"
+#include "io_sch.h"
+
+static struct timer_list recovery_timer;
+static spinlock_t recovery_lock;
+static int recovery_phase;
+static const unsigned long recovery_delay[] = { 3, 30, 300 };
 
 /******************* bus type handling ***********************/
 
@@ -115,19 +122,18 @@ static int ccw_uevent(struct device *dev, struct kobj_uevent_env *env)
 
 struct bus_type ccw_bus_type;
 
-static int io_subchannel_probe (struct subchannel *);
-static int io_subchannel_remove (struct subchannel *);
-static int io_subchannel_notify(struct device *, int);
-static void io_subchannel_verify(struct device *);
-static void io_subchannel_ioterm(struct device *);
+static void io_subchannel_irq(struct subchannel *);
+static int io_subchannel_probe(struct subchannel *);
+static int io_subchannel_remove(struct subchannel *);
+static int io_subchannel_notify(struct subchannel *, int);
+static void io_subchannel_verify(struct subchannel *);
+static void io_subchannel_ioterm(struct subchannel *);
 static void io_subchannel_shutdown(struct subchannel *);
 
 static struct css_driver io_subchannel_driver = {
+	.owner = THIS_MODULE,
 	.subchannel_type = SUBCHANNEL_TYPE_IO,
-	.drv = {
-		.name = "io_subchannel",
-		.bus  = &css_bus_type,
-	},
+	.name = "io_subchannel",
 	.irq = io_subchannel_irq,
 	.notify = io_subchannel_notify,
 	.verify = io_subchannel_verify,
@@ -142,6 +148,8 @@ struct workqueue_struct *ccw_device_notify_work;
 wait_queue_head_t ccw_device_init_wq;
 atomic_t ccw_device_init_count;
 
+static void recovery_func(unsigned long data);
+
 static int __init
 init_ccw_bus_type (void)
 {
@@ -149,6 +157,7 @@ init_ccw_bus_type (void)
 
 	init_waitqueue_head(&ccw_device_init_wq);
 	atomic_set(&ccw_device_init_count, 0);
+	setup_timer(&recovery_timer, recovery_func, 0);
 
 	ccw_device_work = create_singlethread_workqueue("cio");
 	if (!ccw_device_work)
@@ -166,7 +175,8 @@ init_ccw_bus_type (void)
 	if ((ret = bus_register (&ccw_bus_type)))
 		goto out_err;
 
-	if ((ret = driver_register(&io_subchannel_driver.drv)))
+	ret = css_driver_register(&io_subchannel_driver);
+	if (ret)
 		goto out_err;
 
 	wait_event(ccw_device_init_wq,
@@ -186,7 +196,7 @@ out_err:
 static void __exit
 cleanup_ccw_bus_type (void)
 {
-	driver_unregister(&io_subchannel_driver.drv);
+	css_driver_unregister(&io_subchannel_driver);
 	bus_unregister(&ccw_bus_type);
 	destroy_workqueue(ccw_device_notify_work);
 	destroy_workqueue(ccw_device_work);
@@ -773,7 +783,7 @@ static void sch_attach_device(struct subchannel *sch,
 {
 	css_update_ssd_info(sch);
 	spin_lock_irq(sch->lock);
-	sch->dev.driver_data = cdev;
+	sch_set_cdev(sch, cdev);
 	cdev->private->schid = sch->schid;
 	cdev->ccwlock = sch->lock;
 	device_trigger_reprobe(sch);
@@ -795,7 +805,7 @@ static void sch_attach_disconnected_device(struct subchannel *sch,
 		put_device(&other_sch->dev);
 		return;
 	}
-	other_sch->dev.driver_data = NULL;
+	sch_set_cdev(other_sch, NULL);
 	/* No need to keep a subchannel without ccw device around. */
 	css_sch_device_unregister(other_sch);
 	put_device(&other_sch->dev);
@@ -831,12 +841,12 @@ static void sch_create_and_recog_new_device(struct subchannel *sch)
 		return;
 	}
 	spin_lock_irq(sch->lock);
-	sch->dev.driver_data = cdev;
+	sch_set_cdev(sch, cdev);
 	spin_unlock_irq(sch->lock);
 	/* Start recognition for the new ccw device. */
 	if (io_subchannel_recog(cdev, sch)) {
 		spin_lock_irq(sch->lock);
-		sch->dev.driver_data = NULL;
+		sch_set_cdev(sch, NULL);
 		spin_unlock_irq(sch->lock);
 		if (cdev->dev.release)
 			cdev->dev.release(&cdev->dev);
@@ -940,7 +950,7 @@ io_subchannel_register(struct work_struct *work)
 			      cdev->private->dev_id.devno, ret);
 		put_device(&cdev->dev);
 		spin_lock_irqsave(sch->lock, flags);
-		sch->dev.driver_data = NULL;
+		sch_set_cdev(sch, NULL);
 		spin_unlock_irqrestore(sch->lock, flags);
 		kfree (cdev->private);
 		kfree (cdev);
@@ -1022,7 +1032,7 @@ io_subchannel_recog(struct ccw_device *cdev, struct subchannel *sch)
 	int rc;
 	struct ccw_device_private *priv;
 
-	sch->dev.driver_data = cdev;
+	sch_set_cdev(sch, cdev);
 	sch->driver = &io_subchannel_driver;
 	cdev->ccwlock = sch->lock;
 
@@ -1082,7 +1092,7 @@ static void ccw_device_move_to_sch(struct work_struct *work)
 	}
 	if (former_parent) {
 		spin_lock_irq(former_parent->lock);
-		former_parent->dev.driver_data = NULL;
+		sch_set_cdev(former_parent, NULL);
 		spin_unlock_irq(former_parent->lock);
 		css_sch_device_unregister(former_parent);
 		/* Reset intparm to zeroes. */
@@ -1096,6 +1106,18 @@ out:
 	put_device(&cdev->dev);
 }
 
+static void io_subchannel_irq(struct subchannel *sch)
+{
+	struct ccw_device *cdev;
+
+	cdev = sch_get_cdev(sch);
+
+	CIO_TRACE_EVENT(3, "IRQ");
+	CIO_TRACE_EVENT(3, sch->dev.bus_id);
+	if (cdev)
+		dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
+}
+
 static int
 io_subchannel_probe (struct subchannel *sch)
 {
@@ -1104,13 +1126,13 @@ io_subchannel_probe (struct subchannel *sch)
 	unsigned long flags;
 	struct ccw_dev_id dev_id;
 
-	if (sch->dev.driver_data) {
+	cdev = sch_get_cdev(sch);
+	if (cdev) {
 		/*
 		 * This subchannel already has an associated ccw_device.
 		 * Register it and exit. This happens for all early
 		 * device, e.g. the console.
 		 */
-		cdev = sch->dev.driver_data;
 		cdev->dev.groups = ccwdev_attr_groups;
 		device_initialize(&cdev->dev);
 		ccw_device_register(cdev);
@@ -1132,6 +1154,11 @@ io_subchannel_probe (struct subchannel *sch)
 	 */
 	dev_id.devno = sch->schib.pmcw.dev;
 	dev_id.ssid = sch->schid.ssid;
+	/* Allocate I/O subchannel private data. */
+	sch->private = kzalloc(sizeof(struct io_subchannel_private),
+			       GFP_KERNEL | GFP_DMA);
+	if (!sch->private)
+		return -ENOMEM;
 	cdev = get_disc_ccwdev_by_dev_id(&dev_id, NULL);
 	if (!cdev)
 		cdev = get_orphaned_ccwdev_by_dev_id(to_css(sch->dev.parent),
@@ -1149,16 +1176,18 @@ io_subchannel_probe (struct subchannel *sch)
 		return 0;
 	}
 	cdev = io_subchannel_create_ccwdev(sch);
-	if (IS_ERR(cdev))
+	if (IS_ERR(cdev)) {
+		kfree(sch->private);
 		return PTR_ERR(cdev);
-
+	}
 	rc = io_subchannel_recog(cdev, sch);
 	if (rc) {
 		spin_lock_irqsave(sch->lock, flags);
-		sch->dev.driver_data = NULL;
+		sch_set_cdev(sch, NULL);
 		spin_unlock_irqrestore(sch->lock, flags);
 		if (cdev->dev.release)
 			cdev->dev.release(&cdev->dev);
+		kfree(sch->private);
 	}
 
 	return rc;
@@ -1170,25 +1199,25 @@ io_subchannel_remove (struct subchannel *sch)
 	struct ccw_device *cdev;
 	unsigned long flags;
 
-	if (!sch->dev.driver_data)
+	cdev = sch_get_cdev(sch);
+	if (!cdev)
 		return 0;
-	cdev = sch->dev.driver_data;
 	/* Set ccw device to not operational and drop reference. */
 	spin_lock_irqsave(cdev->ccwlock, flags);
-	sch->dev.driver_data = NULL;
+	sch_set_cdev(sch, NULL);
 	cdev->private->state = DEV_STATE_NOT_OPER;
 	spin_unlock_irqrestore(cdev->ccwlock, flags);
 	ccw_device_unregister(cdev);
 	put_device(&cdev->dev);
+	kfree(sch->private);
 	return 0;
 }
 
-static int
-io_subchannel_notify(struct device *dev, int event)
+static int io_subchannel_notify(struct subchannel *sch, int event)
 {
 	struct ccw_device *cdev;
 
-	cdev = dev->driver_data;
+	cdev = sch_get_cdev(sch);
 	if (!cdev)
 		return 0;
 	if (!cdev->drv)
@@ -1198,22 +1227,20 @@ io_subchannel_notify(struct device *dev, int event)
 	return cdev->drv->notify ? cdev->drv->notify(cdev, event) : 0;
 }
 
-static void
-io_subchannel_verify(struct device *dev)
+static void io_subchannel_verify(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	cdev = dev->driver_data;
+	cdev = sch_get_cdev(sch);
 	if (cdev)
 		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
 }
 
-static void
-io_subchannel_ioterm(struct device *dev)
+static void io_subchannel_ioterm(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	cdev = dev->driver_data;
+	cdev = sch_get_cdev(sch);
 	if (!cdev)
 		return;
 	/* Internal I/O will be retried by the interrupt handler. */
@@ -1231,7 +1258,7 @@ io_subchannel_shutdown(struct subchannel *sch)
 	struct ccw_device *cdev;
 	int ret;
 
-	cdev = sch->dev.driver_data;
+	cdev = sch_get_cdev(sch);
 
 	if (cio_is_console(sch->schid))
 		return;
@@ -1271,6 +1298,9 @@ ccw_device_console_enable (struct ccw_device *cdev, struct subchannel *sch)
 {
 	int rc;
 
+	/* Attach subchannel private data. */
+	sch->private = cio_get_console_priv();
+	memset(sch->private, 0, sizeof(struct io_subchannel_private));
 	/* Initialize the ccw_device structure. */
 	cdev->dev.parent= &sch->dev;
 	rc = io_subchannel_recog(cdev, sch);
@@ -1456,6 +1486,7 @@ int ccw_driver_register(struct ccw_driver *cdriver)
 
 	drv->bus = &ccw_bus_type;
 	drv->name = cdriver->name;
+	drv->owner = cdriver->owner;
 
 	return driver_register(drv);
 }
@@ -1481,6 +1512,60 @@ ccw_device_get_subchannel_id(struct ccw_device *cdev)
 	return sch->schid;
 }
 
+static int recovery_check(struct device *dev, void *data)
+{
+	struct ccw_device *cdev = to_ccwdev(dev);
+	int *redo = data;
+
+	spin_lock_irq(cdev->ccwlock);
+	switch (cdev->private->state) {
+	case DEV_STATE_DISCONNECTED:
+		CIO_MSG_EVENT(3, "recovery: trigger 0.%x.%04x\n",
+			      cdev->private->dev_id.ssid,
+			      cdev->private->dev_id.devno);
+		dev_fsm_event(cdev, DEV_EVENT_VERIFY);
+		*redo = 1;
+		break;
+	case DEV_STATE_DISCONNECTED_SENSE_ID:
+		*redo = 1;
+		break;
+	}
+	spin_unlock_irq(cdev->ccwlock);
+
+	return 0;
+}
+
+static void recovery_func(unsigned long data)
+{
+	int redo = 0;
+
+	bus_for_each_dev(&ccw_bus_type, NULL, &redo, recovery_check);
+	if (redo) {
+		spin_lock_irq(&recovery_lock);
+		if (!timer_pending(&recovery_timer)) {
+			if (recovery_phase < ARRAY_SIZE(recovery_delay) - 1)
+				recovery_phase++;
+			mod_timer(&recovery_timer, jiffies +
+				  recovery_delay[recovery_phase] * HZ);
+		}
+		spin_unlock_irq(&recovery_lock);
+	} else
+		CIO_MSG_EVENT(2, "recovery: end\n");
+}
+
+void ccw_device_schedule_recovery(void)
+{
+	unsigned long flags;
+
+	CIO_MSG_EVENT(2, "recovery: schedule\n");
+	spin_lock_irqsave(&recovery_lock, flags);
+	if (!timer_pending(&recovery_timer) || (recovery_phase != 0)) {
+		recovery_phase = 0;
+		mod_timer(&recovery_timer, jiffies + recovery_delay[0] * HZ);
+	}
+	spin_unlock_irqrestore(&recovery_lock, flags);
+}
+
 MODULE_LICENSE("GPL");
 EXPORT_SYMBOL(ccw_device_set_online);
 EXPORT_SYMBOL(ccw_device_set_offline);

+ 4 - 1
drivers/s390/cio/device.h

@@ -5,6 +5,8 @@
 #include <asm/atomic.h>
 #include <linux/wait.h>
 
+#include "io_sch.h"
+
 /*
  * states of the device statemachine
  */
@@ -74,7 +76,6 @@ extern struct workqueue_struct *ccw_device_notify_work;
 extern wait_queue_head_t ccw_device_init_wq;
 extern atomic_t ccw_device_init_count;
 
-void io_subchannel_irq (struct device *pdev);
 void io_subchannel_recog_done(struct ccw_device *cdev);
 
 int ccw_device_cancel_halt_clear(struct ccw_device *);
@@ -87,6 +88,8 @@ int ccw_device_recognition(struct ccw_device *);
 int ccw_device_online(struct ccw_device *);
 int ccw_device_offline(struct ccw_device *);
 
+void ccw_device_schedule_recovery(void);
+
 /* Function prototypes for device status and basic sense stuff. */
 void ccw_device_accumulate_irb(struct ccw_device *, struct irb *);
 void ccw_device_accumulate_basic_sense(struct ccw_device *, struct irb *);

+ 83 - 41
drivers/s390/cio/device_fsm.c

@@ -25,14 +25,16 @@
 #include "ioasm.h"
 #include "chp.h"
 
+static int timeout_log_enabled;
+
 int
 device_is_online(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	if (!sch->dev.driver_data)
+	cdev = sch_get_cdev(sch);
+	if (!cdev)
 		return 0;
-	cdev = sch->dev.driver_data;
 	return (cdev->private->state == DEV_STATE_ONLINE);
 }
 
@@ -41,9 +43,9 @@ device_is_disconnected(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	if (!sch->dev.driver_data)
+	cdev = sch_get_cdev(sch);
+	if (!cdev)
 		return 0;
-	cdev = sch->dev.driver_data;
 	return (cdev->private->state == DEV_STATE_DISCONNECTED ||
 		cdev->private->state == DEV_STATE_DISCONNECTED_SENSE_ID);
 }
@@ -53,19 +55,21 @@ device_set_disconnected(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	if (!sch->dev.driver_data)
+	cdev = sch_get_cdev(sch);
+	if (!cdev)
 		return;
-	cdev = sch->dev.driver_data;
 	ccw_device_set_timeout(cdev, 0);
 	cdev->private->flags.fake_irb = 0;
 	cdev->private->state = DEV_STATE_DISCONNECTED;
+	if (cdev->online)
+		ccw_device_schedule_recovery();
 }
 
 void device_set_intretry(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	cdev = sch->dev.driver_data;
+	cdev = sch_get_cdev(sch);
 	if (!cdev)
 		return;
 	cdev->private->flags.intretry = 1;
@@ -75,13 +79,62 @@ int device_trigger_verify(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	cdev = sch->dev.driver_data;
+	cdev = sch_get_cdev(sch);
 	if (!cdev || !cdev->online)
 		return -EINVAL;
 	dev_fsm_event(cdev, DEV_EVENT_VERIFY);
 	return 0;
 }
 
+static int __init ccw_timeout_log_setup(char *unused)
+{
+	timeout_log_enabled = 1;
+	return 1;
+}
+
+__setup("ccw_timeout_log", ccw_timeout_log_setup);
+
+static void ccw_timeout_log(struct ccw_device *cdev)
+{
+	struct schib schib;
+	struct subchannel *sch;
+	struct io_subchannel_private *private;
+	int cc;
+
+	sch = to_subchannel(cdev->dev.parent);
+	private = to_io_private(sch);
+	cc = stsch(sch->schid, &schib);
+
+	printk(KERN_WARNING "cio: ccw device timeout occurred at %llx, "
+	       "device information:\n", get_clock());
+	printk(KERN_WARNING "cio: orb:\n");
+	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+		       &private->orb, sizeof(private->orb), 0);
+	printk(KERN_WARNING "cio: ccw device bus id: %s\n", cdev->dev.bus_id);
+	printk(KERN_WARNING "cio: subchannel bus id: %s\n", sch->dev.bus_id);
+	printk(KERN_WARNING "cio: subchannel lpm: %02x, opm: %02x, "
+	       "vpm: %02x\n", sch->lpm, sch->opm, sch->vpm);
+
+	if ((void *)(addr_t)private->orb.cpa == &private->sense_ccw ||
+	    (void *)(addr_t)private->orb.cpa == cdev->private->iccws)
+		printk(KERN_WARNING "cio: last channel program (intern):\n");
+	else
+		printk(KERN_WARNING "cio: last channel program:\n");
+
+	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+		       (void *)(addr_t)private->orb.cpa,
+		       sizeof(struct ccw1), 0);
+	printk(KERN_WARNING "cio: ccw device state: %d\n",
+	       cdev->private->state);
+	printk(KERN_WARNING "cio: store subchannel returned: cc=%d\n", cc);
+	printk(KERN_WARNING "cio: schib:\n");
+	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+		       &schib, sizeof(schib), 0);
+	printk(KERN_WARNING "cio: ccw device flags:\n");
+	print_hex_dump(KERN_WARNING, "cio:  ", DUMP_PREFIX_NONE, 16, 1,
+		       &cdev->private->flags, sizeof(cdev->private->flags), 0);
+}
+
 /*
  * Timeout function. It just triggers a DEV_EVENT_TIMEOUT.
  */
@@ -92,6 +145,8 @@ ccw_device_timeout(unsigned long data)
 
 	cdev = (struct ccw_device *) data;
 	spin_lock_irq(cdev->ccwlock);
+	if (timeout_log_enabled)
+		ccw_timeout_log(cdev);
 	dev_fsm_event(cdev, DEV_EVENT_TIMEOUT);
 	spin_unlock_irq(cdev->ccwlock);
 }
@@ -122,9 +177,9 @@ device_kill_pending_timer(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	if (!sch->dev.driver_data)
+	cdev = sch_get_cdev(sch);
+	if (!cdev)
 		return;
-	cdev = sch->dev.driver_data;
 	ccw_device_set_timeout(cdev, 0);
 }
 
@@ -268,7 +323,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
 	switch (state) {
 	case DEV_STATE_NOT_OPER:
 		CIO_DEBUG(KERN_WARNING, 2,
-			  "cio: SenseID : unknown device %04x on subchannel "
+			  "SenseID : unknown device %04x on subchannel "
 			  "0.%x.%04x\n", cdev->private->dev_id.devno,
 			  sch->schid.ssid, sch->schid.sch_no);
 		break;
@@ -294,7 +349,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
 		}
 		/* Issue device info message. */
 		CIO_DEBUG(KERN_INFO, 2,
-			  "cio: SenseID : device 0.%x.%04x reports: "
+			  "SenseID : device 0.%x.%04x reports: "
 			  "CU  Type/Mod = %04X/%02X, Dev Type/Mod = "
 			  "%04X/%02X\n",
 			  cdev->private->dev_id.ssid,
@@ -304,7 +359,7 @@ ccw_device_recog_done(struct ccw_device *cdev, int state)
 		break;
 	case DEV_STATE_BOXED:
 		CIO_DEBUG(KERN_WARNING, 2,
-			  "cio: SenseID : boxed device %04x on subchannel "
+			  "SenseID : boxed device %04x on subchannel "
 			  "0.%x.%04x\n", cdev->private->dev_id.devno,
 			  sch->schid.ssid, sch->schid.sch_no);
 		break;
@@ -349,7 +404,7 @@ ccw_device_oper_notify(struct work_struct *work)
 	sch = to_subchannel(cdev->dev.parent);
 	if (sch->driver && sch->driver->notify) {
 		spin_unlock_irqrestore(cdev->ccwlock, flags);
-		ret = sch->driver->notify(&sch->dev, CIO_OPER);
+		ret = sch->driver->notify(sch, CIO_OPER);
 		spin_lock_irqsave(cdev->ccwlock, flags);
 	} else
 		ret = 0;
@@ -389,7 +444,7 @@ ccw_device_done(struct ccw_device *cdev, int state)
 
 	if (state == DEV_STATE_BOXED)
 		CIO_DEBUG(KERN_WARNING, 2,
-			  "cio: Boxed device %04x on subchannel %04x\n",
+			  "Boxed device %04x on subchannel %04x\n",
 			  cdev->private->dev_id.devno, sch->schid.sch_no);
 
 	if (cdev->private->flags.donotify) {
@@ -500,7 +555,8 @@ ccw_device_recognition(struct ccw_device *cdev)
 	    (cdev->private->state != DEV_STATE_BOXED))
 		return -EINVAL;
 	sch = to_subchannel(cdev->dev.parent);
-	ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+	ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+				    (u32)(addr_t)sch);
 	if (ret != 0)
 		/* Couldn't enable the subchannel for i/o. Sick device. */
 		return ret;
@@ -587,9 +643,10 @@ ccw_device_verify_done(struct ccw_device *cdev, int err)
 	default:
 		/* Reset oper notify indication after verify error. */
 		cdev->private->flags.donotify = 0;
-		if (cdev->online)
+		if (cdev->online) {
+			ccw_device_set_timeout(cdev, 0);
 			dev_fsm_event(cdev, DEV_EVENT_NOTOPER);
-		else
+		} else
 			ccw_device_done(cdev, DEV_STATE_NOT_OPER);
 		break;
 	}
@@ -610,7 +667,8 @@ ccw_device_online(struct ccw_device *cdev)
 	sch = to_subchannel(cdev->dev.parent);
 	if (css_init_done && !get_device(&cdev->dev))
 		return -ENODEV;
-	ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc);
+	ret = cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+				    (u32)(addr_t)sch);
 	if (ret != 0) {
 		/* Couldn't enable the subchannel for i/o. Sick device. */
 		if (ret == -ENODEV)
@@ -937,7 +995,7 @@ void device_kill_io(struct subchannel *sch)
 	int ret;
 	struct ccw_device *cdev;
 
-	cdev = sch->dev.driver_data;
+	cdev = sch_get_cdev(sch);
 	ret = ccw_device_cancel_halt_clear(cdev);
 	if (ret == -EBUSY) {
 		ccw_device_set_timeout(cdev, 3*HZ);
@@ -990,7 +1048,8 @@ ccw_device_start_id(struct ccw_device *cdev, enum dev_event dev_event)
 	struct subchannel *sch;
 
 	sch = to_subchannel(cdev->dev.parent);
-	if (cio_enable_subchannel(sch, sch->schib.pmcw.isc) != 0)
+	if (cio_enable_subchannel(sch, sch->schib.pmcw.isc,
+				  (u32)(addr_t)sch) != 0)
 		/* Couldn't enable the subchannel for i/o. Sick device. */
 		return;
 
@@ -1006,9 +1065,9 @@ device_trigger_reprobe(struct subchannel *sch)
 {
 	struct ccw_device *cdev;
 
-	if (!sch->dev.driver_data)
+	cdev = sch_get_cdev(sch);
+	if (!cdev)
 		return;
-	cdev = sch->dev.driver_data;
 	if (cdev->private->state != DEV_STATE_DISCONNECTED)
 		return;
 
@@ -1028,7 +1087,7 @@ device_trigger_reprobe(struct subchannel *sch)
 	sch->schib.pmcw.ena = 0;
 	if ((sch->lpm & (sch->lpm - 1)) != 0)
 		sch->schib.pmcw.mp = 1;
-	sch->schib.pmcw.intparm = (__u32)(unsigned long)sch;
+	sch->schib.pmcw.intparm = (u32)(addr_t)sch;
 	/* We should also udate ssd info, but this has to wait. */
 	/* Check if this is another device which appeared on the same sch. */
 	if (sch->schib.pmcw.dev != cdev->private->dev_id.devno) {
@@ -1223,21 +1282,4 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
 	},
 };
 
-/*
- * io_subchannel_irq is called for "real" interrupts or for status
- * pending conditions on msch.
- */
-void
-io_subchannel_irq (struct device *pdev)
-{
-	struct ccw_device *cdev;
-
-	cdev = to_subchannel(pdev)->dev.driver_data;
-
-	CIO_TRACE_EVENT (3, "IRQ");
-	CIO_TRACE_EVENT (3, pdev->bus_id);
-	if (cdev)
-		dev_fsm_event(cdev, DEV_EVENT_INTERRUPT);
-}
-
 EXPORT_SYMBOL_GPL(ccw_device_set_timeout);

+ 6 - 3
drivers/s390/cio/device_id.c

@@ -24,6 +24,7 @@
 #include "css.h"
 #include "device.h"
 #include "ioasm.h"
+#include "io_sch.h"
 
 /*
  * Input :
@@ -219,11 +220,13 @@ ccw_device_check_sense_id(struct ccw_device *cdev)
 		return -EAGAIN;
 	}
 	if (irb->scsw.cc == 3) {
-		if ((sch->orb.lpm &
-		     sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
+		u8 lpm;
+
+		lpm = to_io_private(sch)->orb.lpm;
+		if ((lpm & sch->schib.pmcw.pim & sch->schib.pmcw.pam) != 0)
 			CIO_MSG_EVENT(2, "SenseID : path %02X for device %04x "
 				      "on subchannel 0.%x.%04x is "
-				      "'not operational'\n", sch->orb.lpm,
+				      "'not operational'\n", lpm,
 				      cdev->private->dev_id.devno,
 				      sch->schid.ssid, sch->schid.sch_no);
 		return -EACCES;

+ 1 - 1
drivers/s390/cio/device_ops.c

@@ -501,7 +501,7 @@ ccw_device_stlck(struct ccw_device *cdev)
 		return -ENOMEM;
 	}
 	spin_lock_irqsave(sch->lock, flags);
-	ret = cio_enable_subchannel(sch, 3);
+	ret = cio_enable_subchannel(sch, 3, (u32)(addr_t)sch);
 	if (ret)
 		goto out_unlock;
 	/*

+ 5 - 1
drivers/s390/cio/device_pgid.c

@@ -22,6 +22,7 @@
 #include "css.h"
 #include "device.h"
 #include "ioasm.h"
+#include "io_sch.h"
 
 /*
  * Helper function called from interrupt context to decide whether an
@@ -155,10 +156,13 @@ __ccw_device_check_sense_pgid(struct ccw_device *cdev)
 		return -EAGAIN;
 	}
 	if (irb->scsw.cc == 3) {
+		u8 lpm;
+
+		lpm = to_io_private(sch)->orb.lpm;
 		CIO_MSG_EVENT(2, "SNID - Device %04x on Subchannel 0.%x.%04x,"
 			      " lpm %02X, became 'not operational'\n",
 			      cdev->private->dev_id.devno, sch->schid.ssid,
-			      sch->schid.sch_no, sch->orb.lpm);
+			      sch->schid.sch_no, lpm);
 		return -EACCES;
 	}
 	i = 8 - ffs(cdev->private->imask);

+ 8 - 5
drivers/s390/cio/device_status.c

@@ -20,6 +20,7 @@
 #include "css.h"
 #include "device.h"
 #include "ioasm.h"
+#include "io_sch.h"
 
 /*
  * Check for any kind of channel or interface control check but don't
@@ -310,6 +311,7 @@ int
 ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
 {
 	struct subchannel *sch;
+	struct ccw1 *sense_ccw;
 
 	sch = to_subchannel(cdev->dev.parent);
 
@@ -326,15 +328,16 @@ ccw_device_do_sense(struct ccw_device *cdev, struct irb *irb)
 	/*
 	 * We have ending status but no sense information. Do a basic sense.
 	 */
-	sch->sense_ccw.cmd_code = CCW_CMD_BASIC_SENSE;
-	sch->sense_ccw.cda = (__u32) __pa(cdev->private->irb.ecw);
-	sch->sense_ccw.count = SENSE_MAX_COUNT;
-	sch->sense_ccw.flags = CCW_FLAG_SLI;
+	sense_ccw = &to_io_private(sch)->sense_ccw;
+	sense_ccw->cmd_code = CCW_CMD_BASIC_SENSE;
+	sense_ccw->cda = (__u32) __pa(cdev->private->irb.ecw);
+	sense_ccw->count = SENSE_MAX_COUNT;
+	sense_ccw->flags = CCW_FLAG_SLI;
 
 	/* Reset internal retry indication. */
 	cdev->private->flags.intretry = 0;
 
-	return cio_start (sch, &sch->sense_ccw, 0xff);
+	return cio_start(sch, sense_ccw, 0xff);
 }
 
 /*

+ 163 - 0
drivers/s390/cio/io_sch.h

@@ -0,0 +1,163 @@
+#ifndef S390_IO_SCH_H
+#define S390_IO_SCH_H
+
+#include "schid.h"
+
+/*
+ * operation request block
+ */
+struct orb {
+	u32 intparm;	/* interruption parameter */
+	u32 key  : 4;	/* flags, like key, suspend control, etc. */
+	u32 spnd : 1;	/* suspend control */
+	u32 res1 : 1;	/* reserved */
+	u32 mod  : 1;	/* modification control */
+	u32 sync : 1;	/* synchronize control */
+	u32 fmt  : 1;	/* format control */
+	u32 pfch : 1;	/* prefetch control */
+	u32 isic : 1;	/* initial-status-interruption control */
+	u32 alcc : 1;	/* address-limit-checking control */
+	u32 ssic : 1;	/* suppress-suspended-interr. control */
+	u32 res2 : 1;	/* reserved */
+	u32 c64  : 1;	/* IDAW/QDIO 64 bit control  */
+	u32 i2k  : 1;	/* IDAW 2/4kB block size control */
+	u32 lpm  : 8;	/* logical path mask */
+	u32 ils  : 1;	/* incorrect length */
+	u32 zero : 6;	/* reserved zeros */
+	u32 orbx : 1;	/* ORB extension control */
+	u32 cpa;	/* channel program address */
+}  __attribute__ ((packed, aligned(4)));
+
+struct io_subchannel_private {
+	struct orb orb;		/* operation request block */
+	struct ccw1 sense_ccw;	/* static ccw for sense command */
+} __attribute__ ((aligned(8)));
+
+#define to_io_private(n) ((struct io_subchannel_private *)n->private)
+#define sch_get_cdev(n) (dev_get_drvdata(&n->dev))
+#define sch_set_cdev(n, c) (dev_set_drvdata(&n->dev, c))
+
+#define MAX_CIWS 8
+
+/*
+ * sense-id response buffer layout
+ */
+struct senseid {
+	/* common part */
+	u8  reserved;	/* always 0x'FF' */
+	u16 cu_type;	/* control unit type */
+	u8  cu_model;	/* control unit model */
+	u16 dev_type;	/* device type */
+	u8  dev_model;	/* device model */
+	u8  unused;	/* padding byte */
+	/* extended part */
+	struct ciw ciw[MAX_CIWS];	/* variable # of CIWs */
+}  __attribute__ ((packed, aligned(4)));
+
+struct ccw_device_private {
+	struct ccw_device *cdev;
+	struct subchannel *sch;
+	int state;		/* device state */
+	atomic_t onoff;
+	unsigned long registered;
+	struct ccw_dev_id dev_id;	/* device id */
+	struct subchannel_id schid;	/* subchannel number */
+	u8 imask;		/* lpm mask for SNID/SID/SPGID */
+	int iretry;		/* retry counter SNID/SID/SPGID */
+	struct {
+		unsigned int fast:1;	/* post with "channel end" */
+		unsigned int repall:1;	/* report every interrupt status */
+		unsigned int pgroup:1;	/* do path grouping */
+		unsigned int force:1;	/* allow forced online */
+	} __attribute__ ((packed)) options;
+	struct {
+		unsigned int pgid_single:1; /* use single path for Set PGID */
+		unsigned int esid:1;	    /* Ext. SenseID supported by HW */
+		unsigned int dosense:1;	    /* delayed SENSE required */
+		unsigned int doverify:1;    /* delayed path verification */
+		unsigned int donotify:1;    /* call notify function */
+		unsigned int recog_done:1;  /* dev. recog. complete */
+		unsigned int fake_irb:1;    /* deliver faked irb */
+		unsigned int intretry:1;    /* retry internal operation */
+	} __attribute__((packed)) flags;
+	unsigned long intparm;	/* user interruption parameter */
+	struct qdio_irq *qdio_data;
+	struct irb irb;		/* device status */
+	struct senseid senseid;	/* SenseID info */
+	struct pgid pgid[8];	/* path group IDs per chpid*/
+	struct ccw1 iccws[2];	/* ccws for SNID/SID/SPGID commands */
+	struct work_struct kick_work;
+	wait_queue_head_t wait_q;
+	struct timer_list timer;
+	void *cmb;			/* measurement information */
+	struct list_head cmb_list;	/* list of measured devices */
+	u64 cmb_start_time;		/* clock value of cmb reset */
+	void *cmb_wait;			/* deferred cmb enable/disable */
+};
+
+static inline int ssch(struct subchannel_id schid, volatile struct orb *addr)
+{
+	register struct subchannel_id reg1 asm("1") = schid;
+	int ccode;
+
+	asm volatile(
+		"	ssch	0(%2)\n"
+		"	ipm	%0\n"
+		"	srl	%0,28"
+		: "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
+	return ccode;
+}
+
+static inline int rsch(struct subchannel_id schid)
+{
+	register struct subchannel_id reg1 asm("1") = schid;
+	int ccode;
+
+	asm volatile(
+		"	rsch\n"
+		"	ipm	%0\n"
+		"	srl	%0,28"
+		: "=d" (ccode) : "d" (reg1) : "cc");
+	return ccode;
+}
+
+static inline int csch(struct subchannel_id schid)
+{
+	register struct subchannel_id reg1 asm("1") = schid;
+	int ccode;
+
+	asm volatile(
+		"	csch\n"
+		"	ipm	%0\n"
+		"	srl	%0,28"
+		: "=d" (ccode) : "d" (reg1) : "cc");
+	return ccode;
+}
+
+static inline int hsch(struct subchannel_id schid)
+{
+	register struct subchannel_id reg1 asm("1") = schid;
+	int ccode;
+
+	asm volatile(
+		"	hsch\n"
+		"	ipm	%0\n"
+		"	srl	%0,28"
+		: "=d" (ccode) : "d" (reg1) : "cc");
+	return ccode;
+}
+
+static inline int xsch(struct subchannel_id schid)
+{
+	register struct subchannel_id reg1 asm("1") = schid;
+	int ccode;
+
+	asm volatile(
+		"	.insn	rre,0xb2760000,%1,0\n"
+		"	ipm	%0\n"
+		"	srl	%0,28"
+		: "=d" (ccode) : "d" (reg1) : "cc");
+	return ccode;
+}
+
+#endif

+ 0 - 66
drivers/s390/cio/ioasm.h

@@ -109,72 +109,6 @@ static inline int tpi( volatile struct tpi_info *addr)
 	return ccode;
 }
 
-static inline int ssch(struct subchannel_id schid,
-			   volatile struct orb *addr)
-{
-	register struct subchannel_id reg1 asm ("1") = schid;
-	int ccode;
-
-	asm volatile(
-		"	ssch	0(%2)\n"
-		"	ipm	%0\n"
-		"	srl	%0,28"
-		: "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
-	return ccode;
-}
-
-static inline int rsch(struct subchannel_id schid)
-{
-	register struct subchannel_id reg1 asm ("1") = schid;
-	int ccode;
-
-	asm volatile(
-		"	rsch\n"
-		"	ipm	%0\n"
-		"	srl	%0,28"
-		: "=d" (ccode) : "d" (reg1) : "cc");
-	return ccode;
-}
-
-static inline int csch(struct subchannel_id schid)
-{
-	register struct subchannel_id reg1 asm ("1") = schid;
-	int ccode;
-
-	asm volatile(
-		"	csch\n"
-		"	ipm	%0\n"
-		"	srl	%0,28"
-		: "=d" (ccode) : "d" (reg1) : "cc");
-	return ccode;
-}
-
-static inline int hsch(struct subchannel_id schid)
-{
-	register struct subchannel_id reg1 asm ("1") = schid;
-	int ccode;
-
-	asm volatile(
-		"	hsch\n"
-		"	ipm	%0\n"
-		"	srl	%0,28"
-		: "=d" (ccode) : "d" (reg1) : "cc");
-	return ccode;
-}
-
-static inline int xsch(struct subchannel_id schid)
-{
-	register struct subchannel_id reg1 asm ("1") = schid;
-	int ccode;
-
-	asm volatile(
-		"	.insn	rre,0xb2760000,%1,0\n"
-		"	ipm	%0\n"
-		"	srl	%0,28"
-		: "=d" (ccode) : "d" (reg1) : "cc");
-	return ccode;
-}
-
 static inline int chsc(void *chsc_area)
 {
 	typedef struct { char _[4096]; } addr_type;

+ 18 - 20
drivers/s390/cio/qdio.c

@@ -48,11 +48,11 @@
 #include <asm/debug.h>
 #include <asm/s390_rdev.h>
 #include <asm/qdio.h>
+#include <asm/airq.h>
 
 #include "cio.h"
 #include "css.h"
 #include "device.h"
-#include "airq.h"
 #include "qdio.h"
 #include "ioasm.h"
 #include "chsc.h"
@@ -96,7 +96,7 @@ static debug_info_t *qdio_dbf_slsb_in;
 static volatile struct qdio_q *tiq_list=NULL; /* volatile as it could change
 						 during a while loop */
 static DEFINE_SPINLOCK(ttiq_list_lock);
-static int register_thinint_result;
+static void *tiqdio_ind;
 static void tiqdio_tl(unsigned long);
 static DECLARE_TASKLET(tiqdio_tasklet,tiqdio_tl,0);
 
@@ -399,7 +399,7 @@ qdio_get_indicator(void)
 {
 	int i;
 
-	for (i=1;i<INDICATORS_PER_CACHELINE;i++)
+	for (i = 0; i < INDICATORS_PER_CACHELINE; i++)
 		if (!indicator_used[i]) {
 			indicator_used[i]=1;
 			return indicators+i;
@@ -1408,8 +1408,7 @@ __tiqdio_inbound_processing(struct qdio_q *q, int spare_ind_was_set)
 	if (q->hydra_gives_outbound_pcis) {
 		if (!q->siga_sync_done_on_thinints) {
 			SYNC_MEMORY_ALL;
-		} else if ((!q->siga_sync_done_on_outb_tis)&&
-			 (q->hydra_gives_outbound_pcis)) {
+		} else if (!q->siga_sync_done_on_outb_tis) {
 			SYNC_MEMORY_ALL_OUTB;
 		}
 	} else {
@@ -1911,8 +1910,7 @@ qdio_fill_thresholds(struct qdio_irq *irq_ptr,
 	}
 }
 
-static int
-tiqdio_thinint_handler(void)
+static void tiqdio_thinint_handler(void *ind, void *drv_data)
 {
 	QDIO_DBF_TEXT4(0,trace,"thin_int");
 
@@ -1925,7 +1923,6 @@ tiqdio_thinint_handler(void)
 		tiqdio_clear_global_summary();
 
 	tiqdio_inbound_checks();
-	return 0;
 }
 
 static void
@@ -2445,7 +2442,7 @@ tiqdio_set_subchannel_ind(struct qdio_irq *irq_ptr, int reset_to_zero)
 		real_addr_dev_st_chg_ind=0;
 	} else {
 		real_addr_local_summary_bit=
-			virt_to_phys((volatile void *)indicators);
+			virt_to_phys((volatile void *)tiqdio_ind);
 		real_addr_dev_st_chg_ind=
 			virt_to_phys((volatile void *)irq_ptr->dev_st_chg_ind);
 	}
@@ -3740,23 +3737,25 @@ static void
 tiqdio_register_thinints(void)
 {
 	char dbf_text[20];
-	register_thinint_result=
-		s390_register_adapter_interrupt(&tiqdio_thinint_handler);
-	if (register_thinint_result) {
-		sprintf(dbf_text,"regthn%x",(register_thinint_result&0xff));
+
+	tiqdio_ind =
+		s390_register_adapter_interrupt(&tiqdio_thinint_handler, NULL);
+	if (IS_ERR(tiqdio_ind)) {
+		sprintf(dbf_text, "regthn%lx", PTR_ERR(tiqdio_ind));
 		QDIO_DBF_TEXT0(0,setup,dbf_text);
 		QDIO_PRINT_ERR("failed to register adapter handler " \
-			       "(rc=%i).\nAdapter interrupts might " \
+			       "(rc=%li).\nAdapter interrupts might " \
 			       "not work. Continuing.\n",
-			       register_thinint_result);
+			       PTR_ERR(tiqdio_ind));
+		tiqdio_ind = NULL;
 	}
 }
 
 static void
 tiqdio_unregister_thinints(void)
 {
-	if (!register_thinint_result)
-		s390_unregister_adapter_interrupt(&tiqdio_thinint_handler);
+	if (tiqdio_ind)
+		s390_unregister_adapter_interrupt(tiqdio_ind);
 }
 
 static int
@@ -3768,8 +3767,8 @@ qdio_get_qdio_memory(void)
 	for (i=1;i<INDICATORS_PER_CACHELINE;i++)
 		indicator_used[i]=0;
 	indicators = kzalloc(sizeof(__u32)*(INDICATORS_PER_CACHELINE),
-				   GFP_KERNEL);
-       	if (!indicators)
+			     GFP_KERNEL);
+	if (!indicators)
 		return -ENOMEM;
 	return 0;
 }
@@ -3780,7 +3779,6 @@ qdio_release_qdio_memory(void)
 	kfree(indicators);
 }
 
-
 static void
 qdio_unregister_dbf_views(void)
 {

+ 1 - 1
drivers/s390/cio/qdio.h

@@ -57,7 +57,7 @@
 					    of the queue to 0 */
 
 #define QDIO_ESTABLISH_TIMEOUT (1*HZ)
-#define QDIO_ACTIVATE_TIMEOUT ((5*HZ)>>10)
+#define QDIO_ACTIVATE_TIMEOUT (5*HZ)
 #define QDIO_CLEANUP_CLEAR_TIMEOUT (20*HZ)
 #define QDIO_CLEANUP_HALT_TIMEOUT (10*HZ)
 #define QDIO_FORCE_CHECK_TIMEOUT (10*HZ)

+ 1 - 1
drivers/s390/net/claw.c

@@ -2416,7 +2416,7 @@ init_ccw_bk(struct net_device *dev)
 		    privptr->p_buff_pages_perwrite);
 #endif
                    if (p_buff==NULL) {
-                        printk(KERN_INFO "%s:%s __get_free_pages"
+			printk(KERN_INFO "%s:%s __get_free_pages "
 			 	"for writes buf failed : get is for %d pages\n",
 				dev->name,
 				__FUNCTION__,

+ 1 - 1
drivers/s390/net/lcs.c

@@ -1115,7 +1115,7 @@ list_modified:
 			rc = lcs_send_setipm(card, ipm);
 			spin_lock_irqsave(&card->ipm_lock, flags);
 			if (rc) {
-				PRINT_INFO("Adding multicast address failed."
+				PRINT_INFO("Adding multicast address failed. "
 					   "Table possibly full!\n");
 				/* store ipm in failed list -> will be added
 				 * to ipm_list again, so a retry will be done

+ 1 - 2
drivers/s390/net/netiucv.c

@@ -198,8 +198,7 @@ struct iucv_connection {
 /**
  * Linked list of all connection structs.
  */
-static struct list_head iucv_connection_list =
-	LIST_HEAD_INIT(iucv_connection_list);
+static LIST_HEAD(iucv_connection_list);
 static DEFINE_RWLOCK(iucv_connection_rwlock);
 
 /**

+ 2 - 2
drivers/s390/net/qeth_proc.c

@@ -146,7 +146,7 @@ qeth_procfile_seq_show(struct seq_file *s, void *it)
 	return 0;
 }
 
-static struct seq_operations qeth_procfile_seq_ops = {
+static const struct seq_operations qeth_procfile_seq_ops = {
 	.start = qeth_procfile_seq_start,
 	.stop  = qeth_procfile_seq_stop,
 	.next  = qeth_procfile_seq_next,
@@ -264,7 +264,7 @@ qeth_perf_procfile_seq_show(struct seq_file *s, void *it)
 	return 0;
 }
 
-static struct seq_operations qeth_perf_procfile_seq_ops = {
+static const struct seq_operations qeth_perf_procfile_seq_ops = {
 	.start = qeth_procfile_seq_start,
 	.stop  = qeth_procfile_seq_stop,
 	.next  = qeth_procfile_seq_next,

+ 1 - 1
drivers/s390/net/smsgiucv.c

@@ -42,7 +42,7 @@ MODULE_DESCRIPTION ("Linux for S/390 IUCV special message driver");
 static struct iucv_path *smsg_path;
 
 static DEFINE_SPINLOCK(smsg_list_lock);
-static struct list_head smsg_list = LIST_HEAD_INIT(smsg_list);
+static LIST_HEAD(smsg_list);
 
 static int smsg_path_pending(struct iucv_path *, u8 ipvmid[8], u8 ipuser[16]);
 static void smsg_message_pending(struct iucv_path *, struct iucv_message *);

+ 1 - 1
drivers/s390/scsi/zfcp_erp.c

@@ -1286,7 +1286,7 @@ zfcp_erp_strategy_do_action(struct zfcp_erp_action *erp_action)
 	 * note: no lock in subsequent strategy routines
 	 * (this allows these routine to call schedule, e.g.
 	 * kmalloc with such flags or qdio_initialize & friends)
-	 * Note: in case of timeout, the seperate strategies will fail
+	 * Note: in case of timeout, the separate strategies will fail
 	 * anyhow. No need for a special action. Even worse, a nameserver
 	 * failure would not wake up waiting ports without the call.
 	 */

+ 5 - 5
drivers/s390/scsi/zfcp_fsf.c

@@ -502,7 +502,7 @@ zfcp_fsf_fsfstatus_qual_eval(struct zfcp_fsf_req *fsf_req)
 		fsf_req->status |= ZFCP_STATUS_FSFREQ_ERROR;
 		break;
 	case FSF_SQ_NO_RECOM:
-		ZFCP_LOG_NORMAL("bug: No recommendation could be given for a"
+		ZFCP_LOG_NORMAL("bug: No recommendation could be given for a "
 				"problem on the adapter %s "
 				"Stopping all operations on this adapter. ",
 				zfcp_get_busid_by_adapter(fsf_req->adapter));
@@ -813,7 +813,7 @@ zfcp_fsf_status_read_port_closed(struct zfcp_fsf_req *fsf_req)
 	read_unlock_irqrestore(&zfcp_data.config_lock, flags);
 
 	if (!port || (port->d_id != (status_buffer->d_id & ZFCP_DID_MASK))) {
-		ZFCP_LOG_NORMAL("bug: Reopen port indication received for"
+		ZFCP_LOG_NORMAL("bug: Reopen port indication received for "
 				"nonexisting port with d_id 0x%06x on "
 				"adapter %s. Ignored.\n",
 				status_buffer->d_id & ZFCP_DID_MASK,
@@ -2281,7 +2281,7 @@ zfcp_fsf_exchange_port_data(struct zfcp_erp_action *erp_action)
 				     &lock_flags, &fsf_req);
 	if (retval) {
 		ZFCP_LOG_INFO("error: Out of resources. Could not create an "
-			      "exchange port data request for"
+			      "exchange port data request for "
 			      "the adapter %s.\n",
 			      zfcp_get_busid_by_adapter(adapter));
 		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
@@ -2340,7 +2340,7 @@ zfcp_fsf_exchange_port_data_sync(struct zfcp_adapter *adapter,
 				0, NULL, &lock_flags, &fsf_req);
 	if (retval) {
 		ZFCP_LOG_INFO("error: Out of resources. Could not create an "
-			      "exchange port data request for"
+			      "exchange port data request for "
 			      "the adapter %s.\n",
 			      zfcp_get_busid_by_adapter(adapter));
 		write_unlock_irqrestore(&adapter->request_queue.queue_lock,
@@ -4725,7 +4725,7 @@ zfcp_fsf_req_create(struct zfcp_adapter *adapter, u32 fsf_cmd, int req_flags,
 	/* allocate new FSF request */
 	fsf_req = zfcp_fsf_req_alloc(pool, req_flags);
 	if (unlikely(NULL == fsf_req)) {
-		ZFCP_LOG_DEBUG("error: Could not put an FSF request into"
+		ZFCP_LOG_DEBUG("error: Could not put an FSF request into "
 			       "the outbound (send) queue.\n");
 		ret = -ENOMEM;
 		goto failed_fsf_req;

+ 1 - 1
drivers/s390/scsi/zfcp_qdio.c

@@ -529,7 +529,7 @@ zfcp_qdio_sbals_wipe(struct zfcp_fsf_req *fsf_req)
 
 
 /**
- * zfcp_qdio_sbale_fill - set address and lenght in current SBALE
+ * zfcp_qdio_sbale_fill - set address and length in current SBALE
  *	on request_queue
  */
 static void

+ 19 - 0
include/asm-s390/airq.h

@@ -0,0 +1,19 @@
+/*
+ *  include/asm-s390/airq.h
+ *
+ *    Copyright IBM Corp. 2002,2007
+ *    Author(s): Ingo Adlung <adlung@de.ibm.com>
+ *		 Cornelia Huck <cornelia.huck@de.ibm.com>
+ *		 Arnd Bergmann <arndb@de.ibm.com>
+ *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
+ */
+
+#ifndef _ASM_S390_AIRQ_H
+#define _ASM_S390_AIRQ_H
+
+typedef void (*adapter_int_handler_t)(void *, void *);
+
+void *s390_register_adapter_interrupt(adapter_int_handler_t, void *);
+void s390_unregister_adapter_interrupt(void *);
+
+#endif /* _ASM_S390_AIRQ_H */

+ 2 - 2
include/asm-s390/cio.h

@@ -24,8 +24,8 @@
  * @fmt: format
  * @pfch: prefetch
  * @isic: initial-status interruption control
- * @alcc: adress-limit checking control
- * @ssi: supress-suspended interruption
+ * @alcc: address-limit checking control
+ * @ssi: suppress-suspended interruption
  * @zcc: zero condition code
  * @ectl: extended control
  * @pno: path not operational

+ 1 - 1
include/asm-s390/dasd.h

@@ -105,7 +105,7 @@ typedef struct dasd_information_t {
 } dasd_information_t;
 
 /*
- * Read Subsystem Data - Perfomance Statistics
+ * Read Subsystem Data - Performance Statistics
  */ 
 typedef struct dasd_rssd_perf_stats_t {
 	unsigned char  invalid:1;

+ 7 - 1
include/asm-s390/ipl.h

@@ -83,6 +83,8 @@ extern u32 dump_prefix_page;
 extern unsigned int zfcpdump_prefix_array[];
 
 extern void do_reipl(void);
+extern void do_halt(void);
+extern void do_poff(void);
 extern void ipl_save_parameters(void);
 
 enum {
@@ -118,7 +120,7 @@ struct ipl_info
 };
 
 extern struct ipl_info ipl_info;
-extern void setup_ipl_info(void);
+extern void setup_ipl(void);
 
 /*
  * DIAG 308 support
@@ -141,6 +143,10 @@ enum diag308_opt {
 	DIAG308_IPL_OPT_DUMP	= 0x20,
 };
 
+enum diag308_flags {
+	DIAG308_FLAGS_LP_VALID	= 0x80,
+};
+
 enum diag308_rc {
 	DIAG308_RC_OK	= 1,
 };

+ 12 - 15
include/asm-s390/mmu_context.h

@@ -12,10 +12,15 @@
 #include <asm/pgalloc.h>
 #include <asm-generic/mm_hooks.h>
 
-/*
- * get a new mmu context.. S390 don't know about contexts.
- */
-#define init_new_context(tsk,mm)        0
+static inline int init_new_context(struct task_struct *tsk,
+				   struct mm_struct *mm)
+{
+	mm->context = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
+#ifdef CONFIG_64BIT
+	mm->context |= _ASCE_TYPE_REGION3;
+#endif
+	return 0;
+}
 
 #define destroy_context(mm)             do { } while (0)
 
@@ -27,19 +32,11 @@
 
 static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
 {
-	pgd_t *pgd = mm->pgd;
-	unsigned long asce_bits;
-
-	/* Calculate asce bits from the first pgd table entry. */
-	asce_bits = _ASCE_TABLE_LENGTH | _ASCE_USER_BITS;
-#ifdef CONFIG_64BIT
-	asce_bits |= _ASCE_TYPE_REGION3;
-#endif
-	S390_lowcore.user_asce = asce_bits | __pa(pgd);
+	S390_lowcore.user_asce = mm->context | __pa(mm->pgd);
 	if (switch_amode) {
 		/* Load primary space page table origin. */
-		pgd_t *shadow_pgd = get_shadow_table(pgd) ? : pgd;
-		S390_lowcore.user_exec_asce = asce_bits | __pa(shadow_pgd);
+		pgd_t *shadow_pgd = get_shadow_table(mm->pgd) ? : mm->pgd;
+		S390_lowcore.user_exec_asce = mm->context | __pa(shadow_pgd);
 		asm volatile(LCTL_OPCODE" 1,1,%0\n"
 			     : : "m" (S390_lowcore.user_exec_asce) );
 	} else

+ 16 - 30
include/asm-s390/pgtable.h

@@ -104,41 +104,27 @@ extern char empty_zero_page[PAGE_SIZE];
 
 #ifndef __ASSEMBLY__
 /*
- * Just any arbitrary offset to the start of the vmalloc VM area: the
- * current 8MB value just means that there will be a 8MB "hole" after the
- * physical memory until the kernel virtual memory starts.  That means that
- * any out-of-bounds memory accesses will hopefully be caught.
- * The vmalloc() routines leaves a hole of 4kB between each vmalloced
- * area for the same reason. ;)
- * vmalloc area starts at 4GB to prevent syscall table entry exchanging
- * from modules.
- */
-extern unsigned long vmalloc_end;
-
-#ifdef CONFIG_64BIT
-#define VMALLOC_ADDR	(max(0x100000000UL, (unsigned long) high_memory))
-#else
-#define VMALLOC_ADDR	((unsigned long) high_memory)
-#endif
-#define VMALLOC_OFFSET	(8*1024*1024)
-#define VMALLOC_START	((VMALLOC_ADDR + VMALLOC_OFFSET) & ~(VMALLOC_OFFSET-1))
-#define VMALLOC_END	vmalloc_end
-
-/*
- * We need some free virtual space to be able to do vmalloc.
- * VMALLOC_MIN_SIZE defines the minimum size of the vmalloc
- * area. On a machine with 2GB memory we make sure that we
- * have at least 128MB free space for vmalloc. On a machine
- * with 4TB we make sure we have at least 128GB.
+ * The vmalloc area will always be on the topmost area of the kernel
+ * mapping. We reserve 96MB (31bit) / 1GB (64bit) for vmalloc,
+ * which should be enough for any sane case.
+ * By putting vmalloc at the top, we maximise the gap between physical
+ * memory and vmalloc to catch misplaced memory accesses. As a side
+ * effect, this also makes sure that 64 bit module code cannot be used
+ * as system call address.
  */
 #ifndef __s390x__
-#define VMALLOC_MIN_SIZE	0x8000000UL
-#define VMALLOC_END_INIT	0x80000000UL
+#define VMALLOC_START	0x78000000UL
+#define VMALLOC_END	0x7e000000UL
+#define VMEM_MAP_MAX	0x80000000UL
 #else /* __s390x__ */
-#define VMALLOC_MIN_SIZE	0x2000000000UL
-#define VMALLOC_END_INIT	0x40000000000UL
+#define VMALLOC_START	0x3e000000000UL
+#define VMALLOC_END	0x3e040000000UL
+#define VMEM_MAP_MAX	0x40000000000UL
 #endif /* __s390x__ */
 
+#define VMEM_MAP	((struct page *) VMALLOC_END)
+#define VMEM_MAP_SIZE	((VMALLOC_START / PAGE_SIZE) * sizeof(struct page))
+
 /*
  * A 31 bit pagetable entry of S390 has following format:
  *  |   PFRA          |    |  OS  |

+ 0 - 4
include/asm-s390/processor.h

@@ -59,9 +59,6 @@ extern void s390_adjust_jiffies(void);
 extern void print_cpu_info(struct cpuinfo_S390 *);
 extern int get_cpu_capability(unsigned int *);
 
-/* Lazy FPU handling on uni-processor */
-extern struct task_struct *last_task_used_math;
-
 /*
  * User space process size: 2GB for 31 bit, 4TB for 64 bit.
  */
@@ -95,7 +92,6 @@ struct thread_struct {
         unsigned long ksp;              /* kernel stack pointer             */
 	mm_segment_t mm_segment;
         unsigned long prot_addr;        /* address of protection-excep.     */
-        unsigned int error_code;        /* error-code of last prog-excep.   */
         unsigned int trap_no;
         per_struct per_info;
 	/* Used to give failing instruction back to user for ieee exceptions */

+ 8 - 0
include/asm-s390/ptrace.h

@@ -465,6 +465,14 @@ struct user_regs_struct
 #ifdef __KERNEL__
 #define __ARCH_SYS_PTRACE	1
 
+/*
+ * These are defined as per linux/ptrace.h, which see.
+ */
+#define arch_has_single_step()	(1)
+struct task_struct;
+extern void user_enable_single_step(struct task_struct *);
+extern void user_disable_single_step(struct task_struct *);
+
 #define user_mode(regs) (((regs)->psw.mask & PSW_MASK_PSTATE) != 0)
 #define instruction_pointer(regs) ((regs)->psw.addr & PSW_ADDR_INSN)
 #define regs_return_value(regs)((regs)->gprs[2])

+ 1 - 1
include/asm-s390/qdio.h

@@ -184,7 +184,7 @@ struct qdr {
 #endif /* QDIO_32_BIT */
 	unsigned long qiba;             /* queue-information-block address */
 	unsigned int  res8;             /* reserved */
-	unsigned int  qkey    :  4;     /* queue-informatio-block key */
+	unsigned int  qkey    :  4;	/* queue-information-block key */
 	unsigned int  res9    : 28;     /* reserved */
 /*	union _qd {*/ /* why this? */
 		struct qdesfmt0 qdf0[126];

+ 2 - 2
include/asm-s390/rwsem.h

@@ -91,8 +91,8 @@ struct rw_semaphore {
 #endif
 
 #define __RWSEM_INITIALIZER(name) \
-{ RWSEM_UNLOCKED_VALUE, SPIN_LOCK_UNLOCKED, LIST_HEAD_INIT((name).wait_list) \
-  __RWSEM_DEP_MAP_INIT(name) }
+ { RWSEM_UNLOCKED_VALUE, __SPIN_LOCK_UNLOCKED((name).wait.lock), \
+   LIST_HEAD_INIT((name).wait_list) __RWSEM_DEP_MAP_INIT(name) }
 
 #define DECLARE_RWSEM(name) \
 	struct rw_semaphore name = __RWSEM_INITIALIZER(name)

+ 19 - 1
include/asm-s390/sclp.h

@@ -27,7 +27,25 @@ struct sclp_ipl_info {
 	char loadparm[LOADPARM_LEN];
 };
 
-void sclp_readinfo_early(void);
+struct sclp_cpu_entry {
+	u8 address;
+	u8 reserved0[13];
+	u8 type;
+	u8 reserved1;
+} __attribute__((packed));
+
+struct sclp_cpu_info {
+	unsigned int configured;
+	unsigned int standby;
+	unsigned int combined;
+	int has_cpu_type;
+	struct sclp_cpu_entry cpu[255];
+};
+
+int sclp_get_cpu_info(struct sclp_cpu_info *info);
+int sclp_cpu_configure(u8 cpu);
+int sclp_cpu_deconfigure(u8 cpu);
+void sclp_read_info_early(void);
 void sclp_facilities_detect(void);
 unsigned long long sclp_memory_detect(void);
 int sclp_sdias_blk_count(void);

+ 2 - 3
include/asm-s390/smp.h

@@ -35,8 +35,6 @@ extern void machine_restart_smp(char *);
 extern void machine_halt_smp(void);
 extern void machine_power_off_smp(void);
 
-extern void smp_setup_cpu_possible_map(void);
-
 #define NO_PROC_ID		0xFF		/* No processor magic marker */
 
 /*
@@ -92,6 +90,8 @@ extern void __cpu_die (unsigned int cpu);
 extern void cpu_die (void) __attribute__ ((noreturn));
 extern int __cpu_up (unsigned int cpu);
 
+extern int smp_call_function_mask(cpumask_t mask, void (*func)(void *),
+	void *info, int wait);
 #endif
 
 #ifndef CONFIG_SMP
@@ -103,7 +103,6 @@ static inline void smp_send_stop(void)
 
 #define hard_smp_processor_id()		0
 #define smp_cpu_not_running(cpu)	1
-#define smp_setup_cpu_possible_map()	do { } while (0)
 #endif
 
 extern union save_area *zfcpdump_save_areas[NR_CPUS + 1];

Some files were not shown because too many files changed in this diff