aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMike Rapoport <rppt@linux.vnet.ibm.com>2018-05-22 16:06:05 -0400
committerShuah Khan (Samsung OSG) <shuah@kernel.org>2018-05-30 23:28:33 -0400
commit5f8f019380b86f4e84445c2f2f8533de9c91fbe6 (patch)
tree12fcd3da46bfc1db2c78262b5eccbd9fbc511d66
parentd0103c5cb635f7ea3bf148d37bcf392fd228f0a5 (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.c193
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
781struct tcp_server_args {
782 unsigned short port;
783 int ctl[2];
784};
785
786static 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
838cleanup:
839 close(sk);
840 return ret;
841}
842
843static 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
887close_sk:
888 close(sk);
889free_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 */
902static 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
960cleanup:
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 }
776struct memcg_test { 968struct 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