summaryrefslogtreecommitdiffstats
path: root/mm/swap.c
blob: b1a6640bc5e56f7d90429dbffb60e40047807433 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
/*
 *  linux/mm/swap.c
 *
 *  Copyright (C) 1991, 1992, 1993, 1994  Linus Torvalds
 */

/*
 * This file contains the default values for the opereation of the
 * Linux VM subsystem. Fine-tuning documentation can be found in
 * linux/Documentation/sysctl/vm.txt.
 * Started 18.12.91
 * Swap aging added 23.2.95, Stephen Tweedie.
 * Buffermem limits added 12.3.98, Rik van Riel.
 */

#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <linux/swap.h>
#include <linux/swapctl.h>
#include <linux/pagemap.h>
#include <linux/init.h>

#include <asm/dma.h>
#include <asm/uaccess.h> /* for copy_to/from_user */
#include <asm/pgtable.h>

/*
 * We identify three levels of free memory.  We never let free mem
 * fall below the freepages.min except for atomic allocations.  We
 * start background swapping if we fall below freepages.high free
 * pages, and we begin intensive swapping below freepages.low.
 *
 * Actual initialization is done in mm/page_alloc.c
 */
freepages_t freepages = {
	0,	/* freepages.min */
	0,	/* freepages.low */
	0	/* freepages.high */
};

/* How many pages do we try to swap or page in/out together? */
int page_cluster;

/*
 * This variable contains the amount of page steals the system
 * is doing, averaged over a minute. We use this to determine how
 * many inactive pages we should have.
 *
 * In reclaim_page and __alloc_pages: memory_pressure++
 * In __free_pages_ok: memory_pressure--
 * In recalculate_vm_stats the value is decayed (once a second)
 */
int memory_pressure;

/* We track the number of pages currently being asynchronously swapped
   out, so that we don't try to swap TOO many pages out at once */
atomic_t nr_async_pages = ATOMIC_INIT(0);

buffer_mem_t buffer_mem = {
	2,	/* minimum percent buffer */
	10,	/* borrow percent buffer */
	60	/* maximum percent buffer */
};

buffer_mem_t page_cache = {
	2,	/* minimum percent page cache */
	15,	/* borrow percent page cache */
	75	/* maximum */
};

pager_daemon_t pager_daemon = {
	512,	/* base number for calculating the number of tries */
	SWAP_CLUSTER_MAX,	/* minimum number of tries */
	8,	/* do swap I/O in clusters of this size */
};

/**
 * age_page_{up,down} -	page aging helper functions
 * @page - the page we want to age
 * @nolock - are we already holding the pagelist_lru_lock?
 *
 * If the page is on one of the lists (active, inactive_dirty or
 * inactive_clean), we will grab the pagelist_lru_lock as needed.
 * If you're already holding the lock, call this function with the
 * nolock argument non-zero.
 */
void age_page_up_nolock(struct page * page)
{
	/*
	 * We're dealing with an inactive page, move the page
	 * to the active list.
	 */
	if (!page->age)
		activate_page_nolock(page);

	/* The actual page aging bit */
	page->age += PAGE_AGE_ADV;
	if (page->age > PAGE_AGE_MAX)
		page->age = PAGE_AGE_MAX;
}

/*
 * We use this (minimal) function in the case where we
 * know we can't deactivate the page (yet).
 */
void age_page_down_ageonly(struct page * page)
{
	page->age /= 2;
}

void age_page_down_nolock(struct page * page)
{
	/* The actual page aging bit */
	page->age /= 2;

	/*
	 * The page is now an old page. Move to the inactive
	 * list (if possible ... see below).
	 */
	if (!page->age)
	       deactivate_page_nolock(page);
}

void age_page_up(struct page * page)
{
	/*
	 * We're dealing with an inactive page, move the page
	 * to the active list.
	 */
	if (!page->age)
		activate_page(page);

	/* The actual page aging bit */
	page->age += PAGE_AGE_ADV;
	if (page->age > PAGE_AGE_MAX)
		page->age = PAGE_AGE_MAX;
}

void age_page_down(struct page * page)
{
	/* The actual page aging bit */
	page->age /= 2;

	/*
	 * The page is now an old page. Move to the inactive
	 * list (if possible ... see below).
	 */
	if (!page->age)
	       deactivate_page(page);
}


