Skip to content

Commit 9a08862

Browse files
Nagarathnam Muthusamydavem330
authored andcommitted
vDSO for sparc
Following patch is based on work done by Nick Alcock on 64-bit vDSO for sparc in Oracle linux. I have extended it to include support for 32-bit vDSO for sparc on 64-bit kernel. vDSO for sparc is based on the X86 implementation. This patch provides vDSO support for both 64-bit and 32-bit programs on 64-bit kernel. vDSO will be disabled on 32-bit linux kernel on sparc. *) vclock_gettime.c contains all the vdso functions. Since data page is mapped before the vdso code page, the pointer to data page is got by subracting offset from an address in the vdso code page. The return address stored in %i7 is used for this purpose. *) During compilation, both 32-bit and 64-bit vdso images are compiled and are converted into raw bytes by vdso2c program to be ready for mapping into the process. 32-bit images are compiled only if CONFIG_COMPAT is enabled. vdso2c generates two files vdso-image-64.c and vdso-image-32.c which contains the respective vDSO image in C structure. *) During vdso initialization, required number of vdso pages are allocated and raw bytes are copied into the pages. *) During every exec, these pages are mapped into the process through arch_setup_additional_pages and the location of mapping is passed on to the process through aux vector AT_SYSINFO_EHDR which is used by glibc. *) A new update_vsyscall routine for sparc is added to keep the data page in vdso updated. *) As vDSO cannot contain dynamically relocatable references, a new version of cpu_relax is added for the use of vDSO. This change also requires a putback to glibc to use vDSO. For testing, programs planning to try vDSO can be compiled against the generated vdso(64/32).so in the source. Testing: ======== [root@localhost ~]# cat vdso_test.c int main() { struct timespec tv_start, tv_end; struct timeval tv_tmp; int i; int count = 1 * 1000 * 10000; long long diff; clock_gettime(0, &tv_start); for (i = 0; i < count; i++) gettimeofday(&tv_tmp, NULL); clock_gettime(0, &tv_end); diff = (long long)(tv_end.tv_sec - tv_start.tv_sec)*(1*1000*1000*1000); diff += (tv_end.tv_nsec - tv_start.tv_nsec); printf("Start sec: %d\n", tv_start.tv_sec); printf("End sec : %d\n", tv_end.tv_sec); printf("%d cycles in %lld ns = %f ns/cycle\n", count, diff, (double)diff / (double)count); return 0; } [root@localhost ~]# cc vdso_test.c -o t32_without_fix -m32 -lrt [root@localhost ~]# ./t32_without_fix Start sec: 1502396130 End sec : 1502396140 10000000 cycles in 9565148528 ns = 956.514853 ns/cycle [root@localhost ~]# cc vdso_test.c -o t32_with_fix -m32 ./vdso32.so.dbg [root@localhost ~]# ./t32_with_fix Start sec: 1502396168 End sec : 1502396169 10000000 cycles in 798141262 ns = 79.814126 ns/cycle [root@localhost ~]# cc vdso_test.c -o t64_without_fix -m64 -lrt [root@localhost ~]# ./t64_without_fix Start sec: 1502396208 End sec : 1502396218 10000000 cycles in 9846091800 ns = 984.609180 ns/cycle [root@localhost ~]# cc vdso_test.c -o t64_with_fix -m64 ./vdso64.so.dbg [root@localhost ~]# ./t64_with_fix Start sec: 1502396257 End sec : 1502396257 10000000 cycles in 380984048 ns = 38.098405 ns/cycle V1 to V2 Changes: ================= Added hot patching code to switch the read stick instruction to read tick instruction based on the hardware. V2 to V3 Changes: ================= Merged latest changes from sparc-next and moved the initialization of clocksource_tick.archdata.vclock_mode to time_init_early. Disabled queued spinlock and rwlock configuration when simulating 32-bit config to compile 32-bit VDSO. V3 to V4 Changes: ================= Hardcoded the page size as 8192 in linker script for both 64-bit and 32-bit binaries. Removed unused variables in vdso2c.h. Added -mv8plus flag to Makefile to prevent the generation of relocation entries for __lshrdi3 in 32-bit vdso binary. Signed-off-by: Nick Alcock <nick.alcock@oracle.com> Signed-off-by: Nagarathnam Muthusamy <nagarathnam.muthusamy@oracle.com> Reviewed-by: Shannon Nelson <shannon.nelson@oracle.com> Signed-off-by: David S. Miller <davem@davemloft.net>
1 parent 23198dd commit 9a08862

