projects/linux·blob master · refs

157 lines · 4.1 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Author: Andrei Vagin <[email protected]>
4 * Author: Dmitry Safonov <[email protected]>
5 */
6
7#include <linux/cleanup.h>
8#include <linux/mm.h>
9#include <linux/time_namespace.h>
10#include <linux/time.h>
11#include <linux/vdso_datastore.h>
12
13#include <vdso/clocksource.h>
14#include <vdso/datapage.h>
15
16#include "namespace_internal.h"
17
18static struct timens_offset offset_from_ts(struct timespec64 off)
19{
20 struct timens_offset ret;
21
22 ret.sec = off.tv_sec;
23 ret.nsec = off.tv_nsec;
24
25 return ret;
26}
27
28/*
29 * A time namespace VVAR page has the same layout as the VVAR page which
30 * contains the system wide VDSO data.
31 *
32 * For a normal task the VVAR pages are installed in the normal ordering:
33 * VVAR
34 * PVCLOCK
35 * HVCLOCK
36 * TIMENS <- Not really required
37 *
38 * Now for a timens task the pages are installed in the following order:
39 * TIMENS
40 * PVCLOCK
41 * HVCLOCK
42 * VVAR
43 *
44 * The check for vdso_clock->clock_mode is in the unlikely path of
45 * the seq begin magic. So for the non-timens case most of the time
46 * 'seq' is even, so the branch is not taken.
47 *
48 * If 'seq' is odd, i.e. a concurrent update is in progress, the extra check
49 * for vdso_clock->clock_mode is a non-issue. The task is spin waiting for the
50 * update to finish and for 'seq' to become even anyway.
51 *
52 * Timens page has vdso_clock->clock_mode set to VDSO_CLOCKMODE_TIMENS which
53 * enforces the time namespace handling path.
54 */
55static void timens_setup_vdso_clock_data(struct vdso_clock *vc,
56 struct time_namespace *ns)
57{
58 struct timens_offset *offset = vc->offset;
59 struct timens_offset monotonic = offset_from_ts(ns->offsets.monotonic);
60 struct timens_offset boottime = offset_from_ts(ns->offsets.boottime);
61
62 vc->seq = 1;
63 vc->clock_mode = VDSO_CLOCKMODE_TIMENS;
64 offset[CLOCK_MONOTONIC] = monotonic;
65 offset[CLOCK_MONOTONIC_RAW] = monotonic;
66 offset[CLOCK_MONOTONIC_COARSE] = monotonic;
67 offset[CLOCK_BOOTTIME] = boottime;
68 offset[CLOCK_BOOTTIME_ALARM] = boottime;
69}
70
71struct page *find_timens_vvar_page(struct vm_area_struct *vma)
72{
73 if (likely(vma->vm_mm == current->mm))
74 return current->nsproxy->time_ns->vvar_page;
75
76 /*
77 * vvar_fault() protects this from being called through remote interfaces like
78 * /proc/$pid/mem or process_vm_{readv,writev}().
79 */
80
81 WARN(1, "vvar_page accessed remotely");
82
83 return NULL;
84}
85
86static void timens_set_vvar_page(struct task_struct *task,
87 struct time_namespace *ns)
88{
89 struct vdso_time_data *vdata;
90 struct vdso_clock *vc;
91 unsigned int i;
92
93 if (ns == &init_time_ns)
94 return;
95
96 /* Fast-path, taken by every task in namespace except the first. */
97 if (likely(ns->frozen_offsets))
98 return;
99
100 guard(mutex)(&timens_offset_lock);
101 /* Nothing to-do: vvar_page has been already initialized. */
102 if (ns->frozen_offsets)
103 return;
104
105 ns->frozen_offsets = true;
106 vdata = page_address(ns->vvar_page);
107 vc = vdata->clock_data;
108
109 for (i = 0; i < CS_BASES; i++)
110 timens_setup_vdso_clock_data(&vc[i], ns);
111
112 if (IS_ENABLED(CONFIG_POSIX_AUX_CLOCKS)) {
113 for (i = 0; i < ARRAY_SIZE(vdata->aux_clock_data); i++)
114 timens_setup_vdso_clock_data(&vdata->aux_clock_data[i], ns);
115 }
116}
117
118/*
119 * The vvar page layout depends on whether a task belongs to the root or
120 * non-root time namespace. Whenever a task changes its namespace, the VVAR
121 * page tables are cleared and then they will be re-faulted with a
122 * corresponding layout.
123 * See also the comment near timens_setup_vdso_clock_data() for details.
124 */
125static int vdso_join_timens(struct task_struct *task, struct time_namespace *ns)
126{
127 struct mm_struct *mm = task->mm;
128 struct vm_area_struct *vma;
129 VMA_ITERATOR(vmi, mm, 0);
130
131 guard(mmap_read_lock)(mm);
132 for_each_vma(vmi, vma) {
133 if (vma_is_special_mapping(vma, &vdso_vvar_mapping))
134 zap_vma(vma);
135 }
136 return 0;
137}
138
139void timens_commit(struct task_struct *tsk, struct time_namespace *ns)
140{
141 timens_set_vvar_page(tsk, ns);
142 vdso_join_timens(tsk, ns);
143}
144
145int timens_vdso_alloc_vvar_page(struct time_namespace *ns)
146{
147 ns->vvar_page = alloc_page(GFP_KERNEL_ACCOUNT | __GFP_ZERO);
148 if (!ns->vvar_page)
149 return -ENOMEM;
150
151 return 0;
152}
153
154void timens_vdso_free_vvar_page(struct time_namespace *ns)
155{
156 __free_page(ns->vvar_page);
157}