diff options
author | Alexander Viro <viro@parcelfarce.linux.theplanet.co.uk> | 2004-04-28 17:15:20 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2004-04-28 17:15:20 -0700 |
commit | c6845c0ca139cf5b27855481b1cb7179b990296d (patch) | |
tree | b71d1cd2c2e4dad70511cdfc441a5f6349713e80 /mm | |
parent | ae08d237f10fab2abdd315362571b02862789f7b (diff) | |
download | history-c6845c0ca139cf5b27855481b1cb7179b990296d.tar.gz |
[PATCH] Fix might_sleep in /proc/swaps code
This fixes a locking problem noted by Tim Hockin:
* /proc/swaps uses seq_file code, calling seq_path() with swaplock held
* seq_path() calls d_path()
* d_path() calls mntput() which might_sleep()
We add a new semaphore protecting insertions/removals in the set of swap
components + switch of ->start()/->stop() to the same semaphore [fixes
deadlocks] + trivial cleanup of ->next().
Diffstat (limited to 'mm')
-rw-r--r-- | mm/swapfile.c | 14 |
1 files changed, 10 insertions, 4 deletions
diff --git a/mm/swapfile.c b/mm/swapfile.c index 3103178c6e5208..9ca222bc032ada 100644 --- a/mm/swapfile.c +++ b/mm/swapfile.c @@ -45,6 +45,8 @@ struct swap_list_t swap_list = {-1, -1}; struct swap_info_struct swap_info[MAX_SWAPFILES]; +static DECLARE_MUTEX(swapon_sem); + /* * Array of backing blockdevs, for swap_unplug_fn. We need this because the * bdev->unplug_fn can sleep and we cannot hold swap_list_lock while calling @@ -1158,6 +1160,7 @@ asmlinkage long sys_swapoff(const char __user * specialfile) swap_list_unlock(); goto out_dput; } + down(&swapon_sem); down(&swap_bdevs_sem); swap_list_lock(); swap_device_lock(p); @@ -1172,6 +1175,7 @@ asmlinkage long sys_swapoff(const char __user * specialfile) swap_list_unlock(); remove_swap_bdev(p->bdev); up(&swap_bdevs_sem); + up(&swapon_sem); vfree(swap_map); if (S_ISBLK(mapping->host->i_mode)) { struct block_device *bdev = I_BDEV(mapping->host); @@ -1197,7 +1201,7 @@ static void *swap_start(struct seq_file *swap, loff_t *pos) int i; loff_t l = *pos; - swap_list_lock(); + down(&swapon_sem); for (i = 0; i < nr_swapfiles; i++, ptr++) { if (!(ptr->flags & SWP_USED) || !ptr->swap_map) @@ -1212,9 +1216,9 @@ static void *swap_start(struct seq_file *swap, loff_t *pos) static void *swap_next(struct seq_file *swap, void *v, loff_t *pos) { struct swap_info_struct *ptr = v; - void *endptr = (void *) swap_info + nr_swapfiles * sizeof(struct swap_info_struct); + struct swap_info_struct *endptr = swap_info + nr_swapfiles; - for (++ptr; ptr < (struct swap_info_struct *) endptr; ptr++) { + for (++ptr; ptr < endptr; ptr++) { if (!(ptr->flags & SWP_USED) || !ptr->swap_map) continue; ++*pos; @@ -1226,7 +1230,7 @@ static void *swap_next(struct seq_file *swap, void *v, loff_t *pos) static void swap_stop(struct seq_file *swap, void *v) { - swap_list_unlock(); + up(&swapon_sem); } static int swap_show(struct seq_file *swap, void *v) @@ -1513,6 +1517,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) if (error) goto bad_swap; + down(&swapon_sem); down(&swap_bdevs_sem); swap_list_lock(); swap_device_lock(p); @@ -1541,6 +1546,7 @@ asmlinkage long sys_swapon(const char __user * specialfile, int swap_flags) swap_list_unlock(); install_swap_bdev(p->bdev); up(&swap_bdevs_sem); + up(&swapon_sem); error = 0; goto out; bad_swap: |