@cryptotaxi247 / netdata-1 / commits / 7ba16b5ad

Dispatch cgroup discovery into another thread (#10399)

Vladimir Kobal committed Jan 12, 2021 at 14:57 UTC 7ba16b5ad9c972ce308f83f8ac4ace80cb75d504
1 file changed +214 -121
collectors/cgroups.plugin/sys_fs_cgroup.c
+214 -121
@@ -584,6 +584,7 @@ struct cgroup_network_interface {
584 #define CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE 0x00000002
585 #define CGROUP_OPTIONS_IS_UNIFIED 0x00000004
586
587 +// *** WARNING *** The fields are not thread safe. Take care of safe usage.
588 struct cgroup {
589 uint32_t options;
590
@@ -691,9 +692,22 @@ struct cgroup {
692 RRDDIM *rd_io_merged_write;
693
694 struct cgroup *next;
695 + struct cgroup *discovered_next;
696
697 } *cgroup_root = NULL;
698
699 +uv_mutex_t cgroup_root_mutex;
700 +
701 +struct cgroup *discovered_cgroup_root = NULL;
702 +
703 +struct discovery_thread {
704 + uv_thread_t thread;
705 + uv_mutex_t mutex;
706 + uv_cond_t cond_var;
707 + int start_discovery;
708 + int exited;
709 +} discovery_thread;
710 +
711 // ----------------------------------------------------------------------------
712 // read values from /sys
713
@@ -1168,7 +1182,7 @@ static inline void read_all_cgroups(struct cgroup *root) {
1182 struct cgroup *cg;
1183
1184 for(cg = root; cg ; cg = cg->next)
1171 - if(cg->enabled && cg->available && !cg->pending_renames)
1185 + if(cg->enabled && !cg->pending_renames)
1186 cgroup_read(cg);
1187 }
1188
@@ -1380,13 +1394,13 @@ static inline struct cgroup *cgroup_add(const char *id) {
1394
1395 if(cgroup_use_unified_cgroups) cg->options |= CGROUP_OPTIONS_IS_UNIFIED;
1396
1383 - if(!cgroup_root)
1384 - cgroup_root = cg;
1397 + if(!discovered_cgroup_root)
1398 + discovered_cgroup_root = cg;
1399 else {
1400 // append it
1401 struct cgroup *e;
1388 - for(e = cgroup_root; e->next ;e = e->next) ;
1389 - e->next = cg;
1402 + for(e = discovered_cgroup_root; e->discovered_next ;e = e->discovered_next) ;
1403 + e->discovered_next = cg;
1404 }
1405
1406 cgroup_root_count++;
@@ -1452,24 +1466,23 @@ static inline struct cgroup *cgroup_add(const char *id) {
1466 // detect duplicate cgroups
1467 if(cg->enabled) {
1468 struct cgroup *t;
1455 - for (t = cgroup_root; t; t = t->next) {
1469 + for (t = discovered_cgroup_root; t; t = t->discovered_next) {
1470 if (t != cg && t->enabled && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
1457 - if (!strncmp(t->chart_id, "/system.slice/", 14) && !strncmp(cg->chart_id, "/init.scope/system.slice/", 25)) {
1458 - error("CGROUP: chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
1459 - cg->chart_id, t->id, cg->id, t->id);
1460 - debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
1461 - cg->chart_id, t->id, cg->id, t->id);
1462 - t->enabled = 0;
1463 - t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
1464 - }
1465 - else {
1466 - error("CGROUP: chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
1467 - cg->chart_id, t->id, cg->id);
1468 - debug(D_CGROUP, "Control group with chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
1469 - cg->chart_id, t->id, cg->id);
1470 - cg->enabled = 0;
1471 - cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
1472 - }
1471 + // TODO: use it after refactoring if system.slice might be scanned before init.scope/system.slice
1472 + //
1473 + // if (!strncmp(t->id, "/system.slice/", 14) && !strncmp(cg->id, "/init.scope/system.slice/", 25)) {
1474 + // error("CGROUP: chart id '%s' already exists with id '%s' and is enabled. Swapping them by enabling cgroup with id '%s' and disabling cgroup with id '%s'.",
1475 + // cg->chart_id, t->id, cg->id, t->id);
1476 + // t->enabled = 0;
1477 + // t->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
1478 + // }
1479 + // else {}
1480 + //
1481 + // https://github.com/netdata/netdata/issues/797#issuecomment-241248884
1482 + error("CGROUP: chart id '%s' already exists with id '%s' and is enabled and available. Disabling cgroup with id '%s'.",
1483 + cg->chart_id, t->id, cg->id);
1484 + cg->enabled = 0;
1485 + cg->options |= CGROUP_OPTIONS_DISABLED_DUPLICATE;
1486
1487 break;
1488 }
@@ -1560,7 +1573,7 @@ static inline struct cgroup *cgroup_find(const char *id) {
1573 uint32_t hash = simple_hash(id);
1574
1575 struct cgroup *cg;
1563 - for(cg = cgroup_root; cg ; cg = cg->next) {
1576 + for(cg = discovered_cgroup_root; cg ; cg = cg->discovered_next) {
1577 if(hash == cg->hash && strcmp(id, cg->id) == 0)
1578 break;
1579 }
@@ -1686,103 +1699,16 @@ static inline void mark_all_cgroups_as_not_available() {
1699 struct cgroup *cg;
1700
1701 // mark all as not available
1689 - for(cg = cgroup_root; cg ; cg = cg->next) {
1702 + for(cg = discovered_cgroup_root; cg ; cg = cg->discovered_next) {
1703 cg->available = 0;
1704 }
1705 }
1706
1694 -static inline void cleanup_all_cgroups() {
1695 - struct cgroup *cg = cgroup_root, *last = NULL;
1696 -
1697 - for(; cg ;) {
1698 - if(!cg->available) {
1699 - // enable the first duplicate cgroup
1700 - {
1701 - struct cgroup *t;
1702 - for(t = cgroup_root; t ; t = t->next) {
1703 - if(t != cg && t->available && !t->enabled && t->options & CGROUP_OPTIONS_DISABLED_DUPLICATE && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
1704 - debug(D_CGROUP, "Enabling duplicate of cgroup '%s' with id '%s', because the original with id '%s' stopped.", t->chart_id, t->id, cg->id);
1705 - t->enabled = 1;
1706 - t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
1707 - break;
1708 - }
1709 - }
1710 - }
1711 -
1712 - if(!last)
1713 - cgroup_root = cg->next;
1714 - else
1715 - last->next = cg->next;
1716 -
1717 - cgroup_free(cg);
1718 -
1719 - if(!last)
1720 - cg = cgroup_root;
1721 - else
1722 - cg = last->next;
1723 - }
1724 - else {
1725 - last = cg;
1726 - cg = cg->next;
1727 - }
1728 - }
1729 -}
1730 -
1731 -static inline void find_all_cgroups() {
1732 - debug(D_CGROUP, "searching for cgroups");
1733 -
1734 - mark_all_cgroups_as_not_available();
1735 - if(!cgroup_use_unified_cgroups) {
1736 - if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
1737 - if(find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
1738 - cgroup_enable_cpuacct_stat =
1739 - cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_NO;
1740 - error("CGROUP: disabled cpu statistics.");
1741 - }
1742 - }
1743 -
1744 - if(cgroup_enable_blkio_io || cgroup_enable_blkio_ops || cgroup_enable_blkio_throttle_io || cgroup_enable_blkio_throttle_ops || cgroup_enable_blkio_merged_ops || cgroup_enable_blkio_queued_ops) {
1745 - if(find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) {
1746 - cgroup_enable_blkio_io =
1747 - cgroup_enable_blkio_ops =
1748 - cgroup_enable_blkio_throttle_io =
1749 - cgroup_enable_blkio_throttle_ops =
1750 - cgroup_enable_blkio_merged_ops =
1751 - cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_NO;
1752 - error("CGROUP: disabled blkio statistics.");
1753 - }
1754 - }
1755 -
1756 - if(cgroup_enable_memory || cgroup_enable_detailed_memory || cgroup_enable_swap || cgroup_enable_memory_failcnt) {
1757 - if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) {
1758 - cgroup_enable_memory =
1759 - cgroup_enable_detailed_memory =
1760 - cgroup_enable_swap =
1761 - cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_NO;
1762 - error("CGROUP: disabled memory statistics.");
1763 - }
1764 - }
1765 -
1766 - if(cgroup_search_in_devices) {
1767 - if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) {
1768 - cgroup_search_in_devices = 0;
1769 - error("CGROUP: disabled devices statistics.");
1770 - }
1771 - }
1772 - }
1773 - else {
1774 - if (find_dir_in_subdirs(cgroup_unified_base, NULL, found_subdir_in_dir) == -1) {
1775 - cgroup_unified_exist = CONFIG_BOOLEAN_NO;
1776 - error("CGROUP: disabled unified cgroups statistics.");
1777 - }
1778 - }
1779 -
1780 - // remove any non-existing cgroups
1781 - cleanup_all_cgroups();
1782 -
1707 +static inline void update_filenames()
1708 +{
1709 struct cgroup *cg;
1710 struct stat buf;
1785 - for(cg = cgroup_root; cg ; cg = cg->next) {
1711 + for(cg = discovered_cgroup_root; cg ; cg = cg->discovered_next) {
1712 // fprintf(stderr, " >>> CGROUP '%s' (%u - %s) with name '%s'\n", cg->id, cg->hash, cg->available?"available":"stopped", cg->name);
1713
1714 if(unlikely(cg->pending_renames))
@@ -2046,10 +1972,137 @@ static inline void find_all_cgroups() {
1972 }
1973 }
1974 }
1975 +}
1976 +
1977 +static inline void cleanup_all_cgroups() {
1978 + struct cgroup *cg = discovered_cgroup_root, *last = NULL;
1979 +
1980 + for(; cg ;) {
1981 + if(!cg->available) {
1982 + // enable the first duplicate cgroup
1983 + {
1984 + struct cgroup *t;
1985 + for(t = discovered_cgroup_root; t ; t = t->discovered_next) {
1986 + if(t != cg && t->available && !t->enabled && t->options & CGROUP_OPTIONS_DISABLED_DUPLICATE && t->hash_chart == cg->hash_chart && !strcmp(t->chart_id, cg->chart_id)) {
1987 + debug(D_CGROUP, "Enabling duplicate of cgroup '%s' with id '%s', because the original with id '%s' stopped.", t->chart_id, t->id, cg->id);
1988 + t->enabled = 1;
1989 + t->options &= ~CGROUP_OPTIONS_DISABLED_DUPLICATE;
1990 + break;
1991 + }
1992 + }
1993 + }
1994 +
1995 + if(!last)
1996 + discovered_cgroup_root = cg->discovered_next;
1997 + else
1998 + last->discovered_next = cg->discovered_next;
1999 +
2000 + cgroup_free(cg);
2001 +
2002 + if(!last)
2003 + cg = discovered_cgroup_root;
2004 + else
2005 + cg = last->discovered_next;
2006 + }
2007 + else {
2008 + last = cg;
2009 + cg = cg->discovered_next;
2010 + }
2011 + }
2012 +}
2013 +
2014 +static inline void copy_discovered_cgroups()
2015 +{
2016 + debug(D_CGROUP, "copy discovered cgroups to the main group list");
2017 +
2018 + struct cgroup *cg;
2019 +
2020 + for(cg = discovered_cgroup_root; cg ; cg = cg->discovered_next) {
2021 + cg->next = cg->discovered_next;
2022 + }
2023 +
2024 + cgroup_root = discovered_cgroup_root;
2025 +}
2026 +
2027 +static inline void find_all_cgroups() {
2028 + debug(D_CGROUP, "searching for cgroups");
2029 +
2030 + mark_all_cgroups_as_not_available();
2031 + if(!cgroup_use_unified_cgroups) {
2032 + if(cgroup_enable_cpuacct_stat || cgroup_enable_cpuacct_usage) {
2033 + if(find_dir_in_subdirs(cgroup_cpuacct_base, NULL, found_subdir_in_dir) == -1) {
2034 + cgroup_enable_cpuacct_stat =
2035 + cgroup_enable_cpuacct_usage = CONFIG_BOOLEAN_NO;
2036 + error("CGROUP: disabled cpu statistics.");
2037 + }
2038 + }
2039 +
2040 + if(cgroup_enable_blkio_io || cgroup_enable_blkio_ops || cgroup_enable_blkio_throttle_io || cgroup_enable_blkio_throttle_ops || cgroup_enable_blkio_merged_ops || cgroup_enable_blkio_queued_ops) {
2041 + if(find_dir_in_subdirs(cgroup_blkio_base, NULL, found_subdir_in_dir) == -1) {
2042 + cgroup_enable_blkio_io =
2043 + cgroup_enable_blkio_ops =
2044 + cgroup_enable_blkio_throttle_io =
2045 + cgroup_enable_blkio_throttle_ops =
2046 + cgroup_enable_blkio_merged_ops =
2047 + cgroup_enable_blkio_queued_ops = CONFIG_BOOLEAN_NO;
2048 + error("CGROUP: disabled blkio statistics.");
2049 + }
2050 + }
2051 +
2052 + if(cgroup_enable_memory || cgroup_enable_detailed_memory || cgroup_enable_swap || cgroup_enable_memory_failcnt) {
2053 + if(find_dir_in_subdirs(cgroup_memory_base, NULL, found_subdir_in_dir) == -1) {
2054 + cgroup_enable_memory =
2055 + cgroup_enable_detailed_memory =
2056 + cgroup_enable_swap =
2057 + cgroup_enable_memory_failcnt = CONFIG_BOOLEAN_NO;
2058 + error("CGROUP: disabled memory statistics.");
2059 + }
2060 + }
2061 +
2062 + if(cgroup_search_in_devices) {
2063 + if(find_dir_in_subdirs(cgroup_devices_base, NULL, found_subdir_in_dir) == -1) {
2064 + cgroup_search_in_devices = 0;
2065 + error("CGROUP: disabled devices statistics.");
2066 + }
2067 + }
2068 + }
2069 + else {
2070 + if (find_dir_in_subdirs(cgroup_unified_base, NULL, found_subdir_in_dir) == -1) {
2071 + cgroup_unified_exist = CONFIG_BOOLEAN_NO;
2072 + error("CGROUP: disabled unified cgroups statistics.");
2073 + }
2074 + }
2075 +
2076 + update_filenames();
2077 +
2078 + uv_mutex_lock(&cgroup_root_mutex);
2079 + cleanup_all_cgroups();
2080 + copy_discovered_cgroups();
2081 + uv_mutex_unlock(&cgroup_root_mutex);
2082
2083 debug(D_CGROUP, "done searching for cgroups");
2084 }
2085
2086 +void cgroup_discovery_worker(void *ptr)
2087 +{
2088 + UNUSED(ptr);
2089 +
2090 + while (!netdata_exit) {
2091 + uv_mutex_lock(&discovery_thread.mutex);
2092 + while (!discovery_thread.start_discovery)
2093 + uv_cond_wait(&discovery_thread.cond_var, &discovery_thread.mutex);
2094 + discovery_thread.start_discovery = 0;
2095 + uv_mutex_unlock(&discovery_thread.mutex);
2096 +
2097 + if (unlikely(netdata_exit))
2098 + break;
2099 +
2100 + find_all_cgroups();
2101 + }
2102 +
2103 + discovery_thread.exited = 1;
2104 +}
2105 +
2106 // ----------------------------------------------------------------------------
2107 // generate charts
2108
@@ -2631,7 +2684,7 @@ void update_systemd_services_charts(
2684 // update the values
2685 struct cgroup *cg;
2686 for(cg = cgroup_root; cg ; cg = cg->next) {
2634 - if(unlikely(!cg->available || !cg->enabled || cg->pending_renames || !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)))
2687 + if(unlikely(!cg->enabled || cg->pending_renames || !(cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)))
2688 continue;
2689
2690 if(likely(do_cpu && cg->cpuacct_stat.updated)) {
@@ -3022,7 +3075,7 @@ void update_cgroup_charts(int update_every) {
3075
3076 struct cgroup *cg;
3077 for(cg = cgroup_root; cg ; cg = cg->next) {
3025 - if(unlikely(!cg->available || !cg->enabled || cg->pending_renames))
3078 + if(unlikely(!cg->enabled || cg->pending_renames))
3079 continue;
3080
3081 if(likely(cgroup_enable_systemd_services && cg->options & CGROUP_OPTIONS_SYSTEM_SLICE_SERVICE)) {
@@ -3880,6 +3933,21 @@ static void cgroup_main_cleanup(void *ptr) {
3933
3934 info("cleaning up...");
3935
3936 + usec_t max = 2 * USEC_PER_SEC, step = 50000;
3937 +
3938 + if (!discovery_thread.exited) {
3939 + info("stopping discovery thread worker");
3940 + uv_mutex_unlock(&discovery_thread.mutex);
3941 + discovery_thread.start_discovery = 1;
3942 + uv_cond_signal(&discovery_thread.cond_var);
3943 + }
3944 +
3945 + while (!discovery_thread.exited && max > 0) {
3946 + max -= step;
3947 + info("waiting for discovery thread to finish...");
3948 + sleep_usec(step);
3949 + }
3950 +
3951 static_thread->enabled = NETDATA_MAIN_THREAD_EXITED;
3952 }
3953
@@ -3895,6 +3963,31 @@ void *cgroups_main(void *ptr) {
3963
3964 RRDSET *stcpu_thread = NULL;
3965
3966 + if (uv_mutex_init(&cgroup_root_mutex)) {
3967 + error("CGROUP: cannot initialize mutex for the main cgroup list");
3968 + goto exit;
3969 + }
3970 +
3971 + // dispatch a discovery worker thread
3972 + discovery_thread.start_discovery = 0;
3973 + discovery_thread.exited = 0;
3974 +
3975 + if (uv_mutex_init(&discovery_thread.mutex)) {
3976 + error("CGROUP: cannot initialize mutex for discovery thread");
3977 + goto exit;
3978 + }
3979 + if (uv_cond_init(&discovery_thread.cond_var)) {
3980 + error("CGROUP: cannot initialize conditional variable for discovery thread");
3981 + goto exit;
3982 + }
3983 +
3984 + int error = uv_thread_create(&discovery_thread.thread, cgroup_discovery_worker, NULL);
3985 + if (error) {
3986 + error("CGROUP: cannot create tread worker. uv_thread_create(): %s", uv_strerror(error));
3987 + goto exit;
3988 + }
3989 + uv_thread_set_name_np(discovery_thread.thread, "PLUGIN[cgroups]");
3990 +
3991 heartbeat_t hb;
3992 heartbeat_init(&hb);
3993 usec_t step = cgroup_update_every * USEC_PER_SEC;
@@ -3904,19 +3997,18 @@ void *cgroups_main(void *ptr) {
3997 usec_t hb_dt = heartbeat_next(&hb, step);
3998 if(unlikely(netdata_exit)) break;
3999
3907 - // BEGIN -- the job to be done
3908 -
4000 find_dt += hb_dt;
4001 if(unlikely(find_dt >= find_every || cgroups_check)) {
3911 - find_all_cgroups();
4002 + uv_cond_signal(&discovery_thread.cond_var);
4003 + discovery_thread.start_discovery = 1;
4004 find_dt = 0;
4005 cgroups_check = 0;
4006 }
4007
4008 + uv_mutex_lock(&cgroup_root_mutex);
4009 read_all_cgroups(cgroup_root);
4010 update_cgroup_charts(cgroup_update_every);
3918 -
3919 - // END -- the job is done
4011 + uv_mutex_unlock(&cgroup_root_mutex);
4012
4013 // --------------------------------------------------------------------
4014
@@ -3952,6 +4044,7 @@ void *cgroups_main(void *ptr) {
4044 }
4045 }
4046
4047 +exit:
4048 netdata_thread_cleanup_pop(1);
4049 return NULL;
4050 }