26 files changed

+1494
-2
lines changed

arch/sparc/Kbuild

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ obj-y += mm/
77
obj-y += math-emu/
88
obj-y += net/
99
obj-y += crypto/
10+
obj-$(CONFIG_SPARC64) += vdso/

arch/sparc/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ config SPARC64
8585
select HAVE_REGS_AND_STACK_ACCESS_API
8686
select ARCH_USE_QUEUED_RWLOCKS
8787
select ARCH_USE_QUEUED_SPINLOCKS
88+
select GENERIC_TIME_VSYSCALL
89+
select ARCH_CLOCKSOURCE_DATA
8890

8991
config ARCH_DEFCONFIG
9092
string

arch/sparc/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ install:
8080
archclean:
8181
$(Q)$(MAKE) $(clean)=$(boot)
8282

83+
PHONY += vdso_install
84+
vdso_install:
85+
$(Q)$(MAKE) $(build)=arch/sparc/vdso $@
86+
8387
# This is the image used for packaging
8488
KBUILD_IMAGE := $(boot)/zImage
8589

arch/sparc/include/asm/clocksource.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/*
2+
* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
3+
*/
4+
5+
#ifndef _ASM_SPARC_CLOCKSOURCE_H
6+
#define _ASM_SPARC_CLOCKSOURCE_H
7+
8+
/* VDSO clocksources */
9+
#define VCLOCK_NONE 0 /* Nothing userspace can do. */
10+
#define VCLOCK_TICK 1 /* Use %tick. */
11+
#define VCLOCK_STICK 2 /* Use %stick. */
12+
13+
struct arch_clocksource_data {
14+
int vclock_mode;
15+
};
16+
17+
#endif /* _ASM_SPARC_CLOCKSOURCE_H */

arch/sparc/include/asm/elf_64.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,4 +210,18 @@ do { if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \
210210
(current->personality & (~PER_MASK))); \
211211
} while (0)
212212

213+
extern unsigned int vdso_enabled;
214+
215+
#define ARCH_DLINFO \
216+
do { \
217+
if (vdso_enabled) \
218+
NEW_AUX_ENT(AT_SYSINFO_EHDR, \
219+
(unsigned long)current->mm->context.vdso); \
220+
} while (0)
221+
222+
struct linux_binprm;
223+
224+
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
225+
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
226+
int uses_interp);
213227
#endif /* !(__ASM_SPARC64_ELF_H) */

arch/sparc/include/asm/mmu_64.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ typedef struct {
9696
unsigned long thp_pte_count;
9797
struct tsb_config tsb_block[MM_NUM_TSBS];
9898
struct hv_tsb_descr tsb_descr[MM_NUM_TSBS];
99+
void *vdso;
99100
} mm_context_t;
100101

101102
#endif /* !__ASSEMBLY__ */

arch/sparc/include/asm/processor_64.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,13 @@ unsigned long get_wchan(struct task_struct *task);
199199
* To make a long story short, we are trying to yield the current cpu
200200
* strand during busy loops.
201201
*/
202+
#ifdef BUILD_VDSO
203+
#define cpu_relax() asm volatile("\n99:\n\t" \
204+
"rd %%ccr, %%g0\n\t" \
205+
"rd %%ccr, %%g0\n\t" \
206+
"rd %%ccr, %%g0\n\t" \
207+
::: "memory")
208+
#else /* ! BUILD_VDSO */
202209
#define cpu_relax() asm volatile("\n99:\n\t" \
203210
"rd %%ccr, %%g0\n\t" \
204211
"rd %%ccr, %%g0\n\t" \
@@ -210,6 +217,7 @@ unsigned long get_wchan(struct task_struct *task);
210217
"nop\n\t" \
211218
".previous" \
212219
::: "memory")
220+
#endif
213221

