Unverified Commit 24640f23 authored by Vincenzo Frascino's avatar Vincenzo Frascino Committed by Paul Burton
Browse files

mips: Add support for generic vDSO



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

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

Cc: Ralf Baechle <ralf@linux-mips.org>
Cc: Paul Burton <paul.burton@mips.com>
Signed-off-by: default avatarVincenzo Frascino <vincenzo.frascino@arm.com>
[paul.burton@mips.com: Prepend $(src) to config-n32-o32-env.c path.]
Signed-off-by: default avatarPaul Burton <paul.burton@mips.com>
parent c2aeaaea
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ config MIPS
	select GENERIC_CLOCKEVENTS
	select GENERIC_CMOS_UPDATE
	select GENERIC_CPU_AUTOPROBE
	select GENERIC_GETTIMEOFDAY
	select GENERIC_IOMAP
	select GENERIC_IRQ_PROBE
	select GENERIC_IRQ_SHOW
@@ -74,6 +75,7 @@ config MIPS
	select HAVE_STACKPROTECTOR
	select HAVE_SYSCALL_TRACEPOINTS
	select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT || !SMP
	select HAVE_GENERIC_VDSO
	select IRQ_FORCED_THREADING
	select ISA if EISA
	select MODULES_USE_ELF_RELA if MODULES && 64BIT
+2 −76
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@
#define __ASM_VDSO_H

#include <linux/mm_types.h>
#include <vdso/datapage.h>

#include <asm/barrier.h>

@@ -49,84 +50,9 @@ extern struct mips_vdso_image vdso_image_o32;
extern struct mips_vdso_image vdso_image_n32;
#endif

/**
 * union mips_vdso_data - Data provided by the kernel for the VDSO.
 * @xtime_sec:		Current real time (seconds part).
 * @xtime_nsec:		Current real time (nanoseconds part, shifted).
 * @wall_to_mono_sec:	Wall-to-monotonic offset (seconds part).
 * @wall_to_mono_nsec:	Wall-to-monotonic offset (nanoseconds part).
 * @seq_count:		Counter to synchronise updates (odd = updating).
 * @cs_shift:		Clocksource shift value.
 * @clock_mode:		Clocksource to use for time functions.
 * @cs_mult:		Clocksource multiplier value.
 * @cs_cycle_last:	Clock cycle value at last update.
 * @cs_mask:		Clocksource mask value.
 * @tz_minuteswest:	Minutes west of Greenwich (from timezone).
 * @tz_dsttime:		Type of DST correction (from timezone).
 *
 * This structure contains data needed by functions within the VDSO. It is
 * populated by the kernel and mapped read-only into user memory. The time
 * fields are mirrors of internal data from the timekeeping infrastructure.
 *
 * Note: Care should be taken when modifying as the layout must remain the same
 * for both 64- and 32-bit (for 32-bit userland on 64-bit kernel).
 */
union mips_vdso_data {
	struct {
		u64 xtime_sec;
		u64 xtime_nsec;
		u64 wall_to_mono_sec;
		u64 wall_to_mono_nsec;
		u32 seq_count;
		u32 cs_shift;
		u8 clock_mode;
		u32 cs_mult;
		u64 cs_cycle_last;
		u64 cs_mask;
		s32 tz_minuteswest;
		s32 tz_dsttime;
	};

	struct vdso_data data[CS_BASES];
	u8 page[PAGE_SIZE];
};

static inline u32 vdso_data_read_begin(const union mips_vdso_data *data)
{
	u32 seq;

	while (true) {
		seq = READ_ONCE(data->seq_count);
		if (likely(!(seq & 1))) {
			/* Paired with smp_wmb() in vdso_data_write_*(). */
			smp_rmb();
			return seq;
		}

		cpu_relax();
	}
}

static inline bool vdso_data_read_retry(const union mips_vdso_data *data,
					u32 start_seq)
{
	/* Paired with smp_wmb() in vdso_data_write_*(). */
	smp_rmb();
	return unlikely(data->seq_count != start_seq);
}

static inline void vdso_data_write_begin(union mips_vdso_data *data)
{
	++data->seq_count;

	/* Ensure sequence update is written before other data page values. */
	smp_wmb();
}

static inline void vdso_data_write_end(union mips_vdso_data *data)
{
	/* Ensure data values are written before updating sequence again. */
	smp_wmb();
	++data->seq_count;
}

#endif /* __ASM_VDSO_H */
+151 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 ARM Limited
 * Copyright (C) 2015 Imagination Technologies
 * Author: Alex Smith <alex.smith@imgtec.com>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation;  either version 2 of the  License, or (at your
 * option) any later version.
 */
#ifndef __ASM_VDSO_GETTIMEOFDAY_H
#define __ASM_VDSO_GETTIMEOFDAY_H

