mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-16 16:54:20 +08:00
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. ...
This commit is contained in:
commit
0444fa7875
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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.
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
Load Diff
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
*/
|
||||
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)
|
||||
continue;
|
||||
__cpu_logical_map[1] = (__u16) cpu;
|
||||
if (signal_processor(1, sigp_sense) == sigp_not_operational)
|
||||
continue;
|
||||
smp_get_save_area(num_cpus, cpu);
|
||||
num_cpus++;
|
||||
/* Check for stopped state */
|
||||
if (signal_processor_ps(&status, 0, cpu, sigp_sense) ==
|
||||
sigp_status_stored) {
|
||||
if (status & 0x40)
|
||||
return 1;
|
||||
}
|
||||
printk("Detected %d CPU's\n", (int) num_cpus);
|
||||
printk("Boot cpu address %2X\n", boot_cpu_addr);
|
||||
return num_cpus;
|
||||
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;
|
||||
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[CPU_INIT_NO] = cpu_addr;
|
||||
if (!cpu_stopped(CPU_INIT_NO)) {
|
||||
s_cpus++;
|
||||
continue;
|
||||
}
|
||||
smp_get_save_area(c_cpus, cpu_addr);
|
||||
c_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;
|
||||
|
||||
/* Check for stopped state */
|
||||
if (signal_processor_ps(&status, 0, cpu, sigp_sense) ==
|
||||
sigp_status_stored) {
|
||||
if (status & 0x40)
|
||||
return 1;
|
||||
lc_order = sizeof(long) == 8 ? 1 : 0;
|
||||
lowcore = (void *) __get_free_pages(GFP_KERNEL | GFP_DMA, lc_order);
|
||||
if (!lowcore)
|
||||
return -ENOMEM;
|
||||
async_stack = __get_free_pages(GFP_KERNEL, ASYNC_ORDER);
|
||||
if (!async_stack)
|
||||
goto out_async_stack;
|
||||
panic_stack = __get_free_page(GFP_KERNEL);
|
||||
if (!panic_stack)
|
||||
goto out_panic_stack;
|
||||
|
||||
*lowcore = S390_lowcore;
|
||||
lowcore->async_stack = async_stack + ASYNC_SIZE;
|
||||
lowcore->panic_stack = panic_stack + PAGE_SIZE;
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
if (MACHINE_HAS_IEEE) {
|
||||
unsigned long save_area;
|
||||
|
||||
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;
|
||||
|
||||
int __cpu_up(unsigned int cpu)
|
||||
lc_order = sizeof(long) == 8 ? 1 : 0;
|
||||
lowcore = lowcore_ptr[cpu];
|
||||
#ifndef CONFIG_64BIT
|
||||
if (MACHINE_HAS_IEEE)
|
||||
free_page((unsigned long) lowcore->extended_save_area_addr);
|
||||
#endif
|
||||
free_page(lowcore->panic_stack - PAGE_SIZE);
|
||||
free_pages(lowcore->async_stack - ASYNC_SIZE, ASYNC_ORDER);
|
||||
free_pages((unsigned long) lowcore, lc_order);
|
||||
lowcore_ptr[cpu] = NULL;
|
||||
}
|
||||
#endif /* CONFIG_HOTPLUG_CPU */
|
||||
|
||||
/* Upping and downing of CPUs */
|
||||
int __cpuinit __cpu_up(unsigned int cpu)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
for (cpu = 0; cpu < pos_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);
|
||||
int pcpus, 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);
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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" };
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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,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
Load Diff
@ -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 */
|
@ -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;
|
||||
|
@ -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 */
|
@ -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
drivers/s390/block/dasd_alias.c
Normal file
903
drivers/s390/block/dasd_alias.c
Normal file
@ -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);
|
||||
};
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
Load Diff
@ -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 */
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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 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 *);
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
/*
|
||||
* 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);
|
||||
|
@ -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");
|
||||
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);
|
||||
|
||||
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
|
||||
device->stopped &= ~DASD_STOPPED_QUIESCE;
|
||||
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
|
||||
|
||||
dasd_schedule_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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
|
@ -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>");
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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;
|
||||
|
@ -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
drivers/s390/char/sclp_cmd.c
Normal file
398
drivers/s390/char/sclp_cmd.c
Normal file
@ -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;
|
||||
}
|
@ -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>");
|
||||
static char *system_name = "";
|
||||
static char *sysplex_name = "";
|
||||
|
||||
MODULE_DESCRIPTION(
|
||||
"identify this operating system instance to the S/390 "
|
||||
"or zSeries hardware");
|
||||
|
||||
static char *system_name = NULL;
|
||||
module_param(system_name, charp, 0);
|
||||
MODULE_PARM_DESC(system_name, "e.g. hostname - max. 8 characters");
|
||||
|
||||
static char *sysplex_name = NULL;
|
||||
#ifdef ALLOW_SYSPLEX_NAME
|
||||
module_param(sysplex_name, charp, 0);
|
||||
MODULE_PARM_DESC(sysplex_name, "if applicable - max. 8 characters");
|
||||
#endif
|
||||
|
||||
/* use default value for this field (as well as for system level) */
|
||||
static char *system_type = "LINUX";
|
||||
|
||||
static int
|
||||
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
drivers/s390/char/sclp_cpi_sys.c
Normal file
400
drivers/s390/char/sclp_cpi_sys.c
Normal file
@ -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
drivers/s390/char/sclp_cpi_sys.h
Normal file
15
drivers/s390/char/sclp_cpi_sys.h
Normal file
@ -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__ */
|
@ -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);
|
||||
}
|
@ -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 *
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
||||
/*
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
union indicator_t {
|
||||
unsigned long word[NR_AIRQ_WORDS];
|
||||
unsigned char byte[NR_AIRQS];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct airq_t {
|
||||
adapter_int_handler_t handler;
|
||||
void *drv_data;
|
||||
};
|
||||
|
||||
static union indicator_t indicators;
|
||||
static struct airq_t *airqs[NR_AIRQS];
|
||||
|
||||
static int register_airq(struct airq_t *airq)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NR_AIRQS; i++)
|
||||
if (!cmpxchg(&airqs[i], NULL, airq))
|
||||
return i;
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*
|
||||
* 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 ...
|
||||
* Returns:
|
||||
* Pointer to the indicator to be used on success
|
||||
* ERR_PTR() if registration failed
|
||||
*/
|
||||
int
|
||||
s390_register_adapter_interrupt (adapter_int_handler_t handler)
|
||||
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, "rgaint");
|
||||
|
||||
if (handler == NULL)
|
||||
ret = -EINVAL;
|
||||
else
|
||||
ret = (cmpxchg(&adapter_handler, NULL, handler) ? -EBUSY : 0);
|
||||
if (!ret)
|
||||
synchronize_sched(); /* Allow interrupts to complete. */
|
||||
|
||||
sprintf (dbf_txt, "ret:%d", ret);
|
||||
CIO_TRACE_EVENT (4, dbf_txt);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
s390_unregister_adapter_interrupt (adapter_int_handler_t handler)
|
||||
{
|
||||
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);
|
||||
|
||||
EXPORT_SYMBOL (s390_register_adapter_interrupt);
|
||||
EXPORT_SYMBOL (s390_unregister_adapter_interrupt);
|
||||
#define INDICATOR_MASK (0xffUL << ((NR_AIRQS_PER_WORD - 1) * 8))
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
struct chp_id *chpid = data;
|
||||
|
||||
chpid = data;
|
||||
sch = get_subchannel_by_schid(schid);
|
||||
if (!sch)
|
||||
/* Check if the subchannel is now available. */
|
||||
return __chp_add_new_sch(schid);
|
||||
spin_lock_irq(sch->lock);
|
||||
for (i=0; i<8; i++) {
|
||||
mask = 0x80 >> i;
|
||||
if ((sch->schib.pmcw.pim & mask) &&
|
||||
(sch->schib.pmcw.chpid[i] == chpid->id)) {
|
||||
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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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 int slow_eval_known_fn(struct subchannel *sch, void *data)
|
||||
{
|
||||
int eval;
|
||||
int rc;
|
||||
|
||||
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)
|
||||
{
|
||||
struct subchannel_id schid;
|
||||
|
||||
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);
|
||||
}
|
||||
spin_unlock_irq(&slow_subchannel_lock);
|
||||
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");
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
/*
|
||||
|
@ -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);
|
||||
|
@ -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
drivers/s390/cio/io_sch.h
Normal file
163
drivers/s390/cio/io_sch.h
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
|
@ -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__,
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
/**
|
||||
|
@ -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,
|
||||
|
@ -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 *);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
|
@ -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
include/asm-s390/airq.h
Normal file
19
include/asm-s390/airq.h
Normal file
@ -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 */
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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,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
|
||||
|
@ -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 |
|
||||
|
@ -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 */
|
||||
|
@ -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])
|
||||
|
@ -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];
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
@ -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 have changed in this diff Show More
Loading…
Reference in New Issue
Block a user