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