#ifndef __ASSEMBLY__

#include <linux/compiler.h>
#include <linux/time.h>

#include <asm/vdso/vdso.h>
#include <asm/clocksource.h>
#include <asm/io.h>
#include <asm/unistd.h>
#include <asm/vdso.h>

#ifdef CONFIG_MIPS_CLOCK_VSYSCALL

static __always_inline long gettimeofday_fallback(
				struct __kernel_old_timeval *_tv,
				struct timezone *_tz)
{
	register struct timezone *tz asm("a1") = _tz;
	register struct __kernel_old_timeval *tv asm("a0") = _tv;
	register long ret asm("v0");
	register long nr asm("v0") = __NR_gettimeofday;
	register long error asm("a3");

	asm volatile(
	"       syscall\n"
	: "=r" (ret), "=r" (error)
	: "r" (tv), "r" (tz), "r" (nr)
	: "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
	  "$14", "$15", "$24", "$25", "hi", "lo", "memory");

	return error ? -ret : ret;
}

#else

static __always_inline long gettimeofday_fallback(
				struct __kernel_old_timeval *_tv,
				struct timezone *_tz)
{
	return -1;
}

#endif

static __always_inline long clock_gettime_fallback(
					clockid_t _clkid,
					struct __kernel_timespec *_ts)
{
	register struct __kernel_timespec *ts asm("a1") = _ts;
	register clockid_t clkid asm("a0") = _clkid;
	register long ret asm("v0");
#if _MIPS_SIM == _MIPS_SIM_ABI64
	register long nr asm("v0") = __NR_clock_gettime;
#else
	register long nr asm("v0") = __NR_clock_gettime64;
#endif
	register long error asm("a3");

	asm volatile(
	"       syscall\n"
	: "=r" (ret), "=r" (error)
	: "r" (clkid), "r" (ts), "r" (nr)
	: "$1", "$3", "$8", "$9", "$10", "$11", "$12", "$13",
	  "$14", "$15", "$24", "$25", "hi", "lo", "memory");

	return error ? -ret : ret;
}

#ifdef CONFIG_CSRC_R4K

static __always_inline u64 read_r4k_count(void)
{
	unsigned int count;

	__asm__ __volatile__(
	"	.set push\n"
	"	.set mips32r2\n"
	"	rdhwr	%0, $2\n"
	"	.set pop\n"
	: "=r" (count));

	return count;
}

#endif

#ifdef CONFIG_CLKSRC_MIPS_GIC

static __always_inline u64 read_gic_count(const struct vdso_data *data)
{
	void __iomem *gic = get_gic(data);
	u32 hi, hi2, lo;

	do {
		hi = __raw_readl(gic + sizeof(lo));
		lo = __raw_readl(gic);
		hi2 = __raw_readl(gic + sizeof(lo));
	} while (hi2 != hi);

	return (((u64)hi) << 32) + lo;
}

#endif

static __always_inline u64 __arch_get_hw_counter(s32 clock_mode)
{
#ifdef CONFIG_CLKSRC_MIPS_GIC
	const struct vdso_data *data = get_vdso_data();
#endif
	u64 cycle_now;

	switch (clock_mode) {
#ifdef CONFIG_CSRC_R4K
	case VDSO_CLOCK_R4K:
		cycle_now = read_r4k_count();
		break;
#endif
#ifdef CONFIG_CLKSRC_MIPS_GIC
	case VDSO_CLOCK_GIC:
		cycle_now = read_gic_count(data);
		break;
#endif
	default:
		cycle_now = 0;
		break;
	}

	return cycle_now;
}

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

#endif /* !__ASSEMBLY__ */

#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
+3 −3
Original line number Diff line number Diff line
@@ -68,14 +68,14 @@ static inline unsigned long get_vdso_base(void)
	return addr;
}

static inline const union mips_vdso_data *get_vdso_data(void)
static inline const struct vdso_data *get_vdso_data(void)
{
	return (const union mips_vdso_data *)(get_vdso_base() - PAGE_SIZE);
	return (const struct vdso_data *)(get_vdso_base() - PAGE_SIZE);
}

#ifdef CONFIG_CLKSRC_MIPS_GIC

static inline void __iomem *get_gic(const union mips_vdso_data *data)
static inline void __iomem *get_gic(const struct vdso_data *data)
{
	return (void __iomem *)data - PAGE_SIZE;
}
+43 −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>

extern struct vdso_data *vdso_data;

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

static __always_inline
int __mips_get_clock_mode(struct timekeeper *tk)
{
	u32 clock_mode = tk->tkr_mono.clock->archdata.vdso_clock_mode;

	return clock_mode;
}
#define __arch_get_clock_mode __mips_get_clock_mode

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

/* 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 */
Loading