Skip to content

Commit

Permalink
vDSO for sparc
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Nagarathnam Muthusamy authored and David S. Miller committed Nov 15, 2017
1 parent 23198dd commit 9a08862
Show file tree
Hide file tree
Showing 26 changed files with 1,494 additions and 2 deletions.
1 change: 1 addition & 0 deletions arch/sparc/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ obj-y += mm/
obj-y += math-emu/
obj-y += net/
obj-y += crypto/
obj-$(CONFIG_SPARC64) += vdso/
2 changes: 2 additions & 0 deletions arch/sparc/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ config SPARC64
select HAVE_REGS_AND_STACK_ACCESS_API
select ARCH_USE_QUEUED_RWLOCKS
select ARCH_USE_QUEUED_SPINLOCKS
select GENERIC_TIME_VSYSCALL
select ARCH_CLOCKSOURCE_DATA

config ARCH_DEFCONFIG
string
Expand Down
4 changes: 4 additions & 0 deletions arch/sparc/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@ install:
archclean:
$(Q)$(MAKE) $(clean)=$(boot)

PHONY += vdso_install
vdso_install:
$(Q)$(MAKE) $(build)=arch/sparc/vdso $@

# This is the image used for packaging
KBUILD_IMAGE := $(boot)/zImage

Expand Down
17 changes: 17 additions & 0 deletions arch/sparc/include/asm/clocksource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
*/

#ifndef _ASM_SPARC_CLOCKSOURCE_H
#define _ASM_SPARC_CLOCKSOURCE_H

/* VDSO clocksources */
#define VCLOCK_NONE 0 /* Nothing userspace can do. */
#define VCLOCK_TICK 1 /* Use %tick. */
#define VCLOCK_STICK 2 /* Use %stick. */

struct arch_clocksource_data {
int vclock_mode;
};

#endif /* _ASM_SPARC_CLOCKSOURCE_H */
14 changes: 14 additions & 0 deletions arch/sparc/include/asm/elf_64.h
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,18 @@ do { if ((ex).e_ident[EI_CLASS] == ELFCLASS32) \
(current->personality & (~PER_MASK))); \
} while (0)

extern unsigned int vdso_enabled;

#define ARCH_DLINFO \
do { \
if (vdso_enabled) \
NEW_AUX_ENT(AT_SYSINFO_EHDR, \
(unsigned long)current->mm->context.vdso); \
} while (0)

struct linux_binprm;

#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);
#endif /* !(__ASM_SPARC64_ELF_H) */
1 change: 1 addition & 0 deletions arch/sparc/include/asm/mmu_64.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ typedef struct {
unsigned long thp_pte_count;
struct tsb_config tsb_block[MM_NUM_TSBS];
struct hv_tsb_descr tsb_descr[MM_NUM_TSBS];
void *vdso;
} mm_context_t;

#endif /* !__ASSEMBLY__ */
Expand Down
8 changes: 8 additions & 0 deletions arch/sparc/include/asm/processor_64.h
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ unsigned long get_wchan(struct task_struct *task);
* To make a long story short, we are trying to yield the current cpu
* strand during busy loops.
*/
#ifdef BUILD_VDSO
#define cpu_relax() asm volatile("\n99:\n\t" \
"rd %%ccr, %%g0\n\t" \
"rd %%ccr, %%g0\n\t" \
"rd %%ccr, %%g0\n\t" \
::: "memory")
#else /* ! BUILD_VDSO */
#define cpu_relax() asm volatile("\n99:\n\t" \
"rd %%ccr, %%g0\n\t" \
"rd %%ccr, %%g0\n\t" \
Expand All @@ -210,6 +217,7 @@ unsigned long get_wchan(struct task_struct *task);
"nop\n\t" \
".previous" \
::: "memory")
#endif

