Skip to content

Commit ce7d805

Browse files
chleroympe
authored andcommitted
powerpc/vdso: Prepare for switching VDSO to generic C implementation.
Prepare for switching VDSO to generic C implementation in following patch. Here, we: - Prepare the helpers to call the C VDSO functions - Prepare the required callbacks for the C VDSO functions - Prepare the clocksource.h files to define VDSO_ARCH_CLOCKMODES - Add the C trampolines to the generic C VDSO functions powerpc is a bit special for VDSO as well as system calls in the way that it requires setting CR SO bit which cannot be done in C. Therefore, entry/exit needs to be performed in ASM. Implementing __arch_get_vdso_data() would clobber the link register, requiring the caller to save it. As the ASM calling function already has to set a stack frame and saves the link register before calling the C vdso function, retriving the vdso data pointer there is lighter. Implement __arch_vdso_capable() and always return true. Provide vdso_shift_ns(), as the generic x >> s gives the following bad result: 18: 35 25 ff e0 addic. r9,r5,-32 1c: 41 80 00 10 blt 2c <shift+0x14> 20: 7c 64 4c 30 srw r4,r3,r9 24: 38 60 00 00 li r3,0 ... 2c: 54 69 08 3c rlwinm r9,r3,1,0,30 30: 21 45 00 1f subfic r10,r5,31 34: 7c 84 2c 30 srw r4,r4,r5 38: 7d 29 50 30 slw r9,r9,r10 3c: 7c 63 2c 30 srw r3,r3,r5 40: 7d 24 23 78 or r4,r9,r4 In our case the shift is always <= 32. In addition, the upper 32 bits of the result are likely nul. Lets GCC know it, it also optimises the following calculations. With the patch, we get: 0: 21 25 00 20 subfic r9,r5,32 4: 7c 69 48 30 slw r9,r3,r9 8: 7c 84 2c 30 srw r4,r4,r5 c: 7d 24 23 78 or r4,r9,r4 10: 7c 63 2c 30 srw r3,r3,r5 Signed-off-by: Christophe Leroy <[email protected]> Signed-off-by: Michael Ellerman <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 1f1676b commit ce7d805

File tree

6 files changed

+260
-0
lines changed

6 files changed

+260
-0
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_POWERPC_CLOCKSOURCE_H
3+
#define _ASM_POWERPC_CLOCKSOURCE_H
4+
5+
#include <asm/vdso/clocksource.h>
6+
7+
#endif /* _ASM_POWERPC_CLOCKSOURCE_H */

arch/powerpc/include/asm/ppc_asm.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,8 @@ GLUE(.,name):
251251

252252
#define _GLOBAL_TOC(name) _GLOBAL(name)
253253

254+
#define DOTSYM(a) a
255+
254256
#endif
255257

