| 1 | |
| 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 |
|
| 18 | static 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 |
|
| 55 | static 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 |
|
| 71 | struct 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 |
|
| 86 | static 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 |
|
| 97 | if (likely(ns->frozen_offsets))
|
| 98 | return;
|
| 99 |
|
| 100 | guard(mutex)(&timens_offset_lock);
|
| 101 |
|
| 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 |
|
| 125 | static 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 |
|
| 139 | void 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 |
|
| 145 | int 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 |
|
| 154 | void timens_vdso_free_vvar_page(struct time_namespace *ns)
|
| 155 | {
|
| 156 | __free_page(ns->vvar_page);
|
| 157 | }
|