Merge tag 's390-5.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux

Pull s390 updates from Vasily Gorbik:

 - Convert to using the generic entry infrastructure.

 - Add vdso time namespace support.

 - Switch s390 and alpha to 64-bit ino_t. As discussed at

     https://lore.kernel.org/linux-mm/YCV7QiyoweJwvN+m@osiris/

 - Get rid of expensive stck (store clock) usages where possible.
   Utilize cpu alternatives to patch stckf when supported.

 - Make tod_clock usage less error prone by converting it to a union and
   rework code which is using it.

 - Machine check handler fixes and cleanups.

 - Drop couple of minor inline asm optimizations to fix clang build.

 - Default configs changes notably to make libvirt happy.

 - Various changes to rework and improve qdio code.

 - Other small various fixes and improvements all over the code.

* tag 's390-5.12-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (68 commits)
  s390/qdio: remove 'merge_pending' mechanism
  s390/qdio: improve handling of PENDING buffers for QEBSM devices
  s390/qdio: rework q->qdio_error indication
  s390/qdio: inline qdio_kick_handler()
  s390/time: remove get_tod_clock_ext()
  s390/crypto: use store_tod_clock_ext()
  s390/hypfs: use store_tod_clock_ext()
  s390/debug: use union tod_clock
  s390/kvm: use union tod_clock
  s390/vdso: use union tod_clock
  s390/time: convert tod_clock_base to union
  s390/time: introduce new store_tod_clock_ext()
  s390/time: rename store_tod_clock_ext() and use union tod_clock
  s390/time: introduce union tod_clock
  s390,alpha: switch to 64-bit ino_t
  s390: split cleanup_sie
  s390: use r13 in cleanup_sie as temp register
  s390: fix kernel asce loading when sie is interrupted
  s390: add stack for machine check handler
  s390: use WRITE_ONCE when re-allocating async stack
  ...
This commit is contained in:
Linus Torvalds
2021-02-21 13:40:06 -08:00
86 changed files with 1364 additions and 1672 deletions

View File

@@ -94,7 +94,7 @@ parameters may be changed at runtime by the command
(/proc/sys/dev/scsi/logging_level). (/proc/sys/dev/scsi/logging_level).
There is also a nice 'scsi_logging_level' script in the There is also a nice 'scsi_logging_level' script in the
S390-tools package, available for download at S390-tools package, available for download at
https://github.com/ibm-s390-tools/s390-tools/blob/master/scripts/scsi_logging_level https://github.com/ibm-s390-linux/s390-tools/blob/master/scripts/scsi_logging_level
scsi_mod.scan= [SCSI] sync (default) scans SCSI busses as they are scsi_mod.scan= [SCSI] sync (default) scans SCSI busses as they are
discovered. async scans them in kernel threads, discovered. async scans them in kernel threads,

View File

@@ -80,5 +80,5 @@ Keys
---- ----
Every CEC will have a unique public key to enable tooling to build Every CEC will have a unique public key to enable tooling to build
encrypted images. encrypted images.
See `s390-tools <https://github.com/ibm-s390-tools/s390-tools/>`_ See `s390-tools <https://github.com/ibm-s390-linux/s390-tools/>`_
for the tooling. for the tooling.

View File

@@ -295,6 +295,10 @@ config ARCH_32BIT_OFF_T
still support 32-bit off_t. This option is enabled for all such still support 32-bit off_t. This option is enabled for all such
architectures explicitly. architectures explicitly.
# Selected by 64 bit architectures which have a 32 bit f_tinode in struct ustat
config ARCH_32BIT_USTAT_F_TINODE
bool
config HAVE_ASM_MODVERSIONS config HAVE_ASM_MODVERSIONS
bool bool
help help

View File

@@ -2,6 +2,7 @@
config ALPHA config ALPHA
bool bool
default y default y
select ARCH_32BIT_USTAT_F_TINODE
select ARCH_MIGHT_HAVE_PC_PARPORT select ARCH_MIGHT_HAVE_PC_PARPORT
select ARCH_MIGHT_HAVE_PC_SERIO select ARCH_MIGHT_HAVE_PC_SERIO
select ARCH_NO_PREEMPT select ARCH_NO_PREEMPT

View File

@@ -58,6 +58,7 @@ config S390
# Note: keep this list sorted alphabetically # Note: keep this list sorted alphabetically
# #
imply IMA_SECURE_AND_OR_TRUSTED_BOOT imply IMA_SECURE_AND_OR_TRUSTED_BOOT
select ARCH_32BIT_USTAT_F_TINODE
select ARCH_BINFMT_ELF_STATE select ARCH_BINFMT_ELF_STATE
select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DEBUG_VM_PGTABLE
select ARCH_HAS_DEBUG_WX select ARCH_HAS_DEBUG_WX
@@ -123,11 +124,13 @@ config S390
select GENERIC_ALLOCATOR select GENERIC_ALLOCATOR
select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_AUTOPROBE
select GENERIC_CPU_VULNERABILITIES select GENERIC_CPU_VULNERABILITIES
select GENERIC_ENTRY
select GENERIC_FIND_FIRST_BIT select GENERIC_FIND_FIRST_BIT
select GENERIC_GETTIMEOFDAY select GENERIC_GETTIMEOFDAY
select GENERIC_PTDUMP select GENERIC_PTDUMP
select GENERIC_SMP_IDLE_THREAD select GENERIC_SMP_IDLE_THREAD
select GENERIC_TIME_VSYSCALL select GENERIC_TIME_VSYSCALL
select GENERIC_VDSO_TIME_NS
select HAVE_ALIGNED_STRUCT_PAGE if SLUB select HAVE_ALIGNED_STRUCT_PAGE if SLUB
select HAVE_ARCH_AUDITSYSCALL select HAVE_ARCH_AUDITSYSCALL
select HAVE_ARCH_JUMP_LABEL select HAVE_ARCH_JUMP_LABEL

View File

@@ -6,10 +6,12 @@ config TRACE_IRQFLAGS_SUPPORT
config EARLY_PRINTK config EARLY_PRINTK
def_bool y def_bool y
config DEBUG_USER_ASCE config DEBUG_ENTRY
bool "Debug User ASCE" bool "Debug low-level entry code"
depends on DEBUG_KERNEL
help help
Check on exit to user space that address space control This option enables sanity checks in s390 low-level entry code.
elements are setup correctly. Some of these sanity checks may slow down kernel entries and
exits or otherwise impact performance.
If unsure, say N. If unsure, say N.

View File

@@ -40,6 +40,7 @@ CONFIG_USERFAULTFD=y
# CONFIG_COMPAT_BRK is not set # CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y CONFIG_PROFILING=y
CONFIG_LIVEPATCH=y CONFIG_LIVEPATCH=y
CONFIG_MARCH_ZEC12=y
CONFIG_TUNE_ZEC12=y CONFIG_TUNE_ZEC12=y
CONFIG_NR_CPUS=512 CONFIG_NR_CPUS=512
CONFIG_NUMA=y CONFIG_NUMA=y
@@ -176,13 +177,17 @@ CONFIG_NF_CONNTRACK_TFTP=m
CONFIG_NF_CT_NETLINK=m CONFIG_NF_CT_NETLINK=m
CONFIG_NF_CT_NETLINK_TIMEOUT=m CONFIG_NF_CT_NETLINK_TIMEOUT=m
CONFIG_NF_TABLES=m CONFIG_NF_TABLES=m
CONFIG_NF_TABLES_INET=y
CONFIG_NFT_CT=m CONFIG_NFT_CT=m
CONFIG_NFT_COUNTER=m CONFIG_NFT_COUNTER=m
CONFIG_NFT_LOG=m CONFIG_NFT_LOG=m
CONFIG_NFT_LIMIT=m CONFIG_NFT_LIMIT=m
CONFIG_NFT_NAT=m CONFIG_NFT_NAT=m
CONFIG_NFT_OBJREF=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m CONFIG_NFT_HASH=m
CONFIG_NFT_FIB_INET=m
CONFIG_NETFILTER_XT_SET=m CONFIG_NETFILTER_XT_SET=m
CONFIG_NETFILTER_XT_TARGET_AUDIT=m CONFIG_NETFILTER_XT_TARGET_AUDIT=m
CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
@@ -274,6 +279,7 @@ CONFIG_IP_VS_NQ=m
CONFIG_IP_VS_FTP=m CONFIG_IP_VS_FTP=m
CONFIG_IP_VS_PE_SIP=m CONFIG_IP_VS_PE_SIP=m
CONFIG_NF_TABLES_IPV4=y CONFIG_NF_TABLES_IPV4=y
CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=y CONFIG_NF_TABLES_ARP=y
CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_IPTABLES=m
CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_AH=m
@@ -294,6 +300,7 @@ CONFIG_IP_NF_ARPTABLES=m
CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_TABLES_IPV6=y CONFIG_NF_TABLES_IPV6=y
CONFIG_NFT_FIB_IPV6=m
CONFIG_IP6_NF_IPTABLES=m CONFIG_IP6_NF_IPTABLES=m
CONFIG_IP6_NF_MATCH_AH=m CONFIG_IP6_NF_MATCH_AH=m
CONFIG_IP6_NF_MATCH_EUI64=m CONFIG_IP6_NF_MATCH_EUI64=m
@@ -629,7 +636,6 @@ CONFIG_NTFS_RW=y
CONFIG_PROC_KCORE=y CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y CONFIG_TMPFS_POSIX_ACL=y
CONFIG_TMPFS_INODE64=y
CONFIG_HUGETLBFS=y CONFIG_HUGETLBFS=y
CONFIG_CONFIGFS_FS=m CONFIG_CONFIGFS_FS=m
CONFIG_ECRYPT_FS=m CONFIG_ECRYPT_FS=m
@@ -791,6 +797,8 @@ CONFIG_DEBUG_OBJECTS_RCU_HEAD=y
CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y CONFIG_DEBUG_OBJECTS_PERCPU_COUNTER=y
CONFIG_SLUB_DEBUG_ON=y CONFIG_SLUB_DEBUG_ON=y
CONFIG_SLUB_STATS=y CONFIG_SLUB_STATS=y
CONFIG_DEBUG_KMEMLEAK=y
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF=y
CONFIG_DEBUG_STACK_USAGE=y CONFIG_DEBUG_STACK_USAGE=y
CONFIG_DEBUG_VM=y CONFIG_DEBUG_VM=y
CONFIG_DEBUG_VM_VMACACHE=y CONFIG_DEBUG_VM_VMACACHE=y
@@ -831,7 +839,6 @@ CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_HIST_TRIGGERS=y CONFIG_HIST_TRIGGERS=y
CONFIG_FTRACE_STARTUP_TEST=y CONFIG_FTRACE_STARTUP_TEST=y
# CONFIG_EVENT_TRACE_STARTUP_TEST is not set # CONFIG_EVENT_TRACE_STARTUP_TEST is not set
CONFIG_DEBUG_USER_ASCE=y
CONFIG_NOTIFIER_ERROR_INJECTION=m CONFIG_NOTIFIER_ERROR_INJECTION=m
CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m CONFIG_NETDEV_NOTIFIER_ERROR_INJECT=m
CONFIG_FAULT_INJECTION=y CONFIG_FAULT_INJECTION=y
@@ -855,3 +862,4 @@ CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y CONFIG_ATOMIC64_SELFTEST=y
CONFIG_TEST_BITOPS=m CONFIG_TEST_BITOPS=m
CONFIG_TEST_BPF=m CONFIG_TEST_BPF=m
CONFIG_DEBUG_ENTRY=y

View File

@@ -38,6 +38,7 @@ CONFIG_USERFAULTFD=y
# CONFIG_COMPAT_BRK is not set # CONFIG_COMPAT_BRK is not set
CONFIG_PROFILING=y CONFIG_PROFILING=y
CONFIG_LIVEPATCH=y CONFIG_LIVEPATCH=y
CONFIG_MARCH_ZEC12=y
CONFIG_TUNE_ZEC12=y CONFIG_TUNE_ZEC12=y
CONFIG_NR_CPUS=512 CONFIG_NR_CPUS=512
CONFIG_NUMA=y CONFIG_NUMA=y
@@ -167,13 +168,17 @@ CONFIG_NF_CONNTRACK_TFTP=m
CONFIG_NF_CT_NETLINK=m CONFIG_NF_CT_NETLINK=m
CONFIG_NF_CT_NETLINK_TIMEOUT=m CONFIG_NF_CT_NETLINK_TIMEOUT=m
CONFIG_NF_TABLES=m CONFIG_NF_TABLES=m
CONFIG_NF_TABLES_INET=y
CONFIG_NFT_CT=m CONFIG_NFT_CT=m
CONFIG_NFT_COUNTER=m CONFIG_NFT_COUNTER=m
CONFIG_NFT_LOG=m CONFIG_NFT_LOG=m
CONFIG_NFT_LIMIT=m CONFIG_NFT_LIMIT=m
CONFIG_NFT_NAT=m CONFIG_NFT_NAT=m
CONFIG_NFT_OBJREF=m
CONFIG_NFT_REJECT=m
CONFIG_NFT_COMPAT=m CONFIG_NFT_COMPAT=m
CONFIG_NFT_HASH=m CONFIG_NFT_HASH=m
CONFIG_NFT_FIB_INET=m
CONFIG_NETFILTER_XT_SET=m CONFIG_NETFILTER_XT_SET=m
CONFIG_NETFILTER_XT_TARGET_AUDIT=m CONFIG_NETFILTER_XT_TARGET_AUDIT=m
CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
@@ -265,6 +270,7 @@ CONFIG_IP_VS_NQ=m
CONFIG_IP_VS_FTP=m CONFIG_IP_VS_FTP=m
CONFIG_IP_VS_PE_SIP=m CONFIG_IP_VS_PE_SIP=m
CONFIG_NF_TABLES_IPV4=y CONFIG_NF_TABLES_IPV4=y
CONFIG_NFT_FIB_IPV4=m
CONFIG_NF_TABLES_ARP=y CONFIG_NF_TABLES_ARP=y
CONFIG_IP_NF_IPTABLES=m CONFIG_IP_NF_IPTABLES=m
CONFIG_IP_NF_MATCH_AH=m CONFIG_IP_NF_MATCH_AH=m
@@ -285,6 +291,7 @@ CONFIG_IP_NF_ARPTABLES=m
CONFIG_IP_NF_ARPFILTER=m CONFIG_IP_NF_ARPFILTER=m
CONFIG_IP_NF_ARP_MANGLE=m CONFIG_IP_NF_ARP_MANGLE=m
CONFIG_NF_TABLES_IPV6=y CONFIG_NF_TABLES_IPV6=y
CONFIG_NFT_FIB_IPV6=m
CONFIG_IP6_NF_IPTABLES=m CONFIG_IP6_NF_IPTABLES=m
CONFIG_IP6_NF_MATCH_AH=m CONFIG_IP6_NF_MATCH_AH=m
CONFIG_IP6_NF_MATCH_EUI64=m CONFIG_IP6_NF_MATCH_EUI64=m
@@ -617,7 +624,6 @@ CONFIG_NTFS_RW=y
CONFIG_PROC_KCORE=y CONFIG_PROC_KCORE=y
CONFIG_TMPFS=y CONFIG_TMPFS=y
CONFIG_TMPFS_POSIX_ACL=y CONFIG_TMPFS_POSIX_ACL=y
CONFIG_TMPFS_INODE64=y
CONFIG_HUGETLBFS=y CONFIG_HUGETLBFS=y
CONFIG_CONFIGFS_FS=m CONFIG_CONFIGFS_FS=m
CONFIG_ECRYPT_FS=m CONFIG_ECRYPT_FS=m
@@ -779,7 +785,6 @@ CONFIG_FTRACE_SYSCALLS=y
CONFIG_BLK_DEV_IO_TRACE=y CONFIG_BLK_DEV_IO_TRACE=y
CONFIG_BPF_KPROBE_OVERRIDE=y CONFIG_BPF_KPROBE_OVERRIDE=y
CONFIG_HIST_TRIGGERS=y CONFIG_HIST_TRIGGERS=y
CONFIG_DEBUG_USER_ASCE=y
CONFIG_LKDTM=m CONFIG_LKDTM=m
CONFIG_PERCPU_TEST=m CONFIG_PERCPU_TEST=m
CONFIG_ATOMIC64_SELFTEST=y CONFIG_ATOMIC64_SELFTEST=y

View File

@@ -3,11 +3,13 @@ CONFIG_NO_HZ_IDLE=y
CONFIG_HIGH_RES_TIMERS=y CONFIG_HIGH_RES_TIMERS=y
# CONFIG_CPU_ISOLATION is not set # CONFIG_CPU_ISOLATION is not set
# CONFIG_UTS_NS is not set # CONFIG_UTS_NS is not set
# CONFIG_TIME_NS is not set
# CONFIG_PID_NS is not set # CONFIG_PID_NS is not set
# CONFIG_NET_NS is not set # CONFIG_NET_NS is not set
CONFIG_BLK_DEV_INITRD=y CONFIG_BLK_DEV_INITRD=y
CONFIG_CC_OPTIMIZE_FOR_SIZE=y CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_COMPAT_BRK is not set # CONFIG_COMPAT_BRK is not set
CONFIG_MARCH_ZEC12=y
CONFIG_TUNE_ZEC12=y CONFIG_TUNE_ZEC12=y
# CONFIG_COMPAT is not set # CONFIG_COMPAT is not set
CONFIG_NR_CPUS=2 CONFIG_NR_CPUS=2

View File

@@ -22,6 +22,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/delay.h>
#include <crypto/internal/skcipher.h> #include <crypto/internal/skcipher.h>
#include <crypto/xts.h> #include <crypto/xts.h>
#include <asm/cpacf.h> #include <asm/cpacf.h>
@@ -128,6 +129,9 @@ static inline int __paes_keyblob2pkey(struct key_blob *kb,
/* try three times in case of failure */ /* try three times in case of failure */
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
if (i > 0 && ret == -EAGAIN && in_task())
if (msleep_interruptible(1000))
return -EINTR;
ret = pkey_keyblob2pkey(kb->key, kb->keylen, pk); ret = pkey_keyblob2pkey(kb->key, kb->keylen, pk);
if (ret == 0) if (ret == 0)
break; break;
@@ -138,10 +142,12 @@ static inline int __paes_keyblob2pkey(struct key_blob *kb,
static inline int __paes_convert_key(struct s390_paes_ctx *ctx) static inline int __paes_convert_key(struct s390_paes_ctx *ctx)
{ {
int ret;
struct pkey_protkey pkey; struct pkey_protkey pkey;
if (__paes_keyblob2pkey(&ctx->kb, &pkey)) ret = __paes_keyblob2pkey(&ctx->kb, &pkey);
return -EINVAL; if (ret)
return ret;
spin_lock_bh(&ctx->pk_lock); spin_lock_bh(&ctx->pk_lock);
memcpy(&ctx->pk, &pkey, sizeof(pkey)); memcpy(&ctx->pk, &pkey, sizeof(pkey));
@@ -169,10 +175,12 @@ static void ecb_paes_exit(struct crypto_skcipher *tfm)
static inline int __ecb_paes_set_key(struct s390_paes_ctx *ctx) static inline int __ecb_paes_set_key(struct s390_paes_ctx *ctx)
{ {
int rc;
unsigned long fc; unsigned long fc;
if (__paes_convert_key(ctx)) rc = __paes_convert_key(ctx);
return -EINVAL; if (rc)
return rc;
/* Pick the correct function code based on the protected key type */ /* Pick the correct function code based on the protected key type */
fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KM_PAES_128 : fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KM_PAES_128 :
@@ -282,10 +290,12 @@ static void cbc_paes_exit(struct crypto_skcipher *tfm)
static inline int __cbc_paes_set_key(struct s390_paes_ctx *ctx) static inline int __cbc_paes_set_key(struct s390_paes_ctx *ctx)
{ {
int rc;
unsigned long fc; unsigned long fc;
if (__paes_convert_key(ctx)) rc = __paes_convert_key(ctx);
return -EINVAL; if (rc)
return rc;
/* Pick the correct function code based on the protected key type */ /* Pick the correct function code based on the protected key type */
fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KMC_PAES_128 : fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KMC_PAES_128 :
@@ -577,10 +587,12 @@ static void ctr_paes_exit(struct crypto_skcipher *tfm)
static inline int __ctr_paes_set_key(struct s390_paes_ctx *ctx) static inline int __ctr_paes_set_key(struct s390_paes_ctx *ctx)
{ {
int rc;
unsigned long fc; unsigned long fc;
if (__paes_convert_key(ctx)) rc = __paes_convert_key(ctx);
return -EINVAL; if (rc)
return rc;
/* Pick the correct function code based on the protected key type */ /* Pick the correct function code based on the protected key type */
fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KMCTR_PAES_128 : fc = (ctx->pk.type == PKEY_KEYTYPE_AES_128) ? CPACF_KMCTR_PAES_128 :

View File

@@ -414,7 +414,7 @@ static int __init prng_sha512_instantiate(void)
} }
/* append the seed by 16 bytes of unique nonce */ /* append the seed by 16 bytes of unique nonce */
get_tod_clock_ext(seed + seedlen); store_tod_clock_ext((union tod_clock *)(seed + seedlen));
seedlen += 16; seedlen += 16;
/* now initial seed of the prno drng */ /* now initial seed of the prno drng */

View File

@@ -84,7 +84,7 @@ static int dbfs_diag0c_create(void **data, void **data_free_ptr, size_t *size)
if (IS_ERR(diag0c_data)) if (IS_ERR(diag0c_data))
return PTR_ERR(diag0c_data); return PTR_ERR(diag0c_data);
memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr)); memset(&diag0c_data->hdr, 0, sizeof(diag0c_data->hdr));
get_tod_clock_ext(diag0c_data->hdr.tod_ext); store_tod_clock_ext((union tod_clock *)diag0c_data->hdr.tod_ext);
diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry); diag0c_data->hdr.len = count * sizeof(struct hypfs_diag0c_entry);
diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION; diag0c_data->hdr.version = DBFS_D0C_HDR_VERSION;
diag0c_data->hdr.count = count; diag0c_data->hdr.count = count;

View File

@@ -234,7 +234,7 @@ failed:
struct dbfs_d2fc_hdr { struct dbfs_d2fc_hdr {
u64 len; /* Length of d2fc buffer without header */ u64 len; /* Length of d2fc buffer without header */
u16 version; /* Version of header */ u16 version; /* Version of header */
char tod_ext[STORE_CLOCK_EXT_SIZE]; /* TOD clock for d2fc */ union tod_clock tod_ext; /* TOD clock for d2fc */
u64 count; /* Number of VM guests in d2fc buffer */ u64 count; /* Number of VM guests in d2fc buffer */
char reserved[30]; char reserved[30];
} __attribute__ ((packed)); } __attribute__ ((packed));
@@ -252,7 +252,7 @@ static int dbfs_diag2fc_create(void **data, void **data_free_ptr, size_t *size)
d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr)); d2fc = diag2fc_store(guest_query, &count, sizeof(d2fc->hdr));
if (IS_ERR(d2fc)) if (IS_ERR(d2fc))
return PTR_ERR(d2fc); return PTR_ERR(d2fc);
get_tod_clock_ext(d2fc->hdr.tod_ext); store_tod_clock_ext(&d2fc->hdr.tod_ext);
d2fc->hdr.len = count * sizeof(struct diag2fc_data); d2fc->hdr.len = count * sizeof(struct diag2fc_data);
d2fc->hdr.version = DBFS_D2FC_HDR_VERSION; d2fc->hdr.version = DBFS_D2FC_HDR_VERSION;
d2fc->hdr.count = count; d2fc->hdr.count = count;

View File

@@ -145,6 +145,22 @@ void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
asm_inline volatile(ALTERNATIVE_2(oldinstr, altinstr1, facility1, \ asm_inline volatile(ALTERNATIVE_2(oldinstr, altinstr1, facility1, \
altinstr2, facility2) ::: "memory") altinstr2, facility2) ::: "memory")
/* Alternative inline assembly with input. */
#define alternative_input(oldinstr, newinstr, feature, input...) \
asm_inline volatile (ALTERNATIVE(oldinstr, newinstr, feature) \
: : input)
/* Like alternative_input, but with a single output argument */
#define alternative_io(oldinstr, altinstr, facility, output, input...) \
asm_inline volatile(ALTERNATIVE(oldinstr, altinstr, facility) \
: output : input)
/* Use this macro if more than one output parameter is needed. */
#define ASM_OUTPUT2(a...) a
/* Use this macro if clobbers are needed without inputs. */
#define ASM_NO_INPUT_CLOBBER(clobber...) : clobber
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* _ASM_S390_ALTERNATIVE_H */ #endif /* _ASM_S390_ALTERNATIVE_H */

View File

@@ -368,7 +368,7 @@ static inline struct ap_queue_status ap_dqap(ap_qid_t qid,
#if IS_ENABLED(CONFIG_ZCRYPT) #if IS_ENABLED(CONFIG_ZCRYPT)
void ap_bus_cfg_chg(void); void ap_bus_cfg_chg(void);
#else #else
static inline void ap_bus_cfg_chg(void){}; static inline void ap_bus_cfg_chg(void){}
#endif #endif
#endif /* _ASM_S390_AP_H_ */ #endif /* _ASM_S390_AP_H_ */

View File

@@ -44,16 +44,6 @@ static inline int atomic_fetch_add(int i, atomic_t *v)
static inline void atomic_add(int i, atomic_t *v) static inline void atomic_add(int i, atomic_t *v)
{ {
#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
/*
* Order of conditions is important to circumvent gcc 10 bug:
* https://gcc.gnu.org/pipermail/gcc-patches/2020-July/549318.html
*/
if ((i > -129) && (i < 128) && __builtin_constant_p(i)) {
__atomic_add_const(i, &v->counter);
return;
}
#endif
__atomic_add(i, &v->counter); __atomic_add(i, &v->counter);
} }
@@ -115,16 +105,6 @@ static inline s64 atomic64_fetch_add(s64 i, atomic64_t *v)
static inline void atomic64_add(s64 i, atomic64_t *v) static inline void atomic64_add(s64 i, atomic64_t *v)
{ {
#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
/*
* Order of conditions is important to circumvent gcc 10 bug:
* https://gcc.gnu.org/pipermail/gcc-patches/2020-July/549318.html
*/
if ((i > -129) && (i < 128) && __builtin_constant_p(i)) {
__atomic64_add_const(i, (long *)&v->counter);
return;
}
#endif
__atomic64_add(i, (long *)&v->counter); __atomic64_add(i, (long *)&v->counter);
} }

View File

@@ -61,18 +61,6 @@ static __always_inline void arch_set_bit(unsigned long nr, volatile unsigned lon
unsigned long *addr = __bitops_word(nr, ptr); unsigned long *addr = __bitops_word(nr, ptr);
unsigned long mask; unsigned long mask;
#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
if (__builtin_constant_p(nr)) {
unsigned char *caddr = __bitops_byte(nr, ptr);
asm volatile(
"oi %0,%b1\n"
: "+Q" (*caddr)
: "i" (1 << (nr & 7))
: "cc", "memory");
return;
}
#endif
mask = 1UL << (nr & (BITS_PER_LONG - 1)); mask = 1UL << (nr & (BITS_PER_LONG - 1));
__atomic64_or(mask, (long *)addr); __atomic64_or(mask, (long *)addr);
} }
@@ -82,18 +70,6 @@ static __always_inline void arch_clear_bit(unsigned long nr, volatile unsigned l
unsigned long *addr = __bitops_word(nr, ptr); unsigned long *addr = __bitops_word(nr, ptr);
unsigned long mask; unsigned long mask;
#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
if (__builtin_constant_p(nr)) {
unsigned char *caddr = __bitops_byte(nr, ptr);
asm volatile(
"ni %0,%b1\n"
: "+Q" (*caddr)
: "i" (~(1 << (nr & 7)))
: "cc", "memory");
return;
}
#endif
mask = ~(1UL << (nr & (BITS_PER_LONG - 1))); mask = ~(1UL << (nr & (BITS_PER_LONG - 1)));
__atomic64_and(mask, (long *)addr); __atomic64_and(mask, (long *)addr);
} }
@@ -104,18 +80,6 @@ static __always_inline void arch_change_bit(unsigned long nr,
unsigned long *addr = __bitops_word(nr, ptr); unsigned long *addr = __bitops_word(nr, ptr);
unsigned long mask; unsigned long mask;
#ifdef CONFIG_HAVE_MARCH_ZEC12_FEATURES
if (__builtin_constant_p(nr)) {
unsigned char *caddr = __bitops_byte(nr, ptr);
asm volatile(
"xi %0,%b1\n"
: "+Q" (*caddr)
: "i" (1 << (nr & 7))
: "cc", "memory");
return;
}
#endif
mask = 1UL << (nr & (BITS_PER_LONG - 1)); mask = 1UL << (nr & (BITS_PER_LONG - 1));
__atomic64_xor(mask, (long *)addr); __atomic64_xor(mask, (long *)addr);
} }

View File

@@ -35,4 +35,6 @@ u64 arch_cpu_idle_time(int cpu);
#define arch_idle_time(cpu) arch_cpu_idle_time(cpu) #define arch_idle_time(cpu) arch_cpu_idle_time(cpu)
void account_idle_time_irq(void);
#endif /* _S390_CPUTIME_H */ #endif /* _S390_CPUTIME_H */

View File

@@ -233,8 +233,7 @@ extern char elf_platform[];
do { \ do { \
set_personality(PER_LINUX | \ set_personality(PER_LINUX | \
(current->personality & (~PER_MASK))); \ (current->personality & (~PER_MASK))); \
current->thread.sys_call_table = \ current->thread.sys_call_table = sys_call_table; \
(unsigned long) &sys_call_table; \
} while (0) } while (0)
#else /* CONFIG_COMPAT */ #else /* CONFIG_COMPAT */
#define SET_PERSONALITY(ex) \ #define SET_PERSONALITY(ex) \
@@ -245,11 +244,11 @@ do { \
if ((ex).e_ident[EI_CLASS] == ELFCLASS32) { \ if ((ex).e_ident[EI_CLASS] == ELFCLASS32) { \
set_thread_flag(TIF_31BIT); \ set_thread_flag(TIF_31BIT); \
current->thread.sys_call_table = \ current->thread.sys_call_table = \
(unsigned long) &sys_call_table_emu; \ sys_call_table_emu; \
} else { \ } else { \
clear_thread_flag(TIF_31BIT); \ clear_thread_flag(TIF_31BIT); \
current->thread.sys_call_table = \ current->thread.sys_call_table = \
(unsigned long) &sys_call_table; \ sys_call_table; \
} \ } \
} while (0) } while (0)
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */

