Loop Timing Witness
Native controller and runtime interfaces; hardware qualification pending
Loading...
Searching...
No Matches
run_policy.h
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2// Commercial license available
3// © Concepts 1996–2026 Miroslav Šotek. All rights reserved.
4// © Code 2020–2026 Miroslav Šotek. All rights reserved.
5// ORCID: 0009-0009-3560-0851
6// Contact: www.anulum.li | protoscience@anulum.li
7// Loop Timing Witness — actual Linux run scheduling policy
8
12
13#ifndef WITNESS_LINUX_RUN_POLICY_H
14#define WITNESS_LINUX_RUN_POLICY_H
15#include "../run_options.h"
16#include "../refusal.h"
17#include <sched.h>
18#include <sys/resource.h>
19#include <cerrno>
20#include <cstdio>
21#include <cstdlib>
22#include <memory>
23#include <string>
24#include <vector>
25
26namespace witness {
28struct HostPolicy {
32 int nice;
33 std::vector<int> cpus;
34};
35
39 void operator()(cpu_set_t *mask) const noexcept { CPU_FREE(mask); }
40};
41
44 HostPolicy result{};
45 result.scheduler = sched_getscheduler(0);
46 sched_param parameter{};
47 if (result.scheduler < 0 || sched_getparam(0, &parameter) != 0)
48 throw witness::RuntimeRefused("cannot read Linux scheduler policy");
49 result.priority = parameter.sched_priority;
50 errno = 0;
51 result.nice = getpriority(PRIO_PROCESS, 0);
52 if (errno)
53 throw witness::RuntimeRefused("cannot read Linux nice value");
54 result.observed_cpu = sched_getcpu();
55 if (result.observed_cpu < 0)
56 throw witness::RuntimeRefused("cannot read current Linux CPU");
57 for (int capacity = CPU_SETSIZE; capacity <= (1 << 20); capacity *= 2) {
58 const auto count = static_cast<std::size_t>(capacity);
59 const auto size = CPU_ALLOC_SIZE(count);
60 std::unique_ptr<cpu_set_t, FreeCpuMask> mask(CPU_ALLOC(count));
61 if (!mask)
62 throw witness::RuntimeRefused("cannot allocate Linux CPU affinity mask");
63 CPU_ZERO_S(size, mask.get());
64 if (sched_getaffinity(0, size, mask.get()) == 0) {
65 for (int cpu = 0; cpu < capacity; ++cpu)
66 if (CPU_ISSET_S(static_cast<std::size_t>(cpu), size, mask.get()))
67 result.cpus.push_back(cpu);
68 return result;
69 }
70 if (errno != EINVAL)
71 throw witness::RuntimeRefused("cannot read Linux CPU affinity");
72 }
73 throw witness::RuntimeRefused("Linux CPU affinity exceeds supported mask size");
74}
75
77inline HostPolicy apply_host_policy(const RunOptions &options) {
78 validate_run_options(options);
79 const auto inherited = read_host_policy();
80 if (options.cpu >= 0) {
81 bool allowed = false;
82 for (int cpu : inherited.cpus)
83 if (cpu == options.cpu)
84 allowed = true;
85 if (!allowed)
86 throw witness::RuntimeRefused("requested CPU is outside inherited affinity");
87 }
88 if (options.scheduler == 1 && (options.priority < sched_get_priority_min(SCHED_FIFO) ||
89 options.priority > sched_get_priority_max(SCHED_FIFO)))
90 throw witness::RuntimeRefused("FIFO priority is outside Linux scheduler bounds");
91 if (options.cpu >= 0) {
92 const auto count = static_cast<std::size_t>(options.cpu) + 1;
93 const auto size = CPU_ALLOC_SIZE(count);
94 std::unique_ptr<cpu_set_t, FreeCpuMask> mask(CPU_ALLOC(count));
95 if (!mask)
96 throw witness::RuntimeRefused("cannot allocate requested Linux CPU mask");
97 CPU_ZERO_S(size, mask.get());
98 CPU_SET_S(static_cast<std::size_t>(options.cpu), size, mask.get());
99 if (sched_setaffinity(0, size, mask.get()) != 0)
100 throw witness::RuntimeRefused("cannot set Linux CPU affinity (errno " +
101 std::to_string(errno) + ")");
102 }
103 if (options.scheduler >= 0) {
104 sched_param parameter{};
105 parameter.sched_priority = options.scheduler == 1 ? options.priority : 0;
106 if (sched_setscheduler(0, options.scheduler == 1 ? SCHED_FIFO : SCHED_OTHER, &parameter) !=
107 0)
108 throw witness::RuntimeRefused("cannot set Linux scheduler (errno " +
109 std::to_string(errno) + ")");
110 }
111 const auto actual = read_host_policy();
112 if ((options.cpu >= 0 && (actual.cpus.size() != 1 || actual.cpus[0] != options.cpu)) ||
113 (options.scheduler >= 0 &&
114 actual.scheduler != (options.scheduler == 1 ? SCHED_FIFO : SCHED_OTHER)) ||
115 (options.scheduler >= 0 &&
116 actual.priority != (options.scheduler == 1 ? options.priority : 0)))
117 throw witness::RuntimeRefused("Linux scheduler/affinity readback differs from request");
118 return actual;
119}
120
122inline bool write_host_policy(std::FILE *file, const RunOptions &options,
123 const HostPolicy &actual) {
124 bool failed =
125 std::fprintf(file,
126 " \"host_policy\": {\"requested_cpu\": %d, \"requested_scheduler\": %d, "
127 "\"requested_priority\": %d, \"scheduler\": %d, \"priority\": %d, "
128 "\"observed_cpu\": %d, \"nice\": %d, \"affinity_cpus\": [",
129 options.cpu, options.scheduler, options.priority, actual.scheduler,
130 actual.priority, actual.observed_cpu, actual.nice) < 0;
131 for (std::size_t index = 0; index < actual.cpus.size(); ++index)
132 if (std::fprintf(file, "%s%d", index ? ", " : "", actual.cpus[index]) < 0)
133 failed = true;
134 return std::fprintf(file, "]}\n") < 0 || failed;
135}
136} // namespace witness
152
153#endif
void validate_run_options(const RunOptions &options)
Definition run_options.h:32
bool write_host_policy(std::FILE *file, const RunOptions &options, const HostPolicy &actual)
Definition run_policy.h:122
HostPolicy apply_host_policy(const RunOptions &options)
Definition run_policy.h:77
HostPolicy read_host_policy()
Definition run_policy.h:43
void operator()(cpu_set_t *mask) const noexcept
Definition run_policy.h:39
std::vector< int > cpus
Definition run_policy.h:33