patch-2.1.100 linux/drivers/block/ide.c
Next file: linux/drivers/block/ide.h
Previous file: linux/drivers/block/ide-tape.c
Back to the patch index
Back to the overall index
- Lines: 1176
- Date:
Wed May 6 14:42:54 1998
- Orig file:
v2.1.99/linux/drivers/block/ide.c
- Orig date:
Sat May 2 14:19:53 1998
diff -u --recursive --new-file v2.1.99/linux/drivers/block/ide.c linux/drivers/block/ide.c
@@ -86,6 +86,9 @@
* Version 6.12 integrate ioctl and proc interfaces
* fix parsing of "idex=" command line parameter
* Version 6.13 add support for ide4/ide5 courtesy rjones@orchestream.com
+ * Version 6.14 fixed IRQ sharing among PCI devices
+ * Version 6.15 added SMP awareness to IDE drivers
+ * Version 6.16 fixed various bugs; even more SMP friendly
*
* Some additional driver compile-time options are in ide.h
*
@@ -155,13 +158,13 @@
unsigned long t, flags;
int i;
- __save_flags(flags);
- __cli();
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
t = jiffies * 11932;
outb_p(0, 0x43);
i = inb_p(0x40);
i |= inb(0x40) << 8;
- __restore_flags(flags);
+ __restore_flags(flags); /* local CPU only */
return (t - i);
}
#endif /* DISK_RECOVERY_TIME */
@@ -178,15 +181,11 @@
*/
static void init_hwif_data (unsigned int index)
{
- byte *p;
unsigned int unit;
ide_hwif_t *hwif = &ide_hwifs[index];
/* bulk initialize hwif & drive info with zeros */
- p = ((byte *) hwif) + sizeof(ide_hwif_t);
- do {
- *--p = 0;
- } while (p > (byte *) hwif);
+ memset(hwif, 0, sizeof(ide_hwif_t));
/* fill in any non-zero initial values */
hwif->index = index;
@@ -299,11 +298,11 @@
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
unsigned long flags;
- __save_flags(flags);
- __cli();
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
do_vlb_sync(IDE_NSECTOR_REG);
insl(IDE_DATA_REG, buffer, wcount);
- __restore_flags(flags);
+ __restore_flags(flags); /* local CPU only */
} else
#endif /* SUPPORT_VLB_SYNC */
insl(IDE_DATA_REG, buffer, wcount);
@@ -332,11 +331,11 @@
#if SUPPORT_VLB_SYNC
if (io_32bit & 2) {
unsigned long flags;
- __save_flags(flags);
- __cli();
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
do_vlb_sync(IDE_NSECTOR_REG);
outsl(IDE_DATA_REG, buffer, wcount);
- __restore_flags(flags);
+ __restore_flags(flags); /* local CPU only */
} else
#endif /* SUPPORT_VLB_SYNC */
outsl(IDE_DATA_REG, buffer, wcount);
@@ -392,6 +391,85 @@
}
/*
+ * Needed for PCI irq sharing
+ */
+static inline int drive_is_ready (ide_drive_t *drive)
+{
+ if (drive->waiting_for_dma)
+ return HWIF(drive)->dmaproc(ide_dma_test_irq, drive);
+#if 0
+ udelay(1); /* need to guarantee 400ns since last command was issued */
+#endif
+ if (GET_STAT() & BUSY_STAT)
+ return 0; /* drive busy: definitely not interrupting */
+ return 1; /* drive ready: *might* be interrupting */
+}
+
+#if !defined(__SMP__) && defined(DEBUG_SPINLOCKS) && (DEBUG_SPINLOCKS > 1)
+
+static const char *ide_lock_name(spinlock_t *spinlock)
+{
+ int index;
+
+ if (spinlock == &io_request_lock)
+ return "io_request_lock";
+ for (index = 0; index < MAX_HWIFS; index++) {
+ ide_hwif_t *hwif = &ide_hwifs[index];
+ ide_hwgroup_t *hwgroup = hwif->hwgroup;
+ if (spinlock == &hwgroup->spinlock)
+ return hwif->name;
+ }
+ return "?";
+}
+
+#define IDE_SPIN_LOCK_IRQ(msg,spinlock) \
+{ \
+ static int __babble = 20; \
+ __cli(); \
+ if ((spinlock)->lock && __babble) { \
+ __babble--; \
+ printk("ide_lock: %s: already locked (%s)\n", msg, ide_lock_name(spinlock)); \
+ } \
+ /* spin_lock_irq(spinlock); */ \
+ (spinlock)->lock = 1; \
+}
+
+#define IDE_SPIN_LOCK_IRQSAVE(msg,spinlock,flags) \
+{ \
+ __save_flags(flags); \
+ IDE_SPIN_LOCK_IRQ(msg,spinlock); \
+}
+
+#define IDE_SPIN_UNLOCK_IRQRESTORE(msg,spinlock,flags) \
+{ \
+ static int __babble = 20; \
+ __cli(); \
+ if (!((spinlock)->lock) && __babble) { \
+ __babble--; \
+ printk("ide_unlock: %s: not locked (%s)\n", msg, ide_lock_name(spinlock)); \
+ } \
+ /* spin_unlock_irqrestore(msg,spinlock,flags); */ \
+ (spinlock)->lock = 0; \
+ restore_flags(flags); \
+}
+
+#define IDE_SPIN_UNLOCK(msg,spinlock) \
+{ \
+ unsigned long __flags; \
+ __save_flags(__flags); \
+ IDE_SPIN_UNLOCK_IRQRESTORE(msg,spinlock,__flags); \
+}
+
+#else /* DEBUG_SPINLOCKS */
+
+#define IDE_SPIN_LOCK_IRQ(msg,spinlock) spin_lock_irq(spinlock)
+#define IDE_SPIN_LOCK_IRQSAVE(msg,spinlock,flags) spin_lock_irqsave(spinlock,flags)
+#define IDE_SPIN_UNLOCK(msg,spinlock) spin_unlock(spinlock)
+#define IDE_SPIN_UNLOCK_IRQRESTORE(msg,spinlock,flags) spin_unlock_irqrestore(spinlock,flags)
+
+#endif /* DEBUG_SPINLOCKS */
+
+/*
* This should get invoked any time we exit the driver to
* wait for an interrupt response from a drive. handler() points
* at the appropriate code to handle the next interrupt, and a
@@ -400,7 +478,10 @@
*/
void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
{
+ unsigned long flags;
ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+ IDE_SPIN_LOCK_IRQSAVE("ide_set_handler", &hwgroup->spinlock, flags);
#ifdef DEBUG
if (hwgroup->handler != NULL) {
printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
@@ -410,6 +491,7 @@
hwgroup->handler = handler;
hwgroup->timer.expires = jiffies + timeout;
add_timer(&(hwgroup->timer));
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_set_handler", &hwgroup->spinlock, flags);
}
/*
@@ -558,8 +640,8 @@
ide_hwif_t *hwif = HWIF(drive);
ide_hwgroup_t *hwgroup = HWGROUP(drive);
- __save_flags(flags);
- __cli(); /* Why ? */
+ __save_flags(flags); /* local CPU only */
+ __cli(); /* local CPU only */
/* For an ATAPI device, first try an ATAPI SRST. */
if (drive->media != ide_disk && !do_not_try_atapi) {
@@ -569,7 +651,7 @@
OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
- __restore_flags (flags);
+ __restore_flags (flags); /* local CPU only */
return;
}
@@ -597,7 +679,7 @@
ide_set_handler (drive, &reset_pollfunc, HZ/20);
#endif /* OK_TO_RESET_CONTROLLER */
- __restore_flags (flags);
+ __restore_flags (flags); /* local CPU only */
}
/*
@@ -625,15 +707,17 @@
args[2] = IN_BYTE(IDE_NSECTOR_REG);
}
}
- __save_flags(flags);
- __cli();
+ IDE_SPIN_LOCK_IRQSAVE("ide_end_drive_cmd", &io_request_lock, flags);
drive->queue = rq->next;
blk_dev[MAJOR(rq->rq_dev)].current_request = NULL;
HWGROUP(drive)->rq = NULL;
rq->rq_status = RQ_INACTIVE;
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_end_drive_cmd", &io_request_lock, flags);
+ save_flags(flags); /* all CPUs; overkill? */
+ cli(); /* all CPUs; overkill? */
if (rq->sem != NULL)
- up(rq->sem);
- __restore_flags(flags);
+ up(rq->sem); /* inform originator that rq has been serviced */
+ restore_flags(flags); /* all CPUs; overkill? */
}
/*
@@ -644,8 +728,8 @@
unsigned long flags;
byte err = 0;
- __save_flags (flags);
- /* ide_sti(); HACK */
+ __save_flags (flags); /* local CPU only */
+ ide__sti(); /* local CPU only */
printk("%s: %s: status=0x%02x", drive->name, msg, stat);
#if FANCY_STATUS_DUMPS
printk(" { ");
@@ -698,7 +782,7 @@
#endif /* FANCY_STATUS_DUMPS */
printk("\n");
}
- __restore_flags (flags);
+ __restore_flags (flags); /* local CPU only */
return err;
}
@@ -732,7 +816,7 @@
byte err;
err = ide_dump_status(drive, msg, stat);
- if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
+ if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL)
return;
/* retry only "normal" I/O: */
if (rq->cmd == IDE_DRIVE_CMD) {
@@ -784,7 +868,7 @@
void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
{
ide_set_handler (drive, handler, WAIT_CMD);
- OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* clear nIEN */
OUT_BYTE(nsect,IDE_NSECTOR_REG);
OUT_BYTE(cmd,IDE_COMMAND_REG);
}
@@ -799,7 +883,7 @@
byte stat = GET_STAT();
int retries = 10;
- /* ide_sti(); HACK */
+ ide__sti(); /* local CPU only */
if ((stat & DRQ_STAT) && args && args[3]) {
byte io_32bit = drive->io_32bit;
drive->io_32bit = 0;
@@ -857,17 +941,17 @@
udelay(1); /* spec allows drive 400ns to assert "BUSY" */
if ((stat = GET_STAT()) & BUSY_STAT) {
- __save_flags(flags);
- /* ide_sti(); HACK */
+ __save_flags(flags); /* local CPU only */
+ ide__sti(); /* local CPU only */
timeout += jiffies;
while ((stat = GET_STAT()) & BUSY_STAT) {
if (0 < (signed long)(jiffies - timeout)) {
- __restore_flags(flags);
+ __restore_flags(flags); /* local CPU only */
ide_error(drive, "status timeout", stat);
return 1;
}
}
- __restore_flags(flags);
+ __restore_flags(flags); /* local CPU only */
}
udelay(1); /* allow status to settle, then read it again */
if (OK_STAT((stat = GET_STAT()), good, bad))
@@ -909,17 +993,18 @@
}
/*
- * do_request() initiates handling of a new I/O request
+ * start_request() initiates handling of a new I/O request
*/
-static inline void do_request (ide_hwgroup_t *hwgroup, ide_hwif_t *hwif, ide_drive_t *drive)
+static inline void start_request (ide_drive_t *drive)
{
unsigned long block, blockend;
struct request *rq = drive->queue;
unsigned int minor = MINOR(rq->rq_dev), unit = minor >> PARTN_BITS;
+ ide_hwif_t *hwif = HWIF(drive);
- /* ide_sti(); HACK */
+ ide__sti(); /* local CPU only */
#ifdef DEBUG
- printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+ printk("%s: start_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
#endif
if (unit >= MAX_DRIVES) {
printk("%s: bad device number: %s\n", hwif->name, kdevname(rq->rq_dev));
@@ -946,15 +1031,11 @@
#if (DISK_RECOVERY_TIME > 0)
while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
#endif
-
- hwgroup->hwif = hwif;
- hwgroup->drive = drive;
SELECT_DRIVE(hwif, drive);
if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
printk("%s: drive not ready for command\n", drive->name);
return;
}
-
if (!drive->special.all) {
if (rq->cmd == IDE_DRIVE_CMD) {
execute_drive_cmd(drive, rq);
@@ -973,7 +1054,7 @@
if (drive->driver != NULL)
DRIVER(drive)->end_request(0, HWGROUP(drive));
else
- ide_end_request(0, hwgroup);
+ ide_end_request(0, HWGROUP(drive));
}
/*
@@ -1000,21 +1081,19 @@
best = NULL;
drive = hwgroup->drive;
do {
- if (!drive->queue)
- continue;
- if (drive->sleep && 0 < (signed long)(drive->sleep - jiffies))
- continue;
- if (!best) {
- best = drive;
- continue;
+ if (drive->queue && (!drive->sleep || 0 <= (signed long)(jiffies - drive->sleep))) {
+ if (!best
+ || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep)))
+ || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive))))
+ {
+ struct blk_dev_struct *bdev = &blk_dev[HWIF(drive)->major];
+ if (bdev->current_request != &bdev->plug)
+ best = drive;
+ }
}
- if (drive->sleep && (!best->sleep || drive->sleep < best->sleep))
- best = drive;
- if (!best->sleep && WAKEUP(drive) < WAKEUP(best))
- best = drive;
} while ((drive = drive->next) != hwgroup->drive);
- if (best != hwgroup->drive && best && best->service_time > WAIT_MIN_SLEEP && !best->sleep && best->nice1) {
- long t = (signed) (WAKEUP(best) - jiffies); /* BUGGY? */
+ if (best && best->nice1 && !best->sleep && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) {
+ long t = (signed long)(WAKEUP(best) - jiffies);
if (t >= WAIT_MIN_SLEEP) {
/*
* We *may* have some time to spare, but first let's see if
@@ -1022,9 +1101,10 @@
*/
drive = best->next;
do {
- if (drive->sleep) /* this drive tried to be nice to us */
- continue;
- if (WAKEUP(drive) > (jiffies - best->service_time) && WAKEUP(drive) < (jiffies + t)) { /* BUGGY? */
+ if (!drive->sleep
+ && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time))
+ && 0 < (signed long)((jiffies + t) - WAKEUP(drive)))
+ {
ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP));
goto repeat;
}
@@ -1034,33 +1114,6 @@
return best;
}
-static inline void ide_leave_hwgroup (ide_hwgroup_t *hwgroup)
-{
- ide_drive_t *drive = hwgroup->drive;
- unsigned long sleep = 0;
-
- hwgroup->rq = NULL;
- do {
- blk_dev[HWIF(drive)->major].current_request = NULL;
- if (!drive->sleep)
- continue;
- if (!sleep) {
- sleep = drive->sleep;
- continue;
- }
- if (drive->sleep < sleep)
- sleep = drive->sleep;
- } while ((drive = drive->next) != hwgroup->drive);
- if (sleep) {
- if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
- sleep = jiffies + WAIT_MIN_SLEEP;
- hwgroup->timer.expires = sleep;
- add_timer(&hwgroup->timer);
- } else /* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
- ide_release_lock(&ide_lock);
- hwgroup->active = 0;
-}
-
/*
* The driver enables interrupts as much as possible. In order to do this,
* (a) the device-interrupt is always masked before entry, and
@@ -1075,30 +1128,68 @@
* tolerance for latency during I/O. For devices which don't suffer from
* this problem (most don't), the unmask flag can be set using the "hdparm"
* utility, to permit other interrupts during data/cmd transfers.
+ *
+ * Caller must have already acquired spinlock using *spinflags
+ *
*/
-void ide_do_request (ide_hwgroup_t *hwgroup)
+static void ide_do_request (ide_hwgroup_t *hwgroup, unsigned long *hwgroup_flags, int masked_irq)
{
- __cli(); /* paranoia */
- if (hwgroup->handler != NULL) {
- printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
- return;
- }
- do {
- ide_drive_t *drive = choose_drive(hwgroup);
- if (drive != NULL) {
- ide_hwif_t *hwif = HWIF(drive);
- if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif)
- OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
- drive->sleep = 0;
- blk_dev[hwif->major].current_request = hwgroup->rq = drive->queue;
- drive->service_start = jiffies;
- do_request(hwgroup, hwif, drive);
- __cli();
- } else {
- ide_leave_hwgroup(hwgroup); /* no work left for this hwgroup */
+ struct blk_dev_struct *bdev;
+ ide_drive_t *drive;
+ ide_hwif_t *hwif;
+ unsigned long io_flags;
+
+ hwgroup->busy = 1;
+ while (hwgroup->handler == NULL) {
+ IDE_SPIN_LOCK_IRQSAVE("ide_do_request1", &io_request_lock, io_flags);
+ drive = choose_drive(hwgroup);
+ if (drive == NULL) {
+ unsigned long sleep = 0;
+
+ hwgroup->rq = NULL;
+ drive = hwgroup->drive;
+ do {
+ bdev = &blk_dev[HWIF(drive)->major];
+ if (bdev->current_request != &bdev->plug) /* FIXME: this will do for now */
+ bdev->current_request = NULL; /* (broken since patch-2.1.15) */
+ if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep)))
+ sleep = drive->sleep;
+ } while ((drive = drive->next) != hwgroup->drive);
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_do_request2", &io_request_lock, io_flags);
+ if (sleep) {
+ if (0 < (signed long)(jiffies + WAIT_MIN_SLEEP - sleep))
+ sleep = jiffies + WAIT_MIN_SLEEP;
+ hwgroup->timer.expires = sleep;
+ add_timer(&hwgroup->timer);
+ } else {
+ /* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_scheduler? */
+ ide_release_lock(&ide_lock); /* for atari only */
+ }
+ hwgroup->busy = 0;
return;
}
- } while (hwgroup->handler == NULL);
+ hwif = HWIF(drive);
+ if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif) /* set nIEN for previous hwif */
+ OUT_BYTE(hwgroup->drive->ctl|2, hwgroup->hwif->io_ports[IDE_CONTROL_OFFSET]);
+ hwgroup->hwif = hwif;
+ hwgroup->drive = drive;
+ drive->sleep = 0;
+ drive->service_start = jiffies;
+
+ bdev = &blk_dev[hwif->major];
+ if (bdev->current_request == &bdev->plug) /* FIXME: paranoia */
+ printk("%s: Huh? nuking plugged queue\n", drive->name);
+ bdev->current_request = hwgroup->rq = drive->queue;
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_do_request3", &io_request_lock, io_flags);
+
+ if (hwif->irq != masked_irq)
+ disable_irq(hwif->irq);
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_do_request4", &hwgroup->spinlock, *hwgroup_flags);
+ start_request(drive);
+ IDE_SPIN_LOCK_IRQSAVE("ide_do_request5", &hwgroup->spinlock, *hwgroup_flags);
+ if (hwif->irq != masked_irq)
+ enable_irq(hwif->irq);
+ }
}
/*
@@ -1106,100 +1197,136 @@
*/
struct request **ide_get_queue (kdev_t dev)
{
- struct blk_dev_struct *bdev = blk_dev + MAJOR(dev);
- ide_hwif_t *hwif = bdev->data;
+ ide_hwif_t *hwif = (ide_hwif_t *)blk_dev[MAJOR(dev)].data;
return &hwif->drives[DEVICE_NR(dev) & 1].queue;
}
/*
- * do_hwgroup_request() invokes ide_do_request() after first masking
- * all possible interrupts for the current hwgroup. This prevents race
- * conditions in the event that an unexpected interrupt occurs while
- * we are in the driver.
- *
- * Note that the io-request lock will guarantee that the driver never gets
- * re-entered even on another interrupt level, so we no longer need to
- * mask the irq's.
+ * do_hwgroup_request() invokes ide_do_request() after claiming hwgroup->busy.
*/
-static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
+static void do_hwgroup_request (const char *msg, ide_hwgroup_t *hwgroup)
{
- if (hwgroup->handler == NULL) {
- del_timer(&hwgroup->timer);
- ide_get_lock(&ide_lock, ide_intr, hwgroup);
- hwgroup->active = 1;
- ide_do_request (hwgroup);
+ unsigned long flags;
+
+ IDE_SPIN_LOCK_IRQSAVE(msg, &hwgroup->spinlock, flags);
+ if (hwgroup->busy) {
+ IDE_SPIN_UNLOCK_IRQRESTORE(msg, &hwgroup->spinlock, flags);
+ return;
}
+ del_timer(&hwgroup->timer);
+ ide_get_lock(&ide_lock, ide_intr, hwgroup); /* for atari only */
+ ide_do_request(hwgroup, &flags, 0);
+ IDE_SPIN_UNLOCK_IRQRESTORE(msg, &hwgroup->spinlock, flags);
+}
+
+/*
+ * As of linux-2.1.95, ll_rw_blk.c invokes our do_idex_request()
+ * functions with the io_request_spinlock already grabbed.
+ * Since we need to do our own spinlock's internally,
+ * on paths that don't necessarily originate through the
+ * do_idex_request() path.
+ *
+ * We have to undo the spinlock on entry, and restore it again on exit.
+ * Fortunately, this is mostly a nop for non-SMP kernels.
+ */
+static inline void unlock_do_hwgroup_request (ide_hwgroup_t *hwgroup)
+{
+ IDE_SPIN_UNLOCK("unlock_do_hwgroup_request", &io_request_lock);
+ do_hwgroup_request ("from unlock_do_hwgroup_request", hwgroup);
+ IDE_SPIN_LOCK_IRQ("unlock_do_hwgroup_request", &io_request_lock);
}
-void do_ide0_request (void) /* invoked with __cli() */
+void do_ide0_request (void)
{
- do_hwgroup_request (ide_hwifs[0].hwgroup);
+ unlock_do_hwgroup_request (ide_hwifs[0].hwgroup);
}
#if MAX_HWIFS > 1
-void do_ide1_request (void) /* invoked with __cli() */
+void do_ide1_request (void)
{
- do_hwgroup_request (ide_hwifs[1].hwgroup);
+ unlock_do_hwgroup_request (ide_hwifs[1].hwgroup);
}
#endif /* MAX_HWIFS > 1 */
#if MAX_HWIFS > 2
-void do_ide2_request (void) /* invoked with __cli() */
+void do_ide2_request (void)
{
- do_hwgroup_request (ide_hwifs[2].hwgroup);
+ unlock_do_hwgroup_request (ide_hwifs[2].hwgroup);
}
#endif /* MAX_HWIFS > 2 */
#if MAX_HWIFS > 3
-void do_ide3_request (void) /* invoked with __cli() */
+void do_ide3_request (void)
{
- do_hwgroup_request (ide_hwifs[3].hwgroup);
+ unlock_do_hwgroup_request (ide_hwifs[3].hwgroup);
}
#endif /* MAX_HWIFS > 3 */
#if MAX_HWIFS > 4
-void do_ide4_request (void) /* invoked with cli() */
+void do_ide4_request (void)
{
- do_hwgroup_request (ide_hwifs[4].hwgroup);
+ unlock_do_hwgroup_request (ide_hwifs[4].hwgroup);
}
#endif /* MAX_HWIFS > 4 */
#if MAX_HWIFS > 5
-void do_ide5_request (void) /* invoked with cli() */
+void do_ide5_request (void)
{
- do_hwgroup_request (ide_hwifs[5].hwgroup);
+ unlock_do_hwgroup_request (ide_hwifs[5].hwgroup);
}
#endif /* MAX_HWIFS > 5 */
+static void start_next_request (ide_hwgroup_t *hwgroup, int masked_irq)
+{
+ unsigned long flags;
+ ide_drive_t *drive;
+
+ IDE_SPIN_LOCK_IRQSAVE("start_next_request", &hwgroup->spinlock, flags);
+ if (hwgroup->handler != NULL) {
+ IDE_SPIN_UNLOCK_IRQRESTORE("start_next_request", &hwgroup->spinlock, flags);
+ return;
+ }
+ drive = hwgroup->drive;
+ set_recovery_timer(HWIF(drive));
+ drive->service_time = jiffies - drive->service_start;
+ ide_do_request(hwgroup, &flags, masked_irq);
+ IDE_SPIN_UNLOCK_IRQRESTORE("start_next_request", &hwgroup->spinlock, flags);
+}
+
void ide_timer_expiry (unsigned long data)
{
ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
- ide_drive_t *drive = hwgroup->drive;
+ ide_drive_t *drive;
ide_handler_t *handler;
unsigned long flags;
- __save_flags(flags);
- __cli();
-
- if ((handler = hwgroup->handler) != NULL) {
- hwgroup->handler = NULL;
- if (hwgroup->poll_timeout != 0) /* polling in progress? */
- handler(drive);
- else { /* abort the operation */
- if (hwgroup->hwif->dmaproc)
- (void) hwgroup->hwif->dmaproc (ide_dma_end, drive);
- ide_error(drive, "irq timeout", GET_STAT());
- }
- __cli();
- if (hwgroup->handler == NULL) {
- set_recovery_timer(HWIF(drive));
- drive->service_time = jiffies - drive->service_start;
- do_hwgroup_request (hwgroup);
- }
- } else
- do_hwgroup_request (hwgroup);
- __restore_flags(flags);
+ IDE_SPIN_LOCK_IRQSAVE("ide_timer_expiry1", &hwgroup->spinlock, flags);
+ drive = hwgroup->drive;
+ if ((handler = hwgroup->handler) == NULL) {
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_timer_expiry2", &hwgroup->spinlock, flags);
+ do_hwgroup_request("timer do_hwgroup_request", hwgroup);
+ return;
+ }
+ hwgroup->busy = 1; /* should already be "1" */
+ hwgroup->handler = NULL;
+ if (hwgroup->poll_timeout != 0) { /* polling in progress? */
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_timer_expiry3", &hwgroup->spinlock, flags);
+ handler(drive);
+ } else if (drive_is_ready(drive)) {
+ printk("%s: lost interrupt\n", drive->name);
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_timer_expiry4", &hwgroup->spinlock, flags);
+ handler(drive);
+ } else {
+ if (drive->waiting_for_dma) {
+ (void) hwgroup->hwif->dmaproc(ide_dma_end, drive);
+ printk("%s: timeout waiting for DMA\n", drive->name);
+ }
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_timer_expiry5", &hwgroup->spinlock, flags);
+ ide_error(drive, "irq timeout", GET_STAT());
+ }
+ del_timer(&hwgroup->timer);
+ start_next_request(hwgroup, 0);
}
/*
@@ -1238,75 +1365,78 @@
stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]);
if (!OK_STAT(stat, READY_STAT, BAD_STAT)) {
/* Try to not flood the console with msgs */
- static unsigned long last_msgtime = 0;
+ static unsigned long last_msgtime = 0, count = 0;
+ ++count;
if (0 < (signed long)(jiffies - (last_msgtime + HZ))) {
last_msgtime = jiffies;
- printk("%s%s: unexpected interrupt, status=0x%02x\n",
- hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat);
+ printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n",
+ hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count);
}
}
}
} while ((hwif = hwif->next) != hwgroup->hwif);
}
-
-#ifdef __sparc_v9__
-#define IDE_IRQ_EQUAL(irq1, irq2) (1)
-#else
-#define IDE_IRQ_EQUAL(irq1, irq2) ((irq1) == (irq2))
-#endif
-
-static void do_ide_intr (int irq, void *dev_id, struct pt_regs *regs)
-{
- ide_hwgroup_t *hwgroup = dev_id;
- ide_hwif_t *hwif = hwgroup->hwif;
- ide_handler_t *handler;
-
- if (!ide_ack_intr (hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]))
- return;
-
- if (IDE_IRQ_EQUAL(irq, hwif->irq)
- && (handler = hwgroup->handler) != NULL) {
- ide_drive_t *drive = hwgroup->drive;
-#if 1 /* temporary, remove later -- FIXME */
- {
- struct request *rq = hwgroup->rq;
- if (rq != NULL
- &&( MAJOR(rq->rq_dev) != HWIF(drive)->major
- || (MINOR(rq->rq_dev) >> PARTN_BITS) != drive->select.b.unit))
- {
- printk("ide_intr: got IRQ from wrong device: email mlord@pobox.com!!\n");
- return;
- }
- }
-#endif /* temporary */
- hwgroup->handler = NULL;
- del_timer(&(hwgroup->timer));
- /* if (drive->unmask)
- ide_sti(); HACK */
- handler(drive);
- /* this is necessary, as next rq may be different irq */
- if (hwgroup->handler == NULL) {
- set_recovery_timer(HWIF(drive));
- drive->service_time = jiffies - drive->service_start;
- ide_do_request(hwgroup);
- }
- } else {
- unexpected_intr(irq, hwgroup);
- }
- __cli();
- hwif = hwgroup->hwif;
-}
-
/*
* entry point for all interrupts, caller does __cli() for us
*/
void ide_intr (int irq, void *dev_id, struct pt_regs *regs)
{
unsigned long flags;
+ ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id;
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ ide_handler_t *handler;
- spin_lock_irqsave(&io_request_lock, flags);
- do_ide_intr(irq, dev_id, regs);
- spin_unlock_irqrestore(&io_request_lock, flags);
+ __cli(); /* local CPU only */
+ IDE_SPIN_LOCK_IRQSAVE("ide_intr1", &hwgroup->spinlock, flags);
+ hwif = hwgroup->hwif;
+ if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) {
+ /*
+ * Not expecting an interrupt from this drive.
+ * That means this could be:
+ * (1) an interrupt from another PCI device
+ * sharing the same PCI INT# as us.
+ * or (2) a drive just entered sleep or standby mode,
+ * and is interrupting to let us know.
+ * or (3) a spurious interrupt of unknown origin.
+ *
+ * For PCI, we cannot tell the difference,
+ * so in that case we just ignore it and hope it goes away.
+ */
+#ifdef CONFIG_BLK_DEV_IDEPCI
+ if (IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL))
+#endif /* CONFIG_BLK_DEV_IDEPCI */
+ {
+ /*
+ * Probably not a shared PCI interrupt,
+ * so we can safely try to do something about it:
+ */
+ (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]);
+ unexpected_intr(irq, hwgroup);
+ }
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_intr2", &hwgroup->spinlock, flags);
+ return;
+ }
+ drive = hwgroup->drive;
+ if (!drive || !drive_is_ready(drive)) {
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_intr3", &hwgroup->spinlock, flags);
+ return;
+ }
+ hwgroup->handler = NULL;
+ (void)ide_ack_intr(hwif->io_ports[IDE_STATUS_OFFSET], hwif->io_ports[IDE_IRQ_OFFSET]);
+ del_timer(&(hwgroup->timer));
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_intr4", &hwgroup->spinlock, flags);
+ if (drive->unmask)
+ ide__sti(); /* local CPU only */
+ handler(drive); /* service this interrupt, may set handler for next interrupt */
+ /*
+ * Note that handler() may have set things up for another
+ * interrupt to occur soon, but it cannot happen until
+ * we exit from this routine, because it will be the
+ * same irq as is currently being serviced here,
+ * and Linux won't allow another (on any CPU) until we return.
+ */
+ start_next_request(hwgroup, hwif->irq);
}
/*
@@ -1390,10 +1520,8 @@
if (action == ide_wait)
rq->sem = &sem;
- __save_flags(flags);
- __cli();
+ IDE_SPIN_LOCK_IRQSAVE("ide_do_drive_cmd", &io_request_lock, flags);
cur_rq = drive->queue;
-
if (cur_rq == NULL || action == ide_preempt) {
rq->next = cur_rq;
drive->queue = rq;
@@ -1407,13 +1535,13 @@
rq->next = cur_rq->next;
cur_rq->next = rq;
}
- if (!hwgroup->active) {
- do_hwgroup_request(hwgroup);
- __cli();
- }
- if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_do_drive_cmd", &io_request_lock, flags);
+ do_hwgroup_request("drive_cmd do_hwgroup_request", hwgroup);
+ save_flags(flags); /* all CPUs; overkill? */
+ cli(); /* all CPUs; overkill? */
+ if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
down(&sem); /* wait for it to be serviced */
- __restore_flags(flags);
+ restore_flags(flags); /* all CPUs; overkill? */
return rq->errors ? -EIO : 0; /* return -EIO if errors */
}
@@ -1428,6 +1556,7 @@
int ide_revalidate_disk(kdev_t i_rdev)
{
ide_drive_t *drive;
+ ide_hwgroup_t *hwgroup;
unsigned int p, major, minor;
long flags;
@@ -1435,15 +1564,15 @@
return -ENODEV;
major = MAJOR(i_rdev);
minor = drive->select.b.unit << PARTN_BITS;
- __save_flags(flags);
- __cli();
+ hwgroup = HWGROUP(drive);
+ IDE_SPIN_LOCK_IRQSAVE("ide_revalidate_disk", &hwgroup->spinlock, flags);
if (drive->busy || (drive->usage > 1)) {
- __restore_flags(flags);
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_revalidate_disk", &hwgroup->spinlock, flags);
return -EBUSY;
};
drive->busy = 1;
MOD_INC_USE_COUNT;
- __restore_flags(flags);
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_revalidate_disk", &hwgroup->spinlock, flags);
for (p = 0; p < (1<<PARTN_BITS); ++p) {
if (drive->part[p].nr_sects > 0) {
@@ -1589,8 +1718,8 @@
if (index >= MAX_HWIFS)
return;
- __save_flags(flags);
- __cli();
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
hwif = &ide_hwifs[index];
if (!hwif->present)
goto abort;
@@ -1680,7 +1809,7 @@
}
init_hwif_data (index); /* restore hwif data to pristine status */
abort:
- __restore_flags(flags);
+ restore_flags(flags); /* all CPUs */
}
int ide_register (int arg1, int arg2, int irq)
@@ -1719,7 +1848,7 @@
return hwif->present ? index : -1;
}
-void ide_add_setting(ide_drive_t *drive, char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
+void ide_add_setting(ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set)
{
ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL;
@@ -1798,38 +1927,63 @@
int ide_read_setting(ide_drive_t *drive, ide_settings_t *setting)
{
- if (!(setting->rw & SETTING_READ))
- return -EINVAL;
- switch(setting->data_type) {
- case TYPE_BYTE:
- return *((u8 *) setting->data);
- case TYPE_SHORT:
- return *((u16 *) setting->data);
- case TYPE_INT:
- case TYPE_INTA:
- return *((u32 *) setting->data);
- default:
- return -EINVAL;
+ int val = -EINVAL;
+ unsigned long flags;
+
+ if ((setting->rw & SETTING_READ)) {
+ IDE_SPIN_LOCK_IRQSAVE("ide_read_setting", &HWGROUP(drive)->spinlock, flags);
+ switch(setting->data_type) {
+ case TYPE_BYTE:
+ val = *((u8 *) setting->data);
+ break;
+ case TYPE_SHORT:
+ val = *((u16 *) setting->data);
+ break;
+ case TYPE_INT:
+ case TYPE_INTA:
+ val = *((u32 *) setting->data);
+ break;
+ }
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_read_setting", &HWGROUP(drive)->spinlock, flags);
+ }
+ return val;
+}
+
+int ide_spin_wait_hwgroup (const char *msg, ide_drive_t *drive, unsigned long *flags)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ unsigned long timeout = jiffies + (3 * HZ);
+
+ IDE_SPIN_LOCK_IRQSAVE(msg, &hwgroup->spinlock, *flags);
+ while (hwgroup->busy) {
+ IDE_SPIN_UNLOCK_IRQRESTORE(msg, &hwgroup->spinlock, *flags);
+ __sti(); /* local CPU only; needed for jiffies */
+ if (0 < (signed long)(jiffies - timeout)) {
+ printk("%s: %s: channel busy\n", drive->name, msg);
+ return -EBUSY;
+ }
+ IDE_SPIN_LOCK_IRQSAVE(msg, &hwgroup->spinlock, *flags);
}
+ return 0;
}
int ide_write_setting(ide_drive_t *drive, ide_settings_t *setting, int val)
{
unsigned long flags;
- int i, rc = 0;
+ int i;
u32 *p;
- if (!suser())
+ if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!(setting->rw & SETTING_WRITE))
return -EPERM;
if (val < setting->min || val > setting->max)
return -EINVAL;
- __save_flags(flags);
- __cli();
if (setting->set)
- rc = setting->set(drive, val);
- else switch (setting->data_type) {
+ return setting->set(drive, val);
+ if (ide_spin_wait_hwgroup("ide_write_settings", drive, &flags))
+ return -EBUSY;
+ switch (setting->data_type) {
case TYPE_BYTE:
*((u8 *) setting->data) = val;
break;
@@ -1845,8 +1999,8 @@
*p = val;
break;
}
- __restore_flags(flags);
- return rc;
+ IDE_SPIN_UNLOCK_IRQRESTORE("ide_write_setting4", &HWGROUP(drive)->spinlock, flags);
+ return 0;
}
static int set_io_32bit(ide_drive_t *drive, int arg)
@@ -1956,7 +2110,7 @@
return 0;
}
case BLKFLSBUF:
- if (!suser()) return -EACCES;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
fsync_dev(inode->i_rdev);
invalidate_buffers(inode->i_rdev);
return 0;
@@ -1965,21 +2119,17 @@
return put_user(drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects, (long *) arg);
case BLKRRPART: /* Re-read partition tables */
- if (!suser()) return -EACCES;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
return ide_revalidate_disk(inode->i_rdev);
+ case HDIO_OBSOLETE_IDENTITY:
case HDIO_GET_IDENTITY:
if (MINOR(inode->i_rdev) & PARTN_MASK)
return -EINVAL;
if (drive->id == NULL)
return -ENOMSG;
-#if 0
- if (copy_to_user((char *)arg, (char *)drive->id, sizeof(*drive->id)))
+ if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142))
return -EFAULT;
-#else
- if (copy_to_user((char *)arg, (char *)drive->id, 142))
- return -EFAULT;
-#endif
return 0;
case HDIO_GET_NICE:
@@ -1993,7 +2143,7 @@
{
byte args[4], *argbuf = args;
int argsize = 4;
- if (!suser()) return -EACCES;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
if (NULL == (void *) arg)
return ide_do_drive_cmd(drive, &rq, ide_wait);
if (copy_from_user(args, (void *)arg, 4))
@@ -2016,7 +2166,7 @@
case HDIO_SCAN_HWIF:
{
int args[3];
- if (!suser()) return -EACCES;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
if (copy_from_user(args, (void *)arg, 3 * sizeof(int)))
return -EFAULT;
if (ide_register(args[0], args[1], args[2]) == -1)
@@ -2024,7 +2174,7 @@
return 0;
}
case HDIO_SET_NICE:
- if (!suser()) return -EACCES;
+ if (!capable(CAP_SYS_ADMIN)) return -EACCES;
if (drive->driver == NULL)
return -EPERM;
if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1))))
@@ -2311,7 +2461,7 @@
goto bad_option; /* chipset already specified */
if (i <= -7 && hw != 0)
goto bad_hwif; /* chipset drivers are for "ide0=" only */
- if (ide_hwifs[hw^1].chipset != ide_unknown)
+ if (i <= -7 && ide_hwifs[hw^1].chipset != ide_unknown)
goto bad_option; /* chipset for 2nd port already specified */
printk("\n");
}
@@ -2528,12 +2678,6 @@
ide_probe_for_rz100x();
}
#endif /* CONFIG_BLK_DEV_RZ1000 */
-#ifdef CONFIG_BLK_DEV_SL82C105
- {
- extern void ide_probe_for_sl82c105(void);
- ide_probe_for_sl82c105();
- }
-#endif /* CONFIG_BLK_DEV_SL82C105 */
#endif /* CONFIG_BLK_DEV_IDEPCI */
}
#endif /* CONFIG_PCI */
@@ -2560,21 +2704,21 @@
probe_for_hwifs ();
#ifdef CONFIG_BLK_DEV_IDE
-#ifdef __mc68000__
+#if defined(__mc68000__) || defined(CONFIG_APUS)
if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
- ide_get_lock(&ide_lock, NULL, NULL);
+ ide_get_lock(&ide_lock, NULL, NULL); /* for atari only */
disable_irq(ide_hwifs[0].irq);
}
-#endif /* __mc68000__ */
+#endif /* __mc68000__ || CONFIG_APUS */
(void) ideprobe_init();
-#ifdef __mc68000__
+#if defined(__mc68000__) || defined(CONFIG_APUS)
if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) {
enable_irq(ide_hwifs[0].irq);
- ide_release_lock(&ide_lock);
+ ide_release_lock(&ide_lock); /* for atari only */
}
-#endif /* __mc68000__ */
+#endif /* __mc68000__ || CONFIG_APUS */
#endif /* CONFIG_BLK_DEV_IDE */
#ifdef CONFIG_PROC_FS
@@ -2705,16 +2849,15 @@
{
unsigned long flags;
- __save_flags(flags);
- __cli();
- if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL ||
- drive->busy || drive->usage) {
- __restore_flags(flags);
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
+ if (version != IDE_SUBDRIVER_VERSION || !drive->present || drive->driver != NULL || drive->busy || drive->usage) {
+ restore_flags(flags); /* all CPUs */
return 1;
}
drive->driver = driver;
setup_driver_defaults(drive);
- __restore_flags(flags);
+ restore_flags(flags); /* all CPUs */
if (drive->autotune != 2) {
if (driver->supports_dma && HWIF(drive)->dmaproc != NULL)
(void) (HWIF(drive)->dmaproc(ide_dma_check, drive));
@@ -2733,10 +2876,10 @@
{
unsigned long flags;
- __save_flags(flags);
- __cli();
+ save_flags(flags); /* all CPUs */
+ cli(); /* all CPUs */
if (drive->usage || drive->busy || drive->driver == NULL || DRIVER(drive)->busy) {
- __restore_flags(flags);
+ restore_flags(flags); /* all CPUs */
return 1;
}
#ifdef CONFIG_PROC_FS
@@ -2745,7 +2888,7 @@
#endif
auto_remove_settings(drive);
drive->driver = NULL;
- __restore_flags(flags);
+ restore_flags(flags); /* all CPUs */
return 0;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov