irqreturn_t ide_intr (int irq, void *dev_id) { ide_hwif_t *hwif = (ide_hwif_t *)dev_id; ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; unsigned long flags; ide_startstop_t startstop; irqreturn_t irq_ret = IRQ_NONE; int plug_device = 0; if (hwif->host->host_flags & IDE_HFLAG_SERIALIZE) { if (hwif != hwif->host->cur_port) goto out_early; } spin_lock_irqsave(&hwif->lock, flags); if (!ide_ack_intr(hwif)) goto out; handler = hwif->handler; if (handler == NULL || hwif->polling) { /* * 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. * * FIXME: unexpected_intr should be hwif-> then we can * remove all the ifdef PCI crap */ #ifdef CONFIG_BLK_DEV_IDEPCI if (hwif->chipset != ide_pci) #endif /* CONFIG_BLK_DEV_IDEPCI */ { /* * Probably not a shared PCI interrupt, * so we can safely try to do something about it: */ unexpected_intr(irq, hwif); #ifdef CONFIG_BLK_DEV_IDEPCI } else { /* * Whack the status register, just in case * we have a leftover pending IRQ. */ (void)hwif->tp_ops->read_status(hwif); #endif /* CONFIG_BLK_DEV_IDEPCI */ } goto out; } drive = hwif->cur_dev; if (!drive_is_ready(drive)) /* * This happens regularly when we share a PCI IRQ with * another device. Unfortunately, it can also happen * with some buggy drives that trigger the IRQ before * their status register is up to date. Hopefully we have * enough advance overhead that the latter isn't a problem. */ goto out; hwif->handler = NULL; hwif->req_gen++; del_timer(&hwif->timer); spin_unlock(&hwif->lock); if (hwif->port_ops && hwif->port_ops->clear_irq) hwif->port_ops->clear_irq(drive); if (drive->dev_flags & IDE_DFLAG_UNMASK) local_irq_enable_in_hardirq(); /* service this interrupt, may set handler for next interrupt */ startstop = handler(drive); spin_lock_irq(&hwif->lock); /* * 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 of the same (on any CPU) until we return. */ if (startstop == ide_stopped) { BUG_ON(hwif->handler); ide_unlock_port(hwif); plug_device = 1; } irq_ret = IRQ_HANDLED; out: spin_unlock_irqrestore(&hwif->lock, flags); out_early: if (plug_device) { ide_unlock_host(hwif->host); ide_plug_device(drive); } return irq_ret; }
void ide_timer_expiry (unsigned long data) { ide_hwif_t *hwif = (ide_hwif_t *)data; ide_drive_t *uninitialized_var(drive); ide_handler_t *handler; unsigned long flags; unsigned long wait = -1; int plug_device = 0; spin_lock_irqsave(&hwif->lock, flags); handler = hwif->handler; if (handler == NULL || hwif->req_gen != hwif->req_gen_timer) { /* * Either a marginal timeout occurred * (got the interrupt just as timer expired), * or we were "sleeping" to give other devices a chance. * Either way, we don't really want to complain about anything. */ } else { ide_expiry_t *expiry = hwif->expiry; ide_startstop_t startstop = ide_stopped; drive = hwif->cur_dev; if (expiry) { wait = expiry(drive); if (wait > 0) { /* continue */ /* reset timer */ hwif->timer.expires = jiffies + wait; hwif->req_gen_timer = hwif->req_gen; add_timer(&hwif->timer); spin_unlock_irqrestore(&hwif->lock, flags); return; } } hwif->handler = NULL; /* * We need to simulate a real interrupt when invoking * the handler() function, which means we need to * globally mask the specific IRQ: */ spin_unlock(&hwif->lock); /* disable_irq_nosync ?? */ disable_irq(hwif->irq); /* local CPU only, as if we were handling an interrupt */ local_irq_disable(); if (hwif->polling) { startstop = handler(drive); } else if (drive_is_ready(drive)) { if (drive->waiting_for_dma) hwif->dma_ops->dma_lost_irq(drive); (void)ide_ack_intr(hwif); printk(KERN_WARNING "%s: lost interrupt\n", drive->name); startstop = handler(drive); } else { if (drive->waiting_for_dma) startstop = ide_dma_timeout_retry(drive, wait); else startstop = ide_error(drive, "irq timeout", hwif->tp_ops->read_status(hwif)); } spin_lock_irq(&hwif->lock); enable_irq(hwif->irq); if (startstop == ide_stopped) { ide_unlock_port(hwif); plug_device = 1; } } spin_unlock_irqrestore(&hwif->lock, flags); if (plug_device) { ide_unlock_host(hwif->host); ide_plug_device(drive); } }
static int idescsi_eh_reset (struct scsi_cmnd *cmd) { struct request *req; idescsi_scsi_t *scsi = scsihost_to_idescsi(cmd->device->host); ide_drive_t *drive = scsi->drive; int ready = 0; int ret = SUCCESS; /* In idescsi_eh_reset we forcefully remove the command from the ide subsystem and reset the device. */ if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) printk (KERN_WARNING "ide-scsi: reset called for %lu\n", cmd->serial_number); if (!drive) { printk (KERN_WARNING "ide-scsi: Drive not set in idescsi_eh_reset\n"); WARN_ON(1); return FAILED; } spin_lock_irq(cmd->device->host->host_lock); spin_lock(&ide_lock); if (!scsi->pc || (req = scsi->pc->rq) != HWGROUP(drive)->rq || !HWGROUP(drive)->handler) { printk (KERN_WARNING "ide-scsi: No active request in idescsi_eh_reset\n"); spin_unlock(&ide_lock); spin_unlock_irq(cmd->device->host->host_lock); return FAILED; } /* kill current request */ if (__blk_end_request(req, -EIO, 0)) BUG(); if (blk_sense_request(req)) kfree(scsi->pc->buf); kfree(scsi->pc); scsi->pc = NULL; blk_put_request(req); /* now nuke the drive queue */ while ((req = elv_next_request(drive->queue))) { if (__blk_end_request(req, -EIO, 0)) BUG(); } HWGROUP(drive)->rq = NULL; HWGROUP(drive)->handler = NULL; HWGROUP(drive)->busy = 1; /* will set this to zero when ide reset finished */ spin_unlock(&ide_lock); ide_do_reset(drive); /* ide_do_reset starts a polling handler which restarts itself every 50ms until the reset finishes */ do { spin_unlock_irq(cmd->device->host->host_lock); msleep(50); spin_lock_irq(cmd->device->host->host_lock); } while ( HWGROUP(drive)->handler ); ready = drive_is_ready(drive); HWGROUP(drive)->busy--; if (!ready) { printk (KERN_ERR "ide-scsi: reset failed!\n"); ret = FAILED; } spin_unlock_irq(cmd->device->host->host_lock); return ret; }