View File

@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ARCH_S390_ENTRY_COMMON_H
#define ARCH_S390_ENTRY_COMMON_H
#include <linux/sched.h>
#include <linux/audit.h>
#include <linux/tracehook.h>
#include <linux/processor.h>
#include <linux/uaccess.h>
#include <asm/fpu/api.h>
#define ARCH_EXIT_TO_USER_MODE_WORK (_TIF_GUARDED_STORAGE | _TIF_PER_TRAP)
void do_per_trap(struct pt_regs *regs);
void do_syscall(struct pt_regs *regs);
typedef void (*pgm_check_func)(struct pt_regs *regs);
extern pgm_check_func pgm_check_table[128];
#ifdef CONFIG_DEBUG_ENTRY
static __always_inline void arch_check_user_regs(struct pt_regs *regs)
{
debug_user_asce(0);
}
#define arch_check_user_regs arch_check_user_regs
#endif /* CONFIG_DEBUG_ENTRY */
static __always_inline void arch_exit_to_user_mode_work(struct pt_regs *regs,
unsigned long ti_work)
{
if (ti_work & _TIF_PER_TRAP) {
clear_thread_flag(TIF_PER_TRAP);
do_per_trap(regs);
}
if (ti_work & _TIF_GUARDED_STORAGE)
gs_load_bc_cb(regs);
}
#define arch_exit_to_user_mode_work arch_exit_to_user_mode_work
static __always_inline void arch_exit_to_user_mode(void)
{
if (test_cpu_flag(CIF_FPU))
__load_fpu_regs();
if (IS_ENABLED(CONFIG_DEBUG_ENTRY))
debug_user_asce(1);
}
#define arch_exit_to_user_mode arch_exit_to_user_mode
static inline bool on_thread_stack(void)
{
return !(((unsigned long)(current->stack) ^ current_stack_pointer()) & ~(THREAD_SIZE - 1));
}
#endif

View File

