Commit 78ecba08 authored by Hugh Dickins's avatar Hugh Dickins Committed by Linus Torvalds

mm: fix ever-decreasing swap priority

Vegard Nossum has noticed the ever-decreasing negative priority in a
swapon /swapoff loop, which eventually would misprioritize when int wraps
positive.  Not worth spending much code on, but probably better fixed.

It's easy to handle the swapping on and off of just one area, but there's
not much point if a pair or more still misbehave.  To handle the general
case, swapoff should compact negative priorities, keeping them always from
-1 to -MAX_SWAPFILES.  That's a change, but should cause no regression,
since these negative (unspecified) priorities are disjoint from the the
positive specified priorities 0 to 32767.

One small functional difference, which seems appropriate: when swapoff
fails to free all swap from a negative priority area, that area is now
reinserted at lowest priority, rather than at its original priority.

In moving down swapon's setting of priority, I notice that an area is
visible to /proc/swaps when it has swap_map set, yet that was being set
before all the visible fields were properly filled in: corrected.
Signed-off-by: default avatarHugh Dickins <hugh@veritas.com>
Reviewed-by: default avatarKOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Reported-by: default avatarVegard Nossum <vegard.nossum@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 83d1674a
...@@ -37,6 +37,7 @@ DEFINE_SPINLOCK(swap_lock); ...@@ -37,6 +37,7 @@ DEFINE_SPINLOCK(swap_lock);
unsigned int nr_swapfiles; unsigned int nr_swapfiles;
long total_swap_pages; long total_swap_pages;
static int swap_overflow; static int swap_overflow;
static int least_priority;
static const char Bad_file[] = "Bad swap file entry "; static const char Bad_file[] = "Bad swap file entry ";
static const char Unused_file[] = "Unused swap file entry "; static const char Unused_file[] = "Unused swap file entry ";
...@@ -1260,6 +1261,11 @@ asmlinkage long sys_swapoff(const char __user * specialfile) ...@@ -1260,6 +1261,11 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
/* just pick something that's safe... */ /* just pick something that's safe... */
swap_list.next = swap_list.head; swap_list.next = swap_list.head;
} }
if (p->prio < 0) {
for (i = p->next; i >= 0; i = swap_info[i].next)
swap_info[i].prio = p->prio--;
least_priority++;
}
nr_swap_pages -= p->pages; nr_swap_pages -= p->pages;
total_swap_pages -= p->pages; total_swap_pages -= p->pages;
p->flags &= ~SWP_WRITEOK; p->flags &= ~SWP_WRITEOK;
...@@ -1272,9 +1278,14 @@ asmlinkage long sys_swapoff(const char __user * specialfile) ...@@ -1272,9 +1278,14 @@ asmlinkage long sys_swapoff(const char __user * specialfile)
if (err) { if (err) {
/* re-insert swap space back into swap_list */ /* re-insert swap space back into swap_list */
spin_lock(&swap_lock); spin_lock(&swap_lock);
for (prev = -1, i = swap_list.head; i >= 0; prev = i, i = swap_info[i].next) if (p->prio < 0)
p->prio = --least_priority;
prev = -1;
for (i = swap_list.head; i >= 0; i = swap_info[i].next) {
if (p->prio >= swap_info[i].prio) if (p->prio >= swap_info[i].prio)
break; break;
prev = i;
}
p->next = i; p->next = i;
if (prev < 0) if (prev < 0)
swap_list.head = swap_list.next = p - swap_info; swap_list.head = swap_list.next = p - swap_info;
...@@ -1447,7 +1458,6 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) ...@@ -1447,7 +1458,6 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
unsigned int type; unsigned int type;
int i, prev; int i, prev;
int error; int error;
static int least_priority;
union swap_header *swap_header = NULL; union swap_header *swap_header = NULL;
int swap_header_version; int swap_header_version;
unsigned int nr_good_pages = 0; unsigned int nr_good_pages = 0;
...@@ -1455,7 +1465,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) ...@@ -1455,7 +1465,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
sector_t span; sector_t span;
unsigned long maxpages = 1; unsigned long maxpages = 1;
int swapfilesize; int swapfilesize;
unsigned short *swap_map; unsigned short *swap_map = NULL;
struct page *page = NULL; struct page *page = NULL;
struct inode *inode = NULL; struct inode *inode = NULL;
int did_down = 0; int did_down = 0;
...@@ -1474,22 +1484,10 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) ...@@ -1474,22 +1484,10 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
} }
if (type >= nr_swapfiles) if (type >= nr_swapfiles)
nr_swapfiles = type+1; nr_swapfiles = type+1;
memset(p, 0, sizeof(*p));
INIT_LIST_HEAD(&p->extent_list); INIT_LIST_HEAD(&p->extent_list);
p->flags = SWP_USED; p->flags = SWP_USED;
p->swap_file = NULL;
p->old_block_size = 0;
p->swap_map = NULL;
p->lowest_bit = 0;
p->highest_bit = 0;
p->cluster_nr = 0;
p->inuse_pages = 0;
p->next = -1; p->next = -1;
if (swap_flags & SWAP_FLAG_PREFER) {
p->prio =
(swap_flags & SWAP_FLAG_PRIO_MASK)>>SWAP_FLAG_PRIO_SHIFT;
} else {
p->prio = --least_priority;
}
spin_unlock(&swap_lock); spin_unlock(&swap_lock);
name = getname(specialfile); name = getname(specialfile);
error = PTR_ERR(name); error = PTR_ERR(name);
...@@ -1632,19 +1630,20 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) ...@@ -1632,19 +1630,20 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
goto bad_swap; goto bad_swap;
/* OK, set up the swap map and apply the bad block list */ /* OK, set up the swap map and apply the bad block list */
if (!(p->swap_map = vmalloc(maxpages * sizeof(short)))) { swap_map = vmalloc(maxpages * sizeof(short));
if (!swap_map) {
error = -ENOMEM; error = -ENOMEM;
goto bad_swap; goto bad_swap;
} }
error = 0; error = 0;
memset(p->swap_map, 0, maxpages * sizeof(short)); memset(swap_map, 0, maxpages * sizeof(short));
for (i = 0; i < swap_header->info.nr_badpages; i++) { for (i = 0; i < swap_header->info.nr_badpages; i++) {
int page_nr = swap_header->info.badpages[i]; int page_nr = swap_header->info.badpages[i];
if (page_nr <= 0 || page_nr >= swap_header->info.last_page) if (page_nr <= 0 || page_nr >= swap_header->info.last_page)
error = -EINVAL; error = -EINVAL;
else else
p->swap_map[page_nr] = SWAP_MAP_BAD; swap_map[page_nr] = SWAP_MAP_BAD;
} }
nr_good_pages = swap_header->info.last_page - nr_good_pages = swap_header->info.last_page -
swap_header->info.nr_badpages - swap_header->info.nr_badpages -
...@@ -1654,7 +1653,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) ...@@ -1654,7 +1653,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
} }
if (nr_good_pages) { if (nr_good_pages) {
p->swap_map[0] = SWAP_MAP_BAD; swap_map[0] = SWAP_MAP_BAD;
p->max = maxpages; p->max = maxpages;
p->pages = nr_good_pages; p->pages = nr_good_pages;
nr_extents = setup_swap_extents(p, &span); nr_extents = setup_swap_extents(p, &span);
...@@ -1672,6 +1671,12 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) ...@@ -1672,6 +1671,12 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags)
mutex_lock(&swapon_mutex); mutex_lock(&swapon_mutex);
spin_lock(&swap_lock); spin_lock(&swap_lock);
if (swap_flags & SWAP_FLAG_PREFER)
p->prio =
(swap_flags & SWAP_FLAG_PRIO_MASK) >> SWAP_FLAG_PRIO_SHIFT;
else
p->prio = --least_priority;
p->swap_map = swap_map;
p->flags = SWP_ACTIVE; p->flags = SWP_ACTIVE;
nr_swap_pages += nr_good_pages; nr_swap_pages += nr_good_pages;
total_swap_pages += nr_good_pages; total_swap_pages += nr_good_pages;
...@@ -1707,12 +1712,8 @@ bad_swap: ...@@ -1707,12 +1712,8 @@ bad_swap:
destroy_swap_extents(p); destroy_swap_extents(p);
bad_swap_2: bad_swap_2:
spin_lock(&swap_lock); spin_lock(&swap_lock);
swap_map = p->swap_map;
p->swap_file = NULL; p->swap_file = NULL;
p->swap_map = NULL;
p->flags = 0; p->flags = 0;
if (!(swap_flags & SWAP_FLAG_PREFER))
++least_priority;
spin_unlock(&swap_lock); spin_unlock(&swap_lock);
vfree(swap_map); vfree(swap_map);
if (swap_file) if (swap_file)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment