s390 updates for 6.11 merge window

- Remove restrictions on PAI NNPA and crypto counters, enabling
   concurrent per-task and system-wide sampling and counting events
 
 - Switch to GENERIC_CPU_DEVICES by setting up the CPU present mask in
   the architecture code and letting the generic code handle CPU bring-up
 
 - Add support for the diag204 busy indication facility to prevent
   undesirable blocking during hypervisor logical CPU utilization
   queries. Implement results caching
 
 - Improve the handling of Store Data SCLP events by suppressing
   unnecessary warning, preventing buffer release in I/O during failures,
   and adding timeout handling for Store Data requests to address potential
   firmware issues
 
 - Provide optimized __arch_hweight*() implementations
 
 - Remove the unnecessary CPU KOBJ_CHANGE uevents generated during topology
   updates, as they are unused and also not present on other architectures
 
 - Cleanup atomic_ops, optimize __atomic_set() for small values and
   __atomic_cmpxchg_bool() for compilers supporting flag output constraint
 
 - Couple of cleanups for KVM:
   - Move and improve KVM struct definitions for DAT tables from gaccess.c
     to a new header
   - Pass the asce as parameter to sie64a()
 
 - Make the crdte() and cspg() page table handling wrappers return a
   boolean to indicate success, like the other existing "compare and swap"
   wrappers
 
 - Add documentation for HWCAP flags
 
 - Switch to obtaining total RAM pages from memblock instead of
   totalram_pages() during mm init, to ensure correct calculation of zero
   page size, when defer_init is enabled
 
 - Refactor lowcore access and switch to using the get_lowcore() function
   instead of the S390_lowcore macro
 
 - Cleanups for PG_arch_1 and folio handling in UV and hugetlb code
 
 - Add missing MODULE_DESCRIPTION() macros
 
 - Fix VM_FAULT_HWPOISON handling in do_exception()
 -----BEGIN PGP SIGNATURE-----
 
 iQEzBAABCAAdFiEE3QHqV+H2a8xAv27vjYWKoQLXFBgFAmaYGegACgkQjYWKoQLX
 FBjCwwf/aRYoLIXCa9/nHGWFiUjZm6xBgVwZh55bXjfNG9TI2J9UZSsYlOFGUJKl
 gvD2Ym+LqAejK8R4EUHkfD6ftaKMQuIxNDoedxhwuSpfOQ2mZ5teu0MxTh8QcUAx
 4Y2w5XEeCuqE3SuoZ4SJa58K4rGl4cFpPsKNa8ofdzH1ZLFNe8Wqzis4kh0htqLb
 FtPj6nsgfzQ5kg14rVkGxCa4CqoFxonXgsA6nH6xZLbxKUInyq8uV44UBQ+aJq5v
 dsdzZ5XuAJHN2FpBuuOYQYZYw3XIy/kka7o4EjffORi5SGCRMWO4Zt0P6HXaNkh6
 xV8EEO8myeo7rV8dnrk1V4yGjGJmfA==
 =3IGY
 -----END PGP SIGNATURE-----

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

Pull s390 updates from Vasily Gorbik:

 - Remove restrictions on PAI NNPA and crypto counters, enabling
   concurrent per-task and system-wide sampling and counting events

 - Switch to GENERIC_CPU_DEVICES by setting up the CPU present mask in
   the architecture code and letting the generic code handle CPU
   bring-up

 - Add support for the diag204 busy indication facility to prevent
   undesirable blocking during hypervisor logical CPU utilization
   queries. Implement results caching

 - Improve the handling of Store Data SCLP events by suppressing
   unnecessary warning, preventing buffer release in I/O during
   failures, and adding timeout handling for Store Data requests to
   address potential firmware issues

 - Provide optimized __arch_hweight*() implementations

 - Remove the unnecessary CPU KOBJ_CHANGE uevents generated during
   topology updates, as they are unused and also not present on other
   architectures

 - Cleanup atomic_ops, optimize __atomic_set() for small values and
   __atomic_cmpxchg_bool() for compilers supporting flag output
   constraint

 - Couple of cleanups for KVM:
     - Move and improve KVM struct definitions for DAT tables from
       gaccess.c to a new header
     - Pass the asce as parameter to sie64a()

 - Make the crdte() and cspg() page table handling wrappers return a
   boolean to indicate success, like the other existing "compare and
   swap" wrappers

 - Add documentation for HWCAP flags

 - Switch to obtaining total RAM pages from memblock instead of
   totalram_pages() during mm init, to ensure correct calculation of
   zero page size, when defer_init is enabled

 - Refactor lowcore access and switch to using the get_lowcore()
   function instead of the S390_lowcore macro

 - Cleanups for PG_arch_1 and folio handling in UV and hugetlb code

 - Add missing MODULE_DESCRIPTION() macros

 - Fix VM_FAULT_HWPOISON handling in do_exception()

* tag 's390-6.11-1' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux: (54 commits)
  s390/mm: Fix VM_FAULT_HWPOISON handling in do_exception()
  s390/kvm: Move bitfields for dat tables
  s390/entry: Pass the asce as parameter to sie64a()
  s390/sthyi: Use cached data when diag is busy
  s390/sthyi: Move diag operations
  s390/hypfs_diag: Diag204 busy loop
  s390/diag: Add busy-indication-facility requirements
  s390/diag: Diag204 add busy return errno
  s390/diag: Return errno's from diag204
  s390/sclp: Diag204 busy indication facility detection
  s390/atomic_ops: Make use of flag output constraint
  s390/atomic_ops: Improve __atomic_set() for small values
  s390/atomic_ops: Use symbolic names
  s390/smp: Switch to GENERIC_CPU_DEVICES
  s390/hwcaps: Add documentation for HWCAP flags
  s390/pgtable: Make crdte() and cspg() return a value
  s390/topology: Remove CPU KOBJ_CHANGE uevents
  s390/sclp: Add timeout to Store Data requests
  s390/sclp: Prevent release of buffer in I/O
  s390/sclp: Suppress unnecessary Store Data warning
  ...
This commit is contained in:
Linus Torvalds 2024-07-18 15:41:45 -07:00
commit 1c7d0c3af5
88 changed files with 1207 additions and 778 deletions

View File

@ -21,7 +21,7 @@ config ARCH_PROC_KCORE_TEXT
def_bool y def_bool y
config GENERIC_HWEIGHT config GENERIC_HWEIGHT
def_bool y def_bool !HAVE_MARCH_Z196_FEATURES
config GENERIC_BUG config GENERIC_BUG
def_bool y if BUG def_bool y if BUG
@ -142,6 +142,7 @@ config S390
select FUNCTION_ALIGNMENT_8B if CC_IS_GCC select FUNCTION_ALIGNMENT_8B if CC_IS_GCC
select FUNCTION_ALIGNMENT_16B if !CC_IS_GCC select FUNCTION_ALIGNMENT_16B if !CC_IS_GCC
select GENERIC_ALLOCATOR select GENERIC_ALLOCATOR
select GENERIC_CPU_DEVICES
select GENERIC_CPU_AUTOPROBE select GENERIC_CPU_AUTOPROBE
select GENERIC_CPU_VULNERABILITIES select GENERIC_CPU_VULNERABILITIES
select GENERIC_ENTRY select GENERIC_ENTRY

View File

@ -51,11 +51,11 @@ static inline int __diag308(unsigned long subcode, void *addr)
: [r1] "+&d" (r1.pair), : [r1] "+&d" (r1.pair),
[reg1] "=&d" (reg1), [reg1] "=&d" (reg1),
[reg2] "=&a" (reg2), [reg2] "=&a" (reg2),
"+Q" (S390_lowcore.program_new_psw), "+Q" (get_lowcore()->program_new_psw),
"=Q" (old) "=Q" (old)
: [subcode] "d" (subcode), : [subcode] "d" (subcode),
[psw_old] "a" (&old), [psw_old] "a" (&old),
[psw_pgm] "a" (&S390_lowcore.program_new_psw) [psw_pgm] "a" (&get_lowcore()->program_new_psw)
: "cc", "memory"); : "cc", "memory");
return r1.odd; return r1.odd;
} }

View File

@ -106,7 +106,7 @@ int read_ipl_report(void)
* the IPL parameter list, then align the address to a double * the IPL parameter list, then align the address to a double
* word boundary. * word boundary.
*/ */
tmp = (unsigned long) S390_lowcore.ipl_parmblock_ptr; tmp = (unsigned long)get_lowcore()->ipl_parmblock_ptr;
pl_hdr = (struct ipl_pl_hdr *) tmp; pl_hdr = (struct ipl_pl_hdr *) tmp;
tmp = (tmp + pl_hdr->len + 7) & -8UL; tmp = (tmp + pl_hdr->len + 7) & -8UL;
rl_hdr = (struct ipl_rl_hdr *) tmp; rl_hdr = (struct ipl_rl_hdr *) tmp;

View File

@ -145,22 +145,22 @@ void print_stacktrace(unsigned long sp)
void print_pgm_check_info(void) void print_pgm_check_info(void)
{ {
unsigned long *gpregs = (unsigned long *)S390_lowcore.gpregs_save_area; unsigned long *gpregs = (unsigned long *)get_lowcore()->gpregs_save_area;
struct psw_bits *psw = &psw_bits(S390_lowcore.psw_save_area); struct psw_bits *psw = &psw_bits(get_lowcore()->psw_save_area);
decompressor_printk("Linux version %s\n", kernel_version); decompressor_printk("Linux version %s\n", kernel_version);
if (!is_prot_virt_guest() && early_command_line[0]) if (!is_prot_virt_guest() && early_command_line[0])
decompressor_printk("Kernel command line: %s\n", early_command_line); decompressor_printk("Kernel command line: %s\n", early_command_line);
decompressor_printk("Kernel fault: interruption code %04x ilc:%x\n", decompressor_printk("Kernel fault: interruption code %04x ilc:%x\n",
S390_lowcore.pgm_code, S390_lowcore.pgm_ilc >> 1); get_lowcore()->pgm_code, get_lowcore()->pgm_ilc >> 1);
if (kaslr_enabled()) { if (kaslr_enabled()) {
decompressor_printk("Kernel random base: %lx\n", __kaslr_offset); decompressor_printk("Kernel random base: %lx\n", __kaslr_offset);
decompressor_printk("Kernel random base phys: %lx\n", __kaslr_offset_phys); decompressor_printk("Kernel random base phys: %lx\n", __kaslr_offset_phys);
} }
decompressor_printk("PSW : %016lx %016lx (%pS)\n", decompressor_printk("PSW : %016lx %016lx (%pS)\n",
S390_lowcore.psw_save_area.mask, get_lowcore()->psw_save_area.mask,
S390_lowcore.psw_save_area.addr, get_lowcore()->psw_save_area.addr,
(void *)S390_lowcore.psw_save_area.addr); (void *)get_lowcore()->psw_save_area.addr);
decompressor_printk( decompressor_printk(
" R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x P:%x AS:%x CC:%x PM:%x RI:%x EA:%x\n", " R:%x T:%x IO:%x EX:%x Key:%x M:%x W:%x P:%x AS:%x CC:%x PM:%x RI:%x EA:%x\n",
psw->per, psw->dat, psw->io, psw->ext, psw->key, psw->mcheck, psw->per, psw->dat, psw->io, psw->ext, psw->key, psw->mcheck,
@ -174,8 +174,8 @@ void print_pgm_check_info(void)
gpregs[8], gpregs[9], gpregs[10], gpregs[11]); gpregs[8], gpregs[9], gpregs[10], gpregs[11]);
decompressor_printk(" %016lx %016lx %016lx %016lx\n", decompressor_printk(" %016lx %016lx %016lx %016lx\n",
gpregs[12], gpregs[13], gpregs[14], gpregs[15]); gpregs[12], gpregs[13], gpregs[14], gpregs[15]);
print_stacktrace(S390_lowcore.gpregs_save_area[15]); print_stacktrace(get_lowcore()->gpregs_save_area[15]);
decompressor_printk("Last Breaking-Event-Address:\n"); decompressor_printk("Last Breaking-Event-Address:\n");
decompressor_printk(" [<%016lx>] %pS\n", (unsigned long)S390_lowcore.pgm_last_break, decompressor_printk(" [<%016lx>] %pS\n", (unsigned long)get_lowcore()->pgm_last_break,
(void *)S390_lowcore.pgm_last_break); (void *)get_lowcore()->pgm_last_break);
} }

View File

@ -81,11 +81,11 @@ static int __diag260(unsigned long rx1, unsigned long rx2)
[reg2] "=&a" (reg2), [reg2] "=&a" (reg2),
[rc] "+&d" (rc), [rc] "+&d" (rc),
[ry] "+&d" (ry), [ry] "+&d" (ry),
"+Q" (S390_lowcore.program_new_psw), "+Q" (get_lowcore()->program_new_psw),
"=Q" (old) "=Q" (old)
: [rx] "d" (rx.pair), : [rx] "d" (rx.pair),
[psw_old] "a" (&old), [psw_old] "a" (&old),
[psw_pgm] "a" (&S390_lowcore.program_new_psw) [psw_pgm] "a" (&get_lowcore()->program_new_psw)
: "cc", "memory"); : "cc", "memory");
return rc == 0 ? ry : -1; return rc == 0 ? ry : -1;
} }
@ -129,10 +129,10 @@ static int tprot(unsigned long addr)
: [reg1] "=&d" (reg1), : [reg1] "=&d" (reg1),
[reg2] "=&a" (reg2), [reg2] "=&a" (reg2),
[rc] "+&d" (rc), [rc] "+&d" (rc),
"=Q" (S390_lowcore.program_new_psw.addr), "=Q" (get_lowcore()->program_new_psw.addr),
"=Q" (old) "=Q" (old)
: [psw_old] "a" (&old), : [psw_old] "a" (&old),
[psw_pgm] "a" (&S390_lowcore.program_new_psw), [psw_pgm] "a" (&get_lowcore()->program_new_psw),
[addr] "a" (addr) [addr] "a" (addr)
: "cc", "memory"); : "cc", "memory");
return rc; return rc;

View File

@ -78,10 +78,10 @@ static int cmma_test_essa(void)
[reg2] "=&a" (reg2), [reg2] "=&a" (reg2),
[rc] "+&d" (rc), [rc] "+&d" (rc),
[tmp] "=&d" (tmp), [tmp] "=&d" (tmp),
"+Q" (S390_lowcore.program_new_psw), "+Q" (get_lowcore()->program_new_psw),
"=Q" (old) "=Q" (old)
: [psw_old] "a" (&old), : [psw_old] "a" (&old),
[psw_pgm] "a" (&S390_lowcore.program_new_psw), [psw_pgm] "a" (&get_lowcore()->program_new_psw),
[cmd] "i" (ESSA_GET_STATE) [cmd] "i" (ESSA_GET_STATE)
: "cc", "memory"); : "cc", "memory");
return rc; return rc;
@ -101,10 +101,10 @@ static void cmma_init(void)
static void setup_lpp(void) static void setup_lpp(void)
{ {
S390_lowcore.current_pid = 0; get_lowcore()->current_pid = 0;
S390_lowcore.lpp = LPP_MAGIC; get_lowcore()->lpp = LPP_MAGIC;
if (test_facility(40)) if (test_facility(40))
lpp(&S390_lowcore.lpp); lpp(&get_lowcore()->lpp);
} }
#ifdef CONFIG_KERNEL_UNCOMPRESSED #ifdef CONFIG_KERNEL_UNCOMPRESSED
@ -501,7 +501,7 @@ void startup_kernel(void)
* Save KASLR offset for early dumps, before vmcore_info is set. * Save KASLR offset for early dumps, before vmcore_info is set.
* Mark as uneven to distinguish from real vmcore_info pointer. * Mark as uneven to distinguish from real vmcore_info pointer.
*/ */
S390_lowcore.vmcore_info = __kaslr_offset_phys ? __kaslr_offset_phys | 0x1UL : 0; get_lowcore()->vmcore_info = __kaslr_offset_phys ? __kaslr_offset_phys | 0x1UL : 0;
/* /*
* Jump to the decompressed kernel entry point and switch DAT mode on. * Jump to the decompressed kernel entry point and switch DAT mode on.

View File

@ -476,13 +476,13 @@ void setup_vmem(unsigned long kernel_start, unsigned long kernel_end, unsigned l
kasan_populate_shadow(kernel_start, kernel_end); kasan_populate_shadow(kernel_start, kernel_end);
S390_lowcore.kernel_asce.val = swapper_pg_dir | asce_bits; get_lowcore()->kernel_asce.val = swapper_pg_dir | asce_bits;
S390_lowcore.user_asce = s390_invalid_asce; get_lowcore()->user_asce = s390_invalid_asce;
local_ctl_load(1, &S390_lowcore.kernel_asce); local_ctl_load(1, &get_lowcore()->kernel_asce);
local_ctl_load(7, &S390_lowcore.user_asce); local_ctl_load(7, &get_lowcore()->user_asce);
local_ctl_load(13, &S390_lowcore.kernel_asce); local_ctl_load(13, &get_lowcore()->kernel_asce);
init_mm.context.asce = S390_lowcore.kernel_asce.val; init_mm.context.asce = get_lowcore()->kernel_asce.val;
init_mm.pgd = init_mm_pgd; init_mm.pgd = init_mm_pgd;
} }

View File

@ -297,6 +297,7 @@ module_cpu_feature_match(S390_CPU_FEATURE_VXRS, crc_vx_mod_init);
module_exit(crc_vx_mod_exit); module_exit(crc_vx_mod_exit);
MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>"); MODULE_AUTHOR("Hendrik Brueckner <brueckner@linux.vnet.ibm.com>");
MODULE_DESCRIPTION("CRC-32 algorithms using z/Architecture Vector Extension Facility");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_CRYPTO("crc32"); MODULE_ALIAS_CRYPTO("crc32");

View File

@ -39,7 +39,9 @@ static ssize_t dbfs_read(struct file *file, char __user *buf,
return 0; return 0;
df = file_inode(file)->i_private; df = file_inode(file)->i_private;
mutex_lock(&df->lock); if (mutex_lock_interruptible(&df->lock))
return -ERESTARTSYS;
data = hypfs_dbfs_data_alloc(df); data = hypfs_dbfs_data_alloc(df);
if (!data) { if (!data) {
mutex_unlock(&df->lock); mutex_unlock(&df->lock);

View File

@ -140,11 +140,22 @@ fail_alloc:
int diag204_store(void *buf, int pages) int diag204_store(void *buf, int pages)
{ {
unsigned long subcode;
int rc; int rc;
rc = diag204((unsigned long)diag204_store_sc | subcode = diag204_get_info_type();
(unsigned long)diag204_get_info_type(), pages, buf); subcode |= diag204_store_sc;
return rc < 0 ? -EOPNOTSUPP : 0; if (diag204_has_bif())
subcode |= DIAG204_BIF_BIT;
while (1) {
rc = diag204(subcode, pages, buf);
if (rc != -EBUSY)
break;
if (signal_pending(current))
return -ERESTARTSYS;
schedule_timeout_interruptible(DIAG204_BUSY_WAIT);
}
return rc < 0 ? rc : 0;
} }
struct dbfs_d204_hdr { struct dbfs_d204_hdr {

View File

@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_S390_ARCH_HWEIGHT_H
#define _ASM_S390_ARCH_HWEIGHT_H
#include <linux/types.h>
static __always_inline unsigned long popcnt_z196(unsigned long w)
{
unsigned long cnt;
asm volatile(".insn rrf,0xb9e10000,%[cnt],%[w],0,0"
: [cnt] "=d" (cnt)
: [w] "d" (w)
: "cc");
return cnt;
}
static __always_inline unsigned long popcnt_z15(unsigned long w)
{
unsigned long cnt;
asm volatile(".insn rrf,0xb9e10000,%[cnt],%[w],8,0"
: [cnt] "=d" (cnt)
: [w] "d" (w)
: "cc");
return cnt;
}
static __always_inline unsigned long __arch_hweight64(__u64 w)
{
if (IS_ENABLED(CONFIG_HAVE_MARCH_Z15_FEATURES))
return popcnt_z15(w);
if (IS_ENABLED(CONFIG_HAVE_MARCH_Z196_FEATURES)) {
w = popcnt_z196(w);
w += w >> 32;
w += w >> 16;
w += w >> 8;
return w & 0xff;
}
return __sw_hweight64(w);
}
static __always_inline unsigned int __arch_hweight32(unsigned int w)
{
if (IS_ENABLED(CONFIG_HAVE_MARCH_Z15_FEATURES))
return popcnt_z15(w);
if (IS_ENABLED(CONFIG_HAVE_MARCH_Z196_FEATURES)) {
w = popcnt_z196(w);
w += w >> 16;
w += w >> 8;
return w & 0xff;
}
return __sw_hweight32(w);
}
static __always_inline unsigned int __arch_hweight16(unsigned int w)
{
if (IS_ENABLED(CONFIG_HAVE_MARCH_Z15_FEATURES))
return popcnt_z15((unsigned short)w);
if (IS_ENABLED(CONFIG_HAVE_MARCH_Z196_FEATURES)) {
w = popcnt_z196(w);
w += w >> 8;
return w & 0xff;
}
return __sw_hweight16(w);
}
static __always_inline unsigned int __arch_hweight8(unsigned int w)
{
if (IS_ENABLED(CONFIG_HAVE_MARCH_Z196_FEATURES))
return popcnt_z196((unsigned char)w);
return __sw_hweight8(w);
}
#endif /* _ASM_S390_ARCH_HWEIGHT_H */

View File

@ -8,21 +8,29 @@
#ifndef __ARCH_S390_ATOMIC_OPS__ #ifndef __ARCH_S390_ATOMIC_OPS__
#define __ARCH_S390_ATOMIC_OPS__ #define __ARCH_S390_ATOMIC_OPS__
#include <linux/limits.h>
static __always_inline int __atomic_read(const atomic_t *v) static __always_inline int __atomic_read(const atomic_t *v)
{ {
int c; int c;
asm volatile( asm volatile(
" l %0,%1\n" " l %[c],%[counter]\n"
: "=d" (c) : "R" (v->counter)); : [c] "=d" (c) : [counter] "R" (v->counter));
return c; return c;
} }
static __always_inline void __atomic_set(atomic_t *v, int i) static __always_inline void __atomic_set(atomic_t *v, int i)
{ {
asm volatile( if (__builtin_constant_p(i) && i >= S16_MIN && i <= S16_MAX) {
" st %1,%0\n" asm volatile(
: "=R" (v->counter) : "d" (i)); " mvhi %[counter], %[i]\n"
: [counter] "=Q" (v->counter) : [i] "K" (i));
} else {
asm volatile(
" st %[i],%[counter]\n"
: [counter] "=R" (v->counter) : [i] "d" (i));
}
} }
static __always_inline s64 __atomic64_read(const atomic64_t *v) static __always_inline s64 __atomic64_read(const atomic64_t *v)
@ -30,16 +38,22 @@ static __always_inline s64 __atomic64_read(const atomic64_t *v)
s64 c; s64 c;
asm volatile( asm volatile(
" lg %0,%1\n" " lg %[c],%[counter]\n"
: "=d" (c) : "RT" (v->counter)); : [c] "=d" (c) : [counter] "RT" (v->counter));
return c; return c;
} }
static __always_inline void __atomic64_set(atomic64_t *v, s64 i) static __always_inline void __atomic64_set(atomic64_t *v, s64 i)
{ {
asm volatile( if (__builtin_constant_p(i) && i >= S16_MIN && i <= S16_MAX) {
" stg %1,%0\n" asm volatile(
: "=RT" (v->counter) : "d" (i)); " mvghi %[counter], %[i]\n"
: [counter] "=Q" (v->counter) : [i] "K" (i));
} else {
asm volatile(
" stg %[i],%[counter]\n"
: [counter] "=RT" (v->counter) : [i] "d" (i));
}
} }
#ifdef CONFIG_HAVE_MARCH_Z196_FEATURES #ifdef CONFIG_HAVE_MARCH_Z196_FEATURES
@ -164,6 +178,44 @@ static __always_inline int __atomic_cmpxchg(int *ptr, int old, int new)
return old; return old;
} }
static __always_inline long __atomic64_cmpxchg(long *ptr, long old, long new)
{
asm volatile(
" csg %[old],%[new],%[ptr]"
: [old] "+d" (old), [ptr] "+QS" (*ptr)
: [new] "d" (new)
: "cc", "memory");
return old;
}
#ifdef __GCC_ASM_FLAG_OUTPUTS__
static __always_inline bool __atomic_cmpxchg_bool(int *ptr, int old, int new)
{
int cc;
asm volatile(
" cs %[old],%[new],%[ptr]"
: [old] "+d" (old), [ptr] "+Q" (*ptr), "=@cc" (cc)
: [new] "d" (new)
: "memory");
return cc == 0;
}
static __always_inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long new)
{
int cc;
asm volatile(
" csg %[old],%[new],%[ptr]"
: [old] "+d" (old), [ptr] "+QS" (*ptr), "=@cc" (cc)
: [new] "d" (new)
: "memory");
return cc == 0;
}
#else /* __GCC_ASM_FLAG_OUTPUTS__ */
static __always_inline bool __atomic_cmpxchg_bool(int *ptr, int old, int new) static __always_inline bool __atomic_cmpxchg_bool(int *ptr, int old, int new)
{ {
int old_expected = old; int old_expected = old;
@ -176,16 +228,6 @@ static __always_inline bool __atomic_cmpxchg_bool(int *ptr, int old, int new)
return old == old_expected; return old == old_expected;
} }
static __always_inline long __atomic64_cmpxchg(long *ptr, long old, long new)
{
asm volatile(
" csg %[old],%[new],%[ptr]"
: [old] "+d" (old), [ptr] "+QS" (*ptr)
: [new] "d" (new)
: "cc", "memory");
return old;
}
static __always_inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long new) static __always_inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long new)
{ {
long old_expected = old; long old_expected = old;
@ -198,4 +240,6 @@ static __always_inline bool __atomic64_cmpxchg_bool(long *ptr, long old, long ne
return old == old_expected; return old == old_expected;
} }
#endif /* __GCC_ASM_FLAG_OUTPUTS__ */
#endif /* __ARCH_S390_ATOMIC_OPS__ */ #endif /* __ARCH_S390_ATOMIC_OPS__ */

View File

@ -379,8 +379,9 @@ static inline int fls(unsigned int word)
return fls64(word); return fls64(word);
} }
#include <asm/arch_hweight.h>
#include <asm-generic/bitops/const_hweight.h>
#include <asm-generic/bitops/ffz.h> #include <asm-generic/bitops/ffz.h>
#include <asm-generic/bitops/hweight.h>
#include <asm-generic/bitops/sched.h> #include <asm-generic/bitops/sched.h>
#include <asm-generic/bitops/le.h> #include <asm-generic/bitops/le.h>
#include <asm-generic/bitops/ext2-atomic-setbit.h> #include <asm-generic/bitops/ext2-atomic-setbit.h>

