master
c 1,484 lines 55.7 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #include "cgroup-internals.h"
4
5 // main cgroups thread worker jobs
6 #define WORKER_CGROUPS_LOCK 0
7 #define WORKER_CGROUPS_READ 1
8 #define WORKER_CGROUPS_CHART 2
9
10 // ----------------------------------------------------------------------------
11 // cgroup globals
12 unsigned long long host_ram_total = 0;
13 bool is_inside_k8s = false;
14 long system_page_size = 4096; // system will be queried via sysconf() in configuration()
15
16 int cgroup_use_unified_cgroups = CONFIG_BOOLEAN_AUTO;
17 bool cgroup_unified_exist = true;
18
19 bool cgroup_enable_blkio = true;
20 bool cgroup_enable_pressure = true;
21 bool cgroup_enable_memory = true;
22 bool cgroup_enable_cpuacct = true;
23 bool cgroup_enable_cpuacct_cpu_shares = false;
24
25 int cgroup_check_for_new_every = 10;
26 int cgroup_update_every = 1;
27 char *cgroup_cpuacct_base = NULL;
28 char *cgroup_cpuset_base = NULL;
29 char *cgroup_blkio_base = NULL;
30 char *cgroup_memory_base = NULL;
31 char *cgroup_pids_base = NULL;
32 char *cgroup_unified_base = NULL;
33 int cgroup_root_count = 0;
34 int cgroup_root_max = 1000;
35 int cgroup_max_depth = 0;
36 SIMPLE_PATTERN *enabled_cgroup_paths = NULL;
37 SIMPLE_PATTERN *enabled_cgroup_names = NULL;
38 SIMPLE_PATTERN *search_cgroup_paths = NULL;
39 SIMPLE_PATTERN *enabled_cgroup_renames = NULL;
40 SIMPLE_PATTERN *systemd_services_cgroups = NULL;
41 SIMPLE_PATTERN *entrypoint_parent_process_comm = NULL;
42 const char *cgroups_network_interface_script = NULL;
43 int cgroups_check = 0;
44 uint32_t Read_hash = 0;
45 uint32_t Write_hash = 0;
46 uint32_t user_hash = 0;
47 uint32_t system_hash = 0;
48 uint32_t user_usec_hash = 0;
49 uint32_t system_usec_hash = 0;
50 uint32_t nr_periods_hash = 0;
51 uint32_t nr_throttled_hash = 0;
52 uint32_t throttled_time_hash = 0;
53 uint32_t throttled_usec_hash = 0;
54
55 // *** WARNING *** The fields are not thread safe. Take care of safe usage.
56 struct cgroup *cgroup_root = NULL;
57 netdata_mutex_t cgroup_root_mutex;
58
59 struct cgroups_systemd_config_setting cgroups_systemd_options[] = {
60 { .name = "legacy", .setting = SYSTEMD_CGROUP_LEGACY },
61 { .name = "hybrid", .setting = SYSTEMD_CGROUP_HYBRID },
62 { .name = "unified", .setting = SYSTEMD_CGROUP_UNIFIED },
63 { .name = NULL, .setting = SYSTEMD_CGROUP_ERR },
64 };
65
66 struct discovery_thread discovery_thread = {
67 .exited = 1, // Start as "exited" until properly initialized
68 };
69
70
71 /* on Fed systemd is not in PATH for some reason */
72 #define SYSTEMD_CMD_RHEL "/usr/lib/systemd/systemd --version"
73 #define SYSTEMD_HIERARCHY_STRING "default-hierarchy="
74
75 #define MAXSIZE_PROC_CMDLINE 4096
76 static enum cgroups_systemd_setting cgroups_detect_systemd(const char *exec)
77 {
78 enum cgroups_systemd_setting retval = SYSTEMD_CGROUP_ERR;
79 char buf[MAXSIZE_PROC_CMDLINE];
80 char *begin, *end;
81
82 POPEN_INSTANCE *pi = spawn_popen_run(exec);
83 if(!pi)
84 return retval;
85
86 struct pollfd pfd;
87 pfd.fd = spawn_popen_read_fd(pi);
88 pfd.events = POLLIN;
89
90 int timeout = 3000; // milliseconds
91 int ret = poll(&pfd, 1, timeout);
92
93 if (ret == -1) {
94 collector_error("Failed to get the output of \"%s\"", exec);
95 } else if (ret == 0) {
96 collector_info("Cannot get the output of \"%s\" within timeout (%d ms)", exec, timeout);
97 } else {
98 while (fgets(buf, MAXSIZE_PROC_CMDLINE, spawn_popen_stdout(pi)) != NULL) {
99 if ((begin = strstr(buf, SYSTEMD_HIERARCHY_STRING))) {
100 end = begin = begin + strlen(SYSTEMD_HIERARCHY_STRING);
101 if (!*begin)
102 break;
103 while (isalpha(*end))
104 end++;
105 *end = 0;
106 for (int i = 0; cgroups_systemd_options[i].name; i++) {
107 if (!strcmp(begin, cgroups_systemd_options[i].name)) {
108 retval = cgroups_systemd_options[i].setting;
109 break;
110 }
111 }
112 break;
113 }
114 }
115 }
116
117 if(spawn_popen_wait(pi) != 0)
118 return SYSTEMD_CGROUP_ERR;
119
120 return retval;
121 }
122
123 static enum cgroups_type cgroups_try_detect_version()
124 {
125 char filename[FILENAME_MAX + 1];
126 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/sys/fs/cgroup");
127 struct statfs fsinfo;
128
129 // https://github.com/systemd/systemd/blob/main/docs/CGROUP_DELEGATION.md#three-different-tree-setups-
130 // ├── statfs("/sys/fs/cgroup/")
131 // │ └── .f_type
132 // │ ├── CGROUP2_SUPER_MAGIC (Unified mode)
133 // │ └── TMPFS_MAGIC (Legacy or Hybrid mode)
134 // ├── statfs("/sys/fs/cgroup/unified/")
135 // │ └── .f_type
136 // │ ├── CGROUP2_SUPER_MAGIC (Hybrid mode)
137 // │ └── Otherwise, you're in legacy mode
138 if (!statfs(filename, &fsinfo)) {
139 #if defined CGROUP2_SUPER_MAGIC
140 if (fsinfo.f_type == CGROUP2_SUPER_MAGIC)
141 return CGROUPS_V2;
142 #endif
143 #if defined TMPFS_MAGIC
144 if (fsinfo.f_type == TMPFS_MAGIC) {
145 // either hybrid or legacy
146 return CGROUPS_V1;
147 }
148 #endif
149 }
150
151 collector_info("cgroups version: can't detect using statfs (fs type), falling back to heuristics.");
152
153 char buf[MAXSIZE_PROC_CMDLINE];
154 enum cgroups_systemd_setting systemd_setting;
155 int cgroups2_available = 0;
156
157 // 1. check if cgroups2 available on system at all
158 POPEN_INSTANCE *pi = spawn_popen_run("grep cgroup /proc/filesystems");
159 if(!pi) {
160 collector_error("cannot run 'grep cgroup /proc/filesystems'");
161 return CGROUPS_AUTODETECT_FAIL;
162 }
163 while (fgets(buf, MAXSIZE_PROC_CMDLINE, spawn_popen_stdout(pi)) != NULL) {
164 if (strstr(buf, "cgroup2")) {
165 cgroups2_available = 1;
166 break;
167 }
168 }
169 if(spawn_popen_wait(pi) != 0)
170 return CGROUPS_AUTODETECT_FAIL;
171
172 if(!cgroups2_available)
173 return CGROUPS_V1;
174
175 // 3. check systemd compiletime setting
176 if ((systemd_setting = cgroups_detect_systemd("systemd --version")) == SYSTEMD_CGROUP_ERR)
177 systemd_setting = cgroups_detect_systemd(SYSTEMD_CMD_RHEL);
178
179 if(systemd_setting == SYSTEMD_CGROUP_ERR)
180 return CGROUPS_AUTODETECT_FAIL;
181
182 if(systemd_setting == SYSTEMD_CGROUP_LEGACY || systemd_setting == SYSTEMD_CGROUP_HYBRID) {
183 // currently we prefer V1 if HYBRID is set as it seems to be more feature complete
184 // in the future we might want to continue here if SYSTEMD_CGROUP_HYBRID
185 // and go ahead with V2
186 return CGROUPS_V1;
187 }
188
189 // 4. if we are unified as on Fedora (default cgroups2 only mode)
190 // check kernel command line flag that can override that setting
191 FILE *fp = fopen("/proc/cmdline", "r");
192 if (!fp) {
193 collector_error("Error reading kernel boot commandline parameters");
194 return CGROUPS_AUTODETECT_FAIL;
195 }
196
197 if (!fgets(buf, MAXSIZE_PROC_CMDLINE, fp)) {
198 collector_error("couldn't read all cmdline params into buffer");
199 fclose(fp);
200 return CGROUPS_AUTODETECT_FAIL;
201 }
202
203 fclose(fp);
204
205 if (strstr(buf, "systemd.unified_cgroup_hierarchy=0")) {
206 collector_info("cgroups v2 (unified cgroups) is available but are disabled on this system.");
207 return CGROUPS_V1;
208 }
209 return CGROUPS_V2;
210 }
211
212 void set_cgroup_base_path(char *filename, char *path) {
213 if (strncmp(netdata_configured_host_prefix, path, strlen(netdata_configured_host_prefix)) == 0) {
214 snprintfz(filename, FILENAME_MAX, "%s", path);
215 } else {
216 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, path);
217 }
218 }
219
220 void read_cgroup_plugin_configuration() {
221 system_page_size = sysconf(_SC_PAGESIZE);
222
223 Read_hash = simple_hash("Read");
224 Write_hash = simple_hash("Write");
225 user_hash = simple_hash("user");
226 system_hash = simple_hash("system");
227 user_usec_hash = simple_hash("user_usec");
228 system_usec_hash = simple_hash("system_usec");
229 nr_periods_hash = simple_hash("nr_periods");
230 nr_throttled_hash = simple_hash("nr_throttled");
231 throttled_time_hash = simple_hash("throttled_time");
232 throttled_usec_hash = simple_hash("throttled_usec");
233
234 cgroup_update_every = (int)inicfg_get_duration_seconds(&netdata_config, "plugin:cgroups", "update every", localhost->rrd_update_every);
235 if(cgroup_update_every < localhost->rrd_update_every) {
236 cgroup_update_every = localhost->rrd_update_every;
237 inicfg_set_duration_seconds(&netdata_config, "plugin:cgroups", "update every", localhost->rrd_update_every);
238 }
239
240 cgroup_check_for_new_every = (int)inicfg_get_duration_seconds(&netdata_config, "plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every);
241 if(cgroup_check_for_new_every < cgroup_update_every) {
242 cgroup_check_for_new_every = cgroup_update_every;
243 inicfg_set_duration_seconds(&netdata_config, "plugin:cgroups", "check for new cgroups every", cgroup_check_for_new_every);
244 }
245
246 cgroup_use_unified_cgroups = inicfg_get_boolean_ondemand(&netdata_config, "plugin:cgroups", "use unified cgroups", CONFIG_BOOLEAN_AUTO);
247 if (cgroup_use_unified_cgroups == CONFIG_BOOLEAN_AUTO)
248 cgroup_use_unified_cgroups = (cgroups_try_detect_version() == CGROUPS_V2);
249 collector_info("use unified cgroups %s", cgroup_use_unified_cgroups ? "true" : "false");
250
251 char filename[FILENAME_MAX + 1], *s;
252 struct mountinfo *mi, *root = mountinfo_read(0);
253 if (!cgroup_use_unified_cgroups) {
254 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuacct");
255 if (!mi)
256 mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuacct");
257 if (!mi) {
258 collector_error("CGROUP: cannot find cpuacct mountinfo. Assuming default: /sys/fs/cgroup/cpuacct");
259 s = "/sys/fs/cgroup/cpuacct";
260 } else
261 s = mi->mount_point;
262 set_cgroup_base_path(filename, s);
263 cgroup_cpuacct_base = strdupz(filename);
264
265 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "cpuset");
266 if (!mi)
267 mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "cpuset");
268 if (!mi) {
269 collector_error("CGROUP: cannot find cpuset mountinfo. Assuming default: /sys/fs/cgroup/cpuset");
270 s = "/sys/fs/cgroup/cpuset";
271 } else
272 s = mi->mount_point;
273 set_cgroup_base_path(filename, s);
274 cgroup_cpuset_base = strdupz(filename);
275
276 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "blkio");
277 if (!mi)
278 mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "blkio");
279 if (!mi) {
280 collector_error("CGROUP: cannot find blkio mountinfo. Assuming default: /sys/fs/cgroup/blkio");
281 s = "/sys/fs/cgroup/blkio";
282 } else
283 s = mi->mount_point;
284 set_cgroup_base_path(filename, s);
285 cgroup_blkio_base = strdupz(filename);
286
287 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "memory");
288 if (!mi)
289 mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "memory");
290 if (!mi) {
291 collector_error("CGROUP: cannot find memory mountinfo. Assuming default: /sys/fs/cgroup/memory");
292 s = "/sys/fs/cgroup/memory";
293 } else {
294 s = mi->mount_point;
295 }
296 set_cgroup_base_path(filename, s);
297 cgroup_memory_base = strdupz(filename);
298
299 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup", "pids");
300 if (!mi)
301 mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup", "pids");
302 if (!mi) {
303 collector_error("CGROUP: cannot find pids mountinfo. Assuming default: /sys/fs/cgroup/pids");
304 s = "/sys/fs/cgroup/pids";
305 } else {
306 s = mi->mount_point;
307 }
308
309 set_cgroup_base_path(filename, s);
310 cgroup_pids_base = strdupz(filename);
311 } else {
312 //TODO: can there be more than 1 cgroup2 mount point?
313 //there is no cgroup2 specific super option - for now use 'rw' option
314 mi = mountinfo_find_by_filesystem_super_option(root, "cgroup2", "rw");
315 if (!mi) {
316 mi = mountinfo_find_by_filesystem_mount_source(root, "cgroup2", "cgroup");
317 }
318 if (!mi) {
319 collector_error("CGROUP: cannot find cgroup2 mountinfo. Assuming default: /sys/fs/cgroup");
320 s = "/sys/fs/cgroup";
321 } else
322 s = mi->mount_point;
323
324 set_cgroup_base_path(filename, s);
325 cgroup_unified_base = strdupz(filename);
326 }
327
328 cgroup_root_max = (int)inicfg_get_number(&netdata_config, "plugin:cgroups", "max cgroups to allow", cgroup_root_max);
329 cgroup_max_depth = (int)inicfg_get_number(&netdata_config, "plugin:cgroups", "max cgroups depth to monitor", cgroup_max_depth);
330
331 enabled_cgroup_paths = simple_pattern_create(
332 inicfg_get(&netdata_config, "plugin:cgroups", "enable by default cgroups matching",
333 // ----------------------------------------------------------------
334
335 " !*/init.scope " // ignore init.scope
336 " !/system.slice/run-*.scope " // ignore system.slice/run-XXXX.scope
337 " *user.slice/docker-*" // allow docker rootless containers
338 " !*user.slice*" // ignore the rest stuff in user.slice
339 " *.scope " // we need all other *.scope for sure
340
341 // ----------------------------------------------------------------
342
343 " !/machine.slice/*/.control "
344 " !/machine.slice/*/payload* "
345 " !/machine.slice/*/supervisor "
346 " /machine.slice/*.service " // #3367 systemd-nspawn
347
348 // ----------------------------------------------------------------
349
350 " */kubepods/pod*/* " // k8s containers
351 " */kubepods/*/pod*/* " // k8s containers
352 " */*-kubepods-pod*/* " // k8s containers
353 " */*-kubepods-*-pod*/* " // k8s containers
354 " !*kubepods* !*kubelet* " // all other k8s cgroups
355
356 // ----------------------------------------------------------------
357
358 " !*/vcpu* " // libvirtd adds these sub-cgroups
359 " !*/emulator " // libvirtd adds these sub-cgroups
360 " !*.mount "
361 " !*.partition "
362 " !*.service "
363 " !*.service/udev "
364 " !*.socket "
365 " !*.slice "
366 " !*.swap "
367 " !*.user "
368 " !/ "
369 " !/docker "
370 " !*/libvirt "
371 " !/lxc "
372 " !/lxc/*/* " // #1397 #2649
373 " !/lxc.monitor* "
374 " !/lxc.pivot "
375 " !/lxc.payload "
376 " !*lxcfs.service/.control"
377 " !/machine "
378 " !/qemu "
379 " !/system "
380 " !/systemd "
381 " !/user "
382 " * " // enable anything else
383 ), NULL, SIMPLE_PATTERN_EXACT, true);
384
385 enabled_cgroup_names = simple_pattern_create(
386 inicfg_get(&netdata_config, "plugin:cgroups", "enable by default cgroups names matching",
387 " * "
388 ), NULL, SIMPLE_PATTERN_EXACT, true);
389
390 search_cgroup_paths = simple_pattern_create(
391 inicfg_get(&netdata_config, "plugin:cgroups", "search for cgroups in subpaths matching",
392 " !*/init.scope " // ignore init.scope
393 " !*-qemu " // #345
394 " !*.libvirt-qemu " // #3010
395 " !/init.scope "
396 " !/system "
397 " !/systemd "
398 " !/user "
399 " !/lxc/*/* " // #2161 #2649
400 " !/lxc.monitor "
401 " !/lxc.payload/*/* "
402 " !/lxc.payload.* "
403 " * "
404 ), NULL, SIMPLE_PATTERN_EXACT, true);
405
406 snprintfz(filename, FILENAME_MAX, "%s/cgroup-name.sh", netdata_configured_primary_plugins_dir);
407 cgroups_rename_script = inicfg_get(&netdata_config, "plugin:cgroups", "script to get cgroup names", filename);
408
409 snprintfz(filename, FILENAME_MAX, "%s/cgroup-network", netdata_configured_primary_plugins_dir);
410 cgroups_network_interface_script = inicfg_get(&netdata_config, "plugin:cgroups", "script to get cgroup network interfaces", filename);
411
412 enabled_cgroup_renames = simple_pattern_create(
413 inicfg_get(&netdata_config, "plugin:cgroups", "run script to rename cgroups matching",
414 " !/ "
415 " !*.mount "
416 " !*.socket "
417 " !*.partition "
418 " /machine.slice/*.service " // #3367 systemd-nspawn
419 " !*.service "
420 " !*.slice "
421 " !*.swap "
422 " !*.user "
423 " !init.scope "
424 " !*.scope/vcpu* " // libvirtd adds these sub-cgroups
425 " !*.scope/emulator " // libvirtd adds these sub-cgroups
426 " *.scope "
427 " *docker* "
428 " *lxc* "
429 " *qemu* "
430 " */kubepods/pod*/* " // k8s containers
431 " */kubepods/*/pod*/* " // k8s containers
432 " */*-kubepods-pod*/* " // k8s containers
433 " */*-kubepods-*-pod*/* " // k8s containers
434 " !*kubepods* !*kubelet* " // all other k8s cgroups
435 " *.libvirt-qemu " // #3010
436 " * "
437 ), NULL, SIMPLE_PATTERN_EXACT, true);
438
439 systemd_services_cgroups = simple_pattern_create(
440 inicfg_get(&netdata_config,
441 "plugin:cgroups",
442 "cgroups to match as systemd services",
443 " !/system.slice/*/*.service "
444 " /system.slice/*.service "),
445 NULL,
446 SIMPLE_PATTERN_EXACT,
447 true);
448
449 mountinfo_free_all(root);
450 }
451
452 // ---------------------------------------------------------------------------------------------
453
454 static unsigned long long calc_delta(unsigned long long curr, unsigned long long prev) {
455 if (prev > curr) {
456 return 0;
457 }
458 return curr - prev;
459 }
460
461 static unsigned long long calc_percentage(unsigned long long value, unsigned long long total) {
462 if (total == 0) {
463 return 0;
464 }
465 return (unsigned long long)((NETDATA_DOUBLE)value / (NETDATA_DOUBLE)total * 100);
466 }
467
468 // ----------------------------------------------------------------------------
469 // read values from /sys
470
471 static inline void cgroup_read_cpuacct_stat(struct cpuacct_stat *cp) {
472 static procfile *ff = NULL;
473
474 if(likely(cp->filename)) {
475 ff = procfile_reopen(ff, cp->filename, NULL, CGROUP_PROCFILE_FLAG);
476 if(unlikely(!ff)) {
477 cp->updated = 0;
478 cgroups_check = 1;
479 return;
480 }
481
482 ff = procfile_readall(ff);
483 if(unlikely(!ff)) {
484 cp->updated = 0;
485 cgroups_check = 1;
486 return;
487 }
488
489 unsigned long i, lines = procfile_lines(ff);
490
491 if(unlikely(lines < 1)) {
492 collector_error("CGROUP: file '%s' should have 1+ lines.", cp->filename);
493 cp->updated = 0;
494 return;
495 }
496
497 for(i = 0; i < lines ; i++) {
498 char *s = procfile_lineword(ff, i, 0);
499 uint32_t hash = simple_hash(s);
500
501 if(unlikely(hash == user_hash && !strcmp(s, "user")))
502 cp->user = str2ull(procfile_lineword(ff, i, 1), NULL);
503
504 else if(unlikely(hash == system_hash && !strcmp(s, "system")))
505 cp->system = str2ull(procfile_lineword(ff, i, 1), NULL);
506 }
507
508 cp->updated = 1;
509 }
510 }
511
512 static inline void cgroup_read_cpuacct_cpu_stat(struct cpuacct_cpu_throttling *cp) {
513 if (unlikely(!cp->filename)) {
514 return;
515 }
516
517 static procfile *ff = NULL;
518 ff = procfile_reopen(ff, cp->filename, NULL, CGROUP_PROCFILE_FLAG);
519 if (unlikely(!ff)) {
520 cp->updated = 0;
521 cgroups_check = 1;
522 return;
523 }
524
525 ff = procfile_readall(ff);
526 if (unlikely(!ff)) {
527 cp->updated = 0;
528 cgroups_check = 1;
529 return;
530 }
531
532 unsigned long lines = procfile_lines(ff);
533 if (unlikely(lines < 3)) {
534 collector_error("CGROUP: file '%s' should have 3 lines.", cp->filename);
535 cp->updated = 0;
536 return;
537 }
538
539 unsigned long long nr_periods_last = cp->nr_periods;
540 unsigned long long nr_throttled_last = cp->nr_throttled;
541
542 for (unsigned long i = 0; i < lines; i++) {
543 char *s = procfile_lineword(ff, i, 0);
544 uint32_t hash = simple_hash(s);
545
546 if (unlikely(hash == nr_periods_hash && !strcmp(s, "nr_periods"))) {
547 cp->nr_periods = str2ull(procfile_lineword(ff, i, 1), NULL);
548 } else if (unlikely(hash == nr_throttled_hash && !strcmp(s, "nr_throttled"))) {
549 cp->nr_throttled = str2ull(procfile_lineword(ff, i, 1), NULL);
550 } else if (unlikely(hash == throttled_time_hash && !strcmp(s, "throttled_time"))) {
551 cp->throttled_time = str2ull(procfile_lineword(ff, i, 1), NULL);
552 }
553 }
554 cp->nr_throttled_perc =
555 calc_percentage(calc_delta(cp->nr_throttled, nr_throttled_last), calc_delta(cp->nr_periods, nr_periods_last));
556
557 cp->updated = 1;
558 }
559
560 static inline void cgroup2_read_cpuacct_cpu_stat(struct cpuacct_stat *cp, struct cpuacct_cpu_throttling *cpt) {
561 static procfile *ff = NULL;
562 if (unlikely(!cp->filename)) {
563 return;
564 }
565
566 ff = procfile_reopen(ff, cp->filename, NULL, CGROUP_PROCFILE_FLAG);
567 if (unlikely(!ff)) {
568 cp->updated = 0;
569 cgroups_check = 1;
570 return;
571 }
572
573 ff = procfile_readall(ff);
574 if (unlikely(!ff)) {
575 cp->updated = 0;
576 cgroups_check = 1;
577 return;
578 }
579
580 unsigned long lines = procfile_lines(ff);
581
582 if (unlikely(lines < 3)) {
583 collector_error("CGROUP: file '%s' should have at least 3 lines.", cp->filename);
584 cp->updated = 0;
585 return;
586 }
587
588 unsigned long long nr_periods_last = cpt->nr_periods;
589 unsigned long long nr_throttled_last = cpt->nr_throttled;
590
591 for (unsigned long i = 0; i < lines; i++) {
592 char *s = procfile_lineword(ff, i, 0);
593 uint32_t hash = simple_hash(s);
594
595 if (unlikely(hash == user_usec_hash && !strcmp(s, "user_usec"))) {
596 cp->user = str2ull(procfile_lineword(ff, i, 1), NULL);
597 } else if (unlikely(hash == system_usec_hash && !strcmp(s, "system_usec"))) {
598 cp->system = str2ull(procfile_lineword(ff, i, 1), NULL);
599 } else if (unlikely(hash == nr_periods_hash && !strcmp(s, "nr_periods"))) {
600 cpt->nr_periods = str2ull(procfile_lineword(ff, i, 1), NULL);
601 } else if (unlikely(hash == nr_throttled_hash && !strcmp(s, "nr_throttled"))) {
602 cpt->nr_throttled = str2ull(procfile_lineword(ff, i, 1), NULL);
603 } else if (unlikely(hash == throttled_usec_hash && !strcmp(s, "throttled_usec"))) {
604 cpt->throttled_time = str2ull(procfile_lineword(ff, i, 1), NULL) * 1000; // usec -> ns
605 }
606 }
607 cpt->nr_throttled_perc =
608 calc_percentage(calc_delta(cpt->nr_throttled, nr_throttled_last), calc_delta(cpt->nr_periods, nr_periods_last));
609
610 cp->updated = 1;
611 cpt->updated = 1;
612 }
613
614 static inline void cgroup_read_cpuacct_cpu_shares(struct cpuacct_cpu_shares *cp) {
615 if (unlikely(!cp->filename)) {
616 return;
617 }
618
619 if (unlikely(read_single_number_file(cp->filename, &cp->shares))) {
620 cp->updated = 0;
621 cgroups_check = 1;
622 return;
623 }
624
625 cp->updated = 1;
626 }
627
628 static inline void cgroup_read_cpuacct_usage(struct cpuacct_usage *ca) {
629 static procfile *ff = NULL;
630
631 if(likely(ca->filename)) {
632 ff = procfile_reopen(ff, ca->filename, NULL, CGROUP_PROCFILE_FLAG);
633 if(unlikely(!ff)) {
634 ca->updated = 0;
635 cgroups_check = 1;
636 return;
637 }
638
639 ff = procfile_readall(ff);
640 if(unlikely(!ff)) {
641 ca->updated = 0;
642 cgroups_check = 1;
643 return;
644 }
645
646 if(unlikely(procfile_lines(ff) < 1)) {
647 collector_error("CGROUP: file '%s' should have 1+ lines but has %zu.", ca->filename, procfile_lines(ff));
648 ca->updated = 0;
649 return;
650 }
651
652 unsigned long i = procfile_linewords(ff, 0);
653 if(unlikely(i == 0)) {
654 ca->updated = 0;
655 return;
656 }
657
658 // we may have 1 more CPU reported
659 while(i > 0) {
660 char *s = procfile_lineword(ff, 0, i - 1);
661 if(!*s) i--;
662 else break;
663 }
664
665 if(unlikely(i != ca->cpus)) {
666 freez(ca->cpu_percpu);
667 ca->cpu_percpu = mallocz(sizeof(unsigned long long) * i);
668 ca->cpus = (unsigned int)i;
669 }
670
671 unsigned long long total = 0;
672 for(i = 0; i < ca->cpus ;i++) {
673 unsigned long long n = str2ull(procfile_lineword(ff, 0, i), NULL);
674 ca->cpu_percpu[i] = n;
675 total += n;
676 }
677
678 ca->updated = 1;
679 }
680 }
681
682 static inline void cgroup_read_blkio(struct blkio *io) {
683 if (likely(io->filename)) {
684 static procfile *ff = NULL;
685
686 ff = procfile_reopen(ff, io->filename, NULL, CGROUP_PROCFILE_FLAG);
687 if (unlikely(!ff)) {
688 io->updated = 0;
689 cgroups_check = 1;
690 return;
691 }
692
693 ff = procfile_readall(ff);
694 if (unlikely(!ff)) {
695 io->updated = 0;
696 cgroups_check = 1;
697 return;
698 }
699
700 unsigned long i, lines = procfile_lines(ff);
701
702 if (unlikely(lines < 1)) {
703 collector_error("CGROUP: file '%s' should have 1+ lines.", io->filename);
704 io->updated = 0;
705 return;
706 }
707
708 io->Read = 0;
709 io->Write = 0;
710
711 for (i = 0; i < lines; i++) {
712 char *s = procfile_lineword(ff, i, 1);
713 uint32_t hash = simple_hash(s);
714
715 if (unlikely(hash == Read_hash && !strcmp(s, "Read")))
716 io->Read += str2ull(procfile_lineword(ff, i, 2), NULL);
717 else if (unlikely(hash == Write_hash && !strcmp(s, "Write")))
718 io->Write += str2ull(procfile_lineword(ff, i, 2), NULL);
719 }
720
721 io->updated = 1;
722 }
723 }
724
725 static inline void cgroup2_read_blkio(struct blkio *io, unsigned int word_offset) {
726 if (likely(io->filename)) {
727 static procfile *ff = NULL;
728
729 ff = procfile_reopen(ff, io->filename, NULL, CGROUP_PROCFILE_FLAG);
730 if (unlikely(!ff)) {
731 io->updated = 0;
732 cgroups_check = 1;
733 return;
734 }
735
736 ff = procfile_readall(ff);
737 if (unlikely(!ff)) {
738 io->updated = 0;
739 cgroups_check = 1;
740 return;
741 }
742
743 unsigned long i, lines = procfile_lines(ff);
744
745 if (unlikely(lines < 1)) {
746 collector_error("CGROUP: file '%s' should have 1+ lines.", io->filename);
747 io->updated = 0;
748 return;
749 }
750
751 io->Read = 0;
752 io->Write = 0;
753
754 for (i = 0; i < lines; i++) {
755 io->Read += str2ull(procfile_lineword(ff, i, 2 + word_offset), NULL);
756 io->Write += str2ull(procfile_lineword(ff, i, 4 + word_offset), NULL);
757 }
758
759 io->updated = 1;
760 }
761 }
762
763 static inline void cgroup2_read_pressure(struct pressure *res) {
764 static procfile *ff = NULL;
765
766 if (likely(res->filename)) {
767 ff = procfile_reopen(ff, res->filename, " =", CGROUP_PROCFILE_FLAG);
768 if (unlikely(!ff)) {
769 res->updated = 0;
770 cgroups_check = 1;
771 return;
772 }
773
774 ff = procfile_readall(ff);
775 if (unlikely(!ff)) {
776 res->updated = 0;
777 cgroups_check = 1;
778 return;
779 }
780
781 size_t lines = procfile_lines(ff);
782 if (lines < 1) {
783 collector_error("CGROUP: file '%s' should have 1+ lines.", res->filename);
784 res->updated = 0;
785 return;
786 }
787
788 bool did_some = false, did_full = false;
789
790 for(size_t l = 0; l < lines ;l++) {
791 const char *key = procfile_lineword(ff, l, 0);
792 if(strcmp(key, "some") == 0) {
793 res->some.share_time.value10 = strtod(procfile_lineword(ff, l, 2), NULL);
794 res->some.share_time.value60 = strtod(procfile_lineword(ff, l, 4), NULL);
795 res->some.share_time.value300 = strtod(procfile_lineword(ff, l, 6), NULL);
796 res->some.total_time.value_total = str2ull(procfile_lineword(ff, l, 8), NULL) / 1000; // us->ms
797 did_some = true;
798 }
799 else if(strcmp(key, "full") == 0) {
800 res->full.share_time.value10 = strtod(procfile_lineword(ff, l, 2), NULL);
801 res->full.share_time.value60 = strtod(procfile_lineword(ff, l, 4), NULL);
802 res->full.share_time.value300 = strtod(procfile_lineword(ff, l, 6), NULL);
803 res->full.total_time.value_total = str2ull(procfile_lineword(ff, l, 8), NULL) / 1000; // us->ms
804 did_full = true;
805 }
806 }
807
808 res->updated = (did_full || did_some) ? 1 : 0;
809 res->some.available = did_some;
810 res->full.available = did_full;
811 }
812 }
813
814 static inline void cgroup_read_memory(struct memory *mem, char parent_cg_is_unified) {
815 static procfile *ff = NULL;
816
817 if(likely(mem->filename_detailed)) {
818 ff = procfile_reopen(ff, mem->filename_detailed, NULL, CGROUP_PROCFILE_FLAG);
819 if(unlikely(!ff)) {
820 mem->updated_detailed = 0;
821 cgroups_check = 1;
822 goto memory_next;
823 }
824
825 ff = procfile_readall(ff);
826 if(unlikely(!ff)) {
827 mem->updated_detailed = 0;
828 cgroups_check = 1;
829 goto memory_next;
830 }
831
832 unsigned long i, lines = procfile_lines(ff);
833
834 if(unlikely(lines < 1)) {
835 collector_error("CGROUP: file '%s' should have 1+ lines.", mem->filename_detailed);
836 mem->updated_detailed = 0;
837 goto memory_next;
838 }
839
840
841 if(unlikely(!mem->arl_base)) {
842 if(parent_cg_is_unified == 0){
843 mem->arl_base = arl_create("cgroup/memory", NULL, 60);
844
845 arl_expect(mem->arl_base, "total_cache", &mem->total_cache);
846 arl_expect(mem->arl_base, "total_rss", &mem->total_rss);
847 arl_expect(mem->arl_base, "total_rss_huge", &mem->total_rss_huge);
848 arl_expect(mem->arl_base, "total_mapped_file", &mem->total_mapped_file);
849 arl_expect(mem->arl_base, "total_writeback", &mem->total_writeback);
850 mem->arl_dirty = arl_expect(mem->arl_base, "total_dirty", &mem->total_dirty);
851 mem->arl_swap = arl_expect(mem->arl_base, "total_swap", &mem->total_swap);
852 arl_expect(mem->arl_base, "total_pgpgin", &mem->total_pgpgin);
853 arl_expect(mem->arl_base, "total_pgpgout", &mem->total_pgpgout);
854 arl_expect(mem->arl_base, "total_pgfault", &mem->total_pgfault);
855 arl_expect(mem->arl_base, "total_pgmajfault", &mem->total_pgmajfault);
856 arl_expect(mem->arl_base, "total_inactive_file", &mem->total_inactive_file);
857 } else {
858 mem->arl_base = arl_create("cgroup/memory", NULL, 60);
859
860 arl_expect(mem->arl_base, "anon", &mem->anon);
861 arl_expect(mem->arl_base, "kernel_stack", &mem->kernel_stack);
862 arl_expect(mem->arl_base, "slab", &mem->slab);
863 arl_expect(mem->arl_base, "sock", &mem->sock);
864 arl_expect(mem->arl_base, "anon_thp", &mem->anon_thp);
865 arl_expect(mem->arl_base, "file", &mem->total_mapped_file);
866 arl_expect(mem->arl_base, "file_writeback", &mem->total_writeback);
867 mem->arl_dirty = arl_expect(mem->arl_base, "file_dirty", &mem->total_dirty);
868 arl_expect(mem->arl_base, "pgfault", &mem->total_pgfault);
869 arl_expect(mem->arl_base, "pgmajfault", &mem->total_pgmajfault);
870 arl_expect(mem->arl_base, "inactive_file", &mem->total_inactive_file);
871 }
872 }
873
874 arl_begin(mem->arl_base);
875
876 for (i = 0; i < lines; i++) {
877 if (arl_check(mem->arl_base, procfile_lineword(ff, i, 0), procfile_lineword(ff, i, 1)))
878 break;
879 }
880
881 if (unlikely(mem->arl_dirty->flags & ARL_ENTRY_FLAG_FOUND))
882 mem->detailed_has_dirty = 1;
883
884 if (unlikely(parent_cg_is_unified == 0 && mem->arl_swap->flags & ARL_ENTRY_FLAG_FOUND))
885 mem->detailed_has_swap = 1;
886
887 mem->updated_detailed = 1;
888 }
889
890 memory_next:
891
892 if (likely(mem->filename_usage_in_bytes)) {
893 mem->updated_usage_in_bytes = !read_single_number_file(mem->filename_usage_in_bytes, &mem->usage_in_bytes);
894 }
895
896 if (likely(mem->updated_usage_in_bytes && mem->updated_detailed)) {
897 mem->usage_in_bytes =
898 (mem->usage_in_bytes > mem->total_inactive_file) ? (mem->usage_in_bytes - mem->total_inactive_file) : 0;
899 }
900
901 if (likely(mem->filename_msw_usage_in_bytes)) {
902 mem->updated_msw_usage_in_bytes =
903 !read_single_number_file(mem->filename_msw_usage_in_bytes, &mem->msw_usage_in_bytes);
904 }
905
906 if (likely(mem->filename_failcnt)) {
907 mem->updated_failcnt = !read_single_number_file(mem->filename_failcnt, &mem->failcnt);
908 }
909 }
910
911 static void cgroup_read_pids_current(struct pids *pids) {
912 pids->updated = 0;
913
914 if (unlikely(!pids->filename))
915 return;
916
917 pids->updated = !read_single_number_file(pids->filename, &pids->pids_current);
918 }
919
920 static inline void read_cgroup(struct cgroup *cg) {
921 netdata_log_debug(D_CGROUP, "reading metrics for cgroups '%s'", cg->id);
922 if (!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
923 cgroup_read_cpuacct_stat(&cg->cpuacct_stat);
924 cgroup_read_cpuacct_usage(&cg->cpuacct_usage);
925 cgroup_read_cpuacct_cpu_stat(&cg->cpuacct_cpu_throttling);
926 cgroup_read_cpuacct_cpu_shares(&cg->cpuacct_cpu_shares);
927 cgroup_read_memory(&cg->memory, 0);
928 cgroup_read_blkio(&cg->io_service_bytes);
929 cgroup_read_blkio(&cg->io_serviced);
930 cgroup_read_blkio(&cg->throttle_io_service_bytes);
931 cgroup_read_blkio(&cg->throttle_io_serviced);
932 cgroup_read_blkio(&cg->io_merged);
933 cgroup_read_blkio(&cg->io_queued);
934 cgroup_read_pids_current(&cg->pids_current);
935 } else {
936 cgroup2_read_blkio(&cg->io_service_bytes, 0);
937 cgroup2_read_blkio(&cg->io_serviced, 4);
938 cgroup2_read_cpuacct_cpu_stat(&cg->cpuacct_stat, &cg->cpuacct_cpu_throttling);
939 cgroup_read_cpuacct_cpu_shares(&cg->cpuacct_cpu_shares);
940 cgroup2_read_pressure(&cg->cpu_pressure);
941 cgroup2_read_pressure(&cg->io_pressure);
942 cgroup2_read_pressure(&cg->memory_pressure);
943 cgroup2_read_pressure(&cg->irq_pressure);
944 cgroup_read_memory(&cg->memory, 1);
945 cgroup_read_pids_current(&cg->pids_current);
946 }
947 }
948
949 static inline void read_all_discovered_cgroups(struct cgroup *root) {
950 netdata_log_debug(D_CGROUP, "reading metrics for all cgroups");
951
952 struct cgroup *cg;
953 for (cg = root; cg; cg = cg->next) {
954 if (cg->enabled && !cg->pending_renames) {
955 read_cgroup(cg);
956 }
957 }
958 }
959
960 // update CPU and memory limits
961
962 static inline void update_cpu_limits(char **filename, unsigned long long *value, struct cgroup *cg) {
963 if(*filename) {
964 int ret = -1;
965
966 if(value == &cg->cpuset_cpus) {
967 unsigned long ncpus = os_read_cpuset_cpus(*filename, os_get_system_cpus());
968 if(ncpus) {
969 *value = ncpus;
970 ret = 0;
971 }
972 }
973 else if(value == &cg->cpu_cfs_period || value == &cg->cpu_cfs_quota) {
974 ret = read_single_number_file(*filename, value);
975 }
976 else ret = -1;
977
978 if(ret) {
979 collector_error("Cannot refresh cgroup %s cpu limit by reading '%s'. Will not update its limit anymore.", cg->id, *filename);
980 freez(*filename);
981 *filename = NULL;
982 }
983 }
984 }
985
986 static inline void update_cpu_limits2(struct cgroup *cg) {
987 if(cg->filename_cpu_cfs_quota){
988 static procfile *ff = NULL;
989
990 ff = procfile_reopen(ff, cg->filename_cpu_cfs_quota, NULL, CGROUP_PROCFILE_FLAG);
991 if(unlikely(!ff)) {
992 goto cpu_limits2_err;
993 }
994
995 ff = procfile_readall(ff);
996 if(unlikely(!ff)) {
997 goto cpu_limits2_err;
998 }
999
1000 unsigned long lines = procfile_lines(ff);
1001
1002 if (unlikely(lines < 1)) {
1003 collector_error("CGROUP: file '%s' should have 1 lines.", cg->filename_cpu_cfs_quota);
1004 return;
1005 }
1006
1007 cg->cpu_cfs_period = str2ull(procfile_lineword(ff, 0, 1), NULL);
1008 cg->cpuset_cpus = os_get_system_cpus();
1009
1010 char *s = "max\n\0";
1011 if(strcmp(s, procfile_lineword(ff, 0, 0)) == 0){
1012 cg->cpu_cfs_quota = cg->cpu_cfs_period * cg->cpuset_cpus;
1013 } else {
1014 cg->cpu_cfs_quota = str2ull(procfile_lineword(ff, 0, 0), NULL);
1015 }
1016 netdata_log_debug(D_CGROUP, "CPU limits values: %llu %llu %llu", cg->cpu_cfs_period, cg->cpuset_cpus, cg->cpu_cfs_quota);
1017 return;
1018
1019 cpu_limits2_err:
1020 collector_error("Cannot refresh cgroup %s cpu limit by reading '%s'. Will not update its limit anymore.", cg->id, cg->filename_cpu_cfs_quota);
1021 freez(cg->filename_cpu_cfs_quota);
1022 cg->filename_cpu_cfs_quota = NULL;
1023
1024 }
1025 }
1026
1027 static inline int update_memory_limits(struct cgroup *cg) {
1028 char **filename = &cg->filename_memory_limit;
1029 unsigned long long *value = &cg->memory_limit;
1030
1031 if(*filename) {
1032 if(unlikely(!cg->chart_var_memory_limit)) {
1033 cg->chart_var_memory_limit = rrdvar_chart_variable_add_and_acquire(cg->st_mem_usage, "memory_limit");
1034 if(!cg->chart_var_memory_limit) {
1035 collector_error("Cannot create cgroup %s chart variable '%s'. Will not update its limit anymore.", cg->id, "memory_limit");
1036 freez(*filename);
1037 *filename = NULL;
1038 }
1039 }
1040
1041 if(*filename && cg->chart_var_memory_limit) {
1042 if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
1043 if(read_single_number_file(*filename, value)) {
1044 collector_error("Cannot refresh cgroup %s memory limit by reading '%s'. Will not update its limit anymore.", cg->id, *filename);
1045 freez(*filename);
1046 *filename = NULL;
1047 }
1048 else {
1049 rrdvar_chart_variable_set(
1050 cg->st_mem_usage, cg->chart_var_memory_limit, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1051 return 1;
1052 }
1053 } else {
1054 char buffer[32];
1055 int ret = read_txt_file(*filename, buffer, sizeof(buffer));
1056 if(ret) {
1057 collector_error("Cannot refresh cgroup %s memory limit by reading '%s'. Will not update its limit anymore.", cg->id, *filename);
1058 freez(*filename);
1059 *filename = NULL;
1060 return 0;
1061 }
1062 char *s = "max\n\0";
1063 if(strcmp(s, buffer) == 0){
1064 *value = UINT64_MAX;
1065 rrdvar_chart_variable_set(cg->st_mem_usage, cg->chart_var_memory_limit, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1066 return 1;
1067 }
1068 *value = str2ull(buffer, NULL);
1069 rrdvar_chart_variable_set(cg->st_mem_usage, cg->chart_var_memory_limit, (NETDATA_DOUBLE)(*value) / (1024.0 * 1024.0));
1070 return 1;
1071 }
1072 }
1073 }
1074 return 0;
1075 }
1076
1077 // ----------------------------------------------------------------------------
1078 // generate charts
1079
1080 void update_cgroup_systemd_services_charts() {
1081 for (struct cgroup *cg = cgroup_root; cg; cg = cg->next) {
1082 if (unlikely(!cg->enabled || cg->pending_renames || !is_cgroup_systemd_service(cg)))
1083 continue;
1084
1085 if (likely(cg->cpuacct_stat.updated)) {
1086 update_cpu_utilization_chart(cg);
1087 }
1088 if (likely(cg->memory.updated_msw_usage_in_bytes)) {
1089 update_mem_usage_chart(cg);
1090 }
1091 if (likely(cg->memory.updated_failcnt)) {
1092 update_mem_failcnt_chart(cg);
1093 }
1094 if (likely(cg->memory.updated_detailed)) {
1095 update_mem_usage_detailed_chart(cg);
1096 update_mem_writeback_chart(cg);
1097 update_mem_pgfaults_chart(cg);
1098 if (!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
1099 update_mem_activity_chart(cg);
1100 }
1101 }
1102 if (likely(cg->io_service_bytes.updated)) {
1103 update_io_serviced_bytes_chart(cg);
1104 }
1105 if (likely(cg->io_serviced.updated)) {
1106 update_io_serviced_ops_chart(cg);
1107 }
1108 if (likely(cg->throttle_io_service_bytes.updated)) {
1109 update_throttle_io_serviced_bytes_chart(cg);
1110 }
1111 if (likely(cg->throttle_io_serviced.updated)) {
1112 update_throttle_io_serviced_ops_chart(cg);
1113 }
1114 if (likely(cg->io_queued.updated)) {
1115 update_io_queued_ops_chart(cg);
1116 }
1117 if (likely(cg->io_merged.updated)) {
1118 update_io_merged_ops_chart(cg);
1119 }
1120
1121 if (likely(cg->pids_current.updated)) {
1122 update_pids_current_chart(cg);
1123 }
1124
1125 cg->function_ready = true;
1126 }
1127 }
1128
1129 void update_cgroup_charts() {
1130 for (struct cgroup *cg = cgroup_root; cg; cg = cg->next) {
1131 if (unlikely(!cg->enabled || cg->pending_renames || is_cgroup_systemd_service(cg)))
1132 continue;
1133
1134 if (likely(cg->cpuacct_stat.updated)) {
1135 update_cpu_utilization_chart(cg);
1136
1137 if (likely(cg->filename_cpuset_cpus || cg->filename_cpu_cfs_period || cg->filename_cpu_cfs_quota)) {
1138 if (!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
1139 update_cpu_limits(&cg->filename_cpuset_cpus, &cg->cpuset_cpus, cg);
1140 update_cpu_limits(&cg->filename_cpu_cfs_period, &cg->cpu_cfs_period, cg);
1141 update_cpu_limits(&cg->filename_cpu_cfs_quota, &cg->cpu_cfs_quota, cg);
1142 } else {
1143 update_cpu_limits2(cg);
1144 }
1145
1146 if (unlikely(!cg->chart_var_cpu_limit)) {
1147 cg->chart_var_cpu_limit = rrdvar_chart_variable_add_and_acquire(cg->st_cpu, "cpu_limit");
1148 if (!cg->chart_var_cpu_limit) {
1149 collector_error(
1150 "Cannot create cgroup %s chart variable 'cpu_limit'. Will not update its limit anymore.",
1151 cg->id);
1152 if (cg->filename_cpuset_cpus)
1153 freez(cg->filename_cpuset_cpus);
1154 cg->filename_cpuset_cpus = NULL;
1155 if (cg->filename_cpu_cfs_period)
1156 freez(cg->filename_cpu_cfs_period);
1157 cg->filename_cpu_cfs_period = NULL;
1158 if (cg->filename_cpu_cfs_quota)
1159 freez(cg->filename_cpu_cfs_quota);
1160 cg->filename_cpu_cfs_quota = NULL;
1161 }
1162 } else {
1163 NETDATA_DOUBLE value = 0, quota = 0;
1164
1165 if (likely(
1166 ((!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) &&
1167 (cg->filename_cpuset_cpus ||
1168 (cg->filename_cpu_cfs_period && cg->filename_cpu_cfs_quota))) ||
1169 ((cg->options & CGROUP_OPTIONS_IS_UNIFIED) && cg->filename_cpu_cfs_quota))) {
1170 if (unlikely(cg->cpu_cfs_quota > 0))
1171 quota = (NETDATA_DOUBLE)cg->cpu_cfs_quota / (NETDATA_DOUBLE)cg->cpu_cfs_period;
1172
1173 if (unlikely(quota > 0 && quota < cg->cpuset_cpus))
1174 value = quota * 100;
1175 else
1176 value = (NETDATA_DOUBLE)cg->cpuset_cpus * 100;
1177 }
1178 if (likely(value)) {
1179 update_cpu_utilization_limit_chart(cg, value);
1180 } else {
1181 if (unlikely(cg->st_cpu_limit)) {
1182 rrdset_is_obsolete___safe_from_collector_thread(cg->st_cpu_limit);
1183 cg->st_cpu_limit = NULL;
1184 }
1185 rrdvar_chart_variable_set(cg->st_cpu, cg->chart_var_cpu_limit, NAN);
1186 }
1187 }
1188 }
1189 }
1190
1191 if (likely(cg->cpuacct_cpu_throttling.updated)) {
1192 update_cpu_throttled_chart(cg);
1193 update_cpu_throttled_duration_chart(cg);
1194 }
1195
1196 if (unlikely(cg->cpuacct_cpu_shares.updated)) {
1197 update_cpu_shares_chart(cg);
1198 }
1199
1200 if (likely(cg->cpuacct_usage.updated)) {
1201 update_cpu_per_core_usage_chart(cg);
1202 }
1203
1204 if (likely(cg->memory.updated_detailed)) {
1205 update_mem_usage_detailed_chart(cg);
1206 update_mem_writeback_chart(cg);
1207
1208 if(!(cg->options & CGROUP_OPTIONS_IS_UNIFIED)) {
1209 update_mem_activity_chart(cg);
1210 }
1211
1212 update_mem_pgfaults_chart(cg);
1213 }
1214
1215 if (likely(cg->memory.updated_usage_in_bytes)) {
1216 update_mem_usage_chart(cg);
1217
1218 // FIXME: this "if" should be only for unlimited charts
1219 if (likely(host_ram_total)) {
1220 // FIXME: do we need to update mem limits on every data collection?
1221 if (likely(update_memory_limits(cg))) {
1222 unsigned long long memory_limit = host_ram_total;
1223 if (unlikely(cg->memory_limit < host_ram_total))
1224 memory_limit = cg->memory_limit;
1225
1226 update_mem_usage_limit_chart(cg, memory_limit);
1227 update_mem_utilization_chart(cg, memory_limit);
1228 } else {
1229 if (unlikely(cg->st_mem_usage_limit)) {
1230 rrdset_is_obsolete___safe_from_collector_thread(cg->st_mem_usage_limit);
1231 cg->st_mem_usage_limit = NULL;
1232 }
1233
1234 if (unlikely(cg->st_mem_utilization)) {
1235 rrdset_is_obsolete___safe_from_collector_thread(cg->st_mem_utilization);
1236 cg->st_mem_utilization = NULL;
1237 }
1238 }
1239 }
1240 }
1241
1242 if (likely(cg->memory.updated_failcnt)) {
1243 update_mem_failcnt_chart(cg);
1244 }
1245
1246 if (likely(cg->io_service_bytes.updated)) {
1247 update_io_serviced_bytes_chart(cg);
1248 }
1249
1250 if (likely(cg->io_serviced.updated)) {
1251 update_io_serviced_ops_chart(cg);
1252 }
1253
1254 if (likely(cg->throttle_io_service_bytes.updated)) {
1255 update_throttle_io_serviced_bytes_chart(cg);
1256 }
1257
1258 if (likely(cg->throttle_io_serviced.updated)) {
1259 update_throttle_io_serviced_ops_chart(cg);
1260 }
1261
1262 if (likely(cg->io_queued.updated)) {
1263 update_io_queued_ops_chart(cg);
1264 }
1265
1266 if (likely(cg->io_merged.updated)) {
1267 update_io_merged_ops_chart(cg);
1268 }
1269
1270 if (likely(cg->pids_current.updated)) {
1271 update_pids_current_chart(cg);
1272 }
1273
1274 if (cg->options & CGROUP_OPTIONS_IS_UNIFIED) {
1275 if (likely(cg->cpu_pressure.updated)) {
1276 if (cg->cpu_pressure.some.available) {
1277 update_cpu_some_pressure_chart(cg);
1278 update_cpu_some_pressure_stall_time_chart(cg);
1279 }
1280 if (cg->cpu_pressure.full.available) {
1281 update_cpu_full_pressure_chart(cg);
1282 update_cpu_full_pressure_stall_time_chart(cg);
1283 }
1284 }
1285
1286 if (likely(cg->memory_pressure.updated)) {
1287 if (cg->memory_pressure.some.available) {
1288 update_mem_some_pressure_chart(cg);
1289 update_mem_some_pressure_stall_time_chart(cg);
1290 }
1291 if (cg->memory_pressure.full.available) {
1292 update_mem_full_pressure_chart(cg);
1293 update_mem_full_pressure_stall_time_chart(cg);
1294 }
1295 }
1296
1297 if (likely(cg->irq_pressure.updated)) {
1298 if (cg->irq_pressure.some.available) {
1299 update_irq_some_pressure_chart(cg);
1300 update_irq_some_pressure_stall_time_chart(cg);
1301 }
1302 if (cg->irq_pressure.full.available) {
1303 update_irq_full_pressure_chart(cg);
1304 update_irq_full_pressure_stall_time_chart(cg);
1305 }
1306 }
1307
1308 if (likely(cg->io_pressure.updated)) {
1309 if (cg->io_pressure.some.available) {
1310 update_io_some_pressure_chart(cg);
1311 update_io_some_pressure_stall_time_chart(cg);
1312 }
1313 if (cg->io_pressure.full.available) {
1314 update_io_full_pressure_chart(cg);
1315 update_io_full_pressure_stall_time_chart(cg);
1316 }
1317 }
1318 }
1319
1320 cg->function_ready = true;
1321 }
1322 }
1323
1324 // ----------------------------------------------------------------------------
1325 // cgroups main
1326
1327 static void cgroup_main_cleanup(void *pptr) {
1328 struct netdata_static_thread *static_thread = CLEANUP_FUNCTION_GET_PTR(pptr);
1329 if(!static_thread) return;
1330
1331 static_thread->enabled = NETDATA_MAIN_THREAD_EXITING;
1332
1333 worker_unregister();
1334
1335 usec_t max = 2 * USEC_PER_SEC, step = 50000;
1336
1337 if (!__atomic_load_n(&discovery_thread.exited, __ATOMIC_ACQUIRE)) {
1338 collector_info("waiting for discovery thread to finish...");
1339 while (!__atomic_load_n(&discovery_thread.exited, __ATOMIC_ACQUIRE) && max > 0) {
1340 netdata_mutex_lock(&discovery_thread.mutex);
1341 netdata_cond_signal(&discovery_thread.cond_var);
1342 netdata_mutex_unlock(&discovery_thread.mutex);
1343 max -= step;
1344 sleep_usec(step);
1345 }
1346 }
1347 // We should be done, but just in case, avoid blocking shutdown
1348 // Only join and destroy synchronization primitives if thread has exited
1349 if (__atomic_load_n(&discovery_thread.exited, __ATOMIC_ACQUIRE)) {
1350 if (discovery_thread.thread) {
1351 (void) nd_thread_join(discovery_thread.thread);
1352 netdata_cond_destroy(&discovery_thread.cond_var);
1353 netdata_mutex_destroy(&discovery_thread.mutex);
1354 }
1355 }
1356
1357 static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
1358 }
1359
1360 void cgroup_read_host_total_ram() {
1361 procfile *ff = NULL;
1362 char filename[FILENAME_MAX + 1];
1363
1364 snprintfz(filename, FILENAME_MAX, "%s%s", netdata_configured_host_prefix, "/proc/meminfo");
1365 ff = procfile_open(filename, " \t:", PROCFILE_FLAG_DEFAULT);
1366
1367 if (likely((ff = procfile_readall(ff)) && procfile_lines(ff) && !strncmp(procfile_word(ff, 0), "MemTotal", 8)))
1368 host_ram_total = str2ull(procfile_word(ff, 1), NULL) * 1024;
1369 else
1370 collector_error("Cannot read file %s. Will not create RAM limit charts.", filename);
1371
1372 procfile_close(ff);
1373 }
1374
1375 void cgroups_main(void *ptr) {
1376 CLEANUP_FUNCTION_REGISTER(cgroup_main_cleanup) cleanup_ptr = ptr;
1377
1378 worker_register("CGROUPS");
1379 worker_register_job_name(WORKER_CGROUPS_LOCK, "lock");
1380 worker_register_job_name(WORKER_CGROUPS_READ, "read");
1381 worker_register_job_name(WORKER_CGROUPS_CHART, "chart");
1382
1383 if (getenv("KUBERNETES_SERVICE_HOST") != NULL && getenv("KUBERNETES_SERVICE_PORT") != NULL) {
1384 is_inside_k8s = true;
1385 cgroup_enable_cpuacct_cpu_shares = true;
1386 }
1387
1388 read_cgroup_plugin_configuration();
1389
1390 cgroup_read_host_total_ram();
1391
1392 if (netdata_mutex_init(&cgroup_root_mutex)) {
1393 collector_error("CGROUP: cannot initialize mutex for the main cgroup list");
1394 return;
1395 }
1396
1397 // we register this only on localhost
1398 // for the other nodes, the origin server should register it
1399 cgroup_netdev_link_init();
1400
1401 if (netdata_mutex_init(&discovery_thread.mutex)) {
1402 collector_error("CGROUP: cannot initialize mutex for discovery thread");
1403 return;
1404 }
1405 if (netdata_cond_init(&discovery_thread.cond_var)) {
1406 collector_error("CGROUP: cannot initialize conditional variable for discovery thread");
1407 netdata_mutex_destroy(&discovery_thread.mutex);
1408 return;
1409 }
1410
1411 // Mark thread as "running" only after mutex/cond are initialized
1412 // but before creating the thread. This ensures cleanup won't try
1413 // to access uninitialized synchronization primitives.
1414 // Use RELEASE ordering so readers with ACQUIRE see initialized mutex/cond.
1415 __atomic_store_n(&discovery_thread.exited, 0, __ATOMIC_RELEASE);
1416
1417 discovery_thread.thread = nd_thread_create("CGDISCOVER", NETDATA_THREAD_OPTION_DEFAULT, cgroup_discovery_worker, NULL);
1418
1419 if (!discovery_thread.thread) {
1420 collector_error("CGROUP: cannot create thread worker");
1421 __atomic_store_n(&discovery_thread.exited, 1, __ATOMIC_RELEASE); // Reset since thread wasn't created
1422 netdata_cond_destroy(&discovery_thread.cond_var);
1423 netdata_mutex_destroy(&discovery_thread.mutex);
1424 return;
1425 }
1426
1427 rrd_function_add_inline(localhost, NULL, "containers-vms", 10,
1428 RRDFUNCTIONS_PRIORITY_DEFAULT / 2, RRDFUNCTIONS_VERSION_DEFAULT,
1429 RRDFUNCTIONS_CGTOP_HELP,
1430 "top", HTTP_ACCESS_ANONYMOUS_DATA,
1431 cgroup_function_cgroup_top);
1432
1433 rrd_function_add_inline(localhost, NULL, "systemd-services", 10,
1434 RRDFUNCTIONS_PRIORITY_DEFAULT / 3, RRDFUNCTIONS_VERSION_DEFAULT,
1435 RRDFUNCTIONS_SYSTEMD_SERVICES_HELP,
1436 "top", HTTP_ACCESS_ANONYMOUS_DATA,
1437 cgroup_function_systemd_top);
1438
1439 heartbeat_t hb;
1440 heartbeat_init(&hb, cgroup_update_every * USEC_PER_SEC);
1441 usec_t find_every = cgroup_check_for_new_every * USEC_PER_SEC, find_dt = 0;
1442
1443 while(service_running(SERVICE_COLLECTORS)) {
1444 worker_is_idle();
1445
1446 usec_t hb_dt = heartbeat_next(&hb);
1447
1448 if (unlikely(!service_running(SERVICE_COLLECTORS)))
1449 break;
1450
1451 find_dt += hb_dt;
1452 if (unlikely(find_dt >= find_every || (!is_inside_k8s && cgroups_check))) {
1453 netdata_mutex_lock(&discovery_thread.mutex);
1454 netdata_cond_signal(&discovery_thread.cond_var);
1455 netdata_mutex_unlock(&discovery_thread.mutex);
1456 find_dt = 0;
1457 cgroups_check = 0;
1458 }
1459
1460 worker_is_busy(WORKER_CGROUPS_LOCK);
1461 netdata_mutex_lock(&cgroup_root_mutex);
1462
1463 worker_is_busy(WORKER_CGROUPS_READ);
1464 read_all_discovered_cgroups(cgroup_root);
1465
1466 if (unlikely(!service_running(SERVICE_COLLECTORS))) {
1467 netdata_mutex_unlock(&cgroup_root_mutex);
1468 break;
1469 }
1470
1471 worker_is_busy(WORKER_CGROUPS_CHART);
1472
1473 update_cgroup_charts();
1474 update_cgroup_systemd_services_charts();
1475
1476 if (unlikely(!service_running(SERVICE_COLLECTORS))) {
1477 netdata_mutex_unlock(&cgroup_root_mutex);
1478 break;
1479 }
1480
1481 worker_is_idle();
1482 netdata_mutex_unlock(&cgroup_root_mutex);
1483 }
1484 }