/* Prefetch support. This is tuned for UltraSPARC-III and later.
* UltraSPARC-I will treat these as nops, and UltraSPARC-II has
Expand Down
24 changes: 24 additions & 0 deletions arch/sparc/include/asm/vdso.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
*/

#ifndef _ASM_SPARC_VDSO_H
#define _ASM_SPARC_VDSO_H

struct vdso_image {
void *data;
unsigned long size; /* Always a multiple of PAGE_SIZE */
long sym_vvar_start; /* Negative offset to the vvar area */
long sym_vread_tick; /* Start of vread_tick section */
long sym_vread_tick_patch_start; /* Start of tick read */
long sym_vread_tick_patch_end; /* End of tick read */
};

#ifdef CONFIG_SPARC64
extern const struct vdso_image vdso_image_64_builtin;
#endif
#ifdef CONFIG_COMPAT
extern const struct vdso_image vdso_image_32_builtin;
#endif

#endif /* _ASM_SPARC_VDSO_H */
74 changes: 74 additions & 0 deletions arch/sparc/include/asm/vvar.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* Copyright (c) 2017 Oracle and/or its affiliates. All rights reserved.
*/

#ifndef _ASM_SPARC_VVAR_DATA_H
#define _ASM_SPARC_VVAR_DATA_H

#include <asm/clocksource.h>
#include <linux/seqlock.h>
#include <linux/time.h>
#include <linux/types.h>

struct vvar_data {
unsigned int seq;

int vclock_mode;
struct { /* extract of a clocksource struct */
u64 cycle_last;
u64 mask;
int mult;
int shift;
} clock;
/* open coded 'struct timespec' */
u64 wall_time_sec;
u64 wall_time_snsec;
u64 monotonic_time_snsec;
u64 monotonic_time_sec;
u64 monotonic_time_coarse_sec;
u64 monotonic_time_coarse_nsec;
u64 wall_time_coarse_sec;
u64 wall_time_coarse_nsec;

int tz_minuteswest;
int tz_dsttime;
};

extern struct vvar_data *vvar_data;
extern int vdso_fix_stick;

static inline unsigned int vvar_read_begin(const struct vvar_data *s)
{
unsigned int ret;

repeat:
ret = READ_ONCE(s->seq);
if (unlikely(ret & 1)) {
cpu_relax();
goto repeat;
}
smp_rmb(); /* Finish all reads before we return seq */
return ret;
}

static inline int vvar_read_retry(const struct vvar_data *s,
unsigned int start)
{
smp_rmb(); /* Finish all reads before checking the value of seq */
return unlikely(s->seq != start);
}

static inline void vvar_write_begin(struct vvar_data *s)
{
++s->seq;
smp_wmb(); /* Makes sure that increment of seq is reflected */
}

static inline void vvar_write_end(struct vvar_data *s)
{
smp_wmb(); /* Makes the value of seq current before we increment */
++s->seq;
}


#endif /* _ASM_SPARC_VVAR_DATA_H */
4 changes: 4 additions & 0 deletions arch/sparc/include/uapi/asm/auxvec.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
#ifndef __ASMSPARC_AUXVEC_H
#define __ASMSPARC_AUXVEC_H

#define AT_SYSINFO_EHDR 33

#define AT_VECTOR_SIZE_ARCH 1

#endif /* !(__ASMSPARC_AUXVEC_H) */
1 change: 1 addition & 0 deletions arch/sparc/kernel/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ obj-$(CONFIG_SPARC32) += systbls_32.o
obj-y += time_$(BITS).o
obj-$(CONFIG_SPARC32) += windows.o
obj-y += cpu.o
obj-$(CONFIG_SPARC64) += vdso.o
obj-$(CONFIG_SPARC32) += devices.o
obj-y += ptrace_$(BITS).o
obj-y += unaligned_$(BITS).o
Expand Down
11 changes: 9 additions & 2 deletions arch/sparc/kernel/time_64.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@

DEFINE_SPINLOCK(rtc_lock);