View File

@ -14,6 +14,6 @@
struct task_struct; struct task_struct;
#define current ((struct task_struct *const)S390_lowcore.current_task) #define current ((struct task_struct *const)get_lowcore()->current_task)
#endif /* !(_S390_CURRENT_H) */ #endif /* !(_S390_CURRENT_H) */

View File

@ -0,0 +1,170 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* DAT table and related structures
*
* Copyright IBM Corp. 2024
*
*/
#ifndef _S390_DAT_BITS_H
#define _S390_DAT_BITS_H
union asce {
unsigned long val;
struct {
unsigned long rsto: 52;/* Region- or Segment-Table Origin */
unsigned long : 2;
unsigned long g : 1; /* Subspace Group control */
unsigned long p : 1; /* Private Space control */
unsigned long s : 1; /* Storage-Alteration-Event control */
unsigned long x : 1; /* Space-Switch-Event control */
unsigned long r : 1; /* Real-Space control */
unsigned long : 1;
unsigned long dt : 2; /* Designation-Type control */
unsigned long tl : 2; /* Region- or Segment-Table Length */
};
};
enum {
ASCE_TYPE_SEGMENT = 0,
ASCE_TYPE_REGION3 = 1,
ASCE_TYPE_REGION2 = 2,
ASCE_TYPE_REGION1 = 3
};
union region1_table_entry {
unsigned long val;
struct {
unsigned long rto: 52;/* Region-Table Origin */
unsigned long : 2;
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 1;
unsigned long tf : 2; /* Region-Second-Table Offset */
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long : 1;
unsigned long tt : 2; /* Table-Type Bits */
unsigned long tl : 2; /* Region-Second-Table Length */
};
};
union region2_table_entry {
unsigned long val;
struct {
unsigned long rto: 52;/* Region-Table Origin */
unsigned long : 2;
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 1;
unsigned long tf : 2; /* Region-Third-Table Offset */
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long : 1;
unsigned long tt : 2; /* Table-Type Bits */
unsigned long tl : 2; /* Region-Third-Table Length */
};
};
struct region3_table_entry_fc0 {
unsigned long sto: 52;/* Segment-Table Origin */
unsigned long : 1;
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 1;
unsigned long tf : 2; /* Segment-Table Offset */
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long cr : 1; /* Common-Region Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long tl : 2; /* Segment-Table Length */
};
struct region3_table_entry_fc1 {
unsigned long rfaa: 33;/* Region-Frame Absolute Address */
unsigned long : 14;
unsigned long av : 1; /* ACCF-Validity Control */
unsigned long acc : 4; /* Access-Control Bits */
unsigned long f : 1; /* Fetch-Protection Bit */
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long iep : 1; /* Instruction-Execution-Protection */
unsigned long : 2;
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long cr : 1; /* Common-Region Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
union region3_table_entry {
unsigned long val;
struct region3_table_entry_fc0 fc0;
struct region3_table_entry_fc1 fc1;
struct {
unsigned long : 53;
unsigned long fc: 1; /* Format-Control */
unsigned long : 4;
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long cr: 1; /* Common-Region Bit */
unsigned long tt: 2; /* Table-Type Bits */
unsigned long : 2;
};
};
struct segment_table_entry_fc0 {
unsigned long pto: 53;/* Page-Table Origin */
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 3;
unsigned long i : 1; /* Segment-Invalid Bit */
unsigned long cs : 1; /* Common-Segment Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
struct segment_table_entry_fc1 {
unsigned long sfaa: 44;/* Segment-Frame Absolute Address */
unsigned long : 3;
unsigned long av : 1; /* ACCF-Validity Control */
unsigned long acc : 4; /* Access-Control Bits */
unsigned long f : 1; /* Fetch-Protection Bit */
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long iep : 1; /* Instruction-Execution-Protection */
unsigned long : 2;
unsigned long i : 1; /* Segment-Invalid Bit */
unsigned long cs : 1; /* Common-Segment Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
union segment_table_entry {
unsigned long val;
struct segment_table_entry_fc0 fc0;
struct segment_table_entry_fc1 fc1;
struct {
unsigned long : 53;
unsigned long fc: 1; /* Format-Control */
unsigned long : 4;
unsigned long i : 1; /* Segment-Invalid Bit */
unsigned long cs: 1; /* Common-Segment Bit */
unsigned long tt: 2; /* Table-Type Bits */
unsigned long : 2;
};
};
union page_table_entry {
unsigned long val;
struct {
unsigned long pfra: 52;/* Page-Frame Real Address */
unsigned long z : 1; /* Zero Bit */
unsigned long i : 1; /* Page-Invalid Bit */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long iep : 1; /* Instruction-Execution-Protection */
unsigned long : 8;
};
};
enum {
TABLE_TYPE_SEGMENT = 0,
TABLE_TYPE_REGION3 = 1,
TABLE_TYPE_REGION2 = 2,
TABLE_TYPE_REGION1 = 3
};
#endif /* _S390_DAT_BITS_H */

View File

@ -12,6 +12,7 @@
#include <linux/if_ether.h> #include <linux/if_ether.h>
#include <linux/percpu.h> #include <linux/percpu.h>
#include <asm/asm-extable.h> #include <asm/asm-extable.h>
#include <asm/sclp.h>
#include <asm/cio.h> #include <asm/cio.h>
enum diag_stat_enum { enum diag_stat_enum {
@ -117,6 +118,8 @@ enum diag204_sc {
}; };
#define DIAG204_SUBCODE_MASK 0xffff #define DIAG204_SUBCODE_MASK 0xffff
#define DIAG204_BIF_BIT 0x80000000
#define DIAG204_BUSY_WAIT (HZ / 10)
/* The two available diag 204 data formats */ /* The two available diag 204 data formats */
enum diag204_format { enum diag204_format {
@ -326,6 +329,11 @@ union diag318_info {
}; };
}; };
static inline bool diag204_has_bif(void)
{
return sclp.has_diag204_bif;
}
int diag204(unsigned long subcode, unsigned long size, void *addr); int diag204(unsigned long subcode, unsigned long size, void *addr);
int diag224(void *ptr); int diag224(void *ptr);
int diag26c(void *req, void *resp, enum diag26c_sc subcode); int diag26c(void *req, void *resp, enum diag26c_sc subcode);

View File

@ -91,6 +91,14 @@
/* Keep this the last entry. */ /* Keep this the last entry. */
#define R_390_NUM 61 #define R_390_NUM 61
/*
* HWCAP flags - for AT_HWCAP
*
* Bits 32-63 are reserved for use by libc.
* Bit 31 is reserved and will be used by libc to determine if a second
* argument is passed to IFUNC resolvers. This will be implemented when
* there is a need for AT_HWCAP2.
*/
enum { enum {
HWCAP_NR_ESAN3 = 0, HWCAP_NR_ESAN3 = 0,
HWCAP_NR_ZARCH = 1, HWCAP_NR_ZARCH = 1,

View File

@ -92,8 +92,8 @@ static inline void __stfle(u64 *stfle_fac_list, int size)
asm volatile( asm volatile(
" stfl 0(0)\n" " stfl 0(0)\n"
: "=m" (S390_lowcore.stfl_fac_list)); : "=m" (get_lowcore()->stfl_fac_list));
stfl_fac_list = S390_lowcore.stfl_fac_list; stfl_fac_list = get_lowcore()->stfl_fac_list;
memcpy(stfle_fac_list, &stfl_fac_list, 4); memcpy(stfle_fac_list, &stfl_fac_list, 4);
nr = 4; /* bytes stored by stfl */ nr = 4; /* bytes stored by stfl */
if (stfl_fac_list & 0x01000000) { if (stfl_fac_list & 0x01000000) {

View File

@ -13,9 +13,9 @@
#include <asm/lowcore.h> #include <asm/lowcore.h>
#define local_softirq_pending() (S390_lowcore.softirq_pending) #define local_softirq_pending() (get_lowcore()->softirq_pending)
#define set_softirq_pending(x) (S390_lowcore.softirq_pending = (x)) #define set_softirq_pending(x) (get_lowcore()->softirq_pending = (x))
#define or_softirq_pending(x) (S390_lowcore.softirq_pending |= (x)) #define or_softirq_pending(x) (get_lowcore()->softirq_pending |= (x))
#define __ARCH_IRQ_STAT #define __ARCH_IRQ_STAT
#define __ARCH_IRQ_EXIT_IRQS_DISABLED #define __ARCH_IRQ_EXIT_IRQS_DISABLED

View File

@ -1030,11 +1030,12 @@ void kvm_arch_crypto_clear_masks(struct kvm *kvm);
void kvm_arch_crypto_set_masks(struct kvm *kvm, unsigned long *apm, void kvm_arch_crypto_set_masks(struct kvm *kvm, unsigned long *apm,
unsigned long *aqm, unsigned long *adm); unsigned long *aqm, unsigned long *adm);
int __sie64a(phys_addr_t sie_block_phys, struct kvm_s390_sie_block *sie_block, u64 *rsa); int __sie64a(phys_addr_t sie_block_phys, struct kvm_s390_sie_block *sie_block, u64 *rsa,
unsigned long gasce);
static inline int sie64a(struct kvm_s390_sie_block *sie_block, u64 *rsa) static inline int sie64a(struct kvm_s390_sie_block *sie_block, u64 *rsa, unsigned long gasce)
{ {
return __sie64a(virt_to_phys(sie_block), sie_block, rsa); return __sie64a(virt_to_phys(sie_block), sie_block, rsa, gasce);
} }
extern char sie_exit; extern char sie_exit;

View File

@ -213,7 +213,10 @@ struct lowcore {
__u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */ __u8 pad_0x1900[0x2000-0x1900]; /* 0x1900 */
} __packed __aligned(8192); } __packed __aligned(8192);
#define S390_lowcore (*((struct lowcore *) 0)) static __always_inline struct lowcore *get_lowcore(void)
{
return NULL;
}
extern struct lowcore *lowcore_ptr[]; extern struct lowcore *lowcore_ptr[];

View File

@ -76,9 +76,9 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *
int cpu = smp_processor_id(); int cpu = smp_processor_id();
if (next == &init_mm) if (next == &init_mm)
S390_lowcore.user_asce = s390_invalid_asce; get_lowcore()->user_asce = s390_invalid_asce;
else else
S390_lowcore.user_asce.val = next->context.asce; get_lowcore()->user_asce.val = next->context.asce;
cpumask_set_cpu(cpu, &next->context.cpu_attach_mask); cpumask_set_cpu(cpu, &next->context.cpu_attach_mask);
/* Clear previous user-ASCE from CR7 */ /* Clear previous user-ASCE from CR7 */
local_ctl_load(7, &s390_invalid_asce); local_ctl_load(7, &s390_invalid_asce);
@ -111,7 +111,7 @@ static inline void finish_arch_post_lock_switch(void)
__tlb_flush_mm_lazy(mm); __tlb_flush_mm_lazy(mm);
preempt_enable(); preempt_enable();
} }
local_ctl_load(7, &S390_lowcore.user_asce); local_ctl_load(7, &get_lowcore()->user_asce);
} }
#define activate_mm activate_mm #define activate_mm activate_mm
@ -120,7 +120,7 @@ static inline void activate_mm(struct mm_struct *prev,
{ {
switch_mm(prev, next, current); switch_mm(prev, next, current);
cpumask_set_cpu(smp_processor_id(), mm_cpumask(next)); cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
local_ctl_load(7, &S390_lowcore.user_asce); local_ctl_load(7, &get_lowcore()->user_asce);
} }
#include <asm-generic/mmu_context.h> #include <asm-generic/mmu_context.h>

View File

@ -162,6 +162,7 @@ static inline int page_reset_referenced(unsigned long addr)
#define _PAGE_ACC_BITS 0xf0 /* HW access control bits */ #define _PAGE_ACC_BITS 0xf0 /* HW access control bits */
struct page; struct page;
struct folio;
void arch_free_page(struct page *page, int order); void arch_free_page(struct page *page, int order);
void arch_alloc_page(struct page *page, int order); void arch_alloc_page(struct page *page, int order);
@ -174,6 +175,8 @@ static inline int devmem_is_allowed(unsigned long pfn)
#define HAVE_ARCH_ALLOC_PAGE #define HAVE_ARCH_ALLOC_PAGE
#if IS_ENABLED(CONFIG_PGSTE) #if IS_ENABLED(CONFIG_PGSTE)
int arch_make_folio_accessible(struct folio *folio);
#define HAVE_ARCH_MAKE_FOLIO_ACCESSIBLE
int arch_make_page_accessible(struct page *page); int arch_make_page_accessible(struct page *page);
#define HAVE_ARCH_MAKE_PAGE_ACCESSIBLE #define HAVE_ARCH_MAKE_PAGE_ACCESSIBLE
#endif #endif
@ -247,7 +250,9 @@ static inline unsigned long __phys_addr(unsigned long x, bool is_31bit)
#define pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT) #define pfn_to_phys(pfn) ((pfn) << PAGE_SHIFT)
#define phys_to_page(phys) pfn_to_page(phys_to_pfn(phys)) #define phys_to_page(phys) pfn_to_page(phys_to_pfn(phys))
#define phys_to_folio(phys) page_folio(phys_to_page(phys))
#define page_to_phys(page) pfn_to_phys(page_to_pfn(page)) #define page_to_phys(page) pfn_to_phys(page_to_pfn(page))
#define folio_to_phys(page) pfn_to_phys(folio_pfn(folio))
static inline void *pfn_to_virt(unsigned long pfn) static inline void *pfn_to_virt(unsigned long pfn)
{ {

View File

@ -55,11 +55,11 @@ static __always_inline void pai_kernel_enter(struct pt_regs *regs)
return; return;
if (!static_branch_unlikely(&pai_key)) if (!static_branch_unlikely(&pai_key))
return; return;
if (!S390_lowcore.ccd) if (!get_lowcore()->ccd)
return; return;
if (!user_mode(regs)) if (!user_mode(regs))
return; return;
WRITE_ONCE(S390_lowcore.ccd, S390_lowcore.ccd | PAI_CRYPTO_KERNEL_OFFSET); WRITE_ONCE(get_lowcore()->ccd, get_lowcore()->ccd | PAI_CRYPTO_KERNEL_OFFSET);
} }
static __always_inline void pai_kernel_exit(struct pt_regs *regs) static __always_inline void pai_kernel_exit(struct pt_regs *regs)
@ -68,18 +68,15 @@ static __always_inline void pai_kernel_exit(struct pt_regs *regs)
return; return;
if (!static_branch_unlikely(&pai_key)) if (!static_branch_unlikely(&pai_key))
return; return;
if (!S390_lowcore.ccd) if (!get_lowcore()->ccd)
return; return;
if (!user_mode(regs)) if (!user_mode(regs))
return; return;
WRITE_ONCE(S390_lowcore.ccd, S390_lowcore.ccd & ~PAI_CRYPTO_KERNEL_OFFSET); WRITE_ONCE(get_lowcore()->ccd, get_lowcore()->ccd & ~PAI_CRYPTO_KERNEL_OFFSET);
} }
enum paievt_mode {
PAI_MODE_NONE,
PAI_MODE_SAMPLING,
PAI_MODE_COUNTING,
};
#define PAI_SAVE_AREA(x) ((x)->hw.event_base) #define PAI_SAVE_AREA(x) ((x)->hw.event_base)
#define PAI_CPU_MASK(x) ((x)->hw.addr_filters)
#define PAI_SWLIST(x) (&(x)->hw.tp_list)
#endif #endif

View File

@ -9,7 +9,7 @@
* s390 uses its own implementation for per cpu data, the offset of * s390 uses its own implementation for per cpu data, the offset of
* the cpu local data area is cached in the cpu's lowcore memory. * the cpu local data area is cached in the cpu's lowcore memory.
*/ */
#define __my_cpu_offset S390_lowcore.percpu_offset #define __my_cpu_offset get_lowcore()->percpu_offset
/* /*
* For 64 bit module code, the module may be more than 4G above the * For 64 bit module code, the module may be more than 4G above the

View File

@ -609,7 +609,15 @@ static inline void csp(unsigned int *ptr, unsigned int old, unsigned int new)
: "cc"); : "cc");
} }
static inline void cspg(unsigned long *ptr, unsigned long old, unsigned long new) /**
* cspg() - Compare and Swap and Purge (CSPG)
* @ptr: Pointer to the value to be exchanged
* @old: The expected old value
* @new: The new value
*
* Return: True if compare and swap was successful, otherwise false.
*/
static inline bool cspg(unsigned long *ptr, unsigned long old, unsigned long new)
{ {
union register_pair r1 = { .even = old, .odd = new, }; union register_pair r1 = { .even = old, .odd = new, };
unsigned long address = (unsigned long)ptr | 1; unsigned long address = (unsigned long)ptr | 1;
@ -619,6 +627,7 @@ static inline void cspg(unsigned long *ptr, unsigned long old, unsigned long new
: [r1] "+&d" (r1.pair), "+m" (*ptr) : [r1] "+&d" (r1.pair), "+m" (*ptr)
: [address] "d" (address) : [address] "d" (address)
: "cc"); : "cc");
return old == r1.even;
} }
#define CRDTE_DTT_PAGE 0x00UL #define CRDTE_DTT_PAGE 0x00UL
@ -627,7 +636,18 @@ static inline void cspg(unsigned long *ptr, unsigned long old, unsigned long new
#define CRDTE_DTT_REGION2 0x18UL #define CRDTE_DTT_REGION2 0x18UL
#define CRDTE_DTT_REGION1 0x1cUL #define CRDTE_DTT_REGION1 0x1cUL
static inline void crdte(unsigned long old, unsigned long new, /**
* crdte() - Compare and Replace DAT Table Entry
* @old: The expected old value
* @new: The new value
* @table: Pointer to the value to be exchanged
* @dtt: Table type of the table to be exchanged
* @address: The address mapped by the entry to be replaced
* @asce: The ASCE of this entry
*
* Return: True if compare and replace was successful, otherwise false.
*/
static inline bool crdte(unsigned long old, unsigned long new,
unsigned long *table, unsigned long dtt, unsigned long *table, unsigned long dtt,
unsigned long address, unsigned long asce) unsigned long address, unsigned long asce)
{ {
@ -638,6 +658,7 @@ static inline void crdte(unsigned long old, unsigned long new,
: [r1] "+&d" (r1.pair) : [r1] "+&d" (r1.pair)
: [r2] "d" (r2.pair), [asce] "a" (asce) : [r2] "d" (r2.pair), [asce] "a" (asce)
: "memory", "cc"); : "memory", "cc");
return old == r1.even;
} }
/* /*
@ -1167,7 +1188,7 @@ static inline pte_t ptep_get_and_clear(struct mm_struct *mm,
res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID));
/* At this point the reference through the mapping is still present */ /* At this point the reference through the mapping is still present */
if (mm_is_protected(mm) && pte_present(res)) if (mm_is_protected(mm) && pte_present(res))
uv_convert_owned_from_secure(pte_val(res) & PAGE_MASK); uv_convert_from_secure_pte(res);
return res; return res;
} }
@ -1185,7 +1206,7 @@ static inline pte_t ptep_clear_flush(struct vm_area_struct *vma,
res = ptep_xchg_direct(vma->vm_mm, addr, ptep, __pte(_PAGE_INVALID)); res = ptep_xchg_direct(vma->vm_mm, addr, ptep, __pte(_PAGE_INVALID));
/* At this point the reference through the mapping is still present */ /* At this point the reference through the mapping is still present */
if (mm_is_protected(vma->vm_mm) && pte_present(res)) if (mm_is_protected(vma->vm_mm) && pte_present(res))
uv_convert_owned_from_secure(pte_val(res) & PAGE_MASK); uv_convert_from_secure_pte(res);
return res; return res;
} }
@ -1217,14 +1238,14 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm,
* The notifier should have destroyed all protected vCPUs at this * The notifier should have destroyed all protected vCPUs at this
* point, so the destroy should be successful. * point, so the destroy should be successful.
*/ */
if (full && !uv_destroy_owned_page(pte_val(res) & PAGE_MASK)) if (full && !uv_destroy_pte(res))
return res; return res;
/* /*
* If something went wrong and the page could not be destroyed, or * If something went wrong and the page could not be destroyed, or
* if this is not a mm teardown, the slower export is used as * if this is not a mm teardown, the slower export is used as
* fallback instead. * fallback instead.
*/ */
uv_convert_owned_from_secure(pte_val(res) & PAGE_MASK); uv_convert_from_secure_pte(res);
return res; return res;
} }

View File

@ -14,7 +14,7 @@
static __always_inline int preempt_count(void) static __always_inline int preempt_count(void)
{ {
return READ_ONCE(S390_lowcore.preempt_count) & ~PREEMPT_NEED_RESCHED; return READ_ONCE(get_lowcore()->preempt_count) & ~PREEMPT_NEED_RESCHED;
} }
static __always_inline void preempt_count_set(int pc) static __always_inline void preempt_count_set(int pc)
@ -22,26 +22,26 @@ static __always_inline void preempt_count_set(int pc)
int old, new; int old, new;
do { do {
old = READ_ONCE(S390_lowcore.preempt_count); old = READ_ONCE(get_lowcore()->preempt_count);
new = (old & PREEMPT_NEED_RESCHED) | new = (old & PREEMPT_NEED_RESCHED) |
(pc & ~PREEMPT_NEED_RESCHED); (pc & ~PREEMPT_NEED_RESCHED);
} while (__atomic_cmpxchg(&S390_lowcore.preempt_count, } while (__atomic_cmpxchg(&get_lowcore()->preempt_count,
old, new) != old); old, new) != old);
} }
static __always_inline void set_preempt_need_resched(void) static __always_inline void set_preempt_need_resched(void)
{ {
__atomic_and(~PREEMPT_NEED_RESCHED, &S390_lowcore.preempt_count); __atomic_and(~PREEMPT_NEED_RESCHED, &get_lowcore()->preempt_count);
} }
static __always_inline void clear_preempt_need_resched(void) static __always_inline void clear_preempt_need_resched(void)
{ {
__atomic_or(PREEMPT_NEED_RESCHED, &S390_lowcore.preempt_count); __atomic_or(PREEMPT_NEED_RESCHED, &get_lowcore()->preempt_count);
} }
static __always_inline bool test_preempt_need_resched(void) static __always_inline bool test_preempt_need_resched(void)
{ {
return !(READ_ONCE(S390_lowcore.preempt_count) & PREEMPT_NEED_RESCHED); return !(READ_ONCE(get_lowcore()->preempt_count) & PREEMPT_NEED_RESCHED);
} }
static __always_inline void __preempt_count_add(int val) static __always_inline void __preempt_count_add(int val)
@ -52,11 +52,11 @@ static __always_inline void __preempt_count_add(int val)
*/ */
if (!IS_ENABLED(CONFIG_PROFILE_ALL_BRANCHES)) { if (!IS_ENABLED(CONFIG_PROFILE_ALL_BRANCHES)) {
if (__builtin_constant_p(val) && (val >= -128) && (val <= 127)) { if (__builtin_constant_p(val) && (val >= -128) && (val <= 127)) {
__atomic_add_const(val, &S390_lowcore.preempt_count); __atomic_add_const(val, &get_lowcore()->preempt_count);
return; return;
} }
} }
__atomic_add(val, &S390_lowcore.preempt_count); __atomic_add(val, &get_lowcore()->preempt_count);
} }
static __always_inline void __preempt_count_sub(int val) static __always_inline void __preempt_count_sub(int val)
@ -66,12 +66,12 @@ static __always_inline void __preempt_count_sub(int val)
static __always_inline bool __preempt_count_dec_and_test(void) static __always_inline bool __preempt_count_dec_and_test(void)
{ {
return __atomic_add(-1, &S390_lowcore.preempt_count) == 1; return __atomic_add(-1, &get_lowcore()->preempt_count) == 1;
} }
static __always_inline bool should_resched(int preempt_offset) static __always_inline bool should_resched(int preempt_offset)
{ {
return unlikely(READ_ONCE(S390_lowcore.preempt_count) == return unlikely(READ_ONCE(get_lowcore()->preempt_count) ==
preempt_offset); preempt_offset);
} }
@ -81,12 +81,12 @@ static __always_inline bool should_resched(int preempt_offset)
static __always_inline int preempt_count(void) static __always_inline int preempt_count(void)
{ {
return READ_ONCE(S390_lowcore.preempt_count); return READ_ONCE(get_lowcore()->preempt_count);
} }
static __always_inline void preempt_count_set(int pc) static __always_inline void preempt_count_set(int pc)
{ {
S390_lowcore.preempt_count = pc; get_lowcore()->preempt_count = pc;
} }
static __always_inline void set_preempt_need_resched(void) static __always_inline void set_preempt_need_resched(void)
@ -104,17 +104,17 @@ static __always_inline bool test_preempt_need_resched(void)
static __always_inline void __preempt_count_add(int val) static __always_inline void __preempt_count_add(int val)
{ {
S390_lowcore.preempt_count += val; get_lowcore()->preempt_count += val;
} }
static __always_inline void __preempt_count_sub(int val) static __always_inline void __preempt_count_sub(int val)
{ {
S390_lowcore.preempt_count -= val; get_lowcore()->preempt_count -= val;
} }
static __always_inline bool __preempt_count_dec_and_test(void) static __always_inline bool __preempt_count_dec_and_test(void)
{ {
return !--S390_lowcore.preempt_count && tif_need_resched(); return !--get_lowcore()->preempt_count && tif_need_resched();
} }
static __always_inline bool should_resched(int preempt_offset) static __always_inline bool should_resched(int preempt_offset)

