mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Martin Schwidefsky:
"The main new feature is machine support for System zEC12 including
transactional memory, runtime instrumentation, support for scm block
devices via eadm subchannels, and support for CEX4 crypto cards.
In addition there are some nice improvements: bpf jit compiler, arch
backend for cmpxchg_double, relative exception table entries, dasd
partition detection independent from the dasd driver ioctls, and cpu
cache information in /proc/cpuinfo and /sys/device/cpu.
And last but not least a series of cleanup patches from Heiko."
Fix up trivial add-add conflict in arch/s390/Kconfig due to commit
b952741c80
("cputime: Generalize CONFIG_VIRT_CPU_ACCOUNTING")
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (76 commits)
s390: update defconfig
s390/jump label,nss: let shared kernel support depend on !JUMP_LABEL
s390/disassembler: fix decoding of risblg instruction
s390/bpf,jit: add support for BPF_S_ANC_ALU_XOR_X instruction
s390/traps: move call to print_modules() out of show_regs()
s390/mm: mark free_initrd_mem() as __init
s390/dasd: check count address during online setting
drivers/s390/char/monreader.c: fix error return code
s390/cmpxchg,percpu: implement cmpxchg_double()
s390/percpu: implement this_cpu_add_return()
s390/percpu: implement this_cpu_xchg()
s390/kexec: remove CONFIG_KEXEC
s390/irq: use designated initializers for irq class array
s390: add uninitialized_var() to suppress false positive compiler warnings
s390/crashdump: move fill_cpu_elf_notes() prototype to header file
s390/process: add missing header include
s390/ptrace: add missing ifdef
s390/ipl,decrompressor: disable branch profiling
s390/perf_events: compile only for CONFIG_64BIT
s390/tape: remove even more tape block leftovers
...
This commit is contained in:
commit
6c09931b3f
@ -5,3 +5,4 @@ obj-$(CONFIG_CRYPTO_HW) += crypto/
|
||||
obj-$(CONFIG_S390_HYPFS_FS) += hypfs/
|
||||
obj-$(CONFIG_APPLDATA_BASE) += appldata/
|
||||
obj-$(CONFIG_MATHEMU) += math-emu/
|
||||
obj-y += net/
|
||||
|
@ -52,6 +52,12 @@ config PGSTE
|
||||
config ARCH_SUPPORTS_DEBUG_PAGEALLOC
|
||||
def_bool y
|
||||
|
||||
config KEXEC
|
||||
def_bool y
|
||||
|
||||
config AUDIT_ARCH
|
||||
def_bool y
|
||||
|
||||
config S390
|
||||
def_bool y
|
||||
select USE_GENERIC_SMP_HELPERS if SMP
|
||||
@ -81,11 +87,13 @@ config S390
|
||||
select HAVE_KERNEL_XZ
|
||||
select HAVE_ARCH_MUTEX_CPU_RELAX
|
||||
select HAVE_ARCH_JUMP_LABEL if !MARCH_G5
|
||||
select HAVE_BPF_JIT if 64BIT && PACK_STACK
|
||||
select ARCH_SAVE_PAGE_KEYS if HIBERNATION
|
||||
select ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
|
||||
select HAVE_MEMBLOCK
|
||||
select HAVE_MEMBLOCK_NODE_MAP
|
||||
select HAVE_CMPXCHG_LOCAL
|
||||
select HAVE_CMPXCHG_DOUBLE
|
||||
select HAVE_VIRT_CPU_ACCOUNTING
|
||||
select VIRT_CPU_ACCOUNTING
|
||||
select ARCH_DISCARD_MEMBLOCK
|
||||
@ -132,9 +140,79 @@ source "init/Kconfig"
|
||||
|
||||
source "kernel/Kconfig.freezer"
|
||||
|
||||
menu "Base setup"
|
||||
menu "Processor type and features"
|
||||
|
||||
comment "Processor type and features"
|
||||
config HAVE_MARCH_Z900_FEATURES
|
||||
def_bool n
|
||||
|
||||
config HAVE_MARCH_Z990_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z900_FEATURES
|
||||
|
||||
config HAVE_MARCH_Z9_109_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z990_FEATURES
|
||||
|
||||
config HAVE_MARCH_Z10_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z9_109_FEATURES
|
||||
|
||||
config HAVE_MARCH_Z196_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z10_FEATURES
|
||||
|
||||
choice
|
||||
prompt "Processor type"
|
||||
default MARCH_G5
|
||||
|
||||
config MARCH_G5
|
||||
bool "System/390 model G5 and G6"
|
||||
depends on !64BIT
|
||||
help
|
||||
Select this to build a 31 bit kernel that works
|
||||
on all ESA/390 and z/Architecture machines.
|
||||
|
||||
config MARCH_Z900
|
||||
bool "IBM zSeries model z800 and z900"
|
||||
select HAVE_MARCH_Z900_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for model z800/z900 (2064 and
|
||||
2066 series). This will enable some optimizations that are not
|
||||
available on older ESA/390 (31 Bit) only CPUs.
|
||||
|
||||
config MARCH_Z990
|
||||
bool "IBM zSeries model z890 and z990"
|
||||
select HAVE_MARCH_Z990_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for model z890/z990 (2084 and
|
||||
2086 series). The kernel will be slightly faster but will not work
|
||||
on older machines.
|
||||
|
||||
config MARCH_Z9_109
|
||||
bool "IBM System z9"
|
||||
select HAVE_MARCH_Z9_109_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for IBM System z9 (2094 and
|
||||
2096 series). The kernel will be slightly faster but will not work
|
||||
on older machines.
|
||||
|
||||
config MARCH_Z10
|
||||
bool "IBM System z10"
|
||||
select HAVE_MARCH_Z10_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for IBM System z10 (2097 and
|
||||
2098 series). The kernel will be slightly faster but will not work
|
||||
on older machines.
|
||||
|
||||
config MARCH_Z196
|
||||
bool "IBM zEnterprise 114 and 196"
|
||||
select HAVE_MARCH_Z196_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for IBM zEnterprise 114 and 196
|
||||
(2818 and 2817 series). The kernel will be slightly faster but will
|
||||
not work on older machines.
|
||||
|
||||
endchoice
|
||||
|
||||
config 64BIT
|
||||
def_bool y
|
||||
@ -146,6 +224,24 @@ config 64BIT
|
||||
config 32BIT
|
||||
def_bool y if !64BIT
|
||||
|
||||
config COMPAT
|
||||
def_bool y
|
||||
prompt "Kernel support for 31 bit emulation"
|
||||
depends on 64BIT
|
||||
select COMPAT_BINFMT_ELF if BINFMT_ELF
|
||||
select ARCH_WANT_OLD_COMPAT_IPC
|
||||
help
|
||||
Select this option if you want to enable your system kernel to
|
||||
handle system-calls from ELF binaries for 31 bit ESA. This option
|
||||
(and some other stuff like libraries and such) is needed for
|
||||
executing 31 bit applications. It is safe to say "Y".
|
||||
|
||||
config SYSVIPC_COMPAT
|
||||
def_bool y if COMPAT && SYSVIPC
|
||||
|
||||
config KEYS_COMPAT
|
||||
def_bool y if COMPAT && KEYS
|
||||
|
||||
config SMP
|
||||
def_bool y
|
||||
prompt "Symmetric multi-processing support"
|
||||
@ -201,6 +297,8 @@ config SCHED_BOOK
|
||||
Book scheduler support improves the CPU scheduler's decision making
|
||||
when dealing with machines that have several books.
|
||||
|
||||
source kernel/Kconfig.preempt
|
||||
|
||||
config MATHEMU
|
||||
def_bool y
|
||||
prompt "IEEE FPU emulation"
|
||||
@ -210,100 +308,35 @@ config MATHEMU
|
||||
on older ESA/390 machines. Say Y unless you know your machine doesn't
|
||||
need this.
|
||||
|
||||
config COMPAT
|
||||
source kernel/Kconfig.hz
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Memory setup"
|
||||
|
||||
config ARCH_SPARSEMEM_ENABLE
|
||||
def_bool y
|
||||
prompt "Kernel support for 31 bit emulation"
|
||||
depends on 64BIT
|
||||
select COMPAT_BINFMT_ELF if BINFMT_ELF
|
||||
select ARCH_WANT_OLD_COMPAT_IPC
|
||||
help
|
||||
Select this option if you want to enable your system kernel to
|
||||
handle system-calls from ELF binaries for 31 bit ESA. This option
|
||||
(and some other stuff like libraries and such) is needed for
|
||||
executing 31 bit applications. It is safe to say "Y".
|
||||
select SPARSEMEM_VMEMMAP_ENABLE
|
||||
select SPARSEMEM_VMEMMAP
|
||||
select SPARSEMEM_STATIC if !64BIT
|
||||
|
||||
config SYSVIPC_COMPAT
|
||||
def_bool y if COMPAT && SYSVIPC
|
||||
|
||||
config KEYS_COMPAT
|
||||
def_bool y if COMPAT && KEYS
|
||||
|
||||
config AUDIT_ARCH
|
||||
config ARCH_SPARSEMEM_DEFAULT
|
||||
def_bool y
|
||||
|
||||
config HAVE_MARCH_Z900_FEATURES
|
||||
def_bool n
|
||||
config ARCH_SELECT_MEMORY_MODEL
|
||||
def_bool y
|
||||
|
||||
config HAVE_MARCH_Z990_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z900_FEATURES
|
||||
config ARCH_ENABLE_MEMORY_HOTPLUG
|
||||
def_bool y if SPARSEMEM
|
||||
|
||||
config HAVE_MARCH_Z9_109_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z990_FEATURES
|
||||
config ARCH_ENABLE_MEMORY_HOTREMOVE
|
||||
def_bool y
|
||||
|
||||
config HAVE_MARCH_Z10_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z9_109_FEATURES
|
||||
config FORCE_MAX_ZONEORDER
|
||||
int
|
||||
default "9"
|
||||
|
||||
config HAVE_MARCH_Z196_FEATURES
|
||||
def_bool n
|
||||
select HAVE_MARCH_Z10_FEATURES
|
||||
|
||||
comment "Code generation options"
|
||||
|
||||
choice
|
||||
prompt "Processor type"
|
||||
default MARCH_G5
|
||||
|
||||
config MARCH_G5
|
||||
bool "System/390 model G5 and G6"
|
||||
depends on !64BIT
|
||||
help
|
||||
Select this to build a 31 bit kernel that works
|
||||
on all ESA/390 and z/Architecture machines.
|
||||
|
||||
config MARCH_Z900
|
||||
bool "IBM zSeries model z800 and z900"
|
||||
select HAVE_MARCH_Z900_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for model z800/z900 (2064 and
|
||||
2066 series). This will enable some optimizations that are not
|
||||
available on older ESA/390 (31 Bit) only CPUs.
|
||||
|
||||
config MARCH_Z990
|
||||
bool "IBM zSeries model z890 and z990"
|
||||
select HAVE_MARCH_Z990_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for model z890/z990 (2084 and
|
||||
2086 series). The kernel will be slightly faster but will not work
|
||||
on older machines.
|
||||
|
||||
config MARCH_Z9_109
|
||||
bool "IBM System z9"
|
||||
select HAVE_MARCH_Z9_109_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for IBM System z9 (2094 and
|
||||
2096 series). The kernel will be slightly faster but will not work
|
||||
on older machines.
|
||||
|
||||
config MARCH_Z10
|
||||
bool "IBM System z10"
|
||||
select HAVE_MARCH_Z10_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for IBM System z10 (2097 and
|
||||
2098 series). The kernel will be slightly faster but will not work
|
||||
on older machines.
|
||||
|
||||
config MARCH_Z196
|
||||
bool "IBM zEnterprise 114 and 196"
|
||||
select HAVE_MARCH_Z196_FEATURES if 64BIT
|
||||
help
|
||||
Select this to enable optimizations for IBM zEnterprise 114 and 196
|
||||
(2818 and 2817 series). The kernel will be slightly faster but will
|
||||
not work on older machines.
|
||||
|
||||
endchoice
|
||||
source "mm/Kconfig"
|
||||
|
||||
config PACK_STACK
|
||||
def_bool y
|
||||
@ -367,34 +400,9 @@ config WARN_DYNAMIC_STACK
|
||||
|
||||
Say N if you are unsure.
|
||||
|
||||
comment "Kernel preemption"
|
||||
endmenu
|
||||
|
||||
source "kernel/Kconfig.preempt"
|
||||
|
||||
config ARCH_SPARSEMEM_ENABLE
|
||||
def_bool y
|
||||
select SPARSEMEM_VMEMMAP_ENABLE
|
||||
select SPARSEMEM_VMEMMAP
|
||||
select SPARSEMEM_STATIC if !64BIT
|
||||
|
||||
config ARCH_SPARSEMEM_DEFAULT
|
||||
def_bool y
|
||||
|
||||
config ARCH_SELECT_MEMORY_MODEL
|
||||
def_bool y
|
||||
|
||||
config ARCH_ENABLE_MEMORY_HOTPLUG
|
||||
def_bool y if SPARSEMEM
|
||||
|
||||
config ARCH_ENABLE_MEMORY_HOTREMOVE
|
||||
def_bool y
|
||||
|
||||
config ARCH_HIBERNATION_POSSIBLE
|
||||
def_bool y if 64BIT
|
||||
|
||||
source "mm/Kconfig"
|
||||
|
||||
comment "I/O subsystem configuration"
|
||||
menu "I/O subsystem"
|
||||
|
||||
config QDIO
|
||||
def_tristate y
|
||||
@ -425,13 +433,102 @@ config CHSC_SCH
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
comment "Misc"
|
||||
config SCM_BUS
|
||||
def_bool y
|
||||
depends on 64BIT
|
||||
prompt "SCM bus driver"
|
||||
help
|
||||
Bus driver for Storage Class Memory.
|
||||
|
||||
config EADM_SCH
|
||||
def_tristate m
|
||||
prompt "Support for EADM subchannels"
|
||||
depends on SCM_BUS
|
||||
help
|
||||
This driver allows usage of EADM subchannels. EADM subchannels act
|
||||
as a communication vehicle for SCM increments.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called eadm_sch.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Dump support"
|
||||
|
||||
config CRASH_DUMP
|
||||
bool "kernel crash dumps"
|
||||
depends on 64BIT && SMP
|
||||
select KEXEC
|
||||
help
|
||||
Generate crash dump after being started by kexec.
|
||||
Crash dump kernels are loaded in the main kernel with kexec-tools
|
||||
into a specially reserved region and then later executed after
|
||||
a crash by kdump/kexec.
|
||||
For more details see Documentation/kdump/kdump.txt
|
||||
|
||||
config ZFCPDUMP
|
||||
def_bool n
|
||||
prompt "zfcpdump support"
|
||||
select SMP
|
||||
help
|
||||
Select this option if you want to build an zfcpdump enabled kernel.
|
||||
Refer to <file:Documentation/s390/zfcpdump.txt> for more details on this.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Executable file formats / Emulations"
|
||||
|
||||
source "fs/Kconfig.binfmt"
|
||||
|
||||
config FORCE_MAX_ZONEORDER
|
||||
int
|
||||
default "9"
|
||||
config SECCOMP
|
||||
def_bool y
|
||||
prompt "Enable seccomp to safely compute untrusted bytecode"
|
||||
depends on PROC_FS
|
||||
help
|
||||
This kernel feature is useful for number crunching applications
|
||||
that may need to compute untrusted bytecode during their
|
||||
execution. By using pipes or other transports made available to
|
||||
the process as file descriptors supporting the read/write
|
||||
syscalls, it's possible to isolate those applications in
|
||||
their own address space using seccomp. Once seccomp is
|
||||
enabled via /proc/<pid>/seccomp, it cannot be disabled
|
||||
and the task is only allowed to execute a few safe syscalls
|
||||
defined by each seccomp mode.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Power Management"
|
||||
|
||||
config ARCH_HIBERNATION_POSSIBLE
|
||||
def_bool y if 64BIT
|
||||
|
||||
source "kernel/power/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
source "net/Kconfig"
|
||||
|
||||
config PCMCIA
|
||||
def_bool n
|
||||
|
||||
config CCW
|
||||
def_bool y
|
||||
|
||||
source "drivers/Kconfig"
|
||||
|
||||
source "fs/Kconfig"
|
||||
|
||||
source "arch/s390/Kconfig.debug"
|
||||
|
||||
source "security/Kconfig"
|
||||
|
||||
source "crypto/Kconfig"
|
||||
|
||||
source "lib/Kconfig"
|
||||
|
||||
menu "Virtualization"
|
||||
|
||||
config PFAULT
|
||||
def_bool y
|
||||
@ -447,8 +544,8 @@ config PFAULT
|
||||
this option.
|
||||
|
||||
config SHARED_KERNEL
|
||||
def_bool y
|
||||
prompt "VM shared kernel support"
|
||||
bool "VM shared kernel support"
|
||||
depends on !JUMP_LABEL
|
||||
help
|
||||
Select this option, if you want to share the text segment of the
|
||||
Linux kernel between different VM guests. This reduces memory
|
||||
@ -543,8 +640,6 @@ config APPLDATA_NET_SUM
|
||||
This can also be compiled as a module, which will be called
|
||||
appldata_net_sum.o.
|
||||
|
||||
source kernel/Kconfig.hz
|
||||
|
||||
config S390_HYPFS_FS
|
||||
def_bool y
|
||||
prompt "s390 hypervisor file system support"
|
||||
@ -553,90 +648,21 @@ config S390_HYPFS_FS
|
||||
This is a virtual file system intended to provide accounting
|
||||
information in an s390 hypervisor environment.
|
||||
|
||||
config KEXEC
|
||||
def_bool n
|
||||
prompt "kexec system call"
|
||||
help
|
||||
kexec is a system call that implements the ability to shutdown your
|
||||
current kernel, and to start another kernel. It is like a reboot
|
||||
but is independent of hardware/microcode support.
|
||||
|
||||
config CRASH_DUMP
|
||||
bool "kernel crash dumps"
|
||||
depends on 64BIT && SMP
|
||||
select KEXEC
|
||||
help
|
||||
Generate crash dump after being started by kexec.
|
||||
Crash dump kernels are loaded in the main kernel with kexec-tools
|
||||
into a specially reserved region and then later executed after
|
||||
a crash by kdump/kexec.
|
||||
For more details see Documentation/kdump/kdump.txt
|
||||
|
||||
config ZFCPDUMP
|
||||
def_bool n
|
||||
prompt "zfcpdump support"
|
||||
select SMP
|
||||
help
|
||||
Select this option if you want to build an zfcpdump enabled kernel.
|
||||
Refer to <file:Documentation/s390/zfcpdump.txt> for more details on this.
|
||||
source "arch/s390/kvm/Kconfig"
|
||||
|
||||
config S390_GUEST
|
||||
def_bool y
|
||||
prompt "s390 guest support for KVM (EXPERIMENTAL)"
|
||||
prompt "s390 support for virtio devices (EXPERIMENTAL)"
|
||||
depends on 64BIT && EXPERIMENTAL
|
||||
select VIRTUALIZATION
|
||||
select VIRTIO
|
||||
select VIRTIO_RING
|
||||
select VIRTIO_CONSOLE
|
||||
help
|
||||
Enabling this option adds support for virtio based paravirtual device
|
||||
drivers on s390.
|
||||
|
||||
Select this option if you want to run the kernel as a guest under
|
||||
the KVM hypervisor. This will add detection for KVM as well as a
|
||||
virtio transport. If KVM is detected, the virtio console will be
|
||||
the default console.
|
||||
|
||||
config SECCOMP
|
||||
def_bool y
|
||||
prompt "Enable seccomp to safely compute untrusted bytecode"
|
||||
depends on PROC_FS
|
||||
help
|
||||
This kernel feature is useful for number crunching applications
|
||||
that may need to compute untrusted bytecode during their
|
||||
execution. By using pipes or other transports made available to
|
||||
the process as file descriptors supporting the read/write
|
||||
syscalls, it's possible to isolate those applications in
|
||||
their own address space using seccomp. Once seccomp is
|
||||
enabled via /proc/<pid>/seccomp, it cannot be disabled
|
||||
and the task is only allowed to execute a few safe syscalls
|
||||
defined by each seccomp mode.
|
||||
|
||||
If unsure, say Y.
|
||||
the KVM hypervisor.
|
||||
|
||||
endmenu
|
||||
|
||||
menu "Power Management"
|
||||
|
||||
source "kernel/power/Kconfig"
|
||||
|
||||
endmenu
|
||||
|
||||
source "net/Kconfig"
|
||||
|
||||
config PCMCIA
|
||||
def_bool n
|
||||
|
||||
config CCW
|
||||
def_bool y
|
||||
|
||||
source "drivers/Kconfig"
|
||||
|
||||
source "fs/Kconfig"
|
||||
|
||||
source "arch/s390/Kconfig.debug"
|
||||
|
||||
source "security/Kconfig"
|
||||
|
||||
source "crypto/Kconfig"
|
||||
|
||||
source "lib/Kconfig"
|
||||
|
||||
source "arch/s390/kvm/Kconfig"
|
||||
|
@ -11,6 +11,7 @@ targets := vmlinux.lds vmlinux vmlinux.bin vmlinux.bin.gz vmlinux.bin.bz2 \
|
||||
sizes.h head$(BITS).o
|
||||
|
||||
KBUILD_CFLAGS := -m$(BITS) -D__KERNEL__ $(LINUX_INCLUDE) -O2
|
||||
KBUILD_CFLAGS += -DDISABLE_BRANCH_PROFILING
|
||||
KBUILD_CFLAGS += $(cflags-y)
|
||||
KBUILD_CFLAGS += $(call cc-option,-mpacked-stack)
|
||||
KBUILD_CFLAGS += $(call cc-option,-ffreestanding)
|
||||
|
@ -71,34 +71,37 @@ void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
char *xs;
|
||||
|
||||
if (c == 0)
|
||||
return __builtin_memset(s, 0, n);
|
||||
|
||||
xs = (char *) s;
|
||||
if (n > 0)
|
||||
do {
|
||||
*xs++ = c;
|
||||
} while (--n > 0);
|
||||
xs = s;
|
||||
while (n--)
|
||||
*xs++ = c;
|
||||
return s;
|
||||
}
|
||||
|
||||
void *memcpy(void *__dest, __const void *__src, size_t __n)
|
||||
void *memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
return __builtin_memcpy(__dest, __src, __n);
|
||||
const char *s = src;
|
||||
char *d = dest;
|
||||
|
||||
while (n--)
|
||||
*d++ = *s++;
|
||||
return dest;
|
||||
}
|
||||
|
||||
void *memmove(void *__dest, __const void *__src, size_t __n)
|
||||
void *memmove(void *dest, const void *src, size_t n)
|
||||
{
|
||||
char *d;
|
||||
const char *s;
|
||||
const char *s = src;
|
||||
char *d = dest;
|
||||
|
||||
if (__dest <= __src)
|
||||
return __builtin_memcpy(__dest, __src, __n);
|
||||
d = __dest + __n;
|
||||
s = __src + __n;
|
||||
while (__n--)
|
||||
*--d = *--s;
|
||||
return __dest;
|
||||
if (d <= s) {
|
||||
while (n--)
|
||||
*d++ = *s++;
|
||||
} else {
|
||||
d += n;
|
||||
s += n;
|
||||
while (n--)
|
||||
*--d = *--s;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
static void error(char *x)
|
||||
|
@ -16,8 +16,8 @@ CONFIG_CGROUPS=y
|
||||
CONFIG_CPUSETS=y
|
||||
CONFIG_CGROUP_CPUACCT=y
|
||||
CONFIG_RESOURCE_COUNTERS=y
|
||||
CONFIG_CGROUP_MEMCG=y
|
||||
CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
|
||||
CONFIG_MEMCG=y
|
||||
CONFIG_MEMCG_SWAP=y
|
||||
CONFIG_CGROUP_SCHED=y
|
||||
CONFIG_RT_GROUP_SCHED=y
|
||||
CONFIG_BLK_CGROUP=y
|
||||
@ -32,20 +32,19 @@ CONFIG_EXPERT=y
|
||||
CONFIG_PROFILING=y
|
||||
CONFIG_OPROFILE=y
|
||||
CONFIG_KPROBES=y
|
||||
CONFIG_JUMP_LABEL=y
|
||||
CONFIG_MODULES=y
|
||||
CONFIG_MODULE_UNLOAD=y
|
||||
CONFIG_MODVERSIONS=y
|
||||
CONFIG_PARTITION_ADVANCED=y
|
||||
CONFIG_IBM_PARTITION=y
|
||||
CONFIG_DEFAULT_DEADLINE=y
|
||||
CONFIG_PREEMPT=y
|
||||
CONFIG_HZ_100=y
|
||||
CONFIG_MEMORY_HOTPLUG=y
|
||||
CONFIG_MEMORY_HOTREMOVE=y
|
||||
CONFIG_KSM=y
|
||||
CONFIG_BINFMT_MISC=m
|
||||
CONFIG_CMM=m
|
||||
CONFIG_HZ_100=y
|
||||
CONFIG_CRASH_DUMP=y
|
||||
CONFIG_BINFMT_MISC=m
|
||||
CONFIG_HIBERNATION=y
|
||||
CONFIG_PACKET=y
|
||||
CONFIG_UNIX=y
|
||||
@ -75,6 +74,7 @@ CONFIG_NET_CLS_RSVP=m
|
||||
CONFIG_NET_CLS_RSVP6=m
|
||||
CONFIG_NET_CLS_ACT=y
|
||||
CONFIG_NET_ACT_POLICE=y
|
||||
CONFIG_BPF_JIT=y
|
||||
CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
|
||||
CONFIG_DEVTMPFS=y
|
||||
CONFIG_BLK_DEV_LOOP=m
|
||||
@ -121,7 +121,6 @@ CONFIG_DEBUG_NOTIFIERS=y
|
||||
CONFIG_RCU_TRACE=y
|
||||
CONFIG_KPROBES_SANITY_TEST=y
|
||||
CONFIG_DEBUG_FORCE_WEAK_PER_CPU=y
|
||||
CONFIG_CPU_NOTIFIER_ERROR_INJECT=m
|
||||
CONFIG_LATENCYTOP=y
|
||||
CONFIG_DEBUG_PAGEALLOC=y
|
||||
CONFIG_BLK_DEV_IO_TRACE=y
|
||||
@ -173,3 +172,4 @@ CONFIG_CRYPTO_SHA512_S390=m
|
||||
CONFIG_CRYPTO_DES_S390=m
|
||||
CONFIG_CRYPTO_AES_S390=m
|
||||
CONFIG_CRC7=m
|
||||
CONFIG_CMM=m
|
||||
|
@ -70,7 +70,7 @@ static inline int appldata_asm(struct appldata_product_id *id,
|
||||
int ry;
|
||||
|
||||
if (!MACHINE_IS_VM)
|
||||
return -ENOSYS;
|
||||
return -EOPNOTSUPP;
|
||||
parm_list.diag = 0xdc;
|
||||
parm_list.function = fn;
|
||||
parm_list.parlist_length = sizeof(parm_list);
|
||||
|
@ -125,32 +125,4 @@ struct chsc_cpd_info {
|
||||
#define CHSC_INFO_CPD _IOWR(CHSC_IOCTL_MAGIC, 0x87, struct chsc_cpd_info)
|
||||
#define CHSC_INFO_DCAL _IOWR(CHSC_IOCTL_MAGIC, 0x88, struct chsc_dcal)
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct css_general_char {
|
||||
u64 : 12;
|
||||
u32 dynio : 1; /* bit 12 */
|
||||
u32 : 28;
|
||||
u32 aif : 1; /* bit 41 */
|
||||
u32 : 3;
|
||||
u32 mcss : 1; /* bit 45 */
|
||||
u32 fcs : 1; /* bit 46 */
|
||||
u32 : 1;
|
||||
u32 ext_mb : 1; /* bit 48 */
|
||||
u32 : 7;
|
||||
u32 aif_tdd : 1; /* bit 56 */
|
||||
u32 : 1;
|
||||
u32 qebsm : 1; /* bit 58 */
|
||||
u32 : 8;
|
||||
u32 aif_osa : 1; /* bit 67 */
|
||||
u32 : 14;
|
||||
u32 cib : 1; /* bit 82 */
|
||||
u32 : 5;
|
||||
u32 fcx : 1; /* bit 88 */
|
||||
u32 : 7;
|
||||
}__attribute__((packed));
|
||||
|
||||
extern struct css_general_char css_general_characteristics;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif
|
||||
|
@ -79,6 +79,18 @@ struct erw {
|
||||
__u32 res16 : 16;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* struct erw_eadm - EADM Subchannel extended report word
|
||||
* @b: aob error
|
||||
* @r: arsb error
|
||||
*/
|
||||
struct erw_eadm {
|
||||
__u32 : 16;
|
||||
__u32 b : 1;
|
||||
__u32 r : 1;
|
||||
__u32 : 14;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct sublog - subchannel logout area
|
||||
* @res0: reserved
|
||||
@ -169,10 +181,23 @@ struct esw3 {
|
||||
__u32 zeros[3];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* struct esw_eadm - EADM Subchannel Extended Status Word (ESW)
|
||||
* @sublog: subchannel logout
|
||||
* @erw: extended report word
|
||||
*/
|
||||
struct esw_eadm {
|
||||
__u32 sublog;
|
||||
struct erw_eadm erw;
|
||||
__u32 : 32;
|
||||
__u32 : 32;
|
||||
__u32 : 32;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct irb - interruption response block
|
||||
* @scsw: subchannel status word
|
||||
* @esw: extened status word, 4 formats
|
||||
* @esw: extened status word
|
||||
* @ecw: extended control word
|
||||
*
|
||||
* The irb that is handed to the device driver when an interrupt occurs. For
|
||||
@ -191,6 +216,7 @@ struct irb {
|
||||
struct esw1 esw1;
|
||||
struct esw2 esw2;
|
||||
struct esw3 esw3;
|
||||
struct esw_eadm eadm;
|
||||
} esw;
|
||||
__u8 ecw[32];
|
||||
} __attribute__ ((packed,aligned(4)));
|
||||
|
@ -7,7 +7,9 @@
|
||||
#ifndef __ASM_CMPXCHG_H
|
||||
#define __ASM_CMPXCHG_H
|
||||
|
||||
#include <linux/mmdebug.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bug.h>
|
||||
|
||||
extern void __xchg_called_with_bad_pointer(void);
|
||||
|
||||
@ -203,6 +205,65 @@ static inline unsigned long long __cmpxchg64(void *ptr,
|
||||
})
|
||||
#endif /* CONFIG_64BIT */
|
||||
|
||||
#define __cmpxchg_double_op(p1, p2, o1, o2, n1, n2, insn) \
|
||||
({ \
|
||||
register __typeof__(*(p1)) __old1 asm("2") = (o1); \
|
||||
register __typeof__(*(p2)) __old2 asm("3") = (o2); \
|
||||
register __typeof__(*(p1)) __new1 asm("4") = (n1); \
|
||||
register __typeof__(*(p2)) __new2 asm("5") = (n2); \
|
||||
int cc; \
|
||||
asm volatile( \
|
||||
insn " %[old],%[new],%[ptr]\n" \
|
||||
" ipm %[cc]\n" \
|
||||
" srl %[cc],28" \
|
||||
: [cc] "=d" (cc), [old] "+d" (__old1), "+d" (__old2) \
|
||||
: [new] "d" (__new1), "d" (__new2), \
|
||||
[ptr] "Q" (*(p1)), "Q" (*(p2)) \
|
||||
: "memory", "cc"); \
|
||||
!cc; \
|
||||
})
|
||||
|
||||
#define __cmpxchg_double_4(p1, p2, o1, o2, n1, n2) \
|
||||
__cmpxchg_double_op(p1, p2, o1, o2, n1, n2, "cds")
|
||||
|
||||
#define __cmpxchg_double_8(p1, p2, o1, o2, n1, n2) \
|
||||
__cmpxchg_double_op(p1, p2, o1, o2, n1, n2, "cdsg")
|
||||
|
||||
extern void __cmpxchg_double_called_with_bad_pointer(void);
|
||||
|
||||
#define __cmpxchg_double(p1, p2, o1, o2, n1, n2) \
|
||||
({ \
|
||||
int __ret; \
|
||||
switch (sizeof(*(p1))) { \
|
||||
case 4: \
|
||||
__ret = __cmpxchg_double_4(p1, p2, o1, o2, n1, n2); \
|
||||
break; \
|
||||
case 8: \
|
||||
__ret = __cmpxchg_double_8(p1, p2, o1, o2, n1, n2); \
|
||||
break; \
|
||||
default: \
|
||||
__cmpxchg_double_called_with_bad_pointer(); \
|
||||
} \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define cmpxchg_double(p1, p2, o1, o2, n1, n2) \
|
||||
({ \
|
||||
__typeof__(p1) __p1 = (p1); \
|
||||
__typeof__(p2) __p2 = (p2); \
|
||||
int __ret; \
|
||||
BUILD_BUG_ON(sizeof(*(p1)) != sizeof(long)); \
|
||||
BUILD_BUG_ON(sizeof(*(p2)) != sizeof(long)); \
|
||||
VM_BUG_ON((unsigned long)((__p1) + 1) != (unsigned long)(__p2));\
|
||||
if (sizeof(long) == 4) \
|
||||
__ret = __cmpxchg_double_4(__p1, __p2, o1, o2, n1, n2); \
|
||||
else \
|
||||
__ret = __cmpxchg_double_8(__p1, __p2, o1, o2, n1, n2); \
|
||||
__ret; \
|
||||
})
|
||||
|
||||
#define system_has_cmpxchg_double() 1
|
||||
|
||||
#include <asm-generic/cmpxchg-local.h>
|
||||
|
||||
static inline unsigned long __cmpxchg_local(void *ptr,
|
||||
|
@ -21,11 +21,15 @@
|
||||
#define CPU_MF_INT_SF_LSDA (1 << 22) /* loss of sample data alert */
|
||||
#define CPU_MF_INT_CF_CACA (1 << 7) /* counter auth. change alert */
|
||||
#define CPU_MF_INT_CF_LCDA (1 << 6) /* loss of counter data alert */
|
||||
#define CPU_MF_INT_RI_HALTED (1 << 5) /* run-time instr. halted */
|
||||
#define CPU_MF_INT_RI_BUF_FULL (1 << 4) /* run-time instr. program
|
||||
buffer full */
|
||||
|
||||
#define CPU_MF_INT_CF_MASK (CPU_MF_INT_CF_CACA|CPU_MF_INT_CF_LCDA)
|
||||
#define CPU_MF_INT_SF_MASK (CPU_MF_INT_SF_IAE|CPU_MF_INT_SF_ISE| \
|
||||
CPU_MF_INT_SF_PRA|CPU_MF_INT_SF_SACA| \
|
||||
CPU_MF_INT_SF_LSDA)
|
||||
#define CPU_MF_INT_RI_MASK (CPU_MF_INT_RI_HALTED|CPU_MF_INT_RI_BUF_FULL)
|
||||
|
||||
/* CPU measurement facility support */
|
||||
static inline int cpum_cf_avail(void)
|
||||
|
39
arch/s390/include/asm/css_chars.h
Normal file
39
arch/s390/include/asm/css_chars.h
Normal file
@ -0,0 +1,39 @@
|
||||
#ifndef _ASM_CSS_CHARS_H
|
||||
#define _ASM_CSS_CHARS_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
struct css_general_char {
|
||||
u64 : 12;
|
||||
u32 dynio : 1; /* bit 12 */
|
||||
u32 : 4;
|
||||
u32 eadm : 1; /* bit 17 */
|
||||
u32 : 23;
|
||||
u32 aif : 1; /* bit 41 */
|
||||
u32 : 3;
|
||||
u32 mcss : 1; /* bit 45 */
|
||||
u32 fcs : 1; /* bit 46 */
|
||||
u32 : 1;
|
||||
u32 ext_mb : 1; /* bit 48 */
|
||||
u32 : 7;
|
||||
u32 aif_tdd : 1; /* bit 56 */
|
||||
u32 : 1;
|
||||
u32 qebsm : 1; /* bit 58 */
|
||||
u32 : 8;
|
||||
u32 aif_osa : 1; /* bit 67 */
|
||||
u32 : 12;
|
||||
u32 eadm_rf : 1; /* bit 80 */
|
||||
u32 : 1;
|
||||
u32 cib : 1; /* bit 82 */
|
||||
u32 : 5;
|
||||
u32 fcx : 1; /* bit 88 */
|
||||
u32 : 19;
|
||||
u32 alt_ssi : 1; /* bit 108 */
|
||||
} __packed;
|
||||
|
||||
extern struct css_general_char css_general_characteristics;
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
#endif
|
124
arch/s390/include/asm/eadm.h
Normal file
124
arch/s390/include/asm/eadm.h
Normal file
@ -0,0 +1,124 @@
|
||||
#ifndef _ASM_S390_EADM_H
|
||||
#define _ASM_S390_EADM_H
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
|
||||
struct arqb {
|
||||
u64 data;
|
||||
u16 fmt:4;
|
||||
u16:12;
|
||||
u16 cmd_code;
|
||||
u16:16;
|
||||
u16 msb_count;
|
||||
u32 reserved[12];
|
||||
} __packed;
|
||||
|
||||
#define ARQB_CMD_MOVE 1
|
||||
|
||||
struct arsb {
|
||||
u16 fmt:4;
|
||||
u32:28;
|
||||
u8 ef;
|
||||
u8:8;
|
||||
u8 ecbi;
|
||||
u8:8;
|
||||
u8 fvf;
|
||||
u16:16;
|
||||
u8 eqc;
|
||||
u32:32;
|
||||
u64 fail_msb;
|
||||
u64 fail_aidaw;
|
||||
u64 fail_ms;
|
||||
u64 fail_scm;
|
||||
u32 reserved[4];
|
||||
} __packed;
|
||||
|
||||
struct msb {
|
||||
u8 fmt:4;
|
||||
u8 oc:4;
|
||||
u8 flags;
|
||||
u16:12;
|
||||
u16 bs:4;
|
||||
u32 blk_count;
|
||||
u64 data_addr;
|
||||
u64 scm_addr;
|
||||
u64:64;
|
||||
} __packed;
|
||||
|
||||
struct aidaw {
|
||||
u8 flags;
|
||||
u32 :24;
|
||||
u32 :32;
|
||||
u64 data_addr;
|
||||
} __packed;
|
||||
|
||||
#define MSB_OC_CLEAR 0
|
||||
#define MSB_OC_READ 1
|
||||
#define MSB_OC_WRITE 2
|
||||
#define MSB_OC_RELEASE 3
|
||||
|
||||
#define MSB_FLAG_BNM 0x80
|
||||
#define MSB_FLAG_IDA 0x40
|
||||
|
||||
#define MSB_BS_4K 0
|
||||
#define MSB_BS_1M 1
|
||||
|
||||
#define AOB_NR_MSB 124
|
||||
|
||||
struct aob {
|
||||
struct arqb request;
|
||||
struct arsb response;
|
||||
struct msb msb[AOB_NR_MSB];
|
||||
} __packed __aligned(PAGE_SIZE);
|
||||
|
||||
struct aob_rq_header {
|
||||
struct scm_device *scmdev;
|
||||
char data[0];
|
||||
};
|
||||
|
||||
struct scm_device {
|
||||
u64 address;
|
||||
u64 size;
|
||||
unsigned int nr_max_block;
|
||||
struct device dev;
|
||||
struct {
|
||||
unsigned int persistence:4;
|
||||
unsigned int oper_state:4;
|
||||
unsigned int data_state:4;
|
||||
unsigned int rank:4;
|
||||
unsigned int release:1;
|
||||
unsigned int res_id:8;
|
||||
} __packed attrs;
|
||||
};
|
||||
|
||||
#define OP_STATE_GOOD 1
|
||||
#define OP_STATE_TEMP_ERR 2
|
||||
#define OP_STATE_PERM_ERR 3
|
||||
|
||||
struct scm_driver {
|
||||
struct device_driver drv;
|
||||
int (*probe) (struct scm_device *scmdev);
|
||||
int (*remove) (struct scm_device *scmdev);
|
||||
void (*notify) (struct scm_device *scmdev);
|
||||
void (*handler) (struct scm_device *scmdev, void *data, int error);
|
||||
};
|
||||
|
||||
int scm_driver_register(struct scm_driver *scmdrv);
|
||||
void scm_driver_unregister(struct scm_driver *scmdrv);
|
||||
|
||||
int scm_start_aob(struct aob *aob);
|
||||
void scm_irq_handler(struct aob *aob, int error);
|
||||
|
||||
struct eadm_ops {
|
||||
int (*eadm_start) (struct aob *aob);
|
||||
struct module *owner;
|
||||
};
|
||||
|
||||
int scm_get_ref(void);
|
||||
void scm_put_ref(void);
|
||||
|
||||
void register_eadm_ops(struct eadm_ops *ops);
|
||||
void unregister_eadm_ops(struct eadm_ops *ops);
|
||||
|
||||
#endif /* _ASM_S390_EADM_H */
|
@ -101,6 +101,7 @@
|
||||
#define HWCAP_S390_HPAGE 128
|
||||
#define HWCAP_S390_ETF3EH 256
|
||||
#define HWCAP_S390_HIGH_GPRS 512
|
||||
#define HWCAP_S390_TE 1024
|
||||
|
||||
/*
|
||||
* These are used to set parameters in the core dumps.
|
||||
@ -212,4 +213,6 @@ int arch_setup_additional_pages(struct linux_binprm *, int);
|
||||
extern unsigned long arch_randomize_brk(struct mm_struct *mm);
|
||||
#define arch_randomize_brk arch_randomize_brk
|
||||
|
||||
void *fill_cpu_elf_notes(void *ptr, struct save_area *sa);
|
||||
|
||||
#endif
|
||||
|
@ -140,7 +140,7 @@ struct etr_ptff_qto {
|
||||
/* Inline assembly helper functions */
|
||||
static inline int etr_setr(struct etr_eacr *ctrl)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
int rc = -EOPNOTSUPP;
|
||||
|
||||
asm volatile(
|
||||
" .insn s,0xb2160000,%1\n"
|
||||
@ -154,7 +154,7 @@ static inline int etr_setr(struct etr_eacr *ctrl)
|
||||
/* Stores a format 1 aib with 64 bytes */
|
||||
static inline int etr_stetr(struct etr_aib *aib)
|
||||
{
|
||||
int rc = -ENOSYS;
|
||||
int rc = -EOPNOTSUPP;
|
||||
|
||||
asm volatile(
|
||||
" .insn s,0xb2170000,%1\n"
|
||||
@ -169,7 +169,7 @@ static inline int etr_stetr(struct etr_aib *aib)
|
||||
static inline int etr_steai(struct etr_aib *aib, unsigned int func)
|
||||
{
|
||||
register unsigned int reg0 asm("0") = func;
|
||||
int rc = -ENOSYS;
|
||||
int rc = -EOPNOTSUPP;
|
||||
|
||||
asm volatile(
|
||||
" .insn s,0xb2b30000,%1\n"
|
||||
@ -190,7 +190,7 @@ static inline int etr_ptff(void *ptff_block, unsigned int func)
|
||||
{
|
||||
register unsigned int reg0 asm("0") = func;
|
||||
register unsigned long reg1 asm("1") = (unsigned long) ptff_block;
|
||||
int rc = -ENOSYS;
|
||||
int rc = -EOPNOTSUPP;
|
||||
|
||||
asm volatile(
|
||||
" .word 0x0104\n"
|
||||
|
@ -19,6 +19,7 @@ enum interruption_class {
|
||||
EXTINT_IUC,
|
||||
EXTINT_CMS,
|
||||
EXTINT_CMC,
|
||||
EXTINT_CMR,
|
||||
IOINT_CIO,
|
||||
IOINT_QAI,
|
||||
IOINT_DAS,
|
||||
@ -30,6 +31,7 @@ enum interruption_class {
|
||||
IOINT_CLW,
|
||||
IOINT_CTC,
|
||||
IOINT_APB,
|
||||
IOINT_ADM,
|
||||
IOINT_CSC,
|
||||
NMI_NMI,
|
||||
NR_IRQS,
|
||||
|
@ -14,6 +14,7 @@
|
||||
/* Regular I/O interrupts. */
|
||||
#define IO_SCH_ISC 3 /* regular I/O subchannels */
|
||||
#define CONSOLE_ISC 1 /* console I/O subchannel */
|
||||
#define EADM_SCH_ISC 4 /* EADM subchannels */
|
||||
#define CHSC_SCH_ISC 7 /* CHSC subchannels */
|
||||
/* Adapter interrupts. */
|
||||
#define QDIO_AIRQ_ISC IO_SCH_ISC /* I/O subchannel in qdio mode */
|
||||
|
@ -329,9 +329,13 @@ struct _lowcore {
|
||||
__u8 pad_0x1338[0x1340-0x1338]; /* 0x1338 */
|
||||
__u32 access_regs_save_area[16]; /* 0x1340 */
|
||||
__u64 cregs_save_area[16]; /* 0x1380 */
|
||||
__u8 pad_0x1400[0x1800-0x1400]; /* 0x1400 */
|
||||
|
||||
/* Transaction abort diagnostic block */
|
||||
__u8 pgm_tdb[256]; /* 0x1800 */
|
||||
|
||||
/* align to the top of the prefix area */
|
||||
__u8 pad_0x1400[0x2000-0x1400]; /* 0x1400 */
|
||||
__u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */
|
||||
} __packed;
|
||||
|
||||
#endif /* CONFIG_32BIT */
|
||||
|
@ -57,7 +57,7 @@ static inline void update_mm(struct mm_struct *mm, struct task_struct *tsk)
|
||||
pgd_t *pgd = mm->pgd;
|
||||
|
||||
S390_lowcore.user_asce = mm->context.asce_bits | __pa(pgd);
|
||||
if (addressing_mode != HOME_SPACE_MODE) {
|
||||
if (s390_user_mode != HOME_SPACE_MODE) {
|
||||
/* Load primary space page table origin. */
|
||||
asm volatile(LCTL_OPCODE" 1,1,%0\n"
|
||||
: : "m" (S390_lowcore.user_asce) );
|
||||
|
@ -20,7 +20,7 @@
|
||||
#endif
|
||||
|
||||
#define arch_this_cpu_to_op(pcp, val, op) \
|
||||
do { \
|
||||
({ \
|
||||
typedef typeof(pcp) pcp_op_T__; \
|
||||
pcp_op_T__ old__, new__, prev__; \
|
||||
pcp_op_T__ *ptr__; \
|
||||
@ -39,13 +39,19 @@ do { \
|
||||
} \
|
||||
} while (prev__ != old__); \
|
||||
preempt_enable(); \
|
||||
} while (0)
|
||||
new__; \
|
||||
})
|
||||
|
||||
#define this_cpu_add_1(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
#define this_cpu_add_2(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
#define this_cpu_add_4(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
#define this_cpu_add_8(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
|
||||
#define this_cpu_add_return_1(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
#define this_cpu_add_return_2(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
#define this_cpu_add_return_4(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
#define this_cpu_add_return_8(pcp, val) arch_this_cpu_to_op(pcp, val, +)
|
||||
|
||||
#define this_cpu_and_1(pcp, val) arch_this_cpu_to_op(pcp, val, &)
|
||||
#define this_cpu_and_2(pcp, val) arch_this_cpu_to_op(pcp, val, &)
|
||||
#define this_cpu_and_4(pcp, val) arch_this_cpu_to_op(pcp, val, &)
|
||||
@ -61,7 +67,7 @@ do { \
|
||||
#define this_cpu_xor_4(pcp, val) arch_this_cpu_to_op(pcp, val, ^)
|
||||
#define this_cpu_xor_8(pcp, val) arch_this_cpu_to_op(pcp, val, ^)
|
||||
|
||||
#define arch_this_cpu_cmpxchg(pcp, oval, nval) \
|
||||
#define arch_this_cpu_cmpxchg(pcp, oval, nval) \
|
||||
({ \
|
||||
typedef typeof(pcp) pcp_op_T__; \
|
||||
pcp_op_T__ ret__; \
|
||||
@ -84,6 +90,44 @@ do { \
|
||||
#define this_cpu_cmpxchg_4(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
|
||||
#define this_cpu_cmpxchg_8(pcp, oval, nval) arch_this_cpu_cmpxchg(pcp, oval, nval)
|
||||
|
||||
#define arch_this_cpu_xchg(pcp, nval) \
|
||||
({ \
|
||||
typeof(pcp) *ptr__; \
|
||||
typeof(pcp) ret__; \
|
||||
preempt_disable(); \
|
||||
ptr__ = __this_cpu_ptr(&(pcp)); \
|
||||
ret__ = xchg(ptr__, nval); \
|
||||
preempt_enable(); \
|
||||
ret__; \
|
||||
})
|
||||
|
||||
#define this_cpu_xchg_1(pcp, nval) arch_this_cpu_xchg(pcp, nval)
|
||||
#define this_cpu_xchg_2(pcp, nval) arch_this_cpu_xchg(pcp, nval)
|
||||
#define this_cpu_xchg_4(pcp, nval) arch_this_cpu_xchg(pcp, nval)
|
||||
#ifdef CONFIG_64BIT
|
||||
#define this_cpu_xchg_8(pcp, nval) arch_this_cpu_xchg(pcp, nval)
|
||||
#endif
|
||||
|
||||
#define arch_this_cpu_cmpxchg_double(pcp1, pcp2, o1, o2, n1, n2) \
|
||||
({ \
|
||||
typeof(pcp1) o1__ = (o1), n1__ = (n1); \
|
||||
typeof(pcp2) o2__ = (o2), n2__ = (n2); \
|
||||
typeof(pcp1) *p1__; \
|
||||
typeof(pcp2) *p2__; \
|
||||
int ret__; \
|
||||
preempt_disable(); \
|
||||
p1__ = __this_cpu_ptr(&(pcp1)); \
|
||||
p2__ = __this_cpu_ptr(&(pcp2)); \
|
||||
ret__ = __cmpxchg_double(p1__, p2__, o1__, o2__, n1__, n2__); \
|
||||
preempt_enable(); \
|
||||
ret__; \
|
||||
})
|
||||
|
||||
#define this_cpu_cmpxchg_double_4 arch_this_cpu_cmpxchg_double
|
||||
#ifdef CONFIG_64BIT
|
||||
#define this_cpu_cmpxchg_double_8 arch_this_cpu_cmpxchg_double
|
||||
#endif
|
||||
|
||||
#include <asm-generic/percpu.h>
|
||||
|
||||
#endif /* __ARCH_S390_PERCPU__ */
|
||||
|
@ -11,12 +11,15 @@
|
||||
#ifndef __ASM_S390_PROCESSOR_H
|
||||
#define __ASM_S390_PROCESSOR_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <linux/irqflags.h>
|
||||
#include <asm/cpu.h>
|
||||
#include <asm/page.h>
|
||||
#include <asm/ptrace.h>
|
||||
#include <asm/setup.h>
|
||||
#include <asm/runtime_instr.h>
|
||||
|
||||
/*
|
||||
* Default implementation of macro that returns current
|
||||
@ -75,11 +78,20 @@ struct thread_struct {
|
||||
unsigned long gmap_addr; /* address of last gmap fault. */
|
||||
struct per_regs per_user; /* User specified PER registers */
|
||||
struct per_event per_event; /* Cause of the last PER trap */
|
||||
unsigned long per_flags; /* Flags to control debug behavior */
|
||||
/* pfault_wait is used to block the process on a pfault event */
|
||||
unsigned long pfault_wait;
|
||||
struct list_head list;
|
||||
/* cpu runtime instrumentation */
|
||||
struct runtime_instr_cb *ri_cb;
|
||||
int ri_signum;
|
||||
#ifdef CONFIG_64BIT
|
||||
unsigned char trap_tdb[256]; /* Transaction abort diagnose block */
|
||||
#endif
|
||||
};
|
||||
|
||||
#define PER_FLAG_NO_TE 1UL /* Flag to disable transactions. */
|
||||
|
||||
typedef struct thread_struct thread_struct;
|
||||
|
||||
/*
|
||||
@ -130,6 +142,12 @@ struct task_struct;
|
||||
struct mm_struct;
|
||||
struct seq_file;
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
extern void show_cacheinfo(struct seq_file *m);
|
||||
#else
|
||||
static inline void show_cacheinfo(struct seq_file *m) { }
|
||||
#endif
|
||||
|
||||
/* Free all resources held by a thread. */
|
||||
extern void release_thread(struct task_struct *);
|
||||
extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
|
||||
@ -140,6 +158,7 @@ extern int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags);
|
||||
extern unsigned long thread_saved_pc(struct task_struct *t);
|
||||
|
||||
extern void show_code(struct pt_regs *regs);
|
||||
extern void print_fn_code(unsigned char *code, unsigned long len);
|
||||
|
||||
unsigned long get_wchan(struct task_struct *p);
|
||||
#define task_pt_regs(tsk) ((struct pt_regs *) \
|
||||
@ -331,23 +350,6 @@ extern void (*s390_base_ext_handler_fn)(void);
|
||||
|
||||
#define ARCH_LOW_ADDRESS_LIMIT 0x7fffffffUL
|
||||
|
||||
/*
|
||||
* Helper macro for exception table entries
|
||||
*/
|
||||
#ifndef CONFIG_64BIT
|
||||
#define EX_TABLE(_fault,_target) \
|
||||
".section __ex_table,\"a\"\n" \
|
||||
" .align 4\n" \
|
||||
" .long " #_fault "," #_target "\n" \
|
||||
".previous\n"
|
||||
#else
|
||||
#define EX_TABLE(_fault,_target) \
|
||||
".section __ex_table,\"a\"\n" \
|
||||
" .align 8\n" \
|
||||
" .quad " #_fault "," #_target "\n" \
|
||||
".previous\n"
|
||||
#endif
|
||||
|
||||
extern int memcpy_real(void *, void *, size_t);
|
||||
extern void memcpy_absolute(void *, void *, size_t);
|
||||
|
||||
@ -358,4 +360,25 @@ extern void memcpy_absolute(void *, void *, size_t);
|
||||
memcpy_absolute(&(dest), &__tmp, sizeof(__tmp)); \
|
||||
}
|
||||
|
||||
#endif /* __ASM_S390_PROCESSOR_H */
|
||||
/*
|
||||
* Helper macro for exception table entries
|
||||
*/
|
||||
#define EX_TABLE(_fault, _target) \
|
||||
".section __ex_table,\"a\"\n" \
|
||||
".align 4\n" \
|
||||
".long (" #_fault ") - .\n" \
|
||||
".long (" #_target ") - .\n" \
|
||||
".previous\n"
|
||||
|
||||
#else /* __ASSEMBLY__ */
|
||||
|
||||
#define EX_TABLE(_fault, _target) \
|
||||
.section __ex_table,"a" ; \
|
||||
.align 4 ; \
|
||||
.long (_fault) - . ; \
|
||||
.long (_target) - . ; \
|
||||
.previous
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
|
||||
#endif /* __ASM_S390_PROCESSOR_H */
|
||||
|
@ -235,6 +235,7 @@ typedef struct
|
||||
#define PSW_MASK_ASC 0x0000C000UL
|
||||
#define PSW_MASK_CC 0x00003000UL
|
||||
#define PSW_MASK_PM 0x00000F00UL
|
||||
#define PSW_MASK_RI 0x00000000UL
|
||||
#define PSW_MASK_EA 0x00000000UL
|
||||
#define PSW_MASK_BA 0x00000000UL
|
||||
|
||||
@ -264,10 +265,11 @@ typedef struct
|
||||
#define PSW_MASK_ASC 0x0000C00000000000UL
|
||||
#define PSW_MASK_CC 0x0000300000000000UL
|
||||
#define PSW_MASK_PM 0x00000F0000000000UL
|
||||
#define PSW_MASK_RI 0x0000008000000000UL
|
||||
#define PSW_MASK_EA 0x0000000100000000UL
|
||||
#define PSW_MASK_BA 0x0000000080000000UL
|
||||
|
||||
#define PSW_MASK_USER 0x00003F0180000000UL
|
||||
#define PSW_MASK_USER 0x00003F8180000000UL
|
||||
|
||||
#define PSW_ADDR_AMODE 0x0000000000000000UL
|
||||
#define PSW_ADDR_INSN 0xFFFFFFFFFFFFFFFFUL
|
||||
@ -359,17 +361,19 @@ struct per_struct_kernel {
|
||||
unsigned char access_id; /* PER trap access identification */
|
||||
};
|
||||
|
||||
#define PER_EVENT_MASK 0xE9000000UL
|
||||
#define PER_EVENT_MASK 0xEB000000UL
|
||||
|
||||
#define PER_EVENT_BRANCH 0x80000000UL
|
||||
#define PER_EVENT_IFETCH 0x40000000UL
|
||||
#define PER_EVENT_STORE 0x20000000UL
|
||||
#define PER_EVENT_STORE_REAL 0x08000000UL
|
||||
#define PER_EVENT_TRANSACTION_END 0x02000000UL
|
||||
#define PER_EVENT_NULLIFICATION 0x01000000UL
|
||||
|
||||
#define PER_CONTROL_MASK 0x00a00000UL
|
||||
#define PER_CONTROL_MASK 0x00e00000UL
|
||||
|
||||
#define PER_CONTROL_BRANCH_ADDRESS 0x00800000UL
|
||||
#define PER_CONTROL_SUSPENSION 0x00400000UL
|
||||
#define PER_CONTROL_ALTERATION 0x00200000UL
|
||||
|
||||
#endif
|
||||
@ -483,6 +487,8 @@ typedef struct
|
||||
#define PTRACE_GET_LAST_BREAK 0x5006
|
||||
#define PTRACE_PEEK_SYSTEM_CALL 0x5007
|
||||
#define PTRACE_POKE_SYSTEM_CALL 0x5008
|
||||
#define PTRACE_ENABLE_TE 0x5009
|
||||
#define PTRACE_DISABLE_TE 0x5010
|
||||
|
||||
/*
|
||||
* PT_PROT definition is loosely based on hppa bsd definition in
|
||||
|
98
arch/s390/include/asm/runtime_instr.h
Normal file
98
arch/s390/include/asm/runtime_instr.h
Normal file
@ -0,0 +1,98 @@
|
||||
#ifndef _RUNTIME_INSTR_H
|
||||
#define _RUNTIME_INSTR_H
|
||||
|
||||
#define S390_RUNTIME_INSTR_START 0x1
|
||||
#define S390_RUNTIME_INSTR_STOP 0x2
|
||||
|
||||
struct runtime_instr_cb {
|
||||
__u64 buf_current;
|
||||
__u64 buf_origin;
|
||||
__u64 buf_limit;
|
||||
|
||||
__u32 valid : 1;
|
||||
__u32 pstate : 1;
|
||||
__u32 pstate_set_buf : 1;
|
||||
__u32 home_space : 1;
|
||||
__u32 altered : 1;
|
||||
__u32 : 3;
|
||||
__u32 pstate_sample : 1;
|
||||
__u32 sstate_sample : 1;
|
||||
__u32 pstate_collect : 1;
|
||||
__u32 sstate_collect : 1;
|
||||
__u32 : 1;
|
||||
__u32 halted_int : 1;
|
||||
__u32 int_requested : 1;
|
||||
__u32 buffer_full_int : 1;
|
||||
__u32 key : 4;
|
||||
__u32 : 9;
|
||||
__u32 rgs : 3;
|
||||
|
||||
__u32 mode : 4;
|
||||
__u32 next : 1;
|
||||
__u32 mae : 1;
|
||||
__u32 : 2;
|
||||
__u32 call_type_br : 1;
|
||||
__u32 return_type_br : 1;
|
||||
__u32 other_type_br : 1;
|
||||
__u32 bc_other_type : 1;
|
||||
__u32 emit : 1;
|
||||
__u32 tx_abort : 1;
|
||||
__u32 : 2;
|
||||
__u32 bp_xn : 1;
|
||||
__u32 bp_xt : 1;
|
||||
__u32 bp_ti : 1;
|
||||
__u32 bp_ni : 1;
|
||||
__u32 suppr_y : 1;
|
||||
__u32 suppr_z : 1;
|
||||
|
||||
__u32 dc_miss_extra : 1;
|
||||
__u32 lat_lev_ignore : 1;
|
||||
__u32 ic_lat_lev : 4;
|
||||
__u32 dc_lat_lev : 4;
|
||||
|
||||
__u64 reserved1;
|
||||
__u64 scaling_factor;
|
||||
__u64 rsic;
|
||||
__u64 reserved2;
|
||||
} __packed __aligned(8);
|
||||
|
||||
extern struct runtime_instr_cb runtime_instr_empty_cb;
|
||||
|
||||
static inline void load_runtime_instr_cb(struct runtime_instr_cb *cb)
|
||||
{
|
||||
asm volatile(".insn rsy,0xeb0000000060,0,0,%0" /* LRIC */
|
||||
: : "Q" (*cb));
|
||||
}
|
||||
|
||||
static inline void store_runtime_instr_cb(struct runtime_instr_cb *cb)
|
||||
{
|
||||
asm volatile(".insn rsy,0xeb0000000061,0,0,%0" /* STRIC */
|
||||
: "=Q" (*cb) : : "cc");
|
||||
}
|
||||
|
||||
static inline void save_ri_cb(struct runtime_instr_cb *cb_prev)
|
||||
{
|
||||
#ifdef CONFIG_64BIT
|
||||
if (cb_prev)
|
||||
store_runtime_instr_cb(cb_prev);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void restore_ri_cb(struct runtime_instr_cb *cb_next,
|
||||
struct runtime_instr_cb *cb_prev)
|
||||
{
|
||||
#ifdef CONFIG_64BIT
|
||||
if (cb_next)
|
||||
load_runtime_instr_cb(cb_next);
|
||||
else if (cb_prev)
|
||||
load_runtime_instr_cb(&runtime_instr_empty_cb);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
extern void exit_thread_runtime_instr(void);
|
||||
#else
|
||||
static inline void exit_thread_runtime_instr(void) { }
|
||||
#endif
|
||||
|
||||
#endif /* _RUNTIME_INSTR_H */
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Helper functions for scsw access.
|
||||
*
|
||||
* Copyright IBM Corp. 2008, 2009
|
||||
* Copyright IBM Corp. 2008, 2012
|
||||
* Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
|
||||
*/
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
#define _ASM_S390_SCSW_H_
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <asm/chsc.h>
|
||||
#include <asm/css_chars.h>
|
||||
#include <asm/cio.h>
|
||||
|
||||
/**
|
||||
@ -99,15 +99,47 @@ struct tm_scsw {
|
||||
u32 schxs:8;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/**
|
||||
* struct eadm_scsw - subchannel status word for eadm subchannels
|
||||
* @key: subchannel key
|
||||
* @eswf: esw format
|
||||
* @cc: deferred condition code
|
||||
* @ectl: extended control
|
||||
* @fctl: function control
|
||||
* @actl: activity control
|
||||
* @stctl: status control
|
||||
* @aob: AOB address
|
||||
* @dstat: device status
|
||||
* @cstat: subchannel status
|
||||
*/
|
||||
struct eadm_scsw {
|
||||
u32 key:4;
|
||||
u32:1;
|
||||
u32 eswf:1;
|
||||
u32 cc:2;
|
||||
u32:6;
|
||||
u32 ectl:1;
|
||||
u32:2;
|
||||
u32 fctl:3;
|
||||
u32 actl:7;
|
||||
u32 stctl:5;
|
||||
u32 aob;
|
||||
u32 dstat:8;
|
||||
u32 cstat:8;
|
||||
u32:16;
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* union scsw - subchannel status word
|
||||
* @cmd: command-mode SCSW
|
||||
* @tm: transport-mode SCSW
|
||||
* @eadm: eadm SCSW
|
||||
*/
|
||||
union scsw {
|
||||
struct cmd_scsw cmd;
|
||||
struct tm_scsw tm;
|
||||
} __attribute__ ((packed));
|
||||
struct eadm_scsw eadm;
|
||||
} __packed;
|
||||
|
||||
#define SCSW_FCTL_CLEAR_FUNC 0x1
|
||||
#define SCSW_FCTL_HALT_FUNC 0x2
|
||||
|
@ -60,7 +60,7 @@ void create_mem_hole(struct mem_chunk memory_chunk[], unsigned long addr,
|
||||
#define SECONDARY_SPACE_MODE 2
|
||||
#define HOME_SPACE_MODE 3
|
||||
|
||||
extern unsigned int addressing_mode;
|
||||
extern unsigned int s390_user_mode;
|
||||
|
||||
/*
|
||||
* Machine features detected in head.S
|
||||
@ -80,6 +80,7 @@ extern unsigned int addressing_mode;
|
||||
#define MACHINE_FLAG_LPAR (1UL << 12)
|
||||
#define MACHINE_FLAG_SPP (1UL << 13)
|
||||
#define MACHINE_FLAG_TOPOLOGY (1UL << 14)
|
||||
#define MACHINE_FLAG_TE (1UL << 15)
|
||||
|
||||
#define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM)
|
||||
#define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM)
|
||||
@ -98,6 +99,7 @@ extern unsigned int addressing_mode;
|
||||
#define MACHINE_HAS_PFMF (0)
|
||||
#define MACHINE_HAS_SPP (0)
|
||||
#define MACHINE_HAS_TOPOLOGY (0)
|
||||
#define MACHINE_HAS_TE (0)
|
||||
#else /* CONFIG_64BIT */
|
||||
#define MACHINE_HAS_IEEE (1)
|
||||
#define MACHINE_HAS_CSP (1)
|
||||
@ -109,6 +111,7 @@ extern unsigned int addressing_mode;
|
||||
#define MACHINE_HAS_PFMF (S390_lowcore.machine_flags & MACHINE_FLAG_PFMF)
|
||||
#define MACHINE_HAS_SPP (S390_lowcore.machine_flags & MACHINE_FLAG_SPP)
|
||||
#define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY)
|
||||
#define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE)
|
||||
#endif /* CONFIG_64BIT */
|
||||
|
||||
#define ZFCPDUMP_HSA_SIZE (32UL<<20)
|
||||
|
@ -30,6 +30,8 @@ extern int smp_vcpu_scheduled(int cpu);
|
||||
extern void smp_yield_cpu(int cpu);
|
||||
extern void smp_yield(void);
|
||||
extern void smp_stop_cpu(void);
|
||||
extern void smp_cpu_set_polarization(int cpu, int val);
|
||||
extern int smp_cpu_get_polarization(int cpu);
|
||||
|
||||
#else /* CONFIG_SMP */
|
||||
|
||||
@ -43,7 +45,7 @@ static inline void smp_call_online_cpu(void (*func)(void *), void *data)
|
||||
func(data);
|
||||
}
|
||||
|
||||
static inline int smp_find_processor_id(int address) { return 0; }
|
||||
static inline int smp_find_processor_id(u16 address) { return 0; }
|
||||
static inline int smp_store_status(int cpu) { return 0; }
|
||||
static inline int smp_vcpu_scheduled(int cpu) { return 1; }
|
||||
static inline void smp_yield_cpu(int cpu) { }
|
||||
|
@ -96,7 +96,6 @@ static inline char *strcat(char *dst, const char *src)
|
||||
|
||||
static inline char *strcpy(char *dst, const char *src)
|
||||
{
|
||||
#if __GNUC__ < 4
|
||||
register int r0 asm("0") = 0;
|
||||
char *ret = dst;
|
||||
|
||||
@ -106,14 +105,10 @@ static inline char *strcpy(char *dst, const char *src)
|
||||
: "+&a" (dst), "+&a" (src) : "d" (r0)
|
||||
: "cc", "memory");
|
||||
return ret;
|
||||
#else
|
||||
return __builtin_strcpy(dst, src);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t strlen(const char *s)
|
||||
{
|
||||
#if __GNUC__ < 4
|
||||
register unsigned long r0 asm("0") = 0;
|
||||
const char *tmp = s;
|
||||
|
||||
@ -122,9 +117,6 @@ static inline size_t strlen(const char *s)
|
||||
" jo 0b"
|
||||
: "+d" (r0), "+a" (tmp) : : "cc");
|
||||
return r0 - (unsigned long) s;
|
||||
#else
|
||||
return __builtin_strlen(s);
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline size_t strnlen(const char * s, size_t n)
|
||||
|
@ -80,10 +80,12 @@ static inline void restore_access_regs(unsigned int *acrs)
|
||||
if (prev->mm) { \
|
||||
save_fp_regs(&prev->thread.fp_regs); \
|
||||
save_access_regs(&prev->thread.acrs[0]); \
|
||||
save_ri_cb(prev->thread.ri_cb); \
|
||||
} \
|
||||
if (next->mm) { \
|
||||
restore_fp_regs(&next->thread.fp_regs); \
|
||||
restore_access_regs(&next->thread.acrs[0]); \
|
||||
restore_ri_cb(next->thread.ri_cb, prev->thread.ri_cb); \
|
||||
update_per_regs(next); \
|
||||
} \
|
||||
prev = __switch_to(prev,next); \
|
||||
|
@ -17,7 +17,10 @@
|
||||
#include <asm/bitsperlong.h>
|
||||
|
||||
struct sysinfo_1_1_1 {
|
||||
unsigned short :16;
|
||||
unsigned char p:1;
|
||||
unsigned char :6;
|
||||
unsigned char t:1;
|
||||
unsigned char :8;
|
||||
unsigned char ccr;
|
||||
unsigned char cai;
|
||||
char reserved_0[28];
|
||||
@ -30,9 +33,14 @@ struct sysinfo_1_1_1 {
|
||||
char model[16];
|
||||
char model_perm_cap[16];
|
||||
char model_temp_cap[16];
|
||||
char model_cap_rating[4];
|
||||
char model_perm_cap_rating[4];
|
||||
char model_temp_cap_rating[4];
|
||||
unsigned int model_cap_rating;
|
||||
unsigned int model_perm_cap_rating;
|
||||
unsigned int model_temp_cap_rating;
|
||||
unsigned char typepct[5];
|
||||
unsigned char reserved_2[3];
|
||||
unsigned int ncr;
|
||||
unsigned int npr;
|
||||
unsigned int ntr;
|
||||
};
|
||||
|
||||
struct sysinfo_1_2_1 {
|
||||
@ -47,8 +55,9 @@ struct sysinfo_1_2_2 {
|
||||
char format;
|
||||
char reserved_0[1];
|
||||
unsigned short acc_offset;
|
||||
char reserved_1[24];
|
||||
unsigned int secondary_capability;
|
||||
char reserved_1[20];
|
||||
unsigned int nominal_cap;
|
||||
unsigned int secondary_cap;
|
||||
unsigned int capability;
|
||||
unsigned short cpus_total;
|
||||
unsigned short cpus_configured;
|
||||
@ -109,6 +118,8 @@ struct sysinfo_3_2_2 {
|
||||
char reserved_544[3552];
|
||||
};
|
||||
|
||||
extern int topology_max_mnest;
|
||||
|
||||
#define TOPOLOGY_CPU_BITS 64
|
||||
#define TOPOLOGY_NR_MAG 6
|
||||
|
||||
@ -142,21 +153,7 @@ struct sysinfo_15_1_x {
|
||||
union topology_entry tle[0];
|
||||
};
|
||||
|
||||
static inline int stsi(void *sysinfo, int fc, int sel1, int sel2)
|
||||
{
|
||||
register int r0 asm("0") = (fc << 28) | sel1;
|
||||
register int r1 asm("1") = sel2;
|
||||
|
||||
asm volatile(
|
||||
" stsi 0(%2)\n"
|
||||
"0: jz 2f\n"
|
||||
"1: lhi %0,%3\n"
|
||||
"2:\n"
|
||||
EX_TABLE(0b, 1b)
|
||||
: "+d" (r0) : "d" (r1), "a" (sysinfo), "K" (-ENOSYS)
|
||||
: "cc", "memory");
|
||||
return r0;
|
||||
}
|
||||
int stsi(void *sysinfo, int fc, int sel1, int sel2);
|
||||
|
||||
/*
|
||||
* Service level reporting interface.
|
||||
|
@ -2,8 +2,8 @@
|
||||
#define _ASM_S390_TOPOLOGY_H
|
||||
|
||||
#include <linux/cpumask.h>
|
||||
#include <asm/sysinfo.h>
|
||||
|
||||
struct sysinfo_15_1_x;
|
||||
struct cpu;
|
||||
|
||||
#ifdef CONFIG_SCHED_BOOK
|
||||
@ -51,24 +51,6 @@ static inline void topology_expect_change(void) { }
|
||||
#define POLARIZATION_VM (2)
|
||||
#define POLARIZATION_VH (3)
|
||||
|
||||
extern int cpu_polarization[];
|
||||
|
||||
static inline void cpu_set_polarization(int cpu, int val)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_BOOK
|
||||
cpu_polarization[cpu] = val;
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline int cpu_read_polarization(int cpu)
|
||||
{
|
||||
#ifdef CONFIG_SCHED_BOOK
|
||||
return cpu_polarization[cpu];
|
||||
#else
|
||||
return POLARIZATION_HRZ;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SCHED_BOOK
|
||||
void s390_init_cpu_topology(void);
|
||||
#else
|
||||
|
@ -76,9 +76,22 @@ static inline int __range_ok(unsigned long addr, unsigned long size)
|
||||
|
||||
struct exception_table_entry
|
||||
{
|
||||
unsigned long insn, fixup;
|
||||
int insn, fixup;
|
||||
};
|
||||
|
||||
static inline unsigned long extable_insn(const struct exception_table_entry *x)
|
||||
{
|
||||
return (unsigned long)&x->insn + x->insn;
|
||||
}
|
||||
|
||||
static inline unsigned long extable_fixup(const struct exception_table_entry *x)
|
||||
{
|
||||
return (unsigned long)&x->fixup + x->fixup;
|
||||
}
|
||||
|
||||
#define ARCH_HAS_SORT_EXTABLE
|
||||
#define ARCH_HAS_SEARCH_EXTABLE
|
||||
|
||||
struct uaccess_ops {
|
||||
size_t (*copy_from_user)(size_t, const void __user *, void *);
|
||||
size_t (*copy_from_user_small)(size_t, const void __user *, void *);
|
||||
|
@ -277,7 +277,9 @@
|
||||
#define __NR_setns 339
|
||||
#define __NR_process_vm_readv 340
|
||||
#define __NR_process_vm_writev 341
|
||||
#define NR_syscalls 342
|
||||
#define __NR_s390_runtime_instr 342
|
||||
#define __NR_kcmp 343
|
||||
#define NR_syscalls 344
|
||||
|
||||
/*
|
||||
* There are some system calls that are not present on 64 bit, some
|
||||
|
@ -23,10 +23,11 @@ CFLAGS_sysinfo.o += -Iinclude/math-emu -Iarch/s390/math-emu -w
|
||||
obj-y := bitmap.o traps.o time.o process.o base.o early.o setup.o vtime.o \
|
||||
processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o \
|
||||
debug.o irq.o ipl.o dis.o diag.o mem_detect.o sclp.o vdso.o \
|
||||
sysinfo.o jump_label.o lgr.o os_info.o
|
||||
sysinfo.o jump_label.o lgr.o os_info.o machine_kexec.o
|
||||
|
||||
obj-y += $(if $(CONFIG_64BIT),entry64.o,entry.o)
|
||||
obj-y += $(if $(CONFIG_64BIT),reipl64.o,reipl.o)
|
||||
obj-y += $(if $(CONFIG_64BIT),relocate_kernel64.o,relocate_kernel.o)
|
||||
|
||||
extra-y += head.o vmlinux.lds
|
||||
extra-y += $(if $(CONFIG_64BIT),head64.o,head31.o)
|
||||
@ -48,12 +49,11 @@ obj-$(CONFIG_DYNAMIC_FTRACE) += ftrace.o
|
||||
obj-$(CONFIG_FUNCTION_GRAPH_TRACER) += ftrace.o
|
||||
obj-$(CONFIG_FTRACE_SYSCALLS) += ftrace.o
|
||||
obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
|
||||
obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o
|
||||
|
||||
# Kexec part
|
||||
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)
|
||||
ifdef CONFIG_64BIT
|
||||
obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_cpum_cf.o
|
||||
obj-y += runtime_instr.o cache.o
|
||||
endif
|
||||
|
||||
# vdso
|
||||
obj-$(CONFIG_64BIT) += vdso64/
|
||||
|
@ -157,6 +157,8 @@ int main(void)
|
||||
DEFINE(__LC_LAST_BREAK, offsetof(struct _lowcore, breaking_event_addr));
|
||||
DEFINE(__LC_VDSO_PER_CPU, offsetof(struct _lowcore, vdso_per_cpu_data));
|
||||
DEFINE(__LC_GMAP, offsetof(struct _lowcore, gmap));
|
||||
DEFINE(__LC_PGM_TDB, offsetof(struct _lowcore, pgm_tdb));
|
||||
DEFINE(__THREAD_trap_tdb, offsetof(struct task_struct, thread.trap_tdb));
|
||||
DEFINE(__GMAP_ASCE, offsetof(struct gmap, asce));
|
||||
#endif /* CONFIG_32BIT */
|
||||
return 0;
|
||||
|
385
arch/s390/kernel/cache.c
Normal file
385
arch/s390/kernel/cache.c
Normal file
@ -0,0 +1,385 @@
|
||||
/*
|
||||
* Extract CPU cache information and expose them via sysfs.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/notifier.h>
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/cpu.h>
|
||||
#include <asm/facility.h>
|
||||
|
||||
struct cache {
|
||||
unsigned long size;
|
||||
unsigned int line_size;
|
||||
unsigned int associativity;
|
||||
unsigned int nr_sets;
|
||||
unsigned int level : 3;
|
||||
unsigned int type : 2;
|
||||
unsigned int private : 1;
|
||||
struct list_head list;
|
||||
};
|
||||
|
||||
struct cache_dir {
|
||||
struct kobject *kobj;
|
||||
struct cache_index_dir *index;
|
||||
};
|
||||
|
||||
struct cache_index_dir {
|
||||
struct kobject kobj;
|
||||
int cpu;
|
||||
struct cache *cache;
|
||||
struct cache_index_dir *next;
|
||||
};
|
||||
|
||||
enum {
|
||||
CACHE_SCOPE_NOTEXISTS,
|
||||
CACHE_SCOPE_PRIVATE,
|
||||
CACHE_SCOPE_SHARED,
|
||||
CACHE_SCOPE_RESERVED,
|
||||
};
|
||||
|
||||
enum {
|
||||
CACHE_TYPE_SEPARATE,
|
||||
CACHE_TYPE_DATA,
|
||||
CACHE_TYPE_INSTRUCTION,
|
||||
CACHE_TYPE_UNIFIED,
|
||||
};
|
||||
|
||||
enum {
|
||||
EXTRACT_TOPOLOGY,
|
||||
EXTRACT_LINE_SIZE,
|
||||
EXTRACT_SIZE,
|
||||
EXTRACT_ASSOCIATIVITY,
|
||||
};
|
||||
|
||||
enum {
|
||||
CACHE_TI_UNIFIED = 0,
|
||||
CACHE_TI_INSTRUCTION = 0,
|
||||
CACHE_TI_DATA,
|
||||
};
|
||||
|
||||
struct cache_info {
|
||||
unsigned char : 4;
|
||||
unsigned char scope : 2;
|
||||
unsigned char type : 2;
|
||||
};
|
||||
|
||||
#define CACHE_MAX_LEVEL 8
|
||||
|
||||
union cache_topology {
|
||||
struct cache_info ci[CACHE_MAX_LEVEL];
|
||||
unsigned long long raw;
|
||||
};
|
||||
|
||||
static const char * const cache_type_string[] = {
|
||||
"Data",
|
||||
"Instruction",
|
||||
"Unified",
|
||||
};
|
||||
|
||||
static struct cache_dir *cache_dir_cpu[NR_CPUS];
|
||||
static LIST_HEAD(cache_list);
|
||||
|
||||
void show_cacheinfo(struct seq_file *m)
|
||||
{
|
||||
struct cache *cache;
|
||||
int index = 0;
|
||||
|
||||
list_for_each_entry(cache, &cache_list, list) {
|
||||
seq_printf(m, "cache%-11d: ", index);
|
||||
seq_printf(m, "level=%d ", cache->level);
|
||||
seq_printf(m, "type=%s ", cache_type_string[cache->type]);
|
||||
seq_printf(m, "scope=%s ", cache->private ? "Private" : "Shared");
|
||||
seq_printf(m, "size=%luK ", cache->size >> 10);
|
||||
seq_printf(m, "line_size=%u ", cache->line_size);
|
||||
seq_printf(m, "associativity=%d", cache->associativity);
|
||||
seq_puts(m, "\n");
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline unsigned long ecag(int ai, int li, int ti)
|
||||
{
|
||||
unsigned long cmd, val;
|
||||
|
||||
cmd = ai << 4 | li << 1 | ti;
|
||||
asm volatile(".insn rsy,0xeb000000004c,%0,0,0(%1)" /* ecag */
|
||||
: "=d" (val) : "a" (cmd));
|
||||
return val;
|
||||
}
|
||||
|
||||
static int __init cache_add(int level, int private, int type)
|
||||
{
|
||||
struct cache *cache;
|
||||
int ti;
|
||||
|
||||
cache = kzalloc(sizeof(*cache), GFP_KERNEL);
|
||||
if (!cache)
|
||||
return -ENOMEM;
|
||||
ti = type == CACHE_TYPE_DATA ? CACHE_TI_DATA : CACHE_TI_UNIFIED;
|
||||
cache->size = ecag(EXTRACT_SIZE, level, ti);
|
||||
cache->line_size = ecag(EXTRACT_LINE_SIZE, level, ti);
|
||||
cache->associativity = ecag(EXTRACT_ASSOCIATIVITY, level, ti);
|
||||
cache->nr_sets = cache->size / cache->associativity;
|
||||
cache->nr_sets /= cache->line_size;
|
||||
cache->private = private;
|
||||
cache->level = level + 1;
|
||||
cache->type = type - 1;
|
||||
list_add_tail(&cache->list, &cache_list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __init cache_build_info(void)
|
||||
{
|
||||
struct cache *cache, *next;
|
||||
union cache_topology ct;
|
||||
int level, private, rc;
|
||||
|
||||
ct.raw = ecag(EXTRACT_TOPOLOGY, 0, 0);
|
||||
for (level = 0; level < CACHE_MAX_LEVEL; level++) {
|
||||
switch (ct.ci[level].scope) {
|
||||
case CACHE_SCOPE_NOTEXISTS:
|
||||
case CACHE_SCOPE_RESERVED:
|
||||
return;
|
||||
case CACHE_SCOPE_SHARED:
|
||||
private = 0;
|
||||
break;
|
||||
case CACHE_SCOPE_PRIVATE:
|
||||
private = 1;
|
||||
break;
|
||||
}
|
||||
if (ct.ci[level].type == CACHE_TYPE_SEPARATE) {
|
||||
rc = cache_add(level, private, CACHE_TYPE_DATA);
|
||||
rc |= cache_add(level, private, CACHE_TYPE_INSTRUCTION);
|
||||
} else {
|
||||
rc = cache_add(level, private, ct.ci[level].type);
|
||||
}
|
||||
if (rc)
|
||||
goto error;
|
||||
}
|
||||
return;
|
||||
error:
|
||||
list_for_each_entry_safe(cache, next, &cache_list, list) {
|
||||
list_del(&cache->list);
|
||||
kfree(cache);
|
||||
}
|
||||
}
|
||||
|
||||
static struct cache_dir *__cpuinit cache_create_cache_dir(int cpu)
|
||||
{
|
||||
struct cache_dir *cache_dir;
|
||||
struct kobject *kobj = NULL;
|
||||
struct device *dev;
|
||||
|
||||
dev = get_cpu_device(cpu);
|
||||
if (!dev)
|
||||
goto out;
|
||||
kobj = kobject_create_and_add("cache", &dev->kobj);
|
||||
if (!kobj)
|
||||
goto out;
|
||||
cache_dir = kzalloc(sizeof(*cache_dir), GFP_KERNEL);
|
||||
if (!cache_dir)
|
||||
goto out;
|
||||
cache_dir->kobj = kobj;
|
||||
cache_dir_cpu[cpu] = cache_dir;
|
||||
return cache_dir;
|
||||
out:
|
||||
kobject_put(kobj);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct cache_index_dir *kobj_to_cache_index_dir(struct kobject *kobj)
|
||||
{
|
||||
return container_of(kobj, struct cache_index_dir, kobj);
|
||||
}
|
||||
|
||||
static void cache_index_release(struct kobject *kobj)
|
||||
{
|
||||
struct cache_index_dir *index;
|
||||
|
||||
index = kobj_to_cache_index_dir(kobj);
|
||||
kfree(index);
|
||||
}
|
||||
|
||||
static ssize_t cache_index_show(struct kobject *kobj,
|
||||
struct attribute *attr, char *buf)
|
||||
{
|
||||
struct kobj_attribute *kobj_attr;
|
||||
|
||||
kobj_attr = container_of(attr, struct kobj_attribute, attr);
|
||||
return kobj_attr->show(kobj, kobj_attr, buf);
|
||||
}
|
||||
|
||||
#define DEFINE_CACHE_ATTR(_name, _format, _value) \
|
||||
static ssize_t cache_##_name##_show(struct kobject *kobj, \
|
||||
struct kobj_attribute *attr, \
|
||||
char *buf) \
|
||||
{ \
|
||||
struct cache_index_dir *index; \
|
||||
\
|
||||
index = kobj_to_cache_index_dir(kobj); \
|
||||
return sprintf(buf, _format, _value); \
|
||||
} \
|
||||
static struct kobj_attribute cache_##_name##_attr = \
|
||||
__ATTR(_name, 0444, cache_##_name##_show, NULL);
|
||||
|
||||
DEFINE_CACHE_ATTR(size, "%luK\n", index->cache->size >> 10);
|
||||
DEFINE_CACHE_ATTR(coherency_line_size, "%u\n", index->cache->line_size);
|
||||
DEFINE_CACHE_ATTR(number_of_sets, "%u\n", index->cache->nr_sets);
|
||||
DEFINE_CACHE_ATTR(ways_of_associativity, "%u\n", index->cache->associativity);
|
||||
DEFINE_CACHE_ATTR(type, "%s\n", cache_type_string[index->cache->type]);
|
||||
DEFINE_CACHE_ATTR(level, "%d\n", index->cache->level);
|
||||
|
||||
static ssize_t shared_cpu_map_func(struct kobject *kobj, int type, char *buf)
|
||||
{
|
||||
struct cache_index_dir *index;
|
||||
int len;
|
||||
|
||||
index = kobj_to_cache_index_dir(kobj);
|
||||
len = type ?
|
||||
cpulist_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu)) :
|
||||
cpumask_scnprintf(buf, PAGE_SIZE - 2, cpumask_of(index->cpu));
|
||||
len += sprintf(&buf[len], "\n");
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t shared_cpu_map_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return shared_cpu_map_func(kobj, 0, buf);
|
||||
}
|
||||
static struct kobj_attribute cache_shared_cpu_map_attr =
|
||||
__ATTR(shared_cpu_map, 0444, shared_cpu_map_show, NULL);
|
||||
|
||||
static ssize_t shared_cpu_list_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return shared_cpu_map_func(kobj, 1, buf);
|
||||
}
|
||||
static struct kobj_attribute cache_shared_cpu_list_attr =
|
||||
__ATTR(shared_cpu_list, 0444, shared_cpu_list_show, NULL);
|
||||
|
||||
static struct attribute *cache_index_default_attrs[] = {
|
||||
&cache_type_attr.attr,
|
||||
&cache_size_attr.attr,
|
||||
&cache_number_of_sets_attr.attr,
|
||||
&cache_ways_of_associativity_attr.attr,
|
||||
&cache_level_attr.attr,
|
||||
&cache_coherency_line_size_attr.attr,
|
||||
&cache_shared_cpu_map_attr.attr,
|
||||
&cache_shared_cpu_list_attr.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static const struct sysfs_ops cache_index_ops = {
|
||||
.show = cache_index_show,
|
||||
};
|
||||
|
||||
static struct kobj_type cache_index_type = {
|
||||
.sysfs_ops = &cache_index_ops,
|
||||
.release = cache_index_release,
|
||||
.default_attrs = cache_index_default_attrs,
|
||||
};
|
||||
|
||||
static int __cpuinit cache_create_index_dir(struct cache_dir *cache_dir,
|
||||
struct cache *cache, int index,
|
||||
int cpu)
|
||||
{
|
||||
struct cache_index_dir *index_dir;
|
||||
int rc;
|
||||
|
||||
index_dir = kzalloc(sizeof(*index_dir), GFP_KERNEL);
|
||||
if (!index_dir)
|
||||
return -ENOMEM;
|
||||
index_dir->cache = cache;
|
||||
index_dir->cpu = cpu;
|
||||
rc = kobject_init_and_add(&index_dir->kobj, &cache_index_type,
|
||||
cache_dir->kobj, "index%d", index);
|
||||
if (rc)
|
||||
goto out;
|
||||
index_dir->next = cache_dir->index;
|
||||
cache_dir->index = index_dir;
|
||||
return 0;
|
||||
out:
|
||||
kfree(index_dir);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __cpuinit cache_add_cpu(int cpu)
|
||||
{
|
||||
struct cache_dir *cache_dir;
|
||||
struct cache *cache;
|
||||
int rc, index = 0;
|
||||
|
||||
if (list_empty(&cache_list))
|
||||
return 0;
|
||||
cache_dir = cache_create_cache_dir(cpu);
|
||||
if (!cache_dir)
|
||||
return -ENOMEM;
|
||||
list_for_each_entry(cache, &cache_list, list) {
|
||||
if (!cache->private)
|
||||
break;
|
||||
rc = cache_create_index_dir(cache_dir, cache, index, cpu);
|
||||
if (rc)
|
||||
return rc;
|
||||
index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __cpuinit cache_remove_cpu(int cpu)
|
||||
{
|
||||
struct cache_index_dir *index, *next;
|
||||
struct cache_dir *cache_dir;
|
||||
|
||||
cache_dir = cache_dir_cpu[cpu];
|
||||
if (!cache_dir)
|
||||
return;
|
||||
index = cache_dir->index;
|
||||
while (index) {
|
||||
next = index->next;
|
||||
kobject_put(&index->kobj);
|
||||
index = next;
|
||||
}
|
||||
kobject_put(cache_dir->kobj);
|
||||
kfree(cache_dir);
|
||||
cache_dir_cpu[cpu] = NULL;
|
||||
}
|
||||
|
||||
static int __cpuinit cache_hotplug(struct notifier_block *nfb,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
int cpu = (long)hcpu;
|
||||
int rc = 0;
|
||||
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_ONLINE:
|
||||
rc = cache_add_cpu(cpu);
|
||||
if (rc)
|
||||
cache_remove_cpu(cpu);
|
||||
break;
|
||||
case CPU_DEAD:
|
||||
cache_remove_cpu(cpu);
|
||||
break;
|
||||
}
|
||||
return rc ? NOTIFY_BAD : NOTIFY_OK;
|
||||
}
|
||||
|
||||
static int __init cache_init(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (!test_facility(34))
|
||||
return 0;
|
||||
cache_build_info();
|
||||
for_each_online_cpu(cpu)
|
||||
cache_add_cpu(cpu);
|
||||
hotcpu_notifier(cache_hotplug, 0);
|
||||
return 0;
|
||||
}
|
||||
device_initcall(cache_init);
|
@ -1646,3 +1646,16 @@ ENTRY(compat_sys_process_vm_writev_wrapper)
|
||||
llgf %r0,164(%r15) # unsigned long
|
||||
stg %r0,160(%r15)
|
||||
jg compat_sys_process_vm_writev
|
||||
|
||||
ENTRY(sys_s390_runtime_instr_wrapper)
|
||||
lgfr %r2,%r2 # int
|
||||
lgfr %r3,%r3 # int
|
||||
jg sys_s390_runtime_instr
|
||||
|
||||
ENTRY(sys_kcmp_wrapper)
|
||||
lgfr %r2,%r2 # pid_t
|
||||
lgfr %r3,%r3 # pid_t
|
||||
lgfr %r4,%r4 # int
|
||||
llgfr %r5,%r5 # unsigned long
|
||||
llgfr %r6,%r6 # unsigned long
|
||||
jg sys_kcmp
|
||||
|
@ -1,14 +0,0 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2005
|
||||
*
|
||||
* Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/threads.h>
|
||||
#include <linux/kexec.h>
|
||||
#include <linux/reboot.h>
|
||||
|
||||
void machine_crash_shutdown(struct pt_regs *regs)
|
||||
{
|
||||
}
|
@ -13,8 +13,9 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/bootmem.h>
|
||||
#include <linux/elf.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/os_info.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/ipl.h>
|
||||
|
||||
#define PTR_ADD(x, y) (((char *) (x)) + ((unsigned long) (y)))
|
||||
#define PTR_SUB(x, y) (((char *) (x)) - ((unsigned long) (y)))
|
||||
|
@ -315,6 +315,11 @@ enum {
|
||||
LONG_INSN_POPCNT,
|
||||
LONG_INSN_RISBHG,
|
||||
LONG_INSN_RISBLG,
|
||||
LONG_INSN_RINEXT,
|
||||
LONG_INSN_RIEMIT,
|
||||
LONG_INSN_TABORT,
|
||||
LONG_INSN_TBEGIN,
|
||||
LONG_INSN_TBEGINC,
|
||||
};
|
||||
|
||||
static char *long_insn_name[] = {
|
||||
@ -329,7 +334,12 @@ static char *long_insn_name[] = {
|
||||
[LONG_INSN_LLGHRL] = "llghrl",
|
||||
[LONG_INSN_POPCNT] = "popcnt",
|
||||
[LONG_INSN_RISBHG] = "risbhg",
|
||||
[LONG_INSN_RISBLG] = "risblk",
|
||||
[LONG_INSN_RISBLG] = "risblg",
|
||||
[LONG_INSN_RINEXT] = "rinext",
|
||||
[LONG_INSN_RIEMIT] = "riemit",
|
||||
[LONG_INSN_TABORT] = "tabort",
|
||||
[LONG_INSN_TBEGIN] = "tbegin",
|
||||
[LONG_INSN_TBEGINC] = "tbeginc",
|
||||
};
|
||||
|
||||
static struct insn opcode[] = {
|
||||
@ -582,6 +592,17 @@ static struct insn opcode_a7[] = {
|
||||
{ "", 0, INSTR_INVALID }
|
||||
};
|
||||
|
||||
static struct insn opcode_aa[] = {
|
||||
#ifdef CONFIG_64BIT
|
||||
{ { 0, LONG_INSN_RINEXT }, 0x00, INSTR_RI_RI },
|
||||
{ "rion", 0x01, INSTR_RI_RI },
|
||||
{ "tric", 0x02, INSTR_RI_RI },
|
||||
{ "rioff", 0x03, INSTR_RI_RI },
|
||||
{ { 0, LONG_INSN_RIEMIT }, 0x04, INSTR_RI_RI },
|
||||
#endif
|
||||
{ "", 0, INSTR_INVALID }
|
||||
};
|
||||
|
||||
static struct insn opcode_b2[] = {
|
||||
#ifdef CONFIG_64BIT
|
||||
{ "sske", 0x2b, INSTR_RRF_M0RR },
|
||||
@ -594,6 +615,9 @@ static struct insn opcode_b2[] = {
|
||||
{ "lpswe", 0xb2, INSTR_S_RD },
|
||||
{ "srnmt", 0xb9, INSTR_S_RD },
|
||||
{ "lfas", 0xbd, INSTR_S_RD },
|
||||
{ "etndg", 0xec, INSTR_RRE_R0 },
|
||||
{ { 0, LONG_INSN_TABORT }, 0xfc, INSTR_S_RD },
|
||||
{ "tend", 0xf8, INSTR_S_RD },
|
||||
#endif
|
||||
{ "stidp", 0x02, INSTR_S_RD },
|
||||
{ "sck", 0x04, INSTR_S_RD },
|
||||
@ -1150,6 +1174,7 @@ static struct insn opcode_e3[] = {
|
||||
{ "stfh", 0xcb, INSTR_RXY_RRRD },
|
||||
{ "chf", 0xcd, INSTR_RXY_RRRD },
|
||||
{ "clhf", 0xcf, INSTR_RXY_RRRD },
|
||||
{ "ntstg", 0x25, INSTR_RXY_RRRD },
|
||||
#endif
|
||||
{ "lrv", 0x1e, INSTR_RXY_RRRD },
|
||||
{ "lrvh", 0x1f, INSTR_RXY_RRRD },
|
||||
@ -1173,6 +1198,8 @@ static struct insn opcode_e5[] = {
|
||||
{ "mvhhi", 0x44, INSTR_SIL_RDI },
|
||||
{ "mvhi", 0x4c, INSTR_SIL_RDI },
|
||||
{ "mvghi", 0x48, INSTR_SIL_RDI },
|
||||
{ { 0, LONG_INSN_TBEGIN }, 0x60, INSTR_SIL_RDU },
|
||||
{ { 0, LONG_INSN_TBEGINC }, 0x61, INSTR_SIL_RDU },
|
||||
#endif
|
||||
{ "lasp", 0x00, INSTR_SSE_RDRD },
|
||||
{ "tprot", 0x01, INSTR_SSE_RDRD },
|
||||
@ -1210,6 +1237,9 @@ static struct insn opcode_eb[] = {
|
||||
{ "cliy", 0x55, INSTR_SIY_URD },
|
||||
{ "oiy", 0x56, INSTR_SIY_URD },
|
||||
{ "xiy", 0x57, INSTR_SIY_URD },
|
||||
{ "lric", 0x60, INSTR_RSY_RDRM },
|
||||
{ "stric", 0x61, INSTR_RSY_RDRM },
|
||||
{ "mric", 0x62, INSTR_RSY_RDRM },
|
||||
{ "icmh", 0x80, INSTR_RSE_RURD },
|
||||
{ "icmh", 0x80, INSTR_RSY_RURD },
|
||||
{ "icmy", 0x81, INSTR_RSY_RURD },
|
||||
@ -1408,6 +1438,9 @@ static struct insn *find_insn(unsigned char *code)
|
||||
case 0xa7:
|
||||
table = opcode_a7;
|
||||
break;
|
||||
case 0xaa:
|
||||
table = opcode_aa;
|
||||
break;
|
||||
case 0xb2:
|
||||
table = opcode_b2;
|
||||
break;
|
||||
@ -1601,3 +1634,26 @@ void show_code(struct pt_regs *regs)
|
||||
}
|
||||
printk("\n");
|
||||
}
|
||||
|
||||
void print_fn_code(unsigned char *code, unsigned long len)
|
||||
{
|
||||
char buffer[64], *ptr;
|
||||
int opsize, i;
|
||||
|
||||
while (len) {
|
||||
ptr = buffer;
|
||||
opsize = insn_length(*code);
|
||||
ptr += sprintf(ptr, "%p: ", code);
|
||||
for (i = 0; i < opsize; i++)
|
||||
ptr += sprintf(ptr, "%02x", code[i]);
|
||||
*ptr++ = '\t';
|
||||
if (i < 4)
|
||||
*ptr++ = '\t';
|
||||
ptr += print_insn(ptr, code, (unsigned long) code);
|
||||
*ptr++ = '\n';
|
||||
*ptr++ = 0;
|
||||
printk(buffer);
|
||||
code += opsize;
|
||||
len -= opsize;
|
||||
}
|
||||
}
|
||||
|
@ -215,36 +215,54 @@ static noinline __init void init_kernel_storage_key(void)
|
||||
PAGE_DEFAULT_KEY, 0);
|
||||
}
|
||||
|
||||
static __initdata struct sysinfo_3_2_2 vmms __aligned(PAGE_SIZE);
|
||||
static __initdata char sysinfo_page[PAGE_SIZE] __aligned(PAGE_SIZE);
|
||||
|
||||
static noinline __init void detect_machine_type(void)
|
||||
{
|
||||
struct sysinfo_3_2_2 *vmms = (struct sysinfo_3_2_2 *)&sysinfo_page;
|
||||
|
||||
/* Check current-configuration-level */
|
||||
if ((stsi(NULL, 0, 0, 0) >> 28) <= 2) {
|
||||
if (stsi(NULL, 0, 0, 0) <= 2) {
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;
|
||||
return;
|
||||
}
|
||||
/* Get virtual-machine cpu information. */
|
||||
if (stsi(&vmms, 3, 2, 2) == -ENOSYS || !vmms.count)
|
||||
if (stsi(vmms, 3, 2, 2) || !vmms->count)
|
||||
return;
|
||||
|
||||
/* Running under KVM? If not we assume z/VM */
|
||||
if (!memcmp(vmms.vm[0].cpi, "\xd2\xe5\xd4", 3))
|
||||
if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
|
||||
else
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
|
||||
}
|
||||
|
||||
static __init void setup_topology(void)
|
||||
{
|
||||
#ifdef CONFIG_64BIT
|
||||
int max_mnest;
|
||||
|
||||
if (!test_facility(11))
|
||||
return;
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
|
||||
for (max_mnest = 6; max_mnest > 1; max_mnest--) {
|
||||
if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0)
|
||||
break;
|
||||
}
|
||||
topology_max_mnest = max_mnest;
|
||||
#endif
|
||||
}
|
||||
|
||||
static void early_pgm_check_handler(void)
|
||||
{
|
||||
unsigned long addr;
|
||||
const struct exception_table_entry *fixup;
|
||||
unsigned long addr;
|
||||
|
||||
addr = S390_lowcore.program_old_psw.addr;
|
||||
fixup = search_exception_tables(addr & PSW_ADDR_INSN);
|
||||
if (!fixup)
|
||||
disabled_wait(0);
|
||||
S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE;
|
||||
S390_lowcore.program_old_psw.addr = extable_fixup(fixup)|PSW_ADDR_AMODE;
|
||||
}
|
||||
|
||||
static noinline __init void setup_lowcore_early(void)
|
||||
@ -267,12 +285,10 @@ static noinline __init void setup_facility_list(void)
|
||||
|
||||
static noinline __init void setup_hpage(void)
|
||||
{
|
||||
#ifndef CONFIG_DEBUG_PAGEALLOC
|
||||
if (!test_facility(2) || !test_facility(8))
|
||||
return;
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_HPAGE;
|
||||
__ctl_set_bit(0, 23);
|
||||
#endif
|
||||
}
|
||||
|
||||
static __init void detect_mvpg(void)
|
||||
@ -366,12 +382,12 @@ static __init void detect_machine_facilities(void)
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
|
||||
if (test_facility(8))
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_PFMF;
|
||||
if (test_facility(11))
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
|
||||
if (test_facility(27))
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS;
|
||||
if (test_facility(40))
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_SPP;
|
||||
if (test_facility(50) && test_facility(73))
|
||||
S390_lowcore.machine_flags |= MACHINE_FLAG_TE;
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -441,7 +457,6 @@ static void __init setup_boot_command_line(void)
|
||||
append_to_cmdline(append_ipl_scpdata);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Save ipl parameters, clear bss memory, initialize storage keys
|
||||
* and create a kernel NSS at startup if the SAVESYS= parm is defined
|
||||
@ -468,6 +483,7 @@ void __init startup_init(void)
|
||||
detect_diag44();
|
||||
detect_machine_facilities();
|
||||
setup_hpage();
|
||||
setup_topology();
|
||||
sclp_facilities_detect();
|
||||
detect_memory_layout(memory_chunk);
|
||||
#ifdef CONFIG_DYNAMIC_FTRACE
|
||||
|
@ -10,6 +10,7 @@
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/cache.h>
|
||||
#include <asm/errno.h>
|
||||
#include <asm/ptrace.h>
|
||||
@ -412,6 +413,11 @@ ENTRY(pgm_check_handler)
|
||||
1: UPDATE_VTIME %r14,__LC_SYNC_ENTER_TIMER
|
||||
LAST_BREAK %r14
|
||||
lg %r15,__LC_KERNEL_STACK
|
||||
lg %r14,__TI_task(%r12)
|
||||
lghi %r13,__LC_PGM_TDB
|
||||
tm __LC_PGM_ILC+2,0x02 # check for transaction abort
|
||||
jz 2f
|
||||
mvc __THREAD_trap_tdb(256,%r14),0(%r13)
|
||||
2: aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
|
||||
la %r11,STACK_FRAME_OVERHEAD(%r15)
|
||||
stmg %r0,%r7,__PT_R0(%r11)
|
||||
@ -422,13 +428,12 @@ ENTRY(pgm_check_handler)
|
||||
stg %r10,__PT_ARGS(%r11)
|
||||
tm __LC_PGM_ILC+3,0x80 # check for per exception
|
||||
jz 0f
|
||||
lg %r1,__TI_task(%r12)
|
||||
tmhh %r8,0x0001 # kernel per event ?
|
||||
jz pgm_kprobe
|
||||
oi __TI_flags+7(%r12),_TIF_PER_TRAP
|
||||
mvc __THREAD_per_address(8,%r1),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per_cause(2,%r1),__LC_PER_CAUSE
|
||||
mvc __THREAD_per_paid(1,%r1),__LC_PER_PAID
|
||||
mvc __THREAD_per_address(8,%r14),__LC_PER_ADDRESS
|
||||
mvc __THREAD_per_cause(2,%r14),__LC_PER_CAUSE
|
||||
mvc __THREAD_per_paid(1,%r14),__LC_PER_PAID
|
||||
0: REENABLE_IRQS
|
||||
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
|
||||
larl %r1,pgm_check_table
|
||||
@ -1004,9 +1009,7 @@ sie_fault:
|
||||
.Lhost_id:
|
||||
.quad 0
|
||||
|
||||
.section __ex_table,"a"
|
||||
.quad sie_loop,sie_fault
|
||||
.previous
|
||||
EX_TABLE(sie_loop,sie_fault)
|
||||
#endif
|
||||
|
||||
.section .rodata, "a"
|
||||
|
@ -30,33 +30,35 @@ struct irq_class {
|
||||
};
|
||||
|
||||
static const struct irq_class intrclass_names[] = {
|
||||
{.name = "EXT" },
|
||||
{.name = "I/O" },
|
||||
{.name = "CLK", .desc = "[EXT] Clock Comparator" },
|
||||
{.name = "EXC", .desc = "[EXT] External Call" },
|
||||
{.name = "EMS", .desc = "[EXT] Emergency Signal" },
|
||||
{.name = "TMR", .desc = "[EXT] CPU Timer" },
|
||||
{.name = "TAL", .desc = "[EXT] Timing Alert" },
|
||||
{.name = "PFL", .desc = "[EXT] Pseudo Page Fault" },
|
||||
{.name = "DSD", .desc = "[EXT] DASD Diag" },
|
||||
{.name = "VRT", .desc = "[EXT] Virtio" },
|
||||
{.name = "SCP", .desc = "[EXT] Service Call" },
|
||||
{.name = "IUC", .desc = "[EXT] IUCV" },
|
||||
{.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling" },
|
||||
{.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter" },
|
||||
{.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt" },
|
||||
{.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt" },
|
||||
{.name = "DAS", .desc = "[I/O] DASD" },
|
||||
{.name = "C15", .desc = "[I/O] 3215" },
|
||||
{.name = "C70", .desc = "[I/O] 3270" },
|
||||
{.name = "TAP", .desc = "[I/O] Tape" },
|
||||
{.name = "VMR", .desc = "[I/O] Unit Record Devices" },
|
||||
{.name = "LCS", .desc = "[I/O] LCS" },
|
||||
{.name = "CLW", .desc = "[I/O] CLAW" },
|
||||
{.name = "CTC", .desc = "[I/O] CTC" },
|
||||
{.name = "APB", .desc = "[I/O] AP Bus" },
|
||||
{.name = "CSC", .desc = "[I/O] CHSC Subchannel" },
|
||||
{.name = "NMI", .desc = "[NMI] Machine Check" },
|
||||
[EXTERNAL_INTERRUPT] = {.name = "EXT"},
|
||||
[IO_INTERRUPT] = {.name = "I/O"},
|
||||
[EXTINT_CLK] = {.name = "CLK", .desc = "[EXT] Clock Comparator"},
|
||||
[EXTINT_EXC] = {.name = "EXC", .desc = "[EXT] External Call"},
|
||||
[EXTINT_EMS] = {.name = "EMS", .desc = "[EXT] Emergency Signal"},
|
||||
[EXTINT_TMR] = {.name = "TMR", .desc = "[EXT] CPU Timer"},
|
||||
[EXTINT_TLA] = {.name = "TAL", .desc = "[EXT] Timing Alert"},
|
||||
[EXTINT_PFL] = {.name = "PFL", .desc = "[EXT] Pseudo Page Fault"},
|
||||
[EXTINT_DSD] = {.name = "DSD", .desc = "[EXT] DASD Diag"},
|
||||
[EXTINT_VRT] = {.name = "VRT", .desc = "[EXT] Virtio"},
|
||||
[EXTINT_SCP] = {.name = "SCP", .desc = "[EXT] Service Call"},
|
||||
[EXTINT_IUC] = {.name = "IUC", .desc = "[EXT] IUCV"},
|
||||
[EXTINT_CMS] = {.name = "CMS", .desc = "[EXT] CPU-Measurement: Sampling"},
|
||||
[EXTINT_CMC] = {.name = "CMC", .desc = "[EXT] CPU-Measurement: Counter"},
|
||||
[EXTINT_CMR] = {.name = "CMR", .desc = "[EXT] CPU-Measurement: RI"},
|
||||
[IOINT_CIO] = {.name = "CIO", .desc = "[I/O] Common I/O Layer Interrupt"},
|
||||
[IOINT_QAI] = {.name = "QAI", .desc = "[I/O] QDIO Adapter Interrupt"},
|
||||
[IOINT_DAS] = {.name = "DAS", .desc = "[I/O] DASD"},
|
||||
[IOINT_C15] = {.name = "C15", .desc = "[I/O] 3215"},
|
||||
[IOINT_C70] = {.name = "C70", .desc = "[I/O] 3270"},
|
||||
[IOINT_TAP] = {.name = "TAP", .desc = "[I/O] Tape"},
|
||||
[IOINT_VMR] = {.name = "VMR", .desc = "[I/O] Unit Record Devices"},
|
||||
[IOINT_LCS] = {.name = "LCS", .desc = "[I/O] LCS"},
|
||||
[IOINT_CLW] = {.name = "CLW", .desc = "[I/O] CLAW"},
|
||||
[IOINT_CTC] = {.name = "CTC", .desc = "[I/O] CTC"},
|
||||
[IOINT_APB] = {.name = "APB", .desc = "[I/O] AP Bus"},
|
||||
[IOINT_ADM] = {.name = "ADM", .desc = "[I/O] EADM Subchannel"},
|
||||
[IOINT_CSC] = {.name = "CSC", .desc = "[I/O] CHSC Subchannel"},
|
||||
[NMI_NMI] = {.name = "NMI", .desc = "[NMI] Machine Check"},
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -547,7 +547,7 @@ static int __kprobes kprobe_trap_handler(struct pt_regs *regs, int trapnr)
|
||||
*/
|
||||
entry = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
|
||||
if (entry) {
|
||||
regs->psw.addr = entry->fixup | PSW_ADDR_AMODE;
|
||||
regs->psw.addr = extable_fixup(entry) | PSW_ADDR_AMODE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -50,16 +50,6 @@ static struct lgr_info lgr_info_last;
|
||||
static struct lgr_info lgr_info_cur;
|
||||
static struct debug_info *lgr_dbf;
|
||||
|
||||
/*
|
||||
* Return number of valid stsi levels
|
||||
*/
|
||||
static inline int stsi_0(void)
|
||||
{
|
||||
int rc = stsi(NULL, 0, 0, 0);
|
||||
|
||||
return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
|
||||
}
|
||||
|
||||
/*
|
||||
* Copy buffer and then convert it to ASCII
|
||||
*/
|
||||
@ -76,7 +66,7 @@ static void lgr_stsi_1_1_1(struct lgr_info *lgr_info)
|
||||
{
|
||||
struct sysinfo_1_1_1 *si = (void *) lgr_page;
|
||||
|
||||
if (stsi(si, 1, 1, 1) == -ENOSYS)
|
||||
if (stsi(si, 1, 1, 1))
|
||||
return;
|
||||
cpascii(lgr_info->manufacturer, si->manufacturer,
|
||||
sizeof(si->manufacturer));
|
||||
@ -93,7 +83,7 @@ static void lgr_stsi_2_2_2(struct lgr_info *lgr_info)
|
||||
{
|
||||
struct sysinfo_2_2_2 *si = (void *) lgr_page;
|
||||
|
||||
if (stsi(si, 2, 2, 2) == -ENOSYS)
|
||||
if (stsi(si, 2, 2, 2))
|
||||
return;
|
||||
cpascii(lgr_info->name, si->name, sizeof(si->name));
|
||||
memcpy(&lgr_info->lpar_number, &si->lpar_number,
|
||||
@ -108,7 +98,7 @@ static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
|
||||
struct sysinfo_3_2_2 *si = (void *) lgr_page;
|
||||
int i;
|
||||
|
||||
if (stsi(si, 3, 2, 2) == -ENOSYS)
|
||||
if (stsi(si, 3, 2, 2))
|
||||
return;
|
||||
for (i = 0; i < min_t(u8, si->count, VM_LEVEL_MAX); i++) {
|
||||
cpascii(lgr_info->vm[i].name, si->vm[i].name,
|
||||
@ -124,16 +114,17 @@ static void lgr_stsi_3_2_2(struct lgr_info *lgr_info)
|
||||
*/
|
||||
static void lgr_info_get(struct lgr_info *lgr_info)
|
||||
{
|
||||
int level;
|
||||
|
||||
memset(lgr_info, 0, sizeof(*lgr_info));
|
||||
stfle(lgr_info->stfle_fac_list, ARRAY_SIZE(lgr_info->stfle_fac_list));
|
||||
lgr_info->level = stsi_0();
|
||||
if (lgr_info->level == -ENOSYS)
|
||||
return;
|
||||
if (lgr_info->level >= 1)
|
||||
level = stsi(NULL, 0, 0, 0);
|
||||
lgr_info->level = level;
|
||||
if (level >= 1)
|
||||
lgr_stsi_1_1_1(lgr_info);
|
||||
if (lgr_info->level >= 2)
|
||||
if (level >= 2)
|
||||
lgr_stsi_2_2_2(lgr_info);
|
||||
if (lgr_info->level >= 3)
|
||||
if (level >= 3)
|
||||
lgr_stsi_3_2_2(lgr_info);
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <asm/reset.h>
|
||||
#include <asm/ipl.h>
|
||||
#include <asm/diag.h>
|
||||
#include <asm/elf.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/os_info.h>
|
||||
|
||||
@ -31,8 +32,6 @@ extern const unsigned long long relocate_kernel_len;
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
|
||||
void *fill_cpu_elf_notes(void *ptr, struct save_area *sa);
|
||||
|
||||
/*
|
||||
* Create ELF notes for one CPU
|
||||
*/
|
||||
@ -159,7 +158,7 @@ int machine_kexec_prepare(struct kimage *image)
|
||||
|
||||
/* Can't replace kernel image since it is read-only. */
|
||||
if (ipl_flags & IPL_NSS_VALID)
|
||||
return -ENOSYS;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (image->type == KEXEC_TYPE_CRASH)
|
||||
return machine_kexec_prepare_kdump();
|
||||
@ -191,6 +190,10 @@ void machine_shutdown(void)
|
||||
{
|
||||
}
|
||||
|
||||
void machine_crash_shutdown(struct pt_regs *regs)
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* Do normal kexec
|
||||
*/
|
||||
|
@ -26,10 +26,12 @@
|
||||
#include <asm/io.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/vtimer.h>
|
||||
#include <asm/exec.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/nmi.h>
|
||||
#include <asm/smp.h>
|
||||
#include <asm/switch_to.h>
|
||||
#include <asm/runtime_instr.h>
|
||||
#include "entry.h"
|
||||
|
||||
asmlinkage void ret_from_fork(void) asm ("ret_from_fork");
|
||||
@ -132,6 +134,7 @@ EXPORT_SYMBOL(kernel_thread);
|
||||
*/
|
||||
void exit_thread(void)
|
||||
{
|
||||
exit_thread_runtime_instr();
|
||||
}
|
||||
|
||||
void flush_thread(void)
|
||||
@ -170,6 +173,11 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
|
||||
/* Save access registers to new thread structure. */
|
||||
save_access_regs(&p->thread.acrs[0]);
|
||||
|
||||
/* Don't copy runtime instrumentation info */
|
||||
p->thread.ri_cb = NULL;
|
||||
p->thread.ri_signum = 0;
|
||||
frame->childregs.psw.mask &= ~PSW_MASK_RI;
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
/*
|
||||
* save fprs to current->thread.fp_regs to merge them with
|
||||
|
@ -39,9 +39,9 @@ void __cpuinit cpu_init(void)
|
||||
*/
|
||||
static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
{
|
||||
static const char *hwcap_str[10] = {
|
||||
static const char *hwcap_str[] = {
|
||||
"esan3", "zarch", "stfle", "msa", "ldisp", "eimm", "dfp",
|
||||
"edat", "etf3eh", "highgprs"
|
||||
"edat", "etf3eh", "highgprs", "te"
|
||||
};
|
||||
unsigned long n = (unsigned long) v - 1;
|
||||
int i;
|
||||
@ -54,10 +54,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)
|
||||
num_online_cpus(), loops_per_jiffy/(500000/HZ),
|
||||
(loops_per_jiffy/(5000/HZ))%100);
|
||||
seq_puts(m, "features\t: ");
|
||||
for (i = 0; i < 10; i++)
|
||||
for (i = 0; i < ARRAY_SIZE(hwcap_str); i++)
|
||||
if (hwcap_str[i] && (elf_hwcap & (1UL << i)))
|
||||
seq_printf(m, "%s ", hwcap_str[i]);
|
||||
seq_puts(m, "\n");
|
||||
show_cacheinfo(m);
|
||||
}
|
||||
get_online_cpus();
|
||||
if (cpu_online(n)) {
|
||||
|
@ -42,6 +42,7 @@ enum s390_regset {
|
||||
REGSET_GENERAL,
|
||||
REGSET_FP,
|
||||
REGSET_LAST_BREAK,
|
||||
REGSET_TDB,
|
||||
REGSET_SYSTEM_CALL,
|
||||
REGSET_GENERAL_EXTENDED,
|
||||
};
|
||||
@ -52,6 +53,22 @@ void update_per_regs(struct task_struct *task)
|
||||
struct thread_struct *thread = &task->thread;
|
||||
struct per_regs old, new;
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
/* Take care of the enable/disable of transactional execution. */
|
||||
if (MACHINE_HAS_TE) {
|
||||
unsigned long cr0, cr0_new;
|
||||
|
||||
__ctl_store(cr0, 0, 0);
|
||||
/* set or clear transaction execution bits 8 and 9. */
|
||||
if (task->thread.per_flags & PER_FLAG_NO_TE)
|
||||
cr0_new = cr0 & ~(3UL << 54);
|
||||
else
|
||||
cr0_new = cr0 | (3UL << 54);
|
||||
/* Only load control register 0 if necessary. */
|
||||
if (cr0 != cr0_new)
|
||||
__ctl_load(cr0_new, 0, 0);
|
||||
}
|
||||
#endif
|
||||
/* Copy user specified PER registers */
|
||||
new.control = thread->per_user.control;
|
||||
new.start = thread->per_user.start;
|
||||
@ -60,6 +77,10 @@ void update_per_regs(struct task_struct *task)
|
||||
/* merge TIF_SINGLE_STEP into user specified PER registers. */
|
||||
if (test_tsk_thread_flag(task, TIF_SINGLE_STEP)) {
|
||||
new.control |= PER_EVENT_IFETCH;
|
||||
#ifdef CONFIG_64BIT
|
||||
new.control |= PER_CONTROL_SUSPENSION;
|
||||
new.control |= PER_EVENT_TRANSACTION_END;
|
||||
#endif
|
||||
new.start = 0;
|
||||
new.end = PSW_ADDR_INSN;
|
||||
}
|
||||
@ -100,6 +121,7 @@ void ptrace_disable(struct task_struct *task)
|
||||
memset(&task->thread.per_event, 0, sizeof(task->thread.per_event));
|
||||
clear_tsk_thread_flag(task, TIF_SINGLE_STEP);
|
||||
clear_tsk_thread_flag(task, TIF_PER_TRAP);
|
||||
task->thread.per_flags = 0;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_64BIT
|
||||
@ -416,6 +438,16 @@ long arch_ptrace(struct task_struct *child, long request,
|
||||
put_user(task_thread_info(child)->last_break,
|
||||
(unsigned long __user *) data);
|
||||
return 0;
|
||||
case PTRACE_ENABLE_TE:
|
||||
if (!MACHINE_HAS_TE)
|
||||
return -EIO;
|
||||
child->thread.per_flags &= ~PER_FLAG_NO_TE;
|
||||
return 0;
|
||||
case PTRACE_DISABLE_TE:
|
||||
if (!MACHINE_HAS_TE)
|
||||
return -EIO;
|
||||
child->thread.per_flags |= PER_FLAG_NO_TE;
|
||||
return 0;
|
||||
default:
|
||||
/* Removing high order bit from addr (only for 31 bit). */
|
||||
addr &= PSW_ADDR_INSN;
|
||||
@ -903,6 +935,28 @@ static int s390_last_break_set(struct task_struct *target,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s390_tdb_get(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
void *kbuf, void __user *ubuf)
|
||||
{
|
||||
struct pt_regs *regs = task_pt_regs(target);
|
||||
unsigned char *data;
|
||||
|
||||
if (!(regs->int_code & 0x200))
|
||||
return -ENODATA;
|
||||
data = target->thread.trap_tdb;
|
||||
return user_regset_copyout(&pos, &count, &kbuf, &ubuf, data, 0, 256);
|
||||
}
|
||||
|
||||
static int s390_tdb_set(struct task_struct *target,
|
||||
const struct user_regset *regset,
|
||||
unsigned int pos, unsigned int count,
|
||||
const void *kbuf, const void __user *ubuf)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int s390_system_call_get(struct task_struct *target,
|
||||
@ -951,6 +1005,14 @@ static const struct user_regset s390_regsets[] = {
|
||||
.get = s390_last_break_get,
|
||||
.set = s390_last_break_set,
|
||||
},
|
||||
[REGSET_TDB] = {
|
||||
.core_note_type = NT_S390_TDB,
|
||||
.n = 1,
|
||||
.size = 256,
|
||||
.align = 1,
|
||||
.get = s390_tdb_get,
|
||||
.set = s390_tdb_set,
|
||||
},
|
||||
#endif
|
||||
[REGSET_SYSTEM_CALL] = {
|
||||
.core_note_type = NT_S390_SYSTEM_CALL,
|
||||
@ -1148,6 +1210,14 @@ static const struct user_regset s390_compat_regsets[] = {
|
||||
.get = s390_compat_last_break_get,
|
||||
.set = s390_compat_last_break_set,
|
||||
},
|
||||
[REGSET_TDB] = {
|
||||
.core_note_type = NT_S390_TDB,
|
||||
.n = 1,
|
||||
.size = 256,
|
||||
.align = 1,
|
||||
.get = s390_tdb_get,
|
||||
.set = s390_tdb_set,
|
||||
},
|
||||
[REGSET_SYSTEM_CALL] = {
|
||||
.core_note_type = NT_S390_SYSTEM_CALL,
|
||||
.n = 1,
|
||||
|
150
arch/s390/kernel/runtime_instr.c
Normal file
150
arch/s390/kernel/runtime_instr.c
Normal file
@ -0,0 +1,150 @@
|
||||
/*
|
||||
* Copyright IBM Corp. 2012
|
||||
* Author(s): Jan Glauber <jang@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/syscalls.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <asm/runtime_instr.h>
|
||||
#include <asm/cpu_mf.h>
|
||||
#include <asm/irq.h>
|
||||
|
||||
/* empty control block to disable RI by loading it */
|
||||
struct runtime_instr_cb runtime_instr_empty_cb;
|
||||
|
||||
static int runtime_instr_avail(void)
|
||||
{
|
||||
return test_facility(64);
|
||||
}
|
||||
|
||||
static void disable_runtime_instr(void)
|
||||
{
|
||||
struct pt_regs *regs = task_pt_regs(current);
|
||||
|
||||
load_runtime_instr_cb(&runtime_instr_empty_cb);
|
||||
|
||||
/*
|
||||
* Make sure the RI bit is deleted from the PSW. If the user did not
|
||||
* switch off RI before the system call the process will get a
|
||||
* specification exception otherwise.
|
||||
*/
|
||||
regs->psw.mask &= ~PSW_MASK_RI;
|
||||
}
|
||||
|
||||
static void init_runtime_instr_cb(struct runtime_instr_cb *cb)
|
||||
{
|
||||
cb->buf_limit = 0xfff;
|
||||
if (s390_user_mode == HOME_SPACE_MODE)
|
||||
cb->home_space = 1;
|
||||
cb->int_requested = 1;
|
||||
cb->pstate = 1;
|
||||
cb->pstate_set_buf = 1;
|
||||
cb->pstate_sample = 1;
|
||||
cb->pstate_collect = 1;
|
||||
cb->key = PAGE_DEFAULT_KEY;
|
||||
cb->valid = 1;
|
||||
}
|
||||
|
||||
void exit_thread_runtime_instr(void)
|
||||
{
|
||||
struct task_struct *task = current;
|
||||
|
||||
if (!task->thread.ri_cb)
|
||||
return;
|
||||
disable_runtime_instr();
|
||||
kfree(task->thread.ri_cb);
|
||||
task->thread.ri_signum = 0;
|
||||
task->thread.ri_cb = NULL;
|
||||
}
|
||||
|
||||
static void runtime_instr_int_handler(struct ext_code ext_code,
|
||||
unsigned int param32, unsigned long param64)
|
||||
{
|
||||
struct siginfo info;
|
||||
|
||||
if (!(param32 & CPU_MF_INT_RI_MASK))
|
||||
return;
|
||||
|
||||
kstat_cpu(smp_processor_id()).irqs[EXTINT_CMR]++;
|
||||
|
||||
if (!current->thread.ri_cb)
|
||||
return;
|
||||
if (current->thread.ri_signum < SIGRTMIN ||
|
||||
current->thread.ri_signum > SIGRTMAX) {
|
||||
WARN_ON_ONCE(1);
|
||||
return;
|
||||
}
|
||||
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.si_signo = current->thread.ri_signum;
|
||||
info.si_code = SI_QUEUE;
|
||||
if (param32 & CPU_MF_INT_RI_BUF_FULL)
|
||||
info.si_int = ENOBUFS;
|
||||
else if (param32 & CPU_MF_INT_RI_HALTED)
|
||||
info.si_int = ECANCELED;
|
||||
else
|
||||
return; /* unknown reason */
|
||||
|
||||
send_sig_info(current->thread.ri_signum, &info, current);
|
||||
}
|
||||
|
||||
SYSCALL_DEFINE2(s390_runtime_instr, int, command, int, signum)
|
||||
{
|
||||
struct runtime_instr_cb *cb;
|
||||
|
||||
if (!runtime_instr_avail())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (command == S390_RUNTIME_INSTR_STOP) {
|
||||
preempt_disable();
|
||||
exit_thread_runtime_instr();
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (command != S390_RUNTIME_INSTR_START ||
|
||||
(signum < SIGRTMIN || signum > SIGRTMAX))
|
||||
return -EINVAL;
|
||||
|
||||
if (!current->thread.ri_cb) {
|
||||
cb = kzalloc(sizeof(*cb), GFP_KERNEL);
|
||||
if (!cb)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
cb = current->thread.ri_cb;
|
||||
memset(cb, 0, sizeof(*cb));
|
||||
}
|
||||
|
||||
init_runtime_instr_cb(cb);
|
||||
current->thread.ri_signum = signum;
|
||||
|
||||
/* now load the control block to make it available */
|
||||
preempt_disable();
|
||||
current->thread.ri_cb = cb;
|
||||
load_runtime_instr_cb(cb);
|
||||
preempt_enable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init runtime_instr_init(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (!runtime_instr_avail())
|
||||
return 0;
|
||||
|
||||
measurement_alert_subclass_register();
|
||||
rc = register_external_interrupt(0x1407, runtime_instr_int_handler);
|
||||
if (rc)
|
||||
measurement_alert_subclass_unregister();
|
||||
else
|
||||
pr_info("Runtime instrumentation facility initialized\n");
|
||||
return rc;
|
||||
}
|
||||
device_initcall(runtime_instr_init);
|
@ -8,3 +8,5 @@ EXPORT_SYMBOL(_mcount);
|
||||
#if defined(CONFIG_KVM) || defined(CONFIG_KVM_MODULE)
|
||||
EXPORT_SYMBOL(sie64a);
|
||||
#endif
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
EXPORT_SYMBOL(memset);
|
||||
|
@ -302,10 +302,10 @@ static int __init parse_vmalloc(char *arg)
|
||||
}
|
||||
early_param("vmalloc", parse_vmalloc);
|
||||
|
||||
unsigned int addressing_mode = HOME_SPACE_MODE;
|
||||
EXPORT_SYMBOL_GPL(addressing_mode);
|
||||
unsigned int s390_user_mode = PRIMARY_SPACE_MODE;
|
||||
EXPORT_SYMBOL_GPL(s390_user_mode);
|
||||
|
||||
static int set_amode_primary(void)
|
||||
static void __init set_user_mode_primary(void)
|
||||
{
|
||||
psw_kernel_bits = (psw_kernel_bits & ~PSW_MASK_ASC) | PSW_ASC_HOME;
|
||||
psw_user_bits = (psw_user_bits & ~PSW_MASK_ASC) | PSW_ASC_PRIMARY;
|
||||
@ -313,48 +313,30 @@ static int set_amode_primary(void)
|
||||
psw32_user_bits =
|
||||
(psw32_user_bits & ~PSW32_MASK_ASC) | PSW32_ASC_PRIMARY;
|
||||
#endif
|
||||
|
||||
if (MACHINE_HAS_MVCOS) {
|
||||
memcpy(&uaccess, &uaccess_mvcos_switch, sizeof(uaccess));
|
||||
return 1;
|
||||
} else {
|
||||
memcpy(&uaccess, &uaccess_pt, sizeof(uaccess));
|
||||
return 0;
|
||||
}
|
||||
uaccess = MACHINE_HAS_MVCOS ? uaccess_mvcos_switch : uaccess_pt;
|
||||
}
|
||||
|
||||
/*
|
||||
* Switch kernel/user addressing modes?
|
||||
*/
|
||||
static int __init early_parse_switch_amode(char *p)
|
||||
{
|
||||
addressing_mode = PRIMARY_SPACE_MODE;
|
||||
return 0;
|
||||
}
|
||||
early_param("switch_amode", early_parse_switch_amode);
|
||||
|
||||
static int __init early_parse_user_mode(char *p)
|
||||
{
|
||||
if (p && strcmp(p, "primary") == 0)
|
||||
addressing_mode = PRIMARY_SPACE_MODE;
|
||||
s390_user_mode = PRIMARY_SPACE_MODE;
|
||||
else if (!p || strcmp(p, "home") == 0)
|
||||
addressing_mode = HOME_SPACE_MODE;
|
||||
s390_user_mode = HOME_SPACE_MODE;
|
||||
else
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
early_param("user_mode", early_parse_user_mode);
|
||||
|
||||
static void setup_addressing_mode(void)
|
||||
static void __init setup_addressing_mode(void)
|
||||
{
|
||||
if (addressing_mode == PRIMARY_SPACE_MODE) {
|
||||
if (set_amode_primary())
|
||||
pr_info("Address spaces switched, "
|
||||
"mvcos available\n");
|
||||
else
|
||||
pr_info("Address spaces switched, "
|
||||
"mvcos not available\n");
|
||||
}
|
||||
if (s390_user_mode != PRIMARY_SPACE_MODE)
|
||||
return;
|
||||
set_user_mode_primary();
|
||||
if (MACHINE_HAS_MVCOS)
|
||||
pr_info("Address spaces switched, mvcos available\n");
|
||||
else
|
||||
pr_info("Address spaces switched, mvcos not available\n");
|
||||
}
|
||||
|
||||
void *restart_stack __attribute__((__section__(".data")));
|
||||
@ -602,9 +584,7 @@ static void __init setup_memory_end(void)
|
||||
|
||||
static void __init setup_vmcoreinfo(void)
|
||||
{
|
||||
#ifdef CONFIG_KEXEC
|
||||
mem_assign_absolute(S390_lowcore.vmcore_info, paddr_vmcoreinfo_note());
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
@ -980,6 +960,12 @@ static void __init setup_hwcaps(void)
|
||||
* HWCAP_S390_HIGH_GPRS is bit 9.
|
||||
*/
|
||||
elf_hwcap |= HWCAP_S390_HIGH_GPRS;
|
||||
|
||||
/*
|
||||
* Transactional execution support HWCAP_S390_TE is bit 10.
|
||||
*/
|
||||
if (test_facility(50) && test_facility(73))
|
||||
elf_hwcap |= HWCAP_S390_TE;
|
||||
#endif
|
||||
|
||||
get_cpu_id(&cpu_id);
|
||||
|
@ -66,7 +66,7 @@ struct pcpu {
|
||||
unsigned long panic_stack; /* panic stack for the cpu */
|
||||
unsigned long ec_mask; /* bit mask for ec_xxx functions */
|
||||
int state; /* physical cpu state */
|
||||
u32 status; /* last status received via sigp */
|
||||
int polarization; /* physical polarization */
|
||||
u16 address; /* physical cpu address */
|
||||
};
|
||||
|
||||
@ -74,6 +74,10 @@ static u8 boot_cpu_type;
|
||||
static u16 boot_cpu_address;
|
||||
static struct pcpu pcpu_devices[NR_CPUS];
|
||||
|
||||
/*
|
||||
* The smp_cpu_state_mutex must be held when changing the state or polarization
|
||||
* member of a pcpu data structure within the pcpu_devices arreay.
|
||||
*/
|
||||
DEFINE_MUTEX(smp_cpu_state_mutex);
|
||||
|
||||
/*
|
||||
@ -99,7 +103,7 @@ static inline int __pcpu_sigp_relax(u16 addr, u8 order, u32 parm, u32 *status)
|
||||
int cc;
|
||||
|
||||
while (1) {
|
||||
cc = __pcpu_sigp(addr, order, parm, status);
|
||||
cc = __pcpu_sigp(addr, order, parm, NULL);
|
||||
if (cc != SIGP_CC_BUSY)
|
||||
return cc;
|
||||
cpu_relax();
|
||||
@ -111,7 +115,7 @@ static int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm)
|
||||
int cc, retry;
|
||||
|
||||
for (retry = 0; ; retry++) {
|
||||
cc = __pcpu_sigp(pcpu->address, order, parm, &pcpu->status);
|
||||
cc = __pcpu_sigp(pcpu->address, order, parm, NULL);
|
||||
if (cc != SIGP_CC_BUSY)
|
||||
break;
|
||||
if (retry >= 3)
|
||||
@ -122,16 +126,18 @@ static int pcpu_sigp_retry(struct pcpu *pcpu, u8 order, u32 parm)
|
||||
|
||||
static inline int pcpu_stopped(struct pcpu *pcpu)
|
||||
{
|
||||
u32 uninitialized_var(status);
|
||||
|
||||
if (__pcpu_sigp(pcpu->address, SIGP_SENSE,
|
||||
0, &pcpu->status) != SIGP_CC_STATUS_STORED)
|
||||
0, &status) != SIGP_CC_STATUS_STORED)
|
||||
return 0;
|
||||
return !!(pcpu->status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED));
|
||||
return !!(status & (SIGP_STATUS_CHECK_STOP|SIGP_STATUS_STOPPED));
|
||||
}
|
||||
|
||||
static inline int pcpu_running(struct pcpu *pcpu)
|
||||
{
|
||||
if (__pcpu_sigp(pcpu->address, SIGP_SENSE_RUNNING,
|
||||
0, &pcpu->status) != SIGP_CC_STATUS_STORED)
|
||||
0, NULL) != SIGP_CC_STATUS_STORED)
|
||||
return 1;
|
||||
/* Status stored condition code is equivalent to cpu not running. */
|
||||
return 0;
|
||||
@ -586,6 +592,16 @@ static inline void smp_get_save_area(int cpu, u16 address) { }
|
||||
|
||||
#endif /* CONFIG_ZFCPDUMP || CONFIG_CRASH_DUMP */
|
||||
|
||||
void smp_cpu_set_polarization(int cpu, int val)
|
||||
{
|
||||
pcpu_devices[cpu].polarization = val;
|
||||
}
|
||||
|
||||
int smp_cpu_get_polarization(int cpu)
|
||||
{
|
||||
return pcpu_devices[cpu].polarization;
|
||||
}
|
||||
|
||||
static struct sclp_cpu_info *smp_get_cpu_info(void)
|
||||
{
|
||||
static int use_sigp_detection;
|
||||
@ -628,7 +644,7 @@ static int __devinit __smp_rescan_cpus(struct sclp_cpu_info *info,
|
||||
pcpu->address = info->cpu[i].address;
|
||||
pcpu->state = (cpu >= info->configured) ?
|
||||
CPU_STATE_STANDBY : CPU_STATE_CONFIGURED;
|
||||
cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
set_cpu_present(cpu, true);
|
||||
if (sysfs_add && smp_add_present_cpu(cpu) != 0)
|
||||
set_cpu_present(cpu, false);
|
||||
@ -796,7 +812,7 @@ void __init smp_prepare_boot_cpu(void)
|
||||
pcpu->async_stack = S390_lowcore.async_stack - ASYNC_SIZE;
|
||||
pcpu->panic_stack = S390_lowcore.panic_stack - PAGE_SIZE;
|
||||
S390_lowcore.percpu_offset = __per_cpu_offset[0];
|
||||
cpu_set_polarization(0, POLARIZATION_UNKNOWN);
|
||||
smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN);
|
||||
set_cpu_present(0, true);
|
||||
set_cpu_online(0, true);
|
||||
}
|
||||
@ -862,7 +878,7 @@ static ssize_t cpu_configure_store(struct device *dev,
|
||||
if (rc)
|
||||
break;
|
||||
pcpu->state = CPU_STATE_STANDBY;
|
||||
cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
topology_expect_change();
|
||||
break;
|
||||
case 1:
|
||||
@ -872,7 +888,7 @@ static ssize_t cpu_configure_store(struct device *dev,
|
||||
if (rc)
|
||||
break;
|
||||
pcpu->state = CPU_STATE_CONFIGURED;
|
||||
cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
topology_expect_change();
|
||||
break;
|
||||
default:
|
||||
@ -959,23 +975,17 @@ static int __cpuinit smp_cpu_notify(struct notifier_block *self,
|
||||
struct device *s = &c->dev;
|
||||
int err = 0;
|
||||
|
||||
switch (action) {
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_ONLINE:
|
||||
case CPU_ONLINE_FROZEN:
|
||||
err = sysfs_create_group(&s->kobj, &cpu_online_attr_group);
|
||||
break;
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
sysfs_remove_group(&s->kobj, &cpu_online_attr_group);
|
||||
break;
|
||||
}
|
||||
return notifier_from_errno(err);
|
||||
}
|
||||
|
||||
static struct notifier_block __cpuinitdata smp_cpu_nb = {
|
||||
.notifier_call = smp_cpu_notify,
|
||||
};
|
||||
|
||||
static int __devinit smp_add_present_cpu(int cpu)
|
||||
{
|
||||
struct cpu *c = &pcpu_devices[cpu].cpu;
|
||||
@ -1050,7 +1060,7 @@ static int __init s390_smp_init(void)
|
||||
{
|
||||
int cpu, rc;
|
||||
|
||||
register_cpu_notifier(&smp_cpu_nb);
|
||||
hotcpu_notifier(smp_cpu_notify, 0);
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
rc = device_create_file(cpu_subsys.dev_root, &dev_attr_rescan);
|
||||
if (rc)
|
||||
|
@ -350,3 +350,5 @@ SYSCALL(sys_syncfs,sys_syncfs,sys_syncfs_wrapper)
|
||||
SYSCALL(sys_setns,sys_setns,sys_setns_wrapper)
|
||||
SYSCALL(sys_process_vm_readv,sys_process_vm_readv,compat_sys_process_vm_readv_wrapper) /* 340 */
|
||||
SYSCALL(sys_process_vm_writev,sys_process_vm_writev,compat_sys_process_vm_writev_wrapper)
|
||||
SYSCALL(sys_ni_syscall,sys_s390_runtime_instr,sys_s390_runtime_instr_wrapper)
|
||||
SYSCALL(sys_kcmp,sys_kcmp,sys_kcmp_wrapper)
|
||||
|
@ -22,17 +22,41 @@
|
||||
#include <math-emu/soft-fp.h>
|
||||
#include <math-emu/single.h>
|
||||
|
||||
static inline int stsi_0(void)
|
||||
int topology_max_mnest;
|
||||
|
||||
/*
|
||||
* stsi - store system information
|
||||
*
|
||||
* Returns the current configuration level if function code 0 was specified.
|
||||
* Otherwise returns 0 on success or a negative value on error.
|
||||
*/
|
||||
int stsi(void *sysinfo, int fc, int sel1, int sel2)
|
||||
{
|
||||
int rc = stsi(NULL, 0, 0, 0);
|
||||
return rc == -ENOSYS ? rc : (((unsigned int) rc) >> 28);
|
||||
register int r0 asm("0") = (fc << 28) | sel1;
|
||||
register int r1 asm("1") = sel2;
|
||||
int rc = 0;
|
||||
|
||||
asm volatile(
|
||||
" stsi 0(%3)\n"
|
||||
"0: jz 2f\n"
|
||||
"1: lhi %1,%4\n"
|
||||
"2:\n"
|
||||
EX_TABLE(0b, 1b)
|
||||
: "+d" (r0), "+d" (rc)
|
||||
: "d" (r1), "a" (sysinfo), "K" (-EOPNOTSUPP)
|
||||
: "cc", "memory");
|
||||
if (rc)
|
||||
return rc;
|
||||
return fc ? 0 : ((unsigned int) r0) >> 28;
|
||||
}
|
||||
EXPORT_SYMBOL(stsi);
|
||||
|
||||
static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
|
||||
static void stsi_1_1_1(struct seq_file *m, struct sysinfo_1_1_1 *info)
|
||||
{
|
||||
if (stsi(info, 1, 1, 1) == -ENOSYS)
|
||||
return len;
|
||||
int i;
|
||||
|
||||
if (stsi(info, 1, 1, 1))
|
||||
return;
|
||||
EBCASC(info->manufacturer, sizeof(info->manufacturer));
|
||||
EBCASC(info->type, sizeof(info->type));
|
||||
EBCASC(info->model, sizeof(info->model));
|
||||
@ -41,242 +65,197 @@ static int stsi_1_1_1(struct sysinfo_1_1_1 *info, char *page, int len)
|
||||
EBCASC(info->model_capacity, sizeof(info->model_capacity));
|
||||
EBCASC(info->model_perm_cap, sizeof(info->model_perm_cap));
|
||||
EBCASC(info->model_temp_cap, sizeof(info->model_temp_cap));
|
||||
len += sprintf(page + len, "Manufacturer: %-16.16s\n",
|
||||
info->manufacturer);
|
||||
len += sprintf(page + len, "Type: %-4.4s\n",
|
||||
info->type);
|
||||
seq_printf(m, "Manufacturer: %-16.16s\n", info->manufacturer);
|
||||
seq_printf(m, "Type: %-4.4s\n", info->type);
|
||||
/*
|
||||
* Sigh: the model field has been renamed with System z9
|
||||
* to model_capacity and a new model field has been added
|
||||
* after the plant field. To avoid confusing older programs
|
||||
* the "Model:" prints "model_capacity model" or just
|
||||
* "model_capacity" if the model string is empty .
|
||||
*/
|
||||
seq_printf(m, "Model: %-16.16s", info->model_capacity);
|
||||
if (info->model[0] != '\0')
|
||||
/*
|
||||
* Sigh: the model field has been renamed with System z9
|
||||
* to model_capacity and a new model field has been added
|
||||
* after the plant field. To avoid confusing older programs
|
||||
* the "Model:" prints "model_capacity model" or just
|
||||
* "model_capacity" if the model string is empty .
|
||||
*/
|
||||
len += sprintf(page + len,
|
||||
"Model: %-16.16s %-16.16s\n",
|
||||
info->model_capacity, info->model);
|
||||
else
|
||||
len += sprintf(page + len, "Model: %-16.16s\n",
|
||||
info->model_capacity);
|
||||
len += sprintf(page + len, "Sequence Code: %-16.16s\n",
|
||||
info->sequence);
|
||||
len += sprintf(page + len, "Plant: %-4.4s\n",
|
||||
info->plant);
|
||||
len += sprintf(page + len, "Model Capacity: %-16.16s %08u\n",
|
||||
info->model_capacity, *(u32 *) info->model_cap_rating);
|
||||
if (info->model_perm_cap[0] != '\0')
|
||||
len += sprintf(page + len,
|
||||
"Model Perm. Capacity: %-16.16s %08u\n",
|
||||
info->model_perm_cap,
|
||||
*(u32 *) info->model_perm_cap_rating);
|
||||
if (info->model_temp_cap[0] != '\0')
|
||||
len += sprintf(page + len,
|
||||
"Model Temp. Capacity: %-16.16s %08u\n",
|
||||
info->model_temp_cap,
|
||||
*(u32 *) info->model_temp_cap_rating);
|
||||
seq_printf(m, " %-16.16s", info->model);
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, "Sequence Code: %-16.16s\n", info->sequence);
|
||||
seq_printf(m, "Plant: %-4.4s\n", info->plant);
|
||||
seq_printf(m, "Model Capacity: %-16.16s %08u\n",
|
||||
info->model_capacity, info->model_cap_rating);
|
||||
if (info->model_perm_cap_rating)
|
||||
seq_printf(m, "Model Perm. Capacity: %-16.16s %08u\n",
|
||||
info->model_perm_cap,
|
||||
info->model_perm_cap_rating);
|
||||
if (info->model_temp_cap_rating)
|
||||
seq_printf(m, "Model Temp. Capacity: %-16.16s %08u\n",
|
||||
info->model_temp_cap,
|
||||
info->model_temp_cap_rating);
|
||||
if (info->ncr)
|
||||
seq_printf(m, "Nominal Cap. Rating: %08u\n", info->ncr);
|
||||
if (info->npr)
|
||||
seq_printf(m, "Nominal Perm. Rating: %08u\n", info->npr);
|
||||
if (info->ntr)
|
||||
seq_printf(m, "Nominal Temp. Rating: %08u\n", info->ntr);
|
||||
if (info->cai) {
|
||||
len += sprintf(page + len,
|
||||
"Capacity Adj. Ind.: %d\n",
|
||||
info->cai);
|
||||
len += sprintf(page + len, "Capacity Ch. Reason: %d\n",
|
||||
info->ccr);
|
||||
seq_printf(m, "Capacity Adj. Ind.: %d\n", info->cai);
|
||||
seq_printf(m, "Capacity Ch. Reason: %d\n", info->ccr);
|
||||
seq_printf(m, "Capacity Transient: %d\n", info->t);
|
||||
}
|
||||
if (info->p) {
|
||||
for (i = 1; i <= ARRAY_SIZE(info->typepct); i++) {
|
||||
seq_printf(m, "Type %d Percentage: %d\n",
|
||||
i, info->typepct[i - 1]);
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int stsi_15_1_x(struct sysinfo_15_1_x *info, char *page, int len)
|
||||
static void stsi_15_1_x(struct seq_file *m, struct sysinfo_15_1_x *info)
|
||||
{
|
||||
static int max_mnest;
|
||||
int i, rc;
|
||||
|
||||
len += sprintf(page + len, "\n");
|
||||
seq_putc(m, '\n');
|
||||
if (!MACHINE_HAS_TOPOLOGY)
|
||||
return len;
|
||||
if (max_mnest) {
|
||||
stsi(info, 15, 1, max_mnest);
|
||||
} else {
|
||||
for (max_mnest = 6; max_mnest > 1; max_mnest--) {
|
||||
rc = stsi(info, 15, 1, max_mnest);
|
||||
if (rc != -ENOSYS)
|
||||
break;
|
||||
}
|
||||
}
|
||||
len += sprintf(page + len, "CPU Topology HW: ");
|
||||
return;
|
||||
if (stsi(info, 15, 1, topology_max_mnest))
|
||||
return;
|
||||
seq_printf(m, "CPU Topology HW: ");
|
||||
for (i = 0; i < TOPOLOGY_NR_MAG; i++)
|
||||
len += sprintf(page + len, " %d", info->mag[i]);
|
||||
len += sprintf(page + len, "\n");
|
||||
seq_printf(m, " %d", info->mag[i]);
|
||||
seq_putc(m, '\n');
|
||||
#ifdef CONFIG_SCHED_MC
|
||||
store_topology(info);
|
||||
len += sprintf(page + len, "CPU Topology SW: ");
|
||||
seq_printf(m, "CPU Topology SW: ");
|
||||
for (i = 0; i < TOPOLOGY_NR_MAG; i++)
|
||||
len += sprintf(page + len, " %d", info->mag[i]);
|
||||
len += sprintf(page + len, "\n");
|
||||
seq_printf(m, " %d", info->mag[i]);
|
||||
seq_putc(m, '\n');
|
||||
#endif
|
||||
return len;
|
||||
}
|
||||
|
||||
static int stsi_1_2_2(struct sysinfo_1_2_2 *info, char *page, int len)
|
||||
static void stsi_1_2_2(struct seq_file *m, struct sysinfo_1_2_2 *info)
|
||||
{
|
||||
struct sysinfo_1_2_2_extension *ext;
|
||||
int i;
|
||||
|
||||
if (stsi(info, 1, 2, 2) == -ENOSYS)
|
||||
return len;
|
||||
if (stsi(info, 1, 2, 2))
|
||||
return;
|
||||
ext = (struct sysinfo_1_2_2_extension *)
|
||||
((unsigned long) info + info->acc_offset);
|
||||
|
||||
len += sprintf(page + len, "CPUs Total: %d\n",
|
||||
info->cpus_total);
|
||||
len += sprintf(page + len, "CPUs Configured: %d\n",
|
||||
info->cpus_configured);
|
||||
len += sprintf(page + len, "CPUs Standby: %d\n",
|
||||
info->cpus_standby);
|
||||
len += sprintf(page + len, "CPUs Reserved: %d\n",
|
||||
info->cpus_reserved);
|
||||
|
||||
if (info->format == 1) {
|
||||
/*
|
||||
* Sigh 2. According to the specification the alternate
|
||||
* capability field is a 32 bit floating point number
|
||||
* if the higher order 8 bits are not zero. Printing
|
||||
* a floating point number in the kernel is a no-no,
|
||||
* always print the number as 32 bit unsigned integer.
|
||||
* The user-space needs to know about the strange
|
||||
* encoding of the alternate cpu capability.
|
||||
*/
|
||||
len += sprintf(page + len, "Capability: %u %u\n",
|
||||
info->capability, ext->alt_capability);
|
||||
for (i = 2; i <= info->cpus_total; i++)
|
||||
len += sprintf(page + len,
|
||||
"Adjustment %02d-way: %u %u\n",
|
||||
i, info->adjustment[i-2],
|
||||
ext->alt_adjustment[i-2]);
|
||||
|
||||
} else {
|
||||
len += sprintf(page + len, "Capability: %u\n",
|
||||
info->capability);
|
||||
for (i = 2; i <= info->cpus_total; i++)
|
||||
len += sprintf(page + len,
|
||||
"Adjustment %02d-way: %u\n",
|
||||
i, info->adjustment[i-2]);
|
||||
seq_printf(m, "CPUs Total: %d\n", info->cpus_total);
|
||||
seq_printf(m, "CPUs Configured: %d\n", info->cpus_configured);
|
||||
seq_printf(m, "CPUs Standby: %d\n", info->cpus_standby);
|
||||
seq_printf(m, "CPUs Reserved: %d\n", info->cpus_reserved);
|
||||
/*
|
||||
* Sigh 2. According to the specification the alternate
|
||||
* capability field is a 32 bit floating point number
|
||||
* if the higher order 8 bits are not zero. Printing
|
||||
* a floating point number in the kernel is a no-no,
|
||||
* always print the number as 32 bit unsigned integer.
|
||||
* The user-space needs to know about the strange
|
||||
* encoding of the alternate cpu capability.
|
||||
*/
|
||||
seq_printf(m, "Capability: %u", info->capability);
|
||||
if (info->format == 1)
|
||||
seq_printf(m, " %u", ext->alt_capability);
|
||||
seq_putc(m, '\n');
|
||||
if (info->nominal_cap)
|
||||
seq_printf(m, "Nominal Capability: %d\n", info->nominal_cap);
|
||||
if (info->secondary_cap)
|
||||
seq_printf(m, "Secondary Capability: %d\n", info->secondary_cap);
|
||||
for (i = 2; i <= info->cpus_total; i++) {
|
||||
seq_printf(m, "Adjustment %02d-way: %u",
|
||||
i, info->adjustment[i-2]);
|
||||
if (info->format == 1)
|
||||
seq_printf(m, " %u", ext->alt_adjustment[i-2]);
|
||||
seq_putc(m, '\n');
|
||||
}
|
||||
|
||||
if (info->secondary_capability != 0)
|
||||
len += sprintf(page + len, "Secondary Capability: %d\n",
|
||||
info->secondary_capability);
|
||||
return len;
|
||||
}
|
||||
|
||||
static int stsi_2_2_2(struct sysinfo_2_2_2 *info, char *page, int len)
|
||||
static void stsi_2_2_2(struct seq_file *m, struct sysinfo_2_2_2 *info)
|
||||
{
|
||||
if (stsi(info, 2, 2, 2) == -ENOSYS)
|
||||
return len;
|
||||
|
||||
if (stsi(info, 2, 2, 2))
|
||||
return;
|
||||
EBCASC(info->name, sizeof(info->name));
|
||||
|
||||
len += sprintf(page + len, "\n");
|
||||
len += sprintf(page + len, "LPAR Number: %d\n",
|
||||
info->lpar_number);
|
||||
|
||||
len += sprintf(page + len, "LPAR Characteristics: ");
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, "LPAR Number: %d\n", info->lpar_number);
|
||||
seq_printf(m, "LPAR Characteristics: ");
|
||||
if (info->characteristics & LPAR_CHAR_DEDICATED)
|
||||
len += sprintf(page + len, "Dedicated ");
|
||||
seq_printf(m, "Dedicated ");
|
||||
if (info->characteristics & LPAR_CHAR_SHARED)
|
||||
len += sprintf(page + len, "Shared ");
|
||||
seq_printf(m, "Shared ");
|
||||
if (info->characteristics & LPAR_CHAR_LIMITED)
|
||||
len += sprintf(page + len, "Limited ");
|
||||
len += sprintf(page + len, "\n");
|
||||
|
||||
len += sprintf(page + len, "LPAR Name: %-8.8s\n",
|
||||
info->name);
|
||||
|
||||
len += sprintf(page + len, "LPAR Adjustment: %d\n",
|
||||
info->caf);
|
||||
|
||||
len += sprintf(page + len, "LPAR CPUs Total: %d\n",
|
||||
info->cpus_total);
|
||||
len += sprintf(page + len, "LPAR CPUs Configured: %d\n",
|
||||
info->cpus_configured);
|
||||
len += sprintf(page + len, "LPAR CPUs Standby: %d\n",
|
||||
info->cpus_standby);
|
||||
len += sprintf(page + len, "LPAR CPUs Reserved: %d\n",
|
||||
info->cpus_reserved);
|
||||
len += sprintf(page + len, "LPAR CPUs Dedicated: %d\n",
|
||||
info->cpus_dedicated);
|
||||
len += sprintf(page + len, "LPAR CPUs Shared: %d\n",
|
||||
info->cpus_shared);
|
||||
return len;
|
||||
seq_printf(m, "Limited ");
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, "LPAR Name: %-8.8s\n", info->name);
|
||||
seq_printf(m, "LPAR Adjustment: %d\n", info->caf);
|
||||
seq_printf(m, "LPAR CPUs Total: %d\n", info->cpus_total);
|
||||
seq_printf(m, "LPAR CPUs Configured: %d\n", info->cpus_configured);
|
||||
seq_printf(m, "LPAR CPUs Standby: %d\n", info->cpus_standby);
|
||||
seq_printf(m, "LPAR CPUs Reserved: %d\n", info->cpus_reserved);
|
||||
seq_printf(m, "LPAR CPUs Dedicated: %d\n", info->cpus_dedicated);
|
||||
seq_printf(m, "LPAR CPUs Shared: %d\n", info->cpus_shared);
|
||||
}
|
||||
|
||||
static int stsi_3_2_2(struct sysinfo_3_2_2 *info, char *page, int len)
|
||||
static void stsi_3_2_2(struct seq_file *m, struct sysinfo_3_2_2 *info)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (stsi(info, 3, 2, 2) == -ENOSYS)
|
||||
return len;
|
||||
if (stsi(info, 3, 2, 2))
|
||||
return;
|
||||
for (i = 0; i < info->count; i++) {
|
||||
EBCASC(info->vm[i].name, sizeof(info->vm[i].name));
|
||||
EBCASC(info->vm[i].cpi, sizeof(info->vm[i].cpi));
|
||||
len += sprintf(page + len, "\n");
|
||||
len += sprintf(page + len, "VM%02d Name: %-8.8s\n",
|
||||
i, info->vm[i].name);
|
||||
len += sprintf(page + len, "VM%02d Control Program: %-16.16s\n",
|
||||
i, info->vm[i].cpi);
|
||||
|
||||
len += sprintf(page + len, "VM%02d Adjustment: %d\n",
|
||||
i, info->vm[i].caf);
|
||||
|
||||
len += sprintf(page + len, "VM%02d CPUs Total: %d\n",
|
||||
i, info->vm[i].cpus_total);
|
||||
len += sprintf(page + len, "VM%02d CPUs Configured: %d\n",
|
||||
i, info->vm[i].cpus_configured);
|
||||
len += sprintf(page + len, "VM%02d CPUs Standby: %d\n",
|
||||
i, info->vm[i].cpus_standby);
|
||||
len += sprintf(page + len, "VM%02d CPUs Reserved: %d\n",
|
||||
i, info->vm[i].cpus_reserved);
|
||||
seq_putc(m, '\n');
|
||||
seq_printf(m, "VM%02d Name: %-8.8s\n", i, info->vm[i].name);
|
||||
seq_printf(m, "VM%02d Control Program: %-16.16s\n", i, info->vm[i].cpi);
|
||||
seq_printf(m, "VM%02d Adjustment: %d\n", i, info->vm[i].caf);
|
||||
seq_printf(m, "VM%02d CPUs Total: %d\n", i, info->vm[i].cpus_total);
|
||||
seq_printf(m, "VM%02d CPUs Configured: %d\n", i, info->vm[i].cpus_configured);
|
||||
seq_printf(m, "VM%02d CPUs Standby: %d\n", i, info->vm[i].cpus_standby);
|
||||
seq_printf(m, "VM%02d CPUs Reserved: %d\n", i, info->vm[i].cpus_reserved);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static int proc_read_sysinfo(char *page, char **start,
|
||||
off_t off, int count,
|
||||
int *eof, void *data)
|
||||
static int sysinfo_show(struct seq_file *m, void *v)
|
||||
{
|
||||
unsigned long info = get_zeroed_page(GFP_KERNEL);
|
||||
int level, len;
|
||||
void *info = (void *)get_zeroed_page(GFP_KERNEL);
|
||||
int level;
|
||||
|
||||
if (!info)
|
||||
return 0;
|
||||
|
||||
len = 0;
|
||||
level = stsi_0();
|
||||
level = stsi(NULL, 0, 0, 0);
|
||||
if (level >= 1)
|
||||
len = stsi_1_1_1((struct sysinfo_1_1_1 *) info, page, len);
|
||||
|
||||
stsi_1_1_1(m, info);
|
||||
if (level >= 1)
|
||||
len = stsi_15_1_x((struct sysinfo_15_1_x *) info, page, len);
|
||||
|
||||
stsi_15_1_x(m, info);
|
||||
if (level >= 1)
|
||||
len = stsi_1_2_2((struct sysinfo_1_2_2 *) info, page, len);
|
||||
|
||||
stsi_1_2_2(m, info);
|
||||
if (level >= 2)
|
||||
len = stsi_2_2_2((struct sysinfo_2_2_2 *) info, page, len);
|
||||
|
||||
stsi_2_2_2(m, info);
|
||||
if (level >= 3)
|
||||
len = stsi_3_2_2((struct sysinfo_3_2_2 *) info, page, len);
|
||||
|
||||
free_page(info);
|
||||
return len;
|
||||
}
|
||||
|
||||
static __init int create_proc_sysinfo(void)
|
||||
{
|
||||
create_proc_read_entry("sysinfo", 0444, NULL,
|
||||
proc_read_sysinfo, NULL);
|
||||
stsi_3_2_2(m, info);
|
||||
free_page((unsigned long)info);
|
||||
return 0;
|
||||
}
|
||||
device_initcall(create_proc_sysinfo);
|
||||
|
||||
static int sysinfo_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
return single_open(file, sysinfo_show, NULL);
|
||||
}
|
||||
|
||||
static const struct file_operations sysinfo_fops = {
|
||||
.open = sysinfo_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = single_release,
|
||||
};
|
||||
|
||||
static int __init sysinfo_create_proc(void)
|
||||
{
|
||||
proc_create("sysinfo", 0444, NULL, &sysinfo_fops);
|
||||
return 0;
|
||||
}
|
||||
device_initcall(sysinfo_create_proc);
|
||||
|
||||
/*
|
||||
* Service levels interface.
|
||||
@ -407,7 +386,7 @@ void s390_adjust_jiffies(void)
|
||||
if (!info)
|
||||
return;
|
||||
|
||||
if (stsi(info, 1, 2, 2) != -ENOSYS) {
|
||||
if (stsi(info, 1, 2, 2) == 0) {
|
||||
/*
|
||||
* Major sigh. The cpu capability encoding is "special".
|
||||
* If the first 9 bits of info->capability are 0 then it
|
||||
|
@ -329,7 +329,7 @@ static unsigned long clock_sync_flags;
|
||||
* The synchronous get_clock function. It will write the current clock
|
||||
* value to the clock pointer and return 0 if the clock is in sync with
|
||||
* the external time source. If the clock mode is local it will return
|
||||
* -ENOSYS and -EAGAIN if the clock is not in sync with the external
|
||||
* -EOPNOTSUPP and -EAGAIN if the clock is not in sync with the external
|
||||
* reference.
|
||||
*/
|
||||
int get_sync_clock(unsigned long long *clock)
|
||||
@ -347,7 +347,7 @@ int get_sync_clock(unsigned long long *clock)
|
||||
return 0;
|
||||
if (!test_bit(CLOCK_SYNC_HAS_ETR, &clock_sync_flags) &&
|
||||
!test_bit(CLOCK_SYNC_HAS_STP, &clock_sync_flags))
|
||||
return -ENOSYS;
|
||||
return -EOPNOTSUPP;
|
||||
if (!test_bit(CLOCK_SYNC_ETR, &clock_sync_flags) &&
|
||||
!test_bit(CLOCK_SYNC_STP, &clock_sync_flags))
|
||||
return -EACCES;
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <linux/cpu.h>
|
||||
#include <linux/smp.h>
|
||||
#include <linux/mm.h>
|
||||
#include <asm/sysinfo.h>
|
||||
|
||||
#define PTF_HORIZONTAL (0UL)
|
||||
#define PTF_VERTICAL (1UL)
|
||||
@ -44,9 +45,6 @@ static struct mask_info book_info;
|
||||
cpumask_t cpu_book_map[NR_CPUS];
|
||||
unsigned char cpu_book_id[NR_CPUS];
|
||||
|
||||
/* smp_cpu_state_mutex must be held when accessing this array */
|
||||
int cpu_polarization[NR_CPUS];
|
||||
|
||||
static cpumask_t cpu_group_map(struct mask_info *info, unsigned int cpu)
|
||||
{
|
||||
cpumask_t mask;
|
||||
@ -75,10 +73,7 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
|
||||
{
|
||||
unsigned int cpu;
|
||||
|
||||
for (cpu = find_first_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS);
|
||||
cpu < TOPOLOGY_CPU_BITS;
|
||||
cpu = find_next_bit(&tl_cpu->mask[0], TOPOLOGY_CPU_BITS, cpu + 1))
|
||||
{
|
||||
for_each_set_bit(cpu, &tl_cpu->mask[0], TOPOLOGY_CPU_BITS) {
|
||||
unsigned int rcpu;
|
||||
int lcpu;
|
||||
|
||||
@ -94,7 +89,7 @@ static struct mask_info *add_cpus_to_mask(struct topology_cpu *tl_cpu,
|
||||
} else {
|
||||
cpu_core_id[lcpu] = core->id;
|
||||
}
|
||||
cpu_set_polarization(lcpu, tl_cpu->pp);
|
||||
smp_cpu_set_polarization(lcpu, tl_cpu->pp);
|
||||
}
|
||||
}
|
||||
return core;
|
||||
@ -201,7 +196,7 @@ static void topology_update_polarization_simple(void)
|
||||
|
||||
mutex_lock(&smp_cpu_state_mutex);
|
||||
for_each_possible_cpu(cpu)
|
||||
cpu_set_polarization(cpu, POLARIZATION_HRZ);
|
||||
smp_cpu_set_polarization(cpu, POLARIZATION_HRZ);
|
||||
mutex_unlock(&smp_cpu_state_mutex);
|
||||
}
|
||||
|
||||
@ -231,7 +226,7 @@ int topology_set_cpu_management(int fc)
|
||||
if (rc)
|
||||
return -EBUSY;
|
||||
for_each_possible_cpu(cpu)
|
||||
cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -250,12 +245,10 @@ static void update_cpu_core_map(void)
|
||||
|
||||
void store_topology(struct sysinfo_15_1_x *info)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = stsi(info, 15, 1, 3);
|
||||
if (rc != -ENOSYS)
|
||||
return;
|
||||
stsi(info, 15, 1, 2);
|
||||
if (topology_max_mnest >= 3)
|
||||
stsi(info, 15, 1, 3);
|
||||
else
|
||||
stsi(info, 15, 1, 2);
|
||||
}
|
||||
|
||||
int arch_update_cpu_topology(void)
|
||||
@ -415,7 +408,7 @@ static ssize_t cpu_polarization_show(struct device *dev,
|
||||
ssize_t count;
|
||||
|
||||
mutex_lock(&smp_cpu_state_mutex);
|
||||
switch (cpu_read_polarization(cpu)) {
|
||||
switch (smp_cpu_get_polarization(cpu)) {
|
||||
case POLARIZATION_HRZ:
|
||||
count = sprintf(buf, "horizontal\n");
|
||||
break;
|
||||
|
@ -57,6 +57,23 @@ static int kstack_depth_to_print = 12;
|
||||
static int kstack_depth_to_print = 20;
|
||||
#endif /* CONFIG_64BIT */
|
||||
|
||||
static inline void __user *get_trap_ip(struct pt_regs *regs)
|
||||
{
|
||||
#ifdef CONFIG_64BIT
|
||||
unsigned long address;
|
||||
|
||||
if (regs->int_code & 0x200)
|
||||
address = *(unsigned long *)(current->thread.trap_tdb + 24);
|
||||
else
|
||||
address = regs->psw.addr;
|
||||
return (void __user *)
|
||||
((address - (regs->int_code >> 16)) & PSW_ADDR_INSN);
|
||||
#else
|
||||
return (void __user *)
|
||||
((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* For show_trace we have tree different stack to consider:
|
||||
* - the panic stack which is used if the kernel stack has overflown
|
||||
@ -214,7 +231,6 @@ void show_registers(struct pt_regs *regs)
|
||||
|
||||
void show_regs(struct pt_regs *regs)
|
||||
{
|
||||
print_modules();
|
||||
printk("CPU: %d %s %s %.*s\n",
|
||||
task_thread_info(current)->cpu, print_tainted(),
|
||||
init_utsname()->release,
|
||||
@ -254,6 +270,7 @@ void die(struct pt_regs *regs, const char *str)
|
||||
#endif
|
||||
printk("\n");
|
||||
notify_die(DIE_OOPS, str, regs, 0, regs->int_code & 0xffff, SIGSEGV);
|
||||
print_modules();
|
||||
show_regs(regs);
|
||||
bust_spinlocks(0);
|
||||
add_taint(TAINT_DIE);
|
||||
@ -285,12 +302,6 @@ int is_valid_bugaddr(unsigned long addr)
|
||||
return 1;
|
||||
}
|
||||
|
||||
static inline void __user *get_psw_address(struct pt_regs *regs)
|
||||
{
|
||||
return (void __user *)
|
||||
((regs->psw.addr - (regs->int_code >> 16)) & PSW_ADDR_INSN);
|
||||
}
|
||||
|
||||
static void __kprobes do_trap(struct pt_regs *regs,
|
||||
int si_signo, int si_code, char *str)
|
||||
{
|
||||
@ -304,14 +315,14 @@ static void __kprobes do_trap(struct pt_regs *regs,
|
||||
info.si_signo = si_signo;
|
||||
info.si_errno = 0;
|
||||
info.si_code = si_code;
|
||||
info.si_addr = get_psw_address(regs);
|
||||
info.si_addr = get_trap_ip(regs);
|
||||
force_sig_info(si_signo, &info, current);
|
||||
report_user_fault(regs, si_signo);
|
||||
} else {
|
||||
const struct exception_table_entry *fixup;
|
||||
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
|
||||
if (fixup)
|
||||
regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
|
||||
regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE;
|
||||
else {
|
||||
enum bug_trap_type btt;
|
||||
|
||||
@ -381,6 +392,11 @@ DO_ERROR_INFO(special_op_exception, SIGILL, ILL_ILLOPN,
|
||||
DO_ERROR_INFO(translation_exception, SIGILL, ILL_ILLOPN,
|
||||
"translation exception")
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
DO_ERROR_INFO(transaction_exception, SIGILL, ILL_ILLOPN,
|
||||
"transaction constraint exception")
|
||||
#endif
|
||||
|
||||
static inline void do_fp_trap(struct pt_regs *regs, int fpc)
|
||||
{
|
||||
int si_code = 0;
|
||||
@ -408,7 +424,7 @@ static void __kprobes illegal_op(struct pt_regs *regs)
|
||||
__u16 __user *location;
|
||||
int signal = 0;
|
||||
|
||||
location = get_psw_address(regs);
|
||||
location = get_trap_ip(regs);
|
||||
|
||||
if (user_mode(regs)) {
|
||||
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
|
||||
@ -476,7 +492,7 @@ void specification_exception(struct pt_regs *regs)
|
||||
__u16 __user *location = NULL;
|
||||
int signal = 0;
|
||||
|
||||
location = (__u16 __user *) get_psw_address(regs);
|
||||
location = (__u16 __user *) get_trap_ip(regs);
|
||||
|
||||
if (user_mode(regs)) {
|
||||
get_user(*((__u16 *) opcode), location);
|
||||
@ -525,7 +541,7 @@ static void data_exception(struct pt_regs *regs)
|
||||
__u16 __user *location;
|
||||
int signal = 0;
|
||||
|
||||
location = get_psw_address(regs);
|
||||
location = get_trap_ip(regs);
|
||||
|
||||
if (MACHINE_HAS_IEEE)
|
||||
asm volatile("stfpc %0" : "=m" (current->thread.fp_regs.fpc));
|
||||
@ -641,6 +657,7 @@ void __init trap_init(void)
|
||||
pgm_check_table[0x12] = &translation_exception;
|
||||
pgm_check_table[0x13] = &special_op_exception;
|
||||
#ifdef CONFIG_64BIT
|
||||
pgm_check_table[0x18] = &transaction_exception;
|
||||
pgm_check_table[0x38] = &do_asce_exception;
|
||||
pgm_check_table[0x39] = &do_dat_exception;
|
||||
pgm_check_table[0x3A] = &do_dat_exception;
|
||||
|
@ -85,7 +85,7 @@ struct vdso_data *vdso_data = &vdso_data_store.data;
|
||||
static void vdso_init_data(struct vdso_data *vd)
|
||||
{
|
||||
vd->ectg_available =
|
||||
addressing_mode != HOME_SPACE_MODE && test_facility(31);
|
||||
s390_user_mode != HOME_SPACE_MODE && test_facility(31);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
@ -102,7 +102,7 @@ int vdso_alloc_per_cpu(struct _lowcore *lowcore)
|
||||
|
||||
lowcore->vdso_per_cpu_data = __LC_PASTE;
|
||||
|
||||
if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
return 0;
|
||||
|
||||
segment_table = __get_free_pages(GFP_KERNEL, SEGMENT_ORDER);
|
||||
@ -147,7 +147,7 @@ void vdso_free_per_cpu(struct _lowcore *lowcore)
|
||||
unsigned long segment_table, page_table, page_frame;
|
||||
u32 *psal, *aste;
|
||||
|
||||
if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
return;
|
||||
|
||||
psal = (u32 *)(addr_t) lowcore->paste[4];
|
||||
@ -165,7 +165,7 @@ static void vdso_init_cr5(void)
|
||||
{
|
||||
unsigned long cr5;
|
||||
|
||||
if (addressing_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
if (s390_user_mode == HOME_SPACE_MODE || !vdso_enabled)
|
||||
return;
|
||||
cr5 = offsetof(struct _lowcore, paste);
|
||||
__ctl_load(cr5, 5, 5);
|
||||
|
@ -378,9 +378,8 @@ static int __cpuinit s390_nohz_notify(struct notifier_block *self,
|
||||
long cpu = (long) hcpu;
|
||||
|
||||
idle = &per_cpu(s390_idle, cpu);
|
||||
switch (action) {
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_DYING:
|
||||
case CPU_DYING_FROZEN:
|
||||
idle->nohz_delay = 0;
|
||||
default:
|
||||
break;
|
||||
|
@ -5,7 +5,7 @@ source "virt/kvm/Kconfig"
|
||||
|
||||
menuconfig VIRTUALIZATION
|
||||
def_bool y
|
||||
prompt "Virtualization"
|
||||
prompt "KVM"
|
||||
---help---
|
||||
Say Y here to get to see options for using your Linux host to run other
|
||||
operating systems inside virtual machines (guests).
|
||||
|
@ -211,7 +211,7 @@ static void handle_stsi_3_2_2(struct kvm_vcpu *vcpu, struct sysinfo_3_2_2 *mem)
|
||||
spin_unlock(&fi->lock);
|
||||
|
||||
/* deal with other level 3 hypervisors */
|
||||
if (stsi(mem, 3, 2, 2) == -ENOSYS)
|
||||
if (stsi(mem, 3, 2, 2))
|
||||
mem->count = 0;
|
||||
if (mem->count < 8)
|
||||
mem->count++;
|
||||
@ -259,7 +259,7 @@ static int handle_stsi(struct kvm_vcpu *vcpu)
|
||||
mem = get_zeroed_page(GFP_KERNEL);
|
||||
if (!mem)
|
||||
goto out_fail;
|
||||
if (stsi((void *) mem, fc, sel1, sel2) == -ENOSYS)
|
||||
if (stsi((void *) mem, fc, sel1, sel2))
|
||||
goto out_mem;
|
||||
break;
|
||||
case 3:
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
lib-y += delay.o string.o uaccess_std.o uaccess_pt.o
|
||||
obj-y += usercopy.o
|
||||
obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o
|
||||
obj-$(CONFIG_32BIT) += div64.o qrnnd.o ucmpdi2.o mem32.o
|
||||
obj-$(CONFIG_64BIT) += mem64.o
|
||||
lib-$(CONFIG_64BIT) += uaccess_mvcos.o
|
||||
lib-$(CONFIG_SMP) += spinlock.o
|
||||
|
92
arch/s390/lib/mem32.S
Normal file
92
arch/s390/lib/mem32.S
Normal file
@ -0,0 +1,92 @@
|
||||
/*
|
||||
* String handling functions.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
/*
|
||||
* memset implementation
|
||||
*
|
||||
* This code corresponds to the C construct below. We do distinguish
|
||||
* between clearing (c == 0) and setting a memory array (c != 0) simply
|
||||
* because nearly all memset invocations in the kernel clear memory and
|
||||
* the xc instruction is preferred in such cases.
|
||||
*
|
||||
* void *memset(void *s, int c, size_t n)
|
||||
* {
|
||||
* if (likely(c == 0))
|
||||
* return __builtin_memset(s, 0, n);
|
||||
* return __builtin_memset(s, c, n);
|
||||
* }
|
||||
*/
|
||||
ENTRY(memset)
|
||||
basr %r5,%r0
|
||||
.Lmemset_base:
|
||||
ltr %r4,%r4
|
||||
bzr %r14
|
||||
ltr %r3,%r3
|
||||
jnz .Lmemset_fill
|
||||
ahi %r4,-1
|
||||
lr %r3,%r4
|
||||
srl %r3,8
|
||||
ltr %r3,%r3
|
||||
lr %r1,%r2
|
||||
je .Lmemset_clear_rest
|
||||
.Lmemset_clear_loop:
|
||||
xc 0(256,%r1),0(%r1)
|
||||
la %r1,256(%r1)
|
||||
brct %r3,.Lmemset_clear_loop
|
||||
.Lmemset_clear_rest:
|
||||
ex %r4,.Lmemset_xc-.Lmemset_base(%r5)
|
||||
br %r14
|
||||
.Lmemset_fill:
|
||||
stc %r3,0(%r2)
|
||||
chi %r4,1
|
||||
lr %r1,%r2
|
||||
ber %r14
|
||||
ahi %r4,-2
|
||||
lr %r3,%r4
|
||||
srl %r3,8
|
||||
ltr %r3,%r3
|
||||
je .Lmemset_fill_rest
|
||||
.Lmemset_fill_loop:
|
||||
mvc 1(256,%r1),0(%r1)
|
||||
la %r1,256(%r1)
|
||||
brct %r3,.Lmemset_fill_loop
|
||||
.Lmemset_fill_rest:
|
||||
ex %r4,.Lmemset_mvc-.Lmemset_base(%r5)
|
||||
br %r14
|
||||
.Lmemset_xc:
|
||||
xc 0(1,%r1),0(%r1)
|
||||
.Lmemset_mvc:
|
||||
mvc 1(1,%r1),0(%r1)
|
||||
|
||||
/*
|
||||
* memcpy implementation
|
||||
*
|
||||
* void *memcpy(void *dest, const void *src, size_t n)
|
||||
*/
|
||||
ENTRY(memcpy)
|
||||
basr %r5,%r0
|
||||
.Lmemcpy_base:
|
||||
ltr %r4,%r4
|
||||
bzr %r14
|
||||
ahi %r4,-1
|
||||
lr %r0,%r4
|
||||
srl %r0,8
|
||||
ltr %r0,%r0
|
||||
lr %r1,%r2
|
||||
jnz .Lmemcpy_loop
|
||||
.Lmemcpy_rest:
|
||||
ex %r4,.Lmemcpy_mvc-.Lmemcpy_base(%r5)
|
||||
br %r14
|
||||
.Lmemcpy_loop:
|
||||
mvc 0(256,%r1),0(%r3)
|
||||
la %r1,256(%r1)
|
||||
la %r3,256(%r3)
|
||||
brct %r0,.Lmemcpy_loop
|
||||
j .Lmemcpy_rest
|
||||
.Lmemcpy_mvc:
|
||||
mvc 0(1,%r1),0(%r3)
|
88
arch/s390/lib/mem64.S
Normal file
88
arch/s390/lib/mem64.S
Normal file
@ -0,0 +1,88 @@
|
||||
/*
|
||||
* String handling functions.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
|
||||
/*
|
||||
* memset implementation
|
||||
*
|
||||
* This code corresponds to the C construct below. We do distinguish
|
||||
* between clearing (c == 0) and setting a memory array (c != 0) simply
|
||||
* because nearly all memset invocations in the kernel clear memory and
|
||||
* the xc instruction is preferred in such cases.
|
||||
*
|
||||
* void *memset(void *s, int c, size_t n)
|
||||
* {
|
||||
* if (likely(c == 0))
|
||||
* return __builtin_memset(s, 0, n);
|
||||
* return __builtin_memset(s, c, n);
|
||||
* }
|
||||
*/
|
||||
ENTRY(memset)
|
||||
ltgr %r4,%r4
|
||||
bzr %r14
|
||||
ltgr %r3,%r3
|
||||
jnz .Lmemset_fill
|
||||
aghi %r4,-1
|
||||
srlg %r3,%r4,8
|
||||
ltgr %r3,%r3
|
||||
lgr %r1,%r2
|
||||
jz .Lmemset_clear_rest
|
||||
.Lmemset_clear_loop:
|
||||
xc 0(256,%r1),0(%r1)
|
||||
la %r1,256(%r1)
|
||||
brctg %r3,.Lmemset_clear_loop
|
||||
.Lmemset_clear_rest:
|
||||
larl %r3,.Lmemset_xc
|
||||
ex %r4,0(%r3)
|
||||
br %r14
|
||||
.Lmemset_fill:
|
||||
stc %r3,0(%r2)
|
||||
cghi %r4,1
|
||||
lgr %r1,%r2
|
||||
ber %r14
|
||||
aghi %r4,-2
|
||||
srlg %r3,%r4,8
|
||||
ltgr %r3,%r3
|
||||
jz .Lmemset_fill_rest
|
||||
.Lmemset_fill_loop:
|
||||
mvc 1(256,%r1),0(%r1)
|
||||
la %r1,256(%r1)
|
||||
brctg %r3,.Lmemset_fill_loop
|
||||
.Lmemset_fill_rest:
|
||||
larl %r3,.Lmemset_mvc
|
||||
ex %r4,0(%r3)
|
||||
br %r14
|
||||
.Lmemset_xc:
|
||||
xc 0(1,%r1),0(%r1)
|
||||
.Lmemset_mvc:
|
||||
mvc 1(1,%r1),0(%r1)
|
||||
|
||||
/*
|
||||
* memcpy implementation
|
||||
*
|
||||
* void *memcpy(void *dest, const void *src, size_t n)
|
||||
*/
|
||||
ENTRY(memcpy)
|
||||
ltgr %r4,%r4
|
||||
bzr %r14
|
||||
aghi %r4,-1
|
||||
srlg %r5,%r4,8
|
||||
ltgr %r5,%r5
|
||||
lgr %r1,%r2
|
||||
jnz .Lmemcpy_loop
|
||||
.Lmemcpy_rest:
|
||||
larl %r5,.Lmemcpy_mvc
|
||||
ex %r4,0(%r5)
|
||||
br %r14
|
||||
.Lmemcpy_loop:
|
||||
mvc 0(256,%r1),0(%r3)
|
||||
la %r1,256(%r1)
|
||||
la %r3,256(%r3)
|
||||
brctg %r5,.Lmemcpy_loop
|
||||
j .Lmemcpy_rest
|
||||
.Lmemcpy_mvc:
|
||||
mvc 0(1,%r1),0(%r3)
|
@ -43,11 +43,7 @@ static inline char *__strnend(const char *s, size_t n)
|
||||
*/
|
||||
size_t strlen(const char *s)
|
||||
{
|
||||
#if __GNUC__ < 4
|
||||
return __strend(s) - s;
|
||||
#else
|
||||
return __builtin_strlen(s);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(strlen);
|
||||
|
||||
@ -73,7 +69,6 @@ EXPORT_SYMBOL(strnlen);
|
||||
*/
|
||||
char *strcpy(char *dest, const char *src)
|
||||
{
|
||||
#if __GNUC__ < 4
|
||||
register int r0 asm("0") = 0;
|
||||
char *ret = dest;
|
||||
|
||||
@ -82,9 +77,6 @@ char *strcpy(char *dest, const char *src)
|
||||
: "+&a" (dest), "+&a" (src) : "d" (r0)
|
||||
: "cc", "memory" );
|
||||
return ret;
|
||||
#else
|
||||
return __builtin_strcpy(dest, src);
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL(strcpy);
|
||||
|
||||
@ -106,7 +98,7 @@ size_t strlcpy(char *dest, const char *src, size_t size)
|
||||
if (size) {
|
||||
size_t len = (ret >= size) ? size-1 : ret;
|
||||
dest[len] = '\0';
|
||||
__builtin_memcpy(dest, src, len);
|
||||
memcpy(dest, src, len);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
@ -124,8 +116,8 @@ EXPORT_SYMBOL(strlcpy);
|
||||
char *strncpy(char *dest, const char *src, size_t n)
|
||||
{
|
||||
size_t len = __strnend(src, n) - src;
|
||||
__builtin_memset(dest + len, 0, n - len);
|
||||
__builtin_memcpy(dest, src, len);
|
||||
memset(dest + len, 0, n - len);
|
||||
memcpy(dest, src, len);
|
||||
return dest;
|
||||
}
|
||||
EXPORT_SYMBOL(strncpy);
|
||||
@ -171,7 +163,7 @@ size_t strlcat(char *dest, const char *src, size_t n)
|
||||
if (len >= n)
|
||||
len = n - 1;
|
||||
dest[len] = '\0';
|
||||
__builtin_memcpy(dest, src, len);
|
||||
memcpy(dest, src, len);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -194,7 +186,7 @@ char *strncat(char *dest, const char *src, size_t n)
|
||||
char *p = __strend(dest);
|
||||
|
||||
p[len] = '\0';
|
||||
__builtin_memcpy(p, src, len);
|
||||
memcpy(p, src, len);
|
||||
return dest;
|
||||
}
|
||||
EXPORT_SYMBOL(strncat);
|
||||
@ -348,41 +340,3 @@ void *memscan(void *s, int c, size_t n)
|
||||
return (void *) ret;
|
||||
}
|
||||
EXPORT_SYMBOL(memscan);
|
||||
|
||||
/**
|
||||
* memcpy - Copy one area of memory to another
|
||||
* @dest: Where to copy to
|
||||
* @src: Where to copy from
|
||||
* @n: The size of the area.
|
||||
*
|
||||
* returns a pointer to @dest
|
||||
*/
|
||||
void *memcpy(void *dest, const void *src, size_t n)
|
||||
{
|
||||
return __builtin_memcpy(dest, src, n);
|
||||
}
|
||||
EXPORT_SYMBOL(memcpy);
|
||||
|
||||
/**
|
||||
* memset - Fill a region of memory with the given value
|
||||
* @s: Pointer to the start of the area.
|
||||
* @c: The byte to fill the area with
|
||||
* @n: The size of the area.
|
||||
*
|
||||
* returns a pointer to @s
|
||||
*/
|
||||
void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
char *xs;
|
||||
|
||||
if (c == 0)
|
||||
return __builtin_memset(s, 0, n);
|
||||
|
||||
xs = (char *) s;
|
||||
if (n > 0)
|
||||
do {
|
||||
*xs++ = c;
|
||||
} while (--n > 0);
|
||||
return s;
|
||||
}
|
||||
EXPORT_SYMBOL(memset);
|
||||
|
@ -3,7 +3,7 @@
|
||||
#
|
||||
|
||||
obj-y := init.o fault.o extmem.o mmap.o vmem.o pgtable.o maccess.o \
|
||||
page-states.o gup.o
|
||||
page-states.o gup.o extable.o
|
||||
obj-$(CONFIG_CMM) += cmm.o
|
||||
obj-$(CONFIG_HUGETLB_PAGE) += hugetlbpage.o
|
||||
obj-$(CONFIG_DEBUG_SET_MODULE_RONX) += pageattr.o
|
||||
|
81
arch/s390/mm/extable.c
Normal file
81
arch/s390/mm/extable.c
Normal file
@ -0,0 +1,81 @@
|
||||
#include <linux/module.h>
|
||||
#include <linux/sort.h>
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
/*
|
||||
* Search one exception table for an entry corresponding to the
|
||||
* given instruction address, and return the address of the entry,
|
||||
* or NULL if none is found.
|
||||
* We use a binary search, and thus we assume that the table is
|
||||
* already sorted.
|
||||
*/
|
||||
const struct exception_table_entry *
|
||||
search_extable(const struct exception_table_entry *first,
|
||||
const struct exception_table_entry *last,
|
||||
unsigned long value)
|
||||
{
|
||||
const struct exception_table_entry *mid;
|
||||
unsigned long addr;
|
||||
|
||||
while (first <= last) {
|
||||
mid = ((last - first) >> 1) + first;
|
||||
addr = extable_insn(mid);
|
||||
if (addr < value)
|
||||
first = mid + 1;
|
||||
else if (addr > value)
|
||||
last = mid - 1;
|
||||
else
|
||||
return mid;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* The exception table needs to be sorted so that the binary
|
||||
* search that we use to find entries in it works properly.
|
||||
* This is used both for the kernel exception table and for
|
||||
* the exception tables of modules that get loaded.
|
||||
*
|
||||
*/
|
||||
static int cmp_ex(const void *a, const void *b)
|
||||
{
|
||||
const struct exception_table_entry *x = a, *y = b;
|
||||
|
||||
/* This compare is only valid after normalization. */
|
||||
return x->insn - y->insn;
|
||||
}
|
||||
|
||||
void sort_extable(struct exception_table_entry *start,
|
||||
struct exception_table_entry *finish)
|
||||
{
|
||||
struct exception_table_entry *p;
|
||||
int i;
|
||||
|
||||
/* Normalize entries to being relative to the start of the section */
|
||||
for (p = start, i = 0; p < finish; p++, i += 8)
|
||||
p->insn += i;
|
||||
sort(start, finish - start, sizeof(*start), cmp_ex, NULL);
|
||||
/* Denormalize all entries */
|
||||
for (p = start, i = 0; p < finish; p++, i += 8)
|
||||
p->insn -= i;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_MODULES
|
||||
/*
|
||||
* If the exception table is sorted, any referring to the module init
|
||||
* will be at the beginning or the end.
|
||||
*/
|
||||
void trim_init_extable(struct module *m)
|
||||
{
|
||||
/* Trim the beginning */
|
||||
while (m->num_exentries &&
|
||||
within_module_init(extable_insn(&m->extable[0]), m)) {
|
||||
m->extable++;
|
||||
m->num_exentries--;
|
||||
}
|
||||
/* Trim the end */
|
||||
while (m->num_exentries &&
|
||||
within_module_init(extable_insn(&m->extable[m->num_exentries-1]), m))
|
||||
m->num_exentries--;
|
||||
}
|
||||
#endif /* CONFIG_MODULES */
|
@ -111,7 +111,7 @@ static inline int user_space_fault(unsigned long trans_exc_code)
|
||||
if (trans_exc_code == 2)
|
||||
/* Access via secondary space, set_fs setting decides */
|
||||
return current->thread.mm_segment.ar4;
|
||||
if (addressing_mode == HOME_SPACE_MODE)
|
||||
if (s390_user_mode == HOME_SPACE_MODE)
|
||||
/* User space if the access has been done via home space. */
|
||||
return trans_exc_code == 3;
|
||||
/*
|
||||
@ -163,7 +163,7 @@ static noinline void do_no_context(struct pt_regs *regs)
|
||||
/* Are we prepared to handle this kernel fault? */
|
||||
fixup = search_exception_tables(regs->psw.addr & PSW_ADDR_INSN);
|
||||
if (fixup) {
|
||||
regs->psw.addr = fixup->fixup | PSW_ADDR_AMODE;
|
||||
regs->psw.addr = extable_fixup(fixup) | PSW_ADDR_AMODE;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -628,9 +628,8 @@ static int __cpuinit pfault_cpu_notify(struct notifier_block *self,
|
||||
struct thread_struct *thread, *next;
|
||||
struct task_struct *tsk;
|
||||
|
||||
switch (action) {
|
||||
switch (action & ~CPU_TASKS_FROZEN) {
|
||||
case CPU_DEAD:
|
||||
case CPU_DEAD_FROZEN:
|
||||
spin_lock_irq(&pfault_lock);
|
||||
list_for_each_entry_safe(thread, next, &pfault_list, list) {
|
||||
thread->pfault_wait = 0;
|
||||
|
@ -154,6 +154,43 @@ static inline int gup_pud_range(pgd_t *pgdp, pgd_t pgd, unsigned long addr,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Like get_user_pages_fast() except its IRQ-safe in that it won't fall
|
||||
* back to the regular GUP.
|
||||
*/
|
||||
int __get_user_pages_fast(unsigned long start, int nr_pages, int write,
|
||||
struct page **pages)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long addr, len, end;
|
||||
unsigned long next, flags;
|
||||
pgd_t *pgdp, pgd;
|
||||
int nr = 0;
|
||||
|
||||
start &= PAGE_MASK;
|
||||
addr = start;
|
||||
len = (unsigned long) nr_pages << PAGE_SHIFT;
|
||||
end = start + len;
|
||||
if (unlikely(!access_ok(write ? VERIFY_WRITE : VERIFY_READ,
|
||||
(void __user *)start, len)))
|
||||
return 0;
|
||||
|
||||
local_irq_save(flags);
|
||||
pgdp = pgd_offset(mm, addr);
|
||||
do {
|
||||
pgd = *pgdp;
|
||||
barrier();
|
||||
next = pgd_addr_end(addr, end);
|
||||
if (pgd_none(pgd))
|
||||
break;
|
||||
if (!gup_pud_range(pgdp, pgd, addr, next, write, pages, &nr))
|
||||
break;
|
||||
} while (pgdp++, addr = next, addr != end);
|
||||
local_irq_restore(flags);
|
||||
|
||||
return nr;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_user_pages_fast() - pin user pages in memory
|
||||
* @start: starting user address
|
||||
|
@ -42,7 +42,7 @@ pgd_t swapper_pg_dir[PTRS_PER_PGD] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
unsigned long empty_zero_page, zero_page_mask;
|
||||
EXPORT_SYMBOL(empty_zero_page);
|
||||
|
||||
static unsigned long setup_zero_pages(void)
|
||||
static unsigned long __init setup_zero_pages(void)
|
||||
{
|
||||
struct cpuid cpu_id;
|
||||
unsigned int order;
|
||||
@ -212,7 +212,7 @@ void free_initmem(void)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BLK_DEV_INITRD
|
||||
void free_initrd_mem(unsigned long start, unsigned long end)
|
||||
void __init free_initrd_mem(unsigned long start, unsigned long end)
|
||||
{
|
||||
free_init_pages("initrd memory", start, end);
|
||||
}
|
||||
|
@ -609,8 +609,8 @@ static inline unsigned int atomic_xor_bits(atomic_t *v, unsigned int bits)
|
||||
*/
|
||||
unsigned long *page_table_alloc(struct mm_struct *mm, unsigned long vmaddr)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned long *table;
|
||||
unsigned long *uninitialized_var(table);
|
||||
struct page *uninitialized_var(page);
|
||||
unsigned int mask, bit;
|
||||
|
||||
if (mm_has_pgste(mm))
|
||||
@ -796,7 +796,7 @@ int s390_enable_sie(void)
|
||||
struct mm_struct *mm, *old_mm;
|
||||
|
||||
/* Do we have switched amode? If no, we cannot do sie */
|
||||
if (addressing_mode == HOME_SPACE_MODE)
|
||||
if (s390_user_mode == HOME_SPACE_MODE)
|
||||
return -EINVAL;
|
||||
|
||||
/* Do we have pgstes? if yes, we are done */
|
||||
|
@ -107,7 +107,7 @@ static int vmem_add_mem(unsigned long start, unsigned long size, int ro)
|
||||
pte = mk_pte_phys(address, __pgprot(ro ? _PAGE_RO : 0));
|
||||
pm_dir = pmd_offset(pu_dir, address);
|
||||
|
||||
#ifdef CONFIG_64BIT
|
||||
#if defined(CONFIG_64BIT) && !defined(CONFIG_DEBUG_PAGEALLOC)
|
||||
if (MACHINE_HAS_HPAGE && !(address & ~HPAGE_MASK) &&
|
||||
(address + HPAGE_SIZE <= start + size) &&
|
||||
(address >= HPAGE_SIZE)) {
|
||||
|
4
arch/s390/net/Makefile
Normal file
4
arch/s390/net/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
#
|
||||
# Arch-specific network modules
|
||||
#
|
||||
obj-$(CONFIG_BPF_JIT) += bpf_jit.o bpf_jit_comp.o
|
130
arch/s390/net/bpf_jit.S
Normal file
130
arch/s390/net/bpf_jit.S
Normal file
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* BPF Jit compiler for s390, help functions.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
*
|
||||
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
*/
|
||||
#include <linux/linkage.h>
|
||||
|
||||
/*
|
||||
* Calling convention:
|
||||
* registers %r2, %r6-%r8, %r10-%r11, %r13, %r15 are call saved
|
||||
* %r2: skb pointer
|
||||
* %r3: offset parameter
|
||||
* %r5: BPF A accumulator
|
||||
* %r8: return address
|
||||
* %r9: save register for skb pointer
|
||||
* %r10: skb->data
|
||||
* %r11: skb->len - skb->data_len (headlen)
|
||||
* %r12: BPF X accumulator
|
||||
*
|
||||
* skb_copy_bits takes 4 parameters:
|
||||
* %r2 = skb pointer
|
||||
* %r3 = offset into skb data
|
||||
* %r4 = length to copy
|
||||
* %r5 = pointer to temp buffer
|
||||
*/
|
||||
#define SKBDATA %r8
|
||||
|
||||
/* A = *(u32 *) (skb->data+K+X) */
|
||||
ENTRY(sk_load_word_ind)
|
||||
ar %r3,%r12 # offset += X
|
||||
bmr %r8 # < 0 -> return with cc
|
||||
|
||||
/* A = *(u32 *) (skb->data+K) */
|
||||
ENTRY(sk_load_word)
|
||||
llgfr %r1,%r3 # extend offset
|
||||
ahi %r3,4 # offset + 4
|
||||
clr %r11,%r3 # hlen <= offset + 4 ?
|
||||
jl sk_load_word_slow
|
||||
l %r5,0(%r1,%r10) # get word from skb
|
||||
xr %r1,%r1 # set cc to zero
|
||||
br %r8
|
||||
|
||||
sk_load_word_slow:
|
||||
lgr %r9,%r2 # save %r2
|
||||
lhi %r4,4 # 4 bytes
|
||||
la %r5,160(%r15) # pointer to temp buffer
|
||||
brasl %r14,skb_copy_bits # get data from skb
|
||||
l %r5,160(%r15) # load result from temp buffer
|
||||
ltgr %r2,%r2 # set cc to (%r2 != 0)
|
||||
lgr %r2,%r9 # restore %r2
|
||||
br %r8
|
||||
|
||||
/* A = *(u16 *) (skb->data+K+X) */
|
||||
ENTRY(sk_load_half_ind)
|
||||
ar %r3,%r12 # offset += X
|
||||
bmr %r8 # < 0 -> return with cc
|
||||
|
||||
/* A = *(u16 *) (skb->data+K) */
|
||||
ENTRY(sk_load_half)
|
||||
llgfr %r1,%r3 # extend offset
|
||||
ahi %r3,2 # offset + 2
|
||||
clr %r11,%r3 # hlen <= offset + 2 ?
|
||||
jl sk_load_half_slow
|
||||
llgh %r5,0(%r1,%r10) # get half from skb
|
||||
xr %r1,%r1 # set cc to zero
|
||||
br %r8
|
||||
|
||||
sk_load_half_slow:
|
||||
lgr %r9,%r2 # save %r2
|
||||
lhi %r4,2 # 2 bytes
|
||||
la %r5,162(%r15) # pointer to temp buffer
|
||||
brasl %r14,skb_copy_bits # get data from skb
|
||||
xc 160(2,%r15),160(%r15)
|
||||
l %r5,160(%r15) # load result from temp buffer
|
||||
ltgr %r2,%r2 # set cc to (%r2 != 0)
|
||||
lgr %r2,%r9 # restore %r2
|
||||
br %r8
|
||||
|
||||
/* A = *(u8 *) (skb->data+K+X) */
|
||||
ENTRY(sk_load_byte_ind)
|
||||
ar %r3,%r12 # offset += X
|
||||
bmr %r8 # < 0 -> return with cc
|
||||
|
||||
/* A = *(u8 *) (skb->data+K) */
|
||||
ENTRY(sk_load_byte)
|
||||
llgfr %r1,%r3 # extend offset
|
||||
clr %r11,%r3 # hlen < offset ?
|
||||
jle sk_load_byte_slow
|
||||
lhi %r5,0
|
||||
ic %r5,0(%r1,%r10) # get byte from skb
|
||||
xr %r1,%r1 # set cc to zero
|
||||
br %r8
|
||||
|
||||
sk_load_byte_slow:
|
||||
lgr %r9,%r2 # save %r2
|
||||
lhi %r4,1 # 1 bytes
|
||||
la %r5,163(%r15) # pointer to temp buffer
|
||||
brasl %r14,skb_copy_bits # get data from skb
|
||||
xc 160(3,%r15),160(%r15)
|
||||
l %r5,160(%r15) # load result from temp buffer
|
||||
ltgr %r2,%r2 # set cc to (%r2 != 0)
|
||||
lgr %r2,%r9 # restore %r2
|
||||
br %r8
|
||||
|
||||
/* A = (*(u8 *)(skb->data+K) & 0xf) << 2 */
|
||||
ENTRY(sk_load_byte_msh)
|
||||
llgfr %r1,%r3 # extend offset
|
||||
clr %r11,%r3 # hlen < offset ?
|
||||
jle sk_load_byte_slow
|
||||
lhi %r12,0
|
||||
ic %r12,0(%r1,%r10) # get byte from skb
|
||||
nill %r12,0x0f
|
||||
sll %r12,2
|
||||
xr %r1,%r1 # set cc to zero
|
||||
br %r8
|
||||
|
||||
sk_load_byte_msh_slow:
|
||||
lgr %r9,%r2 # save %r2
|
||||
lhi %r4,2 # 2 bytes
|
||||
la %r5,162(%r15) # pointer to temp buffer
|
||||
brasl %r14,skb_copy_bits # get data from skb
|
||||
xc 160(3,%r15),160(%r15)
|
||||
l %r12,160(%r15) # load result from temp buffer
|
||||
nill %r12,0x0f
|
||||
sll %r12,2
|
||||
ltgr %r2,%r2 # set cc to (%r2 != 0)
|
||||
lgr %r2,%r9 # restore %r2
|
||||
br %r8
|
776
arch/s390/net/bpf_jit_comp.c
Normal file
776
arch/s390/net/bpf_jit_comp.c
Normal file
@ -0,0 +1,776 @@
|
||||
/*
|
||||
* BPF Jit compiler for s390.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
*
|
||||
* Author(s): Martin Schwidefsky <schwidefsky@de.ibm.com>
|
||||
*/
|
||||
#include <linux/moduleloader.h>
|
||||
#include <linux/netdevice.h>
|
||||
#include <linux/filter.h>
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/processor.h>
|
||||
#include <asm/facility.h>
|
||||
|
||||
/*
|
||||
* Conventions:
|
||||
* %r2 = skb pointer
|
||||
* %r3 = offset parameter
|
||||
* %r4 = scratch register / length parameter
|
||||
* %r5 = BPF A accumulator
|
||||
* %r8 = return address
|
||||
* %r9 = save register for skb pointer
|
||||
* %r10 = skb->data
|
||||
* %r11 = skb->len - skb->data_len (headlen)
|
||||
* %r12 = BPF X accumulator
|
||||
* %r13 = literal pool pointer
|
||||
* 0(%r15) - 63(%r15) scratch memory array with BPF_MEMWORDS
|
||||
*/
|
||||
int bpf_jit_enable __read_mostly;
|
||||
|
||||
/*
|
||||
* assembly code in arch/x86/net/bpf_jit.S
|
||||
*/
|
||||
extern u8 sk_load_word[], sk_load_half[], sk_load_byte[], sk_load_byte_msh[];
|
||||
extern u8 sk_load_word_ind[], sk_load_half_ind[], sk_load_byte_ind[];
|
||||
|
||||
struct bpf_jit {
|
||||
unsigned int seen;
|
||||
u8 *start;
|
||||
u8 *prg;
|
||||
u8 *mid;
|
||||
u8 *lit;
|
||||
u8 *end;
|
||||
u8 *base_ip;
|
||||
u8 *ret0_ip;
|
||||
u8 *exit_ip;
|
||||
unsigned int off_load_word;
|
||||
unsigned int off_load_half;
|
||||
unsigned int off_load_byte;
|
||||
unsigned int off_load_bmsh;
|
||||
unsigned int off_load_iword;
|
||||
unsigned int off_load_ihalf;
|
||||
unsigned int off_load_ibyte;
|
||||
};
|
||||
|
||||
#define BPF_SIZE_MAX 4096 /* Max size for program */
|
||||
|
||||
#define SEEN_DATAREF 1 /* might call external helpers */
|
||||
#define SEEN_XREG 2 /* ebx is used */
|
||||
#define SEEN_MEM 4 /* use mem[] for temporary storage */
|
||||
#define SEEN_RET0 8 /* pc_ret0 points to a valid return 0 */
|
||||
#define SEEN_LITERAL 16 /* code uses literals */
|
||||
#define SEEN_LOAD_WORD 32 /* code uses sk_load_word */
|
||||
#define SEEN_LOAD_HALF 64 /* code uses sk_load_half */
|
||||
#define SEEN_LOAD_BYTE 128 /* code uses sk_load_byte */
|
||||
#define SEEN_LOAD_BMSH 256 /* code uses sk_load_byte_msh */
|
||||
#define SEEN_LOAD_IWORD 512 /* code uses sk_load_word_ind */
|
||||
#define SEEN_LOAD_IHALF 1024 /* code uses sk_load_half_ind */
|
||||
#define SEEN_LOAD_IBYTE 2048 /* code uses sk_load_byte_ind */
|
||||
|
||||
#define EMIT2(op) \
|
||||
({ \
|
||||
if (jit->prg + 2 <= jit->mid) \
|
||||
*(u16 *) jit->prg = op; \
|
||||
jit->prg += 2; \
|
||||
})
|
||||
|
||||
#define EMIT4(op) \
|
||||
({ \
|
||||
if (jit->prg + 4 <= jit->mid) \
|
||||
*(u32 *) jit->prg = op; \
|
||||
jit->prg += 4; \
|
||||
})
|
||||
|
||||
#define EMIT4_DISP(op, disp) \
|
||||
({ \
|
||||
unsigned int __disp = (disp) & 0xfff; \
|
||||
EMIT4(op | __disp); \
|
||||
})
|
||||
|
||||
#define EMIT4_IMM(op, imm) \
|
||||
({ \
|
||||
unsigned int __imm = (imm) & 0xffff; \
|
||||
EMIT4(op | __imm); \
|
||||
})
|
||||
|
||||
#define EMIT4_PCREL(op, pcrel) \
|
||||
({ \
|
||||
long __pcrel = ((pcrel) >> 1) & 0xffff; \
|
||||
EMIT4(op | __pcrel); \
|
||||
})
|
||||
|
||||
#define EMIT6(op1, op2) \
|
||||
({ \
|
||||
if (jit->prg + 6 <= jit->mid) { \
|
||||
*(u32 *) jit->prg = op1; \
|
||||
*(u16 *) (jit->prg + 4) = op2; \
|
||||
} \
|
||||
jit->prg += 6; \
|
||||
})
|
||||
|
||||
#define EMIT6_DISP(op1, op2, disp) \
|
||||
({ \
|
||||
unsigned int __disp = (disp) & 0xfff; \
|
||||
EMIT6(op1 | __disp, op2); \
|
||||
})
|
||||
|
||||
#define EMIT6_IMM(op, imm) \
|
||||
({ \
|
||||
unsigned int __imm = (imm); \
|
||||
EMIT6(op | (__imm >> 16), __imm & 0xffff); \
|
||||
})
|
||||
|
||||
#define EMIT_CONST(val) \
|
||||
({ \
|
||||
unsigned int ret; \
|
||||
ret = (unsigned int) (jit->lit - jit->base_ip); \
|
||||
jit->seen |= SEEN_LITERAL; \
|
||||
if (jit->lit + 4 <= jit->end) \
|
||||
*(u32 *) jit->lit = val; \
|
||||
jit->lit += 4; \
|
||||
ret; \
|
||||
})
|
||||
|
||||
#define EMIT_FN_CONST(bit, fn) \
|
||||
({ \
|
||||
unsigned int ret; \
|
||||
ret = (unsigned int) (jit->lit - jit->base_ip); \
|
||||
if (jit->seen & bit) { \
|
||||
jit->seen |= SEEN_LITERAL; \
|
||||
if (jit->lit + 8 <= jit->end) \
|
||||
*(void **) jit->lit = fn; \
|
||||
jit->lit += 8; \
|
||||
} \
|
||||
ret; \
|
||||
})
|
||||
|
||||
static void bpf_jit_prologue(struct bpf_jit *jit)
|
||||
{
|
||||
/* Save registers and create stack frame if necessary */
|
||||
if (jit->seen & SEEN_DATAREF) {
|
||||
/* stmg %r8,%r15,88(%r15) */
|
||||
EMIT6(0xeb8ff058, 0x0024);
|
||||
/* lgr %r14,%r15 */
|
||||
EMIT4(0xb90400ef);
|
||||
/* ahi %r15,<offset> */
|
||||
EMIT4_IMM(0xa7fa0000, (jit->seen & SEEN_MEM) ? -112 : -80);
|
||||
/* stg %r14,152(%r15) */
|
||||
EMIT6(0xe3e0f098, 0x0024);
|
||||
} else if ((jit->seen & SEEN_XREG) && (jit->seen & SEEN_LITERAL))
|
||||
/* stmg %r12,%r13,120(%r15) */
|
||||
EMIT6(0xebcdf078, 0x0024);
|
||||
else if (jit->seen & SEEN_XREG)
|
||||
/* stg %r12,120(%r15) */
|
||||
EMIT6(0xe3c0f078, 0x0024);
|
||||
else if (jit->seen & SEEN_LITERAL)
|
||||
/* stg %r13,128(%r15) */
|
||||
EMIT6(0xe3d0f080, 0x0024);
|
||||
|
||||
/* Setup literal pool */
|
||||
if (jit->seen & SEEN_LITERAL) {
|
||||
/* basr %r13,0 */
|
||||
EMIT2(0x0dd0);
|
||||
jit->base_ip = jit->prg;
|
||||
}
|
||||
jit->off_load_word = EMIT_FN_CONST(SEEN_LOAD_WORD, sk_load_word);
|
||||
jit->off_load_half = EMIT_FN_CONST(SEEN_LOAD_HALF, sk_load_half);
|
||||
jit->off_load_byte = EMIT_FN_CONST(SEEN_LOAD_BYTE, sk_load_byte);
|
||||
jit->off_load_bmsh = EMIT_FN_CONST(SEEN_LOAD_BMSH, sk_load_byte_msh);
|
||||
jit->off_load_iword = EMIT_FN_CONST(SEEN_LOAD_IWORD, sk_load_word_ind);
|
||||
jit->off_load_ihalf = EMIT_FN_CONST(SEEN_LOAD_IHALF, sk_load_half_ind);
|
||||
jit->off_load_ibyte = EMIT_FN_CONST(SEEN_LOAD_IBYTE, sk_load_byte_ind);
|
||||
|
||||
/* Filter needs to access skb data */
|
||||
if (jit->seen & SEEN_DATAREF) {
|
||||
/* l %r11,<len>(%r2) */
|
||||
EMIT4_DISP(0x58b02000, offsetof(struct sk_buff, len));
|
||||
/* s %r11,<data_len>(%r2) */
|
||||
EMIT4_DISP(0x5bb02000, offsetof(struct sk_buff, data_len));
|
||||
/* lg %r10,<data>(%r2) */
|
||||
EMIT6_DISP(0xe3a02000, 0x0004,
|
||||
offsetof(struct sk_buff, data));
|
||||
}
|
||||
}
|
||||
|
||||
static void bpf_jit_epilogue(struct bpf_jit *jit)
|
||||
{
|
||||
/* Return 0 */
|
||||
if (jit->seen & SEEN_RET0) {
|
||||
jit->ret0_ip = jit->prg;
|
||||
/* lghi %r2,0 */
|
||||
EMIT4(0xa7290000);
|
||||
}
|
||||
jit->exit_ip = jit->prg;
|
||||
/* Restore registers */
|
||||
if (jit->seen & SEEN_DATAREF)
|
||||
/* lmg %r8,%r15,<offset>(%r15) */
|
||||
EMIT6_DISP(0xeb8ff000, 0x0004,
|
||||
(jit->seen & SEEN_MEM) ? 200 : 168);
|
||||
else if ((jit->seen & SEEN_XREG) && (jit->seen & SEEN_LITERAL))
|
||||
/* lmg %r12,%r13,120(%r15) */
|
||||
EMIT6(0xebcdf078, 0x0004);
|
||||
else if (jit->seen & SEEN_XREG)
|
||||
/* lg %r12,120(%r15) */
|
||||
EMIT6(0xe3c0f078, 0x0004);
|
||||
else if (jit->seen & SEEN_LITERAL)
|
||||
/* lg %r13,128(%r15) */
|
||||
EMIT6(0xe3d0f080, 0x0004);
|
||||
/* br %r14 */
|
||||
EMIT2(0x07fe);
|
||||
}
|
||||
|
||||
/*
|
||||
* make sure we dont leak kernel information to user
|
||||
*/
|
||||
static void bpf_jit_noleaks(struct bpf_jit *jit, struct sock_filter *filter)
|
||||
{
|
||||
/* Clear temporary memory if (seen & SEEN_MEM) */
|
||||
if (jit->seen & SEEN_MEM)
|
||||
/* xc 0(64,%r15),0(%r15) */
|
||||
EMIT6(0xd73ff000, 0xf000);
|
||||
/* Clear X if (seen & SEEN_XREG) */
|
||||
if (jit->seen & SEEN_XREG)
|
||||
/* lhi %r12,0 */
|
||||
EMIT4(0xa7c80000);
|
||||
/* Clear A if the first register does not set it. */
|
||||
switch (filter[0].code) {
|
||||
case BPF_S_LD_W_ABS:
|
||||
case BPF_S_LD_H_ABS:
|
||||
case BPF_S_LD_B_ABS:
|
||||
case BPF_S_LD_W_LEN:
|
||||
case BPF_S_LD_W_IND:
|
||||
case BPF_S_LD_H_IND:
|
||||
case BPF_S_LD_B_IND:
|
||||
case BPF_S_LDX_B_MSH:
|
||||
case BPF_S_LD_IMM:
|
||||
case BPF_S_LD_MEM:
|
||||
case BPF_S_MISC_TXA:
|
||||
case BPF_S_ANC_PROTOCOL:
|
||||
case BPF_S_ANC_PKTTYPE:
|
||||
case BPF_S_ANC_IFINDEX:
|
||||
case BPF_S_ANC_MARK:
|
||||
case BPF_S_ANC_QUEUE:
|
||||
case BPF_S_ANC_HATYPE:
|
||||
case BPF_S_ANC_RXHASH:
|
||||
case BPF_S_ANC_CPU:
|
||||
case BPF_S_RET_K:
|
||||
/* first instruction sets A register */
|
||||
break;
|
||||
default: /* A = 0 */
|
||||
/* lhi %r5,0 */
|
||||
EMIT4(0xa7580000);
|
||||
}
|
||||
}
|
||||
|
||||
static int bpf_jit_insn(struct bpf_jit *jit, struct sock_filter *filter,
|
||||
unsigned int *addrs, int i, int last)
|
||||
{
|
||||
unsigned int K;
|
||||
int offset;
|
||||
unsigned int mask;
|
||||
|
||||
K = filter->k;
|
||||
switch (filter->code) {
|
||||
case BPF_S_ALU_ADD_X: /* A += X */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* ar %r5,%r12 */
|
||||
EMIT2(0x1a5c);
|
||||
break;
|
||||
case BPF_S_ALU_ADD_K: /* A += K */
|
||||
if (!K)
|
||||
break;
|
||||
if (K <= 16383)
|
||||
/* ahi %r5,<K> */
|
||||
EMIT4_IMM(0xa75a0000, K);
|
||||
else if (test_facility(21))
|
||||
/* alfi %r5,<K> */
|
||||
EMIT6_IMM(0xc25b0000, K);
|
||||
else
|
||||
/* a %r5,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5a50d000, EMIT_CONST(K));
|
||||
break;
|
||||
case BPF_S_ALU_SUB_X: /* A -= X */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* sr %r5,%r12 */
|
||||
EMIT2(0x1b5c);
|
||||
break;
|
||||
case BPF_S_ALU_SUB_K: /* A -= K */
|
||||
if (!K)
|
||||
break;
|
||||
if (K <= 16384)
|
||||
/* ahi %r5,-K */
|
||||
EMIT4_IMM(0xa75a0000, -K);
|
||||
else if (test_facility(21))
|
||||
/* alfi %r5,-K */
|
||||
EMIT6_IMM(0xc25b0000, -K);
|
||||
else
|
||||
/* s %r5,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5b50d000, EMIT_CONST(K));
|
||||
break;
|
||||
case BPF_S_ALU_MUL_X: /* A *= X */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* msr %r5,%r12 */
|
||||
EMIT4(0xb252005c);
|
||||
break;
|
||||
case BPF_S_ALU_MUL_K: /* A *= K */
|
||||
if (K <= 16383)
|
||||
/* mhi %r5,K */
|
||||
EMIT4_IMM(0xa75c0000, K);
|
||||
else if (test_facility(34))
|
||||
/* msfi %r5,<K> */
|
||||
EMIT6_IMM(0xc2510000, K);
|
||||
else
|
||||
/* ms %r5,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x7150d000, EMIT_CONST(K));
|
||||
break;
|
||||
case BPF_S_ALU_DIV_X: /* A /= X */
|
||||
jit->seen |= SEEN_XREG | SEEN_RET0;
|
||||
/* ltr %r12,%r12 */
|
||||
EMIT2(0x12cc);
|
||||
/* jz <ret0> */
|
||||
EMIT4_PCREL(0xa7840000, (jit->ret0_ip - jit->prg));
|
||||
/* lhi %r4,0 */
|
||||
EMIT4(0xa7480000);
|
||||
/* dr %r4,%r12 */
|
||||
EMIT2(0x1d4c);
|
||||
break;
|
||||
case BPF_S_ALU_DIV_K: /* A = reciprocal_divide(A, K) */
|
||||
/* m %r4,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5c40d000, EMIT_CONST(K));
|
||||
/* lr %r5,%r4 */
|
||||
EMIT2(0x1854);
|
||||
break;
|
||||
case BPF_S_ALU_AND_X: /* A &= X */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* nr %r5,%r12 */
|
||||
EMIT2(0x145c);
|
||||
break;
|
||||
case BPF_S_ALU_AND_K: /* A &= K */
|
||||
if (test_facility(21))
|
||||
/* nilf %r5,<K> */
|
||||
EMIT6_IMM(0xc05b0000, K);
|
||||
else
|
||||
/* n %r5,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5450d000, EMIT_CONST(K));
|
||||
break;
|
||||
case BPF_S_ALU_OR_X: /* A |= X */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* or %r5,%r12 */
|
||||
EMIT2(0x165c);
|
||||
break;
|
||||
case BPF_S_ALU_OR_K: /* A |= K */
|
||||
if (test_facility(21))
|
||||
/* oilf %r5,<K> */
|
||||
EMIT6_IMM(0xc05d0000, K);
|
||||
else
|
||||
/* o %r5,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5650d000, EMIT_CONST(K));
|
||||
break;
|
||||
case BPF_S_ANC_ALU_XOR_X: /* A ^= X; */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* xr %r5,%r12 */
|
||||
EMIT2(0x175c);
|
||||
break;
|
||||
case BPF_S_ALU_LSH_X: /* A <<= X; */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* sll %r5,0(%r12) */
|
||||
EMIT4(0x8950c000);
|
||||
break;
|
||||
case BPF_S_ALU_LSH_K: /* A <<= K */
|
||||
if (K == 0)
|
||||
break;
|
||||
/* sll %r5,K */
|
||||
EMIT4_DISP(0x89500000, K);
|
||||
break;
|
||||
case BPF_S_ALU_RSH_X: /* A >>= X; */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* srl %r5,0(%r12) */
|
||||
EMIT4(0x8850c000);
|
||||
break;
|
||||
case BPF_S_ALU_RSH_K: /* A >>= K; */
|
||||
if (K == 0)
|
||||
break;
|
||||
/* srl %r5,K */
|
||||
EMIT4_DISP(0x88500000, K);
|
||||
break;
|
||||
case BPF_S_ALU_NEG: /* A = -A */
|
||||
/* lnr %r5,%r5 */
|
||||
EMIT2(0x1155);
|
||||
break;
|
||||
case BPF_S_JMP_JA: /* ip += K */
|
||||
offset = addrs[i + K] + jit->start - jit->prg;
|
||||
EMIT4_PCREL(0xa7f40000, offset);
|
||||
break;
|
||||
case BPF_S_JMP_JGT_K: /* ip += (A > K) ? jt : jf */
|
||||
mask = 0x200000; /* jh */
|
||||
goto kbranch;
|
||||
case BPF_S_JMP_JGE_K: /* ip += (A >= K) ? jt : jf */
|
||||
mask = 0xa00000; /* jhe */
|
||||
goto kbranch;
|
||||
case BPF_S_JMP_JEQ_K: /* ip += (A == K) ? jt : jf */
|
||||
mask = 0x800000; /* je */
|
||||
kbranch: /* Emit compare if the branch targets are different */
|
||||
if (filter->jt != filter->jf) {
|
||||
if (K <= 16383)
|
||||
/* chi %r5,<K> */
|
||||
EMIT4_IMM(0xa75e0000, K);
|
||||
else if (test_facility(21))
|
||||
/* clfi %r5,<K> */
|
||||
EMIT6_IMM(0xc25f0000, K);
|
||||
else
|
||||
/* c %r5,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5950d000, EMIT_CONST(K));
|
||||
}
|
||||
branch: if (filter->jt == filter->jf) {
|
||||
if (filter->jt == 0)
|
||||
break;
|
||||
/* j <jt> */
|
||||
offset = addrs[i + filter->jt] + jit->start - jit->prg;
|
||||
EMIT4_PCREL(0xa7f40000, offset);
|
||||
break;
|
||||
}
|
||||
if (filter->jt != 0) {
|
||||
/* brc <mask>,<jt> */
|
||||
offset = addrs[i + filter->jt] + jit->start - jit->prg;
|
||||
EMIT4_PCREL(0xa7040000 | mask, offset);
|
||||
}
|
||||
if (filter->jf != 0) {
|
||||
/* brc <mask^15>,<jf> */
|
||||
offset = addrs[i + filter->jf] + jit->start - jit->prg;
|
||||
EMIT4_PCREL(0xa7040000 | (mask ^ 0xf00000), offset);
|
||||
}
|
||||
break;
|
||||
case BPF_S_JMP_JSET_K: /* ip += (A & K) ? jt : jf */
|
||||
mask = 0x700000; /* jnz */
|
||||
/* Emit test if the branch targets are different */
|
||||
if (filter->jt != filter->jf) {
|
||||
if (K > 65535) {
|
||||
/* lr %r4,%r5 */
|
||||
EMIT2(0x1845);
|
||||
/* n %r4,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5440d000, EMIT_CONST(K));
|
||||
} else
|
||||
/* tmll %r5,K */
|
||||
EMIT4_IMM(0xa7510000, K);
|
||||
}
|
||||
goto branch;
|
||||
case BPF_S_JMP_JGT_X: /* ip += (A > X) ? jt : jf */
|
||||
mask = 0x200000; /* jh */
|
||||
goto xbranch;
|
||||
case BPF_S_JMP_JGE_X: /* ip += (A >= X) ? jt : jf */
|
||||
mask = 0xa00000; /* jhe */
|
||||
goto xbranch;
|
||||
case BPF_S_JMP_JEQ_X: /* ip += (A == X) ? jt : jf */
|
||||
mask = 0x800000; /* je */
|
||||
xbranch: /* Emit compare if the branch targets are different */
|
||||
if (filter->jt != filter->jf) {
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* cr %r5,%r12 */
|
||||
EMIT2(0x195c);
|
||||
}
|
||||
goto branch;
|
||||
case BPF_S_JMP_JSET_X: /* ip += (A & X) ? jt : jf */
|
||||
mask = 0x700000; /* jnz */
|
||||
/* Emit test if the branch targets are different */
|
||||
if (filter->jt != filter->jf) {
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* lr %r4,%r5 */
|
||||
EMIT2(0x1845);
|
||||
/* nr %r4,%r12 */
|
||||
EMIT2(0x144c);
|
||||
}
|
||||
goto branch;
|
||||
case BPF_S_LD_W_ABS: /* A = *(u32 *) (skb->data+K) */
|
||||
jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_WORD;
|
||||
offset = jit->off_load_word;
|
||||
goto load_abs;
|
||||
case BPF_S_LD_H_ABS: /* A = *(u16 *) (skb->data+K) */
|
||||
jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_HALF;
|
||||
offset = jit->off_load_half;
|
||||
goto load_abs;
|
||||
case BPF_S_LD_B_ABS: /* A = *(u8 *) (skb->data+K) */
|
||||
jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_BYTE;
|
||||
offset = jit->off_load_byte;
|
||||
load_abs: if ((int) K < 0)
|
||||
goto out;
|
||||
call_fn: /* lg %r1,<d(function)>(%r13) */
|
||||
EMIT6_DISP(0xe310d000, 0x0004, offset);
|
||||
/* l %r3,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5830d000, EMIT_CONST(K));
|
||||
/* basr %r8,%r1 */
|
||||
EMIT2(0x0d81);
|
||||
/* jnz <ret0> */
|
||||
EMIT4_PCREL(0xa7740000, (jit->ret0_ip - jit->prg));
|
||||
break;
|
||||
case BPF_S_LD_W_IND: /* A = *(u32 *) (skb->data+K+X) */
|
||||
jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IWORD;
|
||||
offset = jit->off_load_iword;
|
||||
goto call_fn;
|
||||
case BPF_S_LD_H_IND: /* A = *(u16 *) (skb->data+K+X) */
|
||||
jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IHALF;
|
||||
offset = jit->off_load_ihalf;
|
||||
goto call_fn;
|
||||
case BPF_S_LD_B_IND: /* A = *(u8 *) (skb->data+K+X) */
|
||||
jit->seen |= SEEN_DATAREF | SEEN_RET0 | SEEN_LOAD_IBYTE;
|
||||
offset = jit->off_load_ibyte;
|
||||
goto call_fn;
|
||||
case BPF_S_LDX_B_MSH:
|
||||
/* X = (*(u8 *)(skb->data+K) & 0xf) << 2 */
|
||||
jit->seen |= SEEN_RET0;
|
||||
if ((int) K < 0) {
|
||||
/* j <ret0> */
|
||||
EMIT4_PCREL(0xa7f40000, (jit->ret0_ip - jit->prg));
|
||||
break;
|
||||
}
|
||||
jit->seen |= SEEN_DATAREF | SEEN_LOAD_BMSH;
|
||||
offset = jit->off_load_bmsh;
|
||||
goto call_fn;
|
||||
case BPF_S_LD_W_LEN: /* A = skb->len; */
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, len) != 4);
|
||||
/* l %r5,<d(len)>(%r2) */
|
||||
EMIT4_DISP(0x58502000, offsetof(struct sk_buff, len));
|
||||
break;
|
||||
case BPF_S_LDX_W_LEN: /* X = skb->len; */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* l %r12,<d(len)>(%r2) */
|
||||
EMIT4_DISP(0x58c02000, offsetof(struct sk_buff, len));
|
||||
break;
|
||||
case BPF_S_LD_IMM: /* A = K */
|
||||
if (K <= 16383)
|
||||
/* lhi %r5,K */
|
||||
EMIT4_IMM(0xa7580000, K);
|
||||
else if (test_facility(21))
|
||||
/* llilf %r5,<K> */
|
||||
EMIT6_IMM(0xc05f0000, K);
|
||||
else
|
||||
/* l %r5,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x5850d000, EMIT_CONST(K));
|
||||
break;
|
||||
case BPF_S_LDX_IMM: /* X = K */
|
||||
jit->seen |= SEEN_XREG;
|
||||
if (K <= 16383)
|
||||
/* lhi %r12,<K> */
|
||||
EMIT4_IMM(0xa7c80000, K);
|
||||
else if (test_facility(21))
|
||||
/* llilf %r12,<K> */
|
||||
EMIT6_IMM(0xc0cf0000, K);
|
||||
else
|
||||
/* l %r12,<d(K)>(%r13) */
|
||||
EMIT4_DISP(0x58c0d000, EMIT_CONST(K));
|
||||
break;
|
||||
case BPF_S_LD_MEM: /* A = mem[K] */
|
||||
jit->seen |= SEEN_MEM;
|
||||
/* l %r5,<K>(%r15) */
|
||||
EMIT4_DISP(0x5850f000,
|
||||
(jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
|
||||
break;
|
||||
case BPF_S_LDX_MEM: /* X = mem[K] */
|
||||
jit->seen |= SEEN_XREG | SEEN_MEM;
|
||||
/* l %r12,<K>(%r15) */
|
||||
EMIT4_DISP(0x58c0f000,
|
||||
(jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
|
||||
break;
|
||||
case BPF_S_MISC_TAX: /* X = A */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* lr %r12,%r5 */
|
||||
EMIT2(0x18c5);
|
||||
break;
|
||||
case BPF_S_MISC_TXA: /* A = X */
|
||||
jit->seen |= SEEN_XREG;
|
||||
/* lr %r5,%r12 */
|
||||
EMIT2(0x185c);
|
||||
break;
|
||||
case BPF_S_RET_K:
|
||||
if (K == 0) {
|
||||
jit->seen |= SEEN_RET0;
|
||||
if (last)
|
||||
break;
|
||||
/* j <ret0> */
|
||||
EMIT4_PCREL(0xa7f40000, jit->ret0_ip - jit->prg);
|
||||
} else {
|
||||
if (K <= 16383)
|
||||
/* lghi %r2,K */
|
||||
EMIT4_IMM(0xa7290000, K);
|
||||
else
|
||||
/* llgf %r2,<K>(%r13) */
|
||||
EMIT6_DISP(0xe320d000, 0x0016, EMIT_CONST(K));
|
||||
/* j <exit> */
|
||||
if (last && !(jit->seen & SEEN_RET0))
|
||||
break;
|
||||
EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
|
||||
}
|
||||
break;
|
||||
case BPF_S_RET_A:
|
||||
/* llgfr %r2,%r5 */
|
||||
EMIT4(0xb9160025);
|
||||
/* j <exit> */
|
||||
EMIT4_PCREL(0xa7f40000, jit->exit_ip - jit->prg);
|
||||
break;
|
||||
case BPF_S_ST: /* mem[K] = A */
|
||||
jit->seen |= SEEN_MEM;
|
||||
/* st %r5,<K>(%r15) */
|
||||
EMIT4_DISP(0x5050f000,
|
||||
(jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
|
||||
break;
|
||||
case BPF_S_STX: /* mem[K] = X : mov %ebx,off8(%rbp) */
|
||||
jit->seen |= SEEN_XREG | SEEN_MEM;
|
||||
/* st %r12,<K>(%r15) */
|
||||
EMIT4_DISP(0x50c0f000,
|
||||
(jit->seen & SEEN_DATAREF) ? 160 + K*4 : K*4);
|
||||
break;
|
||||
case BPF_S_ANC_PROTOCOL: /* A = ntohs(skb->protocol); */
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, protocol) != 2);
|
||||
/* lhi %r5,0 */
|
||||
EMIT4(0xa7580000);
|
||||
/* icm %r5,3,<d(protocol)>(%r2) */
|
||||
EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, protocol));
|
||||
break;
|
||||
case BPF_S_ANC_IFINDEX: /* if (!skb->dev) return 0;
|
||||
* A = skb->dev->ifindex */
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, ifindex) != 4);
|
||||
jit->seen |= SEEN_RET0;
|
||||
/* lg %r1,<d(dev)>(%r2) */
|
||||
EMIT6_DISP(0xe3102000, 0x0004, offsetof(struct sk_buff, dev));
|
||||
/* ltgr %r1,%r1 */
|
||||
EMIT4(0xb9020011);
|
||||
/* jz <ret0> */
|
||||
EMIT4_PCREL(0xa7840000, jit->ret0_ip - jit->prg);
|
||||
/* l %r5,<d(ifindex)>(%r1) */
|
||||
EMIT4_DISP(0x58501000, offsetof(struct net_device, ifindex));
|
||||
break;
|
||||
case BPF_S_ANC_MARK: /* A = skb->mark */
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, mark) != 4);
|
||||
/* l %r5,<d(mark)>(%r2) */
|
||||
EMIT4_DISP(0x58502000, offsetof(struct sk_buff, mark));
|
||||
break;
|
||||
case BPF_S_ANC_QUEUE: /* A = skb->queue_mapping */
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, queue_mapping) != 2);
|
||||
/* lhi %r5,0 */
|
||||
EMIT4(0xa7580000);
|
||||
/* icm %r5,3,<d(queue_mapping)>(%r2) */
|
||||
EMIT4_DISP(0xbf532000, offsetof(struct sk_buff, queue_mapping));
|
||||
break;
|
||||
case BPF_S_ANC_HATYPE: /* if (!skb->dev) return 0;
|
||||
* A = skb->dev->type */
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct net_device, type) != 2);
|
||||
jit->seen |= SEEN_RET0;
|
||||
/* lg %r1,<d(dev)>(%r2) */
|
||||
EMIT6_DISP(0xe3102000, 0x0004, offsetof(struct sk_buff, dev));
|
||||
/* ltgr %r1,%r1 */
|
||||
EMIT4(0xb9020011);
|
||||
/* jz <ret0> */
|
||||
EMIT4_PCREL(0xa7840000, jit->ret0_ip - jit->prg);
|
||||
/* lhi %r5,0 */
|
||||
EMIT4(0xa7580000);
|
||||
/* icm %r5,3,<d(type)>(%r1) */
|
||||
EMIT4_DISP(0xbf531000, offsetof(struct net_device, type));
|
||||
break;
|
||||
case BPF_S_ANC_RXHASH: /* A = skb->rxhash */
|
||||
BUILD_BUG_ON(FIELD_SIZEOF(struct sk_buff, rxhash) != 4);
|
||||
/* l %r5,<d(rxhash)>(%r2) */
|
||||
EMIT4_DISP(0x58502000, offsetof(struct sk_buff, rxhash));
|
||||
break;
|
||||
case BPF_S_ANC_CPU: /* A = smp_processor_id() */
|
||||
#ifdef CONFIG_SMP
|
||||
/* l %r5,<d(cpu_nr)> */
|
||||
EMIT4_DISP(0x58500000, offsetof(struct _lowcore, cpu_nr));
|
||||
#else
|
||||
/* lhi %r5,0 */
|
||||
EMIT4(0xa7580000);
|
||||
#endif
|
||||
break;
|
||||
default: /* too complex, give up */
|
||||
goto out;
|
||||
}
|
||||
addrs[i] = jit->prg - jit->start;
|
||||
return 0;
|
||||
out:
|
||||
return -1;
|
||||
}
|
||||
|
||||
void bpf_jit_compile(struct sk_filter *fp)
|
||||
{
|
||||
unsigned long size, prg_len, lit_len;
|
||||
struct bpf_jit jit, cjit;
|
||||
unsigned int *addrs;
|
||||
int pass, i;
|
||||
|
||||
if (!bpf_jit_enable)
|
||||
return;
|
||||
addrs = kmalloc(fp->len * sizeof(*addrs), GFP_KERNEL);
|
||||
if (addrs == NULL)
|
||||
return;
|
||||
memset(addrs, 0, fp->len * sizeof(*addrs));
|
||||
memset(&jit, 0, sizeof(cjit));
|
||||
memset(&cjit, 0, sizeof(cjit));
|
||||
|
||||
for (pass = 0; pass < 10; pass++) {
|
||||
jit.prg = jit.start;
|
||||
jit.lit = jit.mid;
|
||||
|
||||
bpf_jit_prologue(&jit);
|
||||
bpf_jit_noleaks(&jit, fp->insns);
|
||||
for (i = 0; i < fp->len; i++) {
|
||||
if (bpf_jit_insn(&jit, fp->insns + i, addrs, i,
|
||||
i == fp->len - 1))
|
||||
goto out;
|
||||
}
|
||||
bpf_jit_epilogue(&jit);
|
||||
if (jit.start) {
|
||||
WARN_ON(jit.prg > cjit.prg || jit.lit > cjit.lit);
|
||||
if (memcmp(&jit, &cjit, sizeof(jit)) == 0)
|
||||
break;
|
||||
} else if (jit.prg == cjit.prg && jit.lit == cjit.lit) {
|
||||
prg_len = jit.prg - jit.start;
|
||||
lit_len = jit.lit - jit.mid;
|
||||
size = max_t(unsigned long, prg_len + lit_len,
|
||||
sizeof(struct work_struct));
|
||||
if (size >= BPF_SIZE_MAX)
|
||||
goto out;
|
||||
jit.start = module_alloc(size);
|
||||
if (!jit.start)
|
||||
goto out;
|
||||
jit.prg = jit.mid = jit.start + prg_len;
|
||||
jit.lit = jit.end = jit.start + prg_len + lit_len;
|
||||
jit.base_ip += (unsigned long) jit.start;
|
||||
jit.exit_ip += (unsigned long) jit.start;
|
||||
jit.ret0_ip += (unsigned long) jit.start;
|
||||
}
|
||||
cjit = jit;
|
||||
}
|
||||
if (bpf_jit_enable > 1) {
|
||||
pr_err("flen=%d proglen=%lu pass=%d image=%p\n",
|
||||
fp->len, jit.end - jit.start, pass, jit.start);
|
||||
if (jit.start) {
|
||||
printk(KERN_ERR "JIT code:\n");
|
||||
print_fn_code(jit.start, jit.mid - jit.start);
|
||||
print_hex_dump(KERN_ERR, "JIT literals:\n",
|
||||
DUMP_PREFIX_ADDRESS, 16, 1,
|
||||
jit.mid, jit.end - jit.mid, false);
|
||||
}
|
||||
}
|
||||
if (jit.start)
|
||||
fp->bpf_func = (void *) jit.start;
|
||||
out:
|
||||
kfree(addrs);
|
||||
}
|
||||
|
||||
static void jit_free_defer(struct work_struct *arg)
|
||||
{
|
||||
module_free(NULL, arg);
|
||||
}
|
||||
|
||||
/* run from softirq, we must use a work_struct to call
|
||||
* module_free() from process context
|
||||
*/
|
||||
void bpf_jit_free(struct sk_filter *fp)
|
||||
{
|
||||
struct work_struct *work;
|
||||
|
||||
if (fp->bpf_func == sk_run_filter)
|
||||
return;
|
||||
work = (struct work_struct *)fp->bpf_func;
|
||||
INIT_WORK(work, jit_free_defer);
|
||||
schedule_work(work);
|
||||
}
|
@ -1,9 +1,8 @@
|
||||
/*
|
||||
* File...........: linux/fs/partitions/ibm.c
|
||||
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
|
||||
* Volker Sameske <sameske@de.ibm.com>
|
||||
* Bugreports.to..: <Linux390@de.ibm.com>
|
||||
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
|
||||
* Copyright IBM Corp. 1999, 2012
|
||||
*/
|
||||
|
||||
#include <linux/buffer_head.h>
|
||||
@ -17,17 +16,23 @@
|
||||
#include "check.h"
|
||||
#include "ibm.h"
|
||||
|
||||
|
||||
union label_t {
|
||||
struct vtoc_volume_label_cdl vol;
|
||||
struct vtoc_volume_label_ldl lnx;
|
||||
struct vtoc_cms_label cms;
|
||||
};
|
||||
|
||||
/*
|
||||
* compute the block number from a
|
||||
* cyl-cyl-head-head structure
|
||||
*/
|
||||
static sector_t
|
||||
cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
|
||||
|
||||
static sector_t cchh2blk(struct vtoc_cchh *ptr, struct hd_geometry *geo)
|
||||
{
|
||||
sector_t cyl;
|
||||
__u16 head;
|
||||
|
||||
/*decode cylinder and heads for large volumes */
|
||||
/* decode cylinder and heads for large volumes */
|
||||
cyl = ptr->hh & 0xFFF0;
|
||||
cyl <<= 12;
|
||||
cyl |= ptr->cc;
|
||||
@ -40,13 +45,12 @@ cchh2blk (struct vtoc_cchh *ptr, struct hd_geometry *geo) {
|
||||
* compute the block number from a
|
||||
* cyl-cyl-head-head-block structure
|
||||
*/
|
||||
static sector_t
|
||||
cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
|
||||
|
||||
static sector_t cchhb2blk(struct vtoc_cchhb *ptr, struct hd_geometry *geo)
|
||||
{
|
||||
sector_t cyl;
|
||||
__u16 head;
|
||||
|
||||
/*decode cylinder and heads for large volumes */
|
||||
/* decode cylinder and heads for large volumes */
|
||||
cyl = ptr->hh & 0xFFF0;
|
||||
cyl <<= 12;
|
||||
cyl |= ptr->cc;
|
||||
@ -56,26 +60,243 @@ cchhb2blk (struct vtoc_cchhb *ptr, struct hd_geometry *geo) {
|
||||
ptr->b;
|
||||
}
|
||||
|
||||
static int find_label(struct parsed_partitions *state,
|
||||
dasd_information2_t *info,
|
||||
struct hd_geometry *geo,
|
||||
int blocksize,
|
||||
sector_t *labelsect,
|
||||
char name[],
|
||||
char type[],
|
||||
union label_t *label)
|
||||
{
|
||||
Sector sect;
|
||||
unsigned char *data;
|
||||
sector_t testsect[3];
|
||||
unsigned char temp[5];
|
||||
int found = 0;
|
||||
int i, testcount;
|
||||
|
||||
/* There a three places where we may find a valid label:
|
||||
* - on an ECKD disk it's block 2
|
||||
* - on an FBA disk it's block 1
|
||||
* - on an CMS formatted FBA disk it is sector 1, even if the block size
|
||||
* is larger than 512 bytes (possible if the DIAG discipline is used)
|
||||
* If we have a valid info structure, then we know exactly which case we
|
||||
* have, otherwise we just search through all possebilities.
|
||||
*/
|
||||
if (info) {
|
||||
if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
|
||||
(info->cu_type == 0x3880 && info->dev_type == 0x3370))
|
||||
testsect[0] = info->label_block;
|
||||
else
|
||||
testsect[0] = info->label_block * (blocksize >> 9);
|
||||
testcount = 1;
|
||||
} else {
|
||||
testsect[0] = 1;
|
||||
testsect[1] = (blocksize >> 9);
|
||||
testsect[2] = 2 * (blocksize >> 9);
|
||||
testcount = 3;
|
||||
}
|
||||
for (i = 0; i < testcount; ++i) {
|
||||
data = read_part_sector(state, testsect[i], §);
|
||||
if (data == NULL)
|
||||
continue;
|
||||
memcpy(label, data, sizeof(*label));
|
||||
memcpy(temp, data, 4);
|
||||
temp[4] = 0;
|
||||
EBCASC(temp, 4);
|
||||
put_dev_sector(sect);
|
||||
if (!strcmp(temp, "VOL1") ||
|
||||
!strcmp(temp, "LNX1") ||
|
||||
!strcmp(temp, "CMS1")) {
|
||||
if (!strcmp(temp, "VOL1")) {
|
||||
strncpy(type, label->vol.vollbl, 4);
|
||||
strncpy(name, label->vol.volid, 6);
|
||||
} else {
|
||||
strncpy(type, label->lnx.vollbl, 4);
|
||||
strncpy(name, label->lnx.volid, 6);
|
||||
}
|
||||
EBCASC(type, 4);
|
||||
EBCASC(name, 6);
|
||||
*labelsect = testsect[i];
|
||||
found = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
memset(label, 0, sizeof(*label));
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
static int find_vol1_partitions(struct parsed_partitions *state,
|
||||
struct hd_geometry *geo,
|
||||
int blocksize,
|
||||
char name[],
|
||||
union label_t *label)
|
||||
{
|
||||
sector_t blk;
|
||||
int counter;
|
||||
char tmp[64];
|
||||
Sector sect;
|
||||
unsigned char *data;
|
||||
loff_t offset, size;
|
||||
struct vtoc_format1_label f1;
|
||||
int secperblk;
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
/*
|
||||
* get start of VTOC from the disk label and then search for format1
|
||||
* and format8 labels
|
||||
*/
|
||||
secperblk = blocksize >> 9;
|
||||
blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
|
||||
counter = 0;
|
||||
data = read_part_sector(state, blk * secperblk, §);
|
||||
while (data != NULL) {
|
||||
memcpy(&f1, data, sizeof(struct vtoc_format1_label));
|
||||
put_dev_sector(sect);
|
||||
/* skip FMT4 / FMT5 / FMT7 labels */
|
||||
if (f1.DS1FMTID == _ascebc['4']
|
||||
|| f1.DS1FMTID == _ascebc['5']
|
||||
|| f1.DS1FMTID == _ascebc['7']
|
||||
|| f1.DS1FMTID == _ascebc['9']) {
|
||||
blk++;
|
||||
data = read_part_sector(state, blk * secperblk, §);
|
||||
continue;
|
||||
}
|
||||
/* only FMT1 and 8 labels valid at this point */
|
||||
if (f1.DS1FMTID != _ascebc['1'] &&
|
||||
f1.DS1FMTID != _ascebc['8'])
|
||||
break;
|
||||
/* OK, we got valid partition data */
|
||||
offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
|
||||
size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
|
||||
offset + geo->sectors;
|
||||
offset *= secperblk;
|
||||
size *= secperblk;
|
||||
if (counter >= state->limit)
|
||||
break;
|
||||
put_partition(state, counter + 1, offset, size);
|
||||
counter++;
|
||||
blk++;
|
||||
data = read_part_sector(state, blk * secperblk, §);
|
||||
}
|
||||
strlcat(state->pp_buf, "\n", PAGE_SIZE);
|
||||
|
||||
if (!data)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int find_lnx1_partitions(struct parsed_partitions *state,
|
||||
struct hd_geometry *geo,
|
||||
int blocksize,
|
||||
char name[],
|
||||
union label_t *label,
|
||||
sector_t labelsect,
|
||||
loff_t i_size,
|
||||
dasd_information2_t *info)
|
||||
{
|
||||
loff_t offset, geo_size, size;
|
||||
char tmp[64];
|
||||
int secperblk;
|
||||
|
||||
snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
secperblk = blocksize >> 9;
|
||||
if (label->lnx.ldl_version == 0xf2) {
|
||||
size = label->lnx.formatted_blocks * secperblk;
|
||||
} else {
|
||||
/*
|
||||
* Formated w/o large volume support. If the sanity check
|
||||
* 'size based on geo == size based on i_size' is true, then
|
||||
* we can safely assume that we know the formatted size of
|
||||
* the disk, otherwise we need additional information
|
||||
* that we can only get from a real DASD device.
|
||||
*/
|
||||
geo_size = geo->cylinders * geo->heads
|
||||
* geo->sectors * secperblk;
|
||||
size = i_size >> 9;
|
||||
if (size != geo_size) {
|
||||
if (!info) {
|
||||
strlcat(state->pp_buf, "\n", PAGE_SIZE);
|
||||
return 1;
|
||||
}
|
||||
if (!strcmp(info->type, "ECKD"))
|
||||
if (geo_size < size)
|
||||
size = geo_size;
|
||||
/* else keep size based on i_size */
|
||||
}
|
||||
}
|
||||
/* first and only partition starts in the first block after the label */
|
||||
offset = labelsect + secperblk;
|
||||
put_partition(state, 1, offset, size - offset);
|
||||
strlcat(state->pp_buf, "\n", PAGE_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int find_cms1_partitions(struct parsed_partitions *state,
|
||||
struct hd_geometry *geo,
|
||||
int blocksize,
|
||||
char name[],
|
||||
union label_t *label,
|
||||
sector_t labelsect)
|
||||
{
|
||||
loff_t offset, size;
|
||||
char tmp[64];
|
||||
int secperblk;
|
||||
|
||||
/*
|
||||
* VM style CMS1 labeled disk
|
||||
*/
|
||||
blocksize = label->cms.block_size;
|
||||
secperblk = blocksize >> 9;
|
||||
if (label->cms.disk_offset != 0) {
|
||||
snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
/* disk is reserved minidisk */
|
||||
offset = label->cms.disk_offset * secperblk;
|
||||
size = (label->cms.block_count - 1) * secperblk;
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
/*
|
||||
* Special case for FBA devices:
|
||||
* If an FBA device is CMS formatted with blocksize > 512 byte
|
||||
* and the DIAG discipline is used, then the CMS label is found
|
||||
* in sector 1 instead of block 1. However, the partition is
|
||||
* still supposed to start in block 2.
|
||||
*/
|
||||
if (labelsect == 1)
|
||||
offset = 2 * secperblk;
|
||||
else
|
||||
offset = labelsect + secperblk;
|
||||
size = label->cms.block_count * secperblk;
|
||||
}
|
||||
|
||||
put_partition(state, 1, offset, size-offset);
|
||||
strlcat(state->pp_buf, "\n", PAGE_SIZE);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This is the main function, called by check.c
|
||||
*/
|
||||
int ibm_partition(struct parsed_partitions *state)
|
||||
{
|
||||
struct block_device *bdev = state->bdev;
|
||||
int blocksize, res;
|
||||
loff_t i_size, offset, size, fmt_size;
|
||||
loff_t i_size, offset, size;
|
||||
dasd_information2_t *info;
|
||||
struct hd_geometry *geo;
|
||||
char type[5] = {0,};
|
||||
char name[7] = {0,};
|
||||
union label_t {
|
||||
struct vtoc_volume_label_cdl vol;
|
||||
struct vtoc_volume_label_ldl lnx;
|
||||
struct vtoc_cms_label cms;
|
||||
} *label;
|
||||
unsigned char *data;
|
||||
Sector sect;
|
||||
sector_t labelsect;
|
||||
char tmp[64];
|
||||
union label_t *label;
|
||||
|
||||
res = 0;
|
||||
blocksize = bdev_logical_block_size(bdev);
|
||||
@ -84,7 +305,6 @@ int ibm_partition(struct parsed_partitions *state)
|
||||
i_size = i_size_read(bdev->bd_inode);
|
||||
if (i_size == 0)
|
||||
goto out_exit;
|
||||
|
||||
info = kmalloc(sizeof(dasd_information2_t), GFP_KERNEL);
|
||||
if (info == NULL)
|
||||
goto out_exit;
|
||||
@ -94,176 +314,45 @@ int ibm_partition(struct parsed_partitions *state)
|
||||
label = kmalloc(sizeof(union label_t), GFP_KERNEL);
|
||||
if (label == NULL)
|
||||
goto out_nolab;
|
||||
|
||||
if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0 ||
|
||||
ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
|
||||
if (ioctl_by_bdev(bdev, HDIO_GETGEO, (unsigned long)geo) != 0)
|
||||
goto out_freeall;
|
||||
|
||||
/*
|
||||
* Special case for FBA disks: label sector does not depend on
|
||||
* blocksize.
|
||||
*/
|
||||
if ((info->cu_type == 0x6310 && info->dev_type == 0x9336) ||
|
||||
(info->cu_type == 0x3880 && info->dev_type == 0x3370))
|
||||
labelsect = info->label_block;
|
||||
else
|
||||
labelsect = info->label_block * (blocksize >> 9);
|
||||
|
||||
/*
|
||||
* Get volume label, extract name and type.
|
||||
*/
|
||||
data = read_part_sector(state, labelsect, §);
|
||||
if (data == NULL)
|
||||
goto out_readerr;
|
||||
|
||||
memcpy(label, data, sizeof(union label_t));
|
||||
put_dev_sector(sect);
|
||||
|
||||
if ((!info->FBA_layout) && (!strcmp(info->type, "ECKD"))) {
|
||||
strncpy(type, label->vol.vollbl, 4);
|
||||
strncpy(name, label->vol.volid, 6);
|
||||
} else {
|
||||
strncpy(type, label->lnx.vollbl, 4);
|
||||
strncpy(name, label->lnx.volid, 6);
|
||||
if (ioctl_by_bdev(bdev, BIODASDINFO2, (unsigned long)info) != 0) {
|
||||
kfree(info);
|
||||
info = NULL;
|
||||
}
|
||||
EBCASC(type, 4);
|
||||
EBCASC(name, 6);
|
||||
|
||||
res = 1;
|
||||
|
||||
/*
|
||||
* Three different formats: LDL, CDL and unformated disk
|
||||
*
|
||||
* identified by info->format
|
||||
*
|
||||
* unformated disks we do not have to care about
|
||||
*/
|
||||
if (info->format == DASD_FORMAT_LDL) {
|
||||
if (strncmp(type, "CMS1", 4) == 0) {
|
||||
/*
|
||||
* VM style CMS1 labeled disk
|
||||
*/
|
||||
blocksize = label->cms.block_size;
|
||||
if (label->cms.disk_offset != 0) {
|
||||
snprintf(tmp, sizeof(tmp), "CMS1/%8s(MDSK):", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
/* disk is reserved minidisk */
|
||||
offset = label->cms.disk_offset;
|
||||
size = (label->cms.block_count - 1)
|
||||
* (blocksize >> 9);
|
||||
} else {
|
||||
snprintf(tmp, sizeof(tmp), "CMS1/%8s:", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
offset = (info->label_block + 1);
|
||||
size = label->cms.block_count
|
||||
* (blocksize >> 9);
|
||||
}
|
||||
put_partition(state, 1, offset*(blocksize >> 9),
|
||||
size-offset*(blocksize >> 9));
|
||||
} else {
|
||||
if (strncmp(type, "LNX1", 4) == 0) {
|
||||
snprintf(tmp, sizeof(tmp), "LNX1/%8s:", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
if (label->lnx.ldl_version == 0xf2) {
|
||||
fmt_size = label->lnx.formatted_blocks
|
||||
* (blocksize >> 9);
|
||||
} else if (!strcmp(info->type, "ECKD")) {
|
||||
/* formated w/o large volume support */
|
||||
fmt_size = geo->cylinders * geo->heads
|
||||
* geo->sectors * (blocksize >> 9);
|
||||
} else {
|
||||
/* old label and no usable disk geometry
|
||||
* (e.g. DIAG) */
|
||||
fmt_size = i_size >> 9;
|
||||
}
|
||||
size = i_size >> 9;
|
||||
if (fmt_size < size)
|
||||
size = fmt_size;
|
||||
offset = (info->label_block + 1);
|
||||
} else {
|
||||
/* unlabeled disk */
|
||||
strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
|
||||
size = i_size >> 9;
|
||||
offset = (info->label_block + 1);
|
||||
}
|
||||
put_partition(state, 1, offset*(blocksize >> 9),
|
||||
size-offset*(blocksize >> 9));
|
||||
if (find_label(state, info, geo, blocksize, &labelsect, name, type,
|
||||
label)) {
|
||||
if (!strncmp(type, "VOL1", 4)) {
|
||||
res = find_vol1_partitions(state, geo, blocksize, name,
|
||||
label);
|
||||
} else if (!strncmp(type, "LNX1", 4)) {
|
||||
res = find_lnx1_partitions(state, geo, blocksize, name,
|
||||
label, labelsect, i_size,
|
||||
info);
|
||||
} else if (!strncmp(type, "CMS1", 4)) {
|
||||
res = find_cms1_partitions(state, geo, blocksize, name,
|
||||
label, labelsect);
|
||||
}
|
||||
} else if (info->format == DASD_FORMAT_CDL) {
|
||||
} else if (info) {
|
||||
/*
|
||||
* New style CDL formatted disk
|
||||
* ugly but needed for backward compatibility:
|
||||
* If the block device is a DASD (i.e. BIODASDINFO2 works),
|
||||
* then we claim it in any case, even though it has no valid
|
||||
* label. If it has the LDL format, then we simply define a
|
||||
* partition as if it had an LNX1 label.
|
||||
*/
|
||||
sector_t blk;
|
||||
int counter;
|
||||
res = 1;
|
||||
if (info->format == DASD_FORMAT_LDL) {
|
||||
strlcat(state->pp_buf, "(nonl)", PAGE_SIZE);
|
||||
size = i_size >> 9;
|
||||
offset = (info->label_block + 1) * (blocksize >> 9);
|
||||
put_partition(state, 1, offset, size-offset);
|
||||
strlcat(state->pp_buf, "\n", PAGE_SIZE);
|
||||
}
|
||||
} else
|
||||
res = 0;
|
||||
|
||||
/*
|
||||
* check if VOL1 label is available
|
||||
* if not, something is wrong, skipping partition detection
|
||||
*/
|
||||
if (strncmp(type, "VOL1", 4) == 0) {
|
||||
snprintf(tmp, sizeof(tmp), "VOL1/%8s:", name);
|
||||
strlcat(state->pp_buf, tmp, PAGE_SIZE);
|
||||
/*
|
||||
* get block number and read then go through format1
|
||||
* labels
|
||||
*/
|
||||
blk = cchhb2blk(&label->vol.vtoc, geo) + 1;
|
||||
counter = 0;
|
||||
data = read_part_sector(state, blk * (blocksize/512),
|
||||
§);
|
||||
while (data != NULL) {
|
||||
struct vtoc_format1_label f1;
|
||||
|
||||
memcpy(&f1, data,
|
||||
sizeof(struct vtoc_format1_label));
|
||||
put_dev_sector(sect);
|
||||
|
||||
/* skip FMT4 / FMT5 / FMT7 labels */
|
||||
if (f1.DS1FMTID == _ascebc['4']
|
||||
|| f1.DS1FMTID == _ascebc['5']
|
||||
|| f1.DS1FMTID == _ascebc['7']
|
||||
|| f1.DS1FMTID == _ascebc['9']) {
|
||||
blk++;
|
||||
data = read_part_sector(state,
|
||||
blk * (blocksize/512), §);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* only FMT1 and 8 labels valid at this point */
|
||||
if (f1.DS1FMTID != _ascebc['1'] &&
|
||||
f1.DS1FMTID != _ascebc['8'])
|
||||
break;
|
||||
|
||||
/* OK, we got valid partition data */
|
||||
offset = cchh2blk(&f1.DS1EXT1.llimit, geo);
|
||||
size = cchh2blk(&f1.DS1EXT1.ulimit, geo) -
|
||||
offset + geo->sectors;
|
||||
if (counter >= state->limit)
|
||||
break;
|
||||
put_partition(state, counter + 1,
|
||||
offset * (blocksize >> 9),
|
||||
size * (blocksize >> 9));
|
||||
counter++;
|
||||
blk++;
|
||||
data = read_part_sector(state,
|
||||
blk * (blocksize/512), §);
|
||||
}
|
||||
|
||||
if (!data)
|
||||
/* Are we not supposed to report this ? */
|
||||
goto out_readerr;
|
||||
} else
|
||||
printk(KERN_INFO "Expected Label VOL1 not "
|
||||
"found, treating as CDL formated Disk");
|
||||
|
||||
}
|
||||
|
||||
strlcat(state->pp_buf, "\n", PAGE_SIZE);
|
||||
goto out_freeall;
|
||||
|
||||
|
||||
out_readerr:
|
||||
res = -1;
|
||||
out_freeall:
|
||||
kfree(label);
|
||||
out_nolab:
|
||||
|
@ -70,3 +70,21 @@ config DASD_EER
|
||||
This driver provides a character device interface to the
|
||||
DASD extended error reporting. This is only needed if you want to
|
||||
use applications written for the EER facility.
|
||||
|
||||
config SCM_BLOCK
|
||||
def_tristate m
|
||||
prompt "Support for Storage Class Memory"
|
||||
depends on S390 && BLOCK && EADM_SCH && SCM_BUS
|
||||
help
|
||||
Block device driver for Storage Class Memory (SCM). This driver
|
||||
provides a block device interface for each available SCM increment.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called scm_block.
|
||||
|
||||
config SCM_BLOCK_CLUSTER_WRITE
|
||||
def_bool y
|
||||
prompt "SCM force cluster writes"
|
||||
depends on SCM_BLOCK
|
||||
help
|
||||
Force writes to Storage Class Memory (SCM) to be in done in clusters.
|
||||
|
@ -17,3 +17,9 @@ obj-$(CONFIG_DASD_ECKD) += dasd_eckd_mod.o
|
||||
obj-$(CONFIG_DASD_FBA) += dasd_fba_mod.o
|
||||
obj-$(CONFIG_BLK_DEV_XPRAM) += xpram.o
|
||||
obj-$(CONFIG_DCSSBLK) += dcssblk.o
|
||||
|
||||
scm_block-objs := scm_drv.o scm_blk.o
|
||||
ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
|
||||
scm_block-objs += scm_blk_cluster.o
|
||||
endif
|
||||
obj-$(CONFIG_SCM_BLOCK) += scm_block.o
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <linux/compat.h>
|
||||
#include <linux/init.h>
|
||||
|
||||
#include <asm/css_chars.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/idals.h>
|
||||
#include <asm/ebcdic.h>
|
||||
@ -31,8 +32,6 @@
|
||||
|
||||
#include "dasd_int.h"
|
||||
#include "dasd_eckd.h"
|
||||
#include "../cio/chsc.h"
|
||||
|
||||
|
||||
#ifdef PRINTK_HEADER
|
||||
#undef PRINTK_HEADER
|
||||
@ -140,6 +139,10 @@ dasd_eckd_set_online(struct ccw_device *cdev)
|
||||
static const int sizes_trk0[] = { 28, 148, 84 };
|
||||
#define LABEL_SIZE 140
|
||||
|
||||
/* head and record addresses of count_area read in analysis ccw */
|
||||
static const int count_area_head[] = { 0, 0, 0, 0, 2 };
|
||||
static const int count_area_rec[] = { 1, 2, 3, 4, 1 };
|
||||
|
||||
static inline unsigned int
|
||||
round_up_multiple(unsigned int no, unsigned int mult)
|
||||
{
|
||||
@ -212,7 +215,7 @@ check_XRC (struct ccw1 *de_ccw,
|
||||
|
||||
rc = get_sync_clock(&data->ep_sys_time);
|
||||
/* Ignore return code if sync clock is switched off. */
|
||||
if (rc == -ENOSYS || rc == -EACCES)
|
||||
if (rc == -EOPNOTSUPP || rc == -EACCES)
|
||||
rc = 0;
|
||||
|
||||
de_ccw->count = sizeof(struct DE_eckd_data);
|
||||
@ -323,7 +326,7 @@ static int check_XRC_on_prefix(struct PFX_eckd_data *pfxdata,
|
||||
|
||||
rc = get_sync_clock(&pfxdata->define_extent.ep_sys_time);
|
||||
/* Ignore return code if sync clock is switched off. */
|
||||
if (rc == -ENOSYS || rc == -EACCES)
|
||||
if (rc == -EOPNOTSUPP || rc == -EACCES)
|
||||
rc = 0;
|
||||
return rc;
|
||||
}
|
||||
@ -1940,7 +1943,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
|
||||
count_area = NULL;
|
||||
for (i = 0; i < 3; i++) {
|
||||
if (private->count_area[i].kl != 4 ||
|
||||
private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4) {
|
||||
private->count_area[i].dl != dasd_eckd_cdl_reclen(i) - 4 ||
|
||||
private->count_area[i].cyl != 0 ||
|
||||
private->count_area[i].head != count_area_head[i] ||
|
||||
private->count_area[i].record != count_area_rec[i]) {
|
||||
private->uses_cdl = 0;
|
||||
break;
|
||||
}
|
||||
@ -1952,7 +1958,10 @@ static int dasd_eckd_end_analysis(struct dasd_block *block)
|
||||
for (i = 0; i < 5; i++) {
|
||||
if ((private->count_area[i].kl != 0) ||
|
||||
(private->count_area[i].dl !=
|
||||
private->count_area[0].dl))
|
||||
private->count_area[0].dl) ||
|
||||
private->count_area[i].cyl != 0 ||
|
||||
private->count_area[i].head != count_area_head[i] ||
|
||||
private->count_area[i].record != count_area_rec[i])
|
||||
break;
|
||||
}
|
||||
if (i == 5)
|
||||
|
@ -292,12 +292,12 @@ out:
|
||||
#else
|
||||
static int dasd_ioctl_reset_profile(struct dasd_block *block)
|
||||
{
|
||||
return -ENOSYS;
|
||||
return -ENOTTY;
|
||||
}
|
||||
|
||||
static int dasd_ioctl_read_profile(struct dasd_block *block, void __user *argp)
|
||||
{
|
||||
return -ENOSYS;
|
||||
return -ENOTTY;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
445
drivers/s390/block/scm_blk.c
Normal file
445
drivers/s390/block/scm_blk.c
Normal file
@ -0,0 +1,445 @@
|
||||
/*
|
||||
* Block driver for s390 storage class memory.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
* Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#define KMSG_COMPONENT "scm_block"
|
||||
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <asm/eadm.h>
|
||||
#include "scm_blk.h"
|
||||
|
||||
debug_info_t *scm_debug;
|
||||
static int scm_major;
|
||||
static DEFINE_SPINLOCK(list_lock);
|
||||
static LIST_HEAD(inactive_requests);
|
||||
static unsigned int nr_requests = 64;
|
||||
static atomic_t nr_devices = ATOMIC_INIT(0);
|
||||
module_param(nr_requests, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(nr_requests, "Number of parallel requests.");
|
||||
|
||||
MODULE_DESCRIPTION("Block driver for s390 storage class memory.");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("scm:scmdev*");
|
||||
|
||||
static void __scm_free_rq(struct scm_request *scmrq)
|
||||
{
|
||||
struct aob_rq_header *aobrq = to_aobrq(scmrq);
|
||||
|
||||
free_page((unsigned long) scmrq->aob);
|
||||
free_page((unsigned long) scmrq->aidaw);
|
||||
__scm_free_rq_cluster(scmrq);
|
||||
kfree(aobrq);
|
||||
}
|
||||
|
||||
static void scm_free_rqs(void)
|
||||
{
|
||||
struct list_head *iter, *safe;
|
||||
struct scm_request *scmrq;
|
||||
|
||||
spin_lock_irq(&list_lock);
|
||||
list_for_each_safe(iter, safe, &inactive_requests) {
|
||||
scmrq = list_entry(iter, struct scm_request, list);
|
||||
list_del(&scmrq->list);
|
||||
__scm_free_rq(scmrq);
|
||||
}
|
||||
spin_unlock_irq(&list_lock);
|
||||
}
|
||||
|
||||
static int __scm_alloc_rq(void)
|
||||
{
|
||||
struct aob_rq_header *aobrq;
|
||||
struct scm_request *scmrq;
|
||||
|
||||
aobrq = kzalloc(sizeof(*aobrq) + sizeof(*scmrq), GFP_KERNEL);
|
||||
if (!aobrq)
|
||||
return -ENOMEM;
|
||||
|
||||
scmrq = (void *) aobrq->data;
|
||||
scmrq->aidaw = (void *) get_zeroed_page(GFP_DMA);
|
||||
scmrq->aob = (void *) get_zeroed_page(GFP_DMA);
|
||||
if (!scmrq->aob || !scmrq->aidaw) {
|
||||
__scm_free_rq(scmrq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (__scm_alloc_rq_cluster(scmrq)) {
|
||||
__scm_free_rq(scmrq);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&scmrq->list);
|
||||
spin_lock_irq(&list_lock);
|
||||
list_add(&scmrq->list, &inactive_requests);
|
||||
spin_unlock_irq(&list_lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int scm_alloc_rqs(unsigned int nrqs)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
while (nrqs-- && !ret)
|
||||
ret = __scm_alloc_rq();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct scm_request *scm_request_fetch(void)
|
||||
{
|
||||
struct scm_request *scmrq = NULL;
|
||||
|
||||
spin_lock(&list_lock);
|
||||
if (list_empty(&inactive_requests))
|
||||
goto out;
|
||||
scmrq = list_first_entry(&inactive_requests, struct scm_request, list);
|
||||
list_del(&scmrq->list);
|
||||
out:
|
||||
spin_unlock(&list_lock);
|
||||
return scmrq;
|
||||
}
|
||||
|
||||
static void scm_request_done(struct scm_request *scmrq)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
list_add(&scmrq->list, &inactive_requests);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
}
|
||||
|
||||
static int scm_open(struct block_device *blkdev, fmode_t mode)
|
||||
{
|
||||
return scm_get_ref();
|
||||
}
|
||||
|
||||
static int scm_release(struct gendisk *gendisk, fmode_t mode)
|
||||
{
|
||||
scm_put_ref();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct block_device_operations scm_blk_devops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = scm_open,
|
||||
.release = scm_release,
|
||||
};
|
||||
|
||||
static void scm_request_prepare(struct scm_request *scmrq)
|
||||
{
|
||||
struct scm_blk_dev *bdev = scmrq->bdev;
|
||||
struct scm_device *scmdev = bdev->gendisk->private_data;
|
||||
struct aidaw *aidaw = scmrq->aidaw;
|
||||
struct msb *msb = &scmrq->aob->msb[0];
|
||||
struct req_iterator iter;
|
||||
struct bio_vec *bv;
|
||||
|
||||
msb->bs = MSB_BS_4K;
|
||||
scmrq->aob->request.msb_count = 1;
|
||||
msb->scm_addr = scmdev->address +
|
||||
((u64) blk_rq_pos(scmrq->request) << 9);
|
||||
msb->oc = (rq_data_dir(scmrq->request) == READ) ?
|
||||
MSB_OC_READ : MSB_OC_WRITE;
|
||||
msb->flags |= MSB_FLAG_IDA;
|
||||
msb->data_addr = (u64) aidaw;
|
||||
|
||||
rq_for_each_segment(bv, scmrq->request, iter) {
|
||||
WARN_ON(bv->bv_offset);
|
||||
msb->blk_count += bv->bv_len >> 12;
|
||||
aidaw->data_addr = (u64) page_address(bv->bv_page);
|
||||
aidaw++;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void scm_request_init(struct scm_blk_dev *bdev,
|
||||
struct scm_request *scmrq,
|
||||
struct request *req)
|
||||
{
|
||||
struct aob_rq_header *aobrq = to_aobrq(scmrq);
|
||||
struct aob *aob = scmrq->aob;
|
||||
|
||||
memset(aob, 0, sizeof(*aob));
|
||||
memset(scmrq->aidaw, 0, PAGE_SIZE);
|
||||
aobrq->scmdev = bdev->scmdev;
|
||||
aob->request.cmd_code = ARQB_CMD_MOVE;
|
||||
aob->request.data = (u64) aobrq;
|
||||
scmrq->request = req;
|
||||
scmrq->bdev = bdev;
|
||||
scmrq->retries = 4;
|
||||
scmrq->error = 0;
|
||||
scm_request_cluster_init(scmrq);
|
||||
}
|
||||
|
||||
static void scm_ensure_queue_restart(struct scm_blk_dev *bdev)
|
||||
{
|
||||
if (atomic_read(&bdev->queued_reqs)) {
|
||||
/* Queue restart is triggered by the next interrupt. */
|
||||
return;
|
||||
}
|
||||
blk_delay_queue(bdev->rq, SCM_QUEUE_DELAY);
|
||||
}
|
||||
|
||||
void scm_request_requeue(struct scm_request *scmrq)
|
||||
{
|
||||
struct scm_blk_dev *bdev = scmrq->bdev;
|
||||
|
||||
scm_release_cluster(scmrq);
|
||||
blk_requeue_request(bdev->rq, scmrq->request);
|
||||
scm_request_done(scmrq);
|
||||
scm_ensure_queue_restart(bdev);
|
||||
}
|
||||
|
||||
void scm_request_finish(struct scm_request *scmrq)
|
||||
{
|
||||
scm_release_cluster(scmrq);
|
||||
blk_end_request_all(scmrq->request, scmrq->error);
|
||||
scm_request_done(scmrq);
|
||||
}
|
||||
|
||||
static void scm_blk_request(struct request_queue *rq)
|
||||
{
|
||||
struct scm_device *scmdev = rq->queuedata;
|
||||
struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
|
||||
struct scm_request *scmrq;
|
||||
struct request *req;
|
||||
int ret;
|
||||
|
||||
while ((req = blk_peek_request(rq))) {
|
||||
if (req->cmd_type != REQ_TYPE_FS)
|
||||
continue;
|
||||
|
||||
scmrq = scm_request_fetch();
|
||||
if (!scmrq) {
|
||||
SCM_LOG(5, "no request");
|
||||
scm_ensure_queue_restart(bdev);
|
||||
return;
|
||||
}
|
||||
scm_request_init(bdev, scmrq, req);
|
||||
if (!scm_reserve_cluster(scmrq)) {
|
||||
SCM_LOG(5, "cluster busy");
|
||||
scm_request_done(scmrq);
|
||||
return;
|
||||
}
|
||||
if (scm_need_cluster_request(scmrq)) {
|
||||
blk_start_request(req);
|
||||
scm_initiate_cluster_request(scmrq);
|
||||
return;
|
||||
}
|
||||
scm_request_prepare(scmrq);
|
||||
blk_start_request(req);
|
||||
|
||||
ret = scm_start_aob(scmrq->aob);
|
||||
if (ret) {
|
||||
SCM_LOG(5, "no subchannel");
|
||||
scm_request_requeue(scmrq);
|
||||
return;
|
||||
}
|
||||
atomic_inc(&bdev->queued_reqs);
|
||||
}
|
||||
}
|
||||
|
||||
static void __scmrq_log_error(struct scm_request *scmrq)
|
||||
{
|
||||
struct aob *aob = scmrq->aob;
|
||||
|
||||
if (scmrq->error == -ETIMEDOUT)
|
||||
SCM_LOG(1, "Request timeout");
|
||||
else {
|
||||
SCM_LOG(1, "Request error");
|
||||
SCM_LOG_HEX(1, &aob->response, sizeof(aob->response));
|
||||
}
|
||||
if (scmrq->retries)
|
||||
SCM_LOG(1, "Retry request");
|
||||
else
|
||||
pr_err("An I/O operation to SCM failed with rc=%d\n",
|
||||
scmrq->error);
|
||||
}
|
||||
|
||||
void scm_blk_irq(struct scm_device *scmdev, void *data, int error)
|
||||
{
|
||||
struct scm_request *scmrq = data;
|
||||
struct scm_blk_dev *bdev = scmrq->bdev;
|
||||
|
||||
scmrq->error = error;
|
||||
if (error)
|
||||
__scmrq_log_error(scmrq);
|
||||
|
||||
spin_lock(&bdev->lock);
|
||||
list_add_tail(&scmrq->list, &bdev->finished_requests);
|
||||
spin_unlock(&bdev->lock);
|
||||
tasklet_hi_schedule(&bdev->tasklet);
|
||||
}
|
||||
|
||||
static void scm_blk_tasklet(struct scm_blk_dev *bdev)
|
||||
{
|
||||
struct scm_request *scmrq;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&bdev->lock, flags);
|
||||
while (!list_empty(&bdev->finished_requests)) {
|
||||
scmrq = list_first_entry(&bdev->finished_requests,
|
||||
struct scm_request, list);
|
||||
list_del(&scmrq->list);
|
||||
spin_unlock_irqrestore(&bdev->lock, flags);
|
||||
|
||||
if (scmrq->error && scmrq->retries-- > 0) {
|
||||
if (scm_start_aob(scmrq->aob)) {
|
||||
spin_lock_irqsave(&bdev->rq_lock, flags);
|
||||
scm_request_requeue(scmrq);
|
||||
spin_unlock_irqrestore(&bdev->rq_lock, flags);
|
||||
}
|
||||
/* Request restarted or requeued, handle next. */
|
||||
spin_lock_irqsave(&bdev->lock, flags);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (scm_test_cluster_request(scmrq)) {
|
||||
scm_cluster_request_irq(scmrq);
|
||||
spin_lock_irqsave(&bdev->lock, flags);
|
||||
continue;
|
||||
}
|
||||
|
||||
scm_request_finish(scmrq);
|
||||
atomic_dec(&bdev->queued_reqs);
|
||||
spin_lock_irqsave(&bdev->lock, flags);
|
||||
}
|
||||
spin_unlock_irqrestore(&bdev->lock, flags);
|
||||
/* Look out for more requests. */
|
||||
blk_run_queue(bdev->rq);
|
||||
}
|
||||
|
||||
int scm_blk_dev_setup(struct scm_blk_dev *bdev, struct scm_device *scmdev)
|
||||
{
|
||||
struct request_queue *rq;
|
||||
int len, ret = -ENOMEM;
|
||||
unsigned int devindex, nr_max_blk;
|
||||
|
||||
devindex = atomic_inc_return(&nr_devices) - 1;
|
||||
/* scma..scmz + scmaa..scmzz */
|
||||
if (devindex > 701) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
bdev->scmdev = scmdev;
|
||||
spin_lock_init(&bdev->rq_lock);
|
||||
spin_lock_init(&bdev->lock);
|
||||
INIT_LIST_HEAD(&bdev->finished_requests);
|
||||
atomic_set(&bdev->queued_reqs, 0);
|
||||
tasklet_init(&bdev->tasklet,
|
||||
(void (*)(unsigned long)) scm_blk_tasklet,
|
||||
(unsigned long) bdev);
|
||||
|
||||
rq = blk_init_queue(scm_blk_request, &bdev->rq_lock);
|
||||
if (!rq)
|
||||
goto out;
|
||||
|
||||
bdev->rq = rq;
|
||||
nr_max_blk = min(scmdev->nr_max_block,
|
||||
(unsigned int) (PAGE_SIZE / sizeof(struct aidaw)));
|
||||
|
||||
blk_queue_logical_block_size(rq, 1 << 12);
|
||||
blk_queue_max_hw_sectors(rq, nr_max_blk << 3); /* 8 * 512 = blk_size */
|
||||
blk_queue_max_segments(rq, nr_max_blk);
|
||||
queue_flag_set_unlocked(QUEUE_FLAG_NONROT, rq);
|
||||
scm_blk_dev_cluster_setup(bdev);
|
||||
|
||||
bdev->gendisk = alloc_disk(SCM_NR_PARTS);
|
||||
if (!bdev->gendisk)
|
||||
goto out_queue;
|
||||
|
||||
rq->queuedata = scmdev;
|
||||
bdev->gendisk->driverfs_dev = &scmdev->dev;
|
||||
bdev->gendisk->private_data = scmdev;
|
||||
bdev->gendisk->fops = &scm_blk_devops;
|
||||
bdev->gendisk->queue = rq;
|
||||
bdev->gendisk->major = scm_major;
|
||||
bdev->gendisk->first_minor = devindex * SCM_NR_PARTS;
|
||||
|
||||
len = snprintf(bdev->gendisk->disk_name, DISK_NAME_LEN, "scm");
|
||||
if (devindex > 25) {
|
||||
len += snprintf(bdev->gendisk->disk_name + len,
|
||||
DISK_NAME_LEN - len, "%c",
|
||||
'a' + (devindex / 26) - 1);
|
||||
devindex = devindex % 26;
|
||||
}
|
||||
snprintf(bdev->gendisk->disk_name + len, DISK_NAME_LEN - len, "%c",
|
||||
'a' + devindex);
|
||||
|
||||
/* 512 byte sectors */
|
||||
set_capacity(bdev->gendisk, scmdev->size >> 9);
|
||||
add_disk(bdev->gendisk);
|
||||
return 0;
|
||||
|
||||
out_queue:
|
||||
blk_cleanup_queue(rq);
|
||||
out:
|
||||
atomic_dec(&nr_devices);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void scm_blk_dev_cleanup(struct scm_blk_dev *bdev)
|
||||
{
|
||||
tasklet_kill(&bdev->tasklet);
|
||||
del_gendisk(bdev->gendisk);
|
||||
blk_cleanup_queue(bdev->gendisk->queue);
|
||||
put_disk(bdev->gendisk);
|
||||
}
|
||||
|
||||
static int __init scm_blk_init(void)
|
||||
{
|
||||
int ret = -EINVAL;
|
||||
|
||||
if (!scm_cluster_size_valid())
|
||||
goto out;
|
||||
|
||||
ret = register_blkdev(0, "scm");
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
scm_major = ret;
|
||||
if (scm_alloc_rqs(nr_requests))
|
||||
goto out_unreg;
|
||||
|
||||
scm_debug = debug_register("scm_log", 16, 1, 16);
|
||||
if (!scm_debug)
|
||||
goto out_free;
|
||||
|
||||
debug_register_view(scm_debug, &debug_hex_ascii_view);
|
||||
debug_set_level(scm_debug, 2);
|
||||
|
||||
ret = scm_drv_init();
|
||||
if (ret)
|
||||
goto out_dbf;
|
||||
|
||||
return ret;
|
||||
|
||||
out_dbf:
|
||||
debug_unregister(scm_debug);
|
||||
out_free:
|
||||
scm_free_rqs();
|
||||
out_unreg:
|
||||
unregister_blkdev(scm_major, "scm");
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
module_init(scm_blk_init);
|
||||
|
||||
static void __exit scm_blk_cleanup(void)
|
||||
{
|
||||
scm_drv_cleanup();
|
||||
debug_unregister(scm_debug);
|
||||
scm_free_rqs();
|
||||
unregister_blkdev(scm_major, "scm");
|
||||
}
|
||||
module_exit(scm_blk_cleanup);
|
117
drivers/s390/block/scm_blk.h
Normal file
117
drivers/s390/block/scm_blk.h
Normal file
@ -0,0 +1,117 @@
|
||||
#ifndef SCM_BLK_H
|
||||
#define SCM_BLK_H
|
||||
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <asm/debug.h>
|
||||
#include <asm/eadm.h>
|
||||
|
||||
#define SCM_NR_PARTS 8
|
||||
#define SCM_QUEUE_DELAY 5
|
||||
|
||||
struct scm_blk_dev {
|
||||
struct tasklet_struct tasklet;
|
||||
struct request_queue *rq;
|
||||
struct gendisk *gendisk;
|
||||
struct scm_device *scmdev;
|
||||
spinlock_t rq_lock; /* guard the request queue */
|
||||
spinlock_t lock; /* guard the rest of the blockdev */
|
||||
atomic_t queued_reqs;
|
||||
struct list_head finished_requests;
|
||||
#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
|
||||
struct list_head cluster_list;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct scm_request {
|
||||
struct scm_blk_dev *bdev;
|
||||
struct request *request;
|
||||
struct aidaw *aidaw;
|
||||
struct aob *aob;
|
||||
struct list_head list;
|
||||
u8 retries;
|
||||
int error;
|
||||
#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
|
||||
struct {
|
||||
enum {CLUSTER_NONE, CLUSTER_READ, CLUSTER_WRITE} state;
|
||||
struct list_head list;
|
||||
void **buf;
|
||||
} cluster;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define to_aobrq(rq) container_of((void *) rq, struct aob_rq_header, data)
|
||||
|
||||
int scm_blk_dev_setup(struct scm_blk_dev *, struct scm_device *);
|
||||
void scm_blk_dev_cleanup(struct scm_blk_dev *);
|
||||
void scm_blk_irq(struct scm_device *, void *, int);
|
||||
|
||||
void scm_request_finish(struct scm_request *);
|
||||
void scm_request_requeue(struct scm_request *);
|
||||
|
||||
int scm_drv_init(void);
|
||||
void scm_drv_cleanup(void);
|
||||
|
||||
#ifdef CONFIG_SCM_BLOCK_CLUSTER_WRITE
|
||||
void __scm_free_rq_cluster(struct scm_request *);
|
||||
int __scm_alloc_rq_cluster(struct scm_request *);
|
||||
void scm_request_cluster_init(struct scm_request *);
|
||||
bool scm_reserve_cluster(struct scm_request *);
|
||||
void scm_release_cluster(struct scm_request *);
|
||||
void scm_blk_dev_cluster_setup(struct scm_blk_dev *);
|
||||
bool scm_need_cluster_request(struct scm_request *);
|
||||
void scm_initiate_cluster_request(struct scm_request *);
|
||||
void scm_cluster_request_irq(struct scm_request *);
|
||||
bool scm_test_cluster_request(struct scm_request *);
|
||||
bool scm_cluster_size_valid(void);
|
||||
#else
|
||||
#define __scm_free_rq_cluster(scmrq) {}
|
||||
#define __scm_alloc_rq_cluster(scmrq) 0
|
||||
#define scm_request_cluster_init(scmrq) {}
|
||||
#define scm_reserve_cluster(scmrq) true
|
||||
#define scm_release_cluster(scmrq) {}
|
||||
#define scm_blk_dev_cluster_setup(bdev) {}
|
||||
#define scm_need_cluster_request(scmrq) false
|
||||
#define scm_initiate_cluster_request(scmrq) {}
|
||||
#define scm_cluster_request_irq(scmrq) {}
|
||||
#define scm_test_cluster_request(scmrq) false
|
||||
#define scm_cluster_size_valid() true
|
||||
#endif
|
||||
|
||||
extern debug_info_t *scm_debug;
|
||||
|
||||
#define SCM_LOG(imp, txt) do { \
|
||||
debug_text_event(scm_debug, imp, txt); \
|
||||
} while (0)
|
||||
|
||||
static inline void SCM_LOG_HEX(int level, void *data, int length)
|
||||
{
|
||||
if (level > scm_debug->level)
|
||||
return;
|
||||
while (length > 0) {
|
||||
debug_event(scm_debug, level, data, length);
|
||||
length -= scm_debug->buf_size;
|
||||
data += scm_debug->buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void SCM_LOG_STATE(int level, struct scm_device *scmdev)
|
||||
{
|
||||
struct {
|
||||
u64 address;
|
||||
u8 oper_state;
|
||||
u8 rank;
|
||||
} __packed data = {
|
||||
.address = scmdev->address,
|
||||
.oper_state = scmdev->attrs.oper_state,
|
||||
.rank = scmdev->attrs.rank,
|
||||
};
|
||||
|
||||
SCM_LOG_HEX(level, &data, sizeof(data));
|
||||
}
|
||||
|
||||
#endif /* SCM_BLK_H */
|
228
drivers/s390/block/scm_blk_cluster.c
Normal file
228
drivers/s390/block/scm_blk_cluster.c
Normal file
@ -0,0 +1,228 @@
|
||||
/*
|
||||
* Block driver for s390 storage class memory.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
* Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/genhd.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
#include <asm/eadm.h>
|
||||
#include "scm_blk.h"
|
||||
|
||||
static unsigned int write_cluster_size = 64;
|
||||
module_param(write_cluster_size, uint, S_IRUGO);
|
||||
MODULE_PARM_DESC(write_cluster_size,
|
||||
"Number of pages used for contiguous writes.");
|
||||
|
||||
#define CLUSTER_SIZE (write_cluster_size * PAGE_SIZE)
|
||||
|
||||
void __scm_free_rq_cluster(struct scm_request *scmrq)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (!scmrq->cluster.buf)
|
||||
return;
|
||||
|
||||
for (i = 0; i < 2 * write_cluster_size; i++)
|
||||
free_page((unsigned long) scmrq->cluster.buf[i]);
|
||||
|
||||
kfree(scmrq->cluster.buf);
|
||||
}
|
||||
|
||||
int __scm_alloc_rq_cluster(struct scm_request *scmrq)
|
||||
{
|
||||
int i;
|
||||
|
||||
scmrq->cluster.buf = kzalloc(sizeof(void *) * 2 * write_cluster_size,
|
||||
GFP_KERNEL);
|
||||
if (!scmrq->cluster.buf)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < 2 * write_cluster_size; i++) {
|
||||
scmrq->cluster.buf[i] = (void *) get_zeroed_page(GFP_DMA);
|
||||
if (!scmrq->cluster.buf[i])
|
||||
return -ENOMEM;
|
||||
}
|
||||
INIT_LIST_HEAD(&scmrq->cluster.list);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void scm_request_cluster_init(struct scm_request *scmrq)
|
||||
{
|
||||
scmrq->cluster.state = CLUSTER_NONE;
|
||||
}
|
||||
|
||||
static bool clusters_intersect(struct scm_request *A, struct scm_request *B)
|
||||
{
|
||||
unsigned long firstA, lastA, firstB, lastB;
|
||||
|
||||
firstA = ((u64) blk_rq_pos(A->request) << 9) / CLUSTER_SIZE;
|
||||
lastA = (((u64) blk_rq_pos(A->request) << 9) +
|
||||
blk_rq_bytes(A->request) - 1) / CLUSTER_SIZE;
|
||||
|
||||
firstB = ((u64) blk_rq_pos(B->request) << 9) / CLUSTER_SIZE;
|
||||
lastB = (((u64) blk_rq_pos(B->request) << 9) +
|
||||
blk_rq_bytes(B->request) - 1) / CLUSTER_SIZE;
|
||||
|
||||
return (firstB <= lastA && firstA <= lastB);
|
||||
}
|
||||
|
||||
bool scm_reserve_cluster(struct scm_request *scmrq)
|
||||
{
|
||||
struct scm_blk_dev *bdev = scmrq->bdev;
|
||||
struct scm_request *iter;
|
||||
|
||||
if (write_cluster_size == 0)
|
||||
return true;
|
||||
|
||||
spin_lock(&bdev->lock);
|
||||
list_for_each_entry(iter, &bdev->cluster_list, cluster.list) {
|
||||
if (clusters_intersect(scmrq, iter) &&
|
||||
(rq_data_dir(scmrq->request) == WRITE ||
|
||||
rq_data_dir(iter->request) == WRITE)) {
|
||||
spin_unlock(&bdev->lock);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
list_add(&scmrq->cluster.list, &bdev->cluster_list);
|
||||
spin_unlock(&bdev->lock);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void scm_release_cluster(struct scm_request *scmrq)
|
||||
{
|
||||
struct scm_blk_dev *bdev = scmrq->bdev;
|
||||
unsigned long flags;
|
||||
|
||||
if (write_cluster_size == 0)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&bdev->lock, flags);
|
||||
list_del(&scmrq->cluster.list);
|
||||
spin_unlock_irqrestore(&bdev->lock, flags);
|
||||
}
|
||||
|
||||
void scm_blk_dev_cluster_setup(struct scm_blk_dev *bdev)
|
||||
{
|
||||
INIT_LIST_HEAD(&bdev->cluster_list);
|
||||
blk_queue_io_opt(bdev->rq, CLUSTER_SIZE);
|
||||
}
|
||||
|
||||
static void scm_prepare_cluster_request(struct scm_request *scmrq)
|
||||
{
|
||||
struct scm_blk_dev *bdev = scmrq->bdev;
|
||||
struct scm_device *scmdev = bdev->gendisk->private_data;
|
||||
struct request *req = scmrq->request;
|
||||
struct aidaw *aidaw = scmrq->aidaw;
|
||||
struct msb *msb = &scmrq->aob->msb[0];
|
||||
struct req_iterator iter;
|
||||
struct bio_vec *bv;
|
||||
int i = 0;
|
||||
u64 addr;
|
||||
|
||||
switch (scmrq->cluster.state) {
|
||||
case CLUSTER_NONE:
|
||||
scmrq->cluster.state = CLUSTER_READ;
|
||||
/* fall through */
|
||||
case CLUSTER_READ:
|
||||
scmrq->aob->request.msb_count = 1;
|
||||
msb->bs = MSB_BS_4K;
|
||||
msb->oc = MSB_OC_READ;
|
||||
msb->flags = MSB_FLAG_IDA;
|
||||
msb->data_addr = (u64) aidaw;
|
||||
msb->blk_count = write_cluster_size;
|
||||
|
||||
addr = scmdev->address + ((u64) blk_rq_pos(req) << 9);
|
||||
msb->scm_addr = round_down(addr, CLUSTER_SIZE);
|
||||
|
||||
if (msb->scm_addr !=
|
||||
round_down(addr + (u64) blk_rq_bytes(req) - 1,
|
||||
CLUSTER_SIZE))
|
||||
msb->blk_count = 2 * write_cluster_size;
|
||||
|
||||
for (i = 0; i < msb->blk_count; i++) {
|
||||
aidaw->data_addr = (u64) scmrq->cluster.buf[i];
|
||||
aidaw++;
|
||||
}
|
||||
|
||||
break;
|
||||
case CLUSTER_WRITE:
|
||||
msb->oc = MSB_OC_WRITE;
|
||||
|
||||
for (addr = msb->scm_addr;
|
||||
addr < scmdev->address + ((u64) blk_rq_pos(req) << 9);
|
||||
addr += PAGE_SIZE) {
|
||||
aidaw->data_addr = (u64) scmrq->cluster.buf[i];
|
||||
aidaw++;
|
||||
i++;
|
||||
}
|
||||
rq_for_each_segment(bv, req, iter) {
|
||||
aidaw->data_addr = (u64) page_address(bv->bv_page);
|
||||
aidaw++;
|
||||
i++;
|
||||
}
|
||||
for (; i < msb->blk_count; i++) {
|
||||
aidaw->data_addr = (u64) scmrq->cluster.buf[i];
|
||||
aidaw++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool scm_need_cluster_request(struct scm_request *scmrq)
|
||||
{
|
||||
if (rq_data_dir(scmrq->request) == READ)
|
||||
return false;
|
||||
|
||||
return blk_rq_bytes(scmrq->request) < CLUSTER_SIZE;
|
||||
}
|
||||
|
||||
/* Called with queue lock held. */
|
||||
void scm_initiate_cluster_request(struct scm_request *scmrq)
|
||||
{
|
||||
scm_prepare_cluster_request(scmrq);
|
||||
if (scm_start_aob(scmrq->aob))
|
||||
scm_request_requeue(scmrq);
|
||||
}
|
||||
|
||||
bool scm_test_cluster_request(struct scm_request *scmrq)
|
||||
{
|
||||
return scmrq->cluster.state != CLUSTER_NONE;
|
||||
}
|
||||
|
||||
void scm_cluster_request_irq(struct scm_request *scmrq)
|
||||
{
|
||||
struct scm_blk_dev *bdev = scmrq->bdev;
|
||||
unsigned long flags;
|
||||
|
||||
switch (scmrq->cluster.state) {
|
||||
case CLUSTER_NONE:
|
||||
BUG();
|
||||
break;
|
||||
case CLUSTER_READ:
|
||||
if (scmrq->error) {
|
||||
scm_request_finish(scmrq);
|
||||
break;
|
||||
}
|
||||
scmrq->cluster.state = CLUSTER_WRITE;
|
||||
spin_lock_irqsave(&bdev->rq_lock, flags);
|
||||
scm_initiate_cluster_request(scmrq);
|
||||
spin_unlock_irqrestore(&bdev->rq_lock, flags);
|
||||
break;
|
||||
case CLUSTER_WRITE:
|
||||
scm_request_finish(scmrq);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool scm_cluster_size_valid(void)
|
||||
{
|
||||
return write_cluster_size == 0 || write_cluster_size == 32 ||
|
||||
write_cluster_size == 64 || write_cluster_size == 128;
|
||||
}
|
81
drivers/s390/block/scm_drv.c
Normal file
81
drivers/s390/block/scm_drv.c
Normal file
@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Device driver for s390 storage class memory.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
* Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#define KMSG_COMPONENT "scm_block"
|
||||
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/eadm.h>
|
||||
#include "scm_blk.h"
|
||||
|
||||
static void notify(struct scm_device *scmdev)
|
||||
{
|
||||
pr_info("%lu: The capabilities of the SCM increment changed\n",
|
||||
(unsigned long) scmdev->address);
|
||||
SCM_LOG(2, "State changed");
|
||||
SCM_LOG_STATE(2, scmdev);
|
||||
}
|
||||
|
||||
static int scm_probe(struct scm_device *scmdev)
|
||||
{
|
||||
struct scm_blk_dev *bdev;
|
||||
int ret;
|
||||
|
||||
SCM_LOG(2, "probe");
|
||||
SCM_LOG_STATE(2, scmdev);
|
||||
|
||||
if (scmdev->attrs.oper_state != OP_STATE_GOOD)
|
||||
return -EINVAL;
|
||||
|
||||
bdev = kzalloc(sizeof(*bdev), GFP_KERNEL);
|
||||
if (!bdev)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&scmdev->dev, bdev);
|
||||
ret = scm_blk_dev_setup(bdev, scmdev);
|
||||
if (ret) {
|
||||
dev_set_drvdata(&scmdev->dev, NULL);
|
||||
kfree(bdev);
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int scm_remove(struct scm_device *scmdev)
|
||||
{
|
||||
struct scm_blk_dev *bdev = dev_get_drvdata(&scmdev->dev);
|
||||
|
||||
scm_blk_dev_cleanup(bdev);
|
||||
dev_set_drvdata(&scmdev->dev, NULL);
|
||||
kfree(bdev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct scm_driver scm_drv = {
|
||||
.drv = {
|
||||
.name = "scm_block",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.notify = notify,
|
||||
.probe = scm_probe,
|
||||
.remove = scm_remove,
|
||||
.handler = scm_blk_irq,
|
||||
};
|
||||
|
||||
int __init scm_drv_init(void)
|
||||
{
|
||||
return scm_driver_register(&scm_drv);
|
||||
}
|
||||
|
||||
void scm_drv_cleanup(void)
|
||||
{
|
||||
scm_driver_unregister(&scm_drv);
|
||||
}
|
@ -35,7 +35,6 @@ static struct raw3270_fn con3270_fn;
|
||||
*/
|
||||
struct con3270 {
|
||||
struct raw3270_view view;
|
||||
spinlock_t lock;
|
||||
struct list_head freemem; /* list of free memory for strings. */
|
||||
|
||||
/* Output stuff. */
|
||||
|
@ -571,8 +571,11 @@ static int __init mon_init(void)
|
||||
if (rc)
|
||||
goto out_iucv;
|
||||
monreader_device = kzalloc(sizeof(struct device), GFP_KERNEL);
|
||||
if (!monreader_device)
|
||||
if (!monreader_device) {
|
||||
rc = -ENOMEM;
|
||||
goto out_driver;
|
||||
}
|
||||
|
||||
dev_set_name(monreader_device, "monreader-dev");
|
||||
monreader_device->bus = &iucv_bus;
|
||||
monreader_device->parent = iucv_root;
|
||||
|
@ -334,7 +334,7 @@ sclp_dispatch_evbufs(struct sccb_header *sccb)
|
||||
reg->receiver_fn(evbuf);
|
||||
spin_lock_irqsave(&sclp_lock, flags);
|
||||
} else if (reg == NULL)
|
||||
rc = -ENOSYS;
|
||||
rc = -EOPNOTSUPP;
|
||||
}
|
||||
spin_unlock_irqrestore(&sclp_lock, flags);
|
||||
return rc;
|
||||
|
@ -463,7 +463,7 @@ sclp_emit_buffer(struct sclp_buffer *buffer,
|
||||
/* Use write priority message */
|
||||
sccb->msg_buf.header.type = EVTYP_PMSGCMD;
|
||||
else
|
||||
return -ENOSYS;
|
||||
return -EOPNOTSUPP;
|
||||
buffer->request.command = SCLP_CMDW_WRITE_EVENT_DATA;
|
||||
buffer->request.status = SCLP_REQ_FILLED;
|
||||
buffer->request.callback = sclp_writedata_callback;
|
||||
|
@ -15,7 +15,6 @@
|
||||
#include <asm/ccwdev.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/idals.h>
|
||||
#include <linux/blkdev.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mtio.h>
|
||||
|
@ -100,11 +100,7 @@ struct tape_request *tape_std_read_block(struct tape_device *, size_t);
|
||||
void tape_std_read_backward(struct tape_device *device,
|
||||
struct tape_request *request);
|
||||
struct tape_request *tape_std_write_block(struct tape_device *, size_t);
|
||||
struct tape_request *tape_std_bread(struct tape_device *, struct request *);
|
||||
void tape_std_free_bread(struct tape_request *);
|
||||
void tape_std_check_locate(struct tape_device *, struct tape_request *);
|
||||
struct tape_request *tape_std_bwrite(struct request *,
|
||||
struct tape_device *, int);
|
||||
|
||||
/* Some non-mtop commands. */
|
||||
int tape_std_assign(struct tape_device *);
|
||||
|
@ -321,7 +321,7 @@ static int vmlogrdr_open (struct inode *inode, struct file *filp)
|
||||
* only allow for blocking reads to be open
|
||||
*/
|
||||
if (filp->f_flags & O_NONBLOCK)
|
||||
return -ENOSYS;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
/* Besure this device hasn't already been opened */
|
||||
spin_lock_bh(&logptr->priv_lock);
|
||||
|
@ -8,6 +8,8 @@ ccw_device-objs += device.o device_fsm.o device_ops.o
|
||||
ccw_device-objs += device_id.o device_pgid.o device_status.o
|
||||
obj-y += ccw_device.o cmf.o
|
||||
obj-$(CONFIG_CHSC_SCH) += chsc_sch.o
|
||||
obj-$(CONFIG_EADM_SCH) += eadm_sch.o
|
||||
obj-$(CONFIG_SCM_BUS) += scm.o
|
||||
obj-$(CONFIG_CCWGROUP) += ccwgroup.o
|
||||
|
||||
qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o
|
||||
|
@ -52,6 +52,11 @@ int chsc_error_from_response(int response)
|
||||
return -EINVAL;
|
||||
case 0x0004:
|
||||
return -EOPNOTSUPP;
|
||||
case 0x000b:
|
||||
return -EBUSY;
|
||||
case 0x0100:
|
||||
case 0x0102:
|
||||
return -ENOMEM;
|
||||
default:
|
||||
return -EIO;
|
||||
}
|
||||
@ -393,6 +398,20 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
|
||||
}
|
||||
}
|
||||
|
||||
static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area)
|
||||
{
|
||||
int ret;
|
||||
|
||||
CIO_CRW_EVENT(4, "chsc: scm change notification\n");
|
||||
if (sei_area->rs != 7)
|
||||
return;
|
||||
|
||||
ret = scm_update_information();
|
||||
if (ret)
|
||||
CIO_CRW_EVENT(0, "chsc: updating change notification"
|
||||
" failed (rc=%d).\n", ret);
|
||||
}
|
||||
|
||||
static void chsc_process_sei(struct chsc_sei_area *sei_area)
|
||||
{
|
||||
/* Check if we might have lost some information. */
|
||||
@ -414,6 +433,9 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
|
||||
case 8: /* channel-path-configuration notification */
|
||||
chsc_process_sei_chp_config(sei_area);
|
||||
break;
|
||||
case 12: /* scm change notification */
|
||||
chsc_process_sei_scm_change(sei_area);
|
||||
break;
|
||||
default: /* other stuff */
|
||||
CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
|
||||
sei_area->cc);
|
||||
@ -1047,3 +1069,33 @@ out:
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(chsc_siosl);
|
||||
|
||||
/**
|
||||
* chsc_scm_info() - store SCM information (SSI)
|
||||
* @scm_area: request and response block for SSI
|
||||
* @token: continuation token
|
||||
*
|
||||
* Returns 0 on success.
|
||||
*/
|
||||
int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token)
|
||||
{
|
||||
int ccode, ret;
|
||||
|
||||
memset(scm_area, 0, sizeof(*scm_area));
|
||||
scm_area->request.length = 0x0020;
|
||||
scm_area->request.code = 0x004C;
|
||||
scm_area->reqtok = token;
|
||||
|
||||
ccode = chsc(scm_area);
|
||||
if (ccode > 0) {
|
||||
ret = (ccode == 3) ? -ENODEV : -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
ret = chsc_error_from_response(scm_area->response.code);
|
||||
if (ret != 0)
|
||||
CIO_MSG_EVENT(2, "chsc: scm info failed (rc=%04x)\n",
|
||||
scm_area->response.code);
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(chsc_scm_info);
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/device.h>
|
||||
#include <asm/css_chars.h>
|
||||
#include <asm/chpid.h>
|
||||
#include <asm/chsc.h>
|
||||
#include <asm/schid.h>
|
||||
@ -118,4 +119,46 @@ int chsc_error_from_response(int response);
|
||||
|
||||
int chsc_siosl(struct subchannel_id schid);
|
||||
|
||||
/* Functions and definitions to query storage-class memory. */
|
||||
struct sale {
|
||||
u64 sa;
|
||||
u32 p:4;
|
||||
u32 op_state:4;
|
||||
u32 data_state:4;
|
||||
u32 rank:4;
|
||||
u32 r:1;
|
||||
u32:7;
|
||||
u32 rid:8;
|
||||
u32:32;
|
||||
} __packed;
|
||||
|
||||
struct chsc_scm_info {
|
||||
struct chsc_header request;
|
||||
u32:32;
|
||||
u64 reqtok;
|
||||
u32 reserved1[4];
|
||||
struct chsc_header response;
|
||||
u64:56;
|
||||
u8 rq;
|
||||
u32 mbc;
|
||||
u64 msa;
|
||||
u16 is;
|
||||
u16 mmc;
|
||||
u32 mci;
|
||||
u64 nr_scm_ini;
|
||||
u64 nr_scm_unini;
|
||||
u32 reserved2[10];
|
||||
u64 restok;
|
||||
struct sale scmal[248];
|
||||
} __packed;
|
||||
|
||||
int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
|
||||
|
||||
#ifdef CONFIG_SCM_BUS
|
||||
int scm_update_information(void);
|
||||
#else /* CONFIG_SCM_BUS */
|
||||
#define scm_update_information() 0
|
||||
#endif /* CONFIG_SCM_BUS */
|
||||
|
||||
|
||||
#endif
|
||||
|
@ -1029,7 +1029,7 @@ extern void do_reipl_asm(__u32 schid);
|
||||
/* Make sure all subchannels are quiet before we re-ipl an lpar. */
|
||||
void reipl_ccw_dev(struct ccw_dev_id *devid)
|
||||
{
|
||||
struct subchannel_id schid;
|
||||
struct subchannel_id uninitialized_var(schid);
|
||||
|
||||
s390_reset_system(NULL, NULL);
|
||||
if (reipl_find_schid(devid, &schid) != 0)
|
||||
|
@ -445,6 +445,7 @@ void css_sched_sch_todo(struct subchannel *sch, enum sch_todo todo)
|
||||
put_device(&sch->dev);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(css_sched_sch_todo);
|
||||
|
||||
static void css_sch_todo(struct work_struct *work)
|
||||
{
|
||||
|
401
drivers/s390/cio/eadm_sch.c
Normal file
401
drivers/s390/cio/eadm_sch.c
Normal file
@ -0,0 +1,401 @@
|
||||
/*
|
||||
* Driver for s390 eadm subchannels
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
* Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel_stat.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include <asm/css_chars.h>
|
||||
#include <asm/debug.h>
|
||||
#include <asm/isc.h>
|
||||
#include <asm/cio.h>
|
||||
#include <asm/scsw.h>
|
||||
#include <asm/eadm.h>
|
||||
|
||||
#include "eadm_sch.h"
|
||||
#include "ioasm.h"
|
||||
#include "cio.h"
|
||||
#include "css.h"
|
||||
#include "orb.h"
|
||||
|
||||
MODULE_DESCRIPTION("driver for s390 eadm subchannels");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
#define EADM_TIMEOUT (5 * HZ)
|
||||
static DEFINE_SPINLOCK(list_lock);
|
||||
static LIST_HEAD(eadm_list);
|
||||
|
||||
static debug_info_t *eadm_debug;
|
||||
|
||||
#define EADM_LOG(imp, txt) do { \
|
||||
debug_text_event(eadm_debug, imp, txt); \
|
||||
} while (0)
|
||||
|
||||
static void EADM_LOG_HEX(int level, void *data, int length)
|
||||
{
|
||||
if (level > eadm_debug->level)
|
||||
return;
|
||||
while (length > 0) {
|
||||
debug_event(eadm_debug, level, data, length);
|
||||
length -= eadm_debug->buf_size;
|
||||
data += eadm_debug->buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
static void orb_init(union orb *orb)
|
||||
{
|
||||
memset(orb, 0, sizeof(union orb));
|
||||
orb->eadm.compat1 = 1;
|
||||
orb->eadm.compat2 = 1;
|
||||
orb->eadm.fmt = 1;
|
||||
orb->eadm.x = 1;
|
||||
}
|
||||
|
||||
static int eadm_subchannel_start(struct subchannel *sch, struct aob *aob)
|
||||
{
|
||||
union orb *orb = &get_eadm_private(sch)->orb;
|
||||
int cc;
|
||||
|
||||
orb_init(orb);
|
||||
orb->eadm.aob = (u32)__pa(aob);
|
||||
orb->eadm.intparm = (u32)(addr_t)sch;
|
||||
orb->eadm.key = PAGE_DEFAULT_KEY >> 4;
|
||||
|
||||
EADM_LOG(6, "start");
|
||||
EADM_LOG_HEX(6, &sch->schid, sizeof(sch->schid));
|
||||
|
||||
cc = ssch(sch->schid, orb);
|
||||
switch (cc) {
|
||||
case 0:
|
||||
sch->schib.scsw.eadm.actl |= SCSW_ACTL_START_PEND;
|
||||
break;
|
||||
case 1: /* status pending */
|
||||
case 2: /* busy */
|
||||
return -EBUSY;
|
||||
case 3: /* not operational */
|
||||
return -ENODEV;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int eadm_subchannel_clear(struct subchannel *sch)
|
||||
{
|
||||
int cc;
|
||||
|
||||
cc = csch(sch->schid);
|
||||
if (cc)
|
||||
return -ENODEV;
|
||||
|
||||
sch->schib.scsw.eadm.actl |= SCSW_ACTL_CLEAR_PEND;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eadm_subchannel_timeout(unsigned long data)
|
||||
{
|
||||
struct subchannel *sch = (struct subchannel *) data;
|
||||
|
||||
spin_lock_irq(sch->lock);
|
||||
EADM_LOG(1, "timeout");
|
||||
EADM_LOG_HEX(1, &sch->schid, sizeof(sch->schid));
|
||||
if (eadm_subchannel_clear(sch))
|
||||
EADM_LOG(0, "clear failed");
|
||||
spin_unlock_irq(sch->lock);
|
||||
}
|
||||
|
||||
static void eadm_subchannel_set_timeout(struct subchannel *sch, int expires)
|
||||
{
|
||||
struct eadm_private *private = get_eadm_private(sch);
|
||||
|
||||
if (expires == 0) {
|
||||
del_timer(&private->timer);
|
||||
return;
|
||||
}
|
||||
if (timer_pending(&private->timer)) {
|
||||
if (mod_timer(&private->timer, jiffies + expires))
|
||||
return;
|
||||
}
|
||||
private->timer.function = eadm_subchannel_timeout;
|
||||
private->timer.data = (unsigned long) sch;
|
||||
private->timer.expires = jiffies + expires;
|
||||
add_timer(&private->timer);
|
||||
}
|
||||
|
||||
static void eadm_subchannel_irq(struct subchannel *sch)
|
||||
{
|
||||
struct eadm_private *private = get_eadm_private(sch);
|
||||
struct eadm_scsw *scsw = &sch->schib.scsw.eadm;
|
||||
struct irb *irb = (struct irb *)&S390_lowcore.irb;
|
||||
int error = 0;
|
||||
|
||||
EADM_LOG(6, "irq");
|
||||
EADM_LOG_HEX(6, irb, sizeof(*irb));
|
||||
|
||||
kstat_cpu(smp_processor_id()).irqs[IOINT_ADM]++;
|
||||
|
||||
if ((scsw->stctl & (SCSW_STCTL_ALERT_STATUS | SCSW_STCTL_STATUS_PEND))
|
||||
&& scsw->eswf == 1 && irb->esw.eadm.erw.r)
|
||||
error = -EIO;
|
||||
|
||||
if (scsw->fctl & SCSW_FCTL_CLEAR_FUNC)
|
||||
error = -ETIMEDOUT;
|
||||
|
||||
eadm_subchannel_set_timeout(sch, 0);
|
||||
|
||||
if (private->state != EADM_BUSY) {
|
||||
EADM_LOG(1, "irq unsol");
|
||||
EADM_LOG_HEX(1, irb, sizeof(*irb));
|
||||
private->state = EADM_NOT_OPER;
|
||||
css_sched_sch_todo(sch, SCH_TODO_EVAL);
|
||||
return;
|
||||
}
|
||||
scm_irq_handler((struct aob *)(unsigned long)scsw->aob, error);
|
||||
private->state = EADM_IDLE;
|
||||
}
|
||||
|
||||
static struct subchannel *eadm_get_idle_sch(void)
|
||||
{
|
||||
struct eadm_private *private;
|
||||
struct subchannel *sch;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&list_lock, flags);
|
||||
list_for_each_entry(private, &eadm_list, head) {
|
||||
sch = private->sch;
|
||||
spin_lock(sch->lock);
|
||||
if (private->state == EADM_IDLE) {
|
||||
private->state = EADM_BUSY;
|
||||
list_move_tail(&private->head, &eadm_list);
|
||||
spin_unlock(sch->lock);
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
return sch;
|
||||
}
|
||||
spin_unlock(sch->lock);
|
||||
}
|
||||
spin_unlock_irqrestore(&list_lock, flags);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int eadm_start_aob(struct aob *aob)
|
||||
{
|
||||
struct eadm_private *private;
|
||||
struct subchannel *sch;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
sch = eadm_get_idle_sch();
|
||||
if (!sch)
|
||||
return -EBUSY;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
eadm_subchannel_set_timeout(sch, EADM_TIMEOUT);
|
||||
ret = eadm_subchannel_start(sch, aob);
|
||||
if (!ret)
|
||||
goto out_unlock;
|
||||
|
||||
/* Handle start subchannel failure. */
|
||||
eadm_subchannel_set_timeout(sch, 0);
|
||||
private = get_eadm_private(sch);
|
||||
private->state = EADM_NOT_OPER;
|
||||
css_sched_sch_todo(sch, SCH_TODO_EVAL);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int eadm_subchannel_probe(struct subchannel *sch)
|
||||
{
|
||||
struct eadm_private *private;
|
||||
int ret;
|
||||
|
||||
private = kzalloc(sizeof(*private), GFP_KERNEL | GFP_DMA);
|
||||
if (!private)
|
||||
return -ENOMEM;
|
||||
|
||||
INIT_LIST_HEAD(&private->head);
|
||||
init_timer(&private->timer);
|
||||
|
||||
spin_lock_irq(sch->lock);
|
||||
set_eadm_private(sch, private);
|
||||
private->state = EADM_IDLE;
|
||||
private->sch = sch;
|
||||
sch->isc = EADM_SCH_ISC;
|
||||
ret = cio_enable_subchannel(sch, (u32)(unsigned long)sch);
|
||||
if (ret) {
|
||||
set_eadm_private(sch, NULL);
|
||||
spin_unlock_irq(sch->lock);
|
||||
kfree(private);
|
||||
goto out;
|
||||
}
|
||||
spin_unlock_irq(sch->lock);
|
||||
|
||||
spin_lock_irq(&list_lock);
|
||||
list_add(&private->head, &eadm_list);
|
||||
spin_unlock_irq(&list_lock);
|
||||
|
||||
if (dev_get_uevent_suppress(&sch->dev)) {
|
||||
dev_set_uevent_suppress(&sch->dev, 0);
|
||||
kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
|
||||
}
|
||||
out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void eadm_quiesce(struct subchannel *sch)
|
||||
{
|
||||
int ret;
|
||||
|
||||
do {
|
||||
spin_lock_irq(sch->lock);
|
||||
ret = cio_disable_subchannel(sch);
|
||||
spin_unlock_irq(sch->lock);
|
||||
} while (ret == -EBUSY);
|
||||
}
|
||||
|
||||
static int eadm_subchannel_remove(struct subchannel *sch)
|
||||
{
|
||||
struct eadm_private *private = get_eadm_private(sch);
|
||||
|
||||
spin_lock_irq(&list_lock);
|
||||
list_del(&private->head);
|
||||
spin_unlock_irq(&list_lock);
|
||||
|
||||
eadm_quiesce(sch);
|
||||
|
||||
spin_lock_irq(sch->lock);
|
||||
set_eadm_private(sch, NULL);
|
||||
spin_unlock_irq(sch->lock);
|
||||
|
||||
kfree(private);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void eadm_subchannel_shutdown(struct subchannel *sch)
|
||||
{
|
||||
eadm_quiesce(sch);
|
||||
}
|
||||
|
||||
static int eadm_subchannel_freeze(struct subchannel *sch)
|
||||
{
|
||||
return cio_disable_subchannel(sch);
|
||||
}
|
||||
|
||||
static int eadm_subchannel_restore(struct subchannel *sch)
|
||||
{
|
||||
return cio_enable_subchannel(sch, (u32)(unsigned long)sch);
|
||||
}
|
||||
|
||||
/**
|
||||
* eadm_subchannel_sch_event - process subchannel event
|
||||
* @sch: subchannel
|
||||
* @process: non-zero if function is called in process context
|
||||
*
|
||||
* An unspecified event occurred for this subchannel. Adjust data according
|
||||
* to the current operational state of the subchannel. Return zero when the
|
||||
* event has been handled sufficiently or -EAGAIN when this function should
|
||||
* be called again in process context.
|
||||
*/
|
||||
static int eadm_subchannel_sch_event(struct subchannel *sch, int process)
|
||||
{
|
||||
struct eadm_private *private;
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
if (!device_is_registered(&sch->dev))
|
||||
goto out_unlock;
|
||||
|
||||
if (work_pending(&sch->todo_work))
|
||||
goto out_unlock;
|
||||
|
||||
if (cio_update_schib(sch)) {
|
||||
css_sched_sch_todo(sch, SCH_TODO_UNREG);
|
||||
goto out_unlock;
|
||||
}
|
||||
private = get_eadm_private(sch);
|
||||
if (private->state == EADM_NOT_OPER)
|
||||
private->state = EADM_IDLE;
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct css_device_id eadm_subchannel_ids[] = {
|
||||
{ .match_flags = 0x1, .type = SUBCHANNEL_TYPE_ADM, },
|
||||
{ /* end of list */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(css, eadm_subchannel_ids);
|
||||
|
||||
static struct css_driver eadm_subchannel_driver = {
|
||||
.drv = {
|
||||
.name = "eadm_subchannel",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.subchannel_type = eadm_subchannel_ids,
|
||||
.irq = eadm_subchannel_irq,
|
||||
.probe = eadm_subchannel_probe,
|
||||
.remove = eadm_subchannel_remove,
|
||||
.shutdown = eadm_subchannel_shutdown,
|
||||
.sch_event = eadm_subchannel_sch_event,
|
||||
.freeze = eadm_subchannel_freeze,
|
||||
.thaw = eadm_subchannel_restore,
|
||||
.restore = eadm_subchannel_restore,
|
||||
};
|
||||
|
||||
static struct eadm_ops eadm_ops = {
|
||||
.eadm_start = eadm_start_aob,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int __init eadm_sch_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (!css_general_characteristics.eadm)
|
||||
return -ENXIO;
|
||||
|
||||
eadm_debug = debug_register("eadm_log", 16, 1, 16);
|
||||
if (!eadm_debug)
|
||||
return -ENOMEM;
|
||||
|
||||
debug_register_view(eadm_debug, &debug_hex_ascii_view);
|
||||
debug_set_level(eadm_debug, 2);
|
||||
|
||||
isc_register(EADM_SCH_ISC);
|
||||
ret = css_driver_register(&eadm_subchannel_driver);
|
||||
if (ret)
|
||||
goto cleanup;
|
||||
|
||||
register_eadm_ops(&eadm_ops);
|
||||
return ret;
|
||||
|
||||
cleanup:
|
||||
isc_unregister(EADM_SCH_ISC);
|
||||
debug_unregister(eadm_debug);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit eadm_sch_exit(void)
|
||||
{
|
||||
unregister_eadm_ops(&eadm_ops);
|
||||
css_driver_unregister(&eadm_subchannel_driver);
|
||||
isc_unregister(EADM_SCH_ISC);
|
||||
debug_unregister(eadm_debug);
|
||||
}
|
||||
module_init(eadm_sch_init);
|
||||
module_exit(eadm_sch_exit);
|
20
drivers/s390/cio/eadm_sch.h
Normal file
20
drivers/s390/cio/eadm_sch.h
Normal file
@ -0,0 +1,20 @@
|
||||
#ifndef EADM_SCH_H
|
||||
#define EADM_SCH_H
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/list.h>
|
||||
#include "orb.h"
|
||||
|
||||
struct eadm_private {
|
||||
union orb orb;
|
||||
enum {EADM_IDLE, EADM_BUSY, EADM_NOT_OPER} state;
|
||||
struct timer_list timer;
|
||||
struct list_head head;
|
||||
struct subchannel *sch;
|
||||
} __aligned(8);
|
||||
|
||||
#define get_eadm_private(n) ((struct eadm_private *)dev_get_drvdata(&n->dev))
|
||||
#define set_eadm_private(n, p) (dev_set_drvdata(&n->dev, p))
|
||||
|
||||
#endif
|
@ -59,9 +59,33 @@ struct tm_orb {
|
||||
u32:32;
|
||||
} __packed __aligned(4);
|
||||
|
||||
/*
|
||||
* eadm operation request block
|
||||
*/
|
||||
struct eadm_orb {
|
||||
u32 intparm;
|
||||
u32 key:4;
|
||||
u32:4;
|
||||
u32 compat1:1;
|
||||
u32 compat2:1;
|
||||
u32:21;
|
||||
u32 x:1;
|
||||
u32 aob;
|
||||
u32 css_prio:8;
|
||||
u32:8;
|
||||
u32 scm_prio:8;
|
||||
u32:8;
|
||||
u32:29;
|
||||
u32 fmt:3;
|
||||
u32:32;
|
||||
u32:32;
|
||||
u32:32;
|
||||
} __packed __aligned(4);
|
||||
|
||||
union orb {
|
||||
struct cmd_orb cmd;
|
||||
struct tm_orb tm;
|
||||
struct eadm_orb eadm;
|
||||
} __packed __aligned(4);
|
||||
|
||||
#endif /* S390_ORB_H */
|
||||
|
@ -37,10 +37,14 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
|
||||
debug_text_event(qdio_dbf_setup, DBF_ERR, debug_buffer); \
|
||||
} while (0)
|
||||
|
||||
#define DBF_HEX(addr, len) \
|
||||
do { \
|
||||
debug_event(qdio_dbf_setup, DBF_ERR, (void*)(addr), len); \
|
||||
} while (0)
|
||||
static inline void DBF_HEX(void *addr, int len)
|
||||
{
|
||||
while (len > 0) {
|
||||
debug_event(qdio_dbf_setup, DBF_ERR, addr, len);
|
||||
len -= qdio_dbf_setup->buf_size;
|
||||
addr += qdio_dbf_setup->buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
#define DBF_ERROR(text...) \
|
||||
do { \
|
||||
@ -49,11 +53,14 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
|
||||
debug_text_event(qdio_dbf_error, DBF_ERR, debug_buffer); \
|
||||
} while (0)
|
||||
|
||||
#define DBF_ERROR_HEX(addr, len) \
|
||||
do { \
|
||||
debug_event(qdio_dbf_error, DBF_ERR, (void*)(addr), len); \
|
||||
} while (0)
|
||||
|
||||
static inline void DBF_ERROR_HEX(void *addr, int len)
|
||||
{
|
||||
while (len > 0) {
|
||||
debug_event(qdio_dbf_error, DBF_ERR, addr, len);
|
||||
len -= qdio_dbf_error->buf_size;
|
||||
addr += qdio_dbf_error->buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
#define DBF_DEV_EVENT(level, device, text...) \
|
||||
do { \
|
||||
@ -64,10 +71,15 @@ static inline int qdio_dbf_passes(debug_info_t *dbf_grp, int level)
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DBF_DEV_HEX(level, device, addr, len) \
|
||||
do { \
|
||||
debug_event(device->debug_area, level, (void*)(addr), len); \
|
||||
} while (0)
|
||||
static inline void DBF_DEV_HEX(struct qdio_irq *dev, void *addr,
|
||||
int len, int level)
|
||||
{
|
||||
while (len > 0) {
|
||||
debug_event(dev->debug_area, level, addr, len);
|
||||
len -= dev->debug_area->buf_size;
|
||||
addr += dev->debug_area->buf_size;
|
||||
}
|
||||
}
|
||||
|
||||
void qdio_allocate_dbf(struct qdio_initialize *init_data,
|
||||
struct qdio_irq *irq_ptr);
|
||||
|
317
drivers/s390/cio/scm.c
Normal file
317
drivers/s390/cio/scm.c
Normal file
@ -0,0 +1,317 @@
|
||||
/*
|
||||
* Recognize and maintain s390 storage class memory.
|
||||
*
|
||||
* Copyright IBM Corp. 2012
|
||||
* Author(s): Sebastian Ott <sebott@linux.vnet.ibm.com>
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <asm/eadm.h>
|
||||
#include "chsc.h"
|
||||
|
||||
static struct device *scm_root;
|
||||
static struct eadm_ops *eadm_ops;
|
||||
static DEFINE_MUTEX(eadm_ops_mutex);
|
||||
|
||||
#define to_scm_dev(n) container_of(n, struct scm_device, dev)
|
||||
#define to_scm_drv(d) container_of(d, struct scm_driver, drv)
|
||||
|
||||
static int scmdev_probe(struct device *dev)
|
||||
{
|
||||
struct scm_device *scmdev = to_scm_dev(dev);
|
||||
struct scm_driver *scmdrv = to_scm_drv(dev->driver);
|
||||
|
||||
return scmdrv->probe ? scmdrv->probe(scmdev) : -ENODEV;
|
||||
}
|
||||
|
||||
static int scmdev_remove(struct device *dev)
|
||||
{
|
||||
struct scm_device *scmdev = to_scm_dev(dev);
|
||||
struct scm_driver *scmdrv = to_scm_drv(dev->driver);
|
||||
|
||||
return scmdrv->remove ? scmdrv->remove(scmdev) : -ENODEV;
|
||||
}
|
||||
|
||||
static int scmdev_uevent(struct device *dev, struct kobj_uevent_env *env)
|
||||
{
|
||||
return add_uevent_var(env, "MODALIAS=scm:scmdev");
|
||||
}
|
||||
|
||||
static struct bus_type scm_bus_type = {
|
||||
.name = "scm",
|
||||
.probe = scmdev_probe,
|
||||
.remove = scmdev_remove,
|
||||
.uevent = scmdev_uevent,
|
||||
};
|
||||
|
||||
/**
|
||||
* scm_driver_register() - register a scm driver
|
||||
* @scmdrv: driver to be registered
|
||||
*/
|
||||
int scm_driver_register(struct scm_driver *scmdrv)
|
||||
{
|
||||
struct device_driver *drv = &scmdrv->drv;
|
||||
|
||||
drv->bus = &scm_bus_type;
|
||||
|
||||
return driver_register(drv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scm_driver_register);
|
||||
|
||||
/**
|
||||
* scm_driver_unregister() - deregister a scm driver
|
||||
* @scmdrv: driver to be deregistered
|
||||
*/
|
||||
void scm_driver_unregister(struct scm_driver *scmdrv)
|
||||
{
|
||||
driver_unregister(&scmdrv->drv);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scm_driver_unregister);
|
||||
|
||||
int scm_get_ref(void)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
mutex_lock(&eadm_ops_mutex);
|
||||
if (!eadm_ops || !try_module_get(eadm_ops->owner))
|
||||
ret = -ENOENT;
|
||||
mutex_unlock(&eadm_ops_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scm_get_ref);
|
||||
|
||||
void scm_put_ref(void)
|
||||
{
|
||||
mutex_lock(&eadm_ops_mutex);
|
||||
module_put(eadm_ops->owner);
|
||||
mutex_unlock(&eadm_ops_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scm_put_ref);
|
||||
|
||||
void register_eadm_ops(struct eadm_ops *ops)
|
||||
{
|
||||
mutex_lock(&eadm_ops_mutex);
|
||||
eadm_ops = ops;
|
||||
mutex_unlock(&eadm_ops_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(register_eadm_ops);
|
||||
|
||||
void unregister_eadm_ops(struct eadm_ops *ops)
|
||||
{
|
||||
mutex_lock(&eadm_ops_mutex);
|
||||
eadm_ops = NULL;
|
||||
mutex_unlock(&eadm_ops_mutex);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(unregister_eadm_ops);
|
||||
|
||||
int scm_start_aob(struct aob *aob)
|
||||
{
|
||||
return eadm_ops->eadm_start(aob);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scm_start_aob);
|
||||
|
||||
void scm_irq_handler(struct aob *aob, int error)
|
||||
{
|
||||
struct aob_rq_header *aobrq = (void *) aob->request.data;
|
||||
struct scm_device *scmdev = aobrq->scmdev;
|
||||
struct scm_driver *scmdrv = to_scm_drv(scmdev->dev.driver);
|
||||
|
||||
scmdrv->handler(scmdev, aobrq->data, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(scm_irq_handler);
|
||||
|
||||
#define scm_attr(name) \
|
||||
static ssize_t show_##name(struct device *dev, \
|
||||
struct device_attribute *attr, char *buf) \
|
||||
{ \
|
||||
struct scm_device *scmdev = to_scm_dev(dev); \
|
||||
int ret; \
|
||||
\
|
||||
device_lock(dev); \
|
||||
ret = sprintf(buf, "%u\n", scmdev->attrs.name); \
|
||||
device_unlock(dev); \
|
||||
\
|
||||
return ret; \
|
||||
} \
|
||||
static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);
|
||||
|
||||
scm_attr(persistence);
|
||||
scm_attr(oper_state);
|
||||
scm_attr(data_state);
|
||||
scm_attr(rank);
|
||||
scm_attr(release);
|
||||
scm_attr(res_id);
|
||||
|
||||
static struct attribute *scmdev_attrs[] = {
|
||||
&dev_attr_persistence.attr,
|
||||
&dev_attr_oper_state.attr,
|
||||
&dev_attr_data_state.attr,
|
||||
&dev_attr_rank.attr,
|
||||
&dev_attr_release.attr,
|
||||
&dev_attr_res_id.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group scmdev_attr_group = {
|
||||
.attrs = scmdev_attrs,
|
||||
};
|
||||
|
||||
static const struct attribute_group *scmdev_attr_groups[] = {
|
||||
&scmdev_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void scmdev_release(struct device *dev)
|
||||
{
|
||||
struct scm_device *scmdev = to_scm_dev(dev);
|
||||
|
||||
kfree(scmdev);
|
||||
}
|
||||
|
||||
static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
|
||||
unsigned int size, unsigned int max_blk_count)
|
||||
{
|
||||
dev_set_name(&scmdev->dev, "%016llx", (unsigned long long) sale->sa);
|
||||
scmdev->nr_max_block = max_blk_count;
|
||||
scmdev->address = sale->sa;
|
||||
scmdev->size = 1UL << size;
|
||||
scmdev->attrs.rank = sale->rank;
|
||||
scmdev->attrs.persistence = sale->p;
|
||||
scmdev->attrs.oper_state = sale->op_state;
|
||||
scmdev->attrs.data_state = sale->data_state;
|
||||
scmdev->attrs.rank = sale->rank;
|
||||
scmdev->attrs.release = sale->r;
|
||||
scmdev->attrs.res_id = sale->rid;
|
||||
scmdev->dev.parent = scm_root;
|
||||
scmdev->dev.bus = &scm_bus_type;
|
||||
scmdev->dev.release = scmdev_release;
|
||||
scmdev->dev.groups = scmdev_attr_groups;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check for state-changes, notify the driver and userspace.
|
||||
*/
|
||||
static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
|
||||
{
|
||||
struct scm_driver *scmdrv;
|
||||
bool changed;
|
||||
|
||||
device_lock(&scmdev->dev);
|
||||
changed = scmdev->attrs.rank != sale->rank ||
|
||||
scmdev->attrs.oper_state != sale->op_state;
|
||||
scmdev->attrs.rank = sale->rank;
|
||||
scmdev->attrs.oper_state = sale->op_state;
|
||||
if (!scmdev->dev.driver)
|
||||
goto out;
|
||||
scmdrv = to_scm_drv(scmdev->dev.driver);
|
||||
if (changed && scmdrv->notify)
|
||||
scmdrv->notify(scmdev);
|
||||
out:
|
||||
device_unlock(&scmdev->dev);
|
||||
if (changed)
|
||||
kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
|
||||
}
|
||||
|
||||
static int check_address(struct device *dev, void *data)
|
||||
{
|
||||
struct scm_device *scmdev = to_scm_dev(dev);
|
||||
struct sale *sale = data;
|
||||
|
||||
return scmdev->address == sale->sa;
|
||||
}
|
||||
|
||||
static struct scm_device *scmdev_find(struct sale *sale)
|
||||
{
|
||||
struct device *dev;
|
||||
|
||||
dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
|
||||
|
||||
return dev ? to_scm_dev(dev) : NULL;
|
||||
}
|
||||
|
||||
static int scm_add(struct chsc_scm_info *scm_info, size_t num)
|
||||
{
|
||||
struct sale *sale, *scmal = scm_info->scmal;
|
||||
struct scm_device *scmdev;
|
||||
int ret;
|
||||
|
||||
for (sale = scmal; sale < scmal + num; sale++) {
|
||||
scmdev = scmdev_find(sale);
|
||||
if (scmdev) {
|
||||
scmdev_update(scmdev, sale);
|
||||
/* Release reference from scm_find(). */
|
||||
put_device(&scmdev->dev);
|
||||
continue;
|
||||
}
|
||||
scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
|
||||
if (!scmdev)
|
||||
return -ENODEV;
|
||||
scmdev_setup(scmdev, sale, scm_info->is, scm_info->mbc);
|
||||
ret = device_register(&scmdev->dev);
|
||||
if (ret) {
|
||||
/* Release reference from device_initialize(). */
|
||||
put_device(&scmdev->dev);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int scm_update_information(void)
|
||||
{
|
||||
struct chsc_scm_info *scm_info;
|
||||
u64 token = 0;
|
||||
size_t num;
|
||||
int ret;
|
||||
|
||||
scm_info = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
|
||||
if (!scm_info)
|
||||
return -ENOMEM;
|
||||
|
||||
do {
|
||||
ret = chsc_scm_info(scm_info, token);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
num = (scm_info->response.length -
|
||||
(offsetof(struct chsc_scm_info, scmal) -
|
||||
offsetof(struct chsc_scm_info, response))
|
||||
) / sizeof(struct sale);
|
||||
|
||||
ret = scm_add(scm_info, num);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
token = scm_info->restok;
|
||||
} while (token);
|
||||
|
||||
free_page((unsigned long)scm_info);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init scm_init(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = bus_register(&scm_bus_type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
scm_root = root_device_register("scm");
|
||||
if (IS_ERR(scm_root)) {
|
||||
bus_unregister(&scm_bus_type);
|
||||
return PTR_ERR(scm_root);
|
||||
}
|
||||
|
||||
scm_update_information();
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall_sync(scm_init);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user