unsigned int __read_mostly vdso_fix_stick;

#ifdef CONFIG_SMP
unsigned long profile_pc(struct pt_regs *regs)
{
Expand Down Expand Up @@ -829,12 +831,17 @@ static void init_tick_ops(struct sparc64_tick_ops *ops)
void __init time_init_early(void)
{
if (tlb_type == spitfire) {
if (is_hummingbird())
if (is_hummingbird()) {
init_tick_ops(&hbtick_operations);
else
clocksource_tick.archdata.vclock_mode = VCLOCK_NONE;
} else {
init_tick_ops(&tick_operations);
clocksource_tick.archdata.vclock_mode = VCLOCK_TICK;
vdso_fix_stick = 1;
}
} else {
init_tick_ops(&stick_operations);
clocksource_tick.archdata.vclock_mode = VCLOCK_STICK;
}
}

Expand Down
70 changes: 70 additions & 0 deletions arch/sparc/kernel/vdso.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2001 Andrea Arcangeli <andrea@suse.de> SuSE
* Copyright 2003 Andi Kleen, SuSE Labs.
*
* Thanks to hpa@transmeta.com for some useful hint.
* Special thanks to Ingo Molnar for his early experience with
* a different vsyscall implementation for Linux/IA32 and for the name.
*/

#include <linux/seqlock.h>
#include <linux/time.h>
#include <linux/timekeeper_internal.h>

#include <asm/vvar.h>

void update_vsyscall_tz(void)
{
if (unlikely(vvar_data == NULL))
return;

vvar_data->tz_minuteswest = sys_tz.tz_minuteswest;
vvar_data->tz_dsttime = sys_tz.tz_dsttime;
}

void update_vsyscall(struct timekeeper *tk)
{
struct vvar_data *vdata = vvar_data;

if (unlikely(vdata == NULL))
return;

vvar_write_begin(vdata);
vdata->vclock_mode = tk->tkr_mono.clock->archdata.vclock_mode;
vdata->clock.cycle_last = tk->tkr_mono.cycle_last;
vdata->clock.mask = tk->tkr_mono.mask;
vdata->clock.mult = tk->tkr_mono.mult;
vdata->clock.shift = tk->tkr_mono.shift;

vdata->wall_time_sec = tk->xtime_sec;
vdata->wall_time_snsec = tk->tkr_mono.xtime_nsec;

vdata->monotonic_time_sec = tk->xtime_sec +
tk->wall_to_monotonic.tv_sec;
vdata->monotonic_time_snsec = tk->tkr_mono.xtime_nsec +
(tk->wall_to_monotonic.tv_nsec <<
tk->tkr_mono.shift);

while (vdata->monotonic_time_snsec >=
(((u64)NSEC_PER_SEC) << tk->tkr_mono.shift)) {
vdata->monotonic_time_snsec -=
((u64)NSEC_PER_SEC) << tk->tkr_mono.shift;
vdata->monotonic_time_sec++;
}

vdata->wall_time_coarse_sec = tk->xtime_sec;
vdata->wall_time_coarse_nsec =
(long)(tk->tkr_mono.xtime_nsec >> tk->tkr_mono.shift);

vdata->monotonic_time_coarse_sec =
vdata->wall_time_coarse_sec + tk->wall_to_monotonic.tv_sec;
vdata->monotonic_time_coarse_nsec =
vdata->wall_time_coarse_nsec + tk->wall_to_monotonic.tv_nsec;

while (vdata->monotonic_time_coarse_nsec >= NSEC_PER_SEC) {
vdata->monotonic_time_coarse_nsec -= NSEC_PER_SEC;
vdata->monotonic_time_coarse_sec++;
}

vvar_write_end(vdata);
}
3 changes: 3 additions & 0 deletions arch/sparc/vdso/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
vdso.lds
vdso-image-*.c
vdso2c
Loading

0 comments on commit 9a08862

Please sign in to comment.