Skip to content

Commit 8b72f5a

Browse files
committed
s390/mm: Reimplement lazy ASCE handling
Reduce system call overhead time (round trip time for invoking a non-existent system call) by 25%. With the removal of set_fs() [1] lazy control register handling was removed in order to keep kernel entry and exit simple. However this made system calls slower. With the conversion to generic entry [2] and numerous follow up changes which simplified the entry code significantly, adding support for lazy asce handling doesn't add much complexity to the entry code anymore. In particular this means: - On kernel entry the primary asce is not modified and contains the user asce - Kernel accesses which require secondary-space mode (for example futex operations) are surrounded by enable_sacf_uaccess() and disable_sacf_uaccess() calls. enable_sacf_uaccess() sets the primary asce to kernel asce so that the sacf instruction can be used to switch to secondary-space mode. The primary asce is changed back to user asce with disable_sacf_uaccess(). The state of the control register which contains the primary asce is reflected with a new TIF_ASCE_PRIMARY bit. This is required on context switch so that the correct asce is restored for the scheduled in process. In result address spaces are now setup like this: CPU running in | %cr1 ASCE | %cr7 ASCE | %cr13 ASCE -----------------------------|-----------|-----------|----------- user space | user | user | kernel kernel (no sacf) | user | user | kernel kernel (during sacf uaccess) | kernel | user | kernel kernel (kvm guest execution) | guest | user | kernel In result cr1 control register content is not changed except for: - futex system calls - legacy s390 PCI system calls - the kvm specific cmpxchg_user_key() uaccess helper This leads to faster system call execution. [1] 87d5986 ("s390/mm: remove set_fs / rework address space handling") [2] 56e62a7 ("s390: convert to generic entry") Reviewed-by: Alexander Gordeev <agordeev@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
1 parent 8ffd015 commit 8b72f5a

12 files changed

Lines changed: 97 additions & 25 deletions

File tree

arch/s390/include/asm/asce.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
3+
#ifndef _ASM_S390_ASCE_H
4+
#define _ASM_S390_ASCE_H
5+
6+
#include <linux/thread_info.h>
7+
#include <linux/irqflags.h>
8+
#include <asm/lowcore.h>
9+
#include <asm/ctlreg.h>
10+
11+
static inline bool enable_sacf_uaccess(void)
12+
{
13+
unsigned long flags;
14+
15+
if (test_thread_flag(TIF_ASCE_PRIMARY))
16+
return true;
17+
local_irq_save(flags);
18+
local_ctl_load(1, &get_lowcore()->kernel_asce);
19+
set_thread_flag(TIF_ASCE_PRIMARY);
20+
local_irq_restore(flags);
21+
return false;
22+
}
23+
24+
static inline void disable_sacf_uaccess(bool previous)
25+
{
26+
unsigned long flags;
27+
28+
if (previous)
29+
return;
30+
local_irq_save(flags);
31+
local_ctl_load(1, &get_lowcore()->user_asce);
32+
clear_thread_flag(TIF_ASCE_PRIMARY);
33+
local_irq_restore(flags);
34+
}
35+
36+
#endif /* _ASM_S390_ASCE_H */

