xref: /linux/Documentation/admin-guide/cgroup-v1/memcg_test.rst (revision 95298d63c67673c654c08952672d016212b26054)
1=====================================================
2Memory Resource Controller(Memcg) Implementation Memo
3=====================================================
4
5Last Updated: 2010/2
6
7Base Kernel Version: based on 2.6.33-rc7-mm(candidate for 34).
8
9Because VM is getting complex (one of reasons is memcg...), memcg's behavior
10is complex. This is a document for memcg's internal behavior.
11Please note that implementation details can be changed.
12
13(*) Topics on API should be in Documentation/admin-guide/cgroup-v1/memory.rst)
14
150. How to record usage ?
16========================
17
18   2 objects are used.
19
20   page_cgroup ....an object per page.
21
22	Allocated at boot or memory hotplug. Freed at memory hot removal.
23
24   swap_cgroup ... an entry per swp_entry.
25
26	Allocated at swapon(). Freed at swapoff().
27
28   The page_cgroup has USED bit and double count against a page_cgroup never
29   occurs. swap_cgroup is used only when a charged page is swapped-out.
30
311. Charge
32=========
33
34   a page/swp_entry may be charged (usage += PAGE_SIZE) at
35
36	mem_cgroup_try_charge()
37
382. Uncharge
39===========
40
41  a page/swp_entry may be uncharged (usage -= PAGE_SIZE) by
42
43	mem_cgroup_uncharge()
44	  Called when a page's refcount goes down to 0.
45
46	mem_cgroup_uncharge_swap()
47	  Called when swp_entry's refcnt goes down to 0. A charge against swap
48	  disappears.
49
503. charge-commit-cancel
51=======================
52
53	Memcg pages are charged in two steps:
54
55		- mem_cgroup_try_charge()
56		- mem_cgroup_commit_charge() or mem_cgroup_cancel_charge()
57
58	At try_charge(), there are no flags to say "this page is charged".
59	at this point, usage += PAGE_SIZE.
60
61	At commit(), the page is associated with the memcg.
62
63	At cancel(), simply usage -= PAGE_SIZE.
64
65Under below explanation, we assume CONFIG_MEM_RES_CTRL_SWAP=y.
66
674. Anonymous
68============
69
70	Anonymous page is newly allocated at
71		  - page fault into MAP_ANONYMOUS mapping.
72		  - Copy-On-Write.
73
74	4.1 Swap-in.
75	At swap-in, the page is taken from swap-cache. There are 2 cases.
76
77	(a) If the SwapCache is newly allocated and read, it has no charges.
78	(b) If the SwapCache has been mapped by processes, it has been
79	    charged already.
80
81	4.2 Swap-out.
82	At swap-out, typical state transition is below.
83
84	(a) add to swap cache. (marked as SwapCache)
85	    swp_entry's refcnt += 1.
86	(b) fully unmapped.
87	    swp_entry's refcnt += # of ptes.
88	(c) write back to swap.
89	(d) delete from swap cache. (remove from SwapCache)
90	    swp_entry's refcnt -= 1.
91
92
93	Finally, at task exit,
94	(e) zap_pte() is called and swp_entry's refcnt -=1 -> 0.
95
965. Page Cache
97=============
98
99	Page Cache is charged at
100	- add_to_page_cache_locked().
101
102	The logic is very clear. (About migration, see below)
103
104	Note:
105	  __remove_from_page_cache() is called by remove_from_page_cache()
106	  and __remove_mapping().
107
1086. Shmem(tmpfs) Page Cache
109===========================
110
111	The best way to understand shmem's page state transition is to read
112	mm/shmem.c.
113
114	But brief explanation of the behavior of memcg around shmem will be
115	helpful to understand the logic.
116
117	Shmem's page (just leaf page, not direct/indirect block) can be on
118
119		- radix-tree of shmem's inode.
120		- SwapCache.
121		- Both on radix-tree and SwapCache. This happens at swap-in
122		  and swap-out,
123
124	It's charged when...
125
126	- A new page is added to shmem's radix-tree.
127	- A swp page is read. (move a charge from swap_cgroup to page_cgroup)
128
1297. Page Migration
130=================
131
132	mem_cgroup_migrate()
133
1348. LRU
135======
136        Each memcg has its own private LRU. Now, its handling is under global
137	VM's control (means that it's handled under global pgdat->lru_lock).
138	Almost all routines around memcg's LRU is called by global LRU's
139	list management functions under pgdat->lru_lock.
140
141	A special function is mem_cgroup_isolate_pages(). This scans
142	memcg's private LRU and call __isolate_lru_page() to extract a page
143	from LRU.
144
145	(By __isolate_lru_page(), the page is removed from both of global and
146	private LRU.)
147
148
1499. Typical Tests.
150=================
151
152 Tests for racy cases.
153
1549.1 Small limit to memcg.
155-------------------------
156
157	When you do test to do racy case, it's good test to set memcg's limit
158	to be very small rather than GB. Many races found in the test under
159	xKB or xxMB limits.
160
161	(Memory behavior under GB and Memory behavior under MB shows very
162	different situation.)
163
1649.2 Shmem
165---------
166
167	Historically, memcg's shmem handling was poor and we saw some amount
168	of troubles here. This is because shmem is page-cache but can be
169	SwapCache. Test with shmem/tmpfs is always good test.
170
1719.3 Migration
172-------------
173
174	For NUMA, migration is an another special case. To do easy test, cpuset
175	is useful. Following is a sample script to do migration::
176
177		mount -t cgroup -o cpuset none /opt/cpuset
178
179		mkdir /opt/cpuset/01
180		echo 1 > /opt/cpuset/01/cpuset.cpus
181		echo 0 > /opt/cpuset/01/cpuset.mems
182		echo 1 > /opt/cpuset/01/cpuset.memory_migrate
183		mkdir /opt/cpuset/02
184		echo 1 > /opt/cpuset/02/cpuset.cpus
185		echo 1 > /opt/cpuset/02/cpuset.mems
186		echo 1 > /opt/cpuset/02/cpuset.memory_migrate
187
188	In above set, when you moves a task from 01 to 02, page migration to
189	node 0 to node 1 will occur. Following is a script to migrate all
190	under cpuset.::
191
192		--
193		move_task()
194		{
195		for pid in $1
196		do
197			/bin/echo $pid >$2/tasks 2>/dev/null
198			echo -n $pid
199			echo -n " "
200		done
201		echo END
202		}
203
204		G1_TASK=`cat ${G1}/tasks`
205		G2_TASK=`cat ${G2}/tasks`
206		move_task "${G1_TASK}" ${G2} &
207		--
208
2099.4 Memory hotplug
210------------------
211
212	memory hotplug test is one of good test.
213
214	to offline memory, do following::
215
216		# echo offline > /sys/devices/system/memory/memoryXXX/state
217
218	(XXX is the place of memory)
219
220	This is an easy way to test page migration, too.
221
2229.5 mkdir/rmdir
223---------------
224
225	When using hierarchy, mkdir/rmdir test should be done.
226	Use tests like the following::
227
228		echo 1 >/opt/cgroup/01/memory/use_hierarchy
229		mkdir /opt/cgroup/01/child_a
230		mkdir /opt/cgroup/01/child_b
231
232		set limit to 01.
233		add limit to 01/child_b
234		run jobs under child_a and child_b
235
236	create/delete following groups at random while jobs are running::
237
238		/opt/cgroup/01/child_a/child_aa
239		/opt/cgroup/01/child_b/child_bb
240		/opt/cgroup/01/child_c
241
242	running new jobs in new group is also good.
243
2449.6 Mount with other subsystems
245-------------------------------
246
247	Mounting with other subsystems is a good test because there is a
248	race and lock dependency with other cgroup subsystems.
249
250	example::
251
252		# mount -t cgroup none /cgroup -o cpuset,memory,cpu,devices
253
254	and do task move, mkdir, rmdir etc...under this.
255
2569.7 swapoff
257-----------
258
259	Besides management of swap is one of complicated parts of memcg,
260	call path of swap-in at swapoff is not same as usual swap-in path..
261	It's worth to be tested explicitly.
262
263	For example, test like following is good:
264
265	(Shell-A)::
266
267		# mount -t cgroup none /cgroup -o memory
268		# mkdir /cgroup/test
269		# echo 40M > /cgroup/test/memory.limit_in_bytes
270		# echo 0 > /cgroup/test/tasks
271
272	Run malloc(100M) program under this. You'll see 60M of swaps.
273
274	(Shell-B)::
275
276		# move all tasks in /cgroup/test to /cgroup
277		# /sbin/swapoff -a
278		# rmdir /cgroup/test
279		# kill malloc task.
280
281	Of course, tmpfs v.s. swapoff test should be tested, too.
282
2839.8 OOM-Killer
284--------------
285
286	Out-of-memory caused by memcg's limit will kill tasks under
287	the memcg. When hierarchy is used, a task under hierarchy
288	will be killed by the kernel.
289
290	In this case, panic_on_oom shouldn't be invoked and tasks
291	in other groups shouldn't be killed.
292
293	It's not difficult to cause OOM under memcg as following.
294
295	Case A) when you can swapoff::
296
297		#swapoff -a
298		#echo 50M > /memory.limit_in_bytes
299
300	run 51M of malloc
301
302	Case B) when you use mem+swap limitation::
303
304		#echo 50M > memory.limit_in_bytes
305		#echo 50M > memory.memsw.limit_in_bytes
306
307	run 51M of malloc
308
3099.9 Move charges at task migration
310----------------------------------
311
312	Charges associated with a task can be moved along with task migration.
313
314	(Shell-A)::
315
316		#mkdir /cgroup/A
317		#echo $$ >/cgroup/A/tasks
318
319	run some programs which uses some amount of memory in /cgroup/A.
320
321	(Shell-B)::
322
323		#mkdir /cgroup/B
324		#echo 1 >/cgroup/B/memory.move_charge_at_immigrate
325		#echo "pid of the program running in group A" >/cgroup/B/tasks
326
327	You can see charges have been moved by reading ``*.usage_in_bytes`` or
328	memory.stat of both A and B.
329
330	See 8.2 of Documentation/admin-guide/cgroup-v1/memory.rst to see what value should
331	be written to move_charge_at_immigrate.
332
3339.10 Memory thresholds
334----------------------
335
336	Memory controller implements memory thresholds using cgroups notification
337	API. You can use tools/cgroup/cgroup_event_listener.c to test it.
338
339	(Shell-A) Create cgroup and run event listener::
340
341		# mkdir /cgroup/A
342		# ./cgroup_event_listener /cgroup/A/memory.usage_in_bytes 5M
343
344	(Shell-B) Add task to cgroup and try to allocate and free memory::
345
346		# echo $$ >/cgroup/A/tasks
347		# a="$(dd if=/dev/zero bs=1M count=10)"
348		# a=
349
350	You will see message from cgroup_event_listener every time you cross
351	the thresholds.
352
353	Use /cgroup/A/memory.memsw.usage_in_bytes to test memsw thresholds.
354
355	It's good idea to test root cgroup as well.
356