Commit 20e2fc42 authored by Vincenzo Frascino's avatar Vincenzo Frascino Committed by Russell King
Browse files

ARM: 8930/1: Add support for generic vDSO



The arm vDSO library requires some adaptations to take advantage of
the newly introduced generic vDSO library.

Introduce the following changes:
 - Modification vdso.c to be compliant with the common vdso datapage
 - Use of lib/vdso for gettimeofday
 - Implementation of elf note

Signed-off-by: default avatarVincenzo Frascino <vincenzo.frascino@arm.com>
Signed-off-by: default avatarRussell King <rmk+kernel@armlinux.org.uk>
parent 9f1984c6
Loading
Loading
Loading
Loading
+74 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * Copyright (C) 2018 ARM Limited
 */
#ifndef __ASM_VDSO_GETTIMEOFDAY_H
#define __ASM_VDSO_GETTIMEOFDAY_H

#ifndef __ASSEMBLY__

#include <asm/barrier.h>
#include <asm/cp15.h>
#include <asm/unistd.h>
#include <uapi/linux/time.h>

extern struct vdso_data *__get_datapage(void);

static __always_inline int gettimeofday_fallback(
				struct __kernel_old_timeval *_tv,
				struct timezone *_tz)
{
	register struct timezone *tz asm("r1") = _tz;
	register struct __kernel_old_timeval *tv asm("r0") = _tv;
	register long ret asm ("r0");
	register long nr asm("r7") = __NR_gettimeofday;

	asm volatile(
	"	swi #0\n"
	: "=r" (ret)
	: "r" (tv), "r" (tz), "r" (nr)
	: "memory");

	return ret;
}

static __always_inline long clock_gettime_fallback(
					clockid_t _clkid,
					struct __kernel_timespec *_ts)
{
	register struct __kernel_timespec *ts asm("r1") = _ts;
	register clockid_t clkid asm("r0") = _clkid;
	register long ret asm ("r0");
	register long nr asm("r7") = __NR_clock_gettime64;

	asm volatile(
	"	swi #0\n"
	: "=r" (ret)
	: "r" (clkid), "r" (ts), "r" (nr)
	: "memory");

	return ret;
}

static __always_inline u64 __arch_get_hw_counter(int clock_mode)
{
#ifdef CONFIG_ARM_ARCH_TIMER
	u64 cycle_now;

	isb();
	cycle_now = read_sysreg(CNTVCT);

	return cycle_now;
#else
	return -EINVAL; /* use fallback */
#endif
}

static __always_inline const struct vdso_data *__arch_get_vdso_data(void)
{
	return __get_datapage();
}

#endif /* !__ASSEMBLY__ */

#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
+71 −0
Original line number Diff line number Diff line
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_VDSO_VSYSCALL_H
#define __ASM_VDSO_VSYSCALL_H

#ifndef __ASSEMBLY__

#include <linux/timekeeper_internal.h>
#include <vdso/datapage.h>
#include <asm/cacheflush.h>

extern struct vdso_data *vdso_data;
extern bool cntvct_ok;

static __always_inline
bool tk_is_cntvct(const struct timekeeper *tk)
{
	if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER))
		return false;

	if (!tk->tkr_mono.clock->archdata.vdso_direct)
		return false;

	return true;
}

/*
 * Update the vDSO data page to keep in sync with kernel timekeeping.
 */
static __always_inline
struct vdso_data *__arm_get_k_vdso_data(void)
{
	return vdso_data;
}
#define __arch_get_k_vdso_data __arm_get_k_vdso_data

static __always_inline
int __arm_update_vdso_data(void)
{
	return !cntvct_ok;
}
#define __arch_update_vdso_data __arm_update_vdso_data

static __always_inline
int __arm_get_clock_mode(struct timekeeper *tk)
{
	u32 __tk_is_cntvct = tk_is_cntvct(tk);

	return __tk_is_cntvct;
}
#define __arch_get_clock_mode __arm_get_clock_mode

static __always_inline
int __arm_use_vsyscall(struct vdso_data *vdata)
{
	return vdata[CS_HRES_COARSE].clock_mode;
}
#define __arch_use_vsyscall __arm_use_vsyscall

static __always_inline
void __arm_sync_vdso_data(struct vdso_data *vdata)
{
	flush_dcache_page(virt_to_page(vdata));
}
#define __arch_sync_vdso_data __arm_sync_vdso_data

/* The asm-generic header needs to be included after the definitions above */
#include <asm-generic/vdso/vsyscall.h>

#endif /* !__ASSEMBLY__ */

#endif /* __ASM_VDSO_VSYSCALL_H */
+3 −26
Original line number Diff line number Diff line
@@ -11,34 +11,11 @@

#ifndef __ASSEMBLY__

#include <vdso/datapage.h>
#include <asm/page.h>

/* Try to be cache-friendly on systems that don't implement the
 * generic timer: fit the unconditionally updated fields in the first
 * 32 bytes.
 */
struct vdso_data {
	u32 seq_count;		/* sequence count - odd during updates */
	u16 tk_is_cntvct;	/* fall back to syscall if false */
	u16 cs_shift;		/* clocksource shift */
	u32 xtime_coarse_sec;	/* coarse time */
	u32 xtime_coarse_nsec;

	u32 wtm_clock_sec;	/* wall to monotonic offset */
	u32 wtm_clock_nsec;
	u32 xtime_clock_sec;	/* CLOCK_REALTIME - seconds */
	u32 cs_mult;		/* clocksource multiplier */

