Subject: [PATCH] (3/6) further block_device cleanups
From: Alexander Viro <[email-masked]>
To: Linus Torvalds <[email-masked]>
Cc: Andrea Arcangeli <[email-masked]>
Date: Sat, 22 Sep 2001 02:05:28 -0400 (EDT)

Part 3: swapfile.c

a) dead code removal.  Not as much as I've expected, though.
b) sys_swapon() does bd_acquire().  Corresponding bdput() added on
failing exit.
c) sys_swapoff() does bdput() (pair to bd_acquire() in swapon()). Both
are in case of swap partitions, obviously.
d) check for duplicates in swapon() (adding already activated swap component)
had been unified - we simply compare ->swap_file->d_inode->i_mapping. Works
both for files and for devices.
e) failure recovery in swapon() fixed - we take swap_map out under
swap_list_lock now and free it afterwards.  Previously we vfree()'d
it before taking the lock and left a dangling pointer for a while.
And yes, it could lead to very unpleasant oopsen.

diff -urN S10-pre13-get_sb_bdev/mm/swapfile.c S10-pre13-current/mm/swapfile.c
--- S10-pre13-get_sb_bdev/mm/swapfile.c	Fri Sep 21 09:45:30 2001
+++ S10-pre13-current/mm/swapfile.c	Fri Sep 21 21:40:04 2001
@@ -567,14 +567,8 @@
 	for (type = swap_list.head; type >= 0; type = swap_info[type].next) {
 		p = swap_info + type;
 		if ((p->flags & SWP_WRITEOK) == SWP_WRITEOK) {
-			if (p->swap_file) {
-				if (p->swap_file == nd.dentry)
-				  break;
-			} else {
-				if (S_ISBLK(nd.dentry->d_inode->i_mode)
-				    && (p->swap_device == nd.dentry->d_inode->i_rdev))
-				  break;
-			}
+			if (p->swap_file == nd.dentry)
+			  break;
 		}
 		prev = type;
 	}
@@ -615,15 +609,17 @@
 		swap_list_unlock();
 		goto out_dput;
 	}
-	if (p->swap_device)
-		blkdev_put(nd.dentry->d_inode->i_bdev, BDEV_SWAP);
+	if (p->swap_device) {
+		blkdev_put(p->swap_file->d_inode->i_bdev, BDEV_SWAP);
+		bdput(p->swap_file->d_inode->i_bdev);
+	}
 	path_release(&nd);
 
 	swap_list_lock();
-	nd.dentry = p->swap_file;
-	p->swap_file = NULL;
 	nd.mnt = p->swap_vfsmnt;
+	nd.dentry = p->swap_file;
 	p->swap_vfsmnt = NULL;
+	p->swap_file = NULL;
 	p->swap_device = 0;
 	p->max = 0;
 	swap_map = p->swap_map;
@@ -711,6 +707,7 @@
 	unsigned long maxpages = 1;
 	int swapfilesize;
 	struct block_device *bdev = NULL;
+	unsigned short *swap_map;
 	
 	if (!capable(CAP_SYS_ADMIN))
 		return -EPERM;
@@ -760,41 +757,39 @@
 		p->swap_device = dev;
 		set_blocksize(dev, PAGE_SIZE);
 		
+		bd_acquire(swap_inode);
 		bdev = swap_inode->i_bdev;
 		bdops = devfs_get_ops(devfs_get_handle_from_inode(swap_inode));
 		if (bdops) bdev->bd_op = bdops;
 
 		error = blkdev_get(bdev, FMODE_READ|FMODE_WRITE, 0, BDEV_SWAP);
-		if (error)
+		if (error) {
+			bdput(bdev);
 			goto bad_swap_2;
+		}
 		set_blocksize(dev, PAGE_SIZE);
 		error = -ENODEV;
 		if (!dev || (blk_size[MAJOR(dev)] &&
 		     !blk_size[MAJOR(dev)][MINOR(dev)]))
 			goto bad_swap;
-		error = -EBUSY;
-		for (i = 0 ; i < nr_swapfiles ; i++) {
-			if (i == type)
-				continue;
-			if (dev == swap_info[i].swap_device)
-				goto bad_swap;
-		}
 		swapfilesize = 0;
 		if (blk_size[MAJOR(dev)])
 			swapfilesize = blk_size[MAJOR(dev)][MINOR(dev)]
 				>> (PAGE_SHIFT - 10);
-	} else if (S_ISREG(swap_inode->i_mode)) {
-		error = -EBUSY;
-		for (i = 0 ; i < nr_swapfiles ; i++) {
-			if (i == type || !swap_info[i].swap_file)
-				continue;
-			if (swap_inode == swap_info[i].swap_file->d_inode)
-				goto bad_swap;
-		}
+	} else if (S_ISREG(swap_inode->i_mode))
 		swapfilesize = swap_inode->i_size >> PAGE_SHIFT;
-	} else
+	else
 		goto bad_swap;
 
+	error = -EBUSY;
+	for (i = 0 ; i < nr_swapfiles ; i++) {
+		struct swap_info_struct *q = &swap_info[i];
+		if (i == type || !q->swap_file)
+			continue;
+		if (swap_inode->i_mapping == q->swap_file->d_inode->i_mapping)
+			goto bad_swap;
+	}
+
 	swap_header = (void *) __get_free_page(GFP_USER);
 	if (!swap_header) {
 		printk("Unable to start swapping: out of memory :-)\n");
@@ -926,14 +921,15 @@
 	error = 0;
 	goto out;
 bad_swap:
-	if (bdev)
+	if (bdev) {
 		blkdev_put(bdev, BDEV_SWAP);
+		bdput(bdev);
+	}
 bad_swap_2:
-	if (p->swap_map)
-		vfree(p->swap_map);
+	swap_list_lock();
+	swap_map = p->swap_map;
 	nd.mnt = p->swap_vfsmnt;
 	nd.dentry = p->swap_file;
-	swap_list_lock();
 	p->swap_device = 0;
 	p->swap_file = NULL;
 	p->swap_vfsmnt = NULL;
@@ -942,6 +938,8 @@
 	if (!(swap_flags & SWAP_FLAG_PREFER))
 		++least_priority;
 	swap_list_unlock();
+	if (swap_map)
+		vfree(swap_map);
 	path_release(&nd);
 out:
 	if (swap_header)