@@ -47,6 +47,8 @@
#include <linux/preempt.h> #include <linux/preempt.h>
void save_fpu_regs(void); void save_fpu_regs(void);
void load_fpu_regs(void);
void __load_fpu_regs(void);
static inline int test_fp_ctl(u32 fpc) static inline int test_fp_ctl(u32 fpc)
{ {

View File

@@ -20,11 +20,13 @@ struct s390_idle_data {
unsigned long long clock_idle_exit; unsigned long long clock_idle_exit;
unsigned long long timer_idle_enter; unsigned long long timer_idle_enter;
unsigned long long timer_idle_exit; unsigned long long timer_idle_exit;
unsigned long mt_cycles_enter[8];
}; };
extern struct device_attribute dev_attr_idle_count; extern struct device_attribute dev_attr_idle_count;
extern struct device_attribute dev_attr_idle_time_us; extern struct device_attribute dev_attr_idle_time_us;
void psw_idle(struct s390_idle_data *, unsigned long); void psw_idle(struct s390_idle_data *data, unsigned long psw_mask);
void psw_idle_exit(void);
#endif /* _S390_IDLE_H */ #endif /* _S390_IDLE_H */

View File

@@ -81,8 +81,8 @@ struct lowcore {
psw_t return_mcck_psw; /* 0x02a0 */ psw_t return_mcck_psw; /* 0x02a0 */
/* CPU accounting and timing values. */ /* CPU accounting and timing values. */
__u64 sync_enter_timer; /* 0x02b0 */ __u64 sys_enter_timer; /* 0x02b0 */
__u64 async_enter_timer; /* 0x02b8 */ __u8 pad_0x02b8[0x02c0-0x02b8]; /* 0x02b8 */
__u64 mcck_enter_timer; /* 0x02c0 */ __u64 mcck_enter_timer; /* 0x02c0 */
__u64 exit_timer; /* 0x02c8 */ __u64 exit_timer; /* 0x02c8 */
__u64 user_timer; /* 0x02d0 */ __u64 user_timer; /* 0x02d0 */
@@ -107,16 +107,15 @@ struct lowcore {
__u64 async_stack; /* 0x0350 */ __u64 async_stack; /* 0x0350 */
__u64 nodat_stack; /* 0x0358 */ __u64 nodat_stack; /* 0x0358 */
__u64 restart_stack; /* 0x0360 */ __u64 restart_stack; /* 0x0360 */
__u64 mcck_stack; /* 0x0368 */
/* Restart function and parameter. */ /* Restart function and parameter. */
__u64 restart_fn; /* 0x0368 */ __u64 restart_fn; /* 0x0370 */
__u64 restart_data; /* 0x0370 */ __u64 restart_data; /* 0x0378 */
__u64 restart_source; /* 0x0378 */ __u64 restart_source; /* 0x0380 */
/* Address space pointer. */ /* Address space pointer. */
__u64 kernel_asce; /* 0x0380 */ __u64 kernel_asce; /* 0x0388 */
__u64 user_asce; /* 0x0388 */ __u64 user_asce; /* 0x0390 */
__u8 pad_0x0390[0x0398-0x0390]; /* 0x0390 */
/* /*
* The lpp and current_pid fields form a * The lpp and current_pid fields form a

View File

@@ -99,6 +99,7 @@ int nmi_alloc_per_cpu(struct lowcore *lc);
void nmi_free_per_cpu(struct lowcore *lc); void nmi_free_per_cpu(struct lowcore *lc);
void s390_handle_mcck(void); void s390_handle_mcck(void);
void __s390_handle_mcck(void);
int s390_do_machine_check(struct pt_regs *regs); int s390_do_machine_check(struct pt_regs *regs);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */

View File

@@ -201,7 +201,7 @@ extern unsigned int s390_pci_no_rid;
Prototypes Prototypes
----------------------------------------------------------------------------- */ ----------------------------------------------------------------------------- */
/* Base stuff */ /* Base stuff */
int zpci_create_device(struct zpci_dev *); int zpci_create_device(u32 fid, u32 fh, enum zpci_state state);
void zpci_remove_device(struct zpci_dev *zdev); void zpci_remove_device(struct zpci_dev *zdev);
int zpci_enable_device(struct zpci_dev *); int zpci_enable_device(struct zpci_dev *);
int zpci_disable_device(struct zpci_dev *); int zpci_disable_device(struct zpci_dev *);
@@ -212,7 +212,7 @@ void zpci_remove_reserved_devices(void);
/* CLP */ /* CLP */
int clp_setup_writeback_mio(void); int clp_setup_writeback_mio(void);
int clp_scan_pci_devices(void); int clp_scan_pci_devices(void);
int clp_add_pci_device(u32, u32, int); int clp_query_pci_fn(struct zpci_dev *zdev);
int clp_enable_fh(struct zpci_dev *, u8); int clp_enable_fh(struct zpci_dev *, u8);
int clp_disable_fh(struct zpci_dev *); int clp_disable_fh(struct zpci_dev *);
int clp_get_state(u32 fid, enum zpci_state *state); int clp_get_state(u32 fid, enum zpci_state *state);

View File

@@ -131,9 +131,9 @@ static inline bool should_resched(int preempt_offset)
#endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */ #endif /* CONFIG_HAVE_MARCH_Z196_FEATURES */
#ifdef CONFIG_PREEMPTION #ifdef CONFIG_PREEMPTION
extern asmlinkage void preempt_schedule(void); extern void preempt_schedule(void);
#define __preempt_schedule() preempt_schedule() #define __preempt_schedule() preempt_schedule()
extern asmlinkage void preempt_schedule_notrace(void); extern void preempt_schedule_notrace(void);
#define __preempt_schedule_notrace() preempt_schedule_notrace() #define __preempt_schedule_notrace() preempt_schedule_notrace()
#endif /* CONFIG_PREEMPTION */ #endif /* CONFIG_PREEMPTION */

View File

@@ -38,6 +38,9 @@
#include <asm/runtime_instr.h> #include <asm/runtime_instr.h>
#include <asm/fpu/types.h> #include <asm/fpu/types.h>
#include <asm/fpu/internal.h> #include <asm/fpu/internal.h>
#include <asm/irqflags.h>
typedef long (*sys_call_ptr_t)(struct pt_regs *regs);
static inline void set_cpu_flag(int flag) static inline void set_cpu_flag(int flag)
{ {
@@ -101,31 +104,32 @@ extern void __bpon(void);
*/ */
struct thread_struct { struct thread_struct {
unsigned int acrs[NUM_ACRS]; unsigned int acrs[NUM_ACRS];
unsigned long ksp; /* kernel stack pointer */ unsigned long ksp; /* kernel stack pointer */
unsigned long user_timer; /* task cputime in user space */ unsigned long user_timer; /* task cputime in user space */
unsigned long guest_timer; /* task cputime in kvm guest */ unsigned long guest_timer; /* task cputime in kvm guest */
unsigned long system_timer; /* task cputime in kernel space */ unsigned long system_timer; /* task cputime in kernel space */
unsigned long hardirq_timer; /* task cputime in hardirq context */ unsigned long hardirq_timer; /* task cputime in hardirq context */
unsigned long softirq_timer; /* task cputime in softirq context */ unsigned long softirq_timer; /* task cputime in softirq context */
unsigned long sys_call_table; /* system call table address */ const sys_call_ptr_t *sys_call_table; /* system call table address */
unsigned long gmap_addr; /* address of last gmap fault. */ unsigned long gmap_addr; /* address of last gmap fault. */
unsigned int gmap_write_flag; /* gmap fault write indication */ unsigned int gmap_write_flag; /* gmap fault write indication */
unsigned int gmap_int_code; /* int code of last gmap fault */ unsigned int gmap_int_code; /* int code of last gmap fault */
unsigned int gmap_pfault; /* signal of a pending guest pfault */ unsigned int gmap_pfault; /* signal of a pending guest pfault */
/* Per-thread information related to debugging */ /* Per-thread information related to debugging */
struct per_regs per_user; /* User specified PER registers */ struct per_regs per_user; /* User specified PER registers */
struct per_event per_event; /* Cause of the last PER trap */ struct per_event per_event; /* Cause of the last PER trap */
unsigned long per_flags; /* Flags to control debug behavior */ unsigned long per_flags; /* Flags to control debug behavior */
unsigned int system_call; /* system call number in signal */ unsigned int system_call; /* system call number in signal */
unsigned long last_break; /* last breaking-event-address. */ unsigned long last_break; /* last breaking-event-address. */
/* pfault_wait is used to block the process on a pfault event */ /* pfault_wait is used to block the process on a pfault event */
unsigned long pfault_wait; unsigned long pfault_wait;
struct list_head list; struct list_head list;
/* cpu runtime instrumentation */ /* cpu runtime instrumentation */
struct runtime_instr_cb *ri_cb; struct runtime_instr_cb *ri_cb;
struct gs_cb *gs_cb; /* Current guarded storage cb */ struct gs_cb *gs_cb; /* Current guarded storage cb */
struct gs_cb *gs_bc_cb; /* Broadcast guarded storage cb */ struct gs_cb *gs_bc_cb; /* Broadcast guarded storage cb */
unsigned char trap_tdb[256]; /* Transaction abort diagnose block */ unsigned char trap_tdb[256]; /* Transaction abort diagnose block */
/* /*
* Warning: 'fpu' is dynamically-sized. It *MUST* be at * Warning: 'fpu' is dynamically-sized. It *MUST* be at
* the end. * the end.
@@ -184,6 +188,7 @@ static inline void release_thread(struct task_struct *tsk) { }
/* Free guarded storage control block */ /* Free guarded storage control block */
void guarded_storage_release(struct task_struct *tsk); void guarded_storage_release(struct task_struct *tsk);
void gs_load_bc_cb(struct pt_regs *regs);
unsigned long get_wchan(struct task_struct *p); unsigned long get_wchan(struct task_struct *p);
#define task_pt_regs(tsk) ((struct pt_regs *) \ #define task_pt_regs(tsk) ((struct pt_regs *) \
@@ -324,6 +329,11 @@ extern void memcpy_absolute(void *, void *, size_t);
extern int s390_isolate_bp(void); extern int s390_isolate_bp(void);
extern int s390_isolate_bp_guest(void); extern int s390_isolate_bp_guest(void);
static __always_inline bool regs_irqs_disabled(struct pt_regs *regs)
{
return arch_irqs_disabled_flags(regs->psw.mask);
}
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* __ASM_S390_PROCESSOR_H */ #endif /* __ASM_S390_PROCESSOR_H */

View File

@@ -11,13 +11,13 @@
#include <uapi/asm/ptrace.h> #include <uapi/asm/ptrace.h>
#define PIF_SYSCALL 0 /* inside a system call */ #define PIF_SYSCALL 0 /* inside a system call */
#define PIF_PER_TRAP 1 /* deliver sigtrap on return to user */ #define PIF_SYSCALL_RESTART 1 /* restart the current system call */
#define PIF_SYSCALL_RESTART 2 /* restart the current system call */ #define PIF_SYSCALL_RET_SET 2 /* return value was set via ptrace */
#define PIF_GUEST_FAULT 3 /* indicates program check in sie64a */ #define PIF_GUEST_FAULT 3 /* indicates program check in sie64a */
#define _PIF_SYSCALL BIT(PIF_SYSCALL) #define _PIF_SYSCALL BIT(PIF_SYSCALL)
#define _PIF_PER_TRAP BIT(PIF_PER_TRAP)
#define _PIF_SYSCALL_RESTART BIT(PIF_SYSCALL_RESTART) #define _PIF_SYSCALL_RESTART BIT(PIF_SYSCALL_RESTART)
#define _PIF_SYSCALL_RET_SET BIT(PIF_SYSCALL_RET_SET)
#define _PIF_GUEST_FAULT BIT(PIF_GUEST_FAULT) #define _PIF_GUEST_FAULT BIT(PIF_GUEST_FAULT)
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
@@ -68,6 +68,9 @@ enum {
&(*(struct psw_bits *)(&(__psw))); \ &(*(struct psw_bits *)(&(__psw))); \
})) }))
#define PGM_INT_CODE_MASK 0x7f
#define PGM_INT_CODE_PER 0x80
/* /*
* The pt_regs struct defines the way the registers are stored on * The pt_regs struct defines the way the registers are stored on
* the stack during a system call. * the stack during a system call.

View File

@@ -250,17 +250,13 @@ struct slsb {
* struct qdio_outbuf_state - SBAL related asynchronous operation information * struct qdio_outbuf_state - SBAL related asynchronous operation information
* (for communication with upper layer programs) * (for communication with upper layer programs)
* (only required for use with completion queues) * (only required for use with completion queues)
* @flags: flags indicating state of buffer
* @user: pointer to upper layer program's state information related to SBAL * @user: pointer to upper layer program's state information related to SBAL
* (stored in user1 data of QAOB) * (stored in user1 data of QAOB)
*/ */
struct qdio_outbuf_state { struct qdio_outbuf_state {
u8 flags;
void *user; void *user;
}; };
#define QDIO_OUTBUF_STATE_FLAG_PENDING 0x01
#define CHSC_AC1_INITIATE_INPUTQ 0x80 #define CHSC_AC1_INITIATE_INPUTQ 0x80
@@ -315,6 +311,7 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int,
#define QDIO_ERROR_GET_BUF_STATE 0x0002 #define QDIO_ERROR_GET_BUF_STATE 0x0002
#define QDIO_ERROR_SET_BUF_STATE 0x0004 #define QDIO_ERROR_SET_BUF_STATE 0x0004
#define QDIO_ERROR_SLSB_STATE 0x0100 #define QDIO_ERROR_SLSB_STATE 0x0100
#define QDIO_ERROR_SLSB_PENDING 0x0200
#define QDIO_ERROR_FATAL 0x00ff #define QDIO_ERROR_FATAL 0x00ff
#define QDIO_ERROR_TEMPORARY 0xff00 #define QDIO_ERROR_TEMPORARY 0xff00
@@ -336,7 +333,7 @@ typedef void qdio_handler_t(struct ccw_device *, unsigned int, int,
* @no_output_qs: number of output queues * @no_output_qs: number of output queues
* @input_handler: handler to be called for input queues * @input_handler: handler to be called for input queues
* @output_handler: handler to be called for output queues * @output_handler: handler to be called for output queues
* @irq_poll: Data IRQ polling handler (NULL when not supported) * @irq_poll: Data IRQ polling handler
* @scan_threshold: # of in-use buffers that triggers scan on output queue * @scan_threshold: # of in-use buffers that triggers scan on output queue
* @int_parm: interruption parameter * @int_parm: interruption parameter
* @input_sbal_addr_array: per-queue array, each element points to 128 SBALs * @input_sbal_addr_array: per-queue array, each element points to 128 SBALs

View File

@@ -525,8 +525,7 @@ static inline int scsw_cmd_is_valid_pno(union scsw *scsw)
return (scsw->cmd.fctl != 0) && return (scsw->cmd.fctl != 0) &&
(scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) && (scsw->cmd.stctl & SCSW_STCTL_STATUS_PEND) &&
(!(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) || (!(scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) ||
((scsw->cmd.stctl & SCSW_STCTL_INTER_STATUS) && (scsw->cmd.actl & SCSW_ACTL_SUSPENDED));
(scsw->cmd.actl & SCSW_ACTL_SUSPENDED)));
} }
/** /**

View File

@@ -14,8 +14,8 @@
#include <linux/err.h> #include <linux/err.h>
#include <asm/ptrace.h> #include <asm/ptrace.h>
extern const unsigned long sys_call_table[]; extern const sys_call_ptr_t sys_call_table[];
extern const unsigned long sys_call_table_emu[]; extern const sys_call_ptr_t sys_call_table_emu[];
static inline long syscall_get_nr(struct task_struct *task, static inline long syscall_get_nr(struct task_struct *task,
struct pt_regs *regs) struct pt_regs *regs)
@@ -56,6 +56,7 @@ static inline void syscall_set_return_value(struct task_struct *task,
struct pt_regs *regs, struct pt_regs *regs,
int error, long val) int error, long val)
{ {
set_pt_regs_flag(regs, PIF_SYSCALL_RET_SET);
regs->gprs[2] = error ? error : val; regs->gprs[2] = error ? error : val;
} }
@@ -97,4 +98,10 @@ static inline int syscall_get_arch(struct task_struct *task)
#endif #endif
return AUDIT_ARCH_S390X; return AUDIT_ARCH_S390X;
} }
static inline bool arch_syscall_is_vdso_sigreturn(struct pt_regs *regs)
{
return false;
}
#endif /* _ASM_SYSCALL_H */ #endif /* _ASM_SYSCALL_H */

View File

@@ -7,6 +7,33 @@
#ifndef _ASM_S390_SYSCALL_WRAPPER_H #ifndef _ASM_S390_SYSCALL_WRAPPER_H
#define _ASM_S390_SYSCALL_WRAPPER_H #define _ASM_S390_SYSCALL_WRAPPER_H
#define __SC_TYPE(t, a) t
#define SYSCALL_PT_ARG6(regs, m, t1, t2, t3, t4, t5, t6)\
SYSCALL_PT_ARG5(regs, m, t1, t2, t3, t4, t5), \
m(t6, (regs->gprs[7]))
#define SYSCALL_PT_ARG5(regs, m, t1, t2, t3, t4, t5) \
SYSCALL_PT_ARG4(regs, m, t1, t2, t3, t4), \
m(t5, (regs->gprs[6]))
#define SYSCALL_PT_ARG4(regs, m, t1, t2, t3, t4) \
SYSCALL_PT_ARG3(regs, m, t1, t2, t3), \
m(t4, (regs->gprs[5]))
#define SYSCALL_PT_ARG3(regs, m, t1, t2, t3) \
SYSCALL_PT_ARG2(regs, m, t1, t2), \
m(t3, (regs->gprs[4]))
#define SYSCALL_PT_ARG2(regs, m, t1, t2) \
SYSCALL_PT_ARG1(regs, m, t1), \
m(t2, (regs->gprs[3]))
#define SYSCALL_PT_ARG1(regs, m, t1) \
m(t1, (regs->orig_gpr2))
#define SYSCALL_PT_ARGS(x, ...) SYSCALL_PT_ARG##x(__VA_ARGS__)
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
#define __SC_COMPAT_TYPE(t, a) \ #define __SC_COMPAT_TYPE(t, a) \
__typeof(__builtin_choose_expr(sizeof(t) > 4, 0L, (t)0)) a __typeof(__builtin_choose_expr(sizeof(t) > 4, 0L, (t)0)) a
@@ -29,14 +56,15 @@
(t)__ReS; \ (t)__ReS; \
}) })
#define __S390_SYS_STUBx(x, name, ...) \ #define __S390_SYS_STUBx(x, name, ...) \
asmlinkage long __s390_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__));\ long __s390_sys##name(struct pt_regs *regs); \
ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \ ALLOW_ERROR_INJECTION(__s390_sys##name, ERRNO); \
asmlinkage long __s390_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__))\ long __s390_sys##name(struct pt_regs *regs) \
{ \ { \
long ret = __s390x_sys##name(__MAP(x,__SC_COMPAT_CAST,__VA_ARGS__));\ long ret = __do_sys##name(SYSCALL_PT_ARGS(x, regs, \
__MAP(x,__SC_TEST,__VA_ARGS__); \ __SC_COMPAT_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \
return ret; \ __MAP(x,__SC_TEST,__VA_ARGS__); \
return ret; \
} }
/* /*
@@ -45,17 +73,17 @@
*/ */
#define COMPAT_SYSCALL_DEFINE0(sname) \ #define COMPAT_SYSCALL_DEFINE0(sname) \
SYSCALL_METADATA(_##sname, 0); \ SYSCALL_METADATA(_##sname, 0); \
asmlinkage long __s390_compat_sys_##sname(void); \ long __s390_compat_sys_##sname(void); \
ALLOW_ERROR_INJECTION(__s390_compat_sys_##sname, ERRNO); \ ALLOW_ERROR_INJECTION(__s390_compat_sys_##sname, ERRNO); \
asmlinkage long __s390_compat_sys_##sname(void) long __s390_compat_sys_##sname(void)
#define SYSCALL_DEFINE0(sname) \ #define SYSCALL_DEFINE0(sname) \
SYSCALL_METADATA(_##sname, 0); \ SYSCALL_METADATA(_##sname, 0); \
asmlinkage long __s390x_sys_##sname(void); \ long __s390x_sys_##sname(void); \
ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \ ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \
asmlinkage long __s390_sys_##sname(void) \ long __s390_sys_##sname(void) \
__attribute__((alias(__stringify(__s390x_sys_##sname)))); \ __attribute__((alias(__stringify(__s390x_sys_##sname)))); \
asmlinkage long __s390x_sys_##sname(void) long __s390x_sys_##sname(void)
#define COND_SYSCALL(name) \ #define COND_SYSCALL(name) \
cond_syscall(__s390x_sys_##name); \ cond_syscall(__s390x_sys_##name); \
@@ -65,23 +93,24 @@
SYSCALL_ALIAS(__s390x_sys_##name, sys_ni_posix_timers); \ SYSCALL_ALIAS(__s390x_sys_##name, sys_ni_posix_timers); \
SYSCALL_ALIAS(__s390_sys_##name, sys_ni_posix_timers) SYSCALL_ALIAS(__s390_sys_##name, sys_ni_posix_timers)
#define COMPAT_SYSCALL_DEFINEx(x, name, ...) \ #define COMPAT_SYSCALL_DEFINEx(x, name, ...) \
__diag_push(); \ __diag_push(); \
__diag_ignore(GCC, 8, "-Wattribute-alias", \ __diag_ignore(GCC, 8, "-Wattribute-alias", \
"Type aliasing is used to sanitize syscall arguments");\ "Type aliasing is used to sanitize syscall arguments"); \
asmlinkage long __s390_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ long __s390_compat_sys##name(struct pt_regs *regs); \
asmlinkage long __s390_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ long __s390_compat_sys##name(struct pt_regs *regs) \
__attribute__((alias(__stringify(__se_compat_sys##name)))); \ __attribute__((alias(__stringify(__se_compat_sys##name)))); \
ALLOW_ERROR_INJECTION(__s390_compat_sys##name, ERRNO); \ ALLOW_ERROR_INJECTION(__s390_compat_sys##name, ERRNO); \
static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
asmlinkage long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ long __se_compat_sys##name(struct pt_regs *regs); \
asmlinkage long __se_compat_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ long __se_compat_sys##name(struct pt_regs *regs) \
{ \ { \
long ret = __do_compat_sys##name(__MAP(x,__SC_DELOUSE,__VA_ARGS__));\ long ret = __do_compat_sys##name(SYSCALL_PT_ARGS(x, regs, __SC_DELOUSE, \
__MAP(x,__SC_TEST,__VA_ARGS__); \ __MAP(x, __SC_TYPE, __VA_ARGS__))); \
return ret; \ __MAP(x,__SC_TEST,__VA_ARGS__); \
} \ return ret; \
__diag_pop(); \ } \
__diag_pop(); \
static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) static inline long __do_compat_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
/* /*
@@ -101,9 +130,9 @@
#define SYSCALL_DEFINE0(sname) \ #define SYSCALL_DEFINE0(sname) \
SYSCALL_METADATA(_##sname, 0); \ SYSCALL_METADATA(_##sname, 0); \
asmlinkage long __s390x_sys_##sname(void); \ long __s390x_sys_##sname(void); \
ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \ ALLOW_ERROR_INJECTION(__s390x_sys_##sname, ERRNO); \
asmlinkage long __s390x_sys_##sname(void) long __s390x_sys_##sname(void)
#define COND_SYSCALL(name) \ #define COND_SYSCALL(name) \
cond_syscall(__s390x_sys_##name) cond_syscall(__s390x_sys_##name)
@@ -113,23 +142,24 @@
#endif /* CONFIG_COMPAT */ #endif /* CONFIG_COMPAT */
#define __SYSCALL_DEFINEx(x, name, ...) \ #define __SYSCALL_DEFINEx(x, name, ...) \
__diag_push(); \ __diag_push(); \
__diag_ignore(GCC, 8, "-Wattribute-alias", \ __diag_ignore(GCC, 8, "-Wattribute-alias", \
"Type aliasing is used to sanitize syscall arguments");\ "Type aliasing is used to sanitize syscall arguments"); \
asmlinkage long __s390x_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ long __s390x_sys##name(struct pt_regs *regs) \
__attribute__((alias(__stringify(__se_sys##name)))); \ __attribute__((alias(__stringify(__se_sys##name)))); \
ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \ ALLOW_ERROR_INJECTION(__s390x_sys##name, ERRNO); \
long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)); \ long __se_sys##name(struct pt_regs *regs); \
__S390_SYS_STUBx(x, name, __VA_ARGS__) \ __S390_SYS_STUBx(x, name, __VA_ARGS__) \
asmlinkage long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ long __se_sys##name(struct pt_regs *regs) \
{ \ { \
long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__)); \ long ret = __do_sys##name(SYSCALL_PT_ARGS(x, regs, \
__MAP(x,__SC_TEST,__VA_ARGS__); \ __SC_CAST, __MAP(x, __SC_TYPE, __VA_ARGS__))); \
return ret; \ __MAP(x,__SC_TEST,__VA_ARGS__); \
} \ return ret; \
__diag_pop(); \ } \
__diag_pop(); \
static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) static inline long __do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__))
#endif /* _ASM_X86_SYSCALL_WRAPPER_H */ #endif /* _ASM_X86_SYSCALL_WRAPPER_H */

View File

@@ -36,6 +36,7 @@
*/ */
struct thread_info { struct thread_info {
unsigned long flags; /* low level flags */ unsigned long flags; /* low level flags */
unsigned long syscall_work; /* SYSCALL_WORK_ flags */
}; };
/* /*
@@ -46,6 +47,8 @@ struct thread_info {
.flags = 0, \ .flags = 0, \
} }
struct task_struct;
void arch_release_task_struct(struct task_struct *tsk); void arch_release_task_struct(struct task_struct *tsk);
int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src); int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src);
@@ -68,6 +71,7 @@ void arch_setup_new_exec(void);
#define TIF_NOTIFY_SIGNAL 7 /* signal notifications exist */ #define TIF_NOTIFY_SIGNAL 7 /* signal notifications exist */
#define TIF_ISOLATE_BP 8 /* Run process with isolated BP */ #define TIF_ISOLATE_BP 8 /* Run process with isolated BP */
#define TIF_ISOLATE_BP_GUEST 9 /* Run KVM guests with isolated BP */ #define TIF_ISOLATE_BP_GUEST 9 /* Run KVM guests with isolated BP */
#define TIF_PER_TRAP 10 /* Need to handle PER trap on exit to usermode */
#define TIF_31BIT 16 /* 32bit process */ #define TIF_31BIT 16 /* 32bit process */
#define TIF_MEMDIE 17 /* is terminating due to OOM killer */ #define TIF_MEMDIE 17 /* is terminating due to OOM killer */
@@ -91,6 +95,7 @@ void arch_setup_new_exec(void);
#define _TIF_PATCH_PENDING BIT(TIF_PATCH_PENDING) #define _TIF_PATCH_PENDING BIT(TIF_PATCH_PENDING)
#define _TIF_ISOLATE_BP BIT(TIF_ISOLATE_BP) #define _TIF_ISOLATE_BP BIT(TIF_ISOLATE_BP)
#define _TIF_ISOLATE_BP_GUEST BIT(TIF_ISOLATE_BP_GUEST) #define _TIF_ISOLATE_BP_GUEST BIT(TIF_ISOLATE_BP_GUEST)
#define _TIF_PER_TRAP BIT(TIF_PER_TRAP)
#define _TIF_31BIT BIT(TIF_31BIT) #define _TIF_31BIT BIT(TIF_31BIT)
#define _TIF_SINGLE_STEP BIT(TIF_SINGLE_STEP) #define _TIF_SINGLE_STEP BIT(TIF_SINGLE_STEP)

View File

@@ -19,6 +19,25 @@
extern u64 clock_comparator_max; extern u64 clock_comparator_max;
union tod_clock {
__uint128_t val;
struct {
__uint128_t ei : 8; /* epoch index */
__uint128_t tod : 64; /* bits 0-63 of tod clock */
__uint128_t : 40;
__uint128_t pf : 16; /* programmable field */
};
struct {
__uint128_t eitod : 72; /* epoch index + bits 0-63 tod clock */
__uint128_t : 56;
};
struct {
__uint128_t us : 60; /* micro-seconds */
__uint128_t sus : 12; /* sub-microseconds */
__uint128_t : 56;
};
} __packed;
/* Inline functions for clock register access. */ /* Inline functions for clock register access. */
static inline int set_tod_clock(__u64 time) static inline int set_tod_clock(__u64 time)
{ {
@@ -32,18 +51,23 @@ static inline int set_tod_clock(__u64 time)
return cc; return cc;
} }
static inline int store_tod_clock(__u64 *time) static inline int store_tod_clock_ext_cc(union tod_clock *clk)
{ {
int cc; int cc;
asm volatile( asm volatile(
" stck %1\n" " stcke %1\n"
" ipm %0\n" " ipm %0\n"
" srl %0,28\n" " srl %0,28\n"
: "=d" (cc), "=Q" (*time) : : "cc"); : "=d" (cc), "=Q" (*clk) : : "cc");
return cc; return cc;
} }
static inline void store_tod_clock_ext(union tod_clock *tod)
{
asm volatile("stcke %0" : "=Q" (*tod) : : "cc");
}
static inline void set_clock_comparator(__u64 time) static inline void set_clock_comparator(__u64 time)
{ {
asm volatile("sckc %0" : : "Q" (time)); asm volatile("sckc %0" : : "Q" (time));
@@ -144,23 +168,15 @@ static inline void local_tick_enable(unsigned long long comp)
} }
#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ #define CLOCK_TICK_RATE 1193180 /* Underlying HZ */
#define STORE_CLOCK_EXT_SIZE 16 /* stcke writes 16 bytes */
typedef unsigned long long cycles_t; typedef unsigned long long cycles_t;
static inline void get_tod_clock_ext(char *clk)
{
typedef struct { char _[STORE_CLOCK_EXT_SIZE]; } addrtype;
asm volatile("stcke %0" : "=Q" (*(addrtype *) clk) : : "cc");
}
static inline unsigned long long get_tod_clock(void) static inline unsigned long long get_tod_clock(void)
{ {
char clk[STORE_CLOCK_EXT_SIZE]; union tod_clock clk;
get_tod_clock_ext(clk); store_tod_clock_ext(&clk);
return *((unsigned long long *)&clk[1]); return clk.tod;
} }
static inline unsigned long long get_tod_clock_fast(void) static inline unsigned long long get_tod_clock_fast(void)
@@ -183,7 +199,7 @@ static inline cycles_t get_cycles(void)
int get_phys_clock(unsigned long *clock); int get_phys_clock(unsigned long *clock);
void init_cpu_timer(void); void init_cpu_timer(void);
extern unsigned char tod_clock_base[16] __aligned(8); extern union tod_clock tod_clock_base;
/** /**
* get_clock_monotonic - returns current time in clock rate units * get_clock_monotonic - returns current time in clock rate units
@@ -197,7 +213,7 @@ static inline unsigned long long get_tod_clock_monotonic(void)
unsigned long long tod; unsigned long long tod;
preempt_disable_notrace(); preempt_disable_notrace();
tod = get_tod_clock() - *(unsigned long long *) &tod_clock_base[1]; tod = get_tod_clock() - tod_clock_base.tod;
preempt_enable_notrace(); preempt_enable_notrace();
return tod; return tod;
} }

View File

@@ -66,7 +66,7 @@ static inline void pte_free_tlb(struct mmu_gather *tlb, pgtable_t pte,
__tlb_adjust_range(tlb, address, PAGE_SIZE); __tlb_adjust_range(tlb, address, PAGE_SIZE);
tlb->mm->context.flush_mm = 1; tlb->mm->context.flush_mm = 1;
tlb->freed_tables = 1; tlb->freed_tables = 1;
tlb->cleared_ptes = 1; tlb->cleared_pmds = 1;
/* /*
* page_table_free_rcu takes care of the allocation bit masks * page_table_free_rcu takes care of the allocation bit masks
* of the 2K table fragments in the 4K page table page, * of the 2K table fragments in the 4K page table page,
@@ -110,7 +110,6 @@ static inline void p4d_free_tlb(struct mmu_gather *tlb, p4d_t *p4d,
__tlb_adjust_range(tlb, address, PAGE_SIZE); __tlb_adjust_range(tlb, address, PAGE_SIZE);
tlb->mm->context.flush_mm = 1; tlb->mm->context.flush_mm = 1;
tlb->freed_tables = 1; tlb->freed_tables = 1;
tlb->cleared_p4ds = 1;
tlb_remove_table(tlb, p4d); tlb_remove_table(tlb, p4d);
} }
@@ -128,7 +127,7 @@ static inline void pud_free_tlb(struct mmu_gather *tlb, pud_t *pud,
return; return;
tlb->mm->context.flush_mm = 1; tlb->mm->context.flush_mm = 1;
tlb->freed_tables = 1; tlb->freed_tables = 1;
tlb->cleared_puds = 1; tlb->cleared_p4ds = 1;
tlb_remove_table(tlb, pud); tlb_remove_table(tlb, pud);
} }

View File

@@ -18,7 +18,7 @@
#include <asm/extable.h> #include <asm/extable.h>
#include <asm/facility.h> #include <asm/facility.h>
void debug_user_asce(void); void debug_user_asce(int exit);
static inline int __range_ok(unsigned long addr, unsigned long size) static inline int __range_ok(unsigned long addr, unsigned long size)
{ {

View File

@@ -4,17 +4,18 @@
#include <vdso/datapage.h> #include <vdso/datapage.h>
/* Default link addresses for the vDSOs */ /* Default link address for the vDSO */
#define VDSO32_LBASE 0
#define VDSO64_LBASE 0 #define VDSO64_LBASE 0
#define __VVAR_PAGES 2
#define VDSO_VERSION_STRING LINUX_2.6.29 #define VDSO_VERSION_STRING LINUX_2.6.29
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
extern struct vdso_data *vdso_data; extern struct vdso_data *vdso_data;
void vdso_getcpu_init(void); int vdso_getcpu_init(void);
#endif /* __ASSEMBLY__ */ #endif /* __ASSEMBLY__ */
#endif /* __S390_VDSO_H__ */ #endif /* __S390_VDSO_H__ */

View File

@@ -24,13 +24,12 @@ static __always_inline const struct vdso_data *__arch_get_vdso_data(void)
static inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_data *vd) static inline u64 __arch_get_hw_counter(s32 clock_mode, const struct vdso_data *vd)
{ {
const struct vdso_data *vdso = __arch_get_vdso_data();
u64 adj, now; u64 adj, now;
now = get_tod_clock(); now = get_tod_clock();
adj = vdso->arch_data.tod_steering_end - now; adj = vd->arch_data.tod_steering_end - now;
if (unlikely((s64) adj > 0)) if (unlikely((s64) adj > 0))
now += (vdso->arch_data.tod_steering_delta < 0) ? (adj >> 15) : -(adj >> 15); now += (vd->arch_data.tod_steering_delta < 0) ? (adj >> 15) : -(adj >> 15);
return now; return now;
} }
@@ -68,4 +67,11 @@ long clock_getres_fallback(clockid_t clkid, struct __kernel_timespec *ts)
return r2; return r2;
} }
#ifdef CONFIG_TIME_NS
static __always_inline const struct vdso_data *__arch_get_timens_vdso_data(void)
{
return _timens_data;
}
#endif
#endif #endif

View File

@@ -4,4 +4,18 @@
#define __ARCH_HAS_VTIME_TASK_SWITCH #define __ARCH_HAS_VTIME_TASK_SWITCH
static inline void update_timer_sys(void)
{
S390_lowcore.system_timer += S390_lowcore.last_update_timer - S390_lowcore.exit_timer;
S390_lowcore.user_timer += S390_lowcore.exit_timer - S390_lowcore.sys_enter_timer;
S390_lowcore.last_update_timer = S390_lowcore.sys_enter_timer;
}
static inline void update_timer_mcck(void)
{
S390_lowcore.system_timer += S390_lowcore.last_update_timer - S390_lowcore.exit_timer;
S390_lowcore.user_timer += S390_lowcore.exit_timer - S390_lowcore.mcck_enter_timer;
S390_lowcore.last_update_timer = S390_lowcore.mcck_enter_timer;
}
#endif /* _S390_VTIME_H */ #endif /* _S390_VTIME_H */

View File

@@ -179,8 +179,9 @@
#define ACR_SIZE 4 #define ACR_SIZE 4
#define PTRACE_OLDSETOPTIONS 21 #define PTRACE_OLDSETOPTIONS 21
#define PTRACE_SYSEMU 31
#define PTRACE_SYSEMU_SINGLESTEP 32
#ifndef __ASSEMBLY__ #ifndef __ASSEMBLY__
#include <linux/stddef.h> #include <linux/stddef.h>
#include <linux/types.h> #include <linux/types.h>

View File

@@ -34,7 +34,7 @@ CFLAGS_dumpstack.o += -fno-optimize-sibling-calls
CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls CFLAGS_unwind_bc.o += -fno-optimize-sibling-calls
obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o obj-y := traps.o time.o process.o base.o early.o setup.o idle.o vtime.o
obj-y += processor.o sys_s390.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o obj-y += processor.o syscall.o ptrace.o signal.o cpcmd.o ebcdic.o nmi.o
obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o obj-y += debug.o irq.o ipl.o dis.o diag.o vdso.o
obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o obj-y += sysinfo.o lgr.o os_info.o machine_kexec.o pgm_check.o
obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o obj-y += runtime_instr.o cache.o fpu.o dumpstack.o guarded_storage.o sthyi.o

View File

@@ -26,26 +26,14 @@ int main(void)
BLANK(); BLANK();
/* thread struct offsets */ /* thread struct offsets */
OFFSET(__THREAD_ksp, thread_struct, ksp); OFFSET(__THREAD_ksp, thread_struct, ksp);
OFFSET(__THREAD_sysc_table, thread_struct, sys_call_table);
OFFSET(__THREAD_last_break, thread_struct, last_break);
OFFSET(__THREAD_FPU_fpc, thread_struct, fpu.fpc);
OFFSET(__THREAD_FPU_regs, thread_struct, fpu.regs);
OFFSET(__THREAD_per_cause, thread_struct, per_event.cause);
OFFSET(__THREAD_per_address, thread_struct, per_event.address);
OFFSET(__THREAD_per_paid, thread_struct, per_event.paid);
OFFSET(__THREAD_trap_tdb, thread_struct, trap_tdb);
BLANK(); BLANK();
/* thread info offsets */ /* thread info offsets */
OFFSET(__TI_flags, task_struct, thread_info.flags); OFFSET(__TI_flags, task_struct, thread_info.flags);
BLANK(); BLANK();
/* pt_regs offsets */ /* pt_regs offsets */
OFFSET(__PT_ARGS, pt_regs, args);
OFFSET(__PT_PSW, pt_regs, psw); OFFSET(__PT_PSW, pt_regs, psw);
OFFSET(__PT_GPRS, pt_regs, gprs); OFFSET(__PT_GPRS, pt_regs, gprs);
OFFSET(__PT_ORIG_GPR2, pt_regs, orig_gpr2); OFFSET(__PT_ORIG_GPR2, pt_regs, orig_gpr2);
OFFSET(__PT_INT_CODE, pt_regs, int_code);
OFFSET(__PT_INT_PARM, pt_regs, int_parm);
OFFSET(__PT_INT_PARM_LONG, pt_regs, int_parm_long);
OFFSET(__PT_FLAGS, pt_regs, flags); OFFSET(__PT_FLAGS, pt_regs, flags);
OFFSET(__PT_CR1, pt_regs, cr1); OFFSET(__PT_CR1, pt_regs, cr1);
DEFINE(__PT_SIZE, sizeof(struct pt_regs)); DEFINE(__PT_SIZE, sizeof(struct pt_regs));
@@ -64,6 +52,7 @@ int main(void)
OFFSET(__CLOCK_IDLE_EXIT, s390_idle_data, clock_idle_exit); OFFSET(__CLOCK_IDLE_EXIT, s390_idle_data, clock_idle_exit);
OFFSET(__TIMER_IDLE_ENTER, s390_idle_data, timer_idle_enter); OFFSET(__TIMER_IDLE_ENTER, s390_idle_data, timer_idle_enter);
OFFSET(__TIMER_IDLE_EXIT, s390_idle_data, timer_idle_exit); OFFSET(__TIMER_IDLE_EXIT, s390_idle_data, timer_idle_exit);
OFFSET(__MT_CYCLES_ENTER, s390_idle_data, mt_cycles_enter);
BLANK(); BLANK();
/* hardware defined lowcore locations 0x000 - 0x1ff */ /* hardware defined lowcore locations 0x000 - 0x1ff */
OFFSET(__LC_EXT_PARAMS, lowcore, ext_params); OFFSET(__LC_EXT_PARAMS, lowcore, ext_params);
@@ -115,13 +104,9 @@ int main(void)
OFFSET(__LC_CPU_FLAGS, lowcore, cpu_flags); OFFSET(__LC_CPU_FLAGS, lowcore, cpu_flags);
OFFSET(__LC_RETURN_PSW, lowcore, return_psw); OFFSET(__LC_RETURN_PSW, lowcore, return_psw);
OFFSET(__LC_RETURN_MCCK_PSW, lowcore, return_mcck_psw); OFFSET(__LC_RETURN_MCCK_PSW, lowcore, return_mcck_psw);
OFFSET(__LC_SYNC_ENTER_TIMER, lowcore, sync_enter_timer); OFFSET(__LC_SYS_ENTER_TIMER, lowcore, sys_enter_timer);
OFFSET(__LC_ASYNC_ENTER_TIMER, lowcore, async_enter_timer);
OFFSET(__LC_MCCK_ENTER_TIMER, lowcore, mcck_enter_timer); OFFSET(__LC_MCCK_ENTER_TIMER, lowcore, mcck_enter_timer);
OFFSET(__LC_EXIT_TIMER, lowcore, exit_timer); OFFSET(__LC_EXIT_TIMER, lowcore, exit_timer);
OFFSET(__LC_USER_TIMER, lowcore, user_timer);
OFFSET(__LC_SYSTEM_TIMER, lowcore, system_timer);
OFFSET(__LC_STEAL_TIMER, lowcore, steal_timer);
OFFSET(__LC_LAST_UPDATE_TIMER, lowcore, last_update_timer); OFFSET(__LC_LAST_UPDATE_TIMER, lowcore, last_update_timer);
OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock); OFFSET(__LC_LAST_UPDATE_CLOCK, lowcore, last_update_clock);
OFFSET(__LC_INT_CLOCK, lowcore, int_clock); OFFSET(__LC_INT_CLOCK, lowcore, int_clock);
@@ -133,6 +118,7 @@ int main(void)
OFFSET(__LC_ASYNC_STACK, lowcore, async_stack); OFFSET(__LC_ASYNC_STACK, lowcore, async_stack);
OFFSET(__LC_NODAT_STACK, lowcore, nodat_stack); OFFSET(__LC_NODAT_STACK, lowcore, nodat_stack);
OFFSET(__LC_RESTART_STACK, lowcore, restart_stack); OFFSET(__LC_RESTART_STACK, lowcore, restart_stack);
OFFSET(__LC_MCCK_STACK, lowcore, mcck_stack);
OFFSET(__LC_RESTART_FN, lowcore, restart_fn); OFFSET(__LC_RESTART_FN, lowcore, restart_fn);
OFFSET(__LC_RESTART_DATA, lowcore, restart_data); OFFSET(__LC_RESTART_DATA, lowcore, restart_data);
OFFSET(__LC_RESTART_SOURCE, lowcore, restart_source); OFFSET(__LC_RESTART_SOURCE, lowcore, restart_source);

View File

@@ -118,6 +118,7 @@ static int restore_sigregs32(struct pt_regs *regs,_sigregs32 __user *sregs)
fpregs_load((_s390_fp_regs *) &user_sregs.fpregs, &current->thread.fpu); fpregs_load((_s390_fp_regs *) &user_sregs.fpregs, &current->thread.fpu);
clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
return 0; return 0;
} }

View File

@@ -829,11 +829,11 @@ static inline debug_entry_t *get_active_entry(debug_info_t *id)
static inline void debug_finish_entry(debug_info_t *id, debug_entry_t *active, static inline void debug_finish_entry(debug_info_t *id, debug_entry_t *active,
int level, int exception) int level, int exception)
{ {
unsigned char clk[STORE_CLOCK_EXT_SIZE];
unsigned long timestamp; unsigned long timestamp;
union tod_clock clk;
get_tod_clock_ext(clk); store_tod_clock_ext(&clk);
timestamp = *(unsigned long *) &clk[0] >> 4; timestamp = clk.us;
timestamp -= TOD_UNIX_EPOCH >> 12; timestamp -= TOD_UNIX_EPOCH >> 12;
active->clock = timestamp; active->clock = timestamp;
active->cpu = smp_processor_id(); active->cpu = smp_processor_id();

View File

@@ -35,16 +35,16 @@
static void __init reset_tod_clock(void) static void __init reset_tod_clock(void)
{ {
u64 time; union tod_clock clk;
if (store_tod_clock(&time) == 0) if (store_tod_clock_ext_cc(&clk) == 0)
return; return;
/* TOD clock not running. Set the clock to Unix Epoch. */ /* TOD clock not running. Set the clock to Unix Epoch. */
if (set_tod_clock(TOD_UNIX_EPOCH) != 0 || store_tod_clock(&time) != 0) if (set_tod_clock(TOD_UNIX_EPOCH) || store_tod_clock_ext_cc(&clk))
disabled_wait(); disabled_wait();
memset(tod_clock_base, 0, 16); memset(&tod_clock_base, 0, sizeof(tod_clock_base));
*(__u64 *) &tod_clock_base[1] = TOD_UNIX_EPOCH; tod_clock_base.tod = TOD_UNIX_EPOCH;
S390_lowcore.last_update_clock = TOD_UNIX_EPOCH; S390_lowcore.last_update_clock = TOD_UNIX_EPOCH;
} }
@@ -230,7 +230,7 @@ static __init void detect_machine_facilities(void)
} }
if (test_facility(133)) if (test_facility(133))
S390_lowcore.machine_flags |= MACHINE_FLAG_GS; S390_lowcore.machine_flags |= MACHINE_FLAG_GS;
if (test_facility(139) && (tod_clock_base[1] & 0x80)) { if (test_facility(139) && (tod_clock_base.tod >> 63)) {
/* Enabled signed clock comparator comparisons */ /* Enabled signed clock comparator comparisons */
S390_lowcore.machine_flags |= MACHINE_FLAG_SCC; S390_lowcore.machine_flags |= MACHINE_FLAG_SCC;
clock_comparator_max = -1ULL >> 1; clock_comparator_max = -1ULL >> 1;

File diff suppressed because it is too large Load Diff

View File

@@ -17,8 +17,9 @@ void io_int_handler(void);
void mcck_int_handler(void); void mcck_int_handler(void);
void restart_int_handler(void); void restart_int_handler(void);
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs); void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs);
asmlinkage void do_syscall_trace_exit(struct pt_regs *regs); void __do_pgm_check(struct pt_regs *regs);
void __do_syscall(struct pt_regs *regs, int per_trap);
void do_protection_exception(struct pt_regs *regs); void do_protection_exception(struct pt_regs *regs);
void do_dat_exception(struct pt_regs *regs); void do_dat_exception(struct pt_regs *regs);
@@ -48,9 +49,7 @@ void translation_exception(struct pt_regs *regs);
void vector_exception(struct pt_regs *regs); void vector_exception(struct pt_regs *regs);
void monitor_event_exception(struct pt_regs *regs); void monitor_event_exception(struct pt_regs *regs);
void do_per_trap(struct pt_regs *regs);
void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str); void do_report_trap(struct pt_regs *regs, int si_signo, int si_code, char *str);
void syscall_trace(struct pt_regs *regs, int entryexit);
void kernel_stack_overflow(struct pt_regs * regs); void kernel_stack_overflow(struct pt_regs * regs);
void do_signal(struct pt_regs *regs); void do_signal(struct pt_regs *regs);
void handle_signal32(struct ksignal *ksig, sigset_t *oldset, void handle_signal32(struct ksignal *ksig, sigset_t *oldset,
@@ -58,7 +57,8 @@ void handle_signal32(struct ksignal *ksig, sigset_t *oldset,
void do_notify_resume(struct pt_regs *regs); void do_notify_resume(struct pt_regs *regs);
void __init init_IRQ(void); void __init init_IRQ(void);
void do_IRQ(struct pt_regs *regs, int irq); void do_io_irq(struct pt_regs *regs);
void do_ext_irq(struct pt_regs *regs);
void do_restart(void); void do_restart(void);
void __init startup_init(void); void __init startup_init(void);
void die(struct pt_regs *regs, const char *str); void die(struct pt_regs *regs, const char *str);
@@ -82,8 +82,6 @@ long sys_s390_sthyi(unsigned long function_code, void __user *buffer, u64 __user
DECLARE_PER_CPU(u64, mt_cycles[8]); DECLARE_PER_CPU(u64, mt_cycles[8]);
void gs_load_bc_cb(struct pt_regs *regs);
unsigned long stack_alloc(void); unsigned long stack_alloc(void);
void stack_free(unsigned long stack); void stack_free(unsigned long stack);

View File

@@ -175,3 +175,91 @@ void __kernel_fpu_end(struct kernel_fpu *state, u32 flags)
: "1", "cc"); : "1", "cc");
} }
EXPORT_SYMBOL(__kernel_fpu_end); EXPORT_SYMBOL(__kernel_fpu_end);
void __load_fpu_regs(void)
{
struct fpu *state = &current->thread.fpu;
unsigned long *regs = current->thread.fpu.regs;
asm volatile("lfpc %0" : : "Q" (state->fpc));
if (likely(MACHINE_HAS_VX)) {
asm volatile("lgr 1,%0\n"
"VLM 0,15,0,1\n"
"VLM 16,31,256,1\n"
:
: "d" (regs)
: "1", "cc", "memory");
} else {
asm volatile("ld 0,%0" : : "Q" (regs[0]));
asm volatile("ld 1,%0" : : "Q" (regs[1]));
asm volatile("ld 2,%0" : : "Q" (regs[2]));
asm volatile("ld 3,%0" : : "Q" (regs[3]));
asm volatile("ld 4,%0" : : "Q" (regs[4]));
asm volatile("ld 5,%0" : : "Q" (regs[5]));
asm volatile("ld 6,%0" : : "Q" (regs[6]));
asm volatile("ld 7,%0" : : "Q" (regs[7]));
asm volatile("ld 8,%0" : : "Q" (regs[8]));
asm volatile("ld 9,%0" : : "Q" (regs[9]));
asm volatile("ld 10,%0" : : "Q" (regs[10]));
asm volatile("ld 11,%0" : : "Q" (regs[11]));
asm volatile("ld 12,%0" : : "Q" (regs[12]));
asm volatile("ld 13,%0" : : "Q" (regs[13]));
asm volatile("ld 14,%0" : : "Q" (regs[14]));
asm volatile("ld 15,%0" : : "Q" (regs[15]));
}
clear_cpu_flag(CIF_FPU);
}
EXPORT_SYMBOL(__load_fpu_regs);
void load_fpu_regs(void)
{
raw_local_irq_disable();
__load_fpu_regs();
raw_local_irq_enable();
}
EXPORT_SYMBOL(load_fpu_regs);
void save_fpu_regs(void)
{
unsigned long flags, *regs;
struct fpu *state;
local_irq_save(flags);
if (test_cpu_flag(CIF_FPU))
goto out;
state = &current->thread.fpu;
regs = current->thread.fpu.regs;
asm volatile("stfpc %0" : "=Q" (state->fpc));
if (likely(MACHINE_HAS_VX)) {
asm volatile("lgr 1,%0\n"
"VSTM 0,15,0,1\n"
"VSTM 16,31,256,1\n"
:
: "d" (regs)
: "1", "cc", "memory");
} else {
asm volatile("std 0,%0" : "=Q" (regs[0]));
asm volatile("std 1,%0" : "=Q" (regs[1]));
asm volatile("std 2,%0" : "=Q" (regs[2]));
asm volatile("std 3,%0" : "=Q" (regs[3]));
asm volatile("std 4,%0" : "=Q" (regs[4]));
asm volatile("std 5,%0" : "=Q" (regs[5]));
asm volatile("std 6,%0" : "=Q" (regs[6]));
asm volatile("std 7,%0" : "=Q" (regs[7]));
asm volatile("std 8,%0" : "=Q" (regs[8]));
asm volatile("std 9,%0" : "=Q" (regs[9]));
asm volatile("std 10,%0" : "=Q" (regs[10]));
asm volatile("std 11,%0" : "=Q" (regs[11]));
asm volatile("std 12,%0" : "=Q" (regs[12]));
asm volatile("std 13,%0" : "=Q" (regs[13]));
asm volatile("std 14,%0" : "=Q" (regs[14]));
asm volatile("std 15,%0" : "=Q" (regs[15]));
}
set_cpu_flag(CIF_FPU);
out:
local_irq_restore(flags);
}
EXPORT_SYMBOL(save_fpu_regs);

View File

@@ -14,12 +14,36 @@
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/sched/cputime.h> #include <linux/sched/cputime.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include <asm/cpu_mf.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/smp.h> #include <asm/smp.h>
#include "entry.h" #include "entry.h"
static DEFINE_PER_CPU(struct s390_idle_data, s390_idle); static DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
void account_idle_time_irq(void)
{
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
u64 cycles_new[8];
int i;
clear_cpu_flag(CIF_ENABLED_WAIT);
if (smp_cpu_mtid) {
stcctm(MT_DIAG, smp_cpu_mtid, cycles_new);
for (i = 0; i < smp_cpu_mtid; i++)
this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]);
}
idle->clock_idle_exit = S390_lowcore.int_clock;
idle->timer_idle_exit = S390_lowcore.sys_enter_timer;
S390_lowcore.steal_timer += idle->clock_idle_enter - S390_lowcore.last_update_clock;
S390_lowcore.last_update_clock = idle->clock_idle_exit;
S390_lowcore.system_timer += S390_lowcore.last_update_timer - idle->timer_idle_enter;
S390_lowcore.last_update_timer = idle->timer_idle_exit;
}
void arch_cpu_idle(void) void arch_cpu_idle(void)
{ {
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);

View File

@@ -21,6 +21,7 @@
#include <linux/init.h> #include <linux/init.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/entry-common.h>
#include <asm/irq_regs.h> #include <asm/irq_regs.h>
#include <asm/cputime.h> #include <asm/cputime.h>
#include <asm/lowcore.h> #include <asm/lowcore.h>
@@ -95,19 +96,97 @@ static const struct irq_class irqclass_sub_desc[] = {
{.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"}, {.irq = CPU_RST, .name = "RST", .desc = "[CPU] CPU Restart"},
}; };
void do_IRQ(struct pt_regs *regs, int irq) static void do_IRQ(struct pt_regs *regs, int irq)
{ {
struct pt_regs *old_regs;
old_regs = set_irq_regs(regs);
irq_enter();
if (tod_after_eq(S390_lowcore.int_clock, if (tod_after_eq(S390_lowcore.int_clock,
S390_lowcore.clock_comparator)) S390_lowcore.clock_comparator))
/* Serve timer interrupts first. */ /* Serve timer interrupts first. */
clock_comparator_work(); clock_comparator_work();
generic_handle_irq(irq); generic_handle_irq(irq);
}
static int on_async_stack(void)
{
unsigned long frame = current_frame_address();
return !!!((S390_lowcore.async_stack - frame) >> (PAGE_SHIFT + THREAD_SIZE_ORDER));
}
static void do_irq_async(struct pt_regs *regs, int irq)
{
if (on_async_stack())
do_IRQ(regs, irq);
else
CALL_ON_STACK(do_IRQ, S390_lowcore.async_stack, 2, regs, irq);
}
static int irq_pending(struct pt_regs *regs)
{
int cc;
asm volatile("tpi 0\n"
"ipm %0" : "=d" (cc) : : "cc");
return cc >> 28;
}
void noinstr do_io_irq(struct pt_regs *regs)
{
irqentry_state_t state = irqentry_enter(regs);
struct pt_regs *old_regs = set_irq_regs(regs);
int from_idle;
irq_enter();
if (user_mode(regs))
update_timer_sys();
from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit;
if (from_idle)
account_idle_time_irq();
do {
memcpy(&regs->int_code, &S390_lowcore.subchannel_id, 12);
if (S390_lowcore.io_int_word & BIT(31))
do_irq_async(regs, THIN_INTERRUPT);
else
do_irq_async(regs, IO_INTERRUPT);
} while (MACHINE_IS_LPAR && irq_pending(regs));
irq_exit(); irq_exit();
set_irq_regs(old_regs); set_irq_regs(old_regs);
irqentry_exit(regs, state);
if (from_idle)
regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
}
void noinstr do_ext_irq(struct pt_regs *regs)
{
irqentry_state_t state = irqentry_enter(regs);
struct pt_regs *old_regs = set_irq_regs(regs);
int from_idle;
irq_enter();
if (user_mode(regs))
update_timer_sys();
memcpy(&regs->int_code, &S390_lowcore.ext_cpu_addr, 4);
regs->int_parm = S390_lowcore.ext_params;
regs->int_parm_long = *(unsigned long *)S390_lowcore.ext_params2;
from_idle = !user_mode(regs) && regs->psw.addr == (unsigned long)psw_idle_exit;
if (from_idle)
account_idle_time_irq();
do_irq_async(regs, EXT_INTERRUPT);
irq_exit();
set_irq_regs(old_regs);
irqentry_exit(regs, state);
if (from_idle)
regs->psw.mask &= ~(PSW_MASK_EXT | PSW_MASK_IO | PSW_MASK_WAIT);
} }
static void show_msi_interrupt(struct seq_file *p, int irq) static void show_msi_interrupt(struct seq_file *p, int irq)

View File

@@ -131,12 +131,11 @@ static notrace void s390_handle_damage(void)
NOKPROBE_SYMBOL(s390_handle_damage); NOKPROBE_SYMBOL(s390_handle_damage);
/* /*
* Main machine check handler function. Will be called with interrupts enabled * Main machine check handler function. Will be called with interrupts disabled
* or disabled and machine checks enabled or disabled. * and machine checks enabled.
*/ */
void s390_handle_mcck(void) void __s390_handle_mcck(void)
{ {
unsigned long flags;
struct mcck_struct mcck; struct mcck_struct mcck;
/* /*
@@ -144,12 +143,10 @@ void s390_handle_mcck(void)
* machine checks. Afterwards delete the old state and enable machine * machine checks. Afterwards delete the old state and enable machine
* checks again. * checks again.
*/ */
local_irq_save(flags);
local_mcck_disable(); local_mcck_disable();
mcck = *this_cpu_ptr(&cpu_mcck); mcck = *this_cpu_ptr(&cpu_mcck);
memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck)); memset(this_cpu_ptr(&cpu_mcck), 0, sizeof(mcck));
local_mcck_enable(); local_mcck_enable();
local_irq_restore(flags);
if (mcck.channel_report) if (mcck.channel_report)
crw_handle_channel_report(); crw_handle_channel_report();
@@ -181,8 +178,13 @@ void s390_handle_mcck(void)
do_exit(SIGSEGV); do_exit(SIGSEGV);
} }
} }
EXPORT_SYMBOL_GPL(s390_handle_mcck);
void noinstr s390_handle_mcck(void)
{
trace_hardirqs_off();
__s390_handle_mcck();
trace_hardirqs_on();
}
/* /*
* returns 0 if all required registers are available * returns 0 if all required registers are available
* returns 1 otherwise * returns 1 otherwise
@@ -344,6 +346,9 @@ int notrace s390_do_machine_check(struct pt_regs *regs)
int mcck_pending = 0; int mcck_pending = 0;
nmi_enter(); nmi_enter();
if (user_mode(regs))
update_timer_mcck();
inc_irq_stat(NMI_NMI); inc_irq_stat(NMI_NMI);
mci.val = S390_lowcore.mcck_interruption_code; mci.val = S390_lowcore.mcck_interruption_code;
mcck = this_cpu_ptr(&cpu_mcck); mcck = this_cpu_ptr(&cpu_mcck);

View File

@@ -118,8 +118,8 @@ static void cf_diag_trailer(struct cf_trailer_entry *te)
if (te->cpu_speed) if (te->cpu_speed)
te->speed = 1; te->speed = 1;
te->clock_base = 1; /* Save clock base */ te->clock_base = 1; /* Save clock base */
memcpy(&te->tod_base, &tod_clock_base[1], 8); te->tod_base = tod_clock_base.tod;
store_tod_clock((__u64 *)&te->timestamp); te->timestamp = get_tod_clock_fast();
} }
/* /*

View File

@@ -1682,7 +1682,7 @@ static void aux_sdb_init(unsigned long sdb)
/* Save clock base */ /* Save clock base */
te->clock_base = 1; te->clock_base = 1;
memcpy(&te->progusage2, &tod_clock_base[1], 8); te->progusage2 = tod_clock_base.tod;
} }
/* /*

View File

@@ -29,6 +29,7 @@
#include <linux/random.h> #include <linux/random.h>
#include <linux/export.h> #include <linux/export.h>
#include <linux/init_task.h> #include <linux/init_task.h>
#include <linux/entry-common.h>
#include <asm/cpu_mf.h> #include <asm/cpu_mf.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/processor.h> #include <asm/processor.h>
@@ -43,9 +44,22 @@
#include <asm/unwind.h> #include <asm/unwind.h>
#include "entry.h" #include "entry.h"
asmlinkage void ret_from_fork(void) asm ("ret_from_fork"); void ret_from_fork(void) asm("ret_from_fork");
extern void kernel_thread_starter(void); void __ret_from_fork(struct task_struct *prev, struct pt_regs *regs)
{
void (*func)(void *arg);
schedule_tail(prev);
if (!user_mode(regs)) {
/* Kernel thread */
func = (void *)regs->gprs[9];
func((void *)regs->gprs[10]);
}
clear_pt_regs_flag(regs, PIF_SYSCALL);
syscall_exit_to_user_mode(regs);
}
void flush_thread(void) void flush_thread(void)
{ {
@@ -108,10 +122,12 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
p->thread.last_break = 1; p->thread.last_break = 1;
frame->sf.back_chain = 0; frame->sf.back_chain = 0;
frame->sf.gprs[5] = (unsigned long)frame + sizeof(struct stack_frame);
frame->sf.gprs[6] = (unsigned long)p;
/* new return point is ret_from_fork */ /* new return point is ret_from_fork */
frame->sf.gprs[8] = (unsigned long) ret_from_fork; frame->sf.gprs[8] = (unsigned long)ret_from_fork;
/* fake return stack for resume(), don't go back to schedule */ /* fake return stack for resume(), don't go back to schedule */
frame->sf.gprs[9] = (unsigned long) frame; frame->sf.gprs[9] = (unsigned long)frame;
/* Store access registers to kernel stack of new process. */ /* Store access registers to kernel stack of new process. */
if (unlikely(p->flags & PF_KTHREAD)) { if (unlikely(p->flags & PF_KTHREAD)) {
@@ -120,10 +136,10 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
frame->childregs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_DAT | frame->childregs.psw.mask = PSW_KERNEL_BITS | PSW_MASK_DAT |
PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK; PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK;
frame->childregs.psw.addr = frame->childregs.psw.addr =
(unsigned long) kernel_thread_starter; (unsigned long)__ret_from_fork;
frame->childregs.gprs[9] = new_stackp; /* function */ frame->childregs.gprs[9] = new_stackp; /* function */
frame->childregs.gprs[10] = arg; frame->childregs.gprs[10] = arg;
frame->childregs.gprs[11] = (unsigned long) do_exit; frame->childregs.gprs[11] = (unsigned long)do_exit;
frame->childregs.orig_gpr2 = -1; frame->childregs.orig_gpr2 = -1;
return 0; return 0;
@@ -153,7 +169,7 @@ int copy_thread(unsigned long clone_flags, unsigned long new_stackp,
return 0; return 0;
} }
asmlinkage void execve_tail(void) void execve_tail(void)
{ {
current->thread.fpu.fpc = 0; current->thread.fpu.fpc = 0;
asm volatile("sfpc %0" : : "d" (0)); asm volatile("sfpc %0" : : "d" (0));

View File

@@ -7,6 +7,7 @@
* Martin Schwidefsky (schwidefsky@de.ibm.com) * Martin Schwidefsky (schwidefsky@de.ibm.com)
*/ */
#include "asm/ptrace.h"
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/sched.h> #include <linux/sched.h>
#include <linux/sched/task_stack.h> #include <linux/sched/task_stack.h>
@@ -37,9 +38,6 @@
#include "compat_ptrace.h" #include "compat_ptrace.h"
#endif #endif
#define CREATE_TRACE_POINTS
#include <trace/events/syscalls.h>
void update_cr_regs(struct task_struct *task) void update_cr_regs(struct task_struct *task)
{ {
struct pt_regs *regs = task_pt_regs(task); struct pt_regs *regs = task_pt_regs(task);
@@ -140,7 +138,7 @@ void ptrace_disable(struct task_struct *task)
memset(&task->thread.per_user, 0, sizeof(task->thread.per_user)); memset(&task->thread.per_user, 0, sizeof(task->thread.per_user));
memset(&task->thread.per_event, 0, sizeof(task->thread.per_event)); 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_SINGLE_STEP);
clear_pt_regs_flag(task_pt_regs(task), PIF_PER_TRAP); clear_tsk_thread_flag(task, TIF_PER_TRAP);
task->thread.per_flags = 0; task->thread.per_flags = 0;
} }
@@ -322,25 +320,6 @@ static inline void __poke_user_per(struct task_struct *child,
child->thread.per_user.end = data; child->thread.per_user.end = data;
} }
static void fixup_int_code(struct task_struct *child, addr_t data)
{
struct pt_regs *regs = task_pt_regs(child);
int ilc = regs->int_code >> 16;
u16 insn;
if (ilc > 6)
return;
if (ptrace_access_vm(child, regs->psw.addr - (regs->int_code >> 16),
&insn, sizeof(insn), FOLL_FORCE) != sizeof(insn))
return;
/* double check that tracee stopped on svc instruction */
if ((insn >> 8) != 0xa)
return;
regs->int_code = 0x20000 | (data & 0xffff);
}
/* /*
* Write a word to the user area of a process at location addr. This * Write a word to the user area of a process at location addr. This
* operation does have an additional problem compared to peek_user. * operation does have an additional problem compared to peek_user.
@@ -374,10 +353,12 @@ static int __poke_user(struct task_struct *child, addr_t addr, addr_t data)
} }
if (test_pt_regs_flag(regs, PIF_SYSCALL) && if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
addr == offsetof(struct user, regs.gprs[2])) addr == offsetof(struct user, regs.gprs[2])) {
fixup_int_code(child, data); struct pt_regs *regs = task_pt_regs(child);
*(addr_t *)((addr_t) &regs->psw + addr) = data;
regs->int_code = 0x20000 | (data & 0xffff);
}
*(addr_t *)((addr_t) &regs->psw + addr) = data;
} else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) { } else if (addr < (addr_t) (&dummy->regs.orig_gpr2)) {
/* /*
* access registers are stored in the thread structure * access registers are stored in the thread structure
@@ -742,10 +723,12 @@ static int __poke_user_compat(struct task_struct *child,
regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) | regs->psw.mask = (regs->psw.mask & ~PSW_MASK_BA) |
(__u64)(tmp & PSW32_ADDR_AMODE); (__u64)(tmp & PSW32_ADDR_AMODE);
} else { } else {
if (test_pt_regs_flag(regs, PIF_SYSCALL) && if (test_pt_regs_flag(regs, PIF_SYSCALL) &&
addr == offsetof(struct compat_user, regs.gprs[2])) addr == offsetof(struct compat_user, regs.gprs[2])) {
fixup_int_code(child, data); struct pt_regs *regs = task_pt_regs(child);
regs->int_code = 0x20000 | (data & 0xffff);
}
/* gpr 0-15 */ /* gpr 0-15 */
*(__u32*)((addr_t) &regs->psw + addr*2 + 4) = tmp; *(__u32*)((addr_t) &regs->psw + addr*2 + 4) = tmp;
} }
@@ -862,82 +845,6 @@ long compat_arch_ptrace(struct task_struct *child, compat_long_t request,
} }
#endif #endif
asmlinkage long do_syscall_trace_enter(struct pt_regs *regs)
{
unsigned long mask = -1UL;
long ret = -1;
if (is_compat_task())
mask = 0xffffffff;
/*
* The sysc_tracesys code in entry.S stored the system
* call number to gprs[2].
*/
if (test_thread_flag(TIF_SYSCALL_TRACE) &&
tracehook_report_syscall_entry(regs)) {
/*
* Tracing decided this syscall should not happen. Skip
* the system call and the system call restart handling.
*/
goto skip;
}
#ifdef CONFIG_SECCOMP
/* Do the secure computing check after ptrace. */
if (unlikely(test_thread_flag(TIF_SECCOMP))) {
struct seccomp_data sd;
if (is_compat_task()) {
sd.instruction_pointer = regs->psw.addr & 0x7fffffff;
sd.arch = AUDIT_ARCH_S390;
} else {
sd.instruction_pointer = regs->psw.addr;
sd.arch = AUDIT_ARCH_S390X;
}
sd.nr = regs->int_code & 0xffff;
sd.args[0] = regs->orig_gpr2 & mask;
sd.args[1] = regs->gprs[3] & mask;
sd.args[2] = regs->gprs[4] & mask;
sd.args[3] = regs->gprs[5] & mask;
sd.args[4] = regs->gprs[6] & mask;
sd.args[5] = regs->gprs[7] & mask;
if (__secure_computing(&sd) == -1)
goto skip;
}
#endif /* CONFIG_SECCOMP */
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_enter(regs, regs->int_code & 0xffff);
audit_syscall_entry(regs->int_code & 0xffff, regs->orig_gpr2 & mask,
regs->gprs[3] &mask, regs->gprs[4] &mask,
regs->gprs[5] &mask);
if ((signed long)regs->gprs[2] >= NR_syscalls) {
regs->gprs[2] = -ENOSYS;
ret = -ENOSYS;
}
return regs->gprs[2];
skip:
clear_pt_regs_flag(regs, PIF_SYSCALL);
return ret;
}
asmlinkage void do_syscall_trace_exit(struct pt_regs *regs)
{
audit_syscall_exit(regs);
if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->gprs[2]);
if (test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, 0);
}
/* /*
* user_regset definitions. * user_regset definitions.
*/ */

