diff options
Diffstat (limited to 'mm/swapfile.c')
-rw-r--r-- | mm/swapfile.c | 90 |
1 files changed, 55 insertions, 35 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c index bd1bb5920306..90cb67a5417c 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c | |||
@@ -33,17 +33,18 @@ | |||
33 | #include <asm/tlbflush.h> | 33 | #include <asm/tlbflush.h> |
34 | #include <linux/swapops.h> | 34 | #include <linux/swapops.h> |
35 | 35 | ||
36 | DEFINE_SPINLOCK(swap_lock); | 36 | static DEFINE_SPINLOCK(swap_lock); |
37 | unsigned int nr_swapfiles; | 37 | static unsigned int nr_swapfiles; |
38 | long total_swap_pages; | 38 | long total_swap_pages; |
39 | static int swap_overflow; | 39 | static int swap_overflow; |
40 | static int least_priority; | ||
40 | 41 | ||
41 | static const char Bad_file[] = "Bad swap file entry "; | 42 | static const char Bad_file[] = "Bad swap file entry "; |
42 | static const char Unused_file[] = "Unused swap file entry "; | 43 | static const char Unused_file[] = "Unused swap file entry "; |
43 | static const char Bad_offset[] = "Bad swap offset entry "; | 44 | static const char Bad_offset[] = "Bad swap offset entry "; |
44 | static const char Unused_offset[] = "Unused swap offset entry "; | 45 | static const char Unused_offset[] = "Unused swap offset entry "; |
45 | 46 | ||
46 | struct swap_list_t swap_list = {-1, -1}; | 47 | static struct swap_list_t swap_list = {-1, -1}; |
47 | 48 | ||
48 | static struct swap_info_struct swap_info[MAX_SWAPFILES]; | 49 | static struct swap_info_struct swap_info[MAX_SWAPFILES]; |
49 | 50 | ||
@@ -343,7 +344,7 @@ int can_share_swap_page(struct page *page) | |||
343 | * Work out if there are any other processes sharing this | 344 | * Work out if there are any other processes sharing this |
344 | * swap cache page. Free it if you can. Return success. | 345 | * swap cache page. Free it if you can. Return success. |
345 | */ | 346 | */ |
346 | int remove_exclusive_swap_page(struct page *page) | 347 | static int remove_exclusive_swap_page_count(struct page *page, int count) |
347 | { | 348 | { |
348 | int retval; | 349 | int retval; |
349 | struct swap_info_struct * p; | 350 | struct swap_info_struct * p; |
@@ -356,7 +357,7 @@ int remove_exclusive_swap_page(struct page *page) | |||
356 | return 0; | 357 | return 0; |
357 | if (PageWriteback(page)) | 358 | if (PageWriteback(page)) |
358 | return 0; | 359 | return 0; |
359 | if (page_count(page) != 2) /* 2: us + cache */ | 360 | if (page_count(page) != count) /* us + cache + ptes */ |
360 | return 0; | 361 | return 0; |
361 | 362 | ||
362 | entry.val = page_private(page); | 363 | entry.val = page_private(page); |
@@ -368,13 +369,13 @@ int remove_exclusive_swap_page(struct page *page) | |||
368 | retval = 0; | 369 | retval = 0; |
369 | if (p->swap_map[swp_offset(entry)] == 1) { | 370 | if (p->swap_map[swp_offset(entry)] == 1) { |
370 | /* Recheck the page count with the swapcache lock held.. */ | 371 | /* Recheck the page count with the swapcache lock held.. */ |
371 | write_lock_irq(&swapper_space.tree_lock); | 372 | spin_lock_irq(&swapper_space.tree_lock); |
372 | if ((page_count(page) == 2) && !PageWriteback(page)) { | 373 | if ((page_count(page) == count) && !PageWriteback(page)) { |
373 | __delete_from_swap_cache(page); | 374 | __delete_from_swap_cache(page); |
374 | SetPageDirty(page); | 375 | SetPageDirty(page); |
375 | retval = 1; | 376 | retval = 1; |
376 | } | 377 | } |
377 | write_unlock_irq(&swapper_space.tree_lock); | 378 | spin_unlock_irq(&swapper_space.tree_lock); |
378 | } | 379 | } |
379 | spin_unlock(&swap_lock); | 380 | spin_unlock(&swap_lock); |
380 | 381 | ||
@@ -387,6 +388,25 @@ int remove_exclusive_swap_page(struct page *page) | |||
387 | } | 388 | } |
388 | 389 | ||
389 | /* | 390 | /* |
391 | * Most of the time the page should have two references: one for the | ||
392 | * process and one for the swap cache. | ||
393 | */ | ||
394 | int remove_exclusive_swap_page(struct page *page) | ||
395 | { | ||
396 | return remove_exclusive_swap_page_count(page, 2); | ||
397 | } | ||
398 | |||
399 | /* | ||
400 | * The pageout code holds an extra reference to the page. That raises | ||
401 | * the reference count to test for to 2 for a page that is only in the | ||
402 | * swap cache plus 1 for each process that maps the page. | ||
403 | */ | ||
404 | int remove_exclusive_swap_page_ref(struct page *page) | ||
405 | { | ||
406 | return remove_exclusive_swap_page_count(page, 2 + page_mapcount(page)); | ||
407 | } | ||
408 | |||
409 | /* | ||
390 | * Free the swap entry like above, but also try to | 410 | * Free the swap entry like above, but also try to |
391 | * free the page cache entry if it is the last user. | 411 | * free the page cache entry if it is the last user. |
392 | */ | 412 | */ |
@@ -402,7 +422,7 @@ void free_swap_and_cache(swp_entry_t entry) | |||
402 | if (p) { | 422 | if (p) { |
403 | if (swap_entry_free(p, swp_offset(entry)) == 1) { | 423 | if (swap_entry_free(p, swp_offset(entry)) == 1) { |
404 | page = find_get_page(&swapper_space, entry.val); | 424 | page = find_get_page(&swapper_space, entry.val); |
405 | if (page && unlikely(TestSetPageLocked(page))) { | 425 | if (page && !trylock_page(page)) { |
406 | page_cache_release(page); | 426 | page_cache_release(page); |
407 | page = NULL; | 427 | page = NULL; |
408 | } | 428 | } |
@@ -655,8 +675,8 @@ static int unuse_mm(struct mm_struct *mm, | |||
655 | 675 | ||
656 | if (!down_read_trylock(&mm->mmap_sem)) { | 676 | if (!down_read_trylock(&mm->mmap_sem)) { |
657 | /* | 677 | /* |
658 | * Activate page so shrink_cache is unlikely to unmap its | 678 | * Activate page so shrink_inactive_list is unlikely to unmap |
659 | * ptes while lock is dropped, so swapoff can make progress. | 679 | * its ptes while lock is dropped, so swapoff can make progress. |
660 | */ | 680 | */ |
661 | activate_page(page); | 681 | activate_page(page); |
662 | unlock_page(page); | 682 | unlock_page(page); |
@@ -1260,6 +1280,11 @@ asmlinkage long sys_swapoff(const char __user * specialfile) | |||
1260 | /* just pick something that's safe... */ | 1280 | /* just pick something that's safe... */ |
1261 | swap_list.next = swap_list.head; | 1281 | swap_list.next = swap_list.head; |
1262 | } | 1282 | } |
1283 | if (p->prio < 0) { | ||
1284 | for (i = p->next; i >= 0; i = swap_info[i].next) | ||
1285 | swap_info[i].prio = p->prio--; | ||
1286 | least_priority++; | ||
1287 | } | ||
1263 | nr_swap_pages -= p->pages; | 1288 | nr_swap_pages -= p->pages; |
1264 | total_swap_pages -= p->pages; | 1289 | total_swap_pages -= p->pages; |
1265 | p->flags &= ~SWP_WRITEOK; | 1290 | p->flags &= ~SWP_WRITEOK; |
@@ -1272,9 +1297,14 @@ asmlinkage long sys_swapoff(const char __user * specialfile) | |||
1272 | if (err) { | 1297 | if (err) { |
1273 | /* re-insert swap space back into swap_list */ | 1298 | /* re-insert swap space back into swap_list */ |
1274 | spin_lock(&swap_lock); | 1299 | spin_lock(&swap_lock); |
1275 | for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next) | 1300 | if (p->prio < 0) |
1301 | p->prio = --least_priority; | ||
1302 | prev = -1; | ||
1303 | for (i = swap_list.head; i >= 0; i = swap_info[i].next) { | ||
1276 | if (p->prio >= swap_info[i].prio) | 1304 | if (p->prio >= swap_info[i].prio) |
1277 | break; | 1305 | break; |
1306 | prev = i; | ||
1307 | } | ||
1278 | p->next = i; | 1308 | p->next = i; |
1279 | if (prev < 0) | 1309 | if (prev < 0) |
1280 | swap_list.head = swap_list.next = p - swap_info; | 1310 | swap_list.head = swap_list.next = p - swap_info; |
@@ -1447,7 +1477,6 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) | |||
1447 | unsigned int type; | 1477 | unsigned int type; |
1448 | int i, prev; | 1478 | int i, prev; |
1449 | int error; | 1479 | int error; |
1450 | static int least_priority; | ||
1451 | union swap_header *swap_header = NULL; | 1480 | union swap_header *swap_header = NULL; |
1452 | int swap_header_version; | 1481 | int swap_header_version; |
1453 | unsigned int nr_good_pages = 0; | 1482 | unsigned int nr_good_pages = 0; |
@@ -1455,7 +1484,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) | |||
1455 | sector_t span; | 1484 | sector_t span; |
1456 | unsigned long maxpages = 1; | 1485 | unsigned long maxpages = 1; |
1457 | int swapfilesize; | 1486 | int swapfilesize; |
1458 | unsigned short *swap_map; | 1487 | unsigned short *swap_map = NULL; |
1459 | struct page *page = NULL; | 1488 | struct page *page = NULL; |
1460 | struct inode *inode = NULL; | 1489 | struct inode *inode = NULL; |
1461 | int did_down = 0; | 1490 | int did_down = 0; |
@@ -1474,22 +1503,10 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) | |||
1474 | } | 1503 | } |
1475 | if (type >= nr_swapfiles) | 1504 | if (type >= nr_swapfiles) |
1476 | nr_swapfiles = type+1; | 1505 | nr_swapfiles = type+1; |
1506 | memset(p, 0, sizeof(*p)); | ||
1477 | INIT_LIST_HEAD(&p->extent_list); | 1507 | INIT_LIST_HEAD(&p->extent_list); |
1478 | p->flags = SWP_USED; | 1508 | p->flags = SWP_USED; |
1479 | p->swap_file = NULL; | ||
1480 | p->old_block_size = 0; | ||
1481 | p->swap_map = NULL; | ||
1482 | p->lowest_bit = 0; | ||
1483 | p->highest_bit = 0; | ||
1484 | p->cluster_nr = 0; | ||
1485 | p->inuse_pages = 0; | ||
1486 | p->next = -1; | 1509 | p->next = -1; |
1487 | if (swap_flags & SWAP_FLAG_PREFER) { | ||
1488 | p->prio = | ||
1489 | (swap_flags & SWAP_FLAG_PRIO_MASK)>>SWAP_FLAG_PRIO_SHIFT; | ||
1490 | } else { | ||
1491 | p->prio = --least_priority; | ||
1492 | } | ||
1493 | spin_unlock(&swap_lock); | 1510 | spin_unlock(&swap_lock); |
1494 | name = getname(specialfile); | 1511 | name = getname(specialfile); |
1495 | error = PTR_ERR(name); | 1512 | error = PTR_ERR(name); |
@@ -1632,19 +1649,20 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) | |||
1632 | goto bad_swap; | 1649 | goto bad_swap; |
1633 | 1650 | ||
1634 | /* OK, set up the swap map and apply the bad block list */ | 1651 | /* OK, set up the swap map and apply the bad block list */ |
1635 | if (!(p->swap_map = vmalloc(maxpages * sizeof(short)))) { | 1652 | swap_map = vmalloc(maxpages * sizeof(short)); |
1653 | if (!swap_map) { | ||
1636 | error = -ENOMEM; | 1654 | error = -ENOMEM; |
1637 | goto bad_swap; | 1655 | goto bad_swap; |
1638 | } | 1656 | } |
1639 | 1657 | ||
1640 | error = 0; | 1658 | error = 0; |
1641 | memset(p->swap_map, 0, maxpages * sizeof(short)); | 1659 | memset(swap_map, 0, maxpages * sizeof(short)); |
1642 | for (i = 0; i < swap_header->info.nr_badpages; i++) { | 1660 | for (i = 0; i < swap_header->info.nr_badpages; i++) { |
1643 | int page_nr = swap_header->info.badpages[i]; | 1661 | int page_nr = swap_header->info.badpages[i]; |
1644 | if (page_nr <= 0 || page_nr >= swap_header->info.last_page) | 1662 | if (page_nr <= 0 || page_nr >= swap_header->info.last_page) |
1645 | error = -EINVAL; | 1663 | error = -EINVAL; |
1646 | else | 1664 | else |
1647 | p->swap_map[page_nr] = SWAP_MAP_BAD; | 1665 | swap_map[page_nr] = SWAP_MAP_BAD; |
1648 | } | 1666 | } |
1649 | nr_good_pages = swap_header->info.last_page - | 1667 | nr_good_pages = swap_header->info.last_page - |
1650 | swap_header->info.nr_badpages - | 1668 | swap_header->info.nr_badpages - |
@@ -1654,7 +1672,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) | |||
1654 | } | 1672 | } |
1655 | 1673 | ||
1656 | if (nr_good_pages) { | 1674 | if (nr_good_pages) { |
1657 | p->swap_map[0] = SWAP_MAP_BAD; | 1675 | swap_map[0] = SWAP_MAP_BAD; |
1658 | p->max = maxpages; | 1676 | p->max = maxpages; |
1659 | p->pages = nr_good_pages; | 1677 | p->pages = nr_good_pages; |
1660 | nr_extents = setup_swap_extents(p, &span); | 1678 | nr_extents = setup_swap_extents(p, &span); |
@@ -1672,6 +1690,12 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) | |||
1672 | 1690 | ||
1673 | mutex_lock(&swapon_mutex); | 1691 | mutex_lock(&swapon_mutex); |
1674 | spin_lock(&swap_lock); | 1692 | spin_lock(&swap_lock); |
1693 | if (swap_flags & SWAP_FLAG_PREFER) | ||
1694 | p->prio = | ||
1695 | (swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT; | ||
1696 | else | ||
1697 | p->prio = --least_priority; | ||
1698 | p->swap_map = swap_map; | ||
1675 | p->flags = SWP_ACTIVE; | 1699 | p->flags = SWP_ACTIVE; |
1676 | nr_swap_pages += nr_good_pages; | 1700 | nr_swap_pages += nr_good_pages; |
1677 | total_swap_pages += nr_good_pages; | 1701 | total_swap_pages += nr_good_pages; |
@@ -1707,12 +1731,8 @@ bad_swap: | |||
1707 | destroy_swap_extents(p); | 1731 | destroy_swap_extents(p); |
1708 | bad_swap_2: | 1732 | bad_swap_2: |
1709 | spin_lock(&swap_lock); | 1733 | spin_lock(&swap_lock); |
1710 | swap_map = p->swap_map; | ||
1711 | p->swap_file = NULL; | 1734 | p->swap_file = NULL; |
1712 | p->swap_map = NULL; | ||
1713 | p->flags = 0; | 1735 | p->flags = 0; |
1714 | if (!(swap_flags & SWAP_FLAG_PREFER)) | ||
1715 | ++least_priority; | ||
1716 | spin_unlock(&swap_lock); | 1736 | spin_unlock(&swap_lock); |
1717 | vfree(swap_map); | 1737 | vfree(swap_map); |
1718 | if (swap_file) | 1738 | if (swap_file) |