APM "SuspendToDisk" implementation problem

Andreas Mohr (a.mohr@mailto.de)
Sat, 21 Nov 1998 16:21:04 +0100 (CET)


Hello all,

I discovered that Linux doesn't reinitialize the EIDE controllers/HDDs
at all when resuming from a "Suspend To Disk".
This causes lost 32bit/DMA/PIO mode/Multiple sector read settings.
That can cause severe problems sometimes:
When you configure "multiple sector read" to 4 with hdparm, Linux expects an
interrupt every four sectors.
However the EIDE power-on default after an APM resume is 16.
Guess what that means...
A whole lot of "lost interrupt".
Furthermore I just hate having to enter /etc/rc.boot/hwtools manually after
every suspend to get my EIDE settings back.

So I decided to begin writing support for IDE APM reconfiguration.
Now I'm experiencing problems with "Scheduling in interrupt".

I changed drivers/block/ide.c like that:
#ifdef CONFIG_APM
#include <linux/apm_bios.h>
#endif

/*
* Reconfigure the IDE settings that were used
* prior to an APM "suspend to disk".
*/
static int ide_apm_handler(apm_event_t event)
{
ide_hwif_t *hwif;
ide_drive_t *drive;
ide_settings_t *setting;
int index, unit;
static int down = 0;

switch (event) {
case APM_SYS_SUSPEND:
case APM_USER_SUSPEND:
case APM_CRITICAL_SUSPEND:
/* FIXME: maybe turn off write cache here for models
which get powered down instantly after saving ? */
if (down) {
printk(KERN_DEBUG "cs: received extra suspend event\n");
break;
}
down = 1;
break;
case APM_NORMAL_RESUME:
case APM_CRITICAL_RESUME:
case APM_STANDBY_RESUME:
if (!down) {
printk(KERN_DEBUG "cs: received bogus resume event\n");
break;
}
down = 0;
for (index = 0; index < MAX_HWIFS; ++index) {
hwif = &ide_hwifs[index];
printk("hwif: %p, ", hwif);
for (unit = 0; unit < MAX_DRIVES; ++unit) {
drive = &ide_hwifs[index].drives[unit];
printk("drive: %p, ", drive);
setting = drive->settings;
while (setting) {
printk("setting: %p, ", setting);
ide_write_setting(drive, setting, ide_read_setting(drive, setting));
setting = setting->next;
}
}
}
break;
}
return 0;
}

And I registered this APM event handler:

/*
* This gets invoked once during initialization, to set *everything* up
*/
__initfunc(int ide_init (void))
{
init_ide_data ();
>>>>> apm_register_callback(ide_apm_handler); <<<<<<

initializing = 1;
ide_init_builtin_drivers();
initializing = 0;

return 0;
}

Now everytime I let my notebook resume, I get "Scheduling in interrupt" and
a DivByZero exception forced by Linux to get a kernel oops/backtrace.
Backtrace ("translated", of course :):
0 __down
1 __down_failed
2 ide_do_drive_cmd
3 set_multcount
4 vc_resize
5 ide_write_setting
6 ide_apm_handler <-- my APM event handler

So _something_ in ide_do_drive_cmd seems to be scheduling, and as the APM
event handler seems to get invoked by an interrupt, this is of course fatal.
How do I prevent scheduling (something like cli/sti, xxx_lock/xxx_unlock) ?
Or do I need to implement this functionality in a completely different way ?

-- 
Andreas Mohr, Wine developer

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