View File

@@ -338,7 +338,7 @@ int __init arch_early_irq_init(void)
return 0; return 0;
} }
static int __init async_stack_realloc(void) static int __init stack_realloc(void)
{ {
unsigned long old, new; unsigned long old, new;
@@ -346,11 +346,18 @@ static int __init async_stack_realloc(void)
new = stack_alloc(); new = stack_alloc();
if (!new) if (!new)
panic("Couldn't allocate async stack"); panic("Couldn't allocate async stack");
S390_lowcore.async_stack = new + STACK_INIT_OFFSET; WRITE_ONCE(S390_lowcore.async_stack, new + STACK_INIT_OFFSET);
free_pages(old, THREAD_SIZE_ORDER); free_pages(old, THREAD_SIZE_ORDER);
old = S390_lowcore.mcck_stack - STACK_INIT_OFFSET;
new = stack_alloc();
if (!new)
panic("Couldn't allocate machine check stack");
WRITE_ONCE(S390_lowcore.mcck_stack, new + STACK_INIT_OFFSET);
memblock_free(old, THREAD_SIZE);
return 0; return 0;
} }
early_initcall(async_stack_realloc); early_initcall(stack_realloc);
void __init arch_call_rest_init(void) void __init arch_call_rest_init(void)
{ {
@@ -372,6 +379,7 @@ void __init arch_call_rest_init(void)
static void __init setup_lowcore_dat_off(void) static void __init setup_lowcore_dat_off(void)
{ {
unsigned long int_psw_mask = PSW_KERNEL_BITS; unsigned long int_psw_mask = PSW_KERNEL_BITS;
unsigned long mcck_stack;
struct lowcore *lc; struct lowcore *lc;
if (IS_ENABLED(CONFIG_KASAN)) if (IS_ENABLED(CONFIG_KASAN))
@@ -411,8 +419,7 @@ static void __init setup_lowcore_dat_off(void)
memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list, memcpy(lc->alt_stfle_fac_list, S390_lowcore.alt_stfle_fac_list,
sizeof(lc->alt_stfle_fac_list)); sizeof(lc->alt_stfle_fac_list));
nmi_alloc_boot_cpu(lc); nmi_alloc_boot_cpu(lc);
lc->sync_enter_timer = S390_lowcore.sync_enter_timer; lc->sys_enter_timer = S390_lowcore.sys_enter_timer;
lc->async_enter_timer = S390_lowcore.async_enter_timer;
lc->exit_timer = S390_lowcore.exit_timer; lc->exit_timer = S390_lowcore.exit_timer;
lc->user_timer = S390_lowcore.user_timer; lc->user_timer = S390_lowcore.user_timer;
lc->system_timer = S390_lowcore.system_timer; lc->system_timer = S390_lowcore.system_timer;
@@ -440,6 +447,12 @@ static void __init setup_lowcore_dat_off(void)
lc->restart_data = 0; lc->restart_data = 0;
lc->restart_source = -1UL; lc->restart_source = -1UL;
mcck_stack = (unsigned long)memblock_alloc(THREAD_SIZE, THREAD_SIZE);
if (!mcck_stack)
panic("%s: Failed to allocate %lu bytes align=0x%lx\n",
__func__, THREAD_SIZE, THREAD_SIZE);
lc->mcck_stack = mcck_stack + STACK_INIT_OFFSET;
/* Setup absolute zero lowcore */ /* Setup absolute zero lowcore */
mem_assign_absolute(S390_lowcore.restart_stack, lc->restart_stack); mem_assign_absolute(S390_lowcore.restart_stack, lc->restart_stack);
mem_assign_absolute(S390_lowcore.restart_fn, lc->restart_fn); mem_assign_absolute(S390_lowcore.restart_fn, lc->restart_fn);

View File

@@ -16,6 +16,7 @@
#include <linux/smp.h> #include <linux/smp.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/signal.h> #include <linux/signal.h>
#include <linux/entry-common.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/ptrace.h> #include <linux/ptrace.h>
@@ -170,6 +171,7 @@ static int restore_sigregs(struct pt_regs *regs, _sigregs __user *sregs)
fpregs_load(&user_sregs.fpregs, &current->thread.fpu); fpregs_load(&user_sregs.fpregs, &current->thread.fpu);
clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */ clear_pt_regs_flag(regs, PIF_SYSCALL); /* No longer in a system call */
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
return 0; return 0;
} }
@@ -459,7 +461,8 @@ static void handle_signal(struct ksignal *ksig, sigset_t *oldset,
* the kernel can handle, and then we build all the user-level signal handling * the kernel can handle, and then we build all the user-level signal handling
* stack-frames in one go after that. * stack-frames in one go after that.
*/ */
void do_signal(struct pt_regs *regs)
void arch_do_signal_or_restart(struct pt_regs *regs, bool has_signal)
{ {
struct ksignal ksig; struct ksignal ksig;
sigset_t *oldset = sigmask_to_save(); sigset_t *oldset = sigmask_to_save();
@@ -472,7 +475,7 @@ void do_signal(struct pt_regs *regs)
current->thread.system_call = current->thread.system_call =
test_pt_regs_flag(regs, PIF_SYSCALL) ? regs->int_code : 0; test_pt_regs_flag(regs, PIF_SYSCALL) ? regs->int_code : 0;
if (test_thread_flag(TIF_SIGPENDING) && get_signal(&ksig)) { if (has_signal && get_signal(&ksig)) {
/* Whee! Actually deliver the signal. */ /* Whee! Actually deliver the signal. */
if (current->thread.system_call) { if (current->thread.system_call) {
regs->int_code = current->thread.system_call; regs->int_code = current->thread.system_call;
@@ -498,6 +501,7 @@ void do_signal(struct pt_regs *regs)
} }
/* No longer in a system call */ /* No longer in a system call */
clear_pt_regs_flag(regs, PIF_SYSCALL); clear_pt_regs_flag(regs, PIF_SYSCALL);
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
rseq_signal_deliver(&ksig, regs); rseq_signal_deliver(&ksig, regs);
if (is_compat_task()) if (is_compat_task())
handle_signal32(&ksig, oldset, regs); handle_signal32(&ksig, oldset, regs);
@@ -508,6 +512,7 @@ void do_signal(struct pt_regs *regs)
/* No handlers present - check for system call restart */ /* No handlers present - check for system call restart */
clear_pt_regs_flag(regs, PIF_SYSCALL); clear_pt_regs_flag(regs, PIF_SYSCALL);
clear_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
if (current->thread.system_call) { if (current->thread.system_call) {
regs->int_code = current->thread.system_call; regs->int_code = current->thread.system_call;
switch (regs->gprs[2]) { switch (regs->gprs[2]) {
@@ -520,9 +525,9 @@ void do_signal(struct pt_regs *regs)
case -ERESTARTNOINTR: case -ERESTARTNOINTR:
/* Restart system call with magic TIF bit. */ /* Restart system call with magic TIF bit. */
regs->gprs[2] = regs->orig_gpr2; regs->gprs[2] = regs->orig_gpr2;
set_pt_regs_flag(regs, PIF_SYSCALL); set_pt_regs_flag(regs, PIF_SYSCALL_RESTART);
if (test_thread_flag(TIF_SINGLE_STEP)) if (test_thread_flag(TIF_SINGLE_STEP))
clear_pt_regs_flag(regs, PIF_PER_TRAP); clear_thread_flag(TIF_PER_TRAP);
break; break;
} }
} }

View File

@@ -189,7 +189,7 @@ static void pcpu_ec_call(struct pcpu *pcpu, int ec_bit)
static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu) static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
{ {
unsigned long async_stack, nodat_stack; unsigned long async_stack, nodat_stack, mcck_stack;
struct lowcore *lc; struct lowcore *lc;
if (pcpu != &pcpu_devices[0]) { if (pcpu != &pcpu_devices[0]) {
@@ -202,13 +202,15 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
nodat_stack = pcpu->lowcore->nodat_stack - STACK_INIT_OFFSET; nodat_stack = pcpu->lowcore->nodat_stack - STACK_INIT_OFFSET;
} }
async_stack = stack_alloc(); async_stack = stack_alloc();
if (!async_stack) mcck_stack = stack_alloc();
goto out; if (!async_stack || !mcck_stack)
goto out_stack;
lc = pcpu->lowcore; lc = pcpu->lowcore;
memcpy(lc, &S390_lowcore, 512); memcpy(lc, &S390_lowcore, 512);
memset((char *) lc + 512, 0, sizeof(*lc) - 512); memset((char *) lc + 512, 0, sizeof(*lc) - 512);
lc->async_stack = async_stack + STACK_INIT_OFFSET; lc->async_stack = async_stack + STACK_INIT_OFFSET;
lc->nodat_stack = nodat_stack + STACK_INIT_OFFSET; lc->nodat_stack = nodat_stack + STACK_INIT_OFFSET;
lc->mcck_stack = mcck_stack + STACK_INIT_OFFSET;
lc->cpu_nr = cpu; lc->cpu_nr = cpu;
lc->spinlock_lockval = arch_spin_lockval(cpu); lc->spinlock_lockval = arch_spin_lockval(cpu);
lc->spinlock_index = 0; lc->spinlock_index = 0;
@@ -216,12 +218,13 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW); lc->return_lpswe = gen_lpswe(__LC_RETURN_PSW);
lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW); lc->return_mcck_lpswe = gen_lpswe(__LC_RETURN_MCCK_PSW);
if (nmi_alloc_per_cpu(lc)) if (nmi_alloc_per_cpu(lc))
goto out_async; goto out_stack;
lowcore_ptr[cpu] = lc; lowcore_ptr[cpu] = lc;
pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, (u32)(unsigned long) lc); pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, (u32)(unsigned long) lc);
return 0; return 0;
out_async: out_stack:
stack_free(mcck_stack);
stack_free(async_stack); stack_free(async_stack);
out: out:
if (pcpu != &pcpu_devices[0]) { if (pcpu != &pcpu_devices[0]) {
@@ -233,16 +236,18 @@ out:
static void pcpu_free_lowcore(struct pcpu *pcpu) static void pcpu_free_lowcore(struct pcpu *pcpu)
{ {
unsigned long async_stack, nodat_stack, lowcore; unsigned long async_stack, nodat_stack, mcck_stack, lowcore;
nodat_stack = pcpu->lowcore->nodat_stack - STACK_INIT_OFFSET; nodat_stack = pcpu->lowcore->nodat_stack - STACK_INIT_OFFSET;
async_stack = pcpu->lowcore->async_stack - STACK_INIT_OFFSET; async_stack = pcpu->lowcore->async_stack - STACK_INIT_OFFSET;
mcck_stack = pcpu->lowcore->mcck_stack - STACK_INIT_OFFSET;
lowcore = (unsigned long) pcpu->lowcore; lowcore = (unsigned long) pcpu->lowcore;
pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, 0); pcpu_sigp_retry(pcpu, SIGP_SET_PREFIX, 0);
lowcore_ptr[pcpu - pcpu_devices] = NULL; lowcore_ptr[pcpu - pcpu_devices] = NULL;
nmi_free_per_cpu(pcpu->lowcore); nmi_free_per_cpu(pcpu->lowcore);
stack_free(async_stack); stack_free(async_stack);
stack_free(mcck_stack);
if (pcpu == &pcpu_devices[0]) if (pcpu == &pcpu_devices[0])
return; return;
free_pages(nodat_stack, THREAD_SIZE_ORDER); free_pages(nodat_stack, THREAD_SIZE_ORDER);
@@ -499,7 +504,7 @@ static void smp_handle_ext_call(void)
if (test_bit(ec_call_function_single, &bits)) if (test_bit(ec_call_function_single, &bits))
generic_smp_call_function_single_interrupt(); generic_smp_call_function_single_interrupt();
if (test_bit(ec_mcck_pending, &bits)) if (test_bit(ec_mcck_pending, &bits))
s390_handle_mcck(); __s390_handle_mcck();
} }
static void do_ext_call_interrupt(struct ext_code ext_code, static void do_ext_call_interrupt(struct ext_code ext_code,

View File

@@ -29,6 +29,13 @@
#include <linux/unistd.h> #include <linux/unistd.h>
#include <linux/ipc.h> #include <linux/ipc.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/string.h>
#include <linux/thread_info.h>
#include <linux/entry-common.h>
#include <asm/ptrace.h>
#include <asm/vtime.h>
#include "entry.h" #include "entry.h"
/* /*
@@ -100,3 +107,62 @@ SYSCALL_DEFINE0(ni_syscall)
{ {
return -ENOSYS; return -ENOSYS;
} }
void do_syscall(struct pt_regs *regs)
{
unsigned long nr;
nr = regs->int_code & 0xffff;
if (!nr) {
nr = regs->gprs[1] & 0xffff;
regs->int_code &= ~0xffffUL;
regs->int_code |= nr;
}
regs->gprs[2] = nr;
nr = syscall_enter_from_user_mode_work(regs, nr);
/*
* In the s390 ptrace ABI, both the syscall number and the return value
* use gpr2. However, userspace puts the syscall number either in the
* svc instruction itself, or uses gpr1. To make at least skipping syscalls
* work, the ptrace code sets PIF_SYSCALL_RET_SET, which is checked here
* and if set, the syscall will be skipped.
*/
if (!test_pt_regs_flag(regs, PIF_SYSCALL_RET_SET)) {
regs->gprs[2] = -ENOSYS;
if (likely(nr < NR_syscalls))
regs->gprs[2] = current->thread.sys_call_table[nr](regs);
} else {
clear_pt_regs_flag(regs, PIF_SYSCALL_RET_SET);
}
syscall_exit_to_user_mode_work(regs);
}
void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
{
enter_from_user_mode(regs);
memcpy(&regs->gprs[8], S390_lowcore.save_area_sync, 8 * sizeof(unsigned long));
memcpy(&regs->int_code, &S390_lowcore.svc_ilc, sizeof(regs->int_code));
regs->psw = S390_lowcore.svc_old_psw;
update_timer_sys();
local_irq_enable();
regs->orig_gpr2 = regs->gprs[2];
if (per_trap)
set_thread_flag(TIF_PER_TRAP);
for (;;) {
regs->flags = 0;
set_pt_regs_flag(regs, PIF_SYSCALL);
do_syscall(regs);
if (!test_pt_regs_flag(regs, PIF_SYSCALL_RESTART))
break;
local_irq_enable();
}
exit_to_user_mode();
}

View File

@@ -55,11 +55,7 @@
#include <asm/cio.h> #include <asm/cio.h>
#include "entry.h" #include "entry.h"
unsigned char tod_clock_base[16] __aligned(8) = { union tod_clock tod_clock_base __section(".data");
/* Force to data section. */
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
};
EXPORT_SYMBOL_GPL(tod_clock_base); EXPORT_SYMBOL_GPL(tod_clock_base);
u64 clock_comparator_max = -1ULL; u64 clock_comparator_max = -1ULL;
@@ -86,7 +82,7 @@ void __init time_early_init(void)
struct ptff_qui qui; struct ptff_qui qui;
/* Initialize TOD steering parameters */ /* Initialize TOD steering parameters */
tod_steering_end = *(unsigned long long *) &tod_clock_base[1]; tod_steering_end = tod_clock_base.tod;
vdso_data->arch_data.tod_steering_end = tod_steering_end; vdso_data->arch_data.tod_steering_end = tod_steering_end;
if (!test_facility(28)) if (!test_facility(28))
@@ -113,18 +109,13 @@ unsigned long long notrace sched_clock(void)
} }
NOKPROBE_SYMBOL(sched_clock); NOKPROBE_SYMBOL(sched_clock);
static void ext_to_timespec64(unsigned char *clk, struct timespec64 *xt) static void ext_to_timespec64(union tod_clock *clk, struct timespec64 *xt)
{ {
unsigned long long high, low, rem, sec, nsec; unsigned long rem, sec, nsec;
/* Split extendnd TOD clock to micro-seconds and sub-micro-seconds */ sec = clk->us;
high = (*(unsigned long long *) clk) >> 4;
low = (*(unsigned long long *)&clk[7]) << 4;
/* Calculate seconds and nano-seconds */
sec = high;
rem = do_div(sec, 1000000); rem = do_div(sec, 1000000);
nsec = (((low >> 32) + (rem << 32)) * 1000) >> 32; nsec = ((clk->sus + (rem << 12)) * 125) >> 9;
xt->tv_sec = sec; xt->tv_sec = sec;
xt->tv_nsec = nsec; xt->tv_nsec = nsec;
} }
@@ -204,30 +195,26 @@ static void stp_reset(void);
void read_persistent_clock64(struct timespec64 *ts) void read_persistent_clock64(struct timespec64 *ts)
{ {
unsigned char clk[STORE_CLOCK_EXT_SIZE]; union tod_clock clk;
__u64 delta; u64 delta;
delta = initial_leap_seconds + TOD_UNIX_EPOCH; delta = initial_leap_seconds + TOD_UNIX_EPOCH;
get_tod_clock_ext(clk); store_tod_clock_ext(&clk);
*(__u64 *) &clk[1] -= delta; clk.eitod -= delta;
if (*(__u64 *) &clk[1] > delta) ext_to_timespec64(&clk, ts);
clk[0]--;
ext_to_timespec64(clk, ts);
} }
void __init read_persistent_wall_and_boot_offset(struct timespec64 *wall_time, void __init read_persistent_wall_and_boot_offset(struct timespec64 *wall_time,
struct timespec64 *boot_offset) struct timespec64 *boot_offset)
{ {
unsigned char clk[STORE_CLOCK_EXT_SIZE];
struct timespec64 boot_time; struct timespec64 boot_time;
__u64 delta; union tod_clock clk;
u64 delta;
delta = initial_leap_seconds + TOD_UNIX_EPOCH; delta = initial_leap_seconds + TOD_UNIX_EPOCH;
memcpy(clk, tod_clock_base, STORE_CLOCK_EXT_SIZE); clk = tod_clock_base;
*(__u64 *)&clk[1] -= delta; clk.eitod -= delta;
if (*(__u64 *)&clk[1] > delta) ext_to_timespec64(&clk, &boot_time);
clk[0]--;
ext_to_timespec64(clk, &boot_time);
read_persistent_clock64(wall_time); read_persistent_clock64(wall_time);
*boot_offset = timespec64_sub(*wall_time, boot_time); *boot_offset = timespec64_sub(*wall_time, boot_time);
@@ -381,10 +368,7 @@ static void clock_sync_global(unsigned long long delta)
struct ptff_qto qto; struct ptff_qto qto;
/* Fixup the monotonic sched clock. */ /* Fixup the monotonic sched clock. */
*(unsigned long long *) &tod_clock_base[1] += delta; tod_clock_base.eitod += delta;
if (*(unsigned long long *) &tod_clock_base[1] < delta)
/* Epoch overflow */
tod_clock_base[0]++;
/* Adjust TOD steering parameters. */ /* Adjust TOD steering parameters. */
now = get_tod_clock(); now = get_tod_clock();
adj = tod_steering_end - now; adj = tod_steering_end - now;

View File

@@ -13,6 +13,8 @@
* 'Traps.c' handles hardware traps and faults after we have saved some * 'Traps.c' handles hardware traps and faults after we have saved some
* state in 'asm.s'. * state in 'asm.s'.
*/ */
#include "asm/irqflags.h"
#include "asm/ptrace.h"
#include <linux/kprobes.h> #include <linux/kprobes.h>
#include <linux/kdebug.h> #include <linux/kdebug.h>
#include <linux/extable.h> #include <linux/extable.h>
@@ -23,7 +25,9 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/uaccess.h> #include <linux/uaccess.h>
#include <linux/cpu.h> #include <linux/cpu.h>
#include <linux/entry-common.h>
#include <asm/fpu/api.h> #include <asm/fpu/api.h>
#include <asm/vtime.h>
#include "entry.h" #include "entry.h"
static inline void __user *get_trap_ip(struct pt_regs *regs) static inline void __user *get_trap_ip(struct pt_regs *regs)
@@ -288,3 +292,64 @@ void __init trap_init(void)
local_mcck_enable(); local_mcck_enable();
test_monitor_call(); test_monitor_call();
} }
void noinstr __do_pgm_check(struct pt_regs *regs)
{
unsigned long last_break = S390_lowcore.breaking_event_addr;
unsigned int trapnr, syscall_redirect = 0;
irqentry_state_t state;
regs->int_code = *(u32 *)&S390_lowcore.pgm_ilc;
regs->int_parm_long = S390_lowcore.trans_exc_code;
state = irqentry_enter(regs);
if (user_mode(regs)) {
update_timer_sys();
if (last_break < 4096)
last_break = 1;
current->thread.last_break = last_break;
regs->args[0] = last_break;
}
if (S390_lowcore.pgm_code & 0x0200) {
/* transaction abort */
memcpy(&current->thread.trap_tdb, &S390_lowcore.pgm_tdb, 256);
}
if (S390_lowcore.pgm_code & PGM_INT_CODE_PER) {
if (user_mode(regs)) {
struct per_event *ev = &current->thread.per_event;
set_thread_flag(TIF_PER_TRAP);
ev->address = S390_lowcore.per_address;
ev->cause = *(u16 *)&S390_lowcore.per_code;
ev->paid = S390_lowcore.per_access_id;
} else {
/* PER event in kernel is kprobes */
__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);
do_per_trap(regs);
goto out;
}
}
if (!irqs_disabled_flags(regs->psw.mask))
trace_hardirqs_on();
__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);
trapnr = regs->int_code & PGM_INT_CODE_MASK;
if (trapnr)
pgm_check_table[trapnr](regs);
syscall_redirect = user_mode(regs) && test_pt_regs_flag(regs, PIF_SYSCALL);
out:
local_irq_disable();
irqentry_exit(regs, state);
if (syscall_redirect) {
enter_from_user_mode(regs);
local_irq_enable();
regs->orig_gpr2 = regs->gprs[2];
do_syscall(regs);
exit_to_user_mode();
}
}

View File

@@ -32,7 +32,7 @@ int arch_uprobe_pre_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
return -EINVAL; return -EINVAL;
if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT) if (!is_compat_task() && psw_bits(regs->psw).eaba == PSW_BITS_AMODE_31BIT)
return -EINVAL; return -EINVAL;
clear_pt_regs_flag(regs, PIF_PER_TRAP); clear_thread_flag(TIF_PER_TRAP);
auprobe->saved_per = psw_bits(regs->psw).per; auprobe->saved_per = psw_bits(regs->psw).per;
auprobe->saved_int_code = regs->int_code; auprobe->saved_int_code = regs->int_code;
regs->int_code = UPROBE_TRAP_NR; regs->int_code = UPROBE_TRAP_NR;
@@ -103,7 +103,7 @@ int arch_uprobe_post_xol(struct arch_uprobe *auprobe, struct pt_regs *regs)
/* fix per address */ /* fix per address */
current->thread.per_event.address = utask->vaddr; current->thread.per_event.address = utask->vaddr;
/* trigger per event */ /* trigger per event */
set_pt_regs_flag(regs, PIF_PER_TRAP); set_thread_flag(TIF_PER_TRAP);
} }
return 0; return 0;
} }
@@ -259,7 +259,7 @@ static void sim_stor_event(struct pt_regs *regs, void *addr, int len)
return; return;
current->thread.per_event.address = regs->psw.addr; current->thread.per_event.address = regs->psw.addr;
current->thread.per_event.cause = PER_EVENT_STORE >> 16; current->thread.per_event.cause = PER_EVENT_STORE >> 16;
set_pt_regs_flag(regs, PIF_PER_TRAP); set_thread_flag(TIF_PER_TRAP);
} }
/* /*

View File

@@ -6,73 +6,39 @@
* Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com) * Author(s): Martin Schwidefsky (schwidefsky@de.ibm.com)
*/ */
#include <linux/init.h> #include <linux/binfmts.h>
#include <linux/compat.h>
#include <linux/elf.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/sched.h> #include <linux/init.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mm.h> #include <linux/mm.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/user.h> #include <linux/smp.h>
#include <linux/elf.h> #include <linux/time_namespace.h>
#include <linux/security.h>
#include <linux/memblock.h>
#include <linux/compat.h>
#include <linux/binfmts.h>
#include <vdso/datapage.h> #include <vdso/datapage.h>
#include <asm/asm-offsets.h>
#include <asm/processor.h>
#include <asm/mmu.h>
#include <asm/mmu_context.h>
#include <asm/sections.h>
#include <asm/vdso.h> #include <asm/vdso.h>
#include <asm/facility.h>
#include <asm/timex.h>
extern char vdso64_start, vdso64_end; extern char vdso64_start[], vdso64_end[];
static void *vdso64_kbase = &vdso64_start; static unsigned int vdso_pages;
static unsigned int vdso64_pages;
static struct page **vdso64_pagelist;
/* static struct vm_special_mapping vvar_mapping;
* Should the kernel map a VDSO page into processes and pass its
* address down to glibc upon exec()?
*/
unsigned int __read_mostly vdso_enabled = 1;
static vm_fault_t vdso_fault(const struct vm_special_mapping *sm, static union {
struct vm_area_struct *vma, struct vm_fault *vmf) struct vdso_data data[CS_BASES];
{ u8 page[PAGE_SIZE];
struct page **vdso_pagelist; } vdso_data_store __page_aligned_data;
unsigned long vdso_pages;
vdso_pagelist = vdso64_pagelist; struct vdso_data *vdso_data = vdso_data_store.data;
vdso_pages = vdso64_pages;
if (vmf->pgoff >= vdso_pages) enum vvar_pages {
return VM_FAULT_SIGBUS; VVAR_DATA_PAGE_OFFSET,
VVAR_TIMENS_PAGE_OFFSET,
vmf->page = vdso_pagelist[vmf->pgoff]; VVAR_NR_PAGES,
get_page(vmf->page);
return 0;
}
static int vdso_mremap(const struct vm_special_mapping *sm,
struct vm_area_struct *vma)
{
current->mm->context.vdso_base = vma->vm_start;
return 0;
}
static const struct vm_special_mapping vdso_mapping = {
.name = "[vdso]",
.fault = vdso_fault,
.mremap = vdso_mremap,
}; };
unsigned int __read_mostly vdso_enabled = 1;
static int __init vdso_setup(char *str) static int __init vdso_setup(char *str)
{ {
bool enabled; bool enabled;
@@ -83,109 +49,183 @@ static int __init vdso_setup(char *str)
} }
__setup("vdso=", vdso_setup); __setup("vdso=", vdso_setup);
/* #ifdef CONFIG_TIME_NS
* The vdso data page struct vdso_data *arch_get_vdso_data(void *vvar_page)
*/
static union {
struct vdso_data data;
u8 page[PAGE_SIZE];
} vdso_data_store __page_aligned_data;
struct vdso_data *vdso_data = (struct vdso_data *)&vdso_data_store.data;
void vdso_getcpu_init(void)
{ {
set_tod_programmable_field(smp_processor_id()); return (struct vdso_data *)(vvar_page);
}
static struct page *find_timens_vvar_page(struct vm_area_struct *vma)
{
if (likely(vma->vm_mm == current->mm))
return current->nsproxy->time_ns->vvar_page;
/*
* VM_PFNMAP | VM_IO protect .fault() handler from being called
* through interfaces like /proc/$pid/mem or
* process_vm_{readv,writev}() as long as there's no .access()
* in special_mapping_vmops().
* For more details check_vma_flags() and __access_remote_vm()
*/
WARN(1, "vvar_page accessed remotely");
return NULL;
} }
/* /*
* This is called from binfmt_elf, we create the special vma for the * The VVAR page layout depends on whether a task belongs to the root or
* vDSO and insert it into the mm struct tree * non-root time namespace. Whenever a task changes its namespace, the VVAR
* page tables are cleared and then they will be re-faulted with a
* corresponding layout.
* See also the comment near timens_setup_vdso_data() for details.
*/ */
int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
{
struct mm_struct *mm = task->mm;
struct vm_area_struct *vma;
mmap_read_lock(mm);
for (vma = mm->mmap; vma; vma = vma->vm_next) {
unsigned long size = vma->vm_end - vma->vm_start;
if (!vma_is_special_mapping(vma, &vvar_mapping))
continue;
zap_page_range(vma, vma->vm_start, size);
break;
}
mmap_read_unlock(mm);
return 0;
}
#else
static inline struct page *find_timens_vvar_page(struct vm_area_struct *vma)
{
return NULL;
}
#endif
static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
struct vm_area_struct *vma, struct vm_fault *vmf)
{
struct page *timens_page = find_timens_vvar_page(vma);
unsigned long addr, pfn;
vm_fault_t err;
switch (vmf->pgoff) {
case VVAR_DATA_PAGE_OFFSET:
pfn = virt_to_pfn(vdso_data);
if (timens_page) {
/*
* Fault in VVAR page too, since it will be accessed
* to get clock data anyway.
*/
addr = vmf->address + VVAR_TIMENS_PAGE_OFFSET * PAGE_SIZE;
err = vmf_insert_pfn(vma, addr, pfn);
if (unlikely(err & VM_FAULT_ERROR))
return err;
pfn = page_to_pfn(timens_page);
}
break;
#ifdef CONFIG_TIME_NS
case VVAR_TIMENS_PAGE_OFFSET:
/*
* If a task belongs to a time namespace then a namespace
* specific VVAR is mapped with the VVAR_DATA_PAGE_OFFSET and
* the real VVAR page is mapped with the VVAR_TIMENS_PAGE_OFFSET
* offset.
* See also the comment near timens_setup_vdso_data().
*/
if (!timens_page)
return VM_FAULT_SIGBUS;
pfn = virt_to_pfn(vdso_data);
break;
#endif /* CONFIG_TIME_NS */
default:
return VM_FAULT_SIGBUS;
}
return vmf_insert_pfn(vma, vmf->address, pfn);
}
static int vdso_mremap(const struct vm_special_mapping *sm,
struct vm_area_struct *vma)
{
current->mm->context.vdso_base = vma->vm_start;
return 0;
}
static struct vm_special_mapping vvar_mapping = {
.name = "[vvar]",
.fault = vvar_fault,
};
static struct vm_special_mapping vdso_mapping = {
.name = "[vdso]",
.mremap = vdso_mremap,
};
int vdso_getcpu_init(void)
{
set_tod_programmable_field(smp_processor_id());
return 0;
}
early_initcall(vdso_getcpu_init); /* Must be called before SMP init */
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp) int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
{ {
unsigned long vdso_text_len, vdso_mapping_len;
unsigned long vvar_start, vdso_text_start;
struct mm_struct *mm = current->mm; struct mm_struct *mm = current->mm;
struct vm_area_struct *vma; struct vm_area_struct *vma;
unsigned long vdso_pages;
unsigned long vdso_base;
int rc; int rc;
if (!vdso_enabled) BUILD_BUG_ON(VVAR_NR_PAGES != __VVAR_PAGES);
if (!vdso_enabled || is_compat_task())
return 0; return 0;
if (is_compat_task())
return 0;
vdso_pages = vdso64_pages;
/*
* vDSO has a problem and was disabled, just don't "enable" it for
* the process
*/
if (vdso_pages == 0)
return 0;
/*
* pick a base address for the vDSO in process space. We try to put
* it at vdso_base which is the "natural" base for it, but we might
* fail and end up putting it elsewhere.
*/
if (mmap_write_lock_killable(mm)) if (mmap_write_lock_killable(mm))
return -EINTR; return -EINTR;
vdso_base = get_unmapped_area(NULL, 0, vdso_pages << PAGE_SHIFT, 0, 0); vdso_text_len = vdso_pages << PAGE_SHIFT;
if (IS_ERR_VALUE(vdso_base)) { vdso_mapping_len = vdso_text_len + VVAR_NR_PAGES * PAGE_SIZE;
rc = vdso_base; vvar_start = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
goto out_up; rc = vvar_start;
} if (IS_ERR_VALUE(vvar_start))
goto out;
/* vma = _install_special_mapping(mm, vvar_start, VVAR_NR_PAGES*PAGE_SIZE,
* our vma flags don't have VM_WRITE so by default, the process VM_READ|VM_MAYREAD|VM_IO|VM_DONTDUMP|
* isn't allowed to write those pages. VM_PFNMAP,
* gdb can break that with ptrace interface, and thus trigger COW &vvar_mapping);
* on those pages but it's then your responsibility to never do that rc = PTR_ERR(vma);
* on the "data" page of the vDSO or you'll stop getting kernel if (IS_ERR(vma))
* updates and your nice userland gettimeofday will be totally dead. goto out;
* It's fine to use that for setting breakpoints in the vDSO code vdso_text_start = vvar_start + VVAR_NR_PAGES * PAGE_SIZE;
* pages though. /* VM_MAYWRITE for COW so gdb can set breakpoints */
*/ vma = _install_special_mapping(mm, vdso_text_start, vdso_text_len,
vma = _install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT,
VM_READ|VM_EXEC| VM_READ|VM_EXEC|
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
&vdso_mapping); &vdso_mapping);
if (IS_ERR(vma)) { if (IS_ERR(vma)) {
do_munmap(mm, vvar_start, PAGE_SIZE, NULL);
rc = PTR_ERR(vma); rc = PTR_ERR(vma);
goto out_up; } else {
current->mm->context.vdso_base = vdso_text_start;
rc = 0;
} }
out:
current->mm->context.vdso_base = vdso_base;
rc = 0;
out_up:
mmap_write_unlock(mm); mmap_write_unlock(mm);
return rc; return rc;
} }
static int __init vdso_init(void) static int __init vdso_init(void)
{ {
struct page **pages;
int i; int i;
vdso_getcpu_init(); vdso_pages = (vdso64_end - vdso64_start) >> PAGE_SHIFT;
/* Calculate the size of the 64 bit vDSO */ pages = kcalloc(vdso_pages + 1, sizeof(struct page *), GFP_KERNEL);
vdso64_pages = ((&vdso64_end - &vdso64_start if (!pages) {
+ PAGE_SIZE - 1) >> PAGE_SHIFT) + 1; vdso_enabled = 0;
return -ENOMEM;
/* Make sure pages are in the correct state */
vdso64_pagelist = kcalloc(vdso64_pages + 1, sizeof(struct page *),
GFP_KERNEL);
BUG_ON(vdso64_pagelist == NULL);
for (i = 0; i < vdso64_pages - 1; i++) {
struct page *pg = virt_to_page(vdso64_kbase + i*PAGE_SIZE);
get_page(pg);
vdso64_pagelist[i] = pg;
} }
vdso64_pagelist[vdso64_pages - 1] = virt_to_page(vdso_data); for (i = 0; i < vdso_pages; i++)
vdso64_pagelist[vdso64_pages] = NULL; pages[i] = virt_to_page(vdso64_start + i * PAGE_SIZE);
pages[vdso_pages] = NULL;
get_page(virt_to_page(vdso_data)); vdso_mapping.pages = pages;
return 0; return 0;
} }
early_initcall(vdso_init); arch_initcall(vdso_init);

