diff options
author | Mike Rapoport <rppt@linux.vnet.ibm.com> | 2018-05-22 16:06:05 -0400 |
---|---|---|
committer | Shuah Khan (Samsung OSG) <shuah@kernel.org> | 2018-05-30 23:28:33 -0400 |
commit | 5f8f019380b86f4e84445c2f2f8533de9c91fbe6 (patch) | |
tree | 12fcd3da46bfc1db2c78262b5eccbd9fbc511d66 | |
parent | d0103c5cb635f7ea3bf148d37bcf392fd228f0a5 (diff) |
selftests: cgroup/memcontrol: add basic test for socket accounting
The test verifies that when there is active TCP connection, the
memory.stat.sock and memory.current values are close.
Signed-off-by: Mike Rapoport <rppt@linux.vnet.ibm.com>
Acked-by: Roman Gushchin <guro@fb.com>
Signed-off-by: Shuah Khan (Samsung OSG) <shuah@kernel.org>
-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 | ||