arch/s390/include/asm/futex.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
static uaccess_kmsan_or_inline int \
1414
__futex_atomic_##name(int oparg, int *old, u32 __user *uaddr) \
1515
{ \
16+
bool sacf_flag; \
1617
int rc, new; \
1718
\
1819
instrument_copy_from_user_before(old, uaddr, sizeof(*old)); \
20+
sacf_flag = enable_sacf_uaccess(); \
1921
asm_inline volatile( \
2022
" sacf 256\n" \
2123
"0: l %[old],%[uaddr]\n" \
@@ -32,6 +34,7 @@ __futex_atomic_##name(int oparg, int *old, u32 __user *uaddr) \
3234
[new] "=&d" (new), [uaddr] "+Q" (*uaddr) \
3335
: [oparg] "d" (oparg) \
3436
: "cc"); \
37+
disable_sacf_uaccess(sacf_flag); \
3538
if (!rc) \
3639
instrument_copy_from_user_after(old, uaddr, sizeof(*old), 0); \
3740
return rc; \
@@ -75,9 +78,11 @@ int arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
7578
static uaccess_kmsan_or_inline
7679
int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval)
7780
{
81+
bool sacf_flag;
7882
int rc;
7983

8084
instrument_copy_from_user_before(uval, uaddr, sizeof(*uval));
85+
sacf_flag = enable_sacf_uaccess();
8186
asm_inline volatile(
8287
" sacf 256\n"
8388
"0: cs %[old],%[new],%[uaddr]\n"
@@ -88,6 +93,7 @@ int futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32
8893
: [rc] "=d" (rc), [old] "+d" (oldval), [uaddr] "+Q" (*uaddr)
8994
: [new] "d" (newval)
9095
: "cc", "memory");
96+
disable_sacf_uaccess(sacf_flag);
9197
*uval = oldval;
9298
instrument_copy_from_user_after(uval, uaddr, sizeof(*uval), 0);
9399
return rc;

arch/s390/include/asm/mmu_context.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <linux/mm_types.h>
1414
#include <asm/tlbflush.h>
1515
#include <asm/ctlreg.h>
16+
#include <asm/asce.h>
1617
#include <asm-generic/mm_hooks.h>
1718

1819
#define init_new_context init_new_context
@@ -77,7 +78,8 @@ static inline void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *
7778
else
7879
get_lowcore()->user_asce.val = next->context.asce;
7980
cpumask_set_cpu(cpu, &next->context.cpu_attach_mask);
80-
/* Clear previous user-ASCE from CR7 */
81+
/* Clear previous user-ASCE from CR1 and CR7 */
82+
local_ctl_load(1, &s390_invalid_asce);
8183
local_ctl_load(7, &s390_invalid_asce);
8284
if (prev != next)
8385
cpumask_clear_cpu(cpu, &prev->context.cpu_attach_mask);
@@ -99,6 +101,7 @@ static inline void finish_arch_post_lock_switch(void)
99101
{
100102
struct task_struct *tsk = current;
101103
struct mm_struct *mm = tsk->mm;
104+
unsigned long flags;
102105

103106
if (mm) {
104107
preempt_disable();
@@ -108,16 +111,30 @@ static inline void finish_arch_post_lock_switch(void)
108111
__tlb_flush_mm_lazy(mm);
109112
preempt_enable();
110113
}
114+
local_irq_save(flags);
115+
if (test_thread_flag(TIF_ASCE_PRIMARY))
116+
local_ctl_load(1, &get_lowcore()->kernel_asce);
117+
else
118+
local_ctl_load(1, &get_lowcore()->user_asce);
111119
local_ctl_load(7, &get_lowcore()->user_asce);
120+
local_irq_restore(flags);
112121
}
113122

114123
#define activate_mm activate_mm
115124
static inline void activate_mm(struct mm_struct *prev,
116125
struct mm_struct *next)
117126
{
127+
unsigned long flags;
128+
118129
switch_mm(prev, next, current);
119130
cpumask_set_cpu(smp_processor_id(), mm_cpumask(next));
131+
local_irq_save(flags);
132+
if (test_thread_flag(TIF_ASCE_PRIMARY))
133+
local_ctl_load(1, &get_lowcore()->kernel_asce);
134+
else
135+
local_ctl_load(1, &get_lowcore()->user_asce);
120136
local_ctl_load(7, &get_lowcore()->user_asce);
137+
local_irq_restore(flags);
121138
}
122139

123140
#include <asm-generic/mmu_context.h>

arch/s390/include/asm/ptrace.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ struct pt_regs {
126126
struct tpi_info tpi_info;
127127
};
128128
unsigned long flags;
129-
unsigned long cr1;
130129
unsigned long last_break;
131130
};
132131

arch/s390/include/asm/thread_info.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ void arch_setup_new_exec(void);
6464
#define TIF_NEED_RESCHED_LAZY 3 /* lazy rescheduling needed */
6565
#define TIF_UPROBE 4 /* breakpointed or single-stepping */
6666
#define TIF_PATCH_PENDING 5 /* pending live patching update */
67+
#define TIF_ASCE_PRIMARY 6 /* primary asce is kernel asce */
6768
#define TIF_NOTIFY_SIGNAL 7 /* signal notifications exist */
6869
#define TIF_GUARDED_STORAGE 8 /* load guarded storage control block */
6970
#define TIF_ISOLATE_BP_GUEST 9 /* Run KVM guests with isolated BP */
@@ -85,6 +86,7 @@ void arch_setup_new_exec(void);
8586
#define _TIF_NEED_RESCHED_LAZY BIT(TIF_NEED_RESCHED_LAZY)
8687
#define _TIF_UPROBE BIT(TIF_UPROBE)
8788
#define _TIF_PATCH_PENDING BIT(TIF_PATCH_PENDING)
89+
#define _TIF_ASCE_PRIMARY BIT(TIF_ASCE_PRIMARY)
8890
#define _TIF_NOTIFY_SIGNAL BIT(TIF_NOTIFY_SIGNAL)
8991
#define _TIF_GUARDED_STORAGE BIT(TIF_GUARDED_STORAGE)
9092
#define _TIF_ISOLATE_BP_GUEST BIT(TIF_ISOLATE_BP_GUEST)