256258
/*
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_POWERPC_VDSO_CLOCKSOURCE_H
3+
#define _ASM_POWERPC_VDSO_CLOCKSOURCE_H
4+
5+
#define VDSO_ARCH_CLOCKMODES VDSO_CLOCKMODE_ARCHTIMER
6+
7+
#endif
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _ASM_POWERPC_VDSO_GETTIMEOFDAY_H
3+
#define _ASM_POWERPC_VDSO_GETTIMEOFDAY_H
4+
5+
#ifdef __ASSEMBLY__
6+
7+
#include <asm/ppc_asm.h>
8+
9+
/*
10+
* The macros sets two stack frames, one for the caller and one for the callee
11+
* because there are no requirement for the caller to set a stack frame when
12+
* calling VDSO so it may have omitted to set one, especially on PPC64
13+
*/
14+
15+
.macro cvdso_call funct
16+
.cfi_startproc
17+
PPC_STLU r1, -PPC_MIN_STKFRM(r1)
18+
mflr r0
19+
.cfi_register lr, r0
20+
PPC_STLU r1, -PPC_MIN_STKFRM(r1)
21+
PPC_STL r0, PPC_MIN_STKFRM + PPC_LR_STKOFF(r1)
22+
get_datapage r5, r0
23+
addi r5, r5, VDSO_DATA_OFFSET
24+
bl DOTSYM(\funct)
25+
PPC_LL r0, PPC_MIN_STKFRM + PPC_LR_STKOFF(r1)
26+
cmpwi r3, 0
27+
mtlr r0
28+
.cfi_restore lr
29+
addi r1, r1, 2 * PPC_MIN_STKFRM
30+
crclr so
31+
beqlr+
32+
crset so
33+
neg r3, r3
34+
blr
35+
.cfi_endproc
36+
.endm
37+
38+
.macro cvdso_call_time funct
39+
.cfi_startproc
40+
PPC_STLU r1, -PPC_MIN_STKFRM(r1)
41+
mflr r0
42+
.cfi_register lr, r0
43+
PPC_STLU r1, -PPC_MIN_STKFRM(r1)
44+
PPC_STL r0, PPC_MIN_STKFRM + PPC_LR_STKOFF(r1)
45+
get_datapage r4, r0
46+
addi r4, r4, VDSO_DATA_OFFSET
47+
bl DOTSYM(\funct)
48+
PPC_LL r0, PPC_MIN_STKFRM + PPC_LR_STKOFF(r1)
49+
crclr so
50+
mtlr r0
51+
.cfi_restore lr
52+
addi r1, r1, 2 * PPC_MIN_STKFRM
53+
blr
54+
.cfi_endproc
55+
.endm
56+
57+
#else
58+
59+
#include <asm/vdso/timebase.h>
60+
#include <asm/barrier.h>
61+
#include <asm/unistd.h>
62+
#include <uapi/linux/time.h>
63+
64+
#define VDSO_HAS_CLOCK_GETRES 1
65+
66+
#define VDSO_HAS_TIME 1
67+
68+
static __always_inline int do_syscall_2(const unsigned long _r0, const unsigned long _r3,
69+
const unsigned long _r4)
70+
{
71+
register long r0 asm("r0") = _r0;
72+
register unsigned long r3 asm("r3") = _r3;
73+
register unsigned long r4 asm("r4") = _r4;
74+
register int ret asm ("r3");
75+
76+
asm volatile(
77+
" sc\n"
78+
" bns+ 1f\n"
79+
" neg %0, %0\n"
80+
"1:\n"
81+
: "=r" (ret), "+r" (r4), "+r" (r0)
82+
: "r" (r3)
83+
: "memory", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "cr0", "ctr");
84+
85+
return ret;
86+
}
87+
88+
static __always_inline
89+
int gettimeofday_fallback(struct __kernel_old_timeval *_tv, struct timezone *_tz)
90+
{
91+
return do_syscall_2(__NR_gettimeofday, (unsigned long)_tv, (unsigned long)_tz);
92+
}
93+
94+
static __always_inline
95+
int clock_gettime_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
96+
{
97+
return do_syscall_2(__NR_clock_gettime, _clkid, (unsigned long)_ts);
98+
}
99+
100+
static __always_inline
101+
int clock_getres_fallback(clockid_t _clkid, struct __kernel_timespec *_ts)
102+
{
103+
return do_syscall_2(__NR_clock_getres, _clkid, (unsigned long)_ts);
104+
}
105+
106+
#ifdef CONFIG_VDSO32
107+
108+
#define BUILD_VDSO32 1
109+
110+
static __always_inline
111+
int clock_gettime32_fallback(clockid_t _clkid, struct old_timespec32 *_ts)
112+
{
113+
return do_syscall_2(__NR_clock_gettime, _clkid, (unsigned long)_ts);
114+
}
115+
116+
static __always_inline
117+
int clock_getres32_fallback(clockid_t _clkid, struct old_timespec32 *_ts)
118+
{
119+
return do_syscall_2(__NR_clock_getres, _clkid, (unsigned long)_ts);
120+
}
121+
#endif
122+
123+
static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
124+
const struct vdso_data *vd)
125+
{
126+
return get_tb();
127+
}
128+
129+
const struct vdso_data *__arch_get_vdso_data(void);
130+
131+
static inline bool vdso_clocksource_ok(const struct vdso_data *vd)
132+
{
133+
return true;
134+
}
135+
#define vdso_clocksource_ok vdso_clocksource_ok
136+
137+
/*
138+
* powerpc specific delta calculation.
139+
*
140+
* This variant removes the masking of the subtraction because the
141+
* clocksource mask of all VDSO capable clocksources on powerpc is U64_MAX
142+
* which would result in a pointless operation. The compiler cannot
143+
* optimize it away as the mask comes from the vdso data and is not compile
144+
* time constant.
145+
*/
146+
static __always_inline u64 vdso_calc_delta(u64 cycles, u64 last, u64 mask, u32 mult)
147+
{
148+
return (cycles - last) * mult;
149+
}
150+
#define vdso_calc_delta vdso_calc_delta
151+
152+
#ifndef __powerpc64__
153+
static __always_inline u64 vdso_shift_ns(u64 ns, unsigned long shift)
154+
{
155+
u32 hi = ns >> 32;
156+
u32 lo = ns;
157+
158+
lo >>= shift;
159+
lo |= hi << (32 - shift);
160+
hi >>= shift;
161+
162+
if (likely(hi == 0))
163+
return lo;
164+
165+
return ((u64)hi << 32) | lo;
166+
}
167+
#define vdso_shift_ns vdso_shift_ns
168+
#endif
169+
170+
#ifdef __powerpc64__
171+
int __c_kernel_clock_gettime(clockid_t clock, struct __kernel_timespec *ts,
172+
const struct vdso_data *vd);
173+
int __c_kernel_clock_getres(clockid_t clock_id, struct __kernel_timespec *res,
174+
const struct vdso_data *vd);
175+
#else
176+
int __c_kernel_clock_gettime(clockid_t clock, struct old_timespec32 *ts,
177+
const struct vdso_data *vd);
178+
int __c_kernel_clock_getres(clockid_t clock_id, struct old_timespec32 *res,
179+
const struct vdso_data *vd);
180+
#endif
181+
int __c_kernel_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz,
182+
const struct vdso_data *vd);
183+
__kernel_old_time_t __c_kernel_time(__kernel_old_time_t *time,
184+
const struct vdso_data *vd);
185+
#endif /* __ASSEMBLY__ */
186+
187+
#endif /* _ASM_POWERPC_VDSO_GETTIMEOFDAY_H */
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Powerpc userspace implementations of gettimeofday() and similar.
4+
*/
5+
#include <linux/types.h>
6+
7+
int __c_kernel_clock_gettime(clockid_t clock, struct old_timespec32 *ts,
8+
const struct vdso_data *vd)
9+
{
10+
return __cvdso_clock_gettime32_data(vd, clock, ts);
11+
}
12+
13+
int __c_kernel_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz,
14+
const struct vdso_data *vd)
15+
{
16+
return __cvdso_gettimeofday_data(vd, tv, tz);
17+
}
18+
19+
int __c_kernel_clock_getres(clockid_t clock_id, struct old_timespec32 *res,
20+
const struct vdso_data *vd)
21+
{
22+
return __cvdso_clock_getres_time32_data(vd, clock_id, res);
23+
}
24+
25+
__kernel_old_time_t __c_kernel_time(__kernel_old_time_t *time, const struct vdso_data *vd)
26+
{
27+
return __cvdso_time_data(vd, time);
28+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Powerpc userspace implementations of gettimeofday() and similar.
4+
*/
5+
#include <linux/time.h>
6+
#include <linux/types.h>
7+
8+
int __c_kernel_clock_gettime(clockid_t clock, struct __kernel_timespec *ts,
9+
const struct vdso_data *vd)
10+
{
11+
return __cvdso_clock_gettime_data(vd, clock, ts);
12+
}
13+
14+
int __c_kernel_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz,
15+
const struct vdso_data *vd)
16+
{
17+
return __cvdso_gettimeofday_data(vd, tv, tz);
18+
}
19+
20+
int __c_kernel_clock_getres(clockid_t clock_id, struct __kernel_timespec *res,
21+
const struct vdso_data *vd)
22+
{
23+
return __cvdso_clock_getres_data(vd, clock_id, res);
24+
}
25+
26+
__kernel_old_time_t __c_kernel_time(__kernel_old_time_t *time, const struct vdso_data *vd)
27+
{
28+
return __cvdso_time_data(vd, time);
29+
}

0 commit comments

Comments
 (0)