blob: 7459e5e733dde58985e058928e5f7a61d82d0987 [file] [log] [blame]
Austin Schuh608514f2022-12-30 15:51:30 -08001#include <linux/securebits.h>
2#include <pwd.h>
3#include <sys/prctl.h>
Austin Schuha63163e2023-02-24 16:18:18 -08004#include <sys/resource.h>
Austin Schuh608514f2022-12-30 15:51:30 -08005#include <sys/types.h>
6
Philipp Schrader790cb542023-07-05 21:06:52 -07007#include "gflags/gflags.h"
8
Austin Schuh608514f2022-12-30 15:51:30 -08009#include "aos/events/shm_event_loop.h"
10#include "aos/init.h"
11#include "aos/starter/irq_affinity_lib.h"
12#include "aos/starter/kthread_generated.h"
13#include "aos/util/top.h"
Austin Schuh608514f2022-12-30 15:51:30 -080014
15DEFINE_string(config, "aos_config.json", "File path of aos configuration");
16
17DEFINE_string(user, "",
18 "Starter runs as though this user ran a SUID binary if set.");
Alexander Yeee61cac32023-02-11 19:40:40 -080019DEFINE_string(irq_config, "rockpi_config.json",
20 "File path of rockpi configuration");
Austin Schuh608514f2022-12-30 15:51:30 -080021
22namespace aos {
23
24cpu_set_t AffinityFromFlatbuffer(const flatbuffers::Vector<uint8_t> *v) {
25 cpu_set_t affinity;
26 CPU_ZERO(&affinity);
27 if (v == nullptr) {
28 for (int i = 0; i < CPU_SETSIZE; ++i) {
29 CPU_SET(i, &affinity);
30 }
31 } else {
32 for (uint8_t cpu : *v) {
33 CPU_SET(cpu, &affinity);
34 }
35 }
36 return affinity;
37}
38
39// Class to hold the configuration for an IRQ.
40struct ParsedIrqConfig {
41 std::string name;
42 cpu_set_t affinity;
43
44 void ConfigureIrq(int interrupt_number) const {
45 const std::string affinity_filename =
46 absl::StrCat("/proc/irq/", interrupt_number, "/smp_affinity");
47 const std::string contents = util::ReadFileToStringOrDie(affinity_filename);
48
49 std::string new_contents = std::string(contents.size() - 1, '0');
50
51 // Contents will be a padded string which is the size of the number of
52 // IRQs.
53 CHECK(!(CPU_SETSIZE & 0xf));
54 for (size_t i = 0; i < CPU_SETSIZE; i += 4) {
55 if (i / 4 >= new_contents.size()) {
56 break;
57 }
58 uint8_t byte = 0;
59 if (CPU_ISSET(i + 0, &affinity)) {
60 byte |= 1;
61 }
62 if (CPU_ISSET(i + 1, &affinity)) {
63 byte |= 2;
64 }
65 if (CPU_ISSET(i + 2, &affinity)) {
66 byte |= 4;
67 }
68 if (CPU_ISSET(i + 3, &affinity)) {
69 byte |= 8;
70 }
71 if (byte < 10) {
72 new_contents[new_contents.size() - 1 - i / 4] = '0' + byte;
73 } else {
74 new_contents[new_contents.size() - 1 - i / 4] = 'a' + (byte - 10);
75 }
76 }
77
78 if (contents != new_contents) {
79 util::WriteStringToFileOrDie(affinity_filename, new_contents);
80 }
81 }
82};
83
84// Class to hold the configuration for a kthread.
85struct ParsedKThreadConfig {
86 bool full_match = false;
87 std::string prefix;
88 std::string postfix;
89 starter::Scheduler scheduler;
90 int priority;
Austin Schuha63163e2023-02-24 16:18:18 -080091 std::optional<int> nice;
Austin Schuh608514f2022-12-30 15:51:30 -080092 cpu_set_t affinity;
93
94 bool Matches(std::string_view candidate) const {
95 if (full_match) {
96 return candidate == prefix;
97 } else {
98 if (candidate.size() < prefix.size() + postfix.size()) {
99 return false;
100 }
101 if (candidate.substr(0, prefix.size()) != prefix) {
102 return false;
103 }
104 if (candidate.substr(candidate.size() - postfix.size(), postfix.size()) !=
105 postfix) {
106 return false;
107 }
108 return true;
109 }
110 }
111
Austin Schuh19731b02024-03-02 16:53:19 -0800112 void ConfigurePid(pid_t pid, std::string_view name) const {
Austin Schuha63163e2023-02-24 16:18:18 -0800113 struct sched_param param;
114 param.sched_priority = priority;
115 int new_scheduler;
116 switch (scheduler) {
117 case starter::Scheduler::SCHEDULER_OTHER:
118 new_scheduler = SCHED_OTHER;
119 break;
120 case starter::Scheduler::SCHEDULER_RR:
121 new_scheduler = SCHED_RR;
122 break;
123 case starter::Scheduler::SCHEDULER_FIFO:
124 new_scheduler = SCHED_FIFO;
125 break;
126 default:
127 LOG(FATAL) << "Unknown scheduler";
Austin Schuh608514f2022-12-30 15:51:30 -0800128 }
Austin Schuh19731b02024-03-02 16:53:19 -0800129 PCHECK(sched_setscheduler(pid, new_scheduler, &param) == 0)
130 << ", Failed to set " << name << "(" << pid << ") to "
131 << (new_scheduler == SCHED_OTHER
132 ? "SCHED_OTHER"
133 : (new_scheduler == SCHED_RR ? "SCHED_RR" : "SCHED_FIFO"));
Austin Schuha63163e2023-02-24 16:18:18 -0800134
135 if (scheduler == starter::Scheduler::SCHEDULER_OTHER && nice.has_value()) {
136 PCHECK(setpriority(PRIO_PROCESS, pid, *nice) == 0)
137 << ": Failed to set priority";
138 }
139
Austin Schuh608514f2022-12-30 15:51:30 -0800140 PCHECK(sched_setaffinity(pid, sizeof(affinity), &affinity) == 0);
141 }
142};
143
144// TODO(austin): Clean this up a bit, and maybe we can add some tests.
145class IrqAffinity {
146 public:
147 IrqAffinity(
148 EventLoop *event_loop,
149 const aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
150 &irq_affinity_config)
Maxwell Gumleyb27245f2024-04-11 15:46:22 -0600151 : top_(event_loop, aos::util::Top::TrackThreadsMode::kDisabled,
152 aos::util::Top::TrackPerThreadInfoMode::kDisabled) {
Austin Schuh608514f2022-12-30 15:51:30 -0800153 if (irq_affinity_config.message().has_kthreads()) {
Austin Schuh4ec20622023-03-11 15:11:50 -0800154 PopulateThreads(irq_affinity_config.message().kthreads(), &kthreads_);
155 }
156 if (irq_affinity_config.message().has_threads()) {
157 PopulateThreads(irq_affinity_config.message().threads(), &threads_);
Austin Schuh608514f2022-12-30 15:51:30 -0800158 }
159
160 if (irq_affinity_config.message().has_irqs()) {
161 irqs_.reserve(irq_affinity_config.message().irqs()->size());
162 for (const starter::IrqConfig *irq_config :
163 *irq_affinity_config.message().irqs()) {
164 CHECK(irq_config->has_name()) << ": Name required";
165 LOG(INFO) << "IRQ " << aos::FlatbufferToJson(irq_config);
166 irqs_.push_back(ParsedIrqConfig{
167 .name = irq_config->name()->str(),
168 .affinity = AffinityFromFlatbuffer(irq_config->affinity()),
169 });
170 }
171 }
172
173 top_.set_track_top_processes(true);
174 top_.set_on_reading_update([this]() {
175 for (const std::pair<const pid_t, util::Top::ProcessReadings> &reading :
176 top_.readings()) {
177 if (reading.second.kthread) {
178 for (const ParsedKThreadConfig &match : kthreads_) {
179 if (match.Matches(reading.second.name)) {
Austin Schuh19731b02024-03-02 16:53:19 -0800180 match.ConfigurePid(reading.first, reading.second.name);
Austin Schuh608514f2022-12-30 15:51:30 -0800181 break;
182 }
183 }
Austin Schuh4ec20622023-03-11 15:11:50 -0800184 } else {
185 for (const ParsedKThreadConfig &match : threads_) {
186 if (match.Matches(reading.second.name)) {
Austin Schuh19731b02024-03-02 16:53:19 -0800187 match.ConfigurePid(reading.first, reading.second.name);
Austin Schuh4ec20622023-03-11 15:11:50 -0800188 break;
189 }
190 }
Austin Schuh608514f2022-12-30 15:51:30 -0800191 }
192 }
193
194 interrupts_status_.Update();
195
196 for (const InterruptsStatus::InterruptState &state :
197 interrupts_status_.states()) {
198 for (const ParsedIrqConfig &match : irqs_) {
199 bool matched = false;
200 for (const std::string &action : state.actions) {
201 if (match.name == action) {
202 matched = true;
203 break;
204 }
205 }
206 if (matched) {
207 match.ConfigureIrq(state.interrupt_number);
208 }
209 }
210 }
211 });
212 }
213
214 private:
Austin Schuh4ec20622023-03-11 15:11:50 -0800215 void PopulateThreads(
216 const flatbuffers::Vector<flatbuffers::Offset<starter::KthreadConfig>>
217 *threads_config,
218 std::vector<ParsedKThreadConfig> *threads) {
219 threads->reserve(threads_config->size());
220 for (const starter::KthreadConfig *kthread_config : *threads_config) {
221 LOG(INFO) << "Kthread " << aos::FlatbufferToJson(kthread_config);
222 CHECK(kthread_config->has_name()) << ": Name required";
223 const size_t star_position =
224 kthread_config->name()->string_view().find('*');
225 const bool has_star = star_position != std::string_view::npos;
226
227 threads->push_back(ParsedKThreadConfig{
228 .full_match = !has_star,
229 .prefix = std::string(
230 !has_star ? kthread_config->name()->string_view()
231 : kthread_config->name()->string_view().substr(
232 0, star_position)),
233 .postfix = std::string(
234 !has_star ? ""
235 : kthread_config->name()->string_view().substr(
236 star_position + 1)),
237 .scheduler = kthread_config->scheduler(),
238 .priority = kthread_config->priority(),
239 .nice = kthread_config->nice(),
240 .affinity = AffinityFromFlatbuffer(kthread_config->affinity()),
241 });
242 }
243 }
244
Austin Schuh608514f2022-12-30 15:51:30 -0800245 util::Top top_;
246
247 // TODO(austin): Publish message with everything in it.
248 // TODO(austin): Make Top report out affinity + priority + scheduler for
249 // posterity.
250
251 std::vector<ParsedKThreadConfig> kthreads_;
Austin Schuh4ec20622023-03-11 15:11:50 -0800252 std::vector<ParsedKThreadConfig> threads_;
Austin Schuh608514f2022-12-30 15:51:30 -0800253 std::vector<ParsedIrqConfig> irqs_;
254
255 InterruptsStatus interrupts_status_;
256};
257
258} // namespace aos
259
260int main(int argc, char **argv) {
261 aos::InitGoogle(&argc, &argv);
262
263 if (!FLAGS_user.empty()) {
264 // Maintain root permissions as we switch to become the user so we can
265 // actually manipulate priorities.
266 PCHECK(prctl(PR_SET_SECUREBITS, SECBIT_NO_SETUID_FIXUP | SECBIT_NOROOT) ==
267 0);
268
269 uid_t uid;
270 uid_t gid;
271 {
272 struct passwd *user_data = getpwnam(FLAGS_user.c_str());
273 if (user_data != nullptr) {
274 uid = user_data->pw_uid;
275 gid = user_data->pw_gid;
276 } else {
277 LOG(FATAL) << "Could not find user " << FLAGS_user;
278 return 1;
279 }
280 }
281 // Change the real and effective IDs to the user we're running as. The
282 // effective IDs mean files we access (like shared memory) will happen as
283 // that user. The real IDs allow child processes with an different effective
284 // ID to still participate in signal sending/receiving.
285 constexpr int kUnchanged = -1;
286 if (setresgid(/* ruid */ gid, /* euid */ gid,
287 /* suid */ kUnchanged) != 0) {
288 PLOG(FATAL) << "Failed to change GID to " << FLAGS_user;
289 }
290
291 if (setresuid(/* ruid */ uid, /* euid */ uid,
292 /* suid */ kUnchanged) != 0) {
293 PLOG(FATAL) << "Failed to change UID to " << FLAGS_user;
294 }
295 }
296
297 aos::FlatbufferDetachedBuffer<aos::Configuration> config =
298 aos::configuration::ReadConfig(FLAGS_config);
299
Austin Schuh608514f2022-12-30 15:51:30 -0800300 aos::FlatbufferDetachedBuffer<aos::starter::IrqAffinityConfig>
301 irq_affinity_config =
Alexander Yeee61cac32023-02-11 19:40:40 -0800302 aos::JsonFileToFlatbuffer<aos::starter::IrqAffinityConfig>(
303 FLAGS_irq_config);
Austin Schuh608514f2022-12-30 15:51:30 -0800304
305 aos::ShmEventLoop shm_event_loop(&config.message());
306
307 aos::IrqAffinity irq_affinity(&shm_event_loop, irq_affinity_config);
308
309 shm_event_loop.Run();
310
311 return 0;
312}