Commit 7b4ee085 authored by Thomas Weißschuh's avatar Thomas Weißschuh Committed by Thomas Gleixner
Browse files

sparc64: vdso: Replace code patching with runtime conditional



The patching logic is unnecessarily complicated and stands in the way of
the adoption of the generic vDSO framework.

Replace it by a simple runtime switch, similar to other architectures.

Suggested-by: default avatarThomas Gleixner <tglx@linutronix.de>
Signed-off-by: default avatarThomas Weißschuh <thomas.weissschuh@linutronix.de>
Signed-off-by: default avatarThomas Gleixner <tglx@kernel.org>
Tested-by: default avatarAndreas Larsson <andreas@gaisler.com>
Reviewed-by: default avatarAndreas Larsson <andreas@gaisler.com>
Acked-by: default avatarAndreas Larsson <andreas@gaisler.com>
Link: https://lore.kernel.org/lkml/87ecu9tfhw.ffs@tglx/
Link: https://patch.msgid.link/20260304-vdso-sparc64-generic-2-v6-6-d8eb3b0e1410@linutronix.de
parent 9fc4fe6e
Loading
Loading
Loading
Loading
+4 −108
Original line number Diff line number Diff line
@@ -148,17 +148,11 @@ notrace static __always_inline u64 vgetsns(struct vvar_data *vvar)
	u64 v;
	u64 cycles;

	if (likely(vvar->vclock_mode == VCLOCK_STICK))
		cycles = vread_tick_stick();
	else
		cycles = vread_tick();
	v = (cycles - vvar->clock.cycle_last) & vvar->clock.mask;
	return v * vvar->clock.mult;
}

notrace static __always_inline u64 vgetsns_stick(struct vvar_data *vvar)
{
	u64 v;
	u64 cycles;

	cycles = vread_tick_stick();
	v = (cycles - vvar->clock.cycle_last) & vvar->clock.mask;
	return v * vvar->clock.mult;
}
@@ -183,26 +177,6 @@ notrace static __always_inline int do_realtime(struct vvar_data *vvar,
	return 0;
}

notrace static __always_inline int do_realtime_stick(struct vvar_data *vvar,
						     struct __kernel_old_timespec *ts)
{
	unsigned long seq;
	u64 ns;

	do {
		seq = vvar_read_begin(vvar);
		ts->tv_sec = vvar->wall_time_sec;
		ns = vvar->wall_time_snsec;
		ns += vgetsns_stick(vvar);
		ns = __shr64(ns, vvar->clock.shift);
	} while (unlikely(vvar_read_retry(vvar, seq)));

	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
	ts->tv_nsec = ns;

	return 0;
}

notrace static __always_inline int do_monotonic(struct vvar_data *vvar,
						struct __kernel_old_timespec *ts)
{
@@ -223,26 +197,6 @@ notrace static __always_inline int do_monotonic(struct vvar_data *vvar,
	return 0;
}

notrace static __always_inline int do_monotonic_stick(struct vvar_data *vvar,
						      struct __kernel_old_timespec *ts)
{
	unsigned long seq;
	u64 ns;

	do {
		seq = vvar_read_begin(vvar);
		ts->tv_sec = vvar->monotonic_time_sec;
		ns = vvar->monotonic_time_snsec;
		ns += vgetsns_stick(vvar);
		ns = __shr64(ns, vvar->clock.shift);
	} while (unlikely(vvar_read_retry(vvar, seq)));

	ts->tv_sec += __iter_div_u64_rem(ns, NSEC_PER_SEC, &ns);
	ts->tv_nsec = ns;

