diff options
| -rw-r--r-- | tools/testing/selftests/cgroup/test_memcontrol.c | 193 |
1 files changed, 193 insertions, 0 deletions
diff --git a/tools/testing/selftests/cgroup/test_memcontrol.c b/tools/testing/selftests/cgroup/test_memcontrol.c index beae06c9c899..cf0bddc9d271 100644 --- a/tools/testing/selftests/cgroup/test_memcontrol.c +++ b/tools/testing/selftests/cgroup/test_memcontrol.c | |||
| @@ -9,6 +9,12 @@ | |||
| 9 | #include <sys/stat.h> | 9 | #include <sys/stat.h> |
| 10 | #include <sys/types.h> | 10 | #include <sys/types.h> |
| 11 | #include <unistd.h> | 11 | #include <unistd.h> |
| 12 | #include <sys/socket.h> | ||
| 13 | #include <sys/wait.h> | ||
| 14 | #include <arpa/inet.h> | ||
| 15 | #include <netinet/in.h> | ||
| 16 | #include <netdb.h> | ||
| 17 | #include <errno.h> | ||
| 12 | 18 | ||
| 13 | #include "../kselftest.h" | 19 | #include "../kselftest.h" |
| 14 | #include "cgroup_util.h" | 20 | #include "cgroup_util.h" |
| @@ -772,6 +778,192 @@ cleanup: | |||
| 772 | return ret; | 778 | return ret; |
| 773 | } | 779 | } |
| 774 | 780 | ||
| 781 | struct tcp_server_args { | ||
| 782 | unsigned short port; | ||
| 783 | int ctl[2]; | ||
| 784 | }; | ||
| 785 | |||
| 786 | static int tcp_server(const char *cgroup, void *arg) | ||
| 787 | { | ||
| 788 | struct tcp_server_args *srv_args = arg; | ||
| 789 | struct sockaddr_in6 saddr = { 0 }; | ||
| 790 | socklen_t slen = sizeof(saddr); | ||
| 791 | int sk, client_sk, ctl_fd, yes = 1, ret = -1; | ||
| 792 | |||
| 793 | close(srv_args->ctl[0]); | ||
| 794 | ctl_fd = srv_args->ctl[1]; | ||
| 795 | |||
| 796 | saddr.sin6_family = AF_INET6; | ||
| 797 | saddr.sin6_addr = in6addr_any; | ||
| 798 | saddr.sin6_port = htons(srv_args->port); | ||
| 799 | |||
| 800 | sk = socket(AF_INET6, SOCK_STREAM, 0); | ||
| 801 | if (sk < 0) | ||
| 802 | return ret; | ||
| 803 | |||
| 804 | if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0) | ||
| 805 | goto cleanup; | ||
| 806 | |||
| 807 | if (bind(sk, (struct sockaddr *)&saddr, slen)) { | ||
| 808 | write(ctl_fd, &errno, sizeof(errno)); | ||
| 809 | goto cleanup; | ||
| 810 | } | ||
| 811 | |||
| 812 | if (listen(sk, 1)) | ||
| 813 | goto cleanup; | ||
| 814 | |||
| 815 | ret = 0; | ||
| 816 | if (write(ctl_fd, &ret, sizeof(ret)) != sizeof(ret)) { | ||
| 817 | ret = -1; | ||
| 818 | goto cleanup; | ||
| 819 | } | ||
| 820 | |||
| 821 | client_sk = accept(sk, NULL, NULL); | ||
| 822 | if (client_sk < 0) | ||
| 823 | goto cleanup; | ||
| 824 | |||
| 825 | ret = -1; | ||
| 826 | for (;;) { | ||
| 827 | uint8_t buf[0x100000]; | ||
| 828 | |||
| 829 | if (write(client_sk, buf, sizeof(buf)) <= 0) { | ||
| 830 | if (errno == ECONNRESET) | ||
| 831 | ret = 0; | ||
| 832 | break; | ||
| 833 | } | ||
| 834 | } | ||
| 835 | |||
| 836 | close(client_sk); | ||
| 837 | |||
| 838 | cleanup: | ||
| 839 | close(sk); | ||
| 840 | return ret; | ||
| 841 | } | ||
| 842 | |||
| 843 | static int tcp_client(const char *cgroup, unsigned short port) | ||
| 844 | { | ||
| 845 | const char server[] = "localhost"; | ||
| 846 | struct addrinfo *ai; | ||
| 847 | char servport[6]; | ||
| 848 | int retries = 0x10; /* nice round number */ | ||
| 849 | int sk, ret; | ||
| 850 | |||
| 851 | snprintf(servport, sizeof(servport), "%hd", port); | ||
| 852 | ret = getaddrinfo(server, servport, NULL, &ai); | ||
| 853 | if (ret) | ||
| 854 | return ret; | ||
| 855 | |||
| 856 | sk = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); | ||
| 857 | if (sk < 0) | ||
| 858 | goto free_ainfo; | ||
| 859 | |||
| 860 | ret = connect(sk, ai->ai_addr, ai->ai_addrlen); | ||
| 861 | if (ret < 0) | ||
| 862 | goto close_sk; | ||
| 863 | |||
| 864 | ret = KSFT_FAIL; | ||
| 865 | while (retries--) { | ||
| 866 | uint8_t buf[0x100000]; | ||
| 867 | long current, sock; | ||
| 868 | |||
| 869 | if (read(sk, buf, sizeof(buf)) <= 0) | ||
| 870 | goto close_sk; | ||
| 871 | |||
| 872 | current = cg_read_long(cgroup, "memory.current"); | ||
| 873 | sock = cg_read_key_long(cgroup, "memory.stat", "sock "); | ||
| 874 | |||
| 875 | if (current < 0 || sock < 0) | ||
| 876 | goto close_sk; | ||
| 877 | |||
| 878 | if (current < sock) | ||
| 879 | goto close_sk; | ||
| 880 | |||
| 881 | if (values_close(current, sock, 10)) { | ||
| 882 | ret = KSFT_PASS; | ||
| 883 | break; | ||
| 884 | } | ||
| 885 | } | ||
| 886 | |||
| 887 | close_sk: | ||
| 888 | close(sk); | ||
| 889 | free_ainfo: | ||
| 890 | freeaddrinfo(ai); | ||
| 891 | return ret; | ||
| 892 | } | ||
| 893 | |||
| 894 | /* | ||
| 895 | * This test checks socket memory accounting. | ||
| 896 | * The test forks a TCP server listens on a random port between 1000 | ||
| 897 | * and 61000. Once it gets a client connection, it starts writing to | ||
| 898 | * its socket. | ||
| 899 | * The TCP client interleaves reads from the socket with check whether | ||
| 900 | * memory.current and memory.stat.sock are similar. | ||
| 901 | */ | ||
| 902 | static int test_memcg_sock(const char *root) | ||
| 903 | { | ||
| 904 | int bind_retries = 5, ret = KSFT_FAIL, pid, err; | ||
| 905 | unsigned short port; | ||
| 906 | char *memcg; | ||
| 907 | |||
| 908 | memcg = cg_name(root, "memcg_test"); | ||
| 909 | if (!memcg) | ||
| 910 | goto cleanup; | ||
| 911 | |||
| 912 | if (cg_create(memcg)) | ||
| 913 | goto cleanup; | ||
| 914 | |||
| 915 | while (bind_retries--) { | ||
| 916 | struct tcp_server_args args; | ||
| 917 | |||
| 918 | if (pipe(args.ctl)) | ||
| 919 | goto cleanup; | ||
| 920 | |||
| 921 | port = args.port = 1000 + rand() % 60000; | ||
| 922 | |||
| 923 | pid = cg_run_nowait(memcg, tcp_server, &args); | ||
| 924 | if (pid < 0) | ||
| 925 | goto cleanup; | ||
| 926 | |||
| 927 | close(args.ctl[1]); | ||
| 928 | if (read(args.ctl[0], &err, sizeof(err)) != sizeof(err)) | ||
| 929 | goto cleanup; | ||
| 930 | close(args.ctl[0]); | ||
| 931 | |||
| 932 | if (!err) | ||
| 933 | break; | ||
| 934 | if (err != EADDRINUSE) | ||
| 935 | goto cleanup; | ||
| 936 | |||
| 937 | waitpid(pid, NULL, 0); | ||
| 938 | } | ||
| 939 | |||
| 940 | if (err == EADDRINUSE) { | ||
| 941 | ret = KSFT_SKIP; | ||
| 942 | goto cleanup; | ||
| 943 | } | ||
| 944 | |||
| 945 | if (tcp_client(memcg, port) != KSFT_PASS) | ||
| 946 | goto cleanup; | ||
| 947 | |||
| 948 | waitpid(pid, &err, 0); | ||
| 949 | if (WEXITSTATUS(err)) | ||
| 950 | goto cleanup; | ||
| 951 | |||
| 952 | if (cg_read_long(memcg, "memory.current") < 0) | ||
| 953 | goto cleanup; | ||
| 954 | |||
| 955 | if (cg_read_key_long(memcg, "memory.stat", "sock ")) | ||
| 956 | goto cleanup; | ||
| 957 | |||
| 958 | ret = KSFT_PASS; | ||
| 959 | |||
| 960 | cleanup: | ||
| 961 | cg_destroy(memcg); | ||
| 962 | free(memcg); | ||
| 963 | |||
| 964 | return ret; | ||
| 965 | } | ||
| 966 | |||
| 775 | #define T(x) { x, #x } | 967 | #define T(x) { x, #x } |
| 776 | struct memcg_test { | 968 | struct memcg_test { |
| 777 | int (*fn)(const char *root); | 969 | int (*fn)(const char *root); |
| @@ -785,6 +977,7 @@ struct memcg_test { | |||
| 785 | T(test_memcg_max), | 977 | T(test_memcg_max), |
| 786 | T(test_memcg_oom_events), | 978 | T(test_memcg_oom_events), |
| 787 | T(test_memcg_swap_max), | 979 | T(test_memcg_swap_max), |
| 980 | T(test_memcg_sock), | ||
| 788 | }; | 981 | }; |
| 789 | #undef T | 982 | #undef T |
| 790 | 983 | ||
