Commit | Line | Data |
---|---|---|
d1a4c0b3 GC |
1 | #include <net/tcp.h> |
2 | #include <net/tcp_memcontrol.h> | |
3 | #include <net/sock.h> | |
3dc43e3e GC |
4 | #include <net/ip.h> |
5 | #include <linux/nsproxy.h> | |
d1a4c0b3 GC |
6 | #include <linux/memcontrol.h> |
7 | #include <linux/module.h> | |
8 | ||
3aaabe23 GC |
9 | static u64 tcp_cgroup_read(struct cgroup *cont, struct cftype *cft); |
10 | static int tcp_cgroup_write(struct cgroup *cont, struct cftype *cft, | |
11 | const char *buffer); | |
ffea59e5 | 12 | static int tcp_cgroup_reset(struct cgroup *cont, unsigned int event); |
3aaabe23 GC |
13 | |
14 | static struct cftype tcp_files[] = { | |
15 | { | |
16 | .name = "kmem.tcp.limit_in_bytes", | |
17 | .write_string = tcp_cgroup_write, | |
18 | .read_u64 = tcp_cgroup_read, | |
19 | .private = RES_LIMIT, | |
20 | }, | |
5a6dd343 GC |
21 | { |
22 | .name = "kmem.tcp.usage_in_bytes", | |
23 | .read_u64 = tcp_cgroup_read, | |
24 | .private = RES_USAGE, | |
25 | }, | |
ffea59e5 GC |
26 | { |
27 | .name = "kmem.tcp.failcnt", | |
28 | .private = RES_FAILCNT, | |
29 | .trigger = tcp_cgroup_reset, | |
30 | .read_u64 = tcp_cgroup_read, | |
31 | }, | |
3aaabe23 GC |
32 | }; |
33 | ||
d1a4c0b3 GC |
34 | static inline struct tcp_memcontrol *tcp_from_cgproto(struct cg_proto *cg_proto) |
35 | { | |
36 | return container_of(cg_proto, struct tcp_memcontrol, cg_proto); | |
37 | } | |
38 | ||
39 | static void memcg_tcp_enter_memory_pressure(struct sock *sk) | |
40 | { | |
41 | if (!sk->sk_cgrp->memory_pressure) | |
42 | *sk->sk_cgrp->memory_pressure = 1; | |
43 | } | |
44 | EXPORT_SYMBOL(memcg_tcp_enter_memory_pressure); | |
45 | ||
46 | int tcp_init_cgroup(struct cgroup *cgrp, struct cgroup_subsys *ss) | |
47 | { | |
48 | /* | |
49 | * The root cgroup does not use res_counters, but rather, | |
50 | * rely on the data already collected by the network | |
51 | * subsystem | |
52 | */ | |
53 | struct res_counter *res_parent = NULL; | |
54 | struct cg_proto *cg_proto, *parent_cg; | |
55 | struct tcp_memcontrol *tcp; | |
56 | struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); | |
57 | struct mem_cgroup *parent = parent_mem_cgroup(memcg); | |
3dc43e3e | 58 | struct net *net = current->nsproxy->net_ns; |
d1a4c0b3 GC |
59 | |
60 | cg_proto = tcp_prot.proto_cgroup(memcg); | |
61 | if (!cg_proto) | |
3aaabe23 | 62 | goto create_files; |
d1a4c0b3 GC |
63 | |
64 | tcp = tcp_from_cgproto(cg_proto); | |
65 | ||
3dc43e3e GC |
66 | tcp->tcp_prot_mem[0] = net->ipv4.sysctl_tcp_mem[0]; |
67 | tcp->tcp_prot_mem[1] = net->ipv4.sysctl_tcp_mem[1]; | |
68 | tcp->tcp_prot_mem[2] = net->ipv4.sysctl_tcp_mem[2]; | |
d1a4c0b3 GC |
69 | tcp->tcp_memory_pressure = 0; |
70 | ||
71 | parent_cg = tcp_prot.proto_cgroup(parent); | |
72 | if (parent_cg) | |
73 | res_parent = parent_cg->memory_allocated; | |
74 | ||
75 | res_counter_init(&tcp->tcp_memory_allocated, res_parent); | |
76 | percpu_counter_init(&tcp->tcp_sockets_allocated, 0); | |
77 | ||
78 | cg_proto->enter_memory_pressure = memcg_tcp_enter_memory_pressure; | |
79 | cg_proto->memory_pressure = &tcp->tcp_memory_pressure; | |
80 | cg_proto->sysctl_mem = tcp->tcp_prot_mem; | |
81 | cg_proto->memory_allocated = &tcp->tcp_memory_allocated; | |
82 | cg_proto->sockets_allocated = &tcp->tcp_sockets_allocated; | |
83 | cg_proto->memcg = memcg; | |
84 | ||
3aaabe23 GC |
85 | create_files: |
86 | return cgroup_add_files(cgrp, ss, tcp_files, | |
87 | ARRAY_SIZE(tcp_files)); | |
d1a4c0b3 GC |
88 | } |
89 | EXPORT_SYMBOL(tcp_init_cgroup); | |
90 | ||
91 | void tcp_destroy_cgroup(struct cgroup *cgrp, struct cgroup_subsys *ss) | |
92 | { | |
93 | struct mem_cgroup *memcg = mem_cgroup_from_cont(cgrp); | |
94 | struct cg_proto *cg_proto; | |
95 | struct tcp_memcontrol *tcp; | |
3aaabe23 | 96 | u64 val; |
d1a4c0b3 GC |
97 | |
98 | cg_proto = tcp_prot.proto_cgroup(memcg); | |
99 | if (!cg_proto) | |
100 | return; | |
101 | ||
102 | tcp = tcp_from_cgproto(cg_proto); | |
103 | percpu_counter_destroy(&tcp->tcp_sockets_allocated); | |
3aaabe23 GC |
104 | |
105 | val = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_USAGE); | |
106 | ||
107 | if (val != RESOURCE_MAX) | |
108 | jump_label_dec(&memcg_socket_limit_enabled); | |
d1a4c0b3 GC |
109 | } |
110 | EXPORT_SYMBOL(tcp_destroy_cgroup); | |
3aaabe23 GC |
111 | |
112 | static int tcp_update_limit(struct mem_cgroup *memcg, u64 val) | |
113 | { | |
114 | struct net *net = current->nsproxy->net_ns; | |
115 | struct tcp_memcontrol *tcp; | |
116 | struct cg_proto *cg_proto; | |
117 | u64 old_lim; | |
118 | int i; | |
119 | int ret; | |
120 | ||
121 | cg_proto = tcp_prot.proto_cgroup(memcg); | |
122 | if (!cg_proto) | |
123 | return -EINVAL; | |
124 | ||
125 | if (val > RESOURCE_MAX) | |
126 | val = RESOURCE_MAX; | |
127 | ||
128 | tcp = tcp_from_cgproto(cg_proto); | |
129 | ||
130 | old_lim = res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT); | |
131 | ret = res_counter_set_limit(&tcp->tcp_memory_allocated, val); | |
132 | if (ret) | |
133 | return ret; | |
134 | ||
135 | for (i = 0; i < 3; i++) | |
136 | tcp->tcp_prot_mem[i] = min_t(long, val >> PAGE_SHIFT, | |
137 | net->ipv4.sysctl_tcp_mem[i]); | |
138 | ||
139 | if (val == RESOURCE_MAX && old_lim != RESOURCE_MAX) | |
140 | jump_label_dec(&memcg_socket_limit_enabled); | |
141 | else if (old_lim == RESOURCE_MAX && val != RESOURCE_MAX) | |
142 | jump_label_inc(&memcg_socket_limit_enabled); | |
143 | ||
144 | return 0; | |
145 | } | |
146 | ||
147 | static int tcp_cgroup_write(struct cgroup *cont, struct cftype *cft, | |
148 | const char *buffer) | |
149 | { | |
150 | struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); | |
151 | unsigned long long val; | |
152 | int ret = 0; | |
153 | ||
154 | switch (cft->private) { | |
155 | case RES_LIMIT: | |
156 | /* see memcontrol.c */ | |
157 | ret = res_counter_memparse_write_strategy(buffer, &val); | |
158 | if (ret) | |
159 | break; | |
160 | ret = tcp_update_limit(memcg, val); | |
161 | break; | |
162 | default: | |
163 | ret = -EINVAL; | |
164 | break; | |
165 | } | |
166 | return ret; | |
167 | } | |
168 | ||
169 | static u64 tcp_read_stat(struct mem_cgroup *memcg, int type, u64 default_val) | |
170 | { | |
171 | struct tcp_memcontrol *tcp; | |
172 | struct cg_proto *cg_proto; | |
173 | ||
174 | cg_proto = tcp_prot.proto_cgroup(memcg); | |
175 | if (!cg_proto) | |
176 | return default_val; | |
177 | ||
178 | tcp = tcp_from_cgproto(cg_proto); | |
179 | return res_counter_read_u64(&tcp->tcp_memory_allocated, type); | |
180 | } | |
181 | ||
5a6dd343 GC |
182 | static u64 tcp_read_usage(struct mem_cgroup *memcg) |
183 | { | |
184 | struct tcp_memcontrol *tcp; | |
185 | struct cg_proto *cg_proto; | |
186 | ||
187 | cg_proto = tcp_prot.proto_cgroup(memcg); | |
188 | if (!cg_proto) | |
189 | return atomic_long_read(&tcp_memory_allocated) << PAGE_SHIFT; | |
190 | ||
191 | tcp = tcp_from_cgproto(cg_proto); | |
192 | return res_counter_read_u64(&tcp->tcp_memory_allocated, RES_USAGE); | |
193 | } | |
194 | ||
3aaabe23 GC |
195 | static u64 tcp_cgroup_read(struct cgroup *cont, struct cftype *cft) |
196 | { | |
197 | struct mem_cgroup *memcg = mem_cgroup_from_cont(cont); | |
198 | u64 val; | |
199 | ||
200 | switch (cft->private) { | |
201 | case RES_LIMIT: | |
202 | val = tcp_read_stat(memcg, RES_LIMIT, RESOURCE_MAX); | |
203 | break; | |
5a6dd343 GC |
204 | case RES_USAGE: |
205 | val = tcp_read_usage(memcg); | |
206 | break; | |
ffea59e5 GC |
207 | case RES_FAILCNT: |
208 | val = tcp_read_stat(memcg, RES_FAILCNT, 0); | |
209 | break; | |
3aaabe23 GC |
210 | default: |
211 | BUG(); | |
212 | } | |
213 | return val; | |
214 | } | |
215 | ||
ffea59e5 GC |
216 | static int tcp_cgroup_reset(struct cgroup *cont, unsigned int event) |
217 | { | |
218 | struct mem_cgroup *memcg; | |
219 | struct tcp_memcontrol *tcp; | |
220 | struct cg_proto *cg_proto; | |
221 | ||
222 | memcg = mem_cgroup_from_cont(cont); | |
223 | cg_proto = tcp_prot.proto_cgroup(memcg); | |
224 | if (!cg_proto) | |
225 | return 0; | |
226 | tcp = tcp_from_cgproto(cg_proto); | |
227 | ||
228 | switch (event) { | |
229 | case RES_FAILCNT: | |
230 | res_counter_reset_failcnt(&tcp->tcp_memory_allocated); | |
231 | break; | |
232 | } | |
233 | ||
234 | return 0; | |
235 | } | |
236 | ||
3aaabe23 GC |
237 | unsigned long long tcp_max_memory(const struct mem_cgroup *memcg) |
238 | { | |
239 | struct tcp_memcontrol *tcp; | |
240 | struct cg_proto *cg_proto; | |
241 | ||
242 | cg_proto = tcp_prot.proto_cgroup((struct mem_cgroup *)memcg); | |
243 | if (!cg_proto) | |
244 | return 0; | |
245 | ||
246 | tcp = tcp_from_cgproto(cg_proto); | |
247 | return res_counter_read_u64(&tcp->tcp_memory_allocated, RES_LIMIT); | |
248 | } | |
249 | ||
250 | void tcp_prot_mem(struct mem_cgroup *memcg, long val, int idx) | |
251 | { | |
252 | struct tcp_memcontrol *tcp; | |
253 | struct cg_proto *cg_proto; | |
254 | ||
255 | cg_proto = tcp_prot.proto_cgroup(memcg); | |
256 | if (!cg_proto) | |
257 | return; | |
258 | ||
259 | tcp = tcp_from_cgproto(cg_proto); | |
260 | ||
261 | tcp->tcp_prot_mem[idx] = val; | |
262 | } |