View File

@@ -8,12 +8,12 @@
int __s390_vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused) int __s390_vdso_getcpu(unsigned *cpu, unsigned *node, struct getcpu_cache *unused)
{ {
__u16 todval[8]; union tod_clock clk;
/* CPU number is stored in the programmable field of the TOD clock */ /* CPU number is stored in the programmable field of the TOD clock */
get_tod_clock_ext((char *)todval); store_tod_clock_ext(&clk);
if (cpu) if (cpu)
*cpu = todval[7]; *cpu = clk.pf;
/* NUMA node is always zero */ /* NUMA node is always zero */
if (node) if (node)
*node = 0; *node = 0;

View File

@@ -13,6 +13,10 @@ ENTRY(_start)
SECTIONS SECTIONS
{ {
PROVIDE(_vdso_data = . - __VVAR_PAGES * PAGE_SIZE);
#ifdef CONFIG_TIME_NS
PROVIDE(_timens_data = _vdso_data + PAGE_SIZE);
#endif
. = VDSO64_LBASE + SIZEOF_HEADERS; . = VDSO64_LBASE + SIZEOF_HEADERS;
.hash : { *(.hash) } :text .hash : { *(.hash) } :text
@@ -94,9 +98,6 @@ SECTIONS
.debug_ranges 0 : { *(.debug_ranges) } .debug_ranges 0 : { *(.debug_ranges) }
.gnu.attributes 0 : { KEEP (*(.gnu.attributes)) } .gnu.attributes 0 : { KEEP (*(.gnu.attributes)) }
. = ALIGN(PAGE_SIZE);
PROVIDE(_vdso_data = .);
/DISCARD/ : { /DISCARD/ : {
*(.note.GNU-stack) *(.note.GNU-stack)
*(.branch_lt) *(.branch_lt)

View File

@@ -13,7 +13,7 @@
#include <linux/timex.h> #include <linux/timex.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/time.h> #include <linux/time.h>
#include <asm/alternative.h>
#include <asm/vtimer.h> #include <asm/vtimer.h>
#include <asm/vtime.h> #include <asm/vtime.h>
#include <asm/cpu_mf.h> #include <asm/cpu_mf.h>
@@ -128,15 +128,13 @@ static int do_account_vtime(struct task_struct *tsk)
timer = S390_lowcore.last_update_timer; timer = S390_lowcore.last_update_timer;
clock = S390_lowcore.last_update_clock; clock = S390_lowcore.last_update_clock;
asm volatile( /* Use STORE CLOCK by default, STORE CLOCK FAST if available. */
" stpt %0\n" /* Store current cpu timer value */ alternative_io("stpt %0\n .insn s,0xb2050000,%1\n",
#ifdef CONFIG_HAVE_MARCH_Z9_109_FEATURES "stpt %0\n .insn s,0xb27c0000,%1\n",
" stckf %1" /* Store current tod clock value */ 25,
#else ASM_OUTPUT2("=Q" (S390_lowcore.last_update_timer),
" stck %1" /* Store current tod clock value */ "=Q" (S390_lowcore.last_update_clock)),
#endif ASM_NO_INPUT_CLOBBER("cc"));
: "=Q" (S390_lowcore.last_update_timer),
"=Q" (S390_lowcore.last_update_clock));
clock = S390_lowcore.last_update_clock - clock; clock = S390_lowcore.last_update_clock - clock;
timer -= S390_lowcore.last_update_timer; timer -= S390_lowcore.last_update_timer;

View File

@@ -45,6 +45,7 @@
#include <asm/timex.h> #include <asm/timex.h>
#include <asm/ap.h> #include <asm/ap.h>
#include <asm/uv.h> #include <asm/uv.h>
#include <asm/fpu/api.h>
#include "kvm-s390.h" #include "kvm-s390.h"
#include "gaccess.h" #include "gaccess.h"
@@ -164,12 +165,6 @@ struct kvm_stats_debugfs_item debugfs_entries[] = {
{ NULL } { NULL }
}; };
struct kvm_s390_tod_clock_ext {
__u8 epoch_idx;
__u64 tod;
__u8 reserved[7];
} __packed;
/* allow nested virtualization in KVM (if enabled by user space) */ /* allow nested virtualization in KVM (if enabled by user space) */
static int nested; static int nested;
module_param(nested, int, S_IRUGO); module_param(nested, int, S_IRUGO);
@@ -1165,17 +1160,17 @@ static int kvm_s390_set_tod(struct kvm *kvm, struct kvm_device_attr *attr)
static void kvm_s390_get_tod_clock(struct kvm *kvm, static void kvm_s390_get_tod_clock(struct kvm *kvm,
struct kvm_s390_vm_tod_clock *gtod) struct kvm_s390_vm_tod_clock *gtod)
{ {
struct kvm_s390_tod_clock_ext htod; union tod_clock clk;
preempt_disable(); preempt_disable();
get_tod_clock_ext((char *)&htod); store_tod_clock_ext(&clk);
gtod->tod = htod.tod + kvm->arch.epoch; gtod->tod = clk.tod + kvm->arch.epoch;
gtod->epoch_idx = 0; gtod->epoch_idx = 0;
if (test_kvm_facility(kvm, 139)) { if (test_kvm_facility(kvm, 139)) {
gtod->epoch_idx = htod.epoch_idx + kvm->arch.epdx; gtod->epoch_idx = clk.ei + kvm->arch.epdx;
if (gtod->tod < htod.tod) if (gtod->tod < clk.tod)
gtod->epoch_idx += 1; gtod->epoch_idx += 1;
} }
@@ -3866,18 +3861,18 @@ void kvm_s390_set_tod_clock(struct kvm *kvm,
const struct kvm_s390_vm_tod_clock *gtod) const struct kvm_s390_vm_tod_clock *gtod)
{ {
struct kvm_vcpu *vcpu; struct kvm_vcpu *vcpu;
struct kvm_s390_tod_clock_ext htod; union tod_clock clk;
int i; int i;
mutex_lock(&kvm->lock); mutex_lock(&kvm->lock);
preempt_disable(); preempt_disable();
get_tod_clock_ext((char *)&htod); store_tod_clock_ext(&clk);
kvm->arch.epoch = gtod->tod - htod.tod; kvm->arch.epoch = gtod->tod - clk.tod;
kvm->arch.epdx = 0; kvm->arch.epdx = 0;
if (test_kvm_facility(kvm, 139)) { if (test_kvm_facility(kvm, 139)) {
kvm->arch.epdx = gtod->epoch_idx - htod.epoch_idx; kvm->arch.epdx = gtod->epoch_idx - clk.ei;
if (kvm->arch.epoch > gtod->tod) if (kvm->arch.epoch > gtod->tod)
kvm->arch.epdx -= 1; kvm->arch.epdx -= 1;
} }
@@ -4147,6 +4142,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
vcpu->run->s.regs.gprs, vcpu->run->s.regs.gprs,
sizeof(sie_page->pv_grregs)); sizeof(sie_page->pv_grregs));
} }
if (test_cpu_flag(CIF_FPU))
load_fpu_regs();
exit_reason = sie64a(vcpu->arch.sie_block, exit_reason = sie64a(vcpu->arch.sie_block,
vcpu->run->s.regs.gprs); vcpu->run->s.regs.gprs);
if (kvm_s390_pv_cpu_is_protected(vcpu)) { if (kvm_s390_pv_cpu_is_protected(vcpu)) {

View File

@@ -18,6 +18,7 @@
#include <asm/sclp.h> #include <asm/sclp.h>
#include <asm/nmi.h> #include <asm/nmi.h>
#include <asm/dis.h> #include <asm/dis.h>
#include <asm/fpu/api.h>
#include "kvm-s390.h" #include "kvm-s390.h"
#include "gaccess.h" #include "gaccess.h"
@@ -1028,6 +1029,8 @@ static int do_vsie_run(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page)
*/ */
vcpu->arch.sie_block->prog0c |= PROG_IN_SIE; vcpu->arch.sie_block->prog0c |= PROG_IN_SIE;
barrier(); barrier();
if (test_cpu_flag(CIF_FPU))
load_fpu_regs();
if (!kvm_s390_vcpu_sie_inhibited(vcpu)) if (!kvm_s390_vcpu_sie_inhibited(vcpu))
rc = sie64a(scb_s, vcpu->run->s.regs.gprs); rc = sie64a(scb_s, vcpu->run->s.regs.gprs);
barrier(); barrier();

View File

@@ -16,8 +16,8 @@
#include <asm/mmu_context.h> #include <asm/mmu_context.h>
#include <asm/facility.h> #include <asm/facility.h>
#ifdef CONFIG_DEBUG_USER_ASCE #ifdef CONFIG_DEBUG_ENTRY
void debug_user_asce(void) void debug_user_asce(int exit)
{ {
unsigned long cr1, cr7; unsigned long cr1, cr7;
@@ -25,12 +25,14 @@ void debug_user_asce(void)
__ctl_store(cr7, 7, 7); __ctl_store(cr7, 7, 7);
if (cr1 == S390_lowcore.kernel_asce && cr7 == S390_lowcore.user_asce) if (cr1 == S390_lowcore.kernel_asce && cr7 == S390_lowcore.user_asce)
return; return;
panic("incorrect ASCE on kernel exit\n" panic("incorrect ASCE on kernel %s\n"
"cr1: %016lx cr7: %016lx\n" "cr1: %016lx cr7: %016lx\n"
"kernel: %016llx user: %016llx\n", "kernel: %016llx user: %016llx\n",
cr1, cr7, S390_lowcore.kernel_asce, S390_lowcore.user_asce); exit ? "exit" : "entry", cr1, cr7,
S390_lowcore.kernel_asce, S390_lowcore.user_asce);
} }
#endif /*CONFIG_DEBUG_USER_ASCE */ #endif /*CONFIG_DEBUG_ENTRY */
#ifndef CONFIG_HAVE_MARCH_Z10_FEATURES #ifndef CONFIG_HAVE_MARCH_Z10_FEATURES
static DEFINE_STATIC_KEY_FALSE(have_mvcos); static DEFINE_STATIC_KEY_FALSE(have_mvcos);

View File

@@ -385,7 +385,7 @@ static inline vm_fault_t do_exception(struct pt_regs *regs, int access)
* The instruction that caused the program check has * The instruction that caused the program check has
* been nullified. Don't signal single step via SIGTRAP. * been nullified. Don't signal single step via SIGTRAP.
*/ */
clear_pt_regs_flag(regs, PIF_PER_TRAP); clear_thread_flag(TIF_PER_TRAP);
if (kprobe_page_fault(regs, 14)) if (kprobe_page_fault(regs, 14))
return 0; return 0;

View File

@@ -695,43 +695,68 @@ void zpci_remove_device(struct zpci_dev *zdev)
} }
} }
int zpci_create_device(struct zpci_dev *zdev) /**
* zpci_create_device() - Create a new zpci_dev and add it to the zbus
* @fid: Function ID of the device to be created
* @fh: Current Function Handle of the device to be created
* @state: Initial state after creation either Standby or Configured
*
* Creates a new zpci device and adds it to its, possibly newly created, zbus
* as well as zpci_list.
*
* Returns: 0 on success, an error value otherwise
*/
int zpci_create_device(u32 fid, u32 fh, enum zpci_state state)
{ {
struct zpci_dev *zdev;
int rc; int rc;
zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, state);
zdev = kzalloc(sizeof(*zdev), GFP_KERNEL);
if (!zdev)
return -ENOMEM;
/* FID and Function Handle are the static/dynamic identifiers */
zdev->fid = fid;
zdev->fh = fh;
/* Query function properties and update zdev */
rc = clp_query_pci_fn(zdev);
if (rc)
goto error;
zdev->state = state;
kref_init(&zdev->kref); kref_init(&zdev->kref);
mutex_init(&zdev->lock);
rc = zpci_init_iommu(zdev);
if (rc)
goto error;
if (zdev->state == ZPCI_FN_STATE_CONFIGURED) {
rc = zpci_enable_device(zdev);
if (rc)
goto error_destroy_iommu;
}
rc = zpci_bus_device_register(zdev, &pci_root_ops);
if (rc)
goto error_disable;
spin_lock(&zpci_list_lock); spin_lock(&zpci_list_lock);
list_add_tail(&zdev->entry, &zpci_list); list_add_tail(&zdev->entry, &zpci_list);
spin_unlock(&zpci_list_lock); spin_unlock(&zpci_list_lock);
rc = zpci_init_iommu(zdev);
if (rc)
goto out;
mutex_init(&zdev->lock);
if (zdev->state == ZPCI_FN_STATE_CONFIGURED) {
rc = zpci_enable_device(zdev);
if (rc)
goto out_destroy_iommu;
}
rc = zpci_bus_device_register(zdev, &pci_root_ops);
if (rc)
goto out_disable;
return 0; return 0;
out_disable: error_disable:
if (zdev->state == ZPCI_FN_STATE_ONLINE) if (zdev->state == ZPCI_FN_STATE_ONLINE)
zpci_disable_device(zdev); zpci_disable_device(zdev);
error_destroy_iommu:
out_destroy_iommu:
zpci_destroy_iommu(zdev); zpci_destroy_iommu(zdev);
out: error:
spin_lock(&zpci_list_lock); zpci_dbg(0, "add fid:%x, rc:%d\n", fid, rc);
list_del(&zdev->entry); kfree(zdev);
spin_unlock(&zpci_list_lock);
return rc; return rc;
} }

View File

@@ -181,7 +181,7 @@ static int clp_store_query_pci_fn(struct zpci_dev *zdev,
return 0; return 0;
} }
static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh) int clp_query_pci_fn(struct zpci_dev *zdev)
{ {
struct clp_req_rsp_query_pci *rrb; struct clp_req_rsp_query_pci *rrb;
int rc; int rc;
@@ -194,7 +194,7 @@ static int clp_query_pci_fn(struct zpci_dev *zdev, u32 fh)
rrb->request.hdr.len = sizeof(rrb->request); rrb->request.hdr.len = sizeof(rrb->request);
rrb->request.hdr.cmd = CLP_QUERY_PCI_FN; rrb->request.hdr.cmd = CLP_QUERY_PCI_FN;
rrb->response.hdr.len = sizeof(rrb->response); rrb->response.hdr.len = sizeof(rrb->response);
rrb->request.fh = fh; rrb->request.fh = zdev->fh;
rc = clp_req(rrb, CLP_LPS_PCI); rc = clp_req(rrb, CLP_LPS_PCI);
if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) { if (!rc && rrb->response.hdr.rsp == CLP_RC_OK) {
@@ -212,40 +212,6 @@ out:
return rc; return rc;
} }
int clp_add_pci_device(u32 fid, u32 fh, int configured)
{
struct zpci_dev *zdev;
int rc = -ENOMEM;
zpci_dbg(3, "add fid:%x, fh:%x, c:%d\n", fid, fh, configured);
zdev = kzalloc(sizeof(*zdev), GFP_KERNEL);
if (!zdev)
goto error;
zdev->fh = fh;
zdev->fid = fid;
/* Query function properties and update zdev */
rc = clp_query_pci_fn(zdev, fh);
if (rc)
goto error;
if (configured)
zdev->state = ZPCI_FN_STATE_CONFIGURED;
else
zdev->state = ZPCI_FN_STATE_STANDBY;
rc = zpci_create_device(zdev);
if (rc)
goto error;
return 0;
error:
zpci_dbg(0, "add fid:%x, rc:%d\n", fid, rc);
kfree(zdev);
return rc;
}
static int clp_refresh_fh(u32 fid); static int clp_refresh_fh(u32 fid);
/* /*
* Enable/Disable a given PCI function and update its function handle if * Enable/Disable a given PCI function and update its function handle if
@@ -408,7 +374,7 @@ static void __clp_add(struct clp_fh_list_entry *entry, void *data)
zdev = get_zdev_by_fid(entry->fid); zdev = get_zdev_by_fid(entry->fid);
if (!zdev) if (!zdev)
clp_add_pci_device(entry->fid, entry->fh, entry->config_state); zpci_create_device(entry->fid, entry->fh, entry->config_state);
} }
int clp_scan_pci_devices(void) int clp_scan_pci_devices(void)

View File

@@ -80,7 +80,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
enum zpci_state state; enum zpci_state state;
int ret; int ret;
if (zdev && zdev->zbus && zdev->zbus->bus) if (zdev && zdev->zbus->bus)
pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn); pdev = pci_get_slot(zdev->zbus->bus, zdev->devfn);
zpci_err("avail CCDF:\n"); zpci_err("avail CCDF:\n");
@@ -89,7 +89,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
switch (ccdf->pec) { switch (ccdf->pec) {
case 0x0301: /* Reserved|Standby -> Configured */ case 0x0301: /* Reserved|Standby -> Configured */
if (!zdev) { if (!zdev) {
ret = clp_add_pci_device(ccdf->fid, ccdf->fh, 1); zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_CONFIGURED);
break; break;
} }
/* the configuration request may be stale */ /* the configuration request may be stale */
@@ -116,7 +116,7 @@ static void __zpci_event_availability(struct zpci_ccdf_avail *ccdf)
break; break;
case 0x0302: /* Reserved -> Standby */ case 0x0302: /* Reserved -> Standby */
if (!zdev) { if (!zdev) {
clp_add_pci_device(ccdf->fid, ccdf->fh, 0); zpci_create_device(ccdf->fid, ccdf->fh, ZPCI_FN_STATE_STANDBY);
break; break;
} }
zdev->fh = ccdf->fh; zdev->fh = ccdf->fh;