214222
/* Prefetch support. This is tuned for UltraSPARC-III and later.
215223
* UltraSPARC-I will treat these as nops, and UltraSPARC-II has

arch/sparc/include/asm/vdso.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
3+
*/
4+
5+
#ifndef _ASM_SPARC_VDSO_H
6+
#define _ASM_SPARC_VDSO_H
7+
8+
struct vdso_image {
9+
void *data;
10+
unsigned long size; /* Always a multiple of PAGE_SIZE */
11+
long sym_vvar_start; /* Negative offset to the vvar area */
12+
long sym_vread_tick; /* Start of vread_tick section */
13+
long sym_vread_tick_patch_start; /* Start of tick read */
14+
long sym_vread_tick_patch_end; /* End of tick read */
15+
};
16+
17+
#ifdef CONFIG_SPARC64
18+
extern const struct vdso_image vdso_image_64_builtin;
19+
#endif
20+
#ifdef CONFIG_COMPAT
21+
extern const struct vdso_image vdso_image_32_builtin;
22+
#endif
23+
24+
#endif /* _ASM_SPARC_VDSO_H */

arch/sparc/include/asm/vvar.h

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
3+
*/
4+
5+
#ifndef _ASM_SPARC_VVAR_DATA_H
6+
#define _ASM_SPARC_VVAR_DATA_H
7+
8+
#include <asm/clocksource.h>
9+
#include <linux/seqlock.h>
10+
#include <linux/time.h>
11+
#include <linux/types.h>
12+
13+
struct vvar_data {
14+
unsigned int seq;
15+
16+
int vclock_mode;
17+
struct { /* extract of a clocksource struct */
18+
u64 cycle_last;
19+
u64 mask;
20+
int mult;
21+
int shift;
22+
} clock;
23+
/* open coded 'struct timespec' */
24+
u64 wall_time_sec;
25+
u64 wall_time_snsec;
26+
u64 monotonic_time_snsec;
27+
u64 monotonic_time_sec;
28+
u64 monotonic_time_coarse_sec;
29+
u64 monotonic_time_coarse_nsec;
30+
u64 wall_time_coarse_sec;
31+
u64 wall_time_coarse_nsec;
32+
33+
int tz_minuteswest;
34+
int tz_dsttime;
35+
};
36+
37+
extern struct vvar_data *vvar_data;
38+
extern int vdso_fix_stick;
39+
40+
static inline unsigned int vvar_read_begin(const struct vvar_data *s)
41+
{
42+
unsigned int ret;
43+
44+
repeat:
45+
ret = READ_ONCE(s->seq);
46+
if (unlikely(ret & 1)) {
47+
cpu_relax();
48+
goto repeat;
49+
}
50+
smp_rmb(); /* Finish all reads before we return seq */
51+
return ret;
52+
}
53+
54+
static inline int vvar_read_retry(const struct vvar_data *s,
55+
unsigned int start)
56+
{
57+
smp_rmb(); /* Finish all reads before checking the value of seq */
58+
return unlikely(s->seq != start);
59+
}
60+
61+
static inline void vvar_write_begin(struct vvar_data *s)
62+
{
63+
++s->seq;
64+
smp_wmb(); /* Makes sure that increment of seq is reflected */
65+
}
66+
67+
static inline void vvar_write_end(struct vvar_data *s)
68+
{
69+
smp_wmb(); /* Makes the value of seq current before we increment */
70+
++s->seq;
71+
}
72+
73+
74+
#endif /* _ASM_SPARC_VVAR_DATA_H */

arch/sparc/include/uapi/asm/auxvec.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
#ifndef __ASMSPARC_AUXVEC_H
22
#define __ASMSPARC_AUXVEC_H
33

4+
#define AT_SYSINFO_EHDR 33
5+
6+
#define AT_VECTOR_SIZE_ARCH 1
7+
48
#endif /* !(__ASMSPARC_AUXVEC_H) */

arch/sparc/kernel/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ obj-$(CONFIG_SPARC32) += systbls_32.o
4242
obj-y += time_$(BITS).o
4343
obj-$(CONFIG_SPARC32) += windows.o
4444
obj-y += cpu.o
45+
obj-$(CONFIG_SPARC64) += vdso.o
4546
obj-$(CONFIG_SPARC32) += devices.o
4647
obj-y += ptrace_$(BITS).o
4748
obj-y += unaligned_$(BITS).o

arch/sparc/kernel/time_64.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@
5252

