Skip to content

Commit df7fcbe

Browse files
t-8chKAGA-KOKO
authored andcommitted
vdso: Add generic time data storage
Historically each architecture defined their own way to store the vDSO data page. Add a generic mechanism to provide storage for that page. Furthermore this generic storage will be extended to also provide uniform storage for *non*-time-related data, like the random state or architecture-specific data. These will have their own pages and data structures, so rename 'vdso_data' into 'vdso_time_data' to make that split clear from the name. Also introduce a new consistent naming scheme for the symbols related to the vDSO, which makes it clear if the symbol is accessible from userspace or kernel space and the type of data behind the symbol. The generic fault handler contains an optimization to prefault the vvar page when the timens page is accessed. This was lifted from s390 and x86. Co-developed-by: Nam Cao <[email protected]> Signed-off-by: Nam Cao <[email protected]> Signed-off-by: Thomas Weißschuh <[email protected]> Signed-off-by: Thomas Gleixner <[email protected]> Link: https://lore.kernel.org/all/[email protected]
1 parent 127b0e0 commit df7fcbe

File tree

10 files changed

+195
-19
lines changed

10 files changed

+195
-19
lines changed

include/asm-generic/vdso/vsyscall.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,28 @@
44

55
#ifndef __ASSEMBLY__
66

7+
#ifdef CONFIG_GENERIC_VDSO_DATA_STORE
8+
9+
#ifndef __arch_get_vdso_u_time_data
10+
static __always_inline const struct vdso_time_data *__arch_get_vdso_u_time_data(void)
11+
{
12+
return vdso_u_time_data;
13+
}
14+
#endif
15+
16+
#else /* !CONFIG_GENERIC_VDSO_DATA_STORE */
17+
718
#ifndef __arch_get_k_vdso_data
819
static __always_inline struct vdso_data *__arch_get_k_vdso_data(void)
920
{
1021
return NULL;
1122
}
1223
#endif /* __arch_get_k_vdso_data */
24+
#define vdso_k_time_data __arch_get_k_vdso_data()
25+
26+
#define __arch_get_vdso_u_time_data __arch_get_vdso_data
27+
28+
#endif /* CONFIG_GENERIC_VDSO_DATA_STORE */
1329

1430
#ifndef __arch_update_vsyscall
1531
static __always_inline void __arch_update_vsyscall(struct vdso_data *vdata)

include/linux/time_namespace.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <linux/ns_common.h>
99
#include <linux/err.h>
1010
#include <linux/time64.h>
11+
#include <vdso/datapage.h>
1112

1213
struct user_namespace;
1314
extern struct user_namespace init_user_ns;

include/linux/vdso_datastore.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
#ifndef _LINUX_VDSO_DATASTORE_H
3+
#define _LINUX_VDSO_DATASTORE_H
4+
5+
#include <linux/mm_types.h>
6+
7+
extern const struct vm_special_mapping vdso_vvar_mapping;
8+
struct vm_area_struct *vdso_install_vvar_mapping(struct mm_struct *mm, unsigned long addr);
9+
10+
#endif /* _LINUX_VDSO_DATASTORE_H */

include/vdso/datapage.h

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -45,19 +45,19 @@ struct arch_vdso_time_data {};
4545
*
4646
* There is one vdso_timestamp object in vvar for each vDSO-accelerated
4747
* clock_id. For high-resolution clocks, this encodes the time
48-
* corresponding to vdso_data.cycle_last. For coarse clocks this encodes
48+
* corresponding to vdso_time_data.cycle_last. For coarse clocks this encodes
4949
* the actual time.
5050
*
5151
* To be noticed that for highres clocks nsec is left-shifted by
52-
* vdso_data.cs[x].shift.
52+
* vdso_time_data[x].shift.
5353
*/
5454
struct vdso_timestamp {
5555
u64 sec;
5656
u64 nsec;
5757
};
5858