View File

@@ -761,7 +761,7 @@ tape_3590_done(struct tape_device *device, struct tape_request *request)
* This function is called, when error recovery was successful * This function is called, when error recovery was successful
*/ */
static inline int static inline int
tape_3590_erp_succeded(struct tape_device *device, struct tape_request *request) tape_3590_erp_succeeded(struct tape_device *device, struct tape_request *request)
{ {
DBF_EVENT(3, "Error Recovery successful for %s\n", DBF_EVENT(3, "Error Recovery successful for %s\n",
tape_op_verbose[request->op]); tape_op_verbose[request->op]);
@@ -831,7 +831,7 @@ tape_3590_erp_basic(struct tape_device *device, struct tape_request *request,
case SENSE_BRA_PER: case SENSE_BRA_PER:
return tape_3590_erp_failed(device, request, irb, rc); return tape_3590_erp_failed(device, request, irb, rc);
case SENSE_BRA_CONT: case SENSE_BRA_CONT:
return tape_3590_erp_succeded(device, request); return tape_3590_erp_succeeded(device, request);
case SENSE_BRA_RE: case SENSE_BRA_RE:
return tape_3590_erp_retry(device, request, irb); return tape_3590_erp_retry(device, request, irb);
case SENSE_BRA_DRE: case SENSE_BRA_DRE:

View File

@@ -225,18 +225,23 @@ struct subchannel *css_alloc_subchannel(struct subchannel_id schid,
INIT_WORK(&sch->todo_work, css_sch_todo); INIT_WORK(&sch->todo_work, css_sch_todo);
sch->dev.release = &css_subchannel_release; sch->dev.release = &css_subchannel_release;
sch->dev.dma_mask = &sch->dma_mask;
device_initialize(&sch->dev); device_initialize(&sch->dev);
/* /*
* The physical addresses of some the dma structures that can * The physical addresses for some of the dma structures that can
* belong to a subchannel need to fit 31 bit width (e.g. ccw). * belong to a subchannel need to fit 31 bit width (e.g. ccw).
*/ */
sch->dev.coherent_dma_mask = DMA_BIT_MASK(31); ret = dma_set_coherent_mask(&sch->dev, DMA_BIT_MASK(31));
if (ret)
goto err;
/* /*
* But we don't have such restrictions imposed on the stuff that * But we don't have such restrictions imposed on the stuff that
* is handled by the streaming API. * is handled by the streaming API.
*/ */
sch->dma_mask = DMA_BIT_MASK(64); ret = dma_set_mask(&sch->dev, DMA_BIT_MASK(64));
sch->dev.dma_mask = &sch->dma_mask; if (ret)
goto err;
return sch; return sch;
err: err:
@@ -970,8 +975,11 @@ static int __init setup_css(int nr)
* css->device as the device argument with the DMA API) * css->device as the device argument with the DMA API)
* and are fine with 64 bit addresses. * and are fine with 64 bit addresses.
*/ */
css->device.coherent_dma_mask = DMA_BIT_MASK(64); ret = dma_coerce_mask_and_coherent(&css->device, DMA_BIT_MASK(64));
css->device.dma_mask = &css->device.coherent_dma_mask; if (ret) {
kfree(css);
goto out_err;
}
mutex_init(&css->mutex); mutex_init(&css->mutex);
ret = chsc_get_cssid_iid(nr, &css->cssid, &css->iid); ret = chsc_get_cssid_iid(nr, &css->cssid, &css->iid);

View File

@@ -621,14 +621,6 @@ static const struct attribute_group *ccwdev_attr_groups[] = {
NULL, NULL,
}; };
static int ccw_device_add(struct ccw_device *cdev)
{
struct device *dev = &cdev->dev;
dev->bus = &ccw_bus_type;
return device_add(dev);
}
static int match_dev_id(struct device *dev, const void *data) static int match_dev_id(struct device *dev, const void *data)
{ {
struct ccw_device *cdev = to_ccwdev(dev); struct ccw_device *cdev = to_ccwdev(dev);
@@ -687,33 +679,47 @@ static struct ccw_device * io_subchannel_allocate_dev(struct subchannel *sch)
{ {
struct ccw_device *cdev; struct ccw_device *cdev;
struct gen_pool *dma_pool; struct gen_pool *dma_pool;
int ret;
cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
if (!cdev) if (!cdev) {
ret = -ENOMEM;
goto err_cdev; goto err_cdev;
}
cdev->private = kzalloc(sizeof(struct ccw_device_private), cdev->private = kzalloc(sizeof(struct ccw_device_private),
GFP_KERNEL | GFP_DMA); GFP_KERNEL | GFP_DMA);
if (!cdev->private) if (!cdev->private) {
ret = -ENOMEM;
goto err_priv; goto err_priv;
cdev->dev.coherent_dma_mask = sch->dev.coherent_dma_mask; }
cdev->dev.dma_mask = sch->dev.dma_mask; cdev->dev.dma_mask = sch->dev.dma_mask;
ret = dma_set_coherent_mask(&cdev->dev, sch->dev.coherent_dma_mask);
if (ret)
goto err_coherent_mask;
dma_pool = cio_gp_dma_create(&cdev->dev, 1); dma_pool = cio_gp_dma_create(&cdev->dev, 1);
if (!dma_pool) if (!dma_pool) {
ret = -ENOMEM;
goto err_dma_pool; goto err_dma_pool;
}
cdev->private->dma_pool = dma_pool; cdev->private->dma_pool = dma_pool;
cdev->private->dma_area = cio_gp_dma_zalloc(dma_pool, &cdev->dev, cdev->private->dma_area = cio_gp_dma_zalloc(dma_pool, &cdev->dev,
sizeof(*cdev->private->dma_area)); sizeof(*cdev->private->dma_area));
if (!cdev->private->dma_area) if (!cdev->private->dma_area) {
ret = -ENOMEM;
goto err_dma_area; goto err_dma_area;
}
return cdev; return cdev;
err_dma_area: err_dma_area:
cio_gp_dma_destroy(dma_pool, &cdev->dev); cio_gp_dma_destroy(dma_pool, &cdev->dev);
err_dma_pool: err_dma_pool:
err_coherent_mask:
kfree(cdev->private); kfree(cdev->private);
err_priv: err_priv:
kfree(cdev); kfree(cdev);
err_cdev: err_cdev:
return ERR_PTR(-ENOMEM); return ERR_PTR(ret);
} }
static void ccw_device_todo(struct work_struct *work); static void ccw_device_todo(struct work_struct *work);
@@ -739,6 +745,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch,
cdev->ccwlock = sch->lock; cdev->ccwlock = sch->lock;
cdev->dev.parent = &sch->dev; cdev->dev.parent = &sch->dev;
cdev->dev.release = ccw_device_release; cdev->dev.release = ccw_device_release;
cdev->dev.bus = &ccw_bus_type;
cdev->dev.groups = ccwdev_attr_groups; cdev->dev.groups = ccwdev_attr_groups;
/* Do first half of device_register. */ /* Do first half of device_register. */
device_initialize(&cdev->dev); device_initialize(&cdev->dev);
@@ -840,7 +847,7 @@ static void io_subchannel_register(struct ccw_device *cdev)
kobject_uevent(&sch->dev.kobj, KOBJ_ADD); kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
} }
/* make it known to the system */ /* make it known to the system */
ret = ccw_device_add(cdev); ret = device_add(&cdev->dev);
if (ret) { if (ret) {
CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n", CIO_MSG_EVENT(0, "Could not register ccw dev 0.%x.%04x: %d\n",
cdev->private->dev_id.ssid, cdev->private->dev_id.ssid,
@@ -1052,7 +1059,7 @@ static int io_subchannel_probe(struct subchannel *sch)
kobject_uevent(&sch->dev.kobj, KOBJ_ADD); kobject_uevent(&sch->dev.kobj, KOBJ_ADD);
} }
cdev = sch_get_cdev(sch); cdev = sch_get_cdev(sch);
rc = ccw_device_add(cdev); rc = device_add(&cdev->dev);
if (rc) { if (rc) {
/* Release online reference. */ /* Release online reference. */
put_device(&cdev->dev); put_device(&cdev->dev);

View File

@@ -139,9 +139,6 @@ struct qdio_dev_perf_stat {
unsigned int qdio_int; unsigned int qdio_int;
unsigned int pci_request_int; unsigned int pci_request_int;
unsigned int tasklet_inbound;
unsigned int tasklet_inbound_resched;
unsigned int tasklet_inbound_resched2;
unsigned int tasklet_outbound; unsigned int tasklet_outbound;
unsigned int siga_read; unsigned int siga_read;
@@ -149,7 +146,6 @@ struct qdio_dev_perf_stat {
unsigned int siga_sync; unsigned int siga_sync;
unsigned int inbound_call; unsigned int inbound_call;
unsigned int inbound_handler;
unsigned int stop_polling; unsigned int stop_polling;
unsigned int inbound_queue_full; unsigned int inbound_queue_full;
unsigned int outbound_call; unsigned int outbound_call;
@@ -193,6 +189,8 @@ struct qdio_output_q {
struct qdio_outbuf_state *sbal_state; struct qdio_outbuf_state *sbal_state;
/* timer to check for more outbound work */ /* timer to check for more outbound work */
struct timer_list timer; struct timer_list timer;
/* tasklet to check for completions */
struct tasklet_struct tasklet;
}; };
/* /*
@@ -216,13 +214,9 @@ struct qdio_q {
/* number of buffers in use by the adapter */ /* number of buffers in use by the adapter */
atomic_t nr_buf_used; atomic_t nr_buf_used;
/* error condition during a data transfer */
unsigned int qdio_error;
/* last scan of the queue */ /* last scan of the queue */
u64 timestamp; u64 timestamp;
struct tasklet_struct tasklet;
struct qdio_queue_perf_stat q_stats; struct qdio_queue_perf_stat q_stats;
struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q] ____cacheline_aligned; struct qdio_buffer *sbal[QDIO_MAX_BUFFERS_PER_Q] ____cacheline_aligned;
@@ -254,6 +248,7 @@ struct qdio_irq {
struct ccw_device *cdev; struct ccw_device *cdev;
struct list_head entry; /* list of thinint devices */ struct list_head entry; /* list of thinint devices */
struct dentry *debugfs_dev; struct dentry *debugfs_dev;
u64 last_data_irq_time;
unsigned long int_parm; unsigned long int_parm;
struct subchannel_id schid; struct subchannel_id schid;
@@ -324,6 +319,14 @@ static inline int multicast_outbound(struct qdio_q *q)
(q->nr == q->irq_ptr->nr_output_qs - 1); (q->nr == q->irq_ptr->nr_output_qs - 1);
} }
static inline void qdio_deliver_irq(struct qdio_irq *irq)
{
if (!test_and_set_bit(QDIO_IRQ_DISABLED, &irq->poll_state))
irq->irq_poll(irq->cdev, irq->int_parm);
else
QDIO_PERF_STAT_INC(irq, int_discarded);
}
#define pci_out_supported(irq) ((irq)->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED) #define pci_out_supported(irq) ((irq)->qib.ac & QIB_AC_OUTBOUND_PCI_SUPPORTED)
#define is_qebsm(q) (q->irq_ptr->sch_token != 0) #define is_qebsm(q) (q->irq_ptr->sch_token != 0)
@@ -357,16 +360,12 @@ extern u64 last_ai_time;
/* prototypes for thin interrupt */ /* prototypes for thin interrupt */
int qdio_establish_thinint(struct qdio_irq *irq_ptr); int qdio_establish_thinint(struct qdio_irq *irq_ptr);
void qdio_shutdown_thinint(struct qdio_irq *irq_ptr); void qdio_shutdown_thinint(struct qdio_irq *irq_ptr);
void tiqdio_add_device(struct qdio_irq *irq_ptr);
void tiqdio_remove_device(struct qdio_irq *irq_ptr);
void tiqdio_inbound_processing(unsigned long q);
int qdio_thinint_init(void); int qdio_thinint_init(void);
void qdio_thinint_exit(void); void qdio_thinint_exit(void);
int test_nonshared_ind(struct qdio_irq *); int test_nonshared_ind(struct qdio_irq *);
/* prototypes for setup */ /* prototypes for setup */
void qdio_inbound_processing(unsigned long data); void qdio_outbound_tasklet(struct tasklet_struct *t);
void qdio_outbound_processing(unsigned long data);
void qdio_outbound_timer(struct timer_list *t); void qdio_outbound_timer(struct timer_list *t);
void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm, void qdio_int_handler(struct ccw_device *cdev, unsigned long intparm,
struct irb *irb); struct irb *irb);

View File

@@ -105,8 +105,9 @@ static int qstat_show(struct seq_file *m, void *v)
if (!q) if (!q)
return 0; return 0;
seq_printf(m, "Timestamp: %Lx Last AI: %Lx\n", seq_printf(m, "Timestamp: %llx\n", q->timestamp);
q->timestamp, last_ai_time); seq_printf(m, "Last Data IRQ: %llx Last AI: %llx\n",
q->irq_ptr->last_data_irq_time, last_ai_time);
seq_printf(m, "nr_used: %d ftc: %d\n", seq_printf(m, "nr_used: %d ftc: %d\n",
atomic_read(&q->nr_buf_used), q->first_to_check); atomic_read(&q->nr_buf_used), q->first_to_check);
if (q->is_input_q) { if (q->is_input_q) {
@@ -197,15 +198,11 @@ static char *qperf_names[] = {
"Assumed adapter interrupts", "Assumed adapter interrupts",
"QDIO interrupts", "QDIO interrupts",
"Requested PCIs", "Requested PCIs",
"Inbound tasklet runs",
"Inbound tasklet resched",
"Inbound tasklet resched2",
"Outbound tasklet runs", "Outbound tasklet runs",
"SIGA read", "SIGA read",
"SIGA write", "SIGA write",
"SIGA sync", "SIGA sync",
"Inbound calls", "Inbound calls",
"Inbound handler",
"Inbound stop_polling", "Inbound stop_polling",
"Inbound queue full", "Inbound queue full",
"Outbound calls", "Outbound calls",

View File

@@ -202,7 +202,7 @@ again:
*/ */
static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr, static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
unsigned char *state, unsigned int count, unsigned char *state, unsigned int count,
int auto_ack, int merge_pending) int auto_ack)
{ {
unsigned char __state = 0; unsigned char __state = 0;
int i = 1; int i = 1;
@@ -217,18 +217,9 @@ static inline int get_buf_states(struct qdio_q *q, unsigned int bufnr,
if (__state & SLSB_OWNER_CU) if (__state & SLSB_OWNER_CU)
goto out; goto out;
if (merge_pending && __state == SLSB_P_OUTPUT_PENDING)
__state = SLSB_P_OUTPUT_EMPTY;
for (; i < count; i++) { for (; i < count; i++) {
bufnr = next_buf(bufnr); bufnr = next_buf(bufnr);
/* merge PENDING into EMPTY: */
if (merge_pending &&
q->slsb.val[bufnr] == SLSB_P_OUTPUT_PENDING &&
__state == SLSB_P_OUTPUT_EMPTY)
continue;
/* stop if next state differs from initial state: */ /* stop if next state differs from initial state: */
if (q->slsb.val[bufnr] != __state) if (q->slsb.val[bufnr] != __state)
break; break;
@@ -242,7 +233,7 @@ out:
static inline int get_buf_state(struct qdio_q *q, unsigned int bufnr, static inline int get_buf_state(struct qdio_q *q, unsigned int bufnr,
unsigned char *state, int auto_ack) unsigned char *state, int auto_ack)
{ {
return get_buf_states(q, bufnr, state, 1, auto_ack, 0); return get_buf_states(q, bufnr, state, 1, auto_ack);
} }
/* wrap-around safe setting of slsb states, returns number of changed buffers */ /* wrap-around safe setting of slsb states, returns number of changed buffers */
@@ -420,8 +411,6 @@ static inline void account_sbals(struct qdio_q *q, unsigned int count)
static void process_buffer_error(struct qdio_q *q, unsigned int start, static void process_buffer_error(struct qdio_q *q, unsigned int start,
int count) int count)
{ {
q->qdio_error = QDIO_ERROR_SLSB_STATE;
/* special handling for no target buffer empty */ /* special handling for no target buffer empty */
if (queue_type(q) == QDIO_IQDIO_QFMT && !q->is_input_q && if (queue_type(q) == QDIO_IQDIO_QFMT && !q->is_input_q &&
q->sbal[start]->element[15].sflags == 0x10) { q->sbal[start]->element[15].sflags == 0x10) {
@@ -450,7 +439,8 @@ static inline void inbound_handle_work(struct qdio_q *q, unsigned int start,
q->u.in.batch_count += count; q->u.in.batch_count += count;
} }
static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start) static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start,
unsigned int *error)
{ {
unsigned char state = 0; unsigned char state = 0;
int count; int count;
@@ -465,7 +455,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start)
* No siga sync here, as a PCI or we after a thin interrupt * No siga sync here, as a PCI or we after a thin interrupt
* already sync'ed the queues. * already sync'ed the queues.
*/ */
count = get_buf_states(q, start, &state, count, 1, 0); count = get_buf_states(q, start, &state, count, 1);
if (!count) if (!count)
return 0; return 0;
@@ -484,6 +474,7 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start)
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in err:%1d %02x", q->nr, DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "in err:%1d %02x", q->nr,
count); count);
*error = QDIO_ERROR_SLSB_STATE;
process_buffer_error(q, start, count); process_buffer_error(q, start, count);
inbound_handle_work(q, start, count, false); inbound_handle_work(q, start, count, false);
if (atomic_sub_return(count, &q->nr_buf_used) == 0) if (atomic_sub_return(count, &q->nr_buf_used) == 0)
@@ -508,11 +499,6 @@ static int get_inbound_buffer_frontier(struct qdio_q *q, unsigned int start)
} }
} }
static int qdio_inbound_q_moved(struct qdio_q *q, unsigned int start)
{
return get_inbound_buffer_frontier(q, start);
}
static inline int qdio_inbound_q_done(struct qdio_q *q, unsigned int start) static inline int qdio_inbound_q_done(struct qdio_q *q, unsigned int start)
{ {
unsigned char state = 0; unsigned char state = 0;
@@ -546,96 +532,23 @@ static inline unsigned long qdio_aob_for_buffer(struct qdio_output_q *q,
WARN_ON_ONCE(phys_aob & 0xFF); WARN_ON_ONCE(phys_aob & 0xFF);
} }
q->sbal_state[bufnr].flags = 0;
return phys_aob; return phys_aob;
} }
static void qdio_kick_handler(struct qdio_q *q, unsigned int start,
unsigned int count)
{
if (unlikely(q->irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return;
if (q->is_input_q) {
qperf_inc(q, inbound_handler);
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "kih s:%02x c:%02x", start, count);
} else {
qperf_inc(q, outbound_handler);
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x",
start, count);
}
q->handler(q->irq_ptr->cdev, q->qdio_error, q->nr, start, count,
q->irq_ptr->int_parm);
/* for the next time */
q->qdio_error = 0;
}
static inline int qdio_tasklet_schedule(struct qdio_q *q) static inline int qdio_tasklet_schedule(struct qdio_q *q)
{ {
if (likely(q->irq_ptr->state == QDIO_IRQ_STATE_ACTIVE)) { if (likely(q->irq_ptr->state == QDIO_IRQ_STATE_ACTIVE)) {
tasklet_schedule(&q->tasklet); tasklet_schedule(&q->u.out.tasklet);
return 0; return 0;
} }
return -EPERM; return -EPERM;
} }
static void __qdio_inbound_processing(struct qdio_q *q) static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start,
{ unsigned int *error)
unsigned int start = q->first_to_check;
int count;
qperf_inc(q, tasklet_inbound);
count = qdio_inbound_q_moved(q, start);
if (count == 0)
return;
qdio_kick_handler(q, start, count);
start = add_buf(start, count);
q->first_to_check = start;
if (!qdio_inbound_q_done(q, start)) {
/* means poll time is not yet over */
qperf_inc(q, tasklet_inbound_resched);
if (!qdio_tasklet_schedule(q))
return;
}
qdio_stop_polling(q);
/*
* We need to check again to not lose initiative after
* resetting the ACK state.
*/
if (!qdio_inbound_q_done(q, start)) {
qperf_inc(q, tasklet_inbound_resched2);
qdio_tasklet_schedule(q);
}
}
void qdio_inbound_processing(unsigned long data)
{
struct qdio_q *q = (struct qdio_q *)data;
__qdio_inbound_processing(q);
}
static void qdio_check_pending(struct qdio_q *q, unsigned int index)
{
unsigned char state;
if (get_buf_state(q, index, &state, 0) > 0 &&
state == SLSB_P_OUTPUT_PENDING &&
q->u.out.aobs[index]) {
q->u.out.sbal_state[index].flags |=
QDIO_OUTBUF_STATE_FLAG_PENDING;
q->u.out.aobs[index] = NULL;
}
}
static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start)
{ {
unsigned char state = 0; unsigned char state = 0;
unsigned int i;
int count; int count;
q->timestamp = get_tod_clock_fast(); q->timestamp = get_tod_clock_fast();
@@ -651,13 +564,19 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start)
if (!count) if (!count)
return 0; return 0;
count = get_buf_states(q, start, &state, count, 0, q->u.out.use_cq); count = get_buf_states(q, start, &state, count, 0);
if (!count) if (!count)
return 0; return 0;
switch (state) { switch (state) {
case SLSB_P_OUTPUT_EMPTY:
case SLSB_P_OUTPUT_PENDING: case SLSB_P_OUTPUT_PENDING:
/* detach the utilized QAOBs: */
for (i = 0; i < count; i++)
q->u.out.aobs[QDIO_BUFNR(start + i)] = NULL;
*error = QDIO_ERROR_SLSB_PENDING;
fallthrough;
case SLSB_P_OUTPUT_EMPTY:
/* the adapter got it */ /* the adapter got it */
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, DBF_DEV_EVENT(DBF_INFO, q->irq_ptr,
"out empty:%1d %02x", q->nr, count); "out empty:%1d %02x", q->nr, count);
@@ -667,6 +586,10 @@ static int get_outbound_buffer_frontier(struct qdio_q *q, unsigned int start)
account_sbals(q, count); account_sbals(q, count);
return count; return count;
case SLSB_P_OUTPUT_ERROR: case SLSB_P_OUTPUT_ERROR:
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out error:%1d %02x",
q->nr, count);
*error = QDIO_ERROR_SLSB_STATE;
process_buffer_error(q, start, count); process_buffer_error(q, start, count);
atomic_sub(count, &q->nr_buf_used); atomic_sub(count, &q->nr_buf_used);
if (q->irq_ptr->perf_stat_enabled) if (q->irq_ptr->perf_stat_enabled)
@@ -697,26 +620,6 @@ static inline int qdio_outbound_q_done(struct qdio_q *q)
return atomic_read(&q->nr_buf_used) == 0; return atomic_read(&q->nr_buf_used) == 0;
} }
static inline int qdio_outbound_q_moved(struct qdio_q *q, unsigned int start)
{
int count;
count = get_outbound_buffer_frontier(q, start);
if (count) {
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "out moved:%1d", q->nr);
if (q->u.out.use_cq) {
unsigned int i;
for (i = 0; i < count; i++)
qdio_check_pending(q, QDIO_BUFNR(start + i));
}
}
return count;
}
static int qdio_kick_outbound_q(struct qdio_q *q, unsigned int count, static int qdio_kick_outbound_q(struct qdio_q *q, unsigned int count,
unsigned long aob) unsigned long aob)
{ {
@@ -760,18 +663,29 @@ retry:
return cc; return cc;
} }
static void __qdio_outbound_processing(struct qdio_q *q) void qdio_outbound_tasklet(struct tasklet_struct *t)
{ {
struct qdio_output_q *out_q = from_tasklet(out_q, t, tasklet);
struct qdio_q *q = container_of(out_q, struct qdio_q, u.out);
unsigned int start = q->first_to_check; unsigned int start = q->first_to_check;
unsigned int error = 0;
int count; int count;
qperf_inc(q, tasklet_outbound); qperf_inc(q, tasklet_outbound);
WARN_ON_ONCE(atomic_read(&q->nr_buf_used) < 0); WARN_ON_ONCE(atomic_read(&q->nr_buf_used) < 0);
count = qdio_outbound_q_moved(q, start); count = get_outbound_buffer_frontier(q, start, &error);
if (count) { if (count) {
q->first_to_check = add_buf(start, count); q->first_to_check = add_buf(start, count);
qdio_kick_handler(q, start, count);
if (q->irq_ptr->state == QDIO_IRQ_STATE_ACTIVE) {
qperf_inc(q, outbound_handler);
DBF_DEV_EVENT(DBF_INFO, q->irq_ptr, "koh: s:%02x c:%02x",
start, count);
q->handler(q->irq_ptr->cdev, error, q->nr, start,
count, q->irq_ptr->int_parm);
}
} }
if (queue_type(q) == QDIO_ZFCP_QFMT && !pci_out_supported(q->irq_ptr) && if (queue_type(q) == QDIO_ZFCP_QFMT && !pci_out_supported(q->irq_ptr) &&
@@ -798,13 +712,6 @@ sched:
qdio_tasklet_schedule(q); qdio_tasklet_schedule(q);
} }
/* outbound tasklet */
void qdio_outbound_processing(unsigned long data)
{
struct qdio_q *q = (struct qdio_q *)data;
__qdio_outbound_processing(q);
}
void qdio_outbound_timer(struct timer_list *t) void qdio_outbound_timer(struct timer_list *t)
{ {
struct qdio_q *q = from_timer(q, t, u.out.timer); struct qdio_q *q = from_timer(q, t, u.out.timer);
@@ -825,19 +732,6 @@ static inline void qdio_check_outbound_pci_queues(struct qdio_irq *irq)
qdio_tasklet_schedule(out); qdio_tasklet_schedule(out);
} }
void tiqdio_inbound_processing(unsigned long data)
{
struct qdio_q *q = (struct qdio_q *)data;
if (need_siga_sync(q) && need_siga_sync_after_ai(q))
qdio_sync_queues(q);
/* The interrupt could be caused by a PCI request: */
qdio_check_outbound_pci_queues(q->irq_ptr);
__qdio_inbound_processing(q);
}
static inline void qdio_set_state(struct qdio_irq *irq_ptr, static inline void qdio_set_state(struct qdio_irq *irq_ptr,
enum qdio_irq_states state) enum qdio_irq_states state)
{ {
@@ -865,15 +759,8 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
if (unlikely(irq_ptr->state != QDIO_IRQ_STATE_ACTIVE)) if (unlikely(irq_ptr->state != QDIO_IRQ_STATE_ACTIVE))
return; return;
if (irq_ptr->irq_poll) { qdio_deliver_irq(irq_ptr);
if (!test_and_set_bit(QDIO_IRQ_DISABLED, &irq_ptr->poll_state)) irq_ptr->last_data_irq_time = S390_lowcore.int_clock;
irq_ptr->irq_poll(irq_ptr->cdev, irq_ptr->int_parm);
else
QDIO_PERF_STAT_INC(irq_ptr, int_discarded);
} else {
for_each_input_queue(irq_ptr, q, i)
tasklet_schedule(&q->tasklet);
}
if (!pci_out_supported(irq_ptr) || !irq_ptr->scan_threshold) if (!pci_out_supported(irq_ptr) || !irq_ptr->scan_threshold)
return; return;
@@ -1016,12 +903,9 @@ static void qdio_shutdown_queues(struct qdio_irq *irq_ptr)
struct qdio_q *q; struct qdio_q *q;
int i; int i;
for_each_input_queue(irq_ptr, q, i)
tasklet_kill(&q->tasklet);
for_each_output_queue(irq_ptr, q, i) { for_each_output_queue(irq_ptr, q, i) {
del_timer_sync(&q->u.out.timer); del_timer_sync(&q->u.out.timer);
tasklet_kill(&q->tasklet); tasklet_kill(&q->u.out.tasklet);
} }
} }
@@ -1059,7 +943,6 @@ int qdio_shutdown(struct ccw_device *cdev, int how)
*/ */
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_STOPPED);
tiqdio_remove_device(irq_ptr);
qdio_shutdown_queues(irq_ptr); qdio_shutdown_queues(irq_ptr);
qdio_shutdown_debug_entries(irq_ptr); qdio_shutdown_debug_entries(irq_ptr);
@@ -1177,7 +1060,6 @@ int qdio_allocate(struct ccw_device *cdev, unsigned int no_input_qs,
if (rc) if (rc)
goto err_queues; goto err_queues;
INIT_LIST_HEAD(&irq_ptr->entry);
cdev->private->qdio_data = irq_ptr; cdev->private->qdio_data = irq_ptr;
qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE); qdio_set_state(irq_ptr, QDIO_IRQ_STATE_INACTIVE);
return 0; return 0;
@@ -1263,6 +1145,9 @@ int qdio_establish(struct ccw_device *cdev,
!init_data->output_sbal_addr_array) !init_data->output_sbal_addr_array)
return -EINVAL; return -EINVAL;
if (!init_data->irq_poll)
return -EINVAL;
mutex_lock(&irq_ptr->setup_mutex); mutex_lock(&irq_ptr->setup_mutex);
qdio_trace_init_data(irq_ptr, init_data); qdio_trace_init_data(irq_ptr, init_data);
qdio_setup_irq(irq_ptr, init_data); qdio_setup_irq(irq_ptr, init_data);
@@ -1357,9 +1242,6 @@ int qdio_activate(struct ccw_device *cdev)
goto out; goto out;
} }
if (is_thinint_irq(irq_ptr))
tiqdio_add_device(irq_ptr);
/* wait for subchannel to become active */ /* wait for subchannel to become active */
msleep(5); msleep(5);
@@ -1557,17 +1439,16 @@ static int __qdio_inspect_queue(struct qdio_q *q, unsigned int *bufnr,
unsigned int start = q->first_to_check; unsigned int start = q->first_to_check;
int count; int count;
count = q->is_input_q ? qdio_inbound_q_moved(q, start) : *error = 0;
qdio_outbound_q_moved(q, start); count = q->is_input_q ? get_inbound_buffer_frontier(q, start, error) :
get_outbound_buffer_frontier(q, start, error);
if (count == 0) if (count == 0)
return 0; return 0;
*bufnr = start; *bufnr = start;
*error = q->qdio_error;
/* for the next time */ /* for the next time */
q->first_to_check = add_buf(start, count); q->first_to_check = add_buf(start, count);
q->qdio_error = 0;
return count; return count;
} }

