linux/tools/testing/selftests/cgroup/test_freezer.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2#include <stdbool.h>
   3#include <linux/limits.h>
   4#include <sys/ptrace.h>
   5#include <sys/types.h>
   6#include <sys/mman.h>
   7#include <unistd.h>
   8#include <stdio.h>
   9#include <errno.h>
  10#include <stdlib.h>
  11#include <string.h>
  12#include <sys/wait.h>
  13
  14#include "../kselftest.h"
  15#include "cgroup_util.h"
  16
  17#define DEBUG
  18#ifdef DEBUG
  19#define debug(args...) fprintf(stderr, args)
  20#else
  21#define debug(args...)
  22#endif
  23
  24/*
  25 * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.
  26 */
  27static int cg_check_frozen(const char *cgroup, bool frozen)
  28{
  29        if (frozen) {
  30                if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
  31                        debug("Cgroup %s isn't frozen\n", cgroup);
  32                        return -1;
  33                }
  34        } else {
  35                /*
  36                 * Check the cgroup.events::frozen value.
  37                 */
  38                if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
  39                        debug("Cgroup %s is frozen\n", cgroup);
  40                        return -1;
  41                }
  42        }
  43
  44        return 0;
  45}
  46
  47/*
  48 * Freeze the given cgroup.
  49 */
  50static int cg_freeze_nowait(const char *cgroup, bool freeze)
  51{
  52        return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
  53}
  54
  55/*
  56 * Attach a task to the given cgroup and wait for a cgroup frozen event.
  57 * All transient events (e.g. populated) are ignored.
  58 */
  59static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
  60                                        bool frozen)
  61{
  62        int fd, ret = -1;
  63        int attempts;
  64
  65        fd = cg_prepare_for_wait(cgroup);
  66        if (fd < 0)
  67                return fd;
  68
  69        ret = cg_enter(cgroup, pid);
  70        if (ret)
  71                goto out;
  72
  73        for (attempts = 0; attempts < 10; attempts++) {
  74                ret = cg_wait_for(fd);
  75                if (ret)
  76                        break;
  77
  78                ret = cg_check_frozen(cgroup, frozen);
  79                if (ret)
  80                        continue;
  81        }
  82
  83out:
  84        close(fd);
  85        return ret;
  86}
  87
  88/*
  89 * Freeze the given cgroup and wait for the inotify signal.
  90 * If there are no events in 10 seconds, treat this as an error.
  91 * Then check that the cgroup is in the desired state.
  92 */
  93static int cg_freeze_wait(const char *cgroup, bool freeze)
  94{
  95        int fd, ret = -1;
  96
  97        fd = cg_prepare_for_wait(cgroup);
  98        if (fd < 0)
  99                return fd;
 100
 101        ret = cg_freeze_nowait(cgroup, freeze);
 102        if (ret) {
 103                debug("Error: cg_freeze_nowait() failed\n");
 104                goto out;
 105        }
 106
 107        ret = cg_wait_for(fd);
 108        if (ret)
 109                goto out;
 110
 111        ret = cg_check_frozen(cgroup, freeze);
 112out:
 113        close(fd);
 114        return ret;
 115}
 116
 117/*
 118 * A simple process running in a sleep loop until being
 119 * re-parented.
 120 */
 121static int child_fn(const char *cgroup, void *arg)
 122{
 123        int ppid = getppid();
 124
 125        while (getppid() == ppid)
 126                usleep(1000);
 127
 128        return getppid() == ppid;
 129}
 130
 131/*
 132 * A simple test for the cgroup freezer: populated the cgroup with 100
 133 * running processes and freeze it. Then unfreeze it. Then it kills all
 134 * processes and destroys the cgroup.
 135 */
 136static int test_cgfreezer_simple(const char *root)
 137{
 138        int ret = KSFT_FAIL;
 139        char *cgroup = NULL;
 140        int i;
 141
 142        cgroup = cg_name(root, "cg_test_simple");
 143        if (!cgroup)
 144                goto cleanup;
 145
 146        if (cg_create(cgroup))
 147                goto cleanup;
 148
 149        for (i = 0; i < 100; i++)
 150                cg_run_nowait(cgroup, child_fn, NULL);
 151
 152        if (cg_wait_for_proc_count(cgroup, 100))
 153                goto cleanup;
 154
 155        if (cg_check_frozen(cgroup, false))
 156                goto cleanup;
 157
 158        if (cg_freeze_wait(cgroup, true))
 159                goto cleanup;
 160
 161        if (cg_freeze_wait(cgroup, false))
 162                goto cleanup;
 163
 164        ret = KSFT_PASS;
 165
 166cleanup:
 167        if (cgroup)
 168                cg_destroy(cgroup);
 169        free(cgroup);
 170        return ret;
 171}
 172
 173/*
 174 * The test creates the following hierarchy:
 175 *       A
 176 *    / / \ \
 177 *   B  E  I K
 178 *  /\  |
 179 * C  D F
 180 *      |
 181 *      G
 182 *      |
 183 *      H
 184 *
 185 * with a process in C, H and 3 processes in K.
 186 * Then it tries to freeze and unfreeze the whole tree.
 187 */
 188static int test_cgfreezer_tree(const char *root)
 189{
 190        char *cgroup[10] = {0};
 191        int ret = KSFT_FAIL;
 192        int i;
 193
 194        cgroup[0] = cg_name(root, "cg_test_tree_A");
 195        if (!cgroup[0])
 196                goto cleanup;
 197
 198        cgroup[1] = cg_name(cgroup[0], "B");
 199        if (!cgroup[1])
 200                goto cleanup;
 201
 202        cgroup[2] = cg_name(cgroup[1], "C");
 203        if (!cgroup[2])
 204                goto cleanup;
 205
 206        cgroup[3] = cg_name(cgroup[1], "D");
 207        if (!cgroup[3])
 208                goto cleanup;
 209
 210        cgroup[4] = cg_name(cgroup[0], "E");
 211        if (!cgroup[4])
 212                goto cleanup;
 213
 214        cgroup[5] = cg_name(cgroup[4], "F");
 215        if (!cgroup[5])
 216                goto cleanup;
 217
 218        cgroup[6] = cg_name(cgroup[5], "G");
 219        if (!cgroup[6])
 220                goto cleanup;
 221
 222        cgroup[7] = cg_name(cgroup[6], "H");
 223        if (!cgroup[7])
 224                goto cleanup;
 225
 226        cgroup[8] = cg_name(cgroup[0], "I");
 227        if (!cgroup[8])
 228                goto cleanup;
 229
 230        cgroup[9] = cg_name(cgroup[0], "K");
 231        if (!cgroup[9])
 232                goto cleanup;
 233
 234        for (i = 0; i < 10; i++)
 235                if (cg_create(cgroup[i]))
 236                        goto cleanup;
 237
 238        cg_run_nowait(cgroup[2], child_fn, NULL);
 239        cg_run_nowait(cgroup[7], child_fn, NULL);
 240        cg_run_nowait(cgroup[9], child_fn, NULL);
 241        cg_run_nowait(cgroup[9], child_fn, NULL);
 242        cg_run_nowait(cgroup[9], child_fn, NULL);
 243
 244        /*
 245         * Wait until all child processes will enter
 246         * corresponding cgroups.
 247         */
 248
 249        if (cg_wait_for_proc_count(cgroup[2], 1) ||
 250            cg_wait_for_proc_count(cgroup[7], 1) ||
 251            cg_wait_for_proc_count(cgroup[9], 3))
 252                goto cleanup;
 253
 254        /*
 255         * Freeze B.
 256         */
 257        if (cg_freeze_wait(cgroup[1], true))
 258                goto cleanup;
 259
 260        /*
 261         * Freeze F.
 262         */
 263        if (cg_freeze_wait(cgroup[5], true))
 264                goto cleanup;
 265
 266        /*
 267         * Freeze G.
 268         */
 269        if (cg_freeze_wait(cgroup[6], true))
 270                goto cleanup;
 271
 272        /*
 273         * Check that A and E are not frozen.
 274         */
 275        if (cg_check_frozen(cgroup[0], false))
 276                goto cleanup;
 277
 278        if (cg_check_frozen(cgroup[4], false))
 279                goto cleanup;
 280
 281        /*
 282         * Freeze A. Check that A, B and E are frozen.
 283         */
 284        if (cg_freeze_wait(cgroup[0], true))
 285                goto cleanup;
 286
 287        if (cg_check_frozen(cgroup[1], true))
 288                goto cleanup;
 289
 290        if (cg_check_frozen(cgroup[4], true))
 291                goto cleanup;
 292
 293        /*
 294         * Unfreeze B, F and G
 295         */
 296        if (cg_freeze_nowait(cgroup[1], false))
 297                goto cleanup;
 298
 299        if (cg_freeze_nowait(cgroup[5], false))
 300                goto cleanup;
 301
 302        if (cg_freeze_nowait(cgroup[6], false))
 303                goto cleanup;
 304
 305        /*
 306         * Check that C and H are still frozen.
 307         */
 308        if (cg_check_frozen(cgroup[2], true))
 309                goto cleanup;
 310
 311        if (cg_check_frozen(cgroup[7], true))
 312                goto cleanup;
 313
 314        /*
 315         * Unfreeze A. Check that A, C and K are not frozen.
 316         */
 317        if (cg_freeze_wait(cgroup[0], false))
 318                goto cleanup;
 319
 320        if (cg_check_frozen(cgroup[2], false))
 321                goto cleanup;
 322
 323        if (cg_check_frozen(cgroup[9], false))
 324                goto cleanup;
 325
 326        ret = KSFT_PASS;
 327
 328cleanup:
 329        for (i = 9; i >= 0 && cgroup[i]; i--) {
 330                cg_destroy(cgroup[i]);
 331                free(cgroup[i]);
 332        }
 333
 334        return ret;
 335}
 336
 337/*
 338 * A fork bomb emulator.
 339 */
 340static int forkbomb_fn(const char *cgroup, void *arg)
 341{
 342        int ppid;
 343
 344        fork();
 345        fork();
 346
 347        ppid = getppid();
 348
 349        while (getppid() == ppid)
 350                usleep(1000);
 351
 352        return getppid() == ppid;
 353}
 354
 355/*
 356 * The test runs a fork bomb in a cgroup and tries to freeze it.
 357 * Then it kills all processes and checks that cgroup isn't populated
 358 * anymore.
 359 */
 360static int test_cgfreezer_forkbomb(const char *root)
 361{
 362        int ret = KSFT_FAIL;
 363        char *cgroup = NULL;
 364
 365        cgroup = cg_name(root, "cg_forkbomb_test");
 366        if (!cgroup)
 367                goto cleanup;
 368
 369        if (cg_create(cgroup))
 370                goto cleanup;
 371
 372        cg_run_nowait(cgroup, forkbomb_fn, NULL);
 373
 374        usleep(100000);
 375
 376        if (cg_freeze_wait(cgroup, true))
 377                goto cleanup;
 378
 379        if (cg_killall(cgroup))
 380                goto cleanup;
 381
 382        if (cg_wait_for_proc_count(cgroup, 0))
 383                goto cleanup;
 384
 385        ret = KSFT_PASS;
 386
 387cleanup:
 388        if (cgroup)
 389                cg_destroy(cgroup);
 390        free(cgroup);
 391        return ret;
 392}
 393
 394/*
 395 * The test creates a cgroups and freezes it. Then it creates a child cgroup
 396 * and populates it with a task. After that it checks that the child cgroup
 397 * is frozen and the parent cgroup remains frozen too.
 398 */
 399static int test_cgfreezer_mkdir(const char *root)
 400{
 401        int ret = KSFT_FAIL;
 402        char *parent, *child = NULL;
 403        int pid;
 404
 405        parent = cg_name(root, "cg_test_mkdir_A");
 406        if (!parent)
 407                goto cleanup;
 408
 409        child = cg_name(parent, "cg_test_mkdir_B");
 410        if (!child)
 411                goto cleanup;
 412
 413        if (cg_create(parent))
 414                goto cleanup;
 415
 416        if (cg_freeze_wait(parent, true))
 417                goto cleanup;
 418
 419        if (cg_create(child))
 420                goto cleanup;
 421
 422        pid = cg_run_nowait(child, child_fn, NULL);
 423        if (pid < 0)
 424                goto cleanup;
 425
 426        if (cg_wait_for_proc_count(child, 1))
 427                goto cleanup;
 428
 429        if (cg_check_frozen(child, true))
 430                goto cleanup;
 431
 432        if (cg_check_frozen(parent, true))
 433                goto cleanup;
 434
 435        ret = KSFT_PASS;
 436
 437cleanup:
 438        if (child)
 439                cg_destroy(child);
 440        free(child);
 441        if (parent)
 442                cg_destroy(parent);
 443        free(parent);
 444        return ret;
 445}
 446
 447/*
 448 * The test creates two nested cgroups, freezes the parent
 449 * and removes the child. Then it checks that the parent cgroup
 450 * remains frozen and it's possible to create a new child
 451 * without unfreezing. The new child is frozen too.
 452 */
 453static int test_cgfreezer_rmdir(const char *root)
 454{
 455        int ret = KSFT_FAIL;
 456        char *parent, *child = NULL;
 457
 458        parent = cg_name(root, "cg_test_rmdir_A");
 459        if (!parent)
 460                goto cleanup;
 461
 462        child = cg_name(parent, "cg_test_rmdir_B");
 463        if (!child)
 464                goto cleanup;
 465
 466        if (cg_create(parent))
 467                goto cleanup;
 468
 469        if (cg_create(child))
 470                goto cleanup;
 471
 472        if (cg_freeze_wait(parent, true))
 473                goto cleanup;
 474
 475        if (cg_destroy(child))
 476                goto cleanup;
 477
 478        if (cg_check_frozen(parent, true))
 479                goto cleanup;
 480
 481        if (cg_create(child))
 482                goto cleanup;
 483
 484        if (cg_check_frozen(child, true))
 485                goto cleanup;
 486
 487        ret = KSFT_PASS;
 488
 489cleanup:
 490        if (child)
 491                cg_destroy(child);
 492        free(child);
 493        if (parent)
 494                cg_destroy(parent);
 495        free(parent);
 496        return ret;
 497}
 498
 499/*
 500 * The test creates two cgroups: A and B, runs a process in A
 501 * and performs several migrations:
 502 * 1) A (running) -> B (frozen)
 503 * 2) B (frozen) -> A (running)
 504 * 3) A (frozen) -> B (frozen)
 505 *
 506 * On each step it checks the actual state of both cgroups.
 507 */
 508static int test_cgfreezer_migrate(const char *root)
 509{
 510        int ret = KSFT_FAIL;
 511        char *cgroup[2] = {0};
 512        int pid;
 513
 514        cgroup[0] = cg_name(root, "cg_test_migrate_A");
 515        if (!cgroup[0])
 516                goto cleanup;
 517
 518        cgroup[1] = cg_name(root, "cg_test_migrate_B");
 519        if (!cgroup[1])
 520                goto cleanup;
 521
 522        if (cg_create(cgroup[0]))
 523                goto cleanup;
 524
 525        if (cg_create(cgroup[1]))
 526                goto cleanup;
 527
 528        pid = cg_run_nowait(cgroup[0], child_fn, NULL);
 529        if (pid < 0)
 530                goto cleanup;
 531
 532        if (cg_wait_for_proc_count(cgroup[0], 1))
 533                goto cleanup;
 534
 535        /*
 536         * Migrate from A (running) to B (frozen)
 537         */
 538        if (cg_freeze_wait(cgroup[1], true))
 539                goto cleanup;
 540
 541        if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
 542                goto cleanup;
 543
 544        if (cg_check_frozen(cgroup[0], false))
 545                goto cleanup;
 546
 547        /*
 548         * Migrate from B (frozen) to A (running)
 549         */
 550        if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
 551                goto cleanup;
 552
 553        if (cg_check_frozen(cgroup[1], true))
 554                goto cleanup;
 555
 556        /*
 557         * Migrate from A (frozen) to B (frozen)
 558         */
 559        if (cg_freeze_wait(cgroup[0], true))
 560                goto cleanup;
 561
 562        if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
 563                goto cleanup;
 564
 565        if (cg_check_frozen(cgroup[0], true))
 566                goto cleanup;
 567
 568        ret = KSFT_PASS;
 569
 570cleanup:
 571        if (cgroup[0])
 572                cg_destroy(cgroup[0]);
 573        free(cgroup[0]);
 574        if (cgroup[1])
 575                cg_destroy(cgroup[1]);
 576        free(cgroup[1]);
 577        return ret;
 578}
 579
 580/*
 581 * The test checks that ptrace works with a tracing process in a frozen cgroup.
 582 */
 583static int test_cgfreezer_ptrace(const char *root)
 584{
 585        int ret = KSFT_FAIL;
 586        char *cgroup = NULL;
 587        siginfo_t siginfo;
 588        int pid;
 589
 590        cgroup = cg_name(root, "cg_test_ptrace");
 591        if (!cgroup)
 592                goto cleanup;
 593
 594        if (cg_create(cgroup))
 595                goto cleanup;
 596
 597        pid = cg_run_nowait(cgroup, child_fn, NULL);
 598        if (pid < 0)
 599                goto cleanup;
 600
 601        if (cg_wait_for_proc_count(cgroup, 1))
 602                goto cleanup;
 603
 604        if (cg_freeze_wait(cgroup, true))
 605                goto cleanup;
 606
 607        if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
 608                goto cleanup;
 609
 610        if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
 611                goto cleanup;
 612
 613        waitpid(pid, NULL, 0);
 614
 615        /*
 616         * Cgroup has to remain frozen, however the test task
 617         * is in traced state.
 618         */
 619        if (cg_check_frozen(cgroup, true))
 620                goto cleanup;
 621
 622        if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
 623                goto cleanup;
 624
 625        if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
 626                goto cleanup;
 627
 628        if (cg_check_frozen(cgroup, true))
 629                goto cleanup;
 630
 631        ret = KSFT_PASS;
 632
 633cleanup:
 634        if (cgroup)
 635                cg_destroy(cgroup);
 636        free(cgroup);
 637        return ret;
 638}
 639
 640/*
 641 * Check if the process is stopped.
 642 */
 643static int proc_check_stopped(int pid)
 644{
 645        char buf[PAGE_SIZE];
 646        int len;
 647
 648        len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
 649        if (len == -1) {
 650                debug("Can't get %d stat\n", pid);
 651                return -1;
 652        }
 653
 654        if (strstr(buf, "(test_freezer) T ") == NULL) {
 655                debug("Process %d in the unexpected state: %s\n", pid, buf);
 656                return -1;
 657        }
 658
 659        return 0;
 660}
 661
 662/*
 663 * Test that it's possible to freeze a cgroup with a stopped process.
 664 */
 665static int test_cgfreezer_stopped(const char *root)
 666{
 667        int pid, ret = KSFT_FAIL;
 668        char *cgroup = NULL;
 669
 670        cgroup = cg_name(root, "cg_test_stopped");
 671        if (!cgroup)
 672                goto cleanup;
 673
 674        if (cg_create(cgroup))
 675                goto cleanup;
 676
 677        pid = cg_run_nowait(cgroup, child_fn, NULL);
 678
 679        if (cg_wait_for_proc_count(cgroup, 1))
 680                goto cleanup;
 681
 682        if (kill(pid, SIGSTOP))
 683                goto cleanup;
 684
 685        if (cg_check_frozen(cgroup, false))
 686                goto cleanup;
 687
 688        if (cg_freeze_wait(cgroup, true))
 689                goto cleanup;
 690
 691        if (cg_freeze_wait(cgroup, false))
 692                goto cleanup;
 693
 694        if (proc_check_stopped(pid))
 695                goto cleanup;
 696
 697        ret = KSFT_PASS;
 698
 699cleanup:
 700        if (cgroup)
 701                cg_destroy(cgroup);
 702        free(cgroup);
 703        return ret;
 704}
 705
 706/*
 707 * Test that it's possible to freeze a cgroup with a ptraced process.
 708 */
 709static int test_cgfreezer_ptraced(const char *root)
 710{
 711        int pid, ret = KSFT_FAIL;
 712        char *cgroup = NULL;
 713        siginfo_t siginfo;
 714
 715        cgroup = cg_name(root, "cg_test_ptraced");
 716        if (!cgroup)
 717                goto cleanup;
 718
 719        if (cg_create(cgroup))
 720                goto cleanup;
 721
 722        pid = cg_run_nowait(cgroup, child_fn, NULL);
 723
 724        if (cg_wait_for_proc_count(cgroup, 1))
 725                goto cleanup;
 726
 727        if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
 728                goto cleanup;
 729
 730        if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
 731                goto cleanup;
 732
 733        waitpid(pid, NULL, 0);
 734
 735        if (cg_check_frozen(cgroup, false))
 736                goto cleanup;
 737
 738        if (cg_freeze_wait(cgroup, true))
 739                goto cleanup;
 740
 741        /*
 742         * cg_check_frozen(cgroup, true) will fail here,
 743         * because the task in in the TRACEd state.
 744         */
 745        if (cg_freeze_wait(cgroup, false))
 746                goto cleanup;
 747
 748        if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
 749                goto cleanup;
 750
 751        if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
 752                goto cleanup;
 753
 754        ret = KSFT_PASS;
 755
 756cleanup:
 757        if (cgroup)
 758                cg_destroy(cgroup);
 759        free(cgroup);
 760        return ret;
 761}
 762
 763static int vfork_fn(const char *cgroup, void *arg)
 764{
 765        int pid = vfork();
 766
 767        if (pid == 0)
 768                while (true)
 769                        sleep(1);
 770
 771        return pid;
 772}
 773
 774/*
 775 * Test that it's possible to freeze a cgroup with a process,
 776 * which called vfork() and is waiting for a child.
 777 */
 778static int test_cgfreezer_vfork(const char *root)
 779{
 780        int ret = KSFT_FAIL;
 781        char *cgroup = NULL;
 782
 783        cgroup = cg_name(root, "cg_test_vfork");
 784        if (!cgroup)
 785                goto cleanup;
 786
 787        if (cg_create(cgroup))
 788                goto cleanup;
 789
 790        cg_run_nowait(cgroup, vfork_fn, NULL);
 791
 792        if (cg_wait_for_proc_count(cgroup, 2))
 793                goto cleanup;
 794
 795        if (cg_freeze_wait(cgroup, true))
 796                goto cleanup;
 797
 798        ret = KSFT_PASS;
 799
 800cleanup:
 801        if (cgroup)
 802                cg_destroy(cgroup);
 803        free(cgroup);
 804        return ret;
 805}
 806
 807#define T(x) { x, #x }
 808struct cgfreezer_test {
 809        int (*fn)(const char *root);
 810        const char *name;
 811} tests[] = {
 812        T(test_cgfreezer_simple),
 813        T(test_cgfreezer_tree),
 814        T(test_cgfreezer_forkbomb),
 815        T(test_cgfreezer_mkdir),
 816        T(test_cgfreezer_rmdir),
 817        T(test_cgfreezer_migrate),
 818        T(test_cgfreezer_ptrace),
 819        T(test_cgfreezer_stopped),
 820        T(test_cgfreezer_ptraced),
 821        T(test_cgfreezer_vfork),
 822};
 823#undef T
 824
 825int main(int argc, char *argv[])
 826{
 827        char root[PATH_MAX];
 828        int i, ret = EXIT_SUCCESS;
 829
 830        if (cg_find_unified_root(root, sizeof(root)))
 831                ksft_exit_skip("cgroup v2 isn't mounted\n");
 832        for (i = 0; i < ARRAY_SIZE(tests); i++) {
 833                switch (tests[i].fn(root)) {
 834                case KSFT_PASS:
 835                        ksft_test_result_pass("%s\n", tests[i].name);
 836                        break;
 837                case KSFT_SKIP:
 838                        ksft_test_result_skip("%s\n", tests[i].name);
 839                        break;
 840                default:
 841                        ret = EXIT_FAILURE;
 842                        ksft_test_result_fail("%s\n", tests[i].name);
 843                        break;
 844                }
 845        }
 846
 847        return ret;
 848}
 849