test_core.c 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. #include <linux/limits.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <stdio.h>
  6. #include <errno.h>
  7. #include "../kselftest.h"
  8. #include "cgroup_util.h"
  9. /*
  10. * A(0) - B(0) - C(1)
  11. * \ D(0)
  12. *
  13. * A, B and C's "populated" fields would be 1 while D's 0.
  14. * test that after the one process in C is moved to root,
  15. * A,B and C's "populated" fields would flip to "0" and file
  16. * modified events will be generated on the
  17. * "cgroup.events" files of both cgroups.
  18. */
  19. static int test_cgcore_populated(const char *root)
  20. {
  21. int ret = KSFT_FAIL;
  22. char *cg_test_a = NULL, *cg_test_b = NULL;
  23. char *cg_test_c = NULL, *cg_test_d = NULL;
  24. cg_test_a = cg_name(root, "cg_test_a");
  25. cg_test_b = cg_name(root, "cg_test_a/cg_test_b");
  26. cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c");
  27. cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d");
  28. if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d)
  29. goto cleanup;
  30. if (cg_create(cg_test_a))
  31. goto cleanup;
  32. if (cg_create(cg_test_b))
  33. goto cleanup;
  34. if (cg_create(cg_test_c))
  35. goto cleanup;
  36. if (cg_create(cg_test_d))
  37. goto cleanup;
  38. if (cg_enter_current(cg_test_c))
  39. goto cleanup;
  40. if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n"))
  41. goto cleanup;
  42. if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n"))
  43. goto cleanup;
  44. if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n"))
  45. goto cleanup;
  46. if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
  47. goto cleanup;
  48. if (cg_enter_current(root))
  49. goto cleanup;
  50. if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n"))
  51. goto cleanup;
  52. if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n"))
  53. goto cleanup;
  54. if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n"))
  55. goto cleanup;
  56. if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
  57. goto cleanup;
  58. ret = KSFT_PASS;
  59. cleanup:
  60. if (cg_test_d)
  61. cg_destroy(cg_test_d);
  62. if (cg_test_c)
  63. cg_destroy(cg_test_c);
  64. if (cg_test_b)
  65. cg_destroy(cg_test_b);
  66. if (cg_test_a)
  67. cg_destroy(cg_test_a);
  68. free(cg_test_d);
  69. free(cg_test_c);
  70. free(cg_test_b);
  71. free(cg_test_a);
  72. return ret;
  73. }
  74. /*
  75. * A (domain threaded) - B (threaded) - C (domain)
  76. *
  77. * test that C can't be used until it is turned into a
  78. * threaded cgroup. "cgroup.type" file will report "domain (invalid)" in
  79. * these cases. Operations which fail due to invalid topology use
  80. * EOPNOTSUPP as the errno.
  81. */
  82. static int test_cgcore_invalid_domain(const char *root)
  83. {
  84. int ret = KSFT_FAIL;
  85. char *grandparent = NULL, *parent = NULL, *child = NULL;
  86. grandparent = cg_name(root, "cg_test_grandparent");
  87. parent = cg_name(root, "cg_test_grandparent/cg_test_parent");
  88. child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child");
  89. if (!parent || !child || !grandparent)
  90. goto cleanup;
  91. if (cg_create(grandparent))
  92. goto cleanup;
  93. if (cg_create(parent))
  94. goto cleanup;
  95. if (cg_create(child))
  96. goto cleanup;
  97. if (cg_write(parent, "cgroup.type", "threaded"))
  98. goto cleanup;
  99. if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n"))
  100. goto cleanup;
  101. if (!cg_enter_current(child))
  102. goto cleanup;
  103. if (errno != EOPNOTSUPP)
  104. goto cleanup;
  105. ret = KSFT_PASS;
  106. cleanup:
  107. cg_enter_current(root);
  108. if (child)
  109. cg_destroy(child);
  110. if (parent)
  111. cg_destroy(parent);
  112. if (grandparent)
  113. cg_destroy(grandparent);
  114. free(child);
  115. free(parent);
  116. free(grandparent);
  117. return ret;
  118. }
  119. /*
  120. * Test that when a child becomes threaded
  121. * the parent type becomes domain threaded.
  122. */
  123. static int test_cgcore_parent_becomes_threaded(const char *root)
  124. {
  125. int ret = KSFT_FAIL;
  126. char *parent = NULL, *child = NULL;
  127. parent = cg_name(root, "cg_test_parent");
  128. child = cg_name(root, "cg_test_parent/cg_test_child");
  129. if (!parent || !child)
  130. goto cleanup;
  131. if (cg_create(parent))
  132. goto cleanup;
  133. if (cg_create(child))
  134. goto cleanup;
  135. if (cg_write(child, "cgroup.type", "threaded"))
  136. goto cleanup;
  137. if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n"))
  138. goto cleanup;
  139. ret = KSFT_PASS;
  140. cleanup:
  141. if (child)
  142. cg_destroy(child);
  143. if (parent)
  144. cg_destroy(parent);
  145. free(child);
  146. free(parent);
  147. return ret;
  148. }
  149. /*
  150. * Test that there's no internal process constrain on threaded cgroups.
  151. * You can add threads/processes on a parent with a controller enabled.
  152. */
  153. static int test_cgcore_no_internal_process_constraint_on_threads(const char *root)
  154. {
  155. int ret = KSFT_FAIL;
  156. char *parent = NULL, *child = NULL;
  157. if (cg_read_strstr(root, "cgroup.controllers", "cpu") ||
  158. cg_read_strstr(root, "cgroup.subtree_control", "cpu")) {
  159. ret = KSFT_SKIP;
  160. goto cleanup;
  161. }
  162. parent = cg_name(root, "cg_test_parent");
  163. child = cg_name(root, "cg_test_parent/cg_test_child");
  164. if (!parent || !child)
  165. goto cleanup;
  166. if (cg_create(parent))
  167. goto cleanup;
  168. if (cg_create(child))
  169. goto cleanup;
  170. if (cg_write(parent, "cgroup.type", "threaded"))
  171. goto cleanup;
  172. if (cg_write(child, "cgroup.type", "threaded"))
  173. goto cleanup;
  174. if (cg_write(parent, "cgroup.subtree_control", "+cpu"))
  175. goto cleanup;
  176. if (cg_enter_current(parent))
  177. goto cleanup;
  178. ret = KSFT_PASS;
  179. cleanup:
  180. cg_enter_current(root);
  181. cg_enter_current(root);
  182. if (child)
  183. cg_destroy(child);
  184. if (parent)
  185. cg_destroy(parent);
  186. free(child);
  187. free(parent);
  188. return ret;
  189. }
  190. /*
  191. * Test that you can't enable a controller on a child if it's not enabled
  192. * on the parent.
  193. */
  194. static int test_cgcore_top_down_constraint_enable(const char *root)
  195. {
  196. int ret = KSFT_FAIL;
  197. char *parent = NULL, *child = NULL;
  198. parent = cg_name(root, "cg_test_parent");
  199. child = cg_name(root, "cg_test_parent/cg_test_child");
  200. if (!parent || !child)
  201. goto cleanup;
  202. if (cg_create(parent))
  203. goto cleanup;
  204. if (cg_create(child))
  205. goto cleanup;
  206. if (!cg_write(child, "cgroup.subtree_control", "+memory"))
  207. goto cleanup;
  208. ret = KSFT_PASS;
  209. cleanup:
  210. if (child)
  211. cg_destroy(child);
  212. if (parent)
  213. cg_destroy(parent);
  214. free(child);
  215. free(parent);
  216. return ret;
  217. }
  218. /*
  219. * Test that you can't disable a controller on a parent
  220. * if it's enabled in a child.
  221. */
  222. static int test_cgcore_top_down_constraint_disable(const char *root)
  223. {
  224. int ret = KSFT_FAIL;
  225. char *parent = NULL, *child = NULL;
  226. parent = cg_name(root, "cg_test_parent");
  227. child = cg_name(root, "cg_test_parent/cg_test_child");
  228. if (!parent || !child)
  229. goto cleanup;
  230. if (cg_create(parent))
  231. goto cleanup;
  232. if (cg_create(child))
  233. goto cleanup;
  234. if (cg_write(parent, "cgroup.subtree_control", "+memory"))
  235. goto cleanup;
  236. if (cg_write(child, "cgroup.subtree_control", "+memory"))
  237. goto cleanup;
  238. if (!cg_write(parent, "cgroup.subtree_control", "-memory"))
  239. goto cleanup;
  240. ret = KSFT_PASS;
  241. cleanup:
  242. if (child)
  243. cg_destroy(child);
  244. if (parent)
  245. cg_destroy(parent);
  246. free(child);
  247. free(parent);
  248. return ret;
  249. }
  250. /*
  251. * Test internal process constraint.
  252. * You can't add a pid to a domain parent if a controller is enabled.
  253. */
  254. static int test_cgcore_internal_process_constraint(const char *root)
  255. {
  256. int ret = KSFT_FAIL;
  257. char *parent = NULL, *child = NULL;
  258. parent = cg_name(root, "cg_test_parent");
  259. child = cg_name(root, "cg_test_parent/cg_test_child");
  260. if (!parent || !child)
  261. goto cleanup;
  262. if (cg_create(parent))
  263. goto cleanup;
  264. if (cg_create(child))
  265. goto cleanup;
  266. if (cg_write(parent, "cgroup.subtree_control", "+memory"))
  267. goto cleanup;
  268. if (!cg_enter_current(parent))
  269. goto cleanup;
  270. ret = KSFT_PASS;
  271. cleanup:
  272. if (child)
  273. cg_destroy(child);
  274. if (parent)
  275. cg_destroy(parent);
  276. free(child);
  277. free(parent);
  278. return ret;
  279. }
  280. #define T(x) { x, #x }
  281. struct corecg_test {
  282. int (*fn)(const char *root);
  283. const char *name;
  284. } tests[] = {
  285. T(test_cgcore_internal_process_constraint),
  286. T(test_cgcore_top_down_constraint_enable),
  287. T(test_cgcore_top_down_constraint_disable),
  288. T(test_cgcore_no_internal_process_constraint_on_threads),
  289. T(test_cgcore_parent_becomes_threaded),
  290. T(test_cgcore_invalid_domain),
  291. T(test_cgcore_populated),
  292. };
  293. #undef T
  294. int main(int argc, char *argv[])
  295. {
  296. char root[PATH_MAX];
  297. int i, ret = EXIT_SUCCESS;
  298. if (cg_find_unified_root(root, sizeof(root)))
  299. ksft_exit_skip("cgroup v2 isn't mounted\n");
  300. for (i = 0; i < ARRAY_SIZE(tests); i++) {
  301. switch (tests[i].fn(root)) {
  302. case KSFT_PASS:
  303. ksft_test_result_pass("%s\n", tests[i].name);
  304. break;
  305. case KSFT_SKIP:
  306. ksft_test_result_skip("%s\n", tests[i].name);
  307. break;
  308. default:
  309. ret = EXIT_FAILURE;
  310. ksft_test_result_fail("%s\n", tests[i].name);
  311. break;
  312. }
  313. }
  314. return ret;
  315. }