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:
Linus Torvalds 2012-10-01 11:49:56 -07:00
commit 6c09931b3f
119 changed files with 7420 additions and 2190 deletions

View File

@ -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/

View File

@ -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"

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)));

View File

@ -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,

View File

@ -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)

View 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

View 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 */

View File

@ -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

View File

@ -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"

View File

@ -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,

View File

@ -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 */

View File

@ -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 */

View File

@ -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) );

View File

@ -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__ */

View File

@ -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 */

View File

@ -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

View 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 */

View File

@ -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

View File

@ -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)

View File

@ -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) { }

View File

@ -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)

View File

@ -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); \

View File

@ -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.

View File

@ -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

View File

@ -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 *);

View File

@ -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

View File

@ -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/

View File

@ -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
View 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);

View File

@ -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

View File

@ -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)
{
}

View File

@ -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)))

View File

@ -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;
}
}

View File

@ -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

View File

@ -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"

View File

@ -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"},
};
/*

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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
*/

View File

@ -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

View File

@ -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)) {

View File

@ -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,

View 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);

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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)

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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).

View File

@ -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:

View File

@ -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
View 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
View 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)

View File

@ -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);

View File

@ -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
View 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 */

View File

@ -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;

View File

@ -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

View File

@ -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);
}

View File

@ -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 */

View File

@ -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
View 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
View 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

View 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);
}

View File

@ -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], &sect);
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, &sect);
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, &sect);
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, &sect);
}
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, &sect);
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),
&sect);
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), &sect);
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), &sect);
}
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:

View File

@ -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.

View File

@ -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

View File

@ -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)

View File

@ -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

View 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);

View 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 */

View 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;
}

View 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);
}

View File

@ -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. */

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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 *);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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)

View File

@ -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
View 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);

View 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

View File

@ -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 */

View File

@ -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
View 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