View File

@ -46,17 +46,17 @@ typedef long (*sys_call_ptr_t)(struct pt_regs *regs);
static __always_inline void set_cpu_flag(int flag) static __always_inline void set_cpu_flag(int flag)
{ {
S390_lowcore.cpu_flags |= (1UL << flag); get_lowcore()->cpu_flags |= (1UL << flag);
} }
static __always_inline void clear_cpu_flag(int flag) static __always_inline void clear_cpu_flag(int flag)
{ {
S390_lowcore.cpu_flags &= ~(1UL << flag); get_lowcore()->cpu_flags &= ~(1UL << flag);
} }
static __always_inline bool test_cpu_flag(int flag) static __always_inline bool test_cpu_flag(int flag)
{ {
return S390_lowcore.cpu_flags & (1UL << flag); return get_lowcore()->cpu_flags & (1UL << flag);
} }
static __always_inline bool test_and_set_cpu_flag(int flag) static __always_inline bool test_and_set_cpu_flag(int flag)
@ -269,7 +269,7 @@ static __always_inline unsigned long __current_stack_pointer(void)
static __always_inline bool on_thread_stack(void) static __always_inline bool on_thread_stack(void)
{ {
unsigned long ksp = S390_lowcore.kernel_stack; unsigned long ksp = get_lowcore()->kernel_stack;
return !((ksp ^ current_stack_pointer) & ~(THREAD_SIZE - 1)); return !((ksp ^ current_stack_pointer) & ~(THREAD_SIZE - 1));
} }

View File

@ -84,6 +84,7 @@ struct sclp_info {
unsigned char has_ibs : 1; unsigned char has_ibs : 1;
unsigned char has_skey : 1; unsigned char has_skey : 1;
unsigned char has_kss : 1; unsigned char has_kss : 1;
unsigned char has_diag204_bif : 1;
unsigned char has_gisaf : 1; unsigned char has_gisaf : 1;
unsigned char has_diag318 : 1; unsigned char has_diag318 : 1;
unsigned char has_diag320 : 1; unsigned char has_diag320 : 1;

View File

@ -77,24 +77,24 @@ extern unsigned long max_mappable;
/* The Write Back bit position in the physaddr is given by the SLPC PCI */ /* The Write Back bit position in the physaddr is given by the SLPC PCI */
extern unsigned long mio_wb_bit_mask; extern unsigned long mio_wb_bit_mask;
#define MACHINE_IS_VM (S390_lowcore.machine_flags & MACHINE_FLAG_VM) #define MACHINE_IS_VM (get_lowcore()->machine_flags & MACHINE_FLAG_VM)
#define MACHINE_IS_KVM (S390_lowcore.machine_flags & MACHINE_FLAG_KVM) #define MACHINE_IS_KVM (get_lowcore()->machine_flags & MACHINE_FLAG_KVM)
#define MACHINE_IS_LPAR (S390_lowcore.machine_flags & MACHINE_FLAG_LPAR) #define MACHINE_IS_LPAR (get_lowcore()->machine_flags & MACHINE_FLAG_LPAR)
#define MACHINE_HAS_DIAG9C (S390_lowcore.machine_flags & MACHINE_FLAG_DIAG9C) #define MACHINE_HAS_DIAG9C (get_lowcore()->machine_flags & MACHINE_FLAG_DIAG9C)
#define MACHINE_HAS_ESOP (S390_lowcore.machine_flags & MACHINE_FLAG_ESOP) #define MACHINE_HAS_ESOP (get_lowcore()->machine_flags & MACHINE_FLAG_ESOP)
#define MACHINE_HAS_IDTE (S390_lowcore.machine_flags & MACHINE_FLAG_IDTE) #define MACHINE_HAS_IDTE (get_lowcore()->machine_flags & MACHINE_FLAG_IDTE)
#define MACHINE_HAS_EDAT1 (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT1) #define MACHINE_HAS_EDAT1 (get_lowcore()->machine_flags & MACHINE_FLAG_EDAT1)
#define MACHINE_HAS_EDAT2 (S390_lowcore.machine_flags & MACHINE_FLAG_EDAT2) #define MACHINE_HAS_EDAT2 (get_lowcore()->machine_flags & MACHINE_FLAG_EDAT2)
#define MACHINE_HAS_TOPOLOGY (S390_lowcore.machine_flags & MACHINE_FLAG_TOPOLOGY) #define MACHINE_HAS_TOPOLOGY (get_lowcore()->machine_flags & MACHINE_FLAG_TOPOLOGY)
#define MACHINE_HAS_TE (S390_lowcore.machine_flags & MACHINE_FLAG_TE) #define MACHINE_HAS_TE (get_lowcore()->machine_flags & MACHINE_FLAG_TE)
#define MACHINE_HAS_TLB_LC (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_LC) #define MACHINE_HAS_TLB_LC (get_lowcore()->machine_flags & MACHINE_FLAG_TLB_LC)
#define MACHINE_HAS_TLB_GUEST (S390_lowcore.machine_flags & MACHINE_FLAG_TLB_GUEST) #define MACHINE_HAS_TLB_GUEST (get_lowcore()->machine_flags & MACHINE_FLAG_TLB_GUEST)
#define MACHINE_HAS_NX (S390_lowcore.machine_flags & MACHINE_FLAG_NX) #define MACHINE_HAS_NX (get_lowcore()->machine_flags & MACHINE_FLAG_NX)
#define MACHINE_HAS_GS (S390_lowcore.machine_flags & MACHINE_FLAG_GS) #define MACHINE_HAS_GS (get_lowcore()->machine_flags & MACHINE_FLAG_GS)
#define MACHINE_HAS_SCC (S390_lowcore.machine_flags & MACHINE_FLAG_SCC) #define MACHINE_HAS_SCC (get_lowcore()->machine_flags & MACHINE_FLAG_SCC)
#define MACHINE_HAS_PCI_MIO (S390_lowcore.machine_flags & MACHINE_FLAG_PCI_MIO) #define MACHINE_HAS_PCI_MIO (get_lowcore()->machine_flags & MACHINE_FLAG_PCI_MIO)
#define MACHINE_HAS_RDP (S390_lowcore.machine_flags & MACHINE_FLAG_RDP) #define MACHINE_HAS_RDP (get_lowcore()->machine_flags & MACHINE_FLAG_RDP)
/* /*
* Console mode. Override with conmode= * Console mode. Override with conmode=

View File

@ -11,7 +11,7 @@
#include <asm/lowcore.h> #include <asm/lowcore.h>
#include <asm/processor.h> #include <asm/processor.h>
#define raw_smp_processor_id() (S390_lowcore.cpu_nr) #define raw_smp_processor_id() (get_lowcore()->cpu_nr)
extern struct mutex smp_cpu_state_mutex; extern struct mutex smp_cpu_state_mutex;
extern unsigned int smp_cpu_mt_shift; extern unsigned int smp_cpu_mt_shift;
@ -59,7 +59,7 @@ static inline void smp_cpus_done(unsigned int max_cpus)
{ {
} }
extern int smp_rescan_cpus(void); extern int smp_rescan_cpus(bool early);
extern void __noreturn cpu_die(void); extern void __noreturn cpu_die(void);
extern void __cpu_die(unsigned int cpu); extern void __cpu_die(unsigned int cpu);
extern int __cpu_disable(void); extern int __cpu_disable(void);

View File

@ -8,7 +8,7 @@
#ifdef CONFIG_SOFTIRQ_ON_OWN_STACK #ifdef CONFIG_SOFTIRQ_ON_OWN_STACK
static inline void do_softirq_own_stack(void) static inline void do_softirq_own_stack(void)
{ {
call_on_stack(0, S390_lowcore.async_stack, void, __do_softirq); call_on_stack(0, get_lowcore()->async_stack, void, __do_softirq);
} }
#endif #endif
#endif /* __ASM_S390_SOFTIRQ_STACK_H */ #endif /* __ASM_S390_SOFTIRQ_STACK_H */

View File

@ -16,7 +16,7 @@
#include <asm/processor.h> #include <asm/processor.h>
#include <asm/alternative.h> #include <asm/alternative.h>
#define SPINLOCK_LOCKVAL (S390_lowcore.spinlock_lockval) #define SPINLOCK_LOCKVAL (get_lowcore()->spinlock_lockval)
extern int spin_retry; extern int spin_retry;

View File

@ -65,6 +65,7 @@ struct stack_frame {
unsigned long sie_reason; unsigned long sie_reason;
unsigned long sie_flags; unsigned long sie_flags;
unsigned long sie_control_block_phys; unsigned long sie_control_block_phys;
unsigned long sie_guest_asce;
}; };
}; };
unsigned long gprs[10]; unsigned long gprs[10];

View File

@ -161,16 +161,16 @@ static inline unsigned long local_tick_disable(void)
{ {
unsigned long old; unsigned long old;
old = S390_lowcore.clock_comparator; old = get_lowcore()->clock_comparator;
S390_lowcore.clock_comparator = clock_comparator_max; get_lowcore()->clock_comparator = clock_comparator_max;
set_clock_comparator(S390_lowcore.clock_comparator); set_clock_comparator(get_lowcore()->clock_comparator);
return old; return old;
} }
static inline void local_tick_enable(unsigned long comp) static inline void local_tick_enable(unsigned long comp)
{ {
S390_lowcore.clock_comparator = comp; get_lowcore()->clock_comparator = comp;
set_clock_comparator(S390_lowcore.clock_comparator); set_clock_comparator(get_lowcore()->clock_comparator);
} }
#define CLOCK_TICK_RATE 1193180 /* Underlying HZ */ #define CLOCK_TICK_RATE 1193180 /* Underlying HZ */

View File

@ -483,9 +483,9 @@ static inline int is_prot_virt_host(void)
int uv_pin_shared(unsigned long paddr); int uv_pin_shared(unsigned long paddr);
int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb); int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb);
int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr); int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr);
int uv_destroy_owned_page(unsigned long paddr); int uv_destroy_folio(struct folio *folio);
int uv_convert_from_secure(unsigned long paddr); int uv_destroy_pte(pte_t pte);
int uv_convert_owned_from_secure(unsigned long paddr); int uv_convert_from_secure_pte(pte_t pte);
int gmap_convert_to_secure(struct gmap *gmap, unsigned long gaddr); int gmap_convert_to_secure(struct gmap *gmap, unsigned long gaddr);
void setup_uv(void); void setup_uv(void);
@ -498,17 +498,17 @@ static inline int uv_pin_shared(unsigned long paddr)
return 0; return 0;
} }
static inline int uv_destroy_owned_page(unsigned long paddr) static inline int uv_destroy_folio(struct folio *folio)
{ {
return 0; return 0;
} }
static inline int uv_convert_from_secure(unsigned long paddr) static inline int uv_destroy_pte(pte_t pte)
{ {
return 0; return 0;
} }
static inline int uv_convert_owned_from_secure(unsigned long paddr) static inline int uv_convert_from_secure_pte(pte_t pte)
{ {
return 0; return 0;
} }

View File

@ -4,16 +4,20 @@
static inline void update_timer_sys(void) static inline void update_timer_sys(void)
{ {
S390_lowcore.system_timer += S390_lowcore.last_update_timer - S390_lowcore.exit_timer; struct lowcore *lc = get_lowcore();
S390_lowcore.user_timer += S390_lowcore.exit_timer - S390_lowcore.sys_enter_timer;
S390_lowcore.last_update_timer = S390_lowcore.sys_enter_timer; lc->system_timer += lc->last_update_timer - lc->exit_timer;
lc->user_timer += lc->exit_timer - lc->sys_enter_timer;
lc->last_update_timer = lc->sys_enter_timer;
} }
static inline void update_timer_mcck(void) static inline void update_timer_mcck(void)
{ {
S390_lowcore.system_timer += S390_lowcore.last_update_timer - S390_lowcore.exit_timer; struct lowcore *lc = get_lowcore();
S390_lowcore.user_timer += S390_lowcore.exit_timer - S390_lowcore.mcck_enter_timer;
S390_lowcore.last_update_timer = S390_lowcore.mcck_enter_timer; lc->system_timer += lc->last_update_timer - lc->exit_timer;
lc->user_timer += lc->exit_timer - lc->mcck_enter_timer;
lc->last_update_timer = lc->mcck_enter_timer;
} }
#endif /* _S390_VTIME_H */ #endif /* _S390_VTIME_H */

View File

@ -63,6 +63,7 @@ int main(void)
OFFSET(__SF_SIE_REASON, stack_frame, sie_reason); OFFSET(__SF_SIE_REASON, stack_frame, sie_reason);
OFFSET(__SF_SIE_FLAGS, stack_frame, sie_flags); OFFSET(__SF_SIE_FLAGS, stack_frame, sie_flags);
OFFSET(__SF_SIE_CONTROL_PHYS, stack_frame, sie_control_block_phys); OFFSET(__SF_SIE_CONTROL_PHYS, stack_frame, sie_control_block_phys);
OFFSET(__SF_SIE_GUEST_ASCE, stack_frame, sie_guest_asce);
DEFINE(STACK_FRAME_OVERHEAD, sizeof(struct stack_frame)); DEFINE(STACK_FRAME_OVERHEAD, sizeof(struct stack_frame));
BLANK(); BLANK();
OFFSET(__SFUSER_BACKCHAIN, stack_frame_user, back_chain); OFFSET(__SFUSER_BACKCHAIN, stack_frame_user, back_chain);

View File

@ -185,6 +185,8 @@ int diag14(unsigned long rx, unsigned long ry1, unsigned long subcode)
} }
EXPORT_SYMBOL(diag14); EXPORT_SYMBOL(diag14);
#define DIAG204_BUSY_RC 8
static inline int __diag204(unsigned long *subcode, unsigned long size, void *addr) static inline int __diag204(unsigned long *subcode, unsigned long size, void *addr)
{ {
union register_pair rp = { .even = *subcode, .odd = size }; union register_pair rp = { .even = *subcode, .odd = size };
@ -215,16 +217,18 @@ int diag204(unsigned long subcode, unsigned long size, void *addr)
{ {
if (addr) { if (addr) {
if (WARN_ON_ONCE(!is_vmalloc_addr(addr))) if (WARN_ON_ONCE(!is_vmalloc_addr(addr)))
return -1; return -EINVAL;
if (WARN_ON_ONCE(!IS_ALIGNED((unsigned long)addr, PAGE_SIZE))) if (WARN_ON_ONCE(!IS_ALIGNED((unsigned long)addr, PAGE_SIZE)))
return -1; return -EINVAL;
} }
if ((subcode & DIAG204_SUBCODE_MASK) == DIAG204_SUBC_STIB4) if ((subcode & DIAG204_SUBCODE_MASK) == DIAG204_SUBC_STIB4)
addr = (void *)pfn_to_phys(vmalloc_to_pfn(addr)); addr = (void *)pfn_to_phys(vmalloc_to_pfn(addr));
diag_stat_inc(DIAG_STAT_X204); diag_stat_inc(DIAG_STAT_X204);
size = __diag204(&subcode, size, addr); size = __diag204(&subcode, size, addr);
if (subcode) if (subcode == DIAG204_BUSY_RC)
return -1; return -EBUSY;
else if (subcode)
return -EOPNOTSUPP;
return size; return size;
} }
EXPORT_SYMBOL(diag204); EXPORT_SYMBOL(diag204);

View File

@ -61,28 +61,28 @@ static bool in_task_stack(unsigned long sp, struct task_struct *task,
static bool in_irq_stack(unsigned long sp, struct stack_info *info) static bool in_irq_stack(unsigned long sp, struct stack_info *info)
{ {
unsigned long stack = S390_lowcore.async_stack - STACK_INIT_OFFSET; unsigned long stack = get_lowcore()->async_stack - STACK_INIT_OFFSET;
return in_stack(sp, info, STACK_TYPE_IRQ, stack); return in_stack(sp, info, STACK_TYPE_IRQ, stack);
} }
static bool in_nodat_stack(unsigned long sp, struct stack_info *info) static bool in_nodat_stack(unsigned long sp, struct stack_info *info)
{ {
unsigned long stack = S390_lowcore.nodat_stack - STACK_INIT_OFFSET; unsigned long stack = get_lowcore()->nodat_stack - STACK_INIT_OFFSET;
return in_stack(sp, info, STACK_TYPE_NODAT, stack); return in_stack(sp, info, STACK_TYPE_NODAT, stack);
} }
static bool in_mcck_stack(unsigned long sp, struct stack_info *info) static bool in_mcck_stack(unsigned long sp, struct stack_info *info)
{ {
unsigned long stack = S390_lowcore.mcck_stack - STACK_INIT_OFFSET; unsigned long stack = get_lowcore()->mcck_stack - STACK_INIT_OFFSET;
return in_stack(sp, info, STACK_TYPE_MCCK, stack); return in_stack(sp, info, STACK_TYPE_MCCK, stack);
} }
static bool in_restart_stack(unsigned long sp, struct stack_info *info) static bool in_restart_stack(unsigned long sp, struct stack_info *info)
{ {
unsigned long stack = S390_lowcore.restart_stack - STACK_INIT_OFFSET; unsigned long stack = get_lowcore()->restart_stack - STACK_INIT_OFFSET;
return in_stack(sp, info, STACK_TYPE_RESTART, stack); return in_stack(sp, info, STACK_TYPE_RESTART, stack);
} }

View File

@ -72,7 +72,7 @@ static void __init reset_tod_clock(void)
memset(&tod_clock_base, 0, sizeof(tod_clock_base)); memset(&tod_clock_base, 0, sizeof(tod_clock_base));
tod_clock_base.tod = TOD_UNIX_EPOCH; tod_clock_base.tod = TOD_UNIX_EPOCH;
S390_lowcore.last_update_clock = TOD_UNIX_EPOCH; get_lowcore()->last_update_clock = TOD_UNIX_EPOCH;
} }
/* /*
@ -99,7 +99,7 @@ static noinline __init void detect_machine_type(void)
/* Check current-configuration-level */ /* Check current-configuration-level */
if (stsi(NULL, 0, 0, 0) <= 2) { if (stsi(NULL, 0, 0, 0) <= 2) {
S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR; get_lowcore()->machine_flags |= MACHINE_FLAG_LPAR;
return; return;
} }
/* Get virtual-machine cpu information. */ /* Get virtual-machine cpu information. */
@ -108,9 +108,9 @@ static noinline __init void detect_machine_type(void)
/* Detect known hypervisors */ /* Detect known hypervisors */
if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3)) if (!memcmp(vmms->vm[0].cpi, "\xd2\xe5\xd4", 3))
S390_lowcore.machine_flags |= MACHINE_FLAG_KVM; get_lowcore()->machine_flags |= MACHINE_FLAG_KVM;
else if (!memcmp(vmms->vm[0].cpi, "\xa9\x61\xe5\xd4", 4)) else if (!memcmp(vmms->vm[0].cpi, "\xa9\x61\xe5\xd4", 4))
S390_lowcore.machine_flags |= MACHINE_FLAG_VM; get_lowcore()->machine_flags |= MACHINE_FLAG_VM;
} }
/* Remove leading, trailing and double whitespace. */ /* Remove leading, trailing and double whitespace. */
@ -166,7 +166,7 @@ static __init void setup_topology(void)
if (!test_facility(11)) if (!test_facility(11))
return; return;
S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY; get_lowcore()->machine_flags |= MACHINE_FLAG_TOPOLOGY;
for (max_mnest = 6; max_mnest > 1; max_mnest--) { for (max_mnest = 6; max_mnest > 1; max_mnest--) {
if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0) if (stsi(&sysinfo_page, 15, 1, max_mnest) == 0)
break; break;
@ -186,8 +186,8 @@ static noinline __init void setup_lowcore_early(void)
psw.addr = (unsigned long)early_pgm_check_handler; psw.addr = (unsigned long)early_pgm_check_handler;
psw.mask = PSW_KERNEL_BITS; psw.mask = PSW_KERNEL_BITS;
S390_lowcore.program_new_psw = psw; get_lowcore()->program_new_psw = psw;
S390_lowcore.preempt_count = INIT_PREEMPT_COUNT; get_lowcore()->preempt_count = INIT_PREEMPT_COUNT;
} }
static noinline __init void setup_facility_list(void) static noinline __init void setup_facility_list(void)
@ -211,43 +211,43 @@ static __init void detect_diag9c(void)
EX_TABLE(0b,1b) EX_TABLE(0b,1b)
: "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc"); : "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
if (!rc) if (!rc)
S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C; get_lowcore()->machine_flags |= MACHINE_FLAG_DIAG9C;
} }
static __init void detect_machine_facilities(void) static __init void detect_machine_facilities(void)
{ {
if (test_facility(8)) { if (test_facility(8)) {
S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT1; get_lowcore()->machine_flags |= MACHINE_FLAG_EDAT1;
system_ctl_set_bit(0, CR0_EDAT_BIT); system_ctl_set_bit(0, CR0_EDAT_BIT);
} }
if (test_facility(78)) if (test_facility(78))
S390_lowcore.machine_flags |= MACHINE_FLAG_EDAT2; get_lowcore()->machine_flags |= MACHINE_FLAG_EDAT2;
if (test_facility(3)) if (test_facility(3))
S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE; get_lowcore()->machine_flags |= MACHINE_FLAG_IDTE;
if (test_facility(50) && test_facility(73)) { if (test_facility(50) && test_facility(73)) {
S390_lowcore.machine_flags |= MACHINE_FLAG_TE; get_lowcore()->machine_flags |= MACHINE_FLAG_TE;
system_ctl_set_bit(0, CR0_TRANSACTIONAL_EXECUTION_BIT); system_ctl_set_bit(0, CR0_TRANSACTIONAL_EXECUTION_BIT);
} }
if (test_facility(51)) if (test_facility(51))
S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_LC; get_lowcore()->machine_flags |= MACHINE_FLAG_TLB_LC;
if (test_facility(129)) if (test_facility(129))
system_ctl_set_bit(0, CR0_VECTOR_BIT); system_ctl_set_bit(0, CR0_VECTOR_BIT);
if (test_facility(130)) if (test_facility(130))
S390_lowcore.machine_flags |= MACHINE_FLAG_NX; get_lowcore()->machine_flags |= MACHINE_FLAG_NX;
if (test_facility(133)) if (test_facility(133))
S390_lowcore.machine_flags |= MACHINE_FLAG_GS; get_lowcore()->machine_flags |= MACHINE_FLAG_GS;
if (test_facility(139) && (tod_clock_base.tod >> 63)) { 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; get_lowcore()->machine_flags |= MACHINE_FLAG_SCC;
clock_comparator_max = -1ULL >> 1; clock_comparator_max = -1ULL >> 1;
system_ctl_set_bit(0, CR0_CLOCK_COMPARATOR_SIGN_BIT); system_ctl_set_bit(0, CR0_CLOCK_COMPARATOR_SIGN_BIT);
} }
if (IS_ENABLED(CONFIG_PCI) && test_facility(153)) { if (IS_ENABLED(CONFIG_PCI) && test_facility(153)) {
S390_lowcore.machine_flags |= MACHINE_FLAG_PCI_MIO; get_lowcore()->machine_flags |= MACHINE_FLAG_PCI_MIO;
/* the control bit is set during PCI initialization */ /* the control bit is set during PCI initialization */
} }
if (test_facility(194)) if (test_facility(194))
S390_lowcore.machine_flags |= MACHINE_FLAG_RDP; get_lowcore()->machine_flags |= MACHINE_FLAG_RDP;
} }
static inline void save_vector_registers(void) static inline void save_vector_registers(void)

View File

@ -179,6 +179,7 @@ SYM_FUNC_END(__switch_to_asm)
* %r2 pointer to sie control block phys * %r2 pointer to sie control block phys
* %r3 pointer to sie control block virt * %r3 pointer to sie control block virt
* %r4 guest register save area * %r4 guest register save area
* %r5 guest asce
*/ */
SYM_FUNC_START(__sie64a) SYM_FUNC_START(__sie64a)
stmg %r6,%r14,__SF_GPRS(%r15) # save kernel registers stmg %r6,%r14,__SF_GPRS(%r15) # save kernel registers
@ -186,15 +187,12 @@ SYM_FUNC_START(__sie64a)
stg %r2,__SF_SIE_CONTROL_PHYS(%r15) # save sie block physical.. stg %r2,__SF_SIE_CONTROL_PHYS(%r15) # save sie block physical..
stg %r3,__SF_SIE_CONTROL(%r15) # ...and virtual addresses stg %r3,__SF_SIE_CONTROL(%r15) # ...and virtual addresses
stg %r4,__SF_SIE_SAVEAREA(%r15) # save guest register save area stg %r4,__SF_SIE_SAVEAREA(%r15) # save guest register save area
stg %r5,__SF_SIE_GUEST_ASCE(%r15) # save guest asce
xc __SF_SIE_REASON(8,%r15),__SF_SIE_REASON(%r15) # reason code = 0 xc __SF_SIE_REASON(8,%r15),__SF_SIE_REASON(%r15) # reason code = 0
mvc __SF_SIE_FLAGS(8,%r15),__TI_flags(%r12) # copy thread flags mvc __SF_SIE_FLAGS(8,%r15),__TI_flags(%r12) # copy thread flags
lmg %r0,%r13,0(%r4) # load guest gprs 0-13 lmg %r0,%r13,0(%r4) # load guest gprs 0-13
lg %r14,__LC_GMAP # get gmap pointer
ltgr %r14,%r14
jz .Lsie_gmap
oi __LC_CPU_FLAGS+7,_CIF_SIE oi __LC_CPU_FLAGS+7,_CIF_SIE
lctlg %c1,%c1,__GMAP_ASCE(%r14) # load primary asce lctlg %c1,%c1,__SF_SIE_GUEST_ASCE(%r15) # load primary asce
.Lsie_gmap:
lg %r14,__SF_SIE_CONTROL(%r15) # get control block pointer lg %r14,__SF_SIE_CONTROL(%r15) # get control block pointer
oi __SIE_PROG0C+3(%r14),1 # we are going into SIE now oi __SIE_PROG0C+3(%r14),1 # we are going into SIE now
tm __SIE_PROG20+3(%r14),3 # last exit... tm __SIE_PROG20+3(%r14),3 # last exit...