View File

@@ -259,14 +259,6 @@ static void setup_queues(struct qdio_irq *irq_ptr,
setup_storage_lists(q, irq_ptr, setup_storage_lists(q, irq_ptr,
qdio_init->input_sbal_addr_array[i], i); qdio_init->input_sbal_addr_array[i], i);
if (is_thinint_irq(irq_ptr)) {
tasklet_init(&q->tasklet, tiqdio_inbound_processing,
(unsigned long) q);
} else {
tasklet_init(&q->tasklet, qdio_inbound_processing,
(unsigned long) q);
}
} }
for_each_output_queue(irq_ptr, q, i) { for_each_output_queue(irq_ptr, q, i) {
@@ -280,8 +272,7 @@ static void setup_queues(struct qdio_irq *irq_ptr,
setup_storage_lists(q, irq_ptr, setup_storage_lists(q, irq_ptr,
qdio_init->output_sbal_addr_array[i], i); qdio_init->output_sbal_addr_array[i], i);
tasklet_init(&q->tasklet, qdio_outbound_processing, tasklet_setup(&q->u.out.tasklet, qdio_outbound_tasklet);
(unsigned long) q);
timer_setup(&q->u.out.timer, qdio_outbound_timer, 0); timer_setup(&q->u.out.timer, qdio_outbound_timer, 0);
} }
} }
@@ -483,12 +474,8 @@ int qdio_setup_irq(struct qdio_irq *irq_ptr, struct qdio_initialize *init_data)
ccw_device_get_schid(cdev, &irq_ptr->schid); ccw_device_get_schid(cdev, &irq_ptr->schid);
setup_queues(irq_ptr, init_data); setup_queues(irq_ptr, init_data);
if (init_data->irq_poll) { irq_ptr->irq_poll = init_data->irq_poll;
irq_ptr->irq_poll = init_data->irq_poll; set_bit(QDIO_IRQ_DISABLED, &irq_ptr->poll_state);
set_bit(QDIO_IRQ_DISABLED, &irq_ptr->poll_state);
} else {
irq_ptr->irq_poll = NULL;
}
setup_qib(irq_ptr, init_data); setup_qib(irq_ptr, init_data);
set_impl_params(irq_ptr, init_data->qib_param_field_format, set_impl_params(irq_ptr, init_data->qib_param_field_format,

View File

@@ -66,22 +66,6 @@ static void put_indicator(u32 *addr)
atomic_dec(&ind->count); atomic_dec(&ind->count);
} }
void tiqdio_add_device(struct qdio_irq *irq_ptr)
{
mutex_lock(&tiq_list_lock);
list_add_rcu(&irq_ptr->entry, &tiq_list);
mutex_unlock(&tiq_list_lock);
}
void tiqdio_remove_device(struct qdio_irq *irq_ptr)
{
mutex_lock(&tiq_list_lock);
list_del_rcu(&irq_ptr->entry);
mutex_unlock(&tiq_list_lock);
synchronize_rcu();
INIT_LIST_HEAD(&irq_ptr->entry);
}
static inline int references_shared_dsci(struct qdio_irq *irq_ptr) static inline int references_shared_dsci(struct qdio_irq *irq_ptr)
{ {
return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind; return irq_ptr->dsci == &q_indicators[TIQDIO_SHARED_IND].ind;
@@ -106,32 +90,6 @@ static inline u32 clear_shared_ind(void)
return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0); return xchg(&q_indicators[TIQDIO_SHARED_IND].ind, 0);
} }
static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
{
struct qdio_q *q;
int i;
if (!references_shared_dsci(irq))
xchg(irq->dsci, 0);
if (irq->irq_poll) {
if (!test_and_set_bit(QDIO_IRQ_DISABLED, &irq->poll_state))
irq->irq_poll(irq->cdev, irq->int_parm);
else
QDIO_PERF_STAT_INC(irq, int_discarded);
return;
}
for_each_input_queue(irq, q, i) {
/*
* Call inbound processing but not directly
* since that could starve other thinint queues.
*/
tasklet_schedule(&q->tasklet);
}
}
/** /**
* tiqdio_thinint_handler - thin interrupt handler for qdio * tiqdio_thinint_handler - thin interrupt handler for qdio
* @airq: pointer to adapter interrupt descriptor * @airq: pointer to adapter interrupt descriptor
@@ -139,10 +97,11 @@ static inline void tiqdio_call_inq_handlers(struct qdio_irq *irq)
*/ */
static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating)
{ {
u64 irq_time = S390_lowcore.int_clock;
u32 si_used = clear_shared_ind(); u32 si_used = clear_shared_ind();
struct qdio_irq *irq; struct qdio_irq *irq;
last_ai_time = S390_lowcore.int_clock; last_ai_time = irq_time;
inc_irq_stat(IRQIO_QAI); inc_irq_stat(IRQIO_QAI);
/* protect tiq_list entries, only changed in activate or shutdown */ /* protect tiq_list entries, only changed in activate or shutdown */
@@ -153,10 +112,15 @@ static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating)
if (unlikely(references_shared_dsci(irq))) { if (unlikely(references_shared_dsci(irq))) {
if (!si_used) if (!si_used)
continue; continue;
} else if (!*irq->dsci) } else {
continue; if (!*irq->dsci)
continue;
tiqdio_call_inq_handlers(irq); xchg(irq->dsci, 0);
}
qdio_deliver_irq(irq);
irq->last_data_irq_time = irq_time;
QDIO_PERF_STAT_INC(irq, adapter_int); QDIO_PERF_STAT_INC(irq, adapter_int);
} }
@@ -208,10 +172,15 @@ int qdio_establish_thinint(struct qdio_irq *irq_ptr)
DBF_HEX(&irq_ptr->dsci, sizeof(void *)); DBF_HEX(&irq_ptr->dsci, sizeof(void *));
rc = set_subchannel_ind(irq_ptr, 0); rc = set_subchannel_ind(irq_ptr, 0);
if (rc) if (rc) {
put_indicator(irq_ptr->dsci); put_indicator(irq_ptr->dsci);
return rc;
}
return rc; mutex_lock(&tiq_list_lock);
list_add_rcu(&irq_ptr->entry, &tiq_list);
mutex_unlock(&tiq_list_lock);
return 0;
} }
void qdio_shutdown_thinint(struct qdio_irq *irq_ptr) void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
@@ -219,6 +188,11 @@ void qdio_shutdown_thinint(struct qdio_irq *irq_ptr)
if (!is_thinint_irq(irq_ptr)) if (!is_thinint_irq(irq_ptr))
return; return;
mutex_lock(&tiq_list_lock);
list_del_rcu(&irq_ptr->entry);
mutex_unlock(&tiq_list_lock);
synchronize_rcu();
/* reset adapter interrupt indicators */ /* reset adapter interrupt indicators */
set_subchannel_ind(irq_ptr, 1); set_subchannel_ind(irq_ptr, 1);
put_indicator(irq_ptr->dsci); put_indicator(irq_ptr->dsci);

