00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <linux/mm.h>
00011 #include <linux/kernel_stat.h>
00012 #include <linux/swap.h>
00013 #include <linux/swapctl.h>
00014 #include <linux/init.h>
00015 #include <linux/pagemap.h>
00016
00017 #include <asm/pgtable.h>
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028 struct inode swapper_inode;
00029
00030 #ifdef SWAP_CACHE_INFO
00031 unsigned long swap_cache_add_total = 0;
00032 unsigned long swap_cache_del_total = 0;
00033 unsigned long swap_cache_find_total = 0;
00034 unsigned long swap_cache_find_success = 0;
00035
00036 void show_swap_cache_info(void)
00037 {
00038 printk("Swap cache: add %ld, delete %ld, find %ld/%ld\n",
00039 swap_cache_add_total,
00040 swap_cache_del_total,
00041 swap_cache_find_success, swap_cache_find_total);
00042 }
00043 #endif
00044
00045 int add_to_swap_cache(struct page *page, unsigned long entry)
00046 {
00047 #ifdef SWAP_CACHE_INFO
00048 swap_cache_add_total++;
00049 #endif
00050 #ifdef DEBUG_SWAP
00051 printk("DebugVM: add_to_swap_cache(%08lx count %d, entry %08lx)\n",
00052 page_address(page), atomic_read(&page->count), entry);
00053 #endif
00054 if (PageTestandSetSwapCache(page)) {
00055 printk(KERN_ERR "swap_cache: replacing non-empty entry %08lx "
00056 "on page %08lx\n",
00057 page->offset, page_address(page));
00058 return 0;
00059 }
00060 if (page->inode) {
00061 printk(KERN_ERR "swap_cache: replacing page-cached entry "
00062 "on page %08lx\n", page_address(page));
00063 return 0;
00064 }
00065 atomic_inc(&page->count);
00066 page->flags = page->flags & ~((1 << PG_uptodate) | (1 << PG_error) | (1 << PG_referenced));
00067 page->inode = &swapper_inode;
00068 page->offset = entry;
00069 add_page_to_hash_queue(page, &swapper_inode, entry);
00070 add_page_to_inode_queue(&swapper_inode, page);
00071 return 1;
00072 }
00073
00074
00075
00076
00077
00078
00079
00080 int swap_duplicate(unsigned long entry)
00081 {
00082 struct swap_info_struct * p;
00083 unsigned long offset, type;
00084 int result = 0;
00085
00086 if (!entry)
00087 goto out;
00088 type = SWP_TYPE(entry);
00089 if (type & SHM_SWP_TYPE)
00090 goto out;
00091 if (type >= nr_swapfiles)
00092 goto bad_file;
00093 p = type + swap_info;
00094 offset = SWP_OFFSET(entry);
00095 if (offset >= p->max)
00096 goto bad_offset;
00097 if (!p->swap_map[offset])
00098 goto bad_unused;
00099
00100
00101
00102 if (p->swap_map[offset] < SWAP_MAP_MAX)
00103 p->swap_map[offset]++;
00104 else {
00105 static int overflow = 0;
00106 if (overflow++ < 5)
00107 printk(KERN_WARNING
00108 "swap_duplicate: entry %08lx map count=%d\n",
00109 entry, p->swap_map[offset]);
00110 p->swap_map[offset] = SWAP_MAP_MAX;
00111 }
00112 result = 1;
00113 #ifdef DEBUG_SWAP
00114 printk("DebugVM: swap_duplicate(entry %08lx, count now %d)\n",
00115 entry, p->swap_map[offset]);
00116 #endif
00117 out:
00118 return result;
00119
00120 bad_file:
00121 printk(KERN_ERR
00122 "swap_duplicate: entry %08lx, nonexistent swap file\n", entry);
00123 goto out;
00124 bad_offset:
00125 printk(KERN_ERR
00126 "swap_duplicate: entry %08lx, offset exceeds max\n", entry);
00127 goto out;
00128 bad_unused:
00129 printk(KERN_ERR
00130 "swap_duplicate at %8p: entry %08lx, unused page\n",
00131 __builtin_return_address(0), entry);
00132 goto out;
00133 }
00134
00135 int swap_count(unsigned long entry)
00136 {
00137 struct swap_info_struct * p;
00138 unsigned long offset, type;
00139 int retval = 0;
00140
00141 if (!entry)
00142 goto bad_entry;
00143 type = SWP_TYPE(entry);
00144 if (type & SHM_SWP_TYPE)
00145 goto out;
00146 if (type >= nr_swapfiles)
00147 goto bad_file;
00148 p = type + swap_info;
00149 offset = SWP_OFFSET(entry);
00150 if (offset >= p->max)
00151 goto bad_offset;
00152 if (!p->swap_map[offset])
00153 goto bad_unused;
00154 retval = p->swap_map[offset];
00155 #ifdef DEBUG_SWAP
00156 printk("DebugVM: swap_count(entry %08lx, count %d)\n",
00157 entry, retval);
00158 #endif
00159 out:
00160 return retval;
00161
00162 bad_entry:
00163 printk(KERN_ERR "swap_count: null entry!\n");
00164 goto out;
00165 bad_file:
00166 printk(KERN_ERR
00167 "swap_count: entry %08lx, nonexistent swap file!\n", entry);
00168 goto out;
00169 bad_offset:
00170 printk(KERN_ERR
00171 "swap_count: entry %08lx, offset exceeds max!\n", entry);
00172 goto out;
00173 bad_unused:
00174 printk(KERN_ERR
00175 "swap_count at %8p: entry %08lx, unused page!\n",
00176 __builtin_return_address(0), entry);
00177 goto out;
00178 }
00179
00180 static inline void remove_from_swap_cache(struct page *page)
00181 {
00182 if (!page->inode) {
00183 printk ("VM: Removing swap cache page with zero inode hash "
00184 "on page %08lx\n", page_address(page));
00185 return;
00186 }
00187 if (page->inode != &swapper_inode) {
00188 printk ("VM: Removing swap cache page with wrong inode hash "
00189 "on page %08lx\n", page_address(page));
00190 }
00191
00192 #ifdef DEBUG_SWAP
00193 printk("DebugVM: remove_from_swap_cache(%08lx count %d)\n",
00194 page_address(page), atomic_read(&page->count));
00195 #endif
00196 PageClearSwapCache (page);
00197 remove_inode_page(page);
00198 }
00199
00200
00201
00202
00203
00204
00205 void delete_from_swap_cache(struct page *page)
00206 {
00207 long entry = page->offset;
00208
00209 #ifdef SWAP_CACHE_INFO
00210 swap_cache_del_total++;
00211 #endif
00212 #ifdef DEBUG_SWAP
00213 printk("DebugVM: delete_from_swap_cache(%08lx count %d, "
00214 "entry %08lx)\n",
00215 page_address(page), atomic_read(&page->count), entry);
00216 #endif
00217 remove_from_swap_cache (page);
00218 swap_free (entry);
00219 }
00220
00221
00222
00223
00224
00225
00226 void free_page_and_swap_cache(unsigned long addr)
00227 {
00228 struct page *page = mem_map + MAP_NR(addr);
00229
00230
00231
00232
00233 if (PageSwapCache(page) && !is_page_shared(page)) {
00234 delete_from_swap_cache(page);
00235 }
00236
00237 __free_page(page);
00238 }
00239
00240
00241
00242
00243
00244
00245
00246
00247 struct page * lookup_swap_cache(unsigned long entry)
00248 {
00249 struct page *found;
00250
00251 #ifdef SWAP_CACHE_INFO
00252 swap_cache_find_total++;
00253 #endif
00254 while (1) {
00255 found = find_page(&swapper_inode, entry);
00256 if (!found)
00257 return 0;
00258 if (found->inode != &swapper_inode || !PageSwapCache(found))
00259 goto out_bad;
00260 if (!PageLocked(found)) {
00261 #ifdef SWAP_CACHE_INFO
00262 swap_cache_find_success++;
00263 #endif
00264 return found;
00265 }
00266 __free_page(found);
00267 __wait_on_page(found);
00268 }
00269
00270 out_bad:
00271 printk (KERN_ERR "VM: Found a non-swapper swap page!\n");
00272 __free_page(found);
00273 return 0;
00274 }
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285 struct page * read_swap_cache_async(unsigned long entry, int wait)
00286 {
00287 struct page *found_page = 0, *new_page;
00288 unsigned long new_page_addr;
00289
00290 #ifdef DEBUG_SWAP
00291 printk("DebugVM: read_swap_cache_async entry %08lx%s\n",
00292 entry, wait ? ", wait" : "");
00293 #endif
00294
00295
00296
00297 if (!swap_duplicate(entry))
00298 goto out;
00299
00300
00301
00302 found_page = lookup_swap_cache(entry);
00303 if (found_page)
00304 goto out_free_swap;
00305
00306 new_page_addr = __get_free_page(GFP_USER);
00307 if (!new_page_addr)
00308 goto out_free_swap;
00309 new_page = mem_map + MAP_NR(new_page_addr);
00310
00311
00312
00313
00314 found_page = lookup_swap_cache(entry);
00315 if (found_page)
00316 goto out_free_page;
00317
00318
00319
00320 if (!add_to_swap_cache(new_page, entry))
00321 goto out_free_page;
00322
00323 set_bit(PG_locked, &new_page->flags);
00324 rw_swap_page(READ, entry, (char *) new_page_addr, wait);
00325 #ifdef DEBUG_SWAP
00326 printk("DebugVM: read_swap_cache_async created "
00327 "entry %08lx at %p\n",
00328 entry, (char *) page_address(new_page));
00329 #endif
00330 return new_page;
00331
00332 out_free_page:
00333 __free_page(new_page);
00334 out_free_swap:
00335 swap_free(entry);
00336 out:
00337 return found_page;
00338 }