View File

@ -24,6 +24,7 @@ static DEFINE_PER_CPU(struct s390_idle_data, s390_idle);
void account_idle_time_irq(void) void account_idle_time_irq(void)
{ {
struct s390_idle_data *idle = this_cpu_ptr(&s390_idle); struct s390_idle_data *idle = this_cpu_ptr(&s390_idle);
struct lowcore *lc = get_lowcore();
unsigned long idle_time; unsigned long idle_time;
u64 cycles_new[8]; u64 cycles_new[8];
int i; int i;
@ -34,13 +35,13 @@ void account_idle_time_irq(void)
this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]); this_cpu_add(mt_cycles[i], cycles_new[i] - idle->mt_cycles_enter[i]);
} }
idle_time = S390_lowcore.int_clock - idle->clock_idle_enter; idle_time = lc->int_clock - idle->clock_idle_enter;
S390_lowcore.steal_timer += idle->clock_idle_enter - S390_lowcore.last_update_clock; lc->steal_timer += idle->clock_idle_enter - lc->last_update_clock;
S390_lowcore.last_update_clock = S390_lowcore.int_clock; lc->last_update_clock = lc->int_clock;
S390_lowcore.system_timer += S390_lowcore.last_update_timer - idle->timer_idle_enter; lc->system_timer += lc->last_update_timer - idle->timer_idle_enter;
S390_lowcore.last_update_timer = S390_lowcore.sys_enter_timer; lc->last_update_timer = lc->sys_enter_timer;
/* Account time spent with enabled wait psw loaded as idle time. */ /* Account time spent with enabled wait psw loaded as idle time. */
WRITE_ONCE(idle->idle_time, READ_ONCE(idle->idle_time) + idle_time); WRITE_ONCE(idle->idle_time, READ_ONCE(idle->idle_time) + idle_time);

View File

@ -100,8 +100,8 @@ static const struct irq_class irqclass_sub_desc[] = {
static void do_IRQ(struct pt_regs *regs, int irq) static void do_IRQ(struct pt_regs *regs, int irq)
{ {
if (tod_after_eq(S390_lowcore.int_clock, if (tod_after_eq(get_lowcore()->int_clock,
S390_lowcore.clock_comparator)) get_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);
@ -111,7 +111,7 @@ static int on_async_stack(void)
{ {
unsigned long frame = current_frame_address(); unsigned long frame = current_frame_address();
return ((S390_lowcore.async_stack ^ frame) & ~(THREAD_SIZE - 1)) == 0; return ((get_lowcore()->async_stack ^ frame) & ~(THREAD_SIZE - 1)) == 0;
} }
static void do_irq_async(struct pt_regs *regs, int irq) static void do_irq_async(struct pt_regs *regs, int irq)
@ -119,7 +119,7 @@ static void do_irq_async(struct pt_regs *regs, int irq)
if (on_async_stack()) { if (on_async_stack()) {
do_IRQ(regs, irq); do_IRQ(regs, irq);
} else { } else {
call_on_stack(2, S390_lowcore.async_stack, void, do_IRQ, call_on_stack(2, get_lowcore()->async_stack, void, do_IRQ,
struct pt_regs *, regs, int, irq); struct pt_regs *, regs, int, irq);
} }
} }
@ -153,8 +153,8 @@ void noinstr do_io_irq(struct pt_regs *regs)
set_cpu_flag(CIF_NOHZ_DELAY); set_cpu_flag(CIF_NOHZ_DELAY);
do { do {
regs->tpi_info = S390_lowcore.tpi_info; regs->tpi_info = get_lowcore()->tpi_info;
if (S390_lowcore.tpi_info.adapter_IO) if (get_lowcore()->tpi_info.adapter_IO)
do_irq_async(regs, THIN_INTERRUPT); do_irq_async(regs, THIN_INTERRUPT);
else else
do_irq_async(regs, IO_INTERRUPT); do_irq_async(regs, IO_INTERRUPT);
@ -183,9 +183,9 @@ void noinstr do_ext_irq(struct pt_regs *regs)
current->thread.last_break = regs->last_break; current->thread.last_break = regs->last_break;
} }
regs->int_code = S390_lowcore.ext_int_code_addr; regs->int_code = get_lowcore()->ext_int_code_addr;
regs->int_parm = S390_lowcore.ext_params; regs->int_parm = get_lowcore()->ext_params;
regs->int_parm_long = S390_lowcore.ext_params2; regs->int_parm_long = get_lowcore()->ext_params2;
from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT); from_idle = test_and_clear_cpu_flag(CIF_ENABLED_WAIT);
if (from_idle) if (from_idle)

View File

@ -52,7 +52,7 @@ static void __do_machine_kdump(void *data)
purgatory = (purgatory_t)image->start; purgatory = (purgatory_t)image->start;
/* store_status() saved the prefix register to lowcore */ /* store_status() saved the prefix register to lowcore */
prefix = (unsigned long) S390_lowcore.prefixreg_save_area; prefix = (unsigned long)get_lowcore()->prefixreg_save_area;
/* Now do the reset */ /* Now do the reset */
s390_reset_system(); s390_reset_system();
@ -91,7 +91,7 @@ static noinline void __machine_kdump(void *image)
continue; continue;
} }
/* Store status of the boot CPU */ /* Store status of the boot CPU */
mcesa = __va(S390_lowcore.mcesad & MCESA_ORIGIN_MASK); mcesa = __va(get_lowcore()->mcesad & MCESA_ORIGIN_MASK);
if (cpu_has_vx()) if (cpu_has_vx())
save_vx_regs((__vector128 *) mcesa->vector_save_area); save_vx_regs((__vector128 *) mcesa->vector_save_area);
if (MACHINE_HAS_GS) { if (MACHINE_HAS_GS) {

View File

@ -117,6 +117,7 @@ static __always_inline char *u64_to_hex(char *dest, u64 val)
static notrace void s390_handle_damage(void) static notrace void s390_handle_damage(void)
{ {
struct lowcore *lc = get_lowcore();
union ctlreg0 cr0, cr0_new; union ctlreg0 cr0, cr0_new;
char message[100]; char message[100];
psw_t psw_save; psw_t psw_save;
@ -125,7 +126,7 @@ static notrace void s390_handle_damage(void)
smp_emergency_stop(); smp_emergency_stop();
diag_amode31_ops.diag308_reset(); diag_amode31_ops.diag308_reset();
ptr = nmi_puts(message, "System stopped due to unrecoverable machine check, code: 0x"); ptr = nmi_puts(message, "System stopped due to unrecoverable machine check, code: 0x");
u64_to_hex(ptr, S390_lowcore.mcck_interruption_code); u64_to_hex(ptr, lc->mcck_interruption_code);
/* /*
* Disable low address protection and make machine check new PSW a * Disable low address protection and make machine check new PSW a
@ -135,17 +136,17 @@ static notrace void s390_handle_damage(void)
cr0_new = cr0; cr0_new = cr0;
cr0_new.lap = 0; cr0_new.lap = 0;
local_ctl_load(0, &cr0_new.reg); local_ctl_load(0, &cr0_new.reg);
psw_save = S390_lowcore.mcck_new_psw; psw_save = lc->mcck_new_psw;
psw_bits(S390_lowcore.mcck_new_psw).io = 0; psw_bits(lc->mcck_new_psw).io = 0;
psw_bits(S390_lowcore.mcck_new_psw).ext = 0; psw_bits(lc->mcck_new_psw).ext = 0;
psw_bits(S390_lowcore.mcck_new_psw).wait = 1; psw_bits(lc->mcck_new_psw).wait = 1;
sclp_emergency_printk(message); sclp_emergency_printk(message);
/* /*
* Restore machine check new PSW and control register 0 to original * Restore machine check new PSW and control register 0 to original
* values. This makes possible system dump analysis easier. * values. This makes possible system dump analysis easier.
*/ */
S390_lowcore.mcck_new_psw = psw_save; lc->mcck_new_psw = psw_save;
local_ctl_load(0, &cr0.reg); local_ctl_load(0, &cr0.reg);
disabled_wait(); disabled_wait();
while (1); while (1);
@ -226,7 +227,7 @@ static bool notrace nmi_registers_valid(union mci mci)
/* /*
* Set the clock comparator register to the next expected value. * Set the clock comparator register to the next expected value.
*/ */
set_clock_comparator(S390_lowcore.clock_comparator); set_clock_comparator(get_lowcore()->clock_comparator);
if (!mci.gr || !mci.fp || !mci.fc) if (!mci.gr || !mci.fp || !mci.fc)
return false; return false;
/* /*
@ -252,7 +253,7 @@ static bool notrace nmi_registers_valid(union mci mci)
* check handling must take care of this. The host values are saved by * check handling must take care of this. The host values are saved by
* KVM and are not affected. * KVM and are not affected.
*/ */
cr2.reg = S390_lowcore.cregs_save_area[2]; cr2.reg = get_lowcore()->cregs_save_area[2];
if (cr2.gse && !mci.gs && !test_cpu_flag(CIF_MCCK_GUEST)) if (cr2.gse && !mci.gs && !test_cpu_flag(CIF_MCCK_GUEST))
return false; return false;
if (!mci.ms || !mci.pm || !mci.ia) if (!mci.ms || !mci.pm || !mci.ia)
@ -278,11 +279,10 @@ static void notrace s390_backup_mcck_info(struct pt_regs *regs)
sie_page = container_of(sie_block, struct sie_page, sie_block); sie_page = container_of(sie_block, struct sie_page, sie_block);
mcck_backup = &sie_page->mcck_info; mcck_backup = &sie_page->mcck_info;
mcck_backup->mcic = S390_lowcore.mcck_interruption_code & mcck_backup->mcic = get_lowcore()->mcck_interruption_code &
~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE); ~(MCCK_CODE_CP | MCCK_CODE_EXT_DAMAGE);
mcck_backup->ext_damage_code = S390_lowcore.external_damage_code; mcck_backup->ext_damage_code = get_lowcore()->external_damage_code;
mcck_backup->failing_storage_address mcck_backup->failing_storage_address = get_lowcore()->failing_storage_address;
= S390_lowcore.failing_storage_address;
} }
NOKPROBE_SYMBOL(s390_backup_mcck_info); NOKPROBE_SYMBOL(s390_backup_mcck_info);
@ -302,6 +302,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
static int ipd_count; static int ipd_count;
static DEFINE_SPINLOCK(ipd_lock); static DEFINE_SPINLOCK(ipd_lock);
static unsigned long long last_ipd; static unsigned long long last_ipd;
struct lowcore *lc = get_lowcore();
struct mcck_struct *mcck; struct mcck_struct *mcck;
unsigned long long tmp; unsigned long long tmp;
irqentry_state_t irq_state; irqentry_state_t irq_state;
@ -314,7 +315,7 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
if (user_mode(regs)) if (user_mode(regs))
update_timer_mcck(); update_timer_mcck();
inc_irq_stat(NMI_NMI); inc_irq_stat(NMI_NMI);
mci.val = S390_lowcore.mcck_interruption_code; mci.val = lc->mcck_interruption_code;
mcck = this_cpu_ptr(&cpu_mcck); mcck = this_cpu_ptr(&cpu_mcck);
/* /*
@ -382,9 +383,9 @@ void notrace s390_do_machine_check(struct pt_regs *regs)
} }
if (mci.ed && mci.ec) { if (mci.ed && mci.ec) {
/* External damage */ /* External damage */
if (S390_lowcore.external_damage_code & (1U << ED_STP_SYNC)) if (lc->external_damage_code & (1U << ED_STP_SYNC))
mcck->stp_queue |= stp_sync_check(); mcck->stp_queue |= stp_sync_check();
if (S390_lowcore.external_damage_code & (1U << ED_STP_ISLAND)) if (lc->external_damage_code & (1U << ED_STP_ISLAND))
mcck->stp_queue |= stp_island_check(); mcck->stp_queue |= stp_island_check();
mcck_pending = 1; mcck_pending = 1;
} }

View File

