Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

cgroup: selftests: Add tests for freezer time

Test cgroup v2 freezer time stat. Freezer time accounting should
be independent of other cgroups in the hierarchy and should increase
iff a cgroup is CGRP_FREEZE (regardless of whether it reaches
CGRP_FROZEN).

Skip these tests on systems without freeze time accounting.

Signed-off-by: Tiffany Yang <ynaffit@google.com>
Cc: Michal Koutný <mkoutny@suse.com>
Signed-off-by: Tejun Heo <tj@kernel.org>

authored by

Tiffany Yang and committed by
Tejun Heo
7b281a45 afa3701c

+663
+663
tools/testing/selftests/cgroup/test_freezer.c
··· 804 804 return ret; 805 805 } 806 806 807 + /* 808 + * Get the current frozen_usec for the cgroup. 809 + */ 810 + static long cg_check_freezetime(const char *cgroup) 811 + { 812 + return cg_read_key_long(cgroup, "cgroup.stat.local", 813 + "frozen_usec "); 814 + } 815 + 816 + /* 817 + * Test that the freeze time will behave as expected for an empty cgroup. 818 + */ 819 + static int test_cgfreezer_time_empty(const char *root) 820 + { 821 + int ret = KSFT_FAIL; 822 + char *cgroup = NULL; 823 + long prev, curr; 824 + 825 + cgroup = cg_name(root, "cg_time_test_empty"); 826 + if (!cgroup) 827 + goto cleanup; 828 + 829 + /* 830 + * 1) Create an empty cgroup and check that its freeze time 831 + * is 0. 832 + */ 833 + if (cg_create(cgroup)) 834 + goto cleanup; 835 + 836 + curr = cg_check_freezetime(cgroup); 837 + if (curr < 0) { 838 + ret = KSFT_SKIP; 839 + goto cleanup; 840 + } 841 + if (curr > 0) { 842 + debug("Expect time (%ld) to be 0\n", curr); 843 + goto cleanup; 844 + } 845 + 846 + if (cg_freeze_nowait(cgroup, true)) 847 + goto cleanup; 848 + 849 + /* 850 + * 2) Sleep for 1000 us. Check that the freeze time is at 851 + * least 1000 us. 852 + */ 853 + usleep(1000); 854 + curr = cg_check_freezetime(cgroup); 855 + if (curr < 1000) { 856 + debug("Expect time (%ld) to be at least 1000 us\n", 857 + curr); 858 + goto cleanup; 859 + } 860 + 861 + /* 862 + * 3) Unfreeze the cgroup. Check that the freeze time is 863 + * larger than at 2). 864 + */ 865 + if (cg_freeze_nowait(cgroup, false)) 866 + goto cleanup; 867 + prev = curr; 868 + curr = cg_check_freezetime(cgroup); 869 + if (curr <= prev) { 870 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 871 + curr, prev); 872 + goto cleanup; 873 + } 874 + 875 + /* 876 + * 4) Check the freeze time again to ensure that it has not 877 + * changed. 878 + */ 879 + prev = curr; 880 + curr = cg_check_freezetime(cgroup); 881 + if (curr != prev) { 882 + debug("Expect time (%ld) to be unchanged from previous check (%ld)\n", 883 + curr, prev); 884 + goto cleanup; 885 + } 886 + 887 + ret = KSFT_PASS; 888 + 889 + cleanup: 890 + if (cgroup) 891 + cg_destroy(cgroup); 892 + free(cgroup); 893 + return ret; 894 + } 895 + 896 + /* 897 + * A simple test for cgroup freezer time accounting. This test follows 898 + * the same flow as test_cgfreezer_time_empty, but with a single process 899 + * in the cgroup. 900 + */ 901 + static int test_cgfreezer_time_simple(const char *root) 902 + { 903 + int ret = KSFT_FAIL; 904 + char *cgroup = NULL; 905 + long prev, curr; 906 + 907 + cgroup = cg_name(root, "cg_time_test_simple"); 908 + if (!cgroup) 909 + goto cleanup; 910 + 911 + /* 912 + * 1) Create a cgroup and check that its freeze time is 0. 913 + */ 914 + if (cg_create(cgroup)) 915 + goto cleanup; 916 + 917 + curr = cg_check_freezetime(cgroup); 918 + if (curr < 0) { 919 + ret = KSFT_SKIP; 920 + goto cleanup; 921 + } 922 + if (curr > 0) { 923 + debug("Expect time (%ld) to be 0\n", curr); 924 + goto cleanup; 925 + } 926 + 927 + /* 928 + * 2) Populate the cgroup with one child and check that the 929 + * freeze time is still 0. 930 + */ 931 + cg_run_nowait(cgroup, child_fn, NULL); 932 + prev = curr; 933 + curr = cg_check_freezetime(cgroup); 934 + if (curr > prev) { 935 + debug("Expect time (%ld) to be 0\n", curr); 936 + goto cleanup; 937 + } 938 + 939 + if (cg_freeze_nowait(cgroup, true)) 940 + goto cleanup; 941 + 942 + /* 943 + * 3) Sleep for 1000 us. Check that the freeze time is at 944 + * least 1000 us. 945 + */ 946 + usleep(1000); 947 + prev = curr; 948 + curr = cg_check_freezetime(cgroup); 949 + if (curr < 1000) { 950 + debug("Expect time (%ld) to be at least 1000 us\n", 951 + curr); 952 + goto cleanup; 953 + } 954 + 955 + /* 956 + * 4) Unfreeze the cgroup. Check that the freeze time is 957 + * larger than at 3). 958 + */ 959 + if (cg_freeze_nowait(cgroup, false)) 960 + goto cleanup; 961 + prev = curr; 962 + curr = cg_check_freezetime(cgroup); 963 + if (curr <= prev) { 964 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 965 + curr, prev); 966 + goto cleanup; 967 + } 968 + 969 + /* 970 + * 5) Sleep for 1000 us. Check that the freeze time is the 971 + * same as at 4). 972 + */ 973 + usleep(1000); 974 + prev = curr; 975 + curr = cg_check_freezetime(cgroup); 976 + if (curr != prev) { 977 + debug("Expect time (%ld) to be unchanged from previous check (%ld)\n", 978 + curr, prev); 979 + goto cleanup; 980 + } 981 + 982 + ret = KSFT_PASS; 983 + 984 + cleanup: 985 + if (cgroup) 986 + cg_destroy(cgroup); 987 + free(cgroup); 988 + return ret; 989 + } 990 + 991 + /* 992 + * Test that freezer time accounting works as expected, even while we're 993 + * populating a cgroup with processes. 994 + */ 995 + static int test_cgfreezer_time_populate(const char *root) 996 + { 997 + int ret = KSFT_FAIL; 998 + char *cgroup = NULL; 999 + long prev, curr; 1000 + int i; 1001 + 1002 + cgroup = cg_name(root, "cg_time_test_populate"); 1003 + if (!cgroup) 1004 + goto cleanup; 1005 + 1006 + if (cg_create(cgroup)) 1007 + goto cleanup; 1008 + 1009 + curr = cg_check_freezetime(cgroup); 1010 + if (curr < 0) { 1011 + ret = KSFT_SKIP; 1012 + goto cleanup; 1013 + } 1014 + if (curr > 0) { 1015 + debug("Expect time (%ld) to be 0\n", curr); 1016 + goto cleanup; 1017 + } 1018 + 1019 + /* 1020 + * 1) Populate the cgroup with 100 processes. Check that 1021 + * the freeze time is 0. 1022 + */ 1023 + for (i = 0; i < 100; i++) 1024 + cg_run_nowait(cgroup, child_fn, NULL); 1025 + prev = curr; 1026 + curr = cg_check_freezetime(cgroup); 1027 + if (curr != prev) { 1028 + debug("Expect time (%ld) to be 0\n", curr); 1029 + goto cleanup; 1030 + } 1031 + 1032 + /* 1033 + * 2) Wait for the group to become fully populated. Check 1034 + * that the freeze time is 0. 1035 + */ 1036 + if (cg_wait_for_proc_count(cgroup, 100)) 1037 + goto cleanup; 1038 + prev = curr; 1039 + curr = cg_check_freezetime(cgroup); 1040 + if (curr != prev) { 1041 + debug("Expect time (%ld) to be 0\n", curr); 1042 + goto cleanup; 1043 + } 1044 + 1045 + /* 1046 + * 3) Freeze the cgroup and then populate it with 100 more 1047 + * processes. Check that the freeze time continues to grow. 1048 + */ 1049 + if (cg_freeze_nowait(cgroup, true)) 1050 + goto cleanup; 1051 + prev = curr; 1052 + curr = cg_check_freezetime(cgroup); 1053 + if (curr <= prev) { 1054 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 1055 + curr, prev); 1056 + goto cleanup; 1057 + } 1058 + 1059 + for (i = 0; i < 100; i++) 1060 + cg_run_nowait(cgroup, child_fn, NULL); 1061 + prev = curr; 1062 + curr = cg_check_freezetime(cgroup); 1063 + if (curr <= prev) { 1064 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 1065 + curr, prev); 1066 + goto cleanup; 1067 + } 1068 + 1069 + /* 1070 + * 4) Wait for the group to become fully populated. Check 1071 + * that the freeze time is larger than at 3). 1072 + */ 1073 + if (cg_wait_for_proc_count(cgroup, 200)) 1074 + goto cleanup; 1075 + prev = curr; 1076 + curr = cg_check_freezetime(cgroup); 1077 + if (curr <= prev) { 1078 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 1079 + curr, prev); 1080 + goto cleanup; 1081 + } 1082 + 1083 + /* 1084 + * 5) Unfreeze the cgroup. Check that the freeze time is 1085 + * larger than at 4). 1086 + */ 1087 + if (cg_freeze_nowait(cgroup, false)) 1088 + goto cleanup; 1089 + prev = curr; 1090 + curr = cg_check_freezetime(cgroup); 1091 + if (curr <= prev) { 1092 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 1093 + curr, prev); 1094 + goto cleanup; 1095 + } 1096 + 1097 + /* 1098 + * 6) Kill the processes. Check that the freeze time is the 1099 + * same as it was at 5). 1100 + */ 1101 + if (cg_killall(cgroup)) 1102 + goto cleanup; 1103 + prev = curr; 1104 + curr = cg_check_freezetime(cgroup); 1105 + if (curr != prev) { 1106 + debug("Expect time (%ld) to be unchanged from previous check (%ld)\n", 1107 + curr, prev); 1108 + goto cleanup; 1109 + } 1110 + 1111 + /* 1112 + * 7) Freeze and unfreeze the cgroup. Check that the freeze 1113 + * time is larger than it was at 6). 1114 + */ 1115 + if (cg_freeze_nowait(cgroup, true)) 1116 + goto cleanup; 1117 + if (cg_freeze_nowait(cgroup, false)) 1118 + goto cleanup; 1119 + prev = curr; 1120 + curr = cg_check_freezetime(cgroup); 1121 + if (curr <= prev) { 1122 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 1123 + curr, prev); 1124 + goto cleanup; 1125 + } 1126 + 1127 + ret = KSFT_PASS; 1128 + 1129 + cleanup: 1130 + if (cgroup) 1131 + cg_destroy(cgroup); 1132 + free(cgroup); 1133 + return ret; 1134 + } 1135 + 1136 + /* 1137 + * Test that frozen time for a cgroup continues to work as expected, 1138 + * even as processes are migrated. Frozen cgroup A's freeze time should 1139 + * continue to increase and running cgroup B's should stay 0. 1140 + */ 1141 + static int test_cgfreezer_time_migrate(const char *root) 1142 + { 1143 + long prev_A, curr_A, curr_B; 1144 + char *cgroup[2] = {0}; 1145 + int ret = KSFT_FAIL; 1146 + int pid; 1147 + 1148 + cgroup[0] = cg_name(root, "cg_time_test_migrate_A"); 1149 + if (!cgroup[0]) 1150 + goto cleanup; 1151 + 1152 + cgroup[1] = cg_name(root, "cg_time_test_migrate_B"); 1153 + if (!cgroup[1]) 1154 + goto cleanup; 1155 + 1156 + if (cg_create(cgroup[0])) 1157 + goto cleanup; 1158 + 1159 + if (cg_check_freezetime(cgroup[0]) < 0) { 1160 + ret = KSFT_SKIP; 1161 + goto cleanup; 1162 + } 1163 + 1164 + if (cg_create(cgroup[1])) 1165 + goto cleanup; 1166 + 1167 + pid = cg_run_nowait(cgroup[0], child_fn, NULL); 1168 + if (pid < 0) 1169 + goto cleanup; 1170 + 1171 + if (cg_wait_for_proc_count(cgroup[0], 1)) 1172 + goto cleanup; 1173 + 1174 + curr_A = cg_check_freezetime(cgroup[0]); 1175 + if (curr_A) { 1176 + debug("Expect time (%ld) to be 0\n", curr_A); 1177 + goto cleanup; 1178 + } 1179 + curr_B = cg_check_freezetime(cgroup[1]); 1180 + if (curr_B) { 1181 + debug("Expect time (%ld) to be 0\n", curr_B); 1182 + goto cleanup; 1183 + } 1184 + 1185 + /* 1186 + * Freeze cgroup A. 1187 + */ 1188 + if (cg_freeze_wait(cgroup[0], true)) 1189 + goto cleanup; 1190 + prev_A = curr_A; 1191 + curr_A = cg_check_freezetime(cgroup[0]); 1192 + if (curr_A <= prev_A) { 1193 + debug("Expect time (%ld) to be > 0\n", curr_A); 1194 + goto cleanup; 1195 + } 1196 + 1197 + /* 1198 + * Migrate from A (frozen) to B (running). 1199 + */ 1200 + if (cg_enter(cgroup[1], pid)) 1201 + goto cleanup; 1202 + 1203 + usleep(1000); 1204 + curr_B = cg_check_freezetime(cgroup[1]); 1205 + if (curr_B) { 1206 + debug("Expect time (%ld) to be 0\n", curr_B); 1207 + goto cleanup; 1208 + } 1209 + 1210 + prev_A = curr_A; 1211 + curr_A = cg_check_freezetime(cgroup[0]); 1212 + if (curr_A <= prev_A) { 1213 + debug("Expect time (%ld) to be more than previous check (%ld)\n", 1214 + curr_A, prev_A); 1215 + goto cleanup; 1216 + } 1217 + 1218 + ret = KSFT_PASS; 1219 + 1220 + cleanup: 1221 + if (cgroup[0]) 1222 + cg_destroy(cgroup[0]); 1223 + free(cgroup[0]); 1224 + if (cgroup[1]) 1225 + cg_destroy(cgroup[1]); 1226 + free(cgroup[1]); 1227 + return ret; 1228 + } 1229 + 1230 + /* 1231 + * The test creates a cgroup and freezes it. Then it creates a child cgroup. 1232 + * After that it checks that the child cgroup has a non-zero freeze time 1233 + * that is less than the parent's. Next, it freezes the child, unfreezes 1234 + * the parent, and sleeps. Finally, it checks that the child's freeze 1235 + * time has grown larger than the parent's. 1236 + */ 1237 + static int test_cgfreezer_time_parent(const char *root) 1238 + { 1239 + char *parent, *child = NULL; 1240 + int ret = KSFT_FAIL; 1241 + long ptime, ctime; 1242 + 1243 + parent = cg_name(root, "cg_test_parent_A"); 1244 + if (!parent) 1245 + goto cleanup; 1246 + 1247 + child = cg_name(parent, "cg_test_parent_B"); 1248 + if (!child) 1249 + goto cleanup; 1250 + 1251 + if (cg_create(parent)) 1252 + goto cleanup; 1253 + 1254 + if (cg_check_freezetime(parent) < 0) { 1255 + ret = KSFT_SKIP; 1256 + goto cleanup; 1257 + } 1258 + 1259 + if (cg_freeze_wait(parent, true)) 1260 + goto cleanup; 1261 + 1262 + usleep(1000); 1263 + if (cg_create(child)) 1264 + goto cleanup; 1265 + 1266 + if (cg_check_frozen(child, true)) 1267 + goto cleanup; 1268 + 1269 + /* 1270 + * Since the parent was frozen the entire time the child cgroup 1271 + * was being created, we expect the parent's freeze time to be 1272 + * larger than the child's. 1273 + * 1274 + * Ideally, we would be able to check both times simultaneously, 1275 + * but here we get the child's after we get the parent's. 1276 + */ 1277 + ptime = cg_check_freezetime(parent); 1278 + ctime = cg_check_freezetime(child); 1279 + if (ptime <= ctime) { 1280 + debug("Expect ptime (%ld) > ctime (%ld)\n", ptime, ctime); 1281 + goto cleanup; 1282 + } 1283 + 1284 + if (cg_freeze_nowait(child, true)) 1285 + goto cleanup; 1286 + 1287 + if (cg_freeze_wait(parent, false)) 1288 + goto cleanup; 1289 + 1290 + if (cg_check_frozen(child, true)) 1291 + goto cleanup; 1292 + 1293 + usleep(100000); 1294 + 1295 + ctime = cg_check_freezetime(child); 1296 + ptime = cg_check_freezetime(parent); 1297 + 1298 + if (ctime <= ptime) { 1299 + debug("Expect ctime (%ld) > ptime (%ld)\n", ctime, ptime); 1300 + goto cleanup; 1301 + } 1302 + 1303 + ret = KSFT_PASS; 1304 + 1305 + cleanup: 1306 + if (child) 1307 + cg_destroy(child); 1308 + free(child); 1309 + if (parent) 1310 + cg_destroy(parent); 1311 + free(parent); 1312 + return ret; 1313 + } 1314 + 1315 + /* 1316 + * The test creates a parent cgroup and a child cgroup. Then, it freezes 1317 + * the child and checks that the child's freeze time is greater than the 1318 + * parent's, which should be zero. 1319 + */ 1320 + static int test_cgfreezer_time_child(const char *root) 1321 + { 1322 + char *parent, *child = NULL; 1323 + int ret = KSFT_FAIL; 1324 + long ptime, ctime; 1325 + 1326 + parent = cg_name(root, "cg_test_child_A"); 1327 + if (!parent) 1328 + goto cleanup; 1329 + 1330 + child = cg_name(parent, "cg_test_child_B"); 1331 + if (!child) 1332 + goto cleanup; 1333 + 1334 + if (cg_create(parent)) 1335 + goto cleanup; 1336 + 1337 + if (cg_check_freezetime(parent) < 0) { 1338 + ret = KSFT_SKIP; 1339 + goto cleanup; 1340 + } 1341 + 1342 + if (cg_create(child)) 1343 + goto cleanup; 1344 + 1345 + if (cg_freeze_wait(child, true)) 1346 + goto cleanup; 1347 + 1348 + ctime = cg_check_freezetime(child); 1349 + ptime = cg_check_freezetime(parent); 1350 + if (ptime != 0) { 1351 + debug("Expect ptime (%ld) to be 0\n", ptime); 1352 + goto cleanup; 1353 + } 1354 + 1355 + if (ctime <= ptime) { 1356 + debug("Expect ctime (%ld) <= ptime (%ld)\n", ctime, ptime); 1357 + goto cleanup; 1358 + } 1359 + 1360 + ret = KSFT_PASS; 1361 + 1362 + cleanup: 1363 + if (child) 1364 + cg_destroy(child); 1365 + free(child); 1366 + if (parent) 1367 + cg_destroy(parent); 1368 + free(parent); 1369 + return ret; 1370 + } 1371 + 1372 + /* 1373 + * The test creates the following hierarchy: 1374 + * A 1375 + * | 1376 + * B 1377 + * | 1378 + * C 1379 + * 1380 + * Then it freezes the cgroups in the order C, B, A. 1381 + * Then it unfreezes the cgroups in the order A, B, C. 1382 + * Then it checks that C's freeze time is larger than B's and 1383 + * that B's is larger than A's. 1384 + */ 1385 + static int test_cgfreezer_time_nested(const char *root) 1386 + { 1387 + char *cgroup[3] = {0}; 1388 + int ret = KSFT_FAIL; 1389 + long time[3] = {0}; 1390 + int i; 1391 + 1392 + cgroup[0] = cg_name(root, "cg_test_time_A"); 1393 + if (!cgroup[0]) 1394 + goto cleanup; 1395 + 1396 + cgroup[1] = cg_name(cgroup[0], "B"); 1397 + if (!cgroup[1]) 1398 + goto cleanup; 1399 + 1400 + cgroup[2] = cg_name(cgroup[1], "C"); 1401 + if (!cgroup[2]) 1402 + goto cleanup; 1403 + 1404 + if (cg_create(cgroup[0])) 1405 + goto cleanup; 1406 + 1407 + if (cg_check_freezetime(cgroup[0]) < 0) { 1408 + ret = KSFT_SKIP; 1409 + goto cleanup; 1410 + } 1411 + 1412 + if (cg_create(cgroup[1])) 1413 + goto cleanup; 1414 + 1415 + if (cg_create(cgroup[2])) 1416 + goto cleanup; 1417 + 1418 + if (cg_freeze_nowait(cgroup[2], true)) 1419 + goto cleanup; 1420 + 1421 + if (cg_freeze_nowait(cgroup[1], true)) 1422 + goto cleanup; 1423 + 1424 + if (cg_freeze_nowait(cgroup[0], true)) 1425 + goto cleanup; 1426 + 1427 + usleep(1000); 1428 + 1429 + if (cg_freeze_nowait(cgroup[0], false)) 1430 + goto cleanup; 1431 + 1432 + if (cg_freeze_nowait(cgroup[1], false)) 1433 + goto cleanup; 1434 + 1435 + if (cg_freeze_nowait(cgroup[2], false)) 1436 + goto cleanup; 1437 + 1438 + time[2] = cg_check_freezetime(cgroup[2]); 1439 + time[1] = cg_check_freezetime(cgroup[1]); 1440 + time[0] = cg_check_freezetime(cgroup[0]); 1441 + 1442 + if (time[2] <= time[1]) { 1443 + debug("Expect C's time (%ld) > B's time (%ld)", time[2], time[1]); 1444 + goto cleanup; 1445 + } 1446 + 1447 + if (time[1] <= time[0]) { 1448 + debug("Expect B's time (%ld) > A's time (%ld)", time[1], time[0]); 1449 + goto cleanup; 1450 + } 1451 + 1452 + ret = KSFT_PASS; 1453 + 1454 + cleanup: 1455 + for (i = 2; i >= 0 && cgroup[i]; i--) { 1456 + cg_destroy(cgroup[i]); 1457 + free(cgroup[i]); 1458 + } 1459 + 1460 + return ret; 1461 + } 1462 + 807 1463 #define T(x) { x, #x } 808 1464 struct cgfreezer_test { 809 1465 int (*fn)(const char *root); ··· 1475 819 T(test_cgfreezer_stopped), 1476 820 T(test_cgfreezer_ptraced), 1477 821 T(test_cgfreezer_vfork), 822 + T(test_cgfreezer_time_empty), 823 + T(test_cgfreezer_time_simple), 824 + T(test_cgfreezer_time_populate), 825 + T(test_cgfreezer_time_migrate), 826 + T(test_cgfreezer_time_parent), 827 + T(test_cgfreezer_time_child), 828 + T(test_cgfreezer_time_nested), 1478 829 }; 1479 830 #undef T 1480 831