[PATCH] 2.6.6 swsusp.c: allow device aliasing

From: Sau Dan Lee
Date: Mon May 10 2004 - 11:27:14 EST



swsusp has been working on my notebook on 2.6.2 and 2.6.5, but does
not work on 2.6.6. It panics. Here are the 'dmesg' after activating
swsusp: (The number of "=" and "." aren't correct, and I have retyped
them from the console message.)

ACPI: (supports S0 S1 S3 S4 S5)
Stopping tasks: =================================|
Freeing memory: .............|
/critical section: counting pages to copy.[no save pfn 0x338].....
(pages needed: 12532+512=13044 free: 28407)
Alloc pagedir
.[nosave pfn 0x338]......................
critical section/: done (12532 pages copied)
Writing data to swap (12532 pages): . <0>Kernel panic:
Not enough swapspace when writing data
<3>swap_free: Unused swap file entry 0000003d

Here is another trial:


ACPI: (supports S0 S1 S3 S4 S5)
Stopping tasks: =================================|
Freeing memory: .............|
/critical section: counting pages to copy.
[nosave pfn 0x338]..................
(pages needed: 12191+512=12703 free: 28748)
Alloc pagedir
.[nosave pfn 0x338].....................critical section/:
done 912191 pages copied)
Writing data to swap (12191 pages): .<0>Kernel panic:
Not enough swapspace when writing data
swap_free: Unused swap file entry 00000026


Cause:


I'm using devfs. (I'm using /dev/hda7 for swsusp.) So, cat
/proc/swaps shows my swap device as:

Filename Type Size Used Priority
/dev/ide/host0/bus0/target0/lun0/part7 partition 498920 0 -1

whereas I specify "resume=/dev/hda7" as a boot parameter. Upon
suspend, swsusp can't find a swap device exactly named "/dev/hda7" and
hence thinks there is no swapspace.


The problem is that:

1) Upon suspend, kernel/power/swsusp.c compares the devices names
"/dev/hda7" and "/dev/ide/.../part7" and they don't match. So
swsusp refuses to suspend to the swap partition. Changing the
boot parameter to "resume=/dev/ide/host0/bus0/target0/lun0/part7"
solves this problem, BUT:

2) Upon resume, kernel/power/swsusp.c doesn't understand what a device
"/dev/ide/host0/bus0/target0/lun0/part7" is. It only knows what
"/dev/hda7". So, I have to set "resume=/dev/hda7" for resume to
work. But this would create problem (1).

So, I can't get both suspend and resume to work! :(

Why did it work in pre-2.6.6? Because that check in swsusp.c in pre
2.6.6 didn't really check if the device is correct upon suspend. The
suspend code only writes the suspend/resume data to the FIRST swap
device. 2.6.6 has corrected this bug, but can't equate "/dev/hda7" to
"/dev/ide/host0/bus0/target0/lun0/part7", although the two refer to
the same device.


The following patch (against 2.6.6) fixes this problem in the suspend
code. It will check whether both the swap device and the "resume="
parameter refer to a block device with the same (major,minor) number.
If so, the swap device is taken to be the one specified for swsusp to
use. So, aliasing of devices is support. This means I must say
"resume=/dev/hda7" and not
"resume=/dev/ide/host0/bus0/target0/lun0/part7". If, however, the
specified swap devices are (swap) files rather block devices, it only
does a filename comparison. (Does swsusp support swap files? If so,
should we also support file aliasing? How?)


--- linux-2.6.6-official/kernel/power/swsusp.c 2004/05/10 14:11:17 1.1
+++ linux-2.6.6-swsusp-dev-aliasing/kernel/power/swsusp.c 2004/05/10 16:11:41 1.3
@@ -215,47 +215,67 @@
else if ((!memcmp("SWAPSPACE2",cur->swh.magic.magic,10)))
memcpy(cur->swh.magic.magic,"S2SUSP....",10);
else panic("\nSwapspace is not swapspace (%.10s)\n", cur->swh.magic.magic);
cur->link.next = prev; /* prev is the first/last swap page of the resume area */
/* link.next lies *no more* in last 4/8 bytes of magic */
}
rw_swap_page_sync(WRITE, entry, page);
__free_page(page);
}

+static int is_resume_file(const struct swap_info_struct *swap_info) {
+ /* check whether the swap device is the specified resume file */
+
+ static char buff[sizeof(resume_file)], *sname;
+ struct file *file = swap_info->swap_file;
+ struct inode *inode = file->f_dentry->d_inode;
+
+ /* now, see if it is a swap (block) device. If so, we compare
+ the device (MAJOR,MINOR), so as to support device inode
+ aliasing.
+ */
+ if (S_ISBLK(inode->i_mode)) {
+ if (resume_device == MKDEV(imajor(inode), iminor(inode)))
+ return 1; /* true */
+ }
+
+ /* otherwise, we compare filenames */
+ sname = d_path(swap_info->swap_file->f_dentry,
+ swap_info->swap_file->f_vfsmnt,
+ buff,
+ sizeof(buff));
+
+ return !strcmp(sname, resume_file);
+}
+
static void read_swapfiles(void) /* This is called before saving image */
{
int i, len;
- static char buff[sizeof(resume_file)], *sname;

len=strlen(resume_file);
root_swap = 0xFFFF;

swap_list_lock();
for(i=0; i<MAX_SWAPFILES; i++) {
if (swap_info[i].flags == 0) {
swapfile_used[i]=SWAPFILE_UNUSED;
} else {
if(!len) {
printk(KERN_WARNING "resume= option should be used to set suspend device" );
if(root_swap == 0xFFFF) {
swapfile_used[i] = SWAPFILE_SUSPEND;
root_swap = i;
} else
swapfile_used[i] = SWAPFILE_IGNORED;
} else {
/* we ignore all swap devices that are not the resume_file */
- sname = d_path(swap_info[i].swap_file->f_dentry,
- swap_info[i].swap_file->f_vfsmnt,
- buff,
- sizeof(buff));
- if (!strcmp(sname, resume_file)) {
+ if (is_resume_file(&swap_info[i])) {
swapfile_used[i] = SWAPFILE_SUSPEND;
root_swap = i;
} else {
#if 0
printk( "Resume: device %s (%x != %x) ignored\n", swap_info[i].swap_file->d_name.name, swap_info[i].swap_device, resume_device );
#endif
swapfile_used[i] = SWAPFILE_IGNORED;
}
}
}




--
Sau Dan LEE ???(Big5) ~{@nJX6X~}(HZ)

E-mail: danlee@xxxxxxxxxxxxxxxxxxxxxxxxxx
Home page: http://www.informatik.uni-freiburg.de/~danlee

-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/