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: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Thomas Weißschuh <thomas.weissschuh@linutronix.de>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Tested-by: Andreas Larsson <andreas@gaisler.com>
Reviewed-by: Andreas Larsson <andreas@gaisler.com>
Acked-by: Andreas 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
This commit is contained in:
Thomas Weißschuh 2026-03-04 08:49:03 +01:00 committed by Thomas Gleixner
parent 9fc4fe6e14
commit 7b4ee085e7
4 changed files with 4 additions and 316 deletions

View File

@ -148,17 +148,11 @@ notrace static __always_inline u64 vgetsns(struct vvar_data *vvar)
u64 v;
u64 cycles;
cycles = vread_tick();
v = (cycles - vvar->clock.cycle_last) & vvar->clock.mask;
return v * vvar->clock.mult;
}
if (likely(vvar->vclock_mode == VCLOCK_STICK))
cycles = vread_tick_stick();
else
cycles = vread_tick();
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);
}

View File

@ -18,10 +18,8 @@ VERSION {
global:
clock_gettime;
__vdso_clock_gettime;
__vdso_clock_gettime_stick;
gettimeofday;
__vdso_gettimeofday;
__vdso_gettimeofday_stick;
local: *;
};
}

View File

@ -17,10 +17,8 @@ VERSION {
global:
clock_gettime;
__vdso_clock_gettime;
__vdso_clock_gettime_stick;
gettimeofday;
__vdso_gettimeofday;
__vdso_gettimeofday_stick;
local: *;
};
}

View File

@ -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.