1
2
3
4
5
6
7
8
9
10#include <linux/mm.h>
11#include <linux/kernel_stat.h>
12#include <linux/swap.h>
13#include <linux/swapctl.h>
14#include <linux/init.h>
15#include <linux/pagemap.h>
16
17#include <asm/pgtable.h>
18
19
20
21
22
23
24
25
26
27
28struct inode swapper_inode;
29
30#ifdef SWAP_CACHE_INFO
31unsigned long swap_cache_add_total = 0;
32unsigned long swap_cache_del_total = 0;
33unsigned long swap_cache_find_total = 0;
34unsigned long swap_cache_find_success = 0;
35
36void show_swap_cache_info(void)
37{
38 printk("Swap cache: add %ld, delete %ld, find %ld/%ld\n",
39 swap_cache_add_total,
40 swap_cache_del_total,
41 swap_cache_find_success, swap_cache_find_total);
42}
43#endif
44
45int add_to_swap_cache(struct page *page, unsigned long entry)
46{
47#ifdef SWAP_CACHE_INFO
48 swap_cache_add_total++;
49#endif
50#ifdef DEBUG_SWAP
51 printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n",
52 page_address(page), atomic_read(&page->count), entry);
53#endif
54 if (PageTestandSetSwapCache(page)) {
55 printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx "
56 "on page %08lx\n",
57 page->offset, page_address(page));
58 return 0;
59 }
60 if (page->inode) {
61 printk(KERN_ERR "swap_cache: replacing page-cached entry "
62 "on page %08lx\n", page_address(page));
63 return 0;
64 }
65 atomic_inc(&page->count);
66 page->flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced));
67 page->inode = &swapper_inode;
68 page->offset = entry;
69 add_page_to_hash_queue(page, &swapper_inode, entry);
70 add_page_to_inode_queue(&swapper_inode, page);
71 return 1;
72}
73
74
75
76
77
78
79
80int swap_duplicate(unsigned long entry)
81{
82 struct swap_info_struct * p;
83 unsigned long offset, type;
84 int result = 0;
85
86 if (!entry)
87 goto out;
88 type = SWP_TYPE(entry);
89 if (type & SHM_SWP_TYPE)
90 goto out;
91 if (type >= nr_swapfiles)
92 goto bad_file;
93 p = type + swap_info;
94 offset = SWP_OFFSET(entry);
95 if (offset >= p->max)
96 goto bad_offset;
97 if (!p->swap_map[offset])
98 goto bad_unused;
99
100
101
102 if (p->swap_map[offset] < SWAP_MAP_MAX)
103 p->swap_map[offset]++;
104 else {
105 static int overflow = 0;
106 if (overflow++ < 5)
107 printk(KERN_WARNING
108 "swap_duplicate: entry %08lx map count=%d\n",
109 entry, p->swap_map[offset]);
110 p->swap_map[offset] = SWAP_MAP_MAX;
111 }
112 result = 1;
113#ifdef DEBUG_SWAP
114 printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
115 entry, p->swap_map[offset]);
116#endif
117out:
118 return result;
119
120bad_file:
121 printk(KERN_ERR
122 "swap_duplicate: entry %08lx, nonexistent swap file\n", entry);
123 goto out;
124bad_offset:
125 printk(KERN_ERR
126 "swap_duplicate: entry %08lx, offset exceeds max\n", entry);
127 goto out;
128bad_unused:
129 printk(KERN_ERR
130 "swap_duplicate at %8p: entry %08lx, unused page\n",
131 __builtin_return_address(0), entry);
132 goto out;
133}
134
135int swap_count(unsigned long entry)
136{
137 struct swap_info_struct * p;
138 unsigned long offset, type;
139 int retval = 0;
140
141 if (!entry)
142 goto bad_entry;
143 type = SWP_TYPE(entry);
144 if (type & SHM_SWP_TYPE)
145 goto out;
146 if (type >= nr_swapfiles)
147 goto bad_file;
148 p = type + swap_info;
149 offset = SWP_OFFSET(entry);
150 if (offset >= p->max)
151 goto bad_offset;
152 if (!p->swap_map[offset])
153 goto bad_unused;
154 retval = p->swap_map[offset];
155#ifdef DEBUG_SWAP
156 printk("DebugVM: swap_count(entry %08lx, count %d)\n",
157 entry, retval);
158#endif
159out:
160 return retval;
161
162bad_entry:
163 printk(KERN_ERR "swap_count: null entry!\n");
164 goto out;
165bad_file:
166 printk(KERN_ERR
167 "swap_count: entry %08lx, nonexistent swap file!\n", entry);
168 goto out;
169bad_offset:
170 printk(KERN_ERR
171 "swap_count: entry %08lx, offset exceeds max!\n", entry);
172 goto out;
173bad_unused:
174 printk(KERN_ERR
175 "swap_count at %8p: entry %08lx, unused page!\n",
176 __builtin_return_address(0), entry);
177 goto out;
178}
179
180static inline void remove_from_swap_cache(struct page *page)
181{
182 if (!page->inode) {
183 printk ("VM: Removing swap cache page with zero inode hash "
184 "on page %08lx\n", page_address(page));
185 return;
186 }
187 if (page->inode != &swapper_inode) {
188 printk ("VM: Removing swap cache page with wrong inode hash "
189 "on page %08lx\n", page_address(page));
190 }
191
192#ifdef DEBUG_SWAP
193 printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
194 page_address(page), atomic_read(&page->count));
195#endif
196 PageClearSwapCache (page);
197 remove_inode_page(page);
198}
199
200
201
202
203
204
205void delete_from_swap_cache(struct page *page)
206{
207 long entry = page->offset;
208
209#ifdef SWAP_CACHE_INFO
210 swap_cache_del_total++;
211#endif
212#ifdef DEBUG_SWAP
213 printk("DebugVM: delete_from_swap_cache(%08lx count %d, "
214 "entry %08lx)\n",
215 page_address(page), atomic_read(&page->count), entry);
216#endif
217 remove_from_swap_cache (page);
218 swap_free (entry);
219}
220
221
222
223
224
225
226void free_page_and_swap_cache(unsigned long addr)
227{
228 struct page *page = mem_map + MAP_NR(addr);
229
230
231
232
233 if (PageSwapCache(page) && !is_page_shared(page)) {
234 delete_from_swap_cache(page);
235 }
236
237 __free_page(page);
238}
239
240
241
242
243
244
245
246
247struct page * lookup_swap_cache(unsigned long entry)
248{
249 struct page *found;
250
251#ifdef SWAP_CACHE_INFO
252 swap_cache_find_total++;
253#endif
254 while (1) {
255 found = find_page(&swapper_inode, entry);
256 if (!found)
257 return 0;
258 if (found->inode != &swapper_inode || !PageSwapCache(found))
259 goto out_bad;
260 if (!PageLocked(found)) {
261#ifdef SWAP_CACHE_INFO
262 swap_cache_find_success++;
263#endif
264 return found;
265 }
266 __free_page(found);
267 __wait_on_page(found);
268 }
269
270out_bad:
271 printk (KERN_ERR "VM: Found a non-swapper swap page!\n");
272 __free_page(found);
273 return 0;
274}
275
276
277
278
279
280
281
282
283
284
285struct page * read_swap_cache_async(unsigned long entry, int wait)
286{
287 struct page *found_page = 0, *new_page;
288 unsigned long new_page_addr;
289
290#ifdef DEBUG_SWAP
291 printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
292 entry, wait ? ", wait" : "");
293#endif
294
295
296
297 if (!swap_duplicate(entry))
298 goto out;
299
300
301
302 found_page = lookup_swap_cache(entry);
303 if (found_page)
304 goto out_free_swap;
305
306 new_page_addr = __get_free_page(GFP_USER);
307 if (!new_page_addr)
308 goto out_free_swap;
309 new_page = mem_map + MAP_NR(new_page_addr);
310
311
312
313
314 found_page = lookup_swap_cache(entry);
315 if (found_page)
316 goto out_free_page;
317
318
319
320 if (!add_to_swap_cache(new_page, entry))
321 goto out_free_page;
322
323 set_bit(PG_locked, &new_page->flags);
324 rw_swap_page(READ, entry, (char *) new_page_addr, wait);
325#ifdef DEBUG_SWAP
326 printk("DebugVM: read_swap_cache_async created "
327 "entry %08lx at %p\n",
328 entry, (char *) page_address(new_page));
329#endif
330 return new_page;
331
332out_free_page:
333 __free_page(new_page);
334out_free_swap:
335 swap_free(entry);
336out:
337 return found_page;
338}
339