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

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>

authored by

Mike Rapoport and committed by
Shuah Khan (Samsung OSG)
5f8f0193 d0103c5c

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