5959
/**
60-
* struct vdso_data - vdso datapage representation
60+
* struct vdso_time_data - vdso datapage representation
6161
* @seq: timebase sequence counter
6262
* @clock_mode: clock mode
6363
* @cycle_last: timebase at clocksource init
@@ -74,7 +74,7 @@ struct vdso_timestamp {
7474
* @arch_data: architecture specific data (optional, defaults
7575
* to an empty struct)
7676
*
77-
* vdso_data will be accessed by 64 bit and compat code at the same time
77+
* vdso_time_data will be accessed by 64 bit and compat code at the same time
7878
* so we should be careful before modifying this structure.
7979
*
8080
* The ordering of the struct members is optimized to have fast access to the
@@ -92,7 +92,7 @@ struct vdso_timestamp {
9292
* For clocks which are not affected by time namespace adjustment the
9393
* offset must be zero.
9494
*/
95-
struct vdso_data {
95+
struct vdso_time_data {
9696
u32 seq;
9797

9898
s32 clock_mode;
@@ -117,6 +117,8 @@ struct vdso_data {
117117
struct arch_vdso_time_data arch_data;
118118
};
119119

120+
#define vdso_data vdso_time_data
121+
120122
/**
121123
* struct vdso_rng_data - vdso RNG state information
122124
* @generation: counter representing the number of RNG reseeds
@@ -136,18 +138,34 @@ struct vdso_rng_data {
136138
* With the hidden visibility, the compiler simply generates a PC-relative
137139
* relocation, and this is what we need.
138140
*/
139-
extern struct vdso_data _vdso_data[CS_BASES] __attribute__((visibility("hidden")));
140-
extern struct vdso_data _timens_data[CS_BASES] __attribute__((visibility("hidden")));
141+
#ifndef CONFIG_GENERIC_VDSO_DATA_STORE
142+
extern struct vdso_time_data _vdso_data[CS_BASES] __attribute__((visibility("hidden")));
143+
extern struct vdso_time_data _timens_data[CS_BASES] __attribute__((visibility("hidden")));
141144
extern struct vdso_rng_data _vdso_rng_data __attribute__((visibility("hidden")));
145+
#else
146+
extern struct vdso_time_data vdso_u_time_data[CS_BASES] __attribute__((visibility("hidden")));
147+
148+
extern struct vdso_time_data *vdso_k_time_data;
149+
#endif
142150

143151
/**
144152
* union vdso_data_store - Generic vDSO data page
145153
*/
146154
union vdso_data_store {
147-
struct vdso_data data[CS_BASES];
155+
struct vdso_time_data data[CS_BASES];
148156
u8 page[1U << CONFIG_PAGE_SHIFT];
149157
};
150158

159+
#ifdef CONFIG_GENERIC_VDSO_DATA_STORE
160+
161+
enum vdso_pages {
162+
VDSO_TIME_PAGE_OFFSET,
163+
VDSO_TIMENS_PAGE_OFFSET,
164+
VDSO_NR_PAGES
165+
};
166+
167+
#endif /* CONFIG_GENERIC_VDSO_DATA_STORE */
168+
151169
/*
152170
* The generic vDSO implementation requires that gettimeofday.h
153171
* provides:
@@ -164,6 +182,13 @@ union vdso_data_store {
164182
#include <asm/vdso/gettimeofday.h>
165183
#endif /* ENABLE_COMPAT_VDSO */
166184

185+
#else /* !__ASSEMBLY__ */
186+
187+
#define VDSO_VVAR_SYMS \
188+
PROVIDE(vdso_u_data = . - __VDSO_PAGES * PAGE_SIZE); \
189+
PROVIDE(vdso_u_time_data = vdso_u_data); \
190+
191+
167192
#endif /* !__ASSEMBLY__ */
168193

169194
#endif /* __VDSO_DATAPAGE_H */

kernel/time/vsyscall.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ static inline void update_vdso_data(struct vdso_data *vdata,
7777

7878
void update_vsyscall(struct timekeeper *tk)
7979
{
80-
struct vdso_data *vdata = __arch_get_k_vdso_data();
80+
struct vdso_data *vdata = vdso_k_time_data;
8181
struct vdso_timestamp *vdso_ts;
8282
s32 clock_mode;
8383
u64 nsec;
@@ -128,7 +128,7 @@ void update_vsyscall(struct timekeeper *tk)
128128

129129
void update_vsyscall_tz(void)
130130
{
131-
struct vdso_data *vdata = __arch_get_k_vdso_data();
131+
struct vdso_data *vdata = vdso_k_time_data;
132132

133133
vdata[CS_HRES_COARSE].tz_minuteswest = sys_tz.tz_minuteswest;
134134
vdata[CS_HRES_COARSE].tz_dsttime = sys_tz.tz_dsttime;
@@ -150,7 +150,7 @@ void update_vsyscall_tz(void)
150150
*/
151151
unsigned long vdso_update_begin(void)
152152
{
153-
struct vdso_data *vdata = __arch_get_k_vdso_data();
153+
struct vdso_data *vdata = vdso_k_time_data;
154154
unsigned long flags = timekeeper_lock_irqsave();
155155

156156
vdso_write_begin(vdata);
@@ -167,7 +167,7 @@ unsigned long vdso_update_begin(void)
167167
*/
168168
void vdso_update_end(unsigned long flags)
169169
{
170-
struct vdso_data *vdata = __arch_get_k_vdso_data();
170+
struct vdso_data *vdata = vdso_k_time_data;
171171

172172
vdso_write_end(vdata);
173173
__arch_sync_vdso_data(vdata);

lib/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ endif
132132
obj-$(CONFIG_DEBUG_INFO_REDUCED) += debug_info.o
133133
CFLAGS_debug_info.o += $(call cc-option, -femit-struct-debug-detailed=any)
134134

135-
obj-y += math/ crypto/
135+
obj-y += math/ crypto/ vdso/
136136

137137
obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
138138
obj-$(CONFIG_HAS_IOMEM) += iomap_copy.o devres.o

lib/vdso/Kconfig

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,8 @@ config VDSO_GETRANDOM
4343
bool
4444
help
4545
Selected by architectures that support vDSO getrandom().
46+
47+
config GENERIC_VDSO_DATA_STORE
48+
bool
49+
help
50+
Selected by architectures that use the generic vDSO data store.

lib/vdso/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# SPDX-License-Identifier: GPL-2.0-only
2+
3+
obj-$(CONFIG_GENERIC_VDSO_DATA_STORE) += datastore.o

lib/vdso/datastore.c

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
3+
#include <linux/linkage.h>
4+
#include <linux/mmap_lock.h>
5+
#include <linux/mm.h>
6+
#include <linux/time_namespace.h>
7+
#include <linux/types.h>
8+
#include <linux/vdso_datastore.h>
9+
#include <vdso/datapage.h>
10+
11+
/*
12+
* The vDSO data page.
13+
*/
14+
#ifdef CONFIG_HAVE_GENERIC_VDSO
15+
static union vdso_data_store vdso_time_data_store __page_aligned_data;
16+
struct vdso_time_data *vdso_k_time_data = vdso_time_data_store.data;
17+
static_assert(sizeof(vdso_time_data_store) == PAGE_SIZE);
18+
#endif /* CONFIG_HAVE_GENERIC_VDSO */
19+
20+
static vm_fault_t vvar_fault(const struct vm_special_mapping *sm,
21+
struct vm_area_struct *vma, struct vm_fault *vmf)
22+
{
23+
struct page *timens_page = find_timens_vvar_page(vma);
24+
unsigned long addr, pfn;
25+
vm_fault_t err;
26+
27+
switch (vmf->pgoff) {
28+
case VDSO_TIME_PAGE_OFFSET:
29+
if (!IS_ENABLED(CONFIG_HAVE_GENERIC_VDSO))
30+
return VM_FAULT_SIGBUS;
31+
pfn = __phys_to_pfn(__pa_symbol(vdso_k_time_data));
32+
if (timens_page) {
33+
/*
34+
* Fault in VVAR page too, since it will be accessed
35+
* to get clock data anyway.
36+
*/
37+
addr = vmf->address + VDSO_TIMENS_PAGE_OFFSET * PAGE_SIZE;
38+
err = vmf_insert_pfn(vma, addr, pfn);
39+
if (unlikely(err & VM_FAULT_ERROR))
40+
return err;
41+
pfn = page_to_pfn(timens_page);
42+
}
43+
break;
44+
case VDSO_TIMENS_PAGE_OFFSET:
45+
/*
46+
* If a task belongs to a time namespace then a namespace
47+
* specific VVAR is mapped with the VVAR_DATA_PAGE_OFFSET and
48+
* the real VVAR page is mapped with the VVAR_TIMENS_PAGE_OFFSET
49+
* offset.
50+
* See also the comment near timens_setup_vdso_data().
51+
*/
52+
if (!IS_ENABLED(CONFIG_TIME_NS) || !timens_page)
53+
return VM_FAULT_SIGBUS;
54+
pfn = __phys_to_pfn(__pa_symbol(vdso_k_time_data));
55+
break;
56+
default:
57+
return VM_FAULT_SIGBUS;
58+
}
59+
60+
return vmf_insert_pfn(vma, vmf->address, pfn);
61+
}
62+
63+
const struct vm_special_mapping vdso_vvar_mapping = {
64+
.name = "[vvar]",
65+
.fault = vvar_fault,
66+
};
67+
68+
struct vm_area_struct *vdso_install_vvar_mapping(struct mm_struct *mm, unsigned long addr)
69+
{
70+
return _install_special_mapping(mm, addr, VDSO_NR_PAGES * PAGE_SIZE,
71+
VM_READ | VM_MAYREAD | VM_IO | VM_DONTDUMP | VM_PFNMAP,
72+
&vdso_vvar_mapping);
73+
}
74+
75+
#ifdef CONFIG_TIME_NS
76+
/*
77+
* The vvar page layout depends on whether a task belongs to the root or
78+
* non-root time namespace. Whenever a task changes its namespace, the VVAR
79+
* page tables are cleared and then they will be re-faulted with a
80+
* corresponding layout.
81+
* See also the comment near timens_setup_vdso_data() for details.
82+
*/
83+
int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
84+
{
85+
struct mm_struct *mm = task->mm;
86+
struct vm_area_struct *vma;
87+
VMA_ITERATOR(vmi, mm, 0);
88+
89+
mmap_read_lock(mm);
90+
for_each_vma(vmi, vma) {
91+
if (vma_is_special_mapping(vma, &vdso_vvar_mapping))
92+
zap_vma_pages(vma);
93+
}
94+
mmap_read_unlock(mm);
95+
96+
return 0;
97+
}
98+
99+
struct vdso_time_data *arch_get_vdso_data(void *vvar_page)
100+
{
101+
return (struct vdso_time_data *)vvar_page;
102+
}
103+
#endif

lib/vdso/gettimeofday.c

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
#include <vdso/datapage.h>
66
#include <vdso/helpers.h>
77

8+
/* Bring in default accessors */
9+
#include <vdso/vsyscall.h>
10+
811
#ifndef vdso_calc_ns
912

1013
#ifdef VDSO_DELTA_NOMASK
@@ -69,6 +72,16 @@ static inline bool vdso_cycles_ok(u64 cycles)
6972
#endif
7073

7174
#ifdef CONFIG_TIME_NS
75+
76+
#ifdef CONFIG_GENERIC_VDSO_DATA_STORE
77+
static __always_inline
78+
const struct vdso_time_data *__arch_get_vdso_u_timens_data(const struct vdso_time_data *vd)
79+
{
80+
return (void *)vd + PAGE_SIZE;
81+
}
82+
#define __arch_get_timens_vdso_data(vd) __arch_get_vdso_u_timens_data(vd)
83+
#endif /* CONFIG_GENERIC_VDSO_DATA_STORE */
84+
7285
static __always_inline int do_hres_timens(const struct vdso_data *vdns, clockid_t clk,
7386
struct __kernel_timespec *ts)
7487
{
@@ -282,7 +295,7 @@ __cvdso_clock_gettime_data(const struct vdso_data *vd, clockid_t clock,
282295
static __maybe_unused int
283296
__cvdso_clock_gettime(clockid_t clock, struct __kernel_timespec *ts)
284297
{
285-
return __cvdso_clock_gettime_data(__arch_get_vdso_data(), clock, ts);
298+
return __cvdso_clock_gettime_data(__arch_get_vdso_u_time_data(), clock, ts);
286299
}
287300

288301
#ifdef BUILD_VDSO32
@@ -308,7 +321,7 @@ __cvdso_clock_gettime32_data(const struct vdso_data *vd, clockid_t clock,
308321
static __maybe_unused int
309322
__cvdso_clock_gettime32(clockid_t clock, struct old_timespec32 *res)
310323
{
311-
return __cvdso_clock_gettime32_data(__arch_get_vdso_data(), clock, res);
324+
return __cvdso_clock_gettime32_data(__arch_get_vdso_u_time_data(), clock, res);
312325
}
313326
#endif /* BUILD_VDSO32 */
314327

@@ -342,7 +355,7 @@ __cvdso_gettimeofday_data(const struct vdso_data *vd,
342355
static __maybe_unused int
343356
__cvdso_gettimeofday(struct __kernel_old_timeval *tv, struct timezone *tz)
344357
{
345-
return __cvdso_gettimeofday_data(__arch_get_vdso_data(), tv, tz);
358+
return __cvdso_gettimeofday_data(__arch_get_vdso_u_time_data(), tv, tz);
346359
}
347360

348361
#ifdef VDSO_HAS_TIME
@@ -365,7 +378,7 @@ __cvdso_time_data(const struct vdso_data *vd, __kernel_old_time_t *time)
365378

366379
static __maybe_unused __kernel_old_time_t __cvdso_time(__kernel_old_time_t *time)
367380
{
368-
return __cvdso_time_data(__arch_get_vdso_data(), time);
381+
return __cvdso_time_data(__arch_get_vdso_u_time_data(), time);
369382
}
370383
#endif /* VDSO_HAS_TIME */
371384

@@ -425,7 +438,7 @@ int __cvdso_clock_getres_data(const struct vdso_data *vd, clockid_t clock,
425438
static __maybe_unused
426439
int __cvdso_clock_getres(clockid_t clock, struct __kernel_timespec *res)
427440
{
428-
return __cvdso_clock_getres_data(__arch_get_vdso_data(), clock, res);
441+
return __cvdso_clock_getres_data(__arch_get_vdso_u_time_data(), clock, res);
429442
}
430443

431444
#ifdef BUILD_VDSO32
@@ -451,7 +464,7 @@ __cvdso_clock_getres_time32_data(const struct vdso_data *vd, clockid_t clock,
451464
static __maybe_unused int
452465
__cvdso_clock_getres_time32(clockid_t clock, struct old_timespec32 *res)
453466
{
454-
return __cvdso_clock_getres_time32_data(__arch_get_vdso_data(),
467+
return __cvdso_clock_getres_time32_data(__arch_get_vdso_u_time_data(),
455468
clock, res);
456469
}
457470
#endif /* BUILD_VDSO32 */

0 commit comments

Comments
 (0)