	u64 cs_cycle_last;	/* last cycle value */
	u64 cs_mask;		/* clocksource mask */

	u64 xtime_clock_snsec;	/* CLOCK_REALTIME sub-ns base */
	u32 tz_minuteswest;	/* timezone info for gettimeofday(2) */
	u32 tz_dsttime;
};

union vdso_data_store {
	struct vdso_data data;
	struct vdso_data	data[CS_BASES];
	u8			page[PAGE_SIZE];
};

+4 −83
Original line number Diff line number Diff line
@@ -23,6 +23,8 @@
#include <asm/vdso.h>
#include <asm/vdso_datapage.h>
#include <clocksource/arm_arch_timer.h>
#include <vdso/helpers.h>
#include <vdso/vsyscall.h>

#define MAX_SYMNAME	64

@@ -37,7 +39,7 @@ unsigned int vdso_total_pages __ro_after_init;
 * The VDSO data page.
 */
static union vdso_data_store vdso_data_store __page_aligned_data;
static struct vdso_data *vdso_data = &vdso_data_store.data;
struct vdso_data *vdso_data = vdso_data_store.data;

static struct page *vdso_data_page __ro_after_init;
static const struct vm_special_mapping vdso_data_mapping = {
@@ -77,7 +79,7 @@ struct elfinfo {
/* Cached result of boot-time check for whether the arch timer exists,
 * and if so, whether the virtual counter is useable.
 */
static bool cntvct_ok __ro_after_init;
bool cntvct_ok __ro_after_init;

static bool __init cntvct_functional(void)
{
@@ -262,84 +264,3 @@ void arm_install_vdso(struct mm_struct *mm, unsigned long addr)
		mm->context.vdso = addr;
}
static void vdso_write_begin(struct vdso_data *vdata)
{
	++vdso_data->seq_count;
	smp_wmb(); /* Pairs with smp_rmb in vdso_read_retry */
}

static void vdso_write_end(struct vdso_data *vdata)
{
	smp_wmb(); /* Pairs with smp_rmb in vdso_read_begin */
	++vdso_data->seq_count;
}

static bool tk_is_cntvct(const struct timekeeper *tk)
{
	if (!IS_ENABLED(CONFIG_ARM_ARCH_TIMER))
		return false;

	if (!tk->tkr_mono.clock->archdata.vdso_direct)
		return false;

	return true;
}

/**
 * update_vsyscall - update the vdso data page
 *
 * Increment the sequence counter, making it odd, indicating to
 * userspace that an update is in progress.  Update the fields used
 * for coarse clocks and, if the architected system timer is in use,
 * the fields used for high precision clocks.  Increment the sequence
 * counter again, making it even, indicating to userspace that the
 * update is finished.
 *
 * Userspace is expected to sample seq_count before reading any other
 * fields from the data page.  If seq_count is odd, userspace is
 * expected to wait until it becomes even.  After copying data from
 * the page, userspace must sample seq_count again; if it has changed
 * from its previous value, userspace must retry the whole sequence.
 *
 * Calls to update_vsyscall are serialized by the timekeeping core.
 */
void update_vsyscall(struct timekeeper *tk)
{
	struct timespec64 *wtm = &tk->wall_to_monotonic;

	if (!cntvct_ok) {
		/* The entry points have been zeroed, so there is no
		 * point in updating the data page.
		 */
		return;
	}

	vdso_write_begin(vdso_data);

	vdso_data->tk_is_cntvct			= tk_is_cntvct(tk);
	vdso_data->xtime_coarse_sec		= tk->xtime_sec;
	vdso_data->xtime_coarse_nsec		= (u32)(tk->tkr_mono.xtime_nsec >>
							tk->tkr_mono.shift);
	vdso_data->wtm_clock_sec		= wtm->tv_sec;
	vdso_data->wtm_clock_nsec		= wtm->tv_nsec;

	if (vdso_data->tk_is_cntvct) {
		vdso_data->cs_cycle_last	= tk->tkr_mono.cycle_last;
		vdso_data->xtime_clock_sec	= tk->xtime_sec;
		vdso_data->xtime_clock_snsec	= tk->tkr_mono.xtime_nsec;
		vdso_data->cs_mult		= tk->tkr_mono.mult;
		vdso_data->cs_shift		= tk->tkr_mono.shift;
		vdso_data->cs_mask		= tk->tkr_mono.mask;
	}

	vdso_write_end(vdso_data);

	flush_dcache_page(virt_to_page(vdso_data));
}

void update_vsyscall_tz(void)
{
	vdso_data->tz_minuteswest	= sys_tz.tz_minuteswest;
	vdso_data->tz_dsttime		= sys_tz.tz_dsttime;
	flush_dcache_page(virt_to_page(vdso_data));
}
+3 −0
Original line number Diff line number Diff line
@@ -896,7 +896,10 @@ config VDSO
	bool "Enable VDSO for acceleration of some system calls"
	depends on AEABI && MMU && CPU_V7
	default y if ARM_ARCH_TIMER
	select HAVE_GENERIC_VDSO
	select GENERIC_TIME_VSYSCALL
	select GENERIC_VDSO_32
	select GENERIC_GETTIMEOFDAY
	help
	  Place in the process address space an ELF shared object
	  providing fast implementations of gettimeofday and
Loading