View File

@@ -1438,6 +1438,8 @@ static int icarsamodexpo_ioctl(struct ap_perms *perms, unsigned long arg)
if (rc == -EAGAIN) if (rc == -EAGAIN)
tr.again_counter++; tr.again_counter++;
} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX)
rc = -EIO;
if (rc) { if (rc) {
ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSAMODEXPO rc=%d\n", rc); ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSAMODEXPO rc=%d\n", rc);
return rc; return rc;
@@ -1481,6 +1483,8 @@ static int icarsacrt_ioctl(struct ap_perms *perms, unsigned long arg)
if (rc == -EAGAIN) if (rc == -EAGAIN)
tr.again_counter++; tr.again_counter++;
} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX)
rc = -EIO;
if (rc) { if (rc) {
ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSACRT rc=%d\n", rc); ZCRYPT_DBF(DBF_DEBUG, "ioctl ICARSACRT rc=%d\n", rc);
return rc; return rc;
@@ -1524,6 +1528,8 @@ static int zsecsendcprb_ioctl(struct ap_perms *perms, unsigned long arg)
if (rc == -EAGAIN) if (rc == -EAGAIN)
tr.again_counter++; tr.again_counter++;
} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX)
rc = -EIO;
if (rc) if (rc)
ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDCPRB rc=%d status=0x%x\n", ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDCPRB rc=%d status=0x%x\n",
rc, xcRB.status); rc, xcRB.status);
@@ -1568,6 +1574,8 @@ static int zsendep11cprb_ioctl(struct ap_perms *perms, unsigned long arg)
if (rc == -EAGAIN) if (rc == -EAGAIN)
tr.again_counter++; tr.again_counter++;
} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX)
rc = -EIO;
if (rc) if (rc)
ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDEP11CPRB rc=%d\n", rc); ZCRYPT_DBF(DBF_DEBUG, "ioctl ZSENDEP11CPRB rc=%d\n", rc);
if (copy_to_user(uxcrb, &xcrb, sizeof(xcrb))) if (copy_to_user(uxcrb, &xcrb, sizeof(xcrb)))
@@ -1744,6 +1752,8 @@ static long trans_modexpo32(struct ap_perms *perms, struct file *filp,
if (rc == -EAGAIN) if (rc == -EAGAIN)
tr.again_counter++; tr.again_counter++;
} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX)
rc = -EIO;
if (rc) if (rc)
return rc; return rc;
return put_user(mex64.outputdatalength, return put_user(mex64.outputdatalength,
@@ -1795,6 +1805,8 @@ static long trans_modexpo_crt32(struct ap_perms *perms, struct file *filp,
if (rc == -EAGAIN) if (rc == -EAGAIN)
tr.again_counter++; tr.again_counter++;
} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX)
rc = -EIO;
if (rc) if (rc)
return rc; return rc;
return put_user(crt64.outputdatalength, return put_user(crt64.outputdatalength,
@@ -1865,6 +1877,8 @@ static long trans_xcRB32(struct ap_perms *perms, struct file *filp,
if (rc == -EAGAIN) if (rc == -EAGAIN)
tr.again_counter++; tr.again_counter++;
} while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX);
if (rc == -EAGAIN && tr.again_counter >= TRACK_AGAIN_MAX)
rc = -EIO;
xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length; xcRB32.reply_control_blk_length = xcRB64.reply_control_blk_length;
xcRB32.reply_data_length = xcRB64.reply_data_length; xcRB32.reply_data_length = xcRB64.reply_data_length;
xcRB32.status = xcRB64.status; xcRB32.status = xcRB64.status;

View File

@@ -662,7 +662,10 @@ int cca_sec2protkey(u16 cardnr, u16 domain,
__func__, __func__,
(int) prepcblk->ccp_rtcode, (int) prepcblk->ccp_rtcode,
(int) prepcblk->ccp_rscode); (int) prepcblk->ccp_rscode);
rc = -EIO; if (prepcblk->ccp_rtcode == 8 && prepcblk->ccp_rscode == 2290)
rc = -EAGAIN;
else
rc = -EIO;
goto out; goto out;
} }
if (prepcblk->ccp_rscode != 0) { if (prepcblk->ccp_rscode != 0) {
@@ -1275,7 +1278,10 @@ int cca_cipher2protkey(u16 cardnr, u16 domain, const u8 *ckey,
__func__, __func__,
(int) prepcblk->ccp_rtcode, (int) prepcblk->ccp_rtcode,
(int) prepcblk->ccp_rscode); (int) prepcblk->ccp_rscode);
rc = -EIO; if (prepcblk->ccp_rtcode == 8 && prepcblk->ccp_rscode == 2290)
rc = -EAGAIN;
else
rc = -EIO;
goto out; goto out;
} }
if (prepcblk->ccp_rscode != 0) { if (prepcblk->ccp_rscode != 0) {
@@ -1441,7 +1447,10 @@ int cca_ecc2protkey(u16 cardnr, u16 domain, const u8 *key,
__func__, __func__,
(int) prepcblk->ccp_rtcode, (int) prepcblk->ccp_rtcode,
(int) prepcblk->ccp_rscode); (int) prepcblk->ccp_rscode);
rc = -EIO; if (prepcblk->ccp_rtcode == 8 && prepcblk->ccp_rscode == 2290)
rc = -EAGAIN;
else
rc = -EIO;
goto out; goto out;
} }
if (prepcblk->ccp_rscode != 0) { if (prepcblk->ccp_rscode != 0) {

View File

@@ -6074,14 +6074,15 @@ int qeth_poll(struct napi_struct *napi, int budget)
EXPORT_SYMBOL_GPL(qeth_poll); EXPORT_SYMBOL_GPL(qeth_poll);
static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue, static void qeth_iqd_tx_complete(struct qeth_qdio_out_q *queue,
unsigned int bidx, bool error, int budget) unsigned int bidx, unsigned int qdio_error,
int budget)
{ {
struct qeth_qdio_out_buffer *buffer = queue->bufs[bidx]; struct qeth_qdio_out_buffer *buffer = queue->bufs[bidx];
u8 sflags = buffer->buffer->element[15].sflags; u8 sflags = buffer->buffer->element[15].sflags;
struct qeth_card *card = queue->card; struct qeth_card *card = queue->card;
bool error = !!qdio_error;
if (queue->bufstates && (queue->bufstates[bidx].flags & if (qdio_error == QDIO_ERROR_SLSB_PENDING) {
QDIO_OUTBUF_STATE_FLAG_PENDING)) {
WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED); WARN_ON_ONCE(card->options.cq != QETH_CQ_ENABLED);
QETH_CARD_TEXT_(card, 5, "pel%u", bidx); QETH_CARD_TEXT_(card, 5, "pel%u", bidx);

View File

@@ -255,7 +255,10 @@ SYSCALL_DEFINE2(ustat, unsigned, dev, struct ustat __user *, ubuf)
memset(&tmp,0,sizeof(struct ustat)); memset(&tmp,0,sizeof(struct ustat));
tmp.f_tfree = sbuf.f_bfree; tmp.f_tfree = sbuf.f_bfree;
tmp.f_tinode = sbuf.f_ffree; if (IS_ENABLED(CONFIG_ARCH_32BIT_USTAT_F_TINODE))
tmp.f_tinode = min_t(u64, sbuf.f_ffree, UINT_MAX);
else
tmp.f_tinode = sbuf.f_ffree;
return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0; return copy_to_user(ubuf, &tmp, sizeof(struct ustat)) ? -EFAULT : 0;
} }

View File

@@ -14,7 +14,7 @@ typedef u32 __kernel_dev_t;
typedef __kernel_fd_set fd_set; typedef __kernel_fd_set fd_set;
typedef __kernel_dev_t dev_t; typedef __kernel_dev_t dev_t;
typedef __kernel_ino_t ino_t; typedef __kernel_ulong_t ino_t;
typedef __kernel_mode_t mode_t; typedef __kernel_mode_t mode_t;
typedef unsigned short umode_t; typedef unsigned short umode_t;
typedef u32 nlink_t; typedef u32 nlink_t;
@@ -189,7 +189,11 @@ struct hlist_node {
struct ustat { struct ustat {
__kernel_daddr_t f_tfree; __kernel_daddr_t f_tfree;
__kernel_ino_t f_tinode; #ifdef CONFIG_ARCH_32BIT_USTAT_F_TINODE
unsigned int f_tinode;
#else
unsigned long f_tinode;
#endif
char f_fname[6]; char f_fname[6];
char f_fpack[6]; char f_fpack[6];
}; };