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