@ -1022,7 +1022,7 @@ static void cpumsf_pmu_enable(struct pmu *pmu)
} }
/* Load current program parameter */ /* Load current program parameter */
lpp(&S390_lowcore.lpp); lpp(&get_lowcore()->lpp);
debug_sprintf_event(sfdbg, 6, "%s: es %i cs %i ed %i cd %i " debug_sprintf_event(sfdbg, 6, "%s: es %i cs %i ed %i cd %i "
"interval %#lx tear %#lx dear %#lx\n", __func__, "interval %#lx tear %#lx dear %#lx\n", __func__,

View File

@ -36,8 +36,8 @@ struct paicrypt_map {
struct pai_userdata *save; /* Page to store no-zero counters */ struct pai_userdata *save; /* Page to store no-zero counters */
unsigned int active_events; /* # of PAI crypto users */ unsigned int active_events; /* # of PAI crypto users */
refcount_t refcnt; /* Reference count mapped buffers */ refcount_t refcnt; /* Reference count mapped buffers */
enum paievt_mode mode; /* Type of event */
struct perf_event *event; /* Perf event for sampling */ struct perf_event *event; /* Perf event for sampling */
struct list_head syswide_list; /* List system-wide sampling events */
}; };
struct paicrypt_mapptr { struct paicrypt_mapptr {
@ -84,20 +84,16 @@ static DEFINE_MUTEX(pai_reserve_mutex);
/* Adjust usage counters and remove allocated memory when all users are /* Adjust usage counters and remove allocated memory when all users are
* gone. * gone.
*/ */
static void paicrypt_event_destroy(struct perf_event *event) static void paicrypt_event_destroy_cpu(struct perf_event *event, int cpu)
{ {
struct paicrypt_mapptr *mp = per_cpu_ptr(paicrypt_root.mapptr, struct paicrypt_mapptr *mp = per_cpu_ptr(paicrypt_root.mapptr, cpu);
event->cpu);
struct paicrypt_map *cpump = mp->mapptr; struct paicrypt_map *cpump = mp->mapptr;
static_branch_dec(&pai_key);
mutex_lock(&pai_reserve_mutex); mutex_lock(&pai_reserve_mutex);
debug_sprintf_event(cfm_dbg, 5, "%s event %#llx cpu %d users %d" debug_sprintf_event(cfm_dbg, 5, "%s event %#llx cpu %d users %d "
" mode %d refcnt %u\n", __func__, "refcnt %u\n", __func__, event->attr.config,
event->attr.config, event->cpu, event->cpu, cpump->active_events,
cpump->active_events, cpump->mode,
refcount_read(&cpump->refcnt)); refcount_read(&cpump->refcnt));
free_page(PAI_SAVE_AREA(event));
if (refcount_dec_and_test(&cpump->refcnt)) { if (refcount_dec_and_test(&cpump->refcnt)) {
debug_sprintf_event(cfm_dbg, 4, "%s page %#lx save %p\n", debug_sprintf_event(cfm_dbg, 4, "%s page %#lx save %p\n",
__func__, (unsigned long)cpump->page, __func__, (unsigned long)cpump->page,
@ -111,6 +107,23 @@ static void paicrypt_event_destroy(struct perf_event *event)
mutex_unlock(&pai_reserve_mutex); mutex_unlock(&pai_reserve_mutex);
} }
static void paicrypt_event_destroy(struct perf_event *event)
{
int cpu;
static_branch_dec(&pai_key);
free_page(PAI_SAVE_AREA(event));
if (event->cpu == -1) {
struct cpumask *mask = PAI_CPU_MASK(event);
for_each_cpu(cpu, mask)
paicrypt_event_destroy_cpu(event, cpu);
kfree(mask);
} else {
paicrypt_event_destroy_cpu(event, event->cpu);
}
}
static u64 paicrypt_getctr(unsigned long *page, int nr, bool kernel) static u64 paicrypt_getctr(unsigned long *page, int nr, bool kernel)
{ {
if (kernel) if (kernel)
@ -156,23 +169,15 @@ static u64 paicrypt_getall(struct perf_event *event)
return sum; return sum;
} }
/* Used to avoid races in checking concurrent access of counting and /* Check concurrent access of counting and sampling for crypto events.
* sampling for crypto events
*
* Only one instance of event pai_crypto/CRYPTO_ALL/ for sampling is
* allowed and when this event is running, no counting event is allowed.
* Several counting events are allowed in parallel, but no sampling event
* is allowed while one (or more) counting events are running.
*
* This function is called in process context and it is save to block. * This function is called in process context and it is save to block.
* When the event initialization functions fails, no other call back will * When the event initialization functions fails, no other call back will
* be invoked. * be invoked.
* *
* Allocate the memory for the event. * Allocate the memory for the event.
*/ */
static struct paicrypt_map *paicrypt_busy(struct perf_event *event) static struct paicrypt_map *paicrypt_busy(struct perf_event *event, int cpu)
{ {
struct perf_event_attr *a = &event->attr;
struct paicrypt_map *cpump = NULL; struct paicrypt_map *cpump = NULL;
struct paicrypt_mapptr *mp; struct paicrypt_mapptr *mp;
int rc; int rc;
@ -185,7 +190,7 @@ static struct paicrypt_map *paicrypt_busy(struct perf_event *event)
goto unlock; goto unlock;
/* Allocate node for this event */ /* Allocate node for this event */
mp = per_cpu_ptr(paicrypt_root.mapptr, event->cpu); mp = per_cpu_ptr(paicrypt_root.mapptr, cpu);
cpump = mp->mapptr; cpump = mp->mapptr;
if (!cpump) { /* Paicrypt_map allocated? */ if (!cpump) { /* Paicrypt_map allocated? */
cpump = kzalloc(sizeof(*cpump), GFP_KERNEL); cpump = kzalloc(sizeof(*cpump), GFP_KERNEL);
@ -193,25 +198,9 @@ static struct paicrypt_map *paicrypt_busy(struct perf_event *event)
rc = -ENOMEM; rc = -ENOMEM;
goto free_root; goto free_root;
} }
INIT_LIST_HEAD(&cpump->syswide_list);
} }
if (a->sample_period) { /* Sampling requested */
if (cpump->mode != PAI_MODE_NONE)
rc = -EBUSY; /* ... sampling/counting active */
} else { /* Counting requested */
if (cpump->mode == PAI_MODE_SAMPLING)
rc = -EBUSY; /* ... and sampling active */
}
/*
* This error case triggers when there is a conflict:
* Either sampling requested and counting already active, or visa
* versa. Therefore the struct paicrypto_map for this CPU is
* needed or the error could not have occurred. Only adjust root
* node refcount.
*/
if (rc)
goto free_root;
/* Allocate memory for counter page and counter extraction. /* Allocate memory for counter page and counter extraction.
* Only the first counting event has to allocate a page. * Only the first counting event has to allocate a page.
*/ */
@ -235,26 +224,58 @@ static struct paicrypt_map *paicrypt_busy(struct perf_event *event)
/* Set mode and reference count */ /* Set mode and reference count */
rc = 0; rc = 0;
refcount_set(&cpump->refcnt, 1); refcount_set(&cpump->refcnt, 1);
cpump->mode = a->sample_period ? PAI_MODE_SAMPLING : PAI_MODE_COUNTING;
mp->mapptr = cpump; mp->mapptr = cpump;
debug_sprintf_event(cfm_dbg, 5, "%s sample_period %#llx users %d" debug_sprintf_event(cfm_dbg, 5, "%s users %d refcnt %u page %#lx "
" mode %d refcnt %u page %#lx save %p rc %d\n", "save %p rc %d\n", __func__, cpump->active_events,
__func__, a->sample_period, cpump->active_events, refcount_read(&cpump->refcnt),
cpump->mode, refcount_read(&cpump->refcnt),
(unsigned long)cpump->page, cpump->save, rc); (unsigned long)cpump->page, cpump->save, rc);
goto unlock; goto unlock;
free_paicrypt_map: free_paicrypt_map:
/* Undo memory allocation */
kfree(cpump); kfree(cpump);
mp->mapptr = NULL; mp->mapptr = NULL;
free_root: free_root:
paicrypt_root_free(); paicrypt_root_free();
unlock: unlock:
mutex_unlock(&pai_reserve_mutex); mutex_unlock(&pai_reserve_mutex);
return rc ? ERR_PTR(rc) : cpump; return rc ? ERR_PTR(rc) : cpump;
} }
static int paicrypt_event_init_all(struct perf_event *event)
{
struct paicrypt_map *cpump;
struct cpumask *maskptr;
int cpu, rc = -ENOMEM;
maskptr = kzalloc(sizeof(*maskptr), GFP_KERNEL);
if (!maskptr)
goto out;
for_each_online_cpu(cpu) {
cpump = paicrypt_busy(event, cpu);
if (IS_ERR(cpump)) {
for_each_cpu(cpu, maskptr)
paicrypt_event_destroy_cpu(event, cpu);
kfree(maskptr);
rc = PTR_ERR(cpump);
goto out;
}
cpumask_set_cpu(cpu, maskptr);
}
/*
* On error all cpumask are freed and all events have been destroyed.
* Save of which CPUs data structures have been allocated for.
* Release them in paicrypt_event_destroy call back function
* for this event.
*/
PAI_CPU_MASK(event) = maskptr;
rc = 0;
out:
return rc;
}
/* Might be called on different CPU than the one the event is intended for. */ /* Might be called on different CPU than the one the event is intended for. */
static int paicrypt_event_init(struct perf_event *event) static int paicrypt_event_init(struct perf_event *event)
{ {
@ -269,10 +290,7 @@ static int paicrypt_event_init(struct perf_event *event)
if (a->config < PAI_CRYPTO_BASE || if (a->config < PAI_CRYPTO_BASE ||
a->config > PAI_CRYPTO_BASE + paicrypt_cnt) a->config > PAI_CRYPTO_BASE + paicrypt_cnt)
return -EINVAL; return -EINVAL;
/* Allow only CPU wide operation, no process context for now. */ /* Allow only CRYPTO_ALL for sampling */
if ((event->attach_state & PERF_ATTACH_TASK) || event->cpu == -1)
return -ENOENT;
/* Allow only CRYPTO_ALL for sampling. */
if (a->sample_period && a->config != PAI_CRYPTO_BASE) if (a->sample_period && a->config != PAI_CRYPTO_BASE)
return -EINVAL; return -EINVAL;
/* Get a page to store last counter values for sampling */ /* Get a page to store last counter values for sampling */
@ -284,13 +302,17 @@ static int paicrypt_event_init(struct perf_event *event)
} }
} }
cpump = paicrypt_busy(event); if (event->cpu >= 0) {
if (IS_ERR(cpump)) { cpump = paicrypt_busy(event, event->cpu);
if (IS_ERR(cpump))
rc = PTR_ERR(cpump);
} else {
rc = paicrypt_event_init_all(event);
}
if (rc) {
free_page(PAI_SAVE_AREA(event)); free_page(PAI_SAVE_AREA(event));
rc = PTR_ERR(cpump);
goto out; goto out;
} }
event->destroy = paicrypt_event_destroy; event->destroy = paicrypt_event_destroy;
if (a->sample_period) { if (a->sample_period) {
@ -331,8 +353,14 @@ static void paicrypt_start(struct perf_event *event, int flags)
sum = paicrypt_getall(event); /* Get current value */ sum = paicrypt_getall(event); /* Get current value */
local64_set(&event->hw.prev_count, sum); local64_set(&event->hw.prev_count, sum);
} else { /* Sampling */ } else { /* Sampling */
cpump->event = event; memcpy((void *)PAI_SAVE_AREA(event), cpump->page, PAGE_SIZE);
perf_sched_cb_inc(event->pmu); /* Enable context switch callback for system-wide sampling */
if (!(event->attach_state & PERF_ATTACH_TASK)) {
list_add_tail(PAI_SWLIST(event), &cpump->syswide_list);
perf_sched_cb_inc(event->pmu);
} else {
cpump->event = event;
}
} }
} }
@ -344,7 +372,7 @@ static int paicrypt_add(struct perf_event *event, int flags)
if (++cpump->active_events == 1) { if (++cpump->active_events == 1) {
ccd = virt_to_phys(cpump->page) | PAI_CRYPTO_KERNEL_OFFSET; ccd = virt_to_phys(cpump->page) | PAI_CRYPTO_KERNEL_OFFSET;
WRITE_ONCE(S390_lowcore.ccd, ccd); WRITE_ONCE(get_lowcore()->ccd, ccd);
local_ctl_set_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT); local_ctl_set_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT);
} }
if (flags & PERF_EF_START) if (flags & PERF_EF_START)
@ -353,6 +381,7 @@ static int paicrypt_add(struct perf_event *event, int flags)
return 0; return 0;
} }
static void paicrypt_have_sample(struct perf_event *, struct paicrypt_map *);
static void paicrypt_stop(struct perf_event *event, int flags) static void paicrypt_stop(struct perf_event *event, int flags)
{ {
struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr); struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
@ -361,8 +390,13 @@ static void paicrypt_stop(struct perf_event *event, int flags)
if (!event->attr.sample_period) { /* Counting */ if (!event->attr.sample_period) { /* Counting */
paicrypt_read(event); paicrypt_read(event);
} else { /* Sampling */ } else { /* Sampling */
perf_sched_cb_dec(event->pmu); if (!(event->attach_state & PERF_ATTACH_TASK)) {
cpump->event = NULL; perf_sched_cb_dec(event->pmu);
list_del(PAI_SWLIST(event));
} else {
paicrypt_have_sample(event, cpump);
cpump->event = NULL;
}
} }
event->hw.state = PERF_HES_STOPPED; event->hw.state = PERF_HES_STOPPED;
} }
@ -375,7 +409,7 @@ static void paicrypt_del(struct perf_event *event, int flags)
paicrypt_stop(event, PERF_EF_UPDATE); paicrypt_stop(event, PERF_EF_UPDATE);
if (--cpump->active_events == 0) { if (--cpump->active_events == 0) {
local_ctl_clear_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT); local_ctl_clear_bit(0, CR0_CRYPTOGRAPHY_COUNTER_BIT);
WRITE_ONCE(S390_lowcore.ccd, 0); WRITE_ONCE(get_lowcore()->ccd, 0);
} }
} }
@ -455,23 +489,30 @@ static int paicrypt_push_sample(size_t rawsize, struct paicrypt_map *cpump,
} }
/* Check if there is data to be saved on schedule out of a task. */ /* Check if there is data to be saved on schedule out of a task. */
static int paicrypt_have_sample(void) static void paicrypt_have_sample(struct perf_event *event,
struct paicrypt_map *cpump)
{
size_t rawsize;
if (!event) /* No event active */
return;
rawsize = paicrypt_copy(cpump->save, cpump->page,
(unsigned long *)PAI_SAVE_AREA(event),
event->attr.exclude_user,
event->attr.exclude_kernel);
if (rawsize) /* No incremented counters */
paicrypt_push_sample(rawsize, cpump, event);
}
/* Check if there is data to be saved on schedule out of a task. */
static void paicrypt_have_samples(void)
{ {
struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr); struct paicrypt_mapptr *mp = this_cpu_ptr(paicrypt_root.mapptr);
struct paicrypt_map *cpump = mp->mapptr; struct paicrypt_map *cpump = mp->mapptr;
struct perf_event *event = cpump->event; struct perf_event *event;
size_t rawsize;
int rc = 0;
if (!event) /* No event active */ list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
return 0; paicrypt_have_sample(event, cpump);
rawsize = paicrypt_copy(cpump->save, cpump->page,
(unsigned long *)PAI_SAVE_AREA(event),
cpump->event->attr.exclude_user,
cpump->event->attr.exclude_kernel);
if (rawsize) /* No incremented counters */
rc = paicrypt_push_sample(rawsize, cpump, event);
return rc;
} }
/* Called on schedule-in and schedule-out. No access to event structure, /* Called on schedule-in and schedule-out. No access to event structure,
@ -480,10 +521,10 @@ static int paicrypt_have_sample(void)
static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in) static void paicrypt_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in)
{ {
/* We started with a clean page on event installation. So read out /* We started with a clean page on event installation. So read out
* results on schedule_out and if page was dirty, clear values. * results on schedule_out and if page was dirty, save old values.
*/ */
if (!sched_in) if (!sched_in)
paicrypt_have_sample(); paicrypt_have_samples();
} }
/* Attribute definitions for paicrypt interface. As with other CPU /* Attribute definitions for paicrypt interface. As with other CPU
@ -527,7 +568,7 @@ static const struct attribute_group *paicrypt_attr_groups[] = {
/* Performance monitoring unit for mapped counters */ /* Performance monitoring unit for mapped counters */
static struct pmu paicrypt = { static struct pmu paicrypt = {
.task_ctx_nr = perf_invalid_context, .task_ctx_nr = perf_hw_context,
.event_init = paicrypt_event_init, .event_init = paicrypt_event_init,
.add = paicrypt_add, .add = paicrypt_add,
.del = paicrypt_del, .del = paicrypt_del,

View File

@ -47,11 +47,11 @@ struct paiext_cb { /* PAI extension 1 control block */
struct paiext_map { struct paiext_map {
unsigned long *area; /* Area for CPU to store counters */ unsigned long *area; /* Area for CPU to store counters */
struct pai_userdata *save; /* Area to store non-zero counters */ struct pai_userdata *save; /* Area to store non-zero counters */
enum paievt_mode mode; /* Type of event */
unsigned int active_events; /* # of PAI Extension users */ unsigned int active_events; /* # of PAI Extension users */
refcount_t refcnt; refcount_t refcnt;
struct perf_event *event; /* Perf event for sampling */ struct perf_event *event; /* Perf event for sampling */
struct paiext_cb *paiext_cb; /* PAI extension control block area */ struct paiext_cb *paiext_cb; /* PAI extension control block area */
struct list_head syswide_list; /* List system-wide sampling events */
}; };
struct paiext_mapptr { struct paiext_mapptr {
@ -70,6 +70,8 @@ static void paiext_root_free(void)
free_percpu(paiext_root.mapptr); free_percpu(paiext_root.mapptr);
paiext_root.mapptr = NULL; paiext_root.mapptr = NULL;
} }
debug_sprintf_event(paiext_dbg, 5, "%s root.refcount %d\n", __func__,
refcount_read(&paiext_root.refcnt));
} }
/* On initialization of first event also allocate per CPU data dynamically. /* On initialization of first event also allocate per CPU data dynamically.
@ -115,20 +117,34 @@ static void paiext_free(struct paiext_mapptr *mp)
} }
/* Release the PMU if event is the last perf event */ /* Release the PMU if event is the last perf event */
static void paiext_event_destroy(struct perf_event *event) static void paiext_event_destroy_cpu(struct perf_event *event, int cpu)
{ {
struct paiext_mapptr *mp = per_cpu_ptr(paiext_root.mapptr, event->cpu); struct paiext_mapptr *mp = per_cpu_ptr(paiext_root.mapptr, cpu);
struct paiext_map *cpump = mp->mapptr; struct paiext_map *cpump = mp->mapptr;
free_page(PAI_SAVE_AREA(event));
mutex_lock(&paiext_reserve_mutex); mutex_lock(&paiext_reserve_mutex);
if (refcount_dec_and_test(&cpump->refcnt)) /* Last reference gone */ if (refcount_dec_and_test(&cpump->refcnt)) /* Last reference gone */
paiext_free(mp); paiext_free(mp);
paiext_root_free(); paiext_root_free();
mutex_unlock(&paiext_reserve_mutex); mutex_unlock(&paiext_reserve_mutex);
debug_sprintf_event(paiext_dbg, 4, "%s cpu %d mapptr %p\n", __func__, }
event->cpu, mp->mapptr);
static void paiext_event_destroy(struct perf_event *event)
{
int cpu;
free_page(PAI_SAVE_AREA(event));
if (event->cpu == -1) {
struct cpumask *mask = PAI_CPU_MASK(event);
for_each_cpu(cpu, mask)
paiext_event_destroy_cpu(event, cpu);
kfree(mask);
} else {
paiext_event_destroy_cpu(event, event->cpu);
}
debug_sprintf_event(paiext_dbg, 4, "%s cpu %d\n", __func__,
event->cpu);
} }
/* Used to avoid races in checking concurrent access of counting and /* Used to avoid races in checking concurrent access of counting and
@ -145,19 +161,18 @@ static void paiext_event_destroy(struct perf_event *event)
* *
* Allocate the memory for the event. * Allocate the memory for the event.
*/ */
static int paiext_alloc(struct perf_event_attr *a, struct perf_event *event) static int paiext_alloc_cpu(struct perf_event *event, int cpu)
{ {
struct paiext_mapptr *mp; struct paiext_mapptr *mp;
struct paiext_map *cpump; struct paiext_map *cpump;
int rc; int rc;
mutex_lock(&paiext_reserve_mutex); mutex_lock(&paiext_reserve_mutex);
rc = paiext_root_alloc(); rc = paiext_root_alloc();
if (rc) if (rc)
goto unlock; goto unlock;
mp = per_cpu_ptr(paiext_root.mapptr, event->cpu); mp = per_cpu_ptr(paiext_root.mapptr, cpu);
cpump = mp->mapptr; cpump = mp->mapptr;
if (!cpump) { /* Paiext_map allocated? */ if (!cpump) { /* Paiext_map allocated? */
rc = -ENOMEM; rc = -ENOMEM;
@ -185,24 +200,13 @@ static int paiext_alloc(struct perf_event_attr *a, struct perf_event *event)
paiext_free(mp); paiext_free(mp);
goto undo; goto undo;
} }
INIT_LIST_HEAD(&cpump->syswide_list);
refcount_set(&cpump->refcnt, 1); refcount_set(&cpump->refcnt, 1);
cpump->mode = a->sample_period ? PAI_MODE_SAMPLING rc = 0;
: PAI_MODE_COUNTING;
} else { } else {
/* Multiple invocation, check what is active.
* Supported are multiple counter events or only one sampling
* event concurrently at any one time.
*/
if (cpump->mode == PAI_MODE_SAMPLING ||
(cpump->mode == PAI_MODE_COUNTING && a->sample_period)) {
rc = -EBUSY;
goto undo;
}
refcount_inc(&cpump->refcnt); refcount_inc(&cpump->refcnt);
} }
rc = 0;
undo: undo:
if (rc) { if (rc) {
/* Error in allocation of event, decrement anchor. Since /* Error in allocation of event, decrement anchor. Since
@ -217,6 +221,38 @@ unlock:
return rc; return rc;
} }
static int paiext_alloc(struct perf_event *event)
{
struct cpumask *maskptr;
int cpu, rc = -ENOMEM;
maskptr = kzalloc(sizeof(*maskptr), GFP_KERNEL);
if (!maskptr)
goto out;
for_each_online_cpu(cpu) {
rc = paiext_alloc_cpu(event, cpu);
if (rc) {
for_each_cpu(cpu, maskptr)
paiext_event_destroy_cpu(event, cpu);
kfree(maskptr);
goto out;
}
cpumask_set_cpu(cpu, maskptr);
}
/*
* On error all cpumask are freed and all events have been destroyed.
* Save of which CPUs data structures have been allocated for.
* Release them in paicrypt_event_destroy call back function
* for this event.
*/
PAI_CPU_MASK(event) = maskptr;
rc = 0;
out:
return rc;
}
/* The PAI extension 1 control block supports up to 128 entries. Return /* The PAI extension 1 control block supports up to 128 entries. Return
* the index within PAIE1_CB given the event number. Also validate event * the index within PAIE1_CB given the event number. Also validate event
* number. * number.
@ -246,9 +282,6 @@ static int paiext_event_init(struct perf_event *event)
rc = paiext_event_valid(event); rc = paiext_event_valid(event);
if (rc) if (rc)
return rc; return rc;
/* Allow only CPU wide operation, no process context for now. */
if ((event->attach_state & PERF_ATTACH_TASK) || event->cpu == -1)
return -ENOENT;
/* Allow only event NNPA_ALL for sampling. */ /* Allow only event NNPA_ALL for sampling. */
if (a->sample_period && a->config != PAI_NNPA_BASE) if (a->sample_period && a->config != PAI_NNPA_BASE)
return -EINVAL; return -EINVAL;
@ -262,7 +295,10 @@ static int paiext_event_init(struct perf_event *event)
return -ENOMEM; return -ENOMEM;
} }
rc = paiext_alloc(a, event); if (event->cpu >= 0)
rc = paiext_alloc_cpu(event, event->cpu);
else
rc = paiext_alloc(event);
if (rc) { if (rc) {
free_page(PAI_SAVE_AREA(event)); free_page(PAI_SAVE_AREA(event));
return rc; return rc;
@ -334,8 +370,15 @@ static void paiext_start(struct perf_event *event, int flags)
sum = paiext_getall(event); /* Get current value */ sum = paiext_getall(event); /* Get current value */
local64_set(&event->hw.prev_count, sum); local64_set(&event->hw.prev_count, sum);
} else { /* Sampling */ } else { /* Sampling */
cpump->event = event; memcpy((void *)PAI_SAVE_AREA(event), cpump->area,
perf_sched_cb_inc(event->pmu); PAIE1_CTRBLOCK_SZ);
/* Enable context switch callback for system-wide sampling */
if (!(event->attach_state & PERF_ATTACH_TASK)) {
list_add_tail(PAI_SWLIST(event), &cpump->syswide_list);
perf_sched_cb_inc(event->pmu);
} else {
cpump->event = event;
}
} }
} }
@ -346,12 +389,10 @@ static int paiext_add(struct perf_event *event, int flags)
struct paiext_cb *pcb = cpump->paiext_cb; struct paiext_cb *pcb = cpump->paiext_cb;
if (++cpump->active_events == 1) { if (++cpump->active_events == 1) {
S390_lowcore.aicd = virt_to_phys(cpump->paiext_cb); get_lowcore()->aicd = virt_to_phys(cpump->paiext_cb);
pcb->acc = virt_to_phys(cpump->area) | 0x1; pcb->acc = virt_to_phys(cpump->area) | 0x1;
/* Enable CPU instruction lookup for PAIE1 control block */ /* Enable CPU instruction lookup for PAIE1 control block */
local_ctl_set_bit(0, CR0_PAI_EXTENSION_BIT); local_ctl_set_bit(0, CR0_PAI_EXTENSION_BIT);
debug_sprintf_event(paiext_dbg, 4, "%s 1508 %llx acc %llx\n",
__func__, S390_lowcore.aicd, pcb->acc);
} }
if (flags & PERF_EF_START) if (flags & PERF_EF_START)
paiext_start(event, PERF_EF_RELOAD); paiext_start(event, PERF_EF_RELOAD);
@ -359,6 +400,7 @@ static int paiext_add(struct perf_event *event, int flags)
return 0; return 0;
} }
static void paiext_have_sample(struct perf_event *, struct paiext_map *);
static void paiext_stop(struct perf_event *event, int flags) static void paiext_stop(struct perf_event *event, int flags)
{ {
struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr); struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
@ -367,8 +409,13 @@ static void paiext_stop(struct perf_event *event, int flags)
if (!event->attr.sample_period) { /* Counting */ if (!event->attr.sample_period) { /* Counting */
paiext_read(event); paiext_read(event);
} else { /* Sampling */ } else { /* Sampling */
perf_sched_cb_dec(event->pmu); if (!(event->attach_state & PERF_ATTACH_TASK)) {
cpump->event = NULL; list_del(PAI_SWLIST(event));
perf_sched_cb_dec(event->pmu);
} else {
paiext_have_sample(event, cpump);
cpump->event = NULL;
}
} }
event->hw.state = PERF_HES_STOPPED; event->hw.state = PERF_HES_STOPPED;
} }
@ -384,9 +431,7 @@ static void paiext_del(struct perf_event *event, int flags)
/* Disable CPU instruction lookup for PAIE1 control block */ /* Disable CPU instruction lookup for PAIE1 control block */
local_ctl_clear_bit(0, CR0_PAI_EXTENSION_BIT); local_ctl_clear_bit(0, CR0_PAI_EXTENSION_BIT);
pcb->acc = 0; pcb->acc = 0;
S390_lowcore.aicd = 0; get_lowcore()->aicd = 0;
debug_sprintf_event(paiext_dbg, 4, "%s 1508 %llx acc %llx\n",
__func__, S390_lowcore.aicd, pcb->acc);
} }
} }
@ -470,21 +515,28 @@ static int paiext_push_sample(size_t rawsize, struct paiext_map *cpump,
} }
/* Check if there is data to be saved on schedule out of a task. */ /* Check if there is data to be saved on schedule out of a task. */
static int paiext_have_sample(void) static void paiext_have_sample(struct perf_event *event,
struct paiext_map *cpump)
{ {
struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
struct paiext_map *cpump = mp->mapptr;
struct perf_event *event = cpump->event;
size_t rawsize; size_t rawsize;
int rc = 0;
if (!event) if (!event)
return 0; return;
rawsize = paiext_copy(cpump->save, cpump->area, rawsize = paiext_copy(cpump->save, cpump->area,
(unsigned long *)PAI_SAVE_AREA(event)); (unsigned long *)PAI_SAVE_AREA(event));
if (rawsize) /* Incremented counters */ if (rawsize) /* Incremented counters */
rc = paiext_push_sample(rawsize, cpump, event); paiext_push_sample(rawsize, cpump, event);
return rc; }
/* Check if there is data to be saved on schedule out of a task. */
static void paiext_have_samples(void)
{
struct paiext_mapptr *mp = this_cpu_ptr(paiext_root.mapptr);
struct paiext_map *cpump = mp->mapptr;
struct perf_event *event;
list_for_each_entry(event, &cpump->syswide_list, hw.tp_list)
paiext_have_sample(event, cpump);
} }
/* Called on schedule-in and schedule-out. No access to event structure, /* Called on schedule-in and schedule-out. No access to event structure,
@ -493,10 +545,10 @@ static int paiext_have_sample(void)
static void paiext_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in) static void paiext_sched_task(struct perf_event_pmu_context *pmu_ctx, bool sched_in)
{ {
/* We started with a clean page on event installation. So read out /* We started with a clean page on event installation. So read out
* results on schedule_out and if page was dirty, clear values. * results on schedule_out and if page was dirty, save old values.
*/ */
if (!sched_in) if (!sched_in)
paiext_have_sample(); paiext_have_samples();
} }
/* Attribute definitions for pai extension1 interface. As with other CPU /* Attribute definitions for pai extension1 interface. As with other CPU
@ -542,7 +594,7 @@ static const struct attribute_group *paiext_attr_groups[] = {
/* Performance monitoring unit for mapped counters */ /* Performance monitoring unit for mapped counters */
static struct pmu paiext = { static struct pmu paiext = {
.task_ctx_nr = perf_invalid_context, .task_ctx_nr = perf_hw_context,
.event_init = paiext_event_init, .event_init = paiext_event_init,
.add = paiext_add, .add = paiext_add,
.del = paiext_del, .del = paiext_del,

View File

@ -71,10 +71,10 @@ void flush_thread(void)
void arch_setup_new_exec(void) void arch_setup_new_exec(void)
{ {
if (S390_lowcore.current_pid != current->pid) { if (get_lowcore()->current_pid != current->pid) {
S390_lowcore.current_pid = current->pid; get_lowcore()->current_pid = current->pid;
if (test_facility(40)) if (test_facility(40))
lpp(&S390_lowcore.lpp); lpp(&get_lowcore()->lpp);
} }
} }

View File

@ -421,16 +421,16 @@ static void __init setup_lowcore(void)
lc->clock_comparator = clock_comparator_max; lc->clock_comparator = clock_comparator_max;
lc->current_task = (unsigned long)&init_task; lc->current_task = (unsigned long)&init_task;
lc->lpp = LPP_MAGIC; lc->lpp = LPP_MAGIC;
lc->machine_flags = S390_lowcore.machine_flags; lc->machine_flags = get_lowcore()->machine_flags;
lc->preempt_count = S390_lowcore.preempt_count; lc->preempt_count = get_lowcore()->preempt_count;
nmi_alloc_mcesa_early(&lc->mcesad); nmi_alloc_mcesa_early(&lc->mcesad);
lc->sys_enter_timer = S390_lowcore.sys_enter_timer; lc->sys_enter_timer = get_lowcore()->sys_enter_timer;
lc->exit_timer = S390_lowcore.exit_timer; lc->exit_timer = get_lowcore()->exit_timer;
lc->user_timer = S390_lowcore.user_timer; lc->user_timer = get_lowcore()->user_timer;
lc->system_timer = S390_lowcore.system_timer; lc->system_timer = get_lowcore()->system_timer;
lc->steal_timer = S390_lowcore.steal_timer; lc->steal_timer = get_lowcore()->steal_timer;
lc->last_update_timer = S390_lowcore.last_update_timer; lc->last_update_timer = get_lowcore()->last_update_timer;
lc->last_update_clock = S390_lowcore.last_update_clock; lc->last_update_clock = get_lowcore()->last_update_clock;
/* /*
* Allocate the global restart stack which is the same for * Allocate the global restart stack which is the same for
* all CPUs in case *one* of them does a PSW restart. * all CPUs in case *one* of them does a PSW restart.
@ -439,7 +439,7 @@ static void __init setup_lowcore(void)
lc->mcck_stack = stack_alloc_early() + STACK_INIT_OFFSET; lc->mcck_stack = stack_alloc_early() + STACK_INIT_OFFSET;
lc->async_stack = stack_alloc_early() + STACK_INIT_OFFSET; lc->async_stack = stack_alloc_early() + STACK_INIT_OFFSET;
lc->nodat_stack = stack_alloc_early() + STACK_INIT_OFFSET; lc->nodat_stack = stack_alloc_early() + STACK_INIT_OFFSET;
lc->kernel_stack = S390_lowcore.kernel_stack; lc->kernel_stack = get_lowcore()->kernel_stack;
/* /*
* Set up PSW restart to call ipl.c:do_restart(). Copy the relevant * Set up PSW restart to call ipl.c:do_restart(). Copy the relevant
* restart data to the absolute zero lowcore. This is necessary if * restart data to the absolute zero lowcore. This is necessary if
@ -455,8 +455,8 @@ static void __init setup_lowcore(void)
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);
lc->preempt_count = PREEMPT_DISABLED; lc->preempt_count = PREEMPT_DISABLED;
lc->kernel_asce = S390_lowcore.kernel_asce; lc->kernel_asce = get_lowcore()->kernel_asce;
lc->user_asce = S390_lowcore.user_asce; lc->user_asce = get_lowcore()->user_asce;
system_ctlreg_init_save_area(lc); system_ctlreg_init_save_area(lc);
abs_lc = get_abs_lowcore(); abs_lc = get_abs_lowcore();

View File

@ -74,8 +74,6 @@ enum {
CPU_STATE_CONFIGURED, CPU_STATE_CONFIGURED,
}; };
static DEFINE_PER_CPU(struct cpu *, cpu_device);
struct pcpu { struct pcpu {
unsigned long ec_mask; /* bit mask for ec_xxx functions */ unsigned long ec_mask; /* bit mask for ec_xxx functions */
unsigned long ec_clk; /* sigp timestamp for ec_xxx */ unsigned long ec_clk; /* sigp timestamp for ec_xxx */
@ -203,7 +201,7 @@ static int pcpu_alloc_lowcore(struct pcpu *pcpu, int cpu)
mcck_stack = stack_alloc(); mcck_stack = stack_alloc();
if (!lc || !nodat_stack || !async_stack || !mcck_stack) if (!lc || !nodat_stack || !async_stack || !mcck_stack)
goto out; goto out;
memcpy(lc, &S390_lowcore, 512); memcpy(lc, get_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;
@ -265,9 +263,9 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
lc->spinlock_lockval = arch_spin_lockval(cpu); lc->spinlock_lockval = arch_spin_lockval(cpu);
lc->spinlock_index = 0; lc->spinlock_index = 0;
lc->percpu_offset = __per_cpu_offset[cpu]; lc->percpu_offset = __per_cpu_offset[cpu];
lc->kernel_asce = S390_lowcore.kernel_asce; lc->kernel_asce = get_lowcore()->kernel_asce;
lc->user_asce = s390_invalid_asce; lc->user_asce = s390_invalid_asce;
lc->machine_flags = S390_lowcore.machine_flags; lc->machine_flags = get_lowcore()->machine_flags;
lc->user_timer = lc->system_timer = lc->user_timer = lc->system_timer =
lc->steal_timer = lc->avg_steal_timer = 0; lc->steal_timer = lc->avg_steal_timer = 0;
abs_lc = get_abs_lowcore(); abs_lc = get_abs_lowcore();
@ -407,7 +405,7 @@ void smp_call_ipl_cpu(void (*func)(void *), void *data)
struct lowcore *lc = lowcore_ptr[0]; struct lowcore *lc = lowcore_ptr[0];
if (pcpu_devices[0].address == stap()) if (pcpu_devices[0].address == stap())
lc = &S390_lowcore; lc = get_lowcore();
pcpu_delegate(&pcpu_devices[0], func, data, pcpu_delegate(&pcpu_devices[0], func, data,
lc->nodat_stack); lc->nodat_stack);
@ -719,8 +717,6 @@ static void __ref smp_get_core_info(struct sclp_core_info *info, int early)
} }
} }
static int smp_add_present_cpu(int cpu);
static int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail, static int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail,
bool configured, bool early) bool configured, bool early)
{ {
@ -744,7 +740,7 @@ static int smp_add_core(struct sclp_core_entry *core, cpumask_t *avail,
pcpu->state = CPU_STATE_STANDBY; pcpu->state = CPU_STATE_STANDBY;
smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN); smp_cpu_set_polarization(cpu, POLARIZATION_UNKNOWN);
set_cpu_present(cpu, true); set_cpu_present(cpu, true);
if (!early && smp_add_present_cpu(cpu) != 0) if (!early && arch_register_cpu(cpu))
set_cpu_present(cpu, false); set_cpu_present(cpu, false);
else else
nr++; nr++;
@ -831,9 +827,6 @@ void __init smp_detect_cpus(void)
s_cpus += smp_cpu_mtid + 1; s_cpus += smp_cpu_mtid + 1;
} }
pr_info("%d configured CPUs, %d standby CPUs\n", c_cpus, s_cpus); pr_info("%d configured CPUs, %d standby CPUs\n", c_cpus, s_cpus);
/* Add CPUs present at boot */
__smp_rescan_cpus(info, true);
memblock_free(info, sizeof(*info)); memblock_free(info, sizeof(*info));
} }
@ -842,15 +835,16 @@ void __init smp_detect_cpus(void)
*/ */
static void smp_start_secondary(void *cpuvoid) static void smp_start_secondary(void *cpuvoid)
{ {
struct lowcore *lc = get_lowcore();
int cpu = raw_smp_processor_id(); int cpu = raw_smp_processor_id();
S390_lowcore.last_update_clock = get_tod_clock(); lc->last_update_clock = get_tod_clock();
S390_lowcore.restart_stack = (unsigned long)restart_stack; lc->restart_stack = (unsigned long)restart_stack;
S390_lowcore.restart_fn = (unsigned long)do_restart; lc->restart_fn = (unsigned long)do_restart;
S390_lowcore.restart_data = 0; lc->restart_data = 0;
S390_lowcore.restart_source = -1U; lc->restart_source = -1U;
S390_lowcore.restart_flags = 0; lc->restart_flags = 0;
restore_access_regs(S390_lowcore.access_regs_save_area); restore_access_regs(lc->access_regs_save_area);
cpu_init(); cpu_init();
rcutree_report_cpu_starting(cpu); rcutree_report_cpu_starting(cpu);
init_cpu_timer(); init_cpu_timer();
@ -973,6 +967,7 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
if (register_external_irq(EXT_IRQ_EXTERNAL_CALL, do_ext_call_interrupt)) if (register_external_irq(EXT_IRQ_EXTERNAL_CALL, do_ext_call_interrupt))
panic("Couldn't request external interrupt 0x1202"); panic("Couldn't request external interrupt 0x1202");
system_ctl_set_bit(0, 13); system_ctl_set_bit(0, 13);
smp_rescan_cpus(true);
} }
void __init smp_prepare_boot_cpu(void) void __init smp_prepare_boot_cpu(void)
@ -981,16 +976,18 @@ void __init smp_prepare_boot_cpu(void)
WARN_ON(!cpu_present(0) || !cpu_online(0)); WARN_ON(!cpu_present(0) || !cpu_online(0));
pcpu->state = CPU_STATE_CONFIGURED; pcpu->state = CPU_STATE_CONFIGURED;
S390_lowcore.percpu_offset = __per_cpu_offset[0]; get_lowcore()->percpu_offset = __per_cpu_offset[0];
smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN); smp_cpu_set_polarization(0, POLARIZATION_UNKNOWN);
} }
void __init smp_setup_processor_id(void) void __init smp_setup_processor_id(void)
{ {
struct lowcore *lc = get_lowcore();
pcpu_devices[0].address = stap(); pcpu_devices[0].address = stap();
S390_lowcore.cpu_nr = 0; lc->cpu_nr = 0;
S390_lowcore.spinlock_lockval = arch_spin_lockval(0); lc->spinlock_lockval = arch_spin_lockval(0);
S390_lowcore.spinlock_index = 0; lc->spinlock_index = 0;
} }
/* /*
@ -1108,35 +1105,34 @@ static struct attribute_group cpu_online_attr_group = {
static int smp_cpu_online(unsigned int cpu) static int smp_cpu_online(unsigned int cpu)
{ {
struct device *s = &per_cpu(cpu_device, cpu)->dev; struct cpu *c = &per_cpu(cpu_devices, cpu);
return sysfs_create_group(&s->kobj, &cpu_online_attr_group); return sysfs_create_group(&c->dev.kobj, &cpu_online_attr_group);
} }
static int smp_cpu_pre_down(unsigned int cpu) static int smp_cpu_pre_down(unsigned int cpu)
{ {
struct device *s = &per_cpu(cpu_device, cpu)->dev; struct cpu *c = &per_cpu(cpu_devices, cpu);
sysfs_remove_group(&s->kobj, &cpu_online_attr_group); sysfs_remove_group(&c->dev.kobj, &cpu_online_attr_group);
return 0; return 0;
} }
static int smp_add_present_cpu(int cpu) bool arch_cpu_is_hotpluggable(int cpu)
{ {
struct device *s; return !!cpu;
struct cpu *c; }
int arch_register_cpu(int cpu)
{
struct cpu *c = &per_cpu(cpu_devices, cpu);
int rc; int rc;
c = kzalloc(sizeof(*c), GFP_KERNEL); c->hotpluggable = arch_cpu_is_hotpluggable(cpu);
if (!c)
return -ENOMEM;
per_cpu(cpu_device, cpu) = c;
s = &c->dev;
c->hotpluggable = !!cpu;
rc = register_cpu(c, cpu); rc = register_cpu(c, cpu);
if (rc) if (rc)
goto out; goto out;
rc = sysfs_create_group(&s->kobj, &cpu_common_attr_group); rc = sysfs_create_group(&c->dev.kobj, &cpu_common_attr_group);
if (rc) if (rc)
goto out_cpu; goto out_cpu;
rc = topology_cpu_init(c); rc = topology_cpu_init(c);
@ -1145,14 +1141,14 @@ static int smp_add_present_cpu(int cpu)
return 0; return 0;
out_topology: out_topology:
sysfs_remove_group(&s->kobj, &cpu_common_attr_group); sysfs_remove_group(&c->dev.kobj, &cpu_common_attr_group);
out_cpu: out_cpu:
unregister_cpu(c); unregister_cpu(c);
out: out:
return rc; return rc;
} }
int __ref smp_rescan_cpus(void) int __ref smp_rescan_cpus(bool early)
{ {
struct sclp_core_info *info; struct sclp_core_info *info;
int nr; int nr;
@ -1161,7 +1157,7 @@ int __ref smp_rescan_cpus(void)
if (!info) if (!info)
return -ENOMEM; return -ENOMEM;
smp_get_core_info(info, 0); smp_get_core_info(info, 0);
nr = __smp_rescan_cpus(info, false); nr = __smp_rescan_cpus(info, early);
kfree(info); kfree(info);
if (nr) if (nr)
topology_schedule_update(); topology_schedule_update();
@ -1178,7 +1174,7 @@ static ssize_t __ref rescan_store(struct device *dev,
rc = lock_device_hotplug_sysfs(); rc = lock_device_hotplug_sysfs();
if (rc) if (rc)
return rc; return rc;
rc = smp_rescan_cpus(); rc = smp_rescan_cpus(false);
unlock_device_hotplug(); unlock_device_hotplug();
return rc ? rc : count; return rc ? rc : count;
} }
@ -1187,7 +1183,7 @@ static DEVICE_ATTR_WO(rescan);
static int __init s390_smp_init(void) static int __init s390_smp_init(void)
{ {
struct device *dev_root; struct device *dev_root;
int cpu, rc = 0; int rc;
dev_root = bus_get_dev_root(&cpu_subsys); dev_root = bus_get_dev_root(&cpu_subsys);
if (dev_root) { if (dev_root) {
@ -1196,17 +1192,9 @@ static int __init s390_smp_init(void)
if (rc) if (rc)
return rc; return rc;
} }
for_each_present_cpu(cpu) {
rc = smp_add_present_cpu(cpu);
if (rc)
goto out;
}
rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "s390/smp:online", rc = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "s390/smp:online",
smp_cpu_online, smp_cpu_pre_down); smp_cpu_online, smp_cpu_pre_down);
rc = rc <= 0 ? rc : 0; rc = rc <= 0 ? rc : 0;
out:
return rc; return rc;
} }
subsys_initcall(s390_smp_init); subsys_initcall(s390_smp_init);

View File

@ -300,34 +300,57 @@ static struct diag204_x_part_block *lpar_cpu_inf(struct lpar_cpu_inf *part_inf,
return (struct diag204_x_part_block *)&block->cpus[i]; return (struct diag204_x_part_block *)&block->cpus[i];
} }
static void fill_diag(struct sthyi_sctns *sctns) static void *diag204_get_data(bool diag204_allow_busy)
{ {
int i, r, pages; unsigned long subcode;
bool this_lpar;
void *diag204_buf; void *diag204_buf;
int pages, rc;
subcode = DIAG204_SUBC_RSI;
subcode |= DIAG204_INFO_EXT;
pages = diag204(subcode, 0, NULL);
if (pages < 0)
return ERR_PTR(pages);
if (pages == 0)
return ERR_PTR(-ENODATA);
diag204_buf = __vmalloc_node(array_size(pages, PAGE_SIZE),
PAGE_SIZE, GFP_KERNEL, NUMA_NO_NODE,
__builtin_return_address(0));
if (!diag204_buf)
return ERR_PTR(-ENOMEM);
subcode = DIAG204_SUBC_STIB7;
subcode |= DIAG204_INFO_EXT;
if (diag204_has_bif() && diag204_allow_busy)
subcode |= DIAG204_BIF_BIT;
rc = diag204(subcode, pages, diag204_buf);
if (rc < 0) {
vfree(diag204_buf);
return ERR_PTR(rc);
}
return diag204_buf;
}
static bool is_diag204_cached(struct sthyi_sctns *sctns)
{
/*
* Check if validity bits are set when diag204 data
* is gathered.
*/
if (sctns->par.infpval1)
return true;
return false;
}
static void fill_diag(struct sthyi_sctns *sctns, void *diag204_buf)
{
int i;
bool this_lpar;
void *diag224_buf = NULL; void *diag224_buf = NULL;
struct diag204_x_info_blk_hdr *ti_hdr; struct diag204_x_info_blk_hdr *ti_hdr;
struct diag204_x_part_block *part_block; struct diag204_x_part_block *part_block;
struct diag204_x_phys_block *phys_block; struct diag204_x_phys_block *phys_block;
struct lpar_cpu_inf lpar_inf = {}; struct lpar_cpu_inf lpar_inf = {};
/* Errors are handled through the validity bits in the response. */
pages = diag204((unsigned long)DIAG204_SUBC_RSI |
(unsigned long)DIAG204_INFO_EXT, 0, NULL);
if (pages <= 0)
return;
diag204_buf = __vmalloc_node(array_size(pages, PAGE_SIZE),
PAGE_SIZE, GFP_KERNEL, NUMA_NO_NODE,
__builtin_return_address(0));
if (!diag204_buf)
return;
r = diag204((unsigned long)DIAG204_SUBC_STIB7 |
(unsigned long)DIAG204_INFO_EXT, pages, diag204_buf);
if (r < 0)
goto out;
diag224_buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA); diag224_buf = (void *)__get_free_page(GFP_KERNEL | GFP_DMA);
if (!diag224_buf || diag224(diag224_buf)) if (!diag224_buf || diag224(diag224_buf))
goto out; goto out;
@ -392,7 +415,6 @@ static void fill_diag(struct sthyi_sctns *sctns)
out: out:
free_page((unsigned long)diag224_buf); free_page((unsigned long)diag224_buf);
vfree(diag204_buf);
} }
static int sthyi(u64 vaddr, u64 *rc) static int sthyi(u64 vaddr, u64 *rc)
@ -414,19 +436,31 @@ static int sthyi(u64 vaddr, u64 *rc)
static int fill_dst(void *dst, u64 *rc) static int fill_dst(void *dst, u64 *rc)
{ {
void *diag204_buf;
struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst; struct sthyi_sctns *sctns = (struct sthyi_sctns *)dst;
/* /*
* If the facility is on, we don't want to emulate the instruction. * If the facility is on, we don't want to emulate the instruction.
* We ask the hypervisor to provide the data. * We ask the hypervisor to provide the data.
*/ */
if (test_facility(74)) if (test_facility(74)) {
memset(dst, 0, PAGE_SIZE);
return sthyi((u64)dst, rc); return sthyi((u64)dst, rc);
}
/*
* When emulating, if diag204 returns BUSY don't reset dst buffer
* and use cached data.
*/
*rc = 0;
diag204_buf = diag204_get_data(is_diag204_cached(sctns));
if (IS_ERR(diag204_buf))
return PTR_ERR(diag204_buf);
memset(dst, 0, PAGE_SIZE);
fill_hdr(sctns); fill_hdr(sctns);
fill_stsi(sctns); fill_stsi(sctns);
fill_diag(sctns); fill_diag(sctns, diag204_buf);
*rc = 0; vfree(diag204_buf);
return 0; return 0;
} }
@ -445,11 +479,14 @@ static int sthyi_update_cache(u64 *rc)
{ {
int r; int r;
memset(sthyi_cache.info, 0, PAGE_SIZE);
r = fill_dst(sthyi_cache.info, rc); r = fill_dst(sthyi_cache.info, rc);
if (r) if (r == 0) {
return r; sthyi_cache.end = jiffies + CACHE_VALID_JIFFIES;
sthyi_cache.end = jiffies + CACHE_VALID_JIFFIES; } else if (r == -EBUSY) {
/* mark as expired and return 0 to keep using cached data */
sthyi_cache.end = jiffies - 1;
r = 0;
}
return r; return r;
} }