/**
 * (de)activate_page - move pages from/to active and inactive lists
 * @page: the page we want to move
 * @nolock - are we already holding the pagemap_lru_lock?
 *
 * Deactivate_page will move an active page to the right
 * inactive list, while activate_page will move a page back
 * from one of the inactive lists to the active list. If
 * called on a page which is not on any of the lists, the
 * page is left alone.
 */
void deactivate_page_nolock(struct page * page)
{
	/*
	 * One for the cache, one for the extra reference the
	 * caller has and (maybe) one for the buffers.
	 *
	 * This isn't perfect, but works for just about everything.
	 * Besides, as long as we don't move unfreeable pages to the
	 * inactive_clean list it doesn't need to be perfect...
	 */
	int maxcount = (page->buffers ? 3 : 2);
	page->age = 0;
	ClearPageReferenced(page);

	/*
	 * Don't touch it if it's not on the active list.
	 * (some pages aren't on any list at all)
	 */
	if (PageActive(page) && page_count(page) <= maxcount && !page_ramdisk(page)) {
		del_page_from_active_list(page);
		add_page_to_inactive_dirty_list(page);
	}
}	

void deactivate_page(struct page * page)
{
	spin_lock(&pagemap_lru_lock);
	deactivate_page_nolock(page);
	spin_unlock(&pagemap_lru_lock);
}

/*
 * Move an inactive page to the active list.
 */
void activate_page_nolock(struct page * page)
{
	if (PageInactiveDirty(page)) {
		del_page_from_inactive_dirty_list(page);
		add_page_to_active_list(page);
	} else if (PageInactiveClean(page)) {
		del_page_from_inactive_clean_list(page);
		add_page_to_active_list(page);
	} else {
		/*
		 * The page was not on any list, so we take care
		 * not to do anything.
		 */
	}

	/* Make sure the page gets a fair chance at staying active. */
	if (page->age < PAGE_AGE_START)
		page->age = PAGE_AGE_START;
}

void activate_page(struct page * page)
{
	spin_lock(&pagemap_lru_lock);
	activate_page_nolock(page);
	spin_unlock(&pagemap_lru_lock);
}

/**
 * lru_cache_add: add a page to the page lists
 * @page: the page to add
 */
void lru_cache_add(struct page * page)
{
	spin_lock(&pagemap_lru_lock);
	if (!PageLocked(page))
		BUG();
	DEBUG_ADD_PAGE
	add_page_to_active_list(page);
	/* This should be relatively rare */
	if (!page->age)
		deactivate_page_nolock(page);
	spin_unlock(&pagemap_lru_lock);
}

/**
 * __lru_cache_del: remove a page from the page lists
 * @page: the page to add
 *
 * This function is for when the caller already holds
 * the pagemap_lru_lock.
 */
void __lru_cache_del(struct page * page)
{
	if (PageActive(page)) {
		del_page_from_active_list(page);
	} else if (PageInactiveDirty(page)) {
		del_page_from_inactive_dirty_list(page);
	} else if (PageInactiveClean(page)) {
		del_page_from_inactive_clean_list(page);
	} else {
		printk("VM: __lru_cache_del, found unknown page ?!\n");
	}
	DEBUG_ADD_PAGE
}

/**
 * lru_cache_del: remove a page from the page lists
 * @page: the page to remove
 */
void lru_cache_del(struct page * page)
{
	if (!PageLocked(page))
		BUG();
	spin_lock(&pagemap_lru_lock);
	__lru_cache_del(page);
	spin_unlock(&pagemap_lru_lock);
}

/**
 * recalculate_vm_stats - recalculate VM statistics
 *
 * This function should be called once a second to recalculate
 * some useful statistics the VM subsystem uses to determine
 * its behaviour.
 */
void recalculate_vm_stats(void)
{
	/*
	 * Substract one second worth of memory_pressure from
	 * memory_pressure.
	 */
	memory_pressure -= (memory_pressure >> INACTIVE_SHIFT);
}

/*
 * Perform any setup for the swap system
 */
void __init swap_setup(void)
{
	/* Use a smaller cluster for memory <16MB or <32MB */
	if (num_physpages < ((16 * 1024 * 1024) >> PAGE_SHIFT))
		page_cluster = 2;
	else if (num_physpages < ((32 * 1024 * 1024) >> PAGE_SHIFT))
		page_cluster = 3;
	else
		page_cluster = 4;
}