blob: 94c9c71f3ec0e208dae95adf45843fde8e56dac7 [file] [log] [blame]
Austin Schuh608514f2022-12-30 15:51:30 -08001#include <linux/securebits.h>
2#include <pwd.h>
Stephan Pleinesf581a072024-05-23 20:59:27 -07003#include <sched.h>
4#include <stdint.h>
Austin Schuh608514f2022-12-30 15:51:30 -08005#include <sys/prctl.h>
Austin Schuha63163e2023-02-24 16:18:18 -08006#include <sys/resource.h>
Stephan Pleinesf581a072024-05-23 20:59:27 -07007#include <unistd.h>
Austin Schuh608514f2022-12-30 15:51:30 -08008
Stephan Pleinesf581a072024-05-23 20:59:27 -07009#include <algorithm>
10#include <map>
11#include <optional>
12#include <ostream>
13#include <string>
14#include <string_view>
15#include <utility>
16#include <vector>
17
18#include "absl/strings/str_cat.h"
19#include "flatbuffers/buffer.h"
20#include "flatbuffers/string.h"
21#include "flatbuffers/vector.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070022#include "gflags/gflags.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070023#include "glog/logging.h"
Philipp Schrader790cb542023-07-05 21:06:52 -070024
Stephan Pleinesf581a072024-05-23 20:59:27 -070025#include "aos/configuration.h"
26#include "aos/events/event_loop.h"
Austin Schuh608514f2022-12-30 15:51:30 -080027#include "aos/events/shm_event_loop.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070028#include "aos/flatbuffers.h"
Austin Schuh608514f2022-12-30 15:51:30 -080029#include "aos/init.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070030#include "aos/json_to_flatbuffer.h"
Austin Schuh608514f2022-12-30 15:51:30 -080031#include "aos/starter/irq_affinity_lib.h"
32#include "aos/starter/kthread_generated.h"
Stephan Pleinesf581a072024-05-23 20:59:27 -070033#include "aos/util/file.h"
Austin Schuh608514f2022-12-30 15:51:30 -080034#include "aos/util/top.h"
Austin Schuh608514f2022-12-30 15:51:30 -080035
36DEFINE_string(config, "aos_config.json", "File path of aos configuration");
37
38DEFINE_string(user, "",
39 "Starter runs as though this user ran a SUID binary if set.");
Alexander Yeee61cac32023-02-11 19:40:40 -080040DEFINE_string(irq_config, "rockpi_config.json",
41 "File path of rockpi configuration");
Austin Schuh608514f2022-12-30 15:51:30 -080042
43namespace aos {
44
45cpu_set_t AffinityFromFlatbuffer(const flatbuffers::Vector<uint8_t> *v) {
46 cpu_set_t affinity;
47 CPU_ZERO(&affinity);
48 if (v == nullptr) {
49 for (int i = 0; i < CPU_SETSIZE; ++i) {
50 CPU_SET(i, &affinity);
51 }
52 } else {
53 for (uint8_t cpu : *v) {
54 CPU_SET(cpu, &affinity);
55 }
56 }
57 return affinity;
58}
59
60// Class to hold the configuration for an IRQ.
61struct ParsedIrqConfig {
62 std::string name;
63 cpu_set_t affinity;
64
65 void ConfigureIrq(int interrupt_number) const {
66 const std::string affinity_filename =
67 absl::StrCat("/proc/irq/", interrupt_number, "/smp_affinity");
68 const std::string contents = util::ReadFileToStringOrDie(affinity_filename);
69
70 std::string new_contents = std::string(contents.size() - 1, '0');
71
72 // Contents will be a padded string which is the size of the number of
73 // IRQs.
74 CHECK(!(CPU_SETSIZE & 0xf));
75 for (size_t i = 0; i < CPU_SETSIZE; i += 4) {
76 if (i / 4 >= new_contents.size()) {
77 break;
78 }
79 uint8_t byte = 0;
80 if (CPU_ISSET(i + 0, &affinity)) {
81 byte |= 1;
82 }
83 if (CPU_ISSET(i + 1, &affinity)) {
84 byte |= 2;
85 }
86 if (CPU_ISSET(i + 2, &affinity)) {
87 byte |= 4;
88 }
89 if (CPU_ISSET(i + 3, &affinity)) {
90 byte |= 8;
91 }
92 if (byte < 10) {
93 new_contents[new_contents.size() - 1 - i / 4] = '0' + byte;
94 } else {
95 new_contents[new_contents.size() - 1 - i / 4] = 'a' + (byte - 10);
96 }
97 }
98
99 if (contents != new_contents) {
100 util::WriteStringToFileOrDie(affinity_filename, new_contents);
101 }
102 }
103};
104
105// Class to hold the configuration for a kthread.
106struct ParsedKThreadConfig {
107 bool full_match = false;
108 std::string prefix;
109 std::string postfix;
110 starter::Scheduler scheduler;
111 int priority;
Austin Schuha63163e2023-02-24 16:18:18 -0800112 std::optional<int> nice;
Austin Schuh608514f2022-12-30 15:51:30 -0800113 cpu_set_t affinity;
114
115 bool Matches(std::string_view candidate) const {
116 if (full_match) {
117 return candidate == prefix;
118 } else {
119 if (candidate.size() < prefix.size() + postfix.size()) {
120 return false;
121 }
122 if (candidate.substr(0, prefix.size()) != prefix) {
123 return false;
124 }
125 if (candidate.substr(candidate.size() - postfix.size(), postfix.size()) !=
126 postfix) {
127 return false;
128 }
129 return true;
130 }
131 }
132
Austin Schuh19731b02024-03-02 16:53:19 -0800133 void ConfigurePid(pid_t pid, std::string_view name) const {
Austin Schuha63163e2023-02-24 16:18:18 -0800134 struct sched_param param;
135 param.sched_priority = priority;
136 int new_scheduler;
137 switch (scheduler) {
138 case starter::Scheduler::SCHEDULER_OTHER:
139 new_scheduler = SCHED_OTHER;
140 break;
141 case starter::Scheduler::SCHEDULER_RR:
142 new_scheduler = SCHED_RR;
143 break;
144 case starter::Scheduler::SCHEDULER_FIFO:
145 new_scheduler = SCHED_FIFO;
146 break;
147 default:
148 LOG(FATAL) << "Unknown scheduler";
Austin Schuh608514f2022-12-30 15:51:30 -0800149 }
Austin Schuh19731b02024-03-02 16:53:19 -0800150 PCHECK(sched_setscheduler(pid, new_scheduler, &param) == 0)
151 << ", Failed to set " << name << "(" << pid << ") to "
152 << (new_scheduler == SCHED_OTHER
153 ? "SCHED_OTHER"
154 : (new_scheduler == SCHED_RR ? "SCHED_RR" : "SCHED_FIFO"));
Austin Schuha63163e2023-02-24 16:18:18 -0800155
156 if (scheduler == starter::Scheduler::SCHEDULER_OTHER && nice.has_value()) {
157 PCHECK(setpriority(PRIO_PROCESS, pid, *nice) == 0)
158 << ": Failed to set priority";
159 }
160
Austin Schuh608514f2022-12-30 15:51:30 -0800161 PCHECK(sched_setaffinity(pid, sizeof(affinity), &affinity) == 0);
162 }
163};
164
165// TODO(austin): Clean this up a bit, and maybe we can add some tests.
166class IrqAffinity {
167 public:
168 IrqAffinity(
169 EventLoop *event_loop,
170 const aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
171 &irq_affinity_config)
Maxwell Gumleyb27245f2024-04-11 15:46:22 -0600172 : top_(event_loop, aos::util::Top::TrackThreadsMode::kDisabled,
173 aos::util::Top::TrackPerThreadInfoMode::kDisabled) {
Austin Schuh608514f2022-12-30 15:51:30 -0800174 if (irq_affinity_config.message().has_kthreads()) {
Austin Schuh4ec20622023-03-11 15:11:50 -0800175 PopulateThreads(irq_affinity_config.message().kthreads(), &kthreads_);
176 }
177 if (irq_affinity_config.message().has_threads()) {
178 PopulateThreads(irq_affinity_config.message().threads(), &threads_);
Austin Schuh608514f2022-12-30 15:51:30 -0800179 }
180
181 if (irq_affinity_config.message().has_irqs()) {
182 irqs_.reserve(irq_affinity_config.message().irqs()->size());
183 for (const starter::IrqConfig *irq_config :
184 *irq_affinity_config.message().irqs()) {
185 CHECK(irq_config->has_name()) << ": Name required";
186 LOG(INFO) << "IRQ " << aos::FlatbufferToJson(irq_config);
187 irqs_.push_back(ParsedIrqConfig{
188 .name = irq_config->name()->str(),
189 .affinity = AffinityFromFlatbuffer(irq_config->affinity()),
190 });
191 }
192 }
193
194 top_.set_track_top_processes(true);
195 top_.set_on_reading_update([this]() {
196 for (const std::pair<const pid_t, util::Top::ProcessReadings> &reading :
197 top_.readings()) {
198 if (reading.second.kthread) {
199 for (const ParsedKThreadConfig &match : kthreads_) {
200 if (match.Matches(reading.second.name)) {
Austin Schuh19731b02024-03-02 16:53:19 -0800201 match.ConfigurePid(reading.first, reading.second.name);
Austin Schuh608514f2022-12-30 15:51:30 -0800202 break;
203 }
204 }
Austin Schuh4ec20622023-03-11 15:11:50 -0800205 } else {
206 for (const ParsedKThreadConfig &match : threads_) {
207 if (match.Matches(reading.second.name)) {
Austin Schuh19731b02024-03-02 16:53:19 -0800208 match.ConfigurePid(reading.first, reading.second.name);
Austin Schuh4ec20622023-03-11 15:11:50 -0800209 break;
210 }
211 }
Austin Schuh608514f2022-12-30 15:51:30 -0800212 }
213 }
214
215 interrupts_status_.Update();
216
217 for (const InterruptsStatus::InterruptState &state :
218 interrupts_status_.states()) {
219 for (const ParsedIrqConfig &match : irqs_) {
220 bool matched = false;
221 for (const std::string &action : state.actions) {
222 if (match.name == action) {
223 matched = true;
224 break;
225 }
226 }
227 if (matched) {
228 match.ConfigureIrq(state.interrupt_number);
229 }
230 }
231 }
232 });
233 }
234
235 private:
Austin Schuh4ec20622023-03-11 15:11:50 -0800236 void PopulateThreads(
237 const flatbuffers::Vector<flatbuffers::Offset<starter::KthreadConfig>>
238 *threads_config,
239 std::vector<ParsedKThreadConfig> *threads) {
240 threads->reserve(threads_config->size());
241 for (const starter::KthreadConfig *kthread_config : *threads_config) {
242 LOG(INFO) << "Kthread " << aos::FlatbufferToJson(kthread_config);
243 CHECK(kthread_config->has_name()) << ": Name required";
244 const size_t star_position =
245 kthread_config->name()->string_view().find('*');
246 const bool has_star = star_position != std::string_view::npos;
247
248 threads->push_back(ParsedKThreadConfig{
249 .full_match = !has_star,
250 .prefix = std::string(
251 !has_star ? kthread_config->name()->string_view()
252 : kthread_config->name()->string_view().substr(
253 0, star_position)),
254 .postfix = std::string(
255 !has_star ? ""
256 : kthread_config->name()->string_view().substr(
257 star_position + 1)),
258 .scheduler = kthread_config->scheduler(),
259 .priority = kthread_config->priority(),
260 .nice = kthread_config->nice(),
261 .affinity = AffinityFromFlatbuffer(kthread_config->affinity()),
262 });
263 }
264 }
265
Austin Schuh608514f2022-12-30 15:51:30 -0800266 util::Top top_;
267
268 // TODO(austin): Publish message with everything in it.
269 // TODO(austin): Make Top report out affinity + priority + scheduler for
270 // posterity.
271
272 std::vector<ParsedKThreadConfig> kthreads_;
Austin Schuh4ec20622023-03-11 15:11:50 -0800273 std::vector<ParsedKThreadConfig> threads_;
Austin Schuh608514f2022-12-30 15:51:30 -0800274 std::vector<ParsedIrqConfig> irqs_;
275
276 InterruptsStatus interrupts_status_;
277};
278
279} // namespace aos
280
281int main(int argc, char **argv) {
282 aos::InitGoogle(&argc, &argv);
283
284 if (!FLAGS_user.empty()) {
285 // Maintain root permissions as we switch to become the user so we can
286 // actually manipulate priorities.
287 PCHECK(prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NOROOT) ==
288 0);
289
290 uid_t uid;
291 uid_t gid;
292 {
293 struct passwd *user_data = getpwnam(FLAGS_user.c_str());
294 if (user_data != nullptr) {
295 uid = user_data->pw_uid;
296 gid = user_data->pw_gid;
297 } else {
298 LOG(FATAL) << "Could not find user " << FLAGS_user;
299 return 1;
300 }
301 }
302 // Change the real and effective IDs to the user we're running as. The
303 // effective IDs mean files we access (like shared memory) will happen as
304 // that user. The real IDs allow child processes with an different effective
305 // ID to still participate in signal sending/receiving.
306 constexpr int kUnchanged = -1;
307 if (setresgid(/* ruid */ gid, /* euid */ gid,
308 /* suid */ kUnchanged) != 0) {
309 PLOG(FATAL) << "Failed to change GID to " << FLAGS_user;
310 }
311
312 if (setresuid(/* ruid */ uid, /* euid */ uid,
313 /* suid */ kUnchanged) != 0) {
314 PLOG(FATAL) << "Failed to change UID to " << FLAGS_user;
315 }
316 }
317
318 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
319 aos::configuration::ReadConfig(FLAGS_config);
320
Austin Schuh608514f2022-12-30 15:51:30 -0800321 aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
322 irq_affinity_config =
Alexander Yeee61cac32023-02-11 19:40:40 -0800323 aos::JsonFileToFlatbuffer<aos::starter::IrqAffinityConfig>(
324 FLAGS_irq_config);
Austin Schuh608514f2022-12-30 15:51:30 -0800325
326 aos::ShmEventLoop shm_event_loop(&config.message());
327
328 aos::IrqAffinity irq_affinity(&shm_event_loop, irq_affinity_config);
329
330 shm_event_loop.Run();
331
332 return 0;
333}