	return 0;
}

notrace static int do_realtime_coarse(struct vvar_data *vvar,
				      struct __kernel_old_timespec *ts)
{
@@ -298,31 +252,6 @@ int
clock_gettime(clockid_t, struct __kernel_old_timespec *)
	__attribute__((weak, alias("__vdso_clock_gettime")));

notrace int
__vdso_clock_gettime_stick(clockid_t clock, struct __kernel_old_timespec *ts)
{
	struct vvar_data *vvd = get_vvar_data();

	switch (clock) {
	case CLOCK_REALTIME:
		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
			break;
		return do_realtime_stick(vvd, ts);
	case CLOCK_MONOTONIC:
		if (unlikely(vvd->vclock_mode == VCLOCK_NONE))
			break;
		return do_monotonic_stick(vvd, ts);
	case CLOCK_REALTIME_COARSE:
		return do_realtime_coarse(vvd, ts);
	case CLOCK_MONOTONIC_COARSE:
		return do_monotonic_coarse(vvd, ts);
	}
	/*
	 * Unknown clock ID ? Fall back to the syscall.
	 */
	return vdso_fallback_gettime(clock, ts);
}

notrace int
__vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
{
@@ -358,36 +287,3 @@ __vdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
int
gettimeofday(struct __kernel_old_timeval *, struct timezone *)
	__attribute__((weak, alias("__vdso_gettimeofday")));

notrace int
__vdso_gettimeofday_stick(struct __kernel_old_timeval *tv, struct timezone *tz)
{
	struct vvar_data *vvd = get_vvar_data();

	if (likely(vvd->vclock_mode != VCLOCK_NONE)) {
		if (likely(tv != NULL)) {
			union tstv_t {
				struct __kernel_old_timespec ts;
				struct __kernel_old_timeval tv;
			} *tstv = (union tstv_t *) tv;
			do_realtime_stick(vvd, &tstv->ts);
			/*
			 * Assign before dividing to ensure that the division is
			 * done in the type of tv_usec, not tv_nsec.
			 *
			 * There cannot be > 1 billion usec in a second:
			 * do_realtime() has already distributed such overflow
			 * into tv_sec.  So we can assign it to an int safely.
			 */
			tstv->tv.tv_usec = tstv->ts.tv_nsec;
			tstv->tv.tv_usec /= 1000;
		}
		if (unlikely(tz != NULL)) {
			/* Avoid memcpy. Some old compilers fail to inline it */
			tz->tz_minuteswest = vvd->tz_minuteswest;
			tz->tz_dsttime = vvd->tz_dsttime;
		}
		return 0;
	}
	return vdso_fallback_gettimeofday(tv, tz);
}
+0 −2
Original line number Diff line number Diff line
@@ -18,10 +18,8 @@ VERSION {
	global:
		clock_gettime;
		__vdso_clock_gettime;
		__vdso_clock_gettime_stick;
		gettimeofday;
		__vdso_gettimeofday;
		__vdso_gettimeofday_stick;
	local: *;
	};
}
+0 −2
Original line number Diff line number Diff line
@@ -17,10 +17,8 @@ VERSION {
	global:
		clock_gettime;
		__vdso_clock_gettime;
		__vdso_clock_gettime_stick;
		gettimeofday;
		__vdso_gettimeofday;
		__vdso_gettimeofday_stick;
	local: *;
	};
}
+0 −204
Original line number Diff line number Diff line
@@ -42,203 +42,6 @@ static struct vm_special_mapping vdso_mapping32 = {

struct vvar_data *vvar_data;

struct vdso_elfinfo32 {
	Elf32_Ehdr	*hdr;
	Elf32_Sym	*dynsym;
	unsigned long	dynsymsize;
	const char	*dynstr;
	unsigned long	text;
};

struct vdso_elfinfo64 {
	Elf64_Ehdr	*hdr;
	Elf64_Sym	*dynsym;
	unsigned long	dynsymsize;
	const char	*dynstr;
	unsigned long	text;
};

struct vdso_elfinfo {
	union {
		struct vdso_elfinfo32 elf32;
		struct vdso_elfinfo64 elf64;
	} u;
};

static void *one_section64(struct vdso_elfinfo64 *e, const char *name,
			   unsigned long *size)
{
	const char *snames;
	Elf64_Shdr *shdrs;
	unsigned int i;

	shdrs = (void *)e->hdr + e->hdr->e_shoff;
	snames = (void *)e->hdr + shdrs[e->hdr->e_shstrndx].sh_offset;
	for (i = 1; i < e->hdr->e_shnum; i++) {
		if (!strcmp(snames+shdrs[i].sh_name, name)) {
			if (size)
				*size = shdrs[i].sh_size;
			return (void *)e->hdr + shdrs[i].sh_offset;
		}
	}
	return NULL;
}

static int find_sections64(const struct vdso_image *image, struct vdso_elfinfo *_e)
{
	struct vdso_elfinfo64 *e = &_e->u.elf64;

	e->hdr = image->data;
	e->dynsym = one_section64(e, ".dynsym", &e->dynsymsize);
	e->dynstr = one_section64(e, ".dynstr", NULL);

	if (!e->dynsym || !e->dynstr) {
		pr_err("VDSO64: Missing symbol sections.\n");
		return -ENODEV;
	}
	return 0;
}

static Elf64_Sym *find_sym64(const struct vdso_elfinfo64 *e, const char *name)
{
	unsigned int i;

	for (i = 0; i < (e->dynsymsize / sizeof(Elf64_Sym)); i++) {
		Elf64_Sym *s = &e->dynsym[i];
		if (s->st_name == 0)
			continue;
		if (!strcmp(e->dynstr + s->st_name, name))
			return s;
	}
	return NULL;
}

static int patchsym64(struct vdso_elfinfo *_e, const char *orig,
		      const char *new)
{
	struct vdso_elfinfo64 *e = &_e->u.elf64;
	Elf64_Sym *osym = find_sym64(e, orig);
	Elf64_Sym *nsym = find_sym64(e, new);

	if (!nsym || !osym) {
		pr_err("VDSO64: Missing symbols.\n");
		return -ENODEV;
	}
	osym->st_value = nsym->st_value;
	osym->st_size = nsym->st_size;
	osym->st_info = nsym->st_info;
	osym->st_other = nsym->st_other;
	osym->st_shndx = nsym->st_shndx;

	return 0;
}

static void *one_section32(struct vdso_elfinfo32 *e, const char *name,
			   unsigned long *size)
{
	const char *snames;
	Elf32_Shdr *shdrs;
	unsigned int i;

	shdrs = (void *)e->hdr + e->hdr->e_shoff;
	snames = (void *)e->hdr + shdrs[e->hdr->e_shstrndx].sh_offset;
	for (i = 1; i < e->hdr->e_shnum; i++) {
		if (!strcmp(snames+shdrs[i].sh_name, name)) {
			if (size)
				*size = shdrs[i].sh_size;
			return (void *)e->hdr + shdrs[i].sh_offset;
		}
	}
	return NULL;
}

static int find_sections32(const struct vdso_image *image, struct vdso_elfinfo *_e)
{
	struct vdso_elfinfo32 *e = &_e->u.elf32;

	e->hdr = image->data;
	e->dynsym = one_section32(e, ".dynsym", &e->dynsymsize);
	e->dynstr = one_section32(e, ".dynstr", NULL);

	if (!e->dynsym || !e->dynstr) {
		pr_err("VDSO32: Missing symbol sections.\n");
		return -ENODEV;
	}
	return 0;
}

static Elf32_Sym *find_sym32(const struct vdso_elfinfo32 *e, const char *name)
{
	unsigned int i;

	for (i = 0; i < (e->dynsymsize / sizeof(Elf32_Sym)); i++) {
		Elf32_Sym *s = &e->dynsym[i];
		if (s->st_name == 0)
			continue;
		if (!strcmp(e->dynstr + s->st_name, name))
			return s;
	}
	return NULL;
}

static int patchsym32(struct vdso_elfinfo *_e, const char *orig,
		      const char *new)
{
	struct vdso_elfinfo32 *e = &_e->u.elf32;
	Elf32_Sym *osym = find_sym32(e, orig);
	Elf32_Sym *nsym = find_sym32(e, new);

	if (!nsym || !osym) {
		pr_err("VDSO32: Missing symbols.\n");
		return -ENODEV;
	}
	osym->st_value = nsym->st_value;
	osym->st_size = nsym->st_size;
	osym->st_info = nsym->st_info;
	osym->st_other = nsym->st_other;
	osym->st_shndx = nsym->st_shndx;

	return 0;
}

static int find_sections(const struct vdso_image *image, struct vdso_elfinfo *e,
			 bool elf64)
{
	if (elf64)
		return find_sections64(image, e);
	else
		return find_sections32(image, e);
}

static int patch_one_symbol(struct vdso_elfinfo *e, const char *orig,
			    const char *new_target, bool elf64)
{
	if (elf64)
		return patchsym64(e, orig, new_target);
	else
		return patchsym32(e, orig, new_target);
}

static int stick_patch(const struct vdso_image *image, struct vdso_elfinfo *e, bool elf64)
{
	int err;

	err = find_sections(image, e, elf64);
	if (err)
		return err;

	err = patch_one_symbol(e,
			       "__vdso_gettimeofday",
			       "__vdso_gettimeofday_stick", elf64);
	if (err)
		return err;

	return patch_one_symbol(e,
				"__vdso_clock_gettime",
				"__vdso_clock_gettime_stick", elf64);
	return 0;
}

/*
 * Allocate pages for the vdso and vvar, and copy in the vdso text from the
 * kernel image.
@@ -250,15 +53,8 @@ static int __init init_vdso_image(const struct vdso_image *image,
	int cnpages = (image->size) / PAGE_SIZE;
	struct page *dp, **dpp = NULL;
	struct page *cp, **cpp = NULL;
	struct vdso_elfinfo ei;
	int i, dnpages = 0;

	if (tlb_type != spitfire) {
		int err = stick_patch(image, &ei, elf64);
		if (err)
			return err;
	}

	/*
	 * First, the vdso text.  This is initialied data, an integral number of
	 * pages long.