arch/s390/include/asm/uaccess.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <asm/extable.h>
2020
#include <asm/facility.h>
2121
#include <asm-generic/access_ok.h>
22+
#include <asm/asce.h>
2223
#include <linux/instrumented.h>
2324

2425
void debug_user_asce(int exit);
@@ -478,6 +479,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
478479
__uint128_t old, __uint128_t new,
479480
unsigned long key, int size)
480481
{
482+
bool sacf_flag;
481483
int rc = 0;
482484

483485
switch (size) {
@@ -490,6 +492,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
490492
_old = ((unsigned int)old & 0xff) << shift;
491493
_new = ((unsigned int)new & 0xff) << shift;
492494
mask = ~(0xff << shift);
495+
sacf_flag = enable_sacf_uaccess();
493496
asm_inline volatile(
494497
" spka 0(%[key])\n"
495498
" sacf 256\n"
@@ -524,6 +527,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
524527
[default_key] "J" (PAGE_DEFAULT_KEY),
525528
[max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS)
526529
: "memory", "cc");
530+
disable_sacf_uaccess(sacf_flag);
527531
*(unsigned char *)uval = prev >> shift;
528532
if (!count)
529533
rc = -EAGAIN;
@@ -538,6 +542,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
538542
_old = ((unsigned int)old & 0xffff) << shift;
539543
_new = ((unsigned int)new & 0xffff) << shift;
540544
mask = ~(0xffff << shift);
545+
sacf_flag = enable_sacf_uaccess();
541546
asm_inline volatile(
542547
" spka 0(%[key])\n"
543548
" sacf 256\n"
@@ -572,6 +577,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
572577
[default_key] "J" (PAGE_DEFAULT_KEY),
573578
[max_loops] "J" (CMPXCHG_USER_KEY_MAX_LOOPS)
574579
: "memory", "cc");
580+
disable_sacf_uaccess(sacf_flag);
575581
*(unsigned short *)uval = prev >> shift;
576582
if (!count)
577583
rc = -EAGAIN;
@@ -580,6 +586,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
580586
case 4: {
581587
unsigned int prev = old;
582588

589+
sacf_flag = enable_sacf_uaccess();
583590
asm_inline volatile(
584591
" spka 0(%[key])\n"
585592
" sacf 256\n"
@@ -595,12 +602,14 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
595602
[key] "a" (key << 4),
596603
[default_key] "J" (PAGE_DEFAULT_KEY)
597604
: "memory", "cc");
605+
disable_sacf_uaccess(sacf_flag);
598606
*(unsigned int *)uval = prev;
599607
return rc;
600608
}
601609
case 8: {
602610
unsigned long prev = old;
603611

612+
sacf_flag = enable_sacf_uaccess();
604613
asm_inline volatile(
605614
" spka 0(%[key])\n"
606615
" sacf 256\n"
@@ -616,12 +625,14 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
616625
[key] "a" (key << 4),
617626
[default_key] "J" (PAGE_DEFAULT_KEY)
618627
: "memory", "cc");
628+
disable_sacf_uaccess(sacf_flag);
619629
*(unsigned long *)uval = prev;
620630
return rc;
621631
}
622632
case 16: {
623633
__uint128_t prev = old;
624634

635+
sacf_flag = enable_sacf_uaccess();
625636
asm_inline volatile(
626637
" spka 0(%[key])\n"
627638
" sacf 256\n"
@@ -637,6 +648,7 @@ static __always_inline int __cmpxchg_user_key(unsigned long address, void *uval,
637648
[key] "a" (key << 4),
638649
[default_key] "J" (PAGE_DEFAULT_KEY)
639650
: "memory", "cc");
651+
disable_sacf_uaccess(sacf_flag);
640652
*(__uint128_t *)uval = prev;
641653
return rc;
642654
}

arch/s390/kernel/asm-offsets.c

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ int main(void)
5050
OFFSET(__PT_ORIG_GPR2, pt_regs, orig_gpr2);
5151
OFFSET(__PT_INT_CODE, pt_regs, int_code);
5252
OFFSET(__PT_FLAGS, pt_regs, flags);
53-
OFFSET(__PT_CR1, pt_regs, cr1);
5453
OFFSET(__PT_LAST_BREAK, pt_regs, last_break);
5554
DEFINE(__PT_SIZE, sizeof(struct pt_regs));
5655
BLANK();

arch/s390/kernel/entry.S

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ _LPP_OFFSET = __LC_LPP
116116
.macro SIEEXIT sie_control,lowcore
117117
lg %r9,\sie_control # get control block pointer
118118
ni __SIE_PROG0C+3(%r9),0xfe # no longer in SIE
119-
lctlg %c1,%c1,__LC_KERNEL_ASCE(\lowcore) # load primary asce
119+
lctlg %c1,%c1,__LC_USER_ASCE(\lowcore) # load primary asce
120120
lg %r9,__LC_CURRENT(\lowcore)
121121
mvi __TI_sie(%r9),0
122122
larl %r9,sie_exit # skip forward to sie_exit
@@ -208,7 +208,7 @@ SYM_FUNC_START(__sie64a)
208208
lg %r14,__SF_SIE_CONTROL(%r15) # get control block pointer
209209
ni __SIE_PROG0C+3(%r14),0xfe # no longer in SIE
210210
GET_LC %r14
211-
lctlg %c1,%c1,__LC_KERNEL_ASCE(%r14) # load primary asce
211+
lctlg %c1,%c1,__LC_USER_ASCE(%r14) # load primary asce
212212
lg %r14,__LC_CURRENT(%r14)
213213
mvi __TI_sie(%r14),0
214214
SYM_INNER_LABEL(sie_exit, SYM_L_GLOBAL)
@@ -240,7 +240,6 @@ SYM_CODE_START(system_call)
240240
lghi %r14,0
241241
.Lsysc_per:
242242
STBEAR __LC_LAST_BREAK(%r13)
243-
lctlg %c1,%c1,__LC_KERNEL_ASCE(%r13)
244243
lg %r15,__LC_KERNEL_STACK(%r13)
245244
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
246245
stmg %r0,%r7,STACK_FRAME_OVERHEAD+__PT_R0(%r15)
@@ -261,7 +260,6 @@ SYM_CODE_START(system_call)
261260
lgr %r3,%r14
262261
brasl %r14,__do_syscall
263262
STACKLEAK_ERASE
264-
lctlg %c1,%c1,__LC_USER_ASCE(%r13)
265263
mvc __LC_RETURN_PSW(16,%r13),STACK_FRAME_OVERHEAD+__PT_PSW(%r15)
266264
BPON
267265
LBEAR STACK_FRAME_OVERHEAD+__PT_LAST_BREAK(%r15)
@@ -278,7 +276,6 @@ SYM_CODE_START(ret_from_fork)
278276
brasl %r14,__ret_from_fork
279277
STACKLEAK_ERASE
280278
GET_LC %r13
281-
lctlg %c1,%c1,__LC_USER_ASCE(%r13)
282279
mvc __LC_RETURN_PSW(16,%r13),STACK_FRAME_OVERHEAD+__PT_PSW(%r15)
283280
BPON
284281
LBEAR STACK_FRAME_OVERHEAD+__PT_LAST_BREAK(%r15)
@@ -299,10 +296,7 @@ SYM_CODE_START(pgm_check_handler)
299296
lmg %r8,%r9,__LC_PGM_OLD_PSW(%r13)
300297
xgr %r10,%r10
301298
tmhh %r8,0x0001 # coming from user space?
302-
jno .Lpgm_skip_asce
303-
lctlg %c1,%c1,__LC_KERNEL_ASCE(%r13)
304-
j 3f # -> fault in user space
305-
.Lpgm_skip_asce:
299+
jo 3f # -> fault in user space
306300
#if IS_ENABLED(CONFIG_KVM)
307301
lg %r11,__LC_CURRENT(%r13)
308302
tm __TI_sie(%r11),0xff
@@ -340,7 +334,6 @@ SYM_CODE_START(pgm_check_handler)
340334
tmhh %r8,0x0001 # returning to user space?
341335
jno .Lpgm_exit_kernel
342336
STACKLEAK_ERASE
343-
lctlg %c1,%c1,__LC_USER_ASCE(%r13)
344337
BPON
345338
stpt __LC_EXIT_TIMER(%r13)
346339
.Lpgm_exit_kernel:
@@ -384,8 +377,7 @@ SYM_CODE_START(\name)
384377
#endif
385378
0: aghi %r15,-(STACK_FRAME_OVERHEAD + __PT_SIZE)
386379
j 2f
387-
1: lctlg %c1,%c1,__LC_KERNEL_ASCE(%r13)
388-
lg %r15,__LC_KERNEL_STACK(%r13)
380+
1: lg %r15,__LC_KERNEL_STACK(%r13)
389381
2: xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
390382
la %r11,STACK_FRAME_OVERHEAD(%r15)
391383
stmg %r0,%r7,__PT_R0(%r11)
@@ -408,7 +400,6 @@ SYM_CODE_START(\name)
408400
tmhh %r8,0x0001 # returning to user ?
409401
jno 2f
410402
STACKLEAK_ERASE
411-
lctlg %c1,%c1,__LC_USER_ASCE(%r13)
412403
BPON
413404
stpt __LC_EXIT_TIMER(%r13)
414405
2: LBEAR __PT_LAST_BREAK(%r11)
@@ -476,8 +467,6 @@ SYM_CODE_START(mcck_int_handler)
476467
.Lmcck_user:
477468
lg %r15,__LC_MCCK_STACK(%r13)
478469
la %r11,STACK_FRAME_OVERHEAD(%r15)
479-
stctg %c1,%c1,__PT_CR1(%r11)
480-
lctlg %c1,%c1,__LC_KERNEL_ASCE(%r13)
481470
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
482471
lay %r14,__LC_GPREGS_SAVE_AREA(%r13)
483472
mvc __PT_R0(128,%r11),0(%r14)
@@ -495,7 +484,6 @@ SYM_CODE_START(mcck_int_handler)
495484
xc __SF_BACKCHAIN(8,%r15),__SF_BACKCHAIN(%r15)
496485
lgr %r2,%r11 # pass pointer to pt_regs
497486
brasl %r14,s390_do_machine_check
498-
lctlg %c1,%c1,__PT_CR1(%r11)
499487
lmg %r0,%r10,__PT_R0(%r11)
500488
mvc __LC_RETURN_MCCK_PSW(16,%r13),__PT_PSW(%r11) # move return PSW
501489
tm __LC_RETURN_MCCK_PSW+1(%r13),0x01 # returning to user ?

arch/s390/kernel/smp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ static void pcpu_prepare_secondary(struct pcpu *pcpu, int cpu)
263263
abs_lc = get_abs_lowcore();
264264
memcpy(lc->cregs_save_area, abs_lc->cregs_save_area, sizeof(lc->cregs_save_area));
265265
put_abs_lowcore(abs_lc);
266-
lc->cregs_save_area[1] = lc->kernel_asce;
266+
lc->cregs_save_area[1] = lc->user_asce;
267267
lc->cregs_save_area[7] = lc->user_asce;
268268
save_access_regs((unsigned int *) lc->access_regs_save_area);
269269
arch_spin_lock_setup(cpu);

arch/s390/lib/uaccess.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,18 @@
1717
#ifdef CONFIG_DEBUG_ENTRY
1818
void debug_user_asce(int exit)
1919
{
20+
struct lowcore *lc = get_lowcore();
2021
struct ctlreg cr1, cr7;
2122

2223
local_ctl_store(1, &cr1);
2324
local_ctl_store(7, &cr7);
24-
if (cr1.val == get_lowcore()->kernel_asce.val && cr7.val == get_lowcore()->user_asce.val)
25+
if (cr1.val == lc->user_asce.val && cr7.val == lc->user_asce.val)
2526
return;
2627
panic("incorrect ASCE on kernel %s\n"
2728
"cr1: %016lx cr7: %016lx\n"
2829
"kernel: %016lx user: %016lx\n",
2930
exit ? "exit" : "entry", cr1.val, cr7.val,
30-
get_lowcore()->kernel_asce.val, get_lowcore()->user_asce.val);
31+
lc->kernel_asce.val, lc->user_asce.val);
3132
}
3233
#endif /*CONFIG_DEBUG_ENTRY */
3334

0 commit comments

Comments
 (0)