5353
DEFINE_SPINLOCK(rtc_lock);
5454

55+
unsigned int __read_mostly vdso_fix_stick;
56+
5557
#ifdef CONFIG_SMP
5658
unsigned long profile_pc(struct pt_regs *regs)
5759
{
@@ -829,12 +831,17 @@ static void init_tick_ops(struct sparc64_tick_ops *ops)
829831
void __init time_init_early(void)
830832
{
831833
if (tlb_type == spitfire) {
832-
if (is_hummingbird())
834+
if (is_hummingbird()) {
833835
init_tick_ops(&hbtick_operations);
834-
else
836+
clocksource_tick.archdata.vclock_mode = VCLOCK_NONE;
837+
} else {
835838
init_tick_ops(&tick_operations);
839+
clocksource_tick.archdata.vclock_mode = VCLOCK_TICK;
840+
vdso_fix_stick = 1;
841+
}
836842
} else {
837843
init_tick_ops(&stick_operations);
844+
clocksource_tick.archdata.vclock_mode = VCLOCK_STICK;
838845
}
839846
}
840847

arch/sparc/kernel/vdso.c

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE
3+
* Copyright 2003 Andi Kleen, SuSE Labs.
4+
*
5+
* Thanks to hpa@transmeta.com for some useful hint.
6+
* Special thanks to Ingo Molnar for his early experience with
7+
* a different vsyscall implementation for Linux/IA32 and for the name.
8+
*/
9+
10+
#include <linux/seqlock.h>
11+
#include <linux/time.h>
12+
#include <linux/timekeeper_internal.h>
13+
14+
#include <asm/vvar.h>
15+
16+
void update_vsyscall_tz(void)
17+
{
18+
if (unlikely(vvar_data == NULL))
19+
return;
20+
21+
vvar_data->tz_minuteswest = sys_tz.tz_minuteswest;
22+
vvar_data->tz_dsttime = sys_tz.tz_dsttime;
23+
}
24+
25+
void update_vsyscall(struct timekeeper *tk)
26+
{
27+
struct vvar_data *vdata = vvar_data;
28+
29+
if (unlikely(vdata == NULL))
30+
return;
31+
32+
vvar_write_begin(vdata);
33+
vdata->vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode;
34+
vdata->clock.cycle_last = tk->tkr_mono.cycle_last;
35+
vdata->clock.mask = tk->tkr_mono.mask;
36+
vdata->clock.mult = tk->tkr_mono.mult;
37+
vdata->clock.shift = tk->tkr_mono.shift;
38+
39+
vdata->wall_time_sec = tk->xtime_sec;
40+
vdata->wall_time_snsec = tk->tkr_mono.xtime_nsec;
41+
42+
vdata->monotonic_time_sec = tk->xtime_sec +
43+
tk->wall_to_monotonic.tv_sec;
44+
vdata->monotonic_time_snsec = tk->tkr_mono.xtime_nsec +
45+
(tk->wall_to_monotonic.tv_nsec <<
46+
tk->tkr_mono.shift);
47+
48+
while (vdata->monotonic_time_snsec >=
49+
(((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
50+
vdata->monotonic_time_snsec -=
51+
((u64)NSEC_PER_SEC) << tk->tkr_mono.shift;
52+
vdata->monotonic_time_sec++;
53+
}
54+
55+
vdata->wall_time_coarse_sec = tk->xtime_sec;
56+
vdata->wall_time_coarse_nsec =
57+
(long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);
58+
59+
vdata->monotonic_time_coarse_sec =
60+
vdata->wall_time_coarse_sec + tk->wall_to_monotonic.tv_sec;
61+
vdata->monotonic_time_coarse_nsec =
62+
vdata->wall_time_coarse_nsec + tk->wall_to_monotonic.tv_nsec;
63+
64+
while (vdata->monotonic_time_coarse_nsec >= NSEC_PER_SEC) {
65+
vdata->monotonic_time_coarse_nsec -= NSEC_PER_SEC;
66+
vdata->monotonic_time_coarse_sec++;
67+
}
68+
69+
vvar_write_end(vdata);
70+
}

arch/sparc/vdso/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vdso.lds
2+
vdso-image-*.c
3+
vdso2c

0 commit comments

Comments
 (0)