View File

@ -124,8 +124,8 @@ void noinstr __do_syscall(struct pt_regs *regs, int per_trap)
{ {
add_random_kstack_offset(); add_random_kstack_offset();
enter_from_user_mode(regs); enter_from_user_mode(regs);
regs->psw = S390_lowcore.svc_old_psw; regs->psw = get_lowcore()->svc_old_psw;
regs->int_code = S390_lowcore.svc_int_code; regs->int_code = get_lowcore()->svc_int_code;
update_timer_sys(); update_timer_sys();
if (static_branch_likely(&cpu_has_bear)) if (static_branch_likely(&cpu_has_bear))
current->thread.last_break = regs->last_break; current->thread.last_break = regs->last_break;

View File

@ -131,7 +131,7 @@ void clock_comparator_work(void)
{ {
struct clock_event_device *cd; struct clock_event_device *cd;
S390_lowcore.clock_comparator = clock_comparator_max; get_lowcore()->clock_comparator = clock_comparator_max;
cd = this_cpu_ptr(&comparators); cd = this_cpu_ptr(&comparators);
cd->event_handler(cd); cd->event_handler(cd);
} }
@ -139,8 +139,8 @@ void clock_comparator_work(void)
static int s390_next_event(unsigned long delta, static int s390_next_event(unsigned long delta,
struct clock_event_device *evt) struct clock_event_device *evt)
{ {
S390_lowcore.clock_comparator = get_tod_clock() + delta; get_lowcore()->clock_comparator = get_tod_clock() + delta;
set_clock_comparator(S390_lowcore.clock_comparator); set_clock_comparator(get_lowcore()->clock_comparator);
return 0; return 0;
} }
@ -153,8 +153,8 @@ void init_cpu_timer(void)
struct clock_event_device *cd; struct clock_event_device *cd;
int cpu; int cpu;
S390_lowcore.clock_comparator = clock_comparator_max; get_lowcore()->clock_comparator = clock_comparator_max;
set_clock_comparator(S390_lowcore.clock_comparator); set_clock_comparator(get_lowcore()->clock_comparator);
cpu = smp_processor_id(); cpu = smp_processor_id();
cd = &per_cpu(comparators, cpu); cd = &per_cpu(comparators, cpu);
@ -184,8 +184,8 @@ static void clock_comparator_interrupt(struct ext_code ext_code,
unsigned long param64) unsigned long param64)
{ {
inc_irq_stat(IRQEXT_CLK); inc_irq_stat(IRQEXT_CLK);
if (S390_lowcore.clock_comparator == clock_comparator_max) if (get_lowcore()->clock_comparator == clock_comparator_max)
set_clock_comparator(S390_lowcore.clock_comparator); set_clock_comparator(get_lowcore()->clock_comparator);
} }
static void stp_timing_alert(struct stp_irq_parm *); static void stp_timing_alert(struct stp_irq_parm *);
@ -408,12 +408,12 @@ static void clock_sync_global(long delta)
static void clock_sync_local(long delta) static void clock_sync_local(long delta)
{ {
/* Add the delta to the clock comparator. */ /* Add the delta to the clock comparator. */
if (S390_lowcore.clock_comparator != clock_comparator_max) { if (get_lowcore()->clock_comparator != clock_comparator_max) {
S390_lowcore.clock_comparator += delta; get_lowcore()->clock_comparator += delta;
set_clock_comparator(S390_lowcore.clock_comparator); set_clock_comparator(get_lowcore()->clock_comparator);
} }
/* Adjust the last_update_clock time-stamp. */ /* Adjust the last_update_clock time-stamp. */
S390_lowcore.last_update_clock += delta; get_lowcore()->last_update_clock += delta;
} }
/* Single threaded workqueue used for stp sync events */ /* Single threaded workqueue used for stp sync events */

View File

@ -320,16 +320,10 @@ static int __arch_update_cpu_topology(void)
int arch_update_cpu_topology(void) int arch_update_cpu_topology(void)
{ {
struct device *dev; int rc;
int cpu, rc;
rc = __arch_update_cpu_topology(); rc = __arch_update_cpu_topology();
on_each_cpu(__arch_update_dedicated_flag, NULL, 0); on_each_cpu(__arch_update_dedicated_flag, NULL, 0);
for_each_online_cpu(cpu) {
dev = get_cpu_device(cpu);
if (dev)
kobject_uevent(&dev->kobj, KOBJ_CHANGE);
}
return rc; return rc;
} }

View File

@ -288,15 +288,16 @@ static void __init test_monitor_call(void)
void __init trap_init(void) void __init trap_init(void)
{ {
struct lowcore *lc = get_lowcore();
unsigned long flags; unsigned long flags;
struct ctlreg cr0; struct ctlreg cr0;
local_irq_save(flags); local_irq_save(flags);
cr0 = local_ctl_clear_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT); cr0 = local_ctl_clear_bit(0, CR0_LOW_ADDRESS_PROTECTION_BIT);
psw_bits(S390_lowcore.external_new_psw).mcheck = 1; psw_bits(lc->external_new_psw).mcheck = 1;
psw_bits(S390_lowcore.program_new_psw).mcheck = 1; psw_bits(lc->program_new_psw).mcheck = 1;
psw_bits(S390_lowcore.svc_new_psw).mcheck = 1; psw_bits(lc->svc_new_psw).mcheck = 1;
psw_bits(S390_lowcore.io_new_psw).mcheck = 1; psw_bits(lc->io_new_psw).mcheck = 1;
local_ctl_load(0, &cr0); local_ctl_load(0, &cr0);
local_irq_restore(flags); local_irq_restore(flags);
local_mcck_enable(); local_mcck_enable();
@ -307,11 +308,12 @@ static void (*pgm_check_table[128])(struct pt_regs *regs);
void noinstr __do_pgm_check(struct pt_regs *regs) void noinstr __do_pgm_check(struct pt_regs *regs)
{ {
unsigned int trapnr; struct lowcore *lc = get_lowcore();
irqentry_state_t state; irqentry_state_t state;
unsigned int trapnr;
regs->int_code = S390_lowcore.pgm_int_code; regs->int_code = lc->pgm_int_code;
regs->int_parm_long = S390_lowcore.trans_exc_code; regs->int_parm_long = lc->trans_exc_code;
state = irqentry_enter(regs); state = irqentry_enter(regs);
@ -324,19 +326,19 @@ void noinstr __do_pgm_check(struct pt_regs *regs)
current->thread.last_break = regs->last_break; current->thread.last_break = regs->last_break;
} }
if (S390_lowcore.pgm_code & 0x0200) { if (lc->pgm_code & 0x0200) {
/* transaction abort */ /* transaction abort */
current->thread.trap_tdb = S390_lowcore.pgm_tdb; current->thread.trap_tdb = lc->pgm_tdb;
} }
if (S390_lowcore.pgm_code & PGM_INT_CODE_PER) { if (lc->pgm_code & PGM_INT_CODE_PER) {
if (user_mode(regs)) { if (user_mode(regs)) {
struct per_event *ev = &current->thread.per_event; struct per_event *ev = &current->thread.per_event;
set_thread_flag(TIF_PER_TRAP); set_thread_flag(TIF_PER_TRAP);
ev->address = S390_lowcore.per_address; ev->address = lc->per_address;
ev->cause = S390_lowcore.per_code_combined; ev->cause = lc->per_code_combined;
ev->paid = S390_lowcore.per_access_id; ev->paid = lc->per_access_id;
} else { } else {
/* PER event in kernel is kprobes */ /* PER event in kernel is kprobes */
__arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER); __arch_local_irq_ssm(regs->psw.mask & ~PSW_MASK_PER);

View File

@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(uv_pin_shared);
* *
* @paddr: Absolute host address of page to be destroyed * @paddr: Absolute host address of page to be destroyed
*/ */
static int uv_destroy_page(unsigned long paddr) static int uv_destroy(unsigned long paddr)
{ {
struct uv_cb_cfs uvcb = { struct uv_cb_cfs uvcb = {
.header.cmd = UVC_CMD_DESTR_SEC_STOR, .header.cmd = UVC_CMD_DESTR_SEC_STOR,
@ -131,28 +131,40 @@ static int uv_destroy_page(unsigned long paddr)
} }
/* /*
* The caller must already hold a reference to the page * The caller must already hold a reference to the folio
*/ */
int uv_destroy_owned_page(unsigned long paddr) int uv_destroy_folio(struct folio *folio)
{ {
struct page *page = phys_to_page(paddr);
int rc; int rc;
get_page(page); /* See gmap_make_secure(): large folios cannot be secure */
rc = uv_destroy_page(paddr); if (unlikely(folio_test_large(folio)))
return 0;
folio_get(folio);
rc = uv_destroy(folio_to_phys(folio));
if (!rc) if (!rc)
clear_bit(PG_arch_1, &page->flags); clear_bit(PG_arch_1, &folio->flags);
put_page(page); folio_put(folio);
return rc; return rc;
} }
/*
* The present PTE still indirectly holds a folio reference through the mapping.
*/
int uv_destroy_pte(pte_t pte)
{
VM_WARN_ON(!pte_present(pte));
return uv_destroy_folio(pfn_folio(pte_pfn(pte)));
}
/* /*
* Requests the Ultravisor to encrypt a guest page and make it * Requests the Ultravisor to encrypt a guest page and make it
* accessible to the host for paging (export). * accessible to the host for paging (export).
* *
* @paddr: Absolute host address of page to be exported * @paddr: Absolute host address of page to be exported
*/ */
int uv_convert_from_secure(unsigned long paddr) static int uv_convert_from_secure(unsigned long paddr)
{ {
struct uv_cb_cfs uvcb = { struct uv_cb_cfs uvcb = {
.header.cmd = UVC_CMD_CONV_FROM_SEC_STOR, .header.cmd = UVC_CMD_CONV_FROM_SEC_STOR,
@ -166,21 +178,33 @@ int uv_convert_from_secure(unsigned long paddr)
} }
/* /*
* The caller must already hold a reference to the page * The caller must already hold a reference to the folio.
*/ */
int uv_convert_owned_from_secure(unsigned long paddr) static int uv_convert_from_secure_folio(struct folio *folio)
{ {
struct page *page = phys_to_page(paddr);
int rc; int rc;
get_page(page); /* See gmap_make_secure(): large folios cannot be secure */
rc = uv_convert_from_secure(paddr); if (unlikely(folio_test_large(folio)))
return 0;
folio_get(folio);
rc = uv_convert_from_secure(folio_to_phys(folio));
if (!rc) if (!rc)
clear_bit(PG_arch_1, &page->flags); clear_bit(PG_arch_1, &folio->flags);
put_page(page); folio_put(folio);
return rc; return rc;
} }
/*
* The present PTE still indirectly holds a folio reference through the mapping.
*/
int uv_convert_from_secure_pte(pte_t pte)
{
VM_WARN_ON(!pte_present(pte));
return uv_convert_from_secure_folio(pfn_folio(pte_pfn(pte)));
}
/* /*
* Calculate the expected ref_count for a folio that would otherwise have no * Calculate the expected ref_count for a folio that would otherwise have no
* further pins. This was cribbed from similar functions in other places in * further pins. This was cribbed from similar functions in other places in
@ -266,6 +290,36 @@ static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_str
return atomic_read(&mm->context.protected_count) > 1; return atomic_read(&mm->context.protected_count) > 1;
} }
/*
* Drain LRU caches: the local one on first invocation and the ones of all
* CPUs on successive invocations. Returns "true" on the first invocation.
*/
static bool drain_lru(bool *drain_lru_called)
{
/*
* If we have tried a local drain and the folio refcount
* still does not match our expected safe value, try with a
* system wide drain. This is needed if the pagevecs holding
* the page are on a different CPU.
*/
if (*drain_lru_called) {
lru_add_drain_all();
/* We give up here, don't retry immediately. */
return false;
}
/*
* We are here if the folio refcount does not match the
* expected safe value. The main culprits are usually
* pagevecs. With lru_add_drain() we drain the pagevecs
* on the local CPU so that hopefully the refcount will
* reach the expected safe value.
*/
lru_add_drain();
*drain_lru_called = true;
/* The caller should try again immediately */
return true;
}
/* /*
* Requests the Ultravisor to make a page accessible to a guest. * Requests the Ultravisor to make a page accessible to a guest.
* If it's brought in the first time, it will be cleared. If * If it's brought in the first time, it will be cleared. If
@ -275,7 +329,7 @@ static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_str
int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb) int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb)
{ {
struct vm_area_struct *vma; struct vm_area_struct *vma;
bool local_drain = false; bool drain_lru_called = false;
spinlock_t *ptelock; spinlock_t *ptelock;
unsigned long uaddr; unsigned long uaddr;
struct folio *folio; struct folio *folio;
@ -308,52 +362,63 @@ again:
goto out; goto out;
if (pte_present(*ptep) && !(pte_val(*ptep) & _PAGE_INVALID) && pte_write(*ptep)) { if (pte_present(*ptep) && !(pte_val(*ptep) & _PAGE_INVALID) && pte_write(*ptep)) {
folio = page_folio(pte_page(*ptep)); folio = page_folio(pte_page(*ptep));
rc = -EINVAL;
if (folio_test_large(folio))
goto unlock;
rc = -EAGAIN; rc = -EAGAIN;
if (folio_trylock(folio)) { if (folio_test_large(folio)) {
rc = -E2BIG;
} else if (folio_trylock(folio)) {
if (should_export_before_import(uvcb, gmap->mm)) if (should_export_before_import(uvcb, gmap->mm))
uv_convert_from_secure(PFN_PHYS(folio_pfn(folio))); uv_convert_from_secure(PFN_PHYS(folio_pfn(folio)));
rc = make_folio_secure(folio, uvcb); rc = make_folio_secure(folio, uvcb);
folio_unlock(folio); folio_unlock(folio);
} }
/*
* Once we drop the PTL, the folio may get unmapped and
* freed immediately. We need a temporary reference.
*/
if (rc == -EAGAIN || rc == -E2BIG)
folio_get(folio);
} }
unlock:
pte_unmap_unlock(ptep, ptelock); pte_unmap_unlock(ptep, ptelock);
out: out:
mmap_read_unlock(gmap->mm); mmap_read_unlock(gmap->mm);
if (rc == -EAGAIN) { switch (rc) {
case -E2BIG:
folio_lock(folio);
rc = split_folio(folio);
folio_unlock(folio);
folio_put(folio);
switch (rc) {
case 0:
/* Splitting succeeded, try again immediately. */
goto again;
case -EAGAIN:
/* Additional folio references. */
if (drain_lru(&drain_lru_called))
goto again;
return -EAGAIN;
case -EBUSY:
/* Unexpected race. */
return -EAGAIN;
}
WARN_ON_ONCE(1);
return -ENXIO;
case -EAGAIN:
/* /*
* If we are here because the UVC returned busy or partial * If we are here because the UVC returned busy or partial
* completion, this is just a useless check, but it is safe. * completion, this is just a useless check, but it is safe.
*/ */
folio_wait_writeback(folio); folio_wait_writeback(folio);
} else if (rc == -EBUSY) { folio_put(folio);
/* return -EAGAIN;
* If we have tried a local drain and the folio refcount case -EBUSY:
* still does not match our expected safe value, try with a /* Additional folio references. */
* system wide drain. This is needed if the pagevecs holding if (drain_lru(&drain_lru_called))
* the page are on a different CPU. goto again;
*/ return -EAGAIN;
if (local_drain) { case -ENXIO:
lru_add_drain_all();
/* We give up here, and let the caller try again */
return -EAGAIN;
}
/*
* We are here if the folio refcount does not match the
* expected safe value. The main culprits are usually
* pagevecs. With lru_add_drain() we drain the pagevecs
* on the local CPU so that hopefully the refcount will
* reach the expected safe value.
*/
lru_add_drain();
local_drain = true;
/* And now we try again immediately after draining */
goto again;
} else if (rc == -ENXIO) {
if (gmap_fault(gmap, gaddr, FAULT_FLAG_WRITE)) if (gmap_fault(gmap, gaddr, FAULT_FLAG_WRITE))
return -EFAULT; return -EFAULT;
return -EAGAIN; return -EAGAIN;
@ -388,6 +453,7 @@ int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr)
{ {
struct vm_area_struct *vma; struct vm_area_struct *vma;
unsigned long uaddr; unsigned long uaddr;
struct folio *folio;
struct page *page; struct page *page;
int rc; int rc;
@ -411,7 +477,8 @@ int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr)
page = follow_page(vma, uaddr, FOLL_WRITE | FOLL_GET); page = follow_page(vma, uaddr, FOLL_WRITE | FOLL_GET);
if (IS_ERR_OR_NULL(page)) if (IS_ERR_OR_NULL(page))
goto out; goto out;
rc = uv_destroy_owned_page(page_to_phys(page)); folio = page_folio(page);
rc = uv_destroy_folio(folio);
/* /*
* Fault handlers can race; it is possible that two CPUs will fault * Fault handlers can race; it is possible that two CPUs will fault
* on the same secure page. One CPU can destroy the page, reboot, * on the same secure page. One CPU can destroy the page, reboot,
@ -422,8 +489,8 @@ int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr)
* we instead try to export the page. * we instead try to export the page.
*/ */
if (rc) if (rc)
rc = uv_convert_owned_from_secure(page_to_phys(page)); rc = uv_convert_from_secure_folio(folio);
put_page(page); folio_put(folio);
out: out:
mmap_read_unlock(gmap->mm); mmap_read_unlock(gmap->mm);
return rc; return rc;
@ -431,47 +498,51 @@ out:
EXPORT_SYMBOL_GPL(gmap_destroy_page); EXPORT_SYMBOL_GPL(gmap_destroy_page);
/* /*
* To be called with the page locked or with an extra reference! This will * To be called with the folio locked or with an extra reference! This will
* prevent gmap_make_secure from touching the page concurrently. Having 2 * prevent gmap_make_secure from touching the folio concurrently. Having 2
* parallel make_page_accessible is fine, as the UV calls will become a * parallel arch_make_folio_accessible is fine, as the UV calls will become a
* no-op if the page is already exported. * no-op if the folio is already exported.
*/ */
int arch_make_page_accessible(struct page *page) int arch_make_folio_accessible(struct folio *folio)
{ {
int rc = 0; int rc = 0;
/* Hugepage cannot be protected, so nothing to do */ /* See gmap_make_secure(): large folios cannot be secure */
if (PageHuge(page)) if (unlikely(folio_test_large(folio)))
return 0; return 0;
/* /*
* PG_arch_1 is used in 3 places: * PG_arch_1 is used in 2 places:
* 1. for kernel page tables during early boot * 1. for storage keys of hugetlb folios and KVM
* 2. for storage keys of huge pages and KVM * 2. As an indication that this small folio might be secure. This can
* 3. As an indication that this page might be secure. This can
* overindicate, e.g. we set the bit before calling * overindicate, e.g. we set the bit before calling
* convert_to_secure. * convert_to_secure.
* As secure pages are never huge, all 3 variants can co-exists. * As secure pages are never large folios, both variants can co-exists.
*/ */
if (!test_bit(PG_arch_1, &page->flags)) if (!test_bit(PG_arch_1, &folio->flags))
return 0; return 0;
rc = uv_pin_shared(page_to_phys(page)); rc = uv_pin_shared(folio_to_phys(folio));
if (!rc) { if (!rc) {
clear_bit(PG_arch_1, &page->flags); clear_bit(PG_arch_1, &folio->flags);
return 0; return 0;
} }
rc = uv_convert_from_secure(page_to_phys(page)); rc = uv_convert_from_secure(folio_to_phys(folio));
if (!rc) { if (!rc) {
clear_bit(PG_arch_1, &page->flags); clear_bit(PG_arch_1, &folio->flags);
return 0; return 0;
} }
return rc; return rc;
} }
EXPORT_SYMBOL_GPL(arch_make_page_accessible); EXPORT_SYMBOL_GPL(arch_make_folio_accessible);
int arch_make_page_accessible(struct page *page)
{
return arch_make_folio_accessible(page_folio(page));
}
EXPORT_SYMBOL_GPL(arch_make_page_accessible);
#endif #endif
#if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) || IS_ENABLED(CONFIG_KVM) #if defined(CONFIG_PROTECTED_VIRTUALIZATION_GUEST) || IS_ENABLED(CONFIG_KVM)

View File

@ -35,14 +35,15 @@ static DEFINE_PER_CPU(u64, mt_scaling_jiffies);
static inline void set_vtimer(u64 expires) static inline void set_vtimer(u64 expires)
{ {
struct lowcore *lc = get_lowcore();
u64 timer; u64 timer;
asm volatile( asm volatile(
" stpt %0\n" /* Store current cpu timer value */ " stpt %0\n" /* Store current cpu timer value */
" spt %1" /* Set new value imm. afterwards */ " spt %1" /* Set new value imm. afterwards */
: "=Q" (timer) : "Q" (expires)); : "=Q" (timer) : "Q" (expires));
S390_lowcore.system_timer += S390_lowcore.last_update_timer - timer; lc->system_timer += lc->last_update_timer - timer;
S390_lowcore.last_update_timer = expires; lc->last_update_timer = expires;
} }
static inline int virt_timer_forward(u64 elapsed) static inline int virt_timer_forward(u64 elapsed)
@ -117,22 +118,23 @@ static void account_system_index_scaled(struct task_struct *p, u64 cputime,
static int do_account_vtime(struct task_struct *tsk) static int do_account_vtime(struct task_struct *tsk)
{ {
u64 timer, clock, user, guest, system, hardirq, softirq; u64 timer, clock, user, guest, system, hardirq, softirq;
struct lowcore *lc = get_lowcore();
timer = S390_lowcore.last_update_timer; timer = lc->last_update_timer;
clock = S390_lowcore.last_update_clock; clock = lc->last_update_clock;
asm volatile( asm volatile(
" stpt %0\n" /* Store current cpu timer value */ " stpt %0\n" /* Store current cpu timer value */
" stckf %1" /* Store current tod clock value */ " stckf %1" /* Store current tod clock value */
: "=Q" (S390_lowcore.last_update_timer), : "=Q" (lc->last_update_timer),
"=Q" (S390_lowcore.last_update_clock) "=Q" (lc->last_update_clock)
: : "cc"); : : "cc");
clock = S390_lowcore.last_update_clock - clock; clock = lc->last_update_clock - clock;
timer -= S390_lowcore.last_update_timer; timer -= lc->last_update_timer;
if (hardirq_count()) if (hardirq_count())
S390_lowcore.hardirq_timer += timer; lc->hardirq_timer += timer;
else else
S390_lowcore.system_timer += timer; lc->system_timer += timer;
/* Update MT utilization calculation */ /* Update MT utilization calculation */
if (smp_cpu_mtid && if (smp_cpu_mtid &&
@ -141,16 +143,16 @@ static int do_account_vtime(struct task_struct *tsk)
/* Calculate cputime delta */ /* Calculate cputime delta */
user = update_tsk_timer(&tsk->thread.user_timer, user = update_tsk_timer(&tsk->thread.user_timer,
READ_ONCE(S390_lowcore.user_timer)); READ_ONCE(lc->user_timer));
guest = update_tsk_timer(&tsk->thread.guest_timer, guest = update_tsk_timer(&tsk->thread.guest_timer,
READ_ONCE(S390_lowcore.guest_timer)); READ_ONCE(lc->guest_timer));
system = update_tsk_timer(&tsk->thread.system_timer, system = update_tsk_timer(&tsk->thread.system_timer,
READ_ONCE(S390_lowcore.system_timer)); READ_ONCE(lc->system_timer));
hardirq = update_tsk_timer(&tsk->thread.hardirq_timer, hardirq = update_tsk_timer(&tsk->thread.hardirq_timer,
READ_ONCE(S390_lowcore.hardirq_timer)); READ_ONCE(lc->hardirq_timer));
softirq = update_tsk_timer(&tsk->thread.softirq_timer, softirq = update_tsk_timer(&tsk->thread.softirq_timer,
READ_ONCE(S390_lowcore.softirq_timer)); READ_ONCE(lc->softirq_timer));
S390_lowcore.steal_timer += lc->steal_timer +=
clock - user - guest - system - hardirq - softirq; clock - user - guest - system - hardirq - softirq;
/* Push account value */ /* Push account value */
@ -176,17 +178,19 @@ static int do_account_vtime(struct task_struct *tsk)
void vtime_task_switch(struct task_struct *prev) void vtime_task_switch(struct task_struct *prev)
{ {
struct lowcore *lc = get_lowcore();
do_account_vtime(prev); do_account_vtime(prev);
prev->thread.user_timer = S390_lowcore.user_timer; prev->thread.user_timer = lc->user_timer;
prev->thread.guest_timer = S390_lowcore.guest_timer; prev->thread.guest_timer = lc->guest_timer;
prev->thread.system_timer = S390_lowcore.system_timer; prev->thread.system_timer = lc->system_timer;
prev->thread.hardirq_timer = S390_lowcore.hardirq_timer; prev->thread.hardirq_timer = lc->hardirq_timer;
prev->thread.softirq_timer = S390_lowcore.softirq_timer; prev->thread.softirq_timer = lc->softirq_timer;
S390_lowcore.user_timer = current->thread.user_timer; lc->user_timer = current->thread.user_timer;
S390_lowcore.guest_timer = current->thread.guest_timer; lc->guest_timer = current->thread.guest_timer;
S390_lowcore.system_timer = current->thread.system_timer; lc->system_timer = current->thread.system_timer;
S390_lowcore.hardirq_timer = current->thread.hardirq_timer; lc->hardirq_timer = current->thread.hardirq_timer;
S390_lowcore.softirq_timer = current->thread.softirq_timer; lc->softirq_timer = current->thread.softirq_timer;
} }
/* /*
@ -196,28 +200,29 @@ void vtime_task_switch(struct task_struct *prev)
*/ */
void vtime_flush(struct task_struct *tsk) void vtime_flush(struct task_struct *tsk)
{ {
struct lowcore *lc = get_lowcore();
u64 steal, avg_steal; u64 steal, avg_steal;
if (do_account_vtime(tsk)) if (do_account_vtime(tsk))
virt_timer_expire(); virt_timer_expire();
steal = S390_lowcore.steal_timer; steal = lc->steal_timer;
avg_steal = S390_lowcore.avg_steal_timer; avg_steal = lc->avg_steal_timer;
if ((s64) steal > 0) { if ((s64) steal > 0) {
S390_lowcore.steal_timer = 0; lc->steal_timer = 0;
account_steal_time(cputime_to_nsecs(steal)); account_steal_time(cputime_to_nsecs(steal));
avg_steal += steal; avg_steal += steal;
} }
S390_lowcore.avg_steal_timer = avg_steal / 2; lc->avg_steal_timer = avg_steal / 2;
} }
static u64 vtime_delta(void) static u64 vtime_delta(void)
{ {
u64 timer = S390_lowcore.last_update_timer; struct lowcore *lc = get_lowcore();
u64 timer = lc->last_update_timer;
S390_lowcore.last_update_timer = get_cpu_timer(); lc->last_update_timer = get_cpu_timer();
return timer - lc->last_update_timer;
return timer - S390_lowcore.last_update_timer;
} }
/* /*
@ -226,12 +231,13 @@ static u64 vtime_delta(void)
*/ */
void vtime_account_kernel(struct task_struct *tsk) void vtime_account_kernel(struct task_struct *tsk)
{ {
struct lowcore *lc = get_lowcore();
u64 delta = vtime_delta(); u64 delta = vtime_delta();
if (tsk->flags & PF_VCPU) if (tsk->flags & PF_VCPU)
S390_lowcore.guest_timer += delta; lc->guest_timer += delta;
else else
S390_lowcore.system_timer += delta; lc->system_timer += delta;
virt_timer_forward(delta); virt_timer_forward(delta);
} }
@ -241,7 +247,7 @@ void vtime_account_softirq(struct task_struct *tsk)
{ {
u64 delta = vtime_delta(); u64 delta = vtime_delta();
S390_lowcore.softirq_timer += delta; get_lowcore()->softirq_timer += delta;
virt_timer_forward(delta); virt_timer_forward(delta);
} }
@ -250,7 +256,7 @@ void vtime_account_hardirq(struct task_struct *tsk)
{ {
u64 delta = vtime_delta(); u64 delta = vtime_delta();
S390_lowcore.hardirq_timer += delta; get_lowcore()->hardirq_timer += delta;
virt_timer_forward(delta); virt_timer_forward(delta);
} }

View File

@ -14,167 +14,10 @@
#include <asm/access-regs.h> #include <asm/access-regs.h>
#include <asm/fault.h> #include <asm/fault.h>
#include <asm/gmap.h> #include <asm/gmap.h>
#include <asm/dat-bits.h>
#include "kvm-s390.h" #include "kvm-s390.h"
#include "gaccess.h" #include "gaccess.h"
union asce {
unsigned long val;
struct {
unsigned long origin : 52; /* Region- or Segment-Table Origin */
unsigned long : 2;
unsigned long g : 1; /* Subspace Group Control */
unsigned long p : 1; /* Private Space Control */
unsigned long s : 1; /* Storage-Alteration-Event Control */
unsigned long x : 1; /* Space-Switch-Event Control */
unsigned long r : 1; /* Real-Space Control */
unsigned long : 1;
unsigned long dt : 2; /* Designation-Type Control */
unsigned long tl : 2; /* Region- or Segment-Table Length */
};
};
enum {
ASCE_TYPE_SEGMENT = 0,
ASCE_TYPE_REGION3 = 1,
ASCE_TYPE_REGION2 = 2,
ASCE_TYPE_REGION1 = 3
};
union region1_table_entry {
unsigned long val;
struct {
unsigned long rto: 52;/* Region-Table Origin */
unsigned long : 2;
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 1;
unsigned long tf : 2; /* Region-Second-Table Offset */
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long : 1;
unsigned long tt : 2; /* Table-Type Bits */
unsigned long tl : 2; /* Region-Second-Table Length */
};
};
union region2_table_entry {
unsigned long val;
struct {
unsigned long rto: 52;/* Region-Table Origin */
unsigned long : 2;
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 1;
unsigned long tf : 2; /* Region-Third-Table Offset */
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long : 1;
unsigned long tt : 2; /* Table-Type Bits */
unsigned long tl : 2; /* Region-Third-Table Length */
};
};
struct region3_table_entry_fc0 {
unsigned long sto: 52;/* Segment-Table Origin */
unsigned long : 1;
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 1;
unsigned long tf : 2; /* Segment-Table Offset */
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long cr : 1; /* Common-Region Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long tl : 2; /* Segment-Table Length */
};
struct region3_table_entry_fc1 {
unsigned long rfaa : 33; /* Region-Frame Absolute Address */
unsigned long : 14;
unsigned long av : 1; /* ACCF-Validity Control */
unsigned long acc: 4; /* Access-Control Bits */
unsigned long f : 1; /* Fetch-Protection Bit */
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long iep: 1; /* Instruction-Execution-Protection */
unsigned long : 2;
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long cr : 1; /* Common-Region Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
union region3_table_entry {
unsigned long val;
struct region3_table_entry_fc0 fc0;
struct region3_table_entry_fc1 fc1;
struct {
unsigned long : 53;
unsigned long fc : 1; /* Format-Control */
unsigned long : 4;
unsigned long i : 1; /* Region-Invalid Bit */
unsigned long cr : 1; /* Common-Region Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
};
struct segment_entry_fc0 {
unsigned long pto: 53;/* Page-Table Origin */
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long : 3;
unsigned long i : 1; /* Segment-Invalid Bit */
unsigned long cs : 1; /* Common-Segment Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
struct segment_entry_fc1 {
unsigned long sfaa : 44; /* Segment-Frame Absolute Address */
unsigned long : 3;
unsigned long av : 1; /* ACCF-Validity Control */
unsigned long acc: 4; /* Access-Control Bits */
unsigned long f : 1; /* Fetch-Protection Bit */
unsigned long fc : 1; /* Format-Control */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long iep: 1; /* Instruction-Execution-Protection */
unsigned long : 2;
unsigned long i : 1; /* Segment-Invalid Bit */
unsigned long cs : 1; /* Common-Segment Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
union segment_table_entry {
unsigned long val;
struct segment_entry_fc0 fc0;
struct segment_entry_fc1 fc1;
struct {
unsigned long : 53;
unsigned long fc : 1; /* Format-Control */
unsigned long : 4;
unsigned long i : 1; /* Segment-Invalid Bit */
unsigned long cs : 1; /* Common-Segment Bit */
unsigned long tt : 2; /* Table-Type Bits */
unsigned long : 2;
};
};
enum {
TABLE_TYPE_SEGMENT = 0,
TABLE_TYPE_REGION3 = 1,
TABLE_TYPE_REGION2 = 2,
TABLE_TYPE_REGION1 = 3
};
union page_table_entry {
unsigned long val;
struct {
unsigned long pfra : 52; /* Page-Frame Real Address */
unsigned long z : 1; /* Zero Bit */
unsigned long i : 1; /* Page-Invalid Bit */
unsigned long p : 1; /* DAT-Protection Bit */
unsigned long iep: 1; /* Instruction-Execution-Protection */
unsigned long : 8;
};
};
/* /*
* vaddress union in order to easily decode a virtual address into its * vaddress union in order to easily decode a virtual address into its
* region first index, region second index etc. parts. * region first index, region second index etc. parts.
@ -632,7 +475,7 @@ static unsigned long guest_translate(struct kvm_vcpu *vcpu, unsigned long gva,
iep = ctlreg0.iep && test_kvm_facility(vcpu->kvm, 130); iep = ctlreg0.iep && test_kvm_facility(vcpu->kvm, 130);
if (asce.r) if (asce.r)
goto real_address; goto real_address;
ptr = asce.origin * PAGE_SIZE; ptr = asce.rsto * PAGE_SIZE;
switch (asce.dt) { switch (asce.dt) {
case ASCE_TYPE_REGION1: case ASCE_TYPE_REGION1:
if (vaddr.rfx01 > asce.tl) if (vaddr.rfx01 > asce.tl)
@ -1379,7 +1222,7 @@ static int kvm_s390_shadow_tables(struct gmap *sg, unsigned long saddr,
parent = sg->parent; parent = sg->parent;
vaddr.addr = saddr; vaddr.addr = saddr;
asce.val = sg->orig_asce; asce.val = sg->orig_asce;
ptr = asce.origin * PAGE_SIZE; ptr = asce.rsto * PAGE_SIZE;
if (asce.r) { if (asce.r) {
*fake = 1; *fake = 1;
ptr = 0; ptr = 0;

View File

@ -4080,7 +4080,7 @@ static void kvm_gmap_notifier(struct gmap *gmap, unsigned long start,
bool kvm_arch_no_poll(struct kvm_vcpu *vcpu) bool kvm_arch_no_poll(struct kvm_vcpu *vcpu)
{ {
/* do not poll with more than halt_poll_max_steal percent of steal time */ /* do not poll with more than halt_poll_max_steal percent of steal time */
if (S390_lowcore.avg_steal_timer * 100 / (TICK_USEC << 12) >= if (get_lowcore()->avg_steal_timer * 100 / (TICK_USEC << 12) >=
READ_ONCE(halt_poll_max_steal)) { READ_ONCE(halt_poll_max_steal)) {
vcpu->stat.halt_no_poll_steal++; vcpu->stat.halt_no_poll_steal++;
return true; return true;
@ -4830,7 +4830,8 @@ static int __vcpu_run(struct kvm_vcpu *vcpu)
sizeof(sie_page->pv_grregs)); sizeof(sie_page->pv_grregs));
} }
exit_reason = sie64a(vcpu->arch.sie_block, exit_reason = sie64a(vcpu->arch.sie_block,
vcpu->run->s.regs.gprs); vcpu->run->s.regs.gprs,
gmap_get_enabled()->asce);
if (kvm_s390_pv_cpu_is_protected(vcpu)) { if (kvm_s390_pv_cpu_is_protected(vcpu)) {
memcpy(vcpu->run->s.regs.gprs, memcpy(vcpu->run->s.regs.gprs,
sie_page->pv_grregs, sie_page->pv_grregs,

View File

@ -1150,7 +1150,7 @@ 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 (!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, gmap_get_enabled()->asce);
barrier(); barrier();
vcpu->arch.sie_block->prog0c &= ~PROG_IN_SIE; vcpu->arch.sie_block->prog0c &= ~PROG_IN_SIE;

View File

@ -119,7 +119,7 @@ static inline void arch_spin_lock_queued(arch_spinlock_t *lp)
struct spin_wait *node, *next; struct spin_wait *node, *next;
int lockval, ix, node_id, tail_id, old, new, owner, count; int lockval, ix, node_id, tail_id, old, new, owner, count;
ix = S390_lowcore.spinlock_index++; ix = get_lowcore()->spinlock_index++;
barrier(); barrier();
lockval = SPINLOCK_LOCKVAL; /* cpu + 1 */ lockval = SPINLOCK_LOCKVAL; /* cpu + 1 */
node = this_cpu_ptr(&spin_wait[ix]); node = this_cpu_ptr(&spin_wait[ix]);
@ -205,7 +205,7 @@ static inline void arch_spin_lock_queued(arch_spinlock_t *lp)
} }
out: out:
S390_lowcore.spinlock_index--; get_lowcore()->spinlock_index--;
} }
static inline void arch_spin_lock_classic(arch_spinlock_t *lp) static inline void arch_spin_lock_classic(arch_spinlock_t *lp)

View File

@ -72,4 +72,5 @@ static struct kunit_suite kprobes_test_suite = {
kunit_test_suites(&kprobes_test_suite); kunit_test_suites(&kprobes_test_suite);
MODULE_DESCRIPTION("KUnit tests for kprobes");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -29,4 +29,5 @@ static struct kunit_suite modules_test_suite = {
kunit_test_suites(&modules_test_suite); kunit_test_suites(&modules_test_suite);
MODULE_DESCRIPTION("KUnit test that modules with many relocations are loaded properly");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -356,7 +356,7 @@ static noinline int unwindme_func2(struct unwindme *u)
if (u->flags & UWM_SWITCH_STACK) { if (u->flags & UWM_SWITCH_STACK) {
local_irq_save(flags); local_irq_save(flags);
local_mcck_save(mflags); local_mcck_save(mflags);
rc = call_on_stack(1, S390_lowcore.nodat_stack, rc = call_on_stack(1, get_lowcore()->nodat_stack,
int, unwindme_func3, struct unwindme *, u); int, unwindme_func3, struct unwindme *, u);
local_mcck_restore(mflags); local_mcck_restore(mflags);
local_irq_restore(flags); local_irq_restore(flags);
@ -519,4 +519,5 @@ static struct kunit_suite test_unwind_suite = {
kunit_test_suites(&test_unwind_suite); kunit_test_suites(&test_unwind_suite);
MODULE_DESCRIPTION("KUnit test for unwind_for_each_frame");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -21,13 +21,13 @@ void debug_user_asce(int exit)
local_ctl_store(1, &cr1); local_ctl_store(1, &cr1);
local_ctl_store(7, &cr7); local_ctl_store(7, &cr7);
if (cr1.val == S390_lowcore.kernel_asce.val && cr7.val == S390_lowcore.user_asce.val) if (cr1.val == get_lowcore()->kernel_asce.val && cr7.val == get_lowcore()->user_asce.val)
return; return;
panic("incorrect ASCE on kernel %s\n" panic("incorrect ASCE on kernel %s\n"
"cr1: %016lx cr7: %016lx\n" "cr1: %016lx cr7: %016lx\n"
"kernel: %016lx user: %016lx\n", "kernel: %016lx user: %016lx\n",
exit ? "exit" : "entry", cr1.val, cr7.val, exit ? "exit" : "entry", cr1.val, cr7.val,
S390_lowcore.kernel_asce.val, S390_lowcore.user_asce.val); get_lowcore()->kernel_asce.val, get_lowcore()->user_asce.val);
} }
#endif /*CONFIG_DEBUG_ENTRY */ #endif /*CONFIG_DEBUG_ENTRY */

View File

@ -427,4 +427,5 @@ static void __exit cmm_exit(void)
} }
module_exit(cmm_exit); module_exit(cmm_exit);
MODULE_DESCRIPTION("Cooperative memory management interface");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -288,7 +288,7 @@ static int pt_dump_init(void)
* kernel ASCE. We need this to keep the page table walker functions * kernel ASCE. We need this to keep the page table walker functions
* from accessing non-existent entries. * from accessing non-existent entries.
*/ */
max_addr = (S390_lowcore.kernel_asce.val & _REGION_ENTRY_TYPE_MASK) >> 2; max_addr = (get_lowcore()->kernel_asce.val & _REGION_ENTRY_TYPE_MASK) >> 2;
max_addr = 1UL << (max_addr * 11 + 31); max_addr = 1UL << (max_addr * 11 + 31);
address_markers[IDENTITY_AFTER_END_NR].start_address = ident_map_size; address_markers[IDENTITY_AFTER_END_NR].start_address = ident_map_size;
address_markers[AMODE31_START_NR].start_address = (unsigned long)__samode31; address_markers[AMODE31_START_NR].start_address = (unsigned long)__samode31;

View File

@ -74,7 +74,7 @@ static enum fault_type get_fault_type(struct pt_regs *regs)
return USER_FAULT; return USER_FAULT;
if (!IS_ENABLED(CONFIG_PGSTE)) if (!IS_ENABLED(CONFIG_PGSTE))
return KERNEL_FAULT; return KERNEL_FAULT;
gmap = (struct gmap *)S390_lowcore.gmap; gmap = (struct gmap *)get_lowcore()->gmap;
if (gmap && gmap->asce == regs->cr1) if (gmap && gmap->asce == regs->cr1)
return GMAP_FAULT; return GMAP_FAULT;
return KERNEL_FAULT; return KERNEL_FAULT;
@ -182,15 +182,15 @@ static void dump_fault_info(struct pt_regs *regs)
pr_cont("mode while using "); pr_cont("mode while using ");
switch (get_fault_type(regs)) { switch (get_fault_type(regs)) {
case USER_FAULT: case USER_FAULT:
asce = S390_lowcore.user_asce.val; asce = get_lowcore()->user_asce.val;
pr_cont("user "); pr_cont("user ");
break; break;
case GMAP_FAULT: case GMAP_FAULT:
asce = ((struct gmap *)S390_lowcore.gmap)->asce; asce = ((struct gmap *)get_lowcore()->gmap)->asce;
pr_cont("gmap "); pr_cont("gmap ");
break; break;
case KERNEL_FAULT: case KERNEL_FAULT:
asce = S390_lowcore.kernel_asce.val; asce = get_lowcore()->kernel_asce.val;
pr_cont("kernel "); pr_cont("kernel ");
break; break;
default: default:
@ -351,7 +351,7 @@ lock_mmap:
mmap_read_lock(mm); mmap_read_lock(mm);
gmap = NULL; gmap = NULL;
if (IS_ENABLED(CONFIG_PGSTE) && type == GMAP_FAULT) { if (IS_ENABLED(CONFIG_PGSTE) && type == GMAP_FAULT) {
gmap = (struct gmap *)S390_lowcore.gmap; gmap = (struct gmap *)get_lowcore()->gmap;
current->thread.gmap_addr = address; current->thread.gmap_addr = address;
current->thread.gmap_write_flag = !!(flags & FAULT_FLAG_WRITE); current->thread.gmap_write_flag = !!(flags & FAULT_FLAG_WRITE);
current->thread.gmap_int_code = regs->int_code & 0xffff; current->thread.gmap_int_code = regs->int_code & 0xffff;
@ -433,12 +433,13 @@ error:
handle_fault_error_nolock(regs, 0); handle_fault_error_nolock(regs, 0);
else else
do_sigsegv(regs, SEGV_MAPERR); do_sigsegv(regs, SEGV_MAPERR);
} else if (fault & VM_FAULT_SIGBUS) { } else if (fault & (VM_FAULT_SIGBUS | VM_FAULT_HWPOISON)) {
if (!user_mode(regs)) if (!user_mode(regs))
handle_fault_error_nolock(regs, 0); handle_fault_error_nolock(regs, 0);
else else
do_sigbus(regs); do_sigbus(regs);
} else { } else {
pr_emerg("Unexpected fault flags: %08x\n", fault);
BUG(); BUG();
} }
} }
@ -492,6 +493,7 @@ void do_secure_storage_access(struct pt_regs *regs)
unsigned long addr = get_fault_address(regs); unsigned long addr = get_fault_address(regs);
struct vm_area_struct *vma; struct vm_area_struct *vma;
struct mm_struct *mm; struct mm_struct *mm;
struct folio *folio;
struct page *page; struct page *page;
struct gmap *gmap; struct gmap *gmap;
int rc; int rc;
@ -521,7 +523,7 @@ void do_secure_storage_access(struct pt_regs *regs)
switch (get_fault_type(regs)) { switch (get_fault_type(regs)) {
case GMAP_FAULT: case GMAP_FAULT:
mm = current->mm; mm = current->mm;
gmap = (struct gmap *)S390_lowcore.gmap; gmap = (struct gmap *)get_lowcore()->gmap;
mmap_read_lock(mm); mmap_read_lock(mm);
addr = __gmap_translate(gmap, addr); addr = __gmap_translate(gmap, addr);
mmap_read_unlock(mm); mmap_read_unlock(mm);
@ -539,17 +541,18 @@ void do_secure_storage_access(struct pt_regs *regs)
mmap_read_unlock(mm); mmap_read_unlock(mm);
break; break;
} }
if (arch_make_page_accessible(page)) folio = page_folio(page);
if (arch_make_folio_accessible(folio))
send_sig(SIGSEGV, current, 0); send_sig(SIGSEGV, current, 0);
put_page(page); folio_put(folio);
mmap_read_unlock(mm); mmap_read_unlock(mm);
break; break;
case KERNEL_FAULT: case KERNEL_FAULT:
page = phys_to_page(addr); folio = phys_to_folio(addr);
if (unlikely(!try_get_page(page))) if (unlikely(!folio_try_get(folio)))
break; break;
rc = arch_make_page_accessible(page); rc = arch_make_folio_accessible(folio);
put_page(page); folio_put(folio);
if (rc) if (rc)
BUG(); BUG();
break; break;
@ -561,7 +564,7 @@ NOKPROBE_SYMBOL(do_secure_storage_access);
void do_non_secure_storage_access(struct pt_regs *regs) void do_non_secure_storage_access(struct pt_regs *regs)
{ {
struct gmap *gmap = (struct gmap *)S390_lowcore.gmap; struct gmap *gmap = (struct gmap *)get_lowcore()->gmap;
unsigned long gaddr = get_fault_address(regs); unsigned long gaddr = get_fault_address(regs);
if (WARN_ON_ONCE(get_fault_type(regs) != GMAP_FAULT)) if (WARN_ON_ONCE(get_fault_type(regs) != GMAP_FAULT))
@ -573,7 +576,7 @@ NOKPROBE_SYMBOL(do_non_secure_storage_access);
void do_secure_storage_violation(struct pt_regs *regs) void do_secure_storage_violation(struct pt_regs *regs)
{ {
struct gmap *gmap = (struct gmap *)S390_lowcore.gmap; struct gmap *gmap = (struct gmap *)get_lowcore()->gmap;
unsigned long gaddr = get_fault_address(regs); unsigned long gaddr = get_fault_address(regs);
/* /*

View File

@ -287,7 +287,7 @@ EXPORT_SYMBOL_GPL(gmap_remove);
*/ */
void gmap_enable(struct gmap *gmap) void gmap_enable(struct gmap *gmap)
{ {
S390_lowcore.gmap = (unsigned long) gmap; get_lowcore()->gmap = (unsigned long)gmap;
} }
EXPORT_SYMBOL_GPL(gmap_enable); EXPORT_SYMBOL_GPL(gmap_enable);
@ -297,7 +297,7 @@ EXPORT_SYMBOL_GPL(gmap_enable);
*/ */
void gmap_disable(struct gmap *gmap) void gmap_disable(struct gmap *gmap)
{ {
S390_lowcore.gmap = 0UL; get_lowcore()->gmap = 0UL;
} }
EXPORT_SYMBOL_GPL(gmap_disable); EXPORT_SYMBOL_GPL(gmap_disable);
@ -308,7 +308,7 @@ EXPORT_SYMBOL_GPL(gmap_disable);
*/ */
struct gmap *gmap_get_enabled(void) struct gmap *gmap_get_enabled(void)
{ {
return (struct gmap *) S390_lowcore.gmap; return (struct gmap *)get_lowcore()->gmap;
} }
EXPORT_SYMBOL_GPL(gmap_get_enabled); EXPORT_SYMBOL_GPL(gmap_get_enabled);
@ -2733,7 +2733,7 @@ static int __s390_enable_skey_hugetlb(pte_t *pte, unsigned long addr,
{ {
pmd_t *pmd = (pmd_t *)pte; pmd_t *pmd = (pmd_t *)pte;
unsigned long start, end; unsigned long start, end;
struct page *page = pmd_page(*pmd); struct folio *folio = page_folio(pmd_page(*pmd));
/* /*
* The write check makes sure we do not set a key on shared * The write check makes sure we do not set a key on shared
@ -2748,7 +2748,7 @@ static int __s390_enable_skey_hugetlb(pte_t *pte, unsigned long addr,
start = pmd_val(*pmd) & HPAGE_MASK; start = pmd_val(*pmd) & HPAGE_MASK;
end = start + HPAGE_SIZE; end = start + HPAGE_SIZE;
__storage_key_init_range(start, end); __storage_key_init_range(start, end);
set_bit(PG_arch_1, &page->flags); set_bit(PG_arch_1, &folio->flags);
cond_resched(); cond_resched();
return 0; return 0;
} }
@ -2841,13 +2841,15 @@ static const struct mm_walk_ops gather_pages_ops = {
*/ */
void s390_uv_destroy_pfns(unsigned long count, unsigned long *pfns) void s390_uv_destroy_pfns(unsigned long count, unsigned long *pfns)
{ {
struct folio *folio;
unsigned long i; unsigned long i;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
folio = pfn_folio(pfns[i]);
/* we always have an extra reference */ /* we always have an extra reference */
uv_destroy_owned_page(pfn_to_phys(pfns[i])); uv_destroy_folio(folio);
/* get rid of the extra reference */ /* get rid of the extra reference */
put_page(pfn_to_page(pfns[i])); folio_put(folio);
cond_resched(); cond_resched();
} }
} }

View File

@ -121,7 +121,7 @@ static inline pte_t __rste_to_pte(unsigned long rste)
static void clear_huge_pte_skeys(struct mm_struct *mm, unsigned long rste) static void clear_huge_pte_skeys(struct mm_struct *mm, unsigned long rste)
{ {
struct page *page; struct folio *folio;
unsigned long size, paddr; unsigned long size, paddr;
if (!mm_uses_skeys(mm) || if (!mm_uses_skeys(mm) ||
@ -129,16 +129,16 @@ static void clear_huge_pte_skeys(struct mm_struct *mm, unsigned long rste)
return; return;
if ((rste & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) { if ((rste & _REGION_ENTRY_TYPE_MASK) == _REGION_ENTRY_TYPE_R3) {
page = pud_page(__pud(rste)); folio = page_folio(pud_page(__pud(rste)));
size = PUD_SIZE; size = PUD_SIZE;
paddr = rste & PUD_MASK; paddr = rste & PUD_MASK;
} else { } else {
page = pmd_page(__pmd(rste)); folio = page_folio(pmd_page(__pmd(rste)));
size = PMD_SIZE; size = PMD_SIZE;
paddr = rste & PMD_MASK; paddr = rste & PMD_MASK;
} }
if (!test_and_set_bit(PG_arch_1, &page->flags)) if (!test_and_set_bit(PG_arch_1, &folio->flags))
__storage_key_init_range(paddr, paddr + size); __storage_key_init_range(paddr, paddr + size);
} }

View File

@ -62,6 +62,7 @@ EXPORT_SYMBOL(zero_page_mask);
static void __init setup_zero_pages(void) static void __init setup_zero_pages(void)
{ {
unsigned long total_pages = PHYS_PFN(memblock_phys_mem_size() - memblock_reserved_size());
unsigned int order; unsigned int order;
struct page *page; struct page *page;
int i; int i;
@ -70,7 +71,7 @@ static void __init setup_zero_pages(void)
order = 7; order = 7;
/* Limit number of empty zero pages for small memory sizes */ /* Limit number of empty zero pages for small memory sizes */
while (order > 2 && (totalram_pages() >> 10) < (1UL << order)) while (order > 2 && (total_pages >> 10) < (1UL << order))
order--; order--;
empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order); empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order);

View File

@ -75,7 +75,7 @@ static void pgt_set(unsigned long *old, unsigned long new, unsigned long addr,
break; break;
} }
table = (unsigned long *)((unsigned long)old & mask); table = (unsigned long *)((unsigned long)old & mask);
crdte(*old, new, table, dtt, addr, S390_lowcore.kernel_asce.val); crdte(*old, new, table, dtt, addr, get_lowcore()->kernel_asce.val);
} else if (MACHINE_HAS_IDTE) { } else if (MACHINE_HAS_IDTE) {
cspg(old, *old, new); cspg(old, *old, new);
} else { } else {

View File

@ -66,8 +66,8 @@ static void __crst_table_upgrade(void *arg)
/* change all active ASCEs to avoid the creation of new TLBs */ /* change all active ASCEs to avoid the creation of new TLBs */
if (current->active_mm == mm) { if (current->active_mm == mm) {
S390_lowcore.user_asce.val = mm->context.asce; get_lowcore()->user_asce.val = mm->context.asce;
local_ctl_load(7, &S390_lowcore.user_asce); local_ctl_load(7, &get_lowcore()->user_asce);
} }
__tlb_flush_local(); __tlb_flush_local();
} }

View File

@ -1064,7 +1064,7 @@ char * __init pcibios_setup(char *str)
return NULL; return NULL;
} }
if (!strcmp(str, "nomio")) { if (!strcmp(str, "nomio")) {
S390_lowcore.machine_flags &= ~MACHINE_FLAG_PCI_MIO; get_lowcore()->machine_flags &= ~MACHINE_FLAG_PCI_MIO;
return NULL; return NULL;
} }
if (!strcmp(str, "force_floating")) { if (!strcmp(str, "force_floating")) {

View File

@ -1032,4 +1032,5 @@ MODULE_PARM_DESC(segments, "Name of DCSS segment(s) to be loaded, "
"the contiguous segments - \n" "the contiguous segments - \n"
"e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\""); "e.g. segments=\"mydcss1,mydcss2:mydcss3,mydcss4(local)\"");
MODULE_DESCRIPTION("S/390 block driver for DCSS memory");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");

View File

@ -2185,6 +2185,7 @@ con3270_init(void)
console_initcall(con3270_init); console_initcall(con3270_init);
#endif #endif
MODULE_DESCRIPTION("IBM/3270 Driver - tty functions");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR); MODULE_ALIAS_CHARDEV_MAJOR(IBM_TTY3270_MAJOR);

View File

@ -559,6 +559,7 @@ static void __exit fs3270_exit(void)
__unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270"); __unregister_chrdev(IBM_FS3270_MAJOR, 0, 1, "fs3270");
} }
MODULE_DESCRIPTION("IBM/3270 Driver - fullscreen driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR); MODULE_ALIAS_CHARDEV_MAJOR(IBM_FS3270_MAJOR);

View File

@ -1341,6 +1341,7 @@ static void raw3270_exit(void)
class_unregister(&class3270); class_unregister(&class3270);
} }
MODULE_DESCRIPTION("IBM/3270 Driver - core functions");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
module_init(raw3270_init); module_init(raw3270_init);

View File

@ -31,6 +31,9 @@
#include "sclp.h" #include "sclp.h"
#define SCLP_CMDW_ASSIGN_STORAGE 0x000d0001
#define SCLP_CMDW_UNASSIGN_STORAGE 0x000c0001
static void sclp_sync_callback(struct sclp_req *req, void *data) static void sclp_sync_callback(struct sclp_req *req, void *data)
{ {
struct completion *completion = data; struct completion *completion = data;
@ -225,7 +228,7 @@ static int sclp_assign_storage(u16 rn)
unsigned long long start; unsigned long long start;
int rc; int rc;
rc = do_assign_storage(0x000d0001, rn); rc = do_assign_storage(SCLP_CMDW_ASSIGN_STORAGE, rn);
if (rc) if (rc)
return rc; return rc;
start = rn2addr(rn); start = rn2addr(rn);
@ -235,7 +238,7 @@ static int sclp_assign_storage(u16 rn)
static int sclp_unassign_storage(u16 rn) static int sclp_unassign_storage(u16 rn)
{ {
return do_assign_storage(0x000c0001, rn); return do_assign_storage(SCLP_CMDW_UNASSIGN_STORAGE, rn);
} }
struct attach_storage_sccb { struct attach_storage_sccb {

View File

@ -60,7 +60,7 @@ static void sclp_cpu_capability_notify(struct work_struct *work)
static void __ref sclp_cpu_change_notify(struct work_struct *work) static void __ref sclp_cpu_change_notify(struct work_struct *work)
{ {
lock_device_hotplug(); lock_device_hotplug();
smp_rescan_cpus(); smp_rescan_cpus(false);
unlock_device_hotplug(); unlock_device_hotplug();
} }

View File

@ -50,9 +50,10 @@ static void __init sclp_early_facilities_detect(void)
sclp.has_aisi = !!(sccb->fac118 & 0x10); sclp.has_aisi = !!(sccb->fac118 & 0x10);
sclp.has_zpci_lsi = !!(sccb->fac118 & 0x01); sclp.has_zpci_lsi = !!(sccb->fac118 & 0x01);
if (sccb->fac85 & 0x02) if (sccb->fac85 & 0x02)
S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP; get_lowcore()->machine_flags |= MACHINE_FLAG_ESOP;
if (sccb->fac91 & 0x40) if (sccb->fac91 & 0x40)
S390_lowcore.machine_flags |= MACHINE_FLAG_TLB_GUEST; get_lowcore()->machine_flags |= MACHINE_FLAG_TLB_GUEST;
sclp.has_diag204_bif = !!(sccb->fac98 & 0x80);
if (sccb->cpuoff > 134) { if (sccb->cpuoff > 134) {
sclp.has_diag318 = !!(sccb->byte_134 & 0x80); sclp.has_diag318 = !!(sccb->byte_134 & 0x80);
sclp.has_diag320 = !!(sccb->byte_134 & 0x04); sclp.has_diag320 = !!(sccb->byte_134 & 0x04);

View File

@ -38,11 +38,11 @@ void sclp_early_wait_irq(void)
cr0_new.sssm = 1; cr0_new.sssm = 1;
local_ctl_load(0, &cr0_new.reg); local_ctl_load(0, &cr0_new.reg);
psw_ext_save = S390_lowcore.external_new_psw; psw_ext_save = get_lowcore()->external_new_psw;
psw_mask = __extract_psw(); psw_mask = __extract_psw();
S390_lowcore.external_new_psw.mask = psw_mask; get_lowcore()->external_new_psw.mask = psw_mask;
psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT; psw_wait.mask = psw_mask | PSW_MASK_EXT | PSW_MASK_WAIT;
S390_lowcore.ext_int_code = 0; get_lowcore()->ext_int_code = 0;
do { do {
asm volatile( asm volatile(
@ -53,12 +53,12 @@ void sclp_early_wait_irq(void)
"0:\n" "0:\n"
: [addr] "=&d" (addr), : [addr] "=&d" (addr),
[psw_wait_addr] "=Q" (psw_wait.addr), [psw_wait_addr] "=Q" (psw_wait.addr),
[psw_ext_addr] "=Q" (S390_lowcore.external_new_psw.addr) [psw_ext_addr] "=Q" (get_lowcore()->external_new_psw.addr)
: [psw_wait] "Q" (psw_wait) : [psw_wait] "Q" (psw_wait)
: "cc", "memory"); : "cc", "memory");
} while (S390_lowcore.ext_int_code != EXT_IRQ_SERVICE_SIG); } while (get_lowcore()->ext_int_code != EXT_IRQ_SERVICE_SIG);
S390_lowcore.external_new_psw = psw_ext_save; get_lowcore()->external_new_psw = psw_ext_save;
local_ctl_load(0, &cr0.reg); local_ctl_load(0, &cr0.reg);
} }

View File

@ -9,6 +9,7 @@
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
#include <linux/completion.h> #include <linux/completion.h>
#include <linux/jiffies.h>
#include <linux/kobject.h> #include <linux/kobject.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/printk.h> #include <linux/printk.h>
@ -28,6 +29,8 @@
#define SD_DI_CONFIG 3 #define SD_DI_CONFIG 3
#define SD_TIMEOUT msecs_to_jiffies(30000)
struct sclp_sd_evbuf { struct sclp_sd_evbuf {
struct evbuf_header hdr; struct evbuf_header hdr;
u8 eq; u8 eq;
@ -194,6 +197,10 @@ static int sclp_sd_sync(unsigned long page, u8 eq, u8 di, u64 sat, u64 sa,
struct sclp_sd_evbuf *evbuf; struct sclp_sd_evbuf *evbuf;
int rc; int rc;
if (!sclp_sd_register.sclp_send_mask ||
!sclp_sd_register.sclp_receive_mask)
return -EIO;
sclp_sd_listener_init(&listener, __pa(sccb)); sclp_sd_listener_init(&listener, __pa(sccb));
sclp_sd_listener_add(&listener); sclp_sd_listener_add(&listener);
@ -230,9 +237,12 @@ static int sclp_sd_sync(unsigned long page, u8 eq, u8 di, u64 sat, u64 sa,
goto out; goto out;
} }
if (!(evbuf->rflags & 0x80)) { if (!(evbuf->rflags & 0x80)) {
rc = wait_for_completion_interruptible(&listener.completion); rc = wait_for_completion_interruptible_timeout(&listener.completion, SD_TIMEOUT);
if (rc) if (rc == 0)
rc = -ETIME;
if (rc < 0)
goto out; goto out;
rc = 0;
evbuf = &listener.evbuf; evbuf = &listener.evbuf;
} }
switch (evbuf->status) { switch (evbuf->status) {
@ -319,9 +329,15 @@ static int sclp_sd_store_data(struct sclp_sd_data *result, u8 di)
rc = sclp_sd_sync(page, SD_EQ_STORE_DATA, di, asce, (u64) data, &dsize, rc = sclp_sd_sync(page, SD_EQ_STORE_DATA, di, asce, (u64) data, &dsize,
&esize); &esize);
if (rc) { if (rc) {
/* Cancel running request if interrupted */ /* Cancel running request if interrupted or timed out */
if (rc == -ERESTARTSYS) if (rc == -ERESTARTSYS || rc == -ETIME) {
sclp_sd_sync(page, SD_EQ_HALT, di, 0, 0, NULL, NULL); if (sclp_sd_sync(page, SD_EQ_HALT, di, 0, 0, NULL, NULL)) {
pr_warn("Could not stop Store Data request - leaking at least %zu bytes\n",
(size_t)dsize * PAGE_SIZE);
data = NULL;
asce = 0;
}
}
vfree(data); vfree(data);
goto out; goto out;
} }

View File

@ -695,7 +695,7 @@ static void qdio_int_handler_pci(struct qdio_irq *irq_ptr)
return; return;
qdio_deliver_irq(irq_ptr); qdio_deliver_irq(irq_ptr);
irq_ptr->last_data_irq_time = S390_lowcore.int_clock; irq_ptr->last_data_irq_time = get_lowcore()->int_clock;
} }
static void qdio_handle_activate_check(struct qdio_irq *irq_ptr, static void qdio_handle_activate_check(struct qdio_irq *irq_ptr,

View File

@ -99,7 +99,7 @@ static inline u32 clear_shared_ind(void)
static void tiqdio_thinint_handler(struct airq_struct *airq, static void tiqdio_thinint_handler(struct airq_struct *airq,
struct tpi_info *tpi_info) struct tpi_info *tpi_info)
{ {
u64 irq_time = S390_lowcore.int_clock; u64 irq_time = get_lowcore()->int_clock;
u32 si_used = clear_shared_ind(); u32 si_used = clear_shared_ind();
struct qdio_irq *irq; struct qdio_irq *irq;

View File

@ -169,7 +169,7 @@ TRACE_EVENT(s390_cio_tpi,
else if (addr) else if (addr)
__entry->tpi_info = *addr; __entry->tpi_info = *addr;
else else
__entry->tpi_info = S390_lowcore.tpi_info; __entry->tpi_info = get_lowcore()->tpi_info;
__entry->cssid = __entry->tpi_info.schid.cssid; __entry->cssid = __entry->tpi_info.schid.cssid;
__entry->ssid = __entry->tpi_info.schid.ssid; __entry->ssid = __entry->tpi_info.schid.ssid;
__entry->schno = __entry->tpi_info.schid.sch_no; __entry->schno = __entry->tpi_info.schid.sch_no;