From: Jens Axboe 2.6 breaks really really easily if you have any traffic on a device and issue a hdparm (or similar) command to it. Things like set_using_dma() and ide_set_xfer_rate() just stomp all over the drive regardless of what it's doing right now. I hacked something up for the SUSE kernel to fix this _almost_, it still doesn't handle cases where you want to serialize across more than a single channel. Not a common case, but I think there is such hardware out there (which?). Clearly something needs to be done about this, it's extremely frustrating not to be able to reliably turn on dma on a drive at all. I'm just tossing this one out there to solve 99% of the case, I'd like some input from you on what you feel we should do. Signed-off-by: Andrew Morton --- 25-akpm/drivers/ide/ide-io.c | 40 ++++++++++++++++++++++++++++++++++++++++ 25-akpm/drivers/ide/ide-lib.c | 10 +++++++--- 25-akpm/drivers/ide/ide.c | 22 ++++++++++++++++------ 25-akpm/include/linux/ide.h | 3 +++ 4 files changed, 66 insertions(+), 9 deletions(-) diff -puN drivers/ide/ide.c~serialize-access-to-ide-devices drivers/ide/ide.c --- 25/drivers/ide/ide.c~serialize-access-to-ide-devices 2005-01-23 14:47:07.219099696 -0800 +++ 25-akpm/drivers/ide/ide.c 2005-01-23 14:47:07.228098328 -0800 @@ -1229,18 +1229,28 @@ static int set_io_32bit(ide_drive_t *dri static int set_using_dma (ide_drive_t *drive, int arg) { #ifdef CONFIG_BLK_DEV_IDEDMA + int ret = -EPERM; + + ide_pin_hwgroup(drive); + if (!drive->id || !(drive->id->capability & 1)) - return -EPERM; + goto out; if (HWIF(drive)->ide_dma_check == NULL) - return -EPERM; + goto out; + ret = -EIO; if (arg) { - if (HWIF(drive)->ide_dma_check(drive)) return -EIO; - if (HWIF(drive)->ide_dma_on(drive)) return -EIO; + if (HWIF(drive)->ide_dma_check(drive)) + goto out; + if (HWIF(drive)->ide_dma_on(drive)) + goto out; } else { if (__ide_dma_off(drive)) - return -EIO; + goto out; } - return 0; + ret = 0; +out: + ide_unpin_hwgroup(drive); + return ret; #else return -EPERM; #endif diff -puN drivers/ide/ide-io.c~serialize-access-to-ide-devices drivers/ide/ide-io.c --- 25/drivers/ide/ide-io.c~serialize-access-to-ide-devices 2005-01-23 14:47:07.221099392 -0800 +++ 25-akpm/drivers/ide/ide-io.c 2005-01-23 14:47:07.230098024 -0800 @@ -1086,6 +1086,46 @@ void ide_stall_queue (ide_drive_t *drive drive->sleep = timeout + jiffies; } +void ide_unpin_hwgroup(ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + if (hwgroup) { + spin_lock_irq(&ide_lock); + HWGROUP(drive)->busy = 0; + drive->blocked = 0; + do_ide_request(drive->queue); + spin_unlock_irq(&ide_lock); + } +} + +void ide_pin_hwgroup(ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + /* + * should only happen very early, so not a problem + */ + if (!hwgroup) + return; + + spin_lock_irq(&ide_lock); + do { + if (!hwgroup->busy && !drive->blocked && !drive->doing_barrier) + break; + spin_unlock_irq(&ide_lock); + schedule_timeout(HZ/100); + spin_lock_irq(&ide_lock); + } while (hwgroup->busy || drive->blocked || drive->doing_barrier); + + /* + * we've now secured exclusive access to this hwgroup + */ + hwgroup->busy = 1; + drive->blocked = 1; + spin_unlock_irq(&ide_lock); +} + EXPORT_SYMBOL(ide_stall_queue); #define WAKEUP(drive) ((drive)->service_start + 2 * (drive)->service_time) diff -puN drivers/ide/ide-lib.c~serialize-access-to-ide-devices drivers/ide/ide-lib.c --- 25/drivers/ide/ide-lib.c~serialize-access-to-ide-devices 2005-01-23 14:47:07.222099240 -0800 +++ 25-akpm/drivers/ide/ide-lib.c 2005-01-23 14:47:07.230098024 -0800 @@ -434,13 +434,17 @@ void ide_toggle_bounce(ide_drive_t *driv int ide_set_xfer_rate(ide_drive_t *drive, u8 rate) { + int ret; #ifndef CONFIG_BLK_DEV_IDEDMA rate = min(rate, (u8) XFER_PIO_4); #endif - if(HWIF(drive)->speedproc) - return HWIF(drive)->speedproc(drive, rate); + ide_pin_hwgroup(drive); + if (HWIF(drive)->speedproc) + ret = HWIF(drive)->speedproc(drive, rate); else - return -1; + ret = -1; + ide_unpin_hwgroup(drive); + return ret; } EXPORT_SYMBOL_GPL(ide_set_xfer_rate); diff -puN include/linux/ide.h~serialize-access-to-ide-devices include/linux/ide.h --- 25/include/linux/ide.h~serialize-access-to-ide-devices 2005-01-23 14:47:07.224098936 -0800 +++ 25-akpm/include/linux/ide.h 2005-01-23 14:47:07.232097720 -0800 @@ -1356,6 +1356,9 @@ extern irqreturn_t ide_intr(int irq, voi extern void do_ide_request(request_queue_t *); extern void ide_init_subdrivers(void); +extern void ide_pin_hwgroup(ide_drive_t *); +extern void ide_unpin_hwgroup(ide_drive_t *); + extern struct block_device_operations ide_fops[]; extern ide_proc_entry_t generic_subdriver_entries[]; _