libata-eh.c
来自「linux 内核源代码」· C语言 代码 · 共 2,561 行 · 第 1/5 页
C
2,561 行
if (ap->pflags & ATA_PFLAG_RECOVERED) ata_port_printk(ap, KERN_INFO, "EH complete\n"); ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED); /* tell wait_eh that we're done */ ap->pflags &= ~ATA_PFLAG_EH_IN_PROGRESS; wake_up_all(&ap->eh_wait_q); spin_unlock_irqrestore(ap->lock, flags); DPRINTK("EXIT\n");}/** * ata_port_wait_eh - Wait for the currently pending EH to complete * @ap: Port to wait EH for * * Wait until the currently pending EH is complete. * * LOCKING: * Kernel thread context (may sleep). */void ata_port_wait_eh(struct ata_port *ap){ unsigned long flags; DEFINE_WAIT(wait); retry: spin_lock_irqsave(ap->lock, flags); while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) { prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE); spin_unlock_irqrestore(ap->lock, flags); schedule(); spin_lock_irqsave(ap->lock, flags); } finish_wait(&ap->eh_wait_q, &wait); spin_unlock_irqrestore(ap->lock, flags); /* make sure SCSI EH is complete */ if (scsi_host_in_recovery(ap->scsi_host)) { msleep(10); goto retry; }}static int ata_eh_nr_in_flight(struct ata_port *ap){ unsigned int tag; int nr = 0; /* count only non-internal commands */ for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) if (ata_qc_from_tag(ap, tag)) nr++; return nr;}void ata_eh_fastdrain_timerfn(unsigned long arg){ struct ata_port *ap = (void *)arg; unsigned long flags; int cnt; spin_lock_irqsave(ap->lock, flags); cnt = ata_eh_nr_in_flight(ap); /* are we done? */ if (!cnt) goto out_unlock; if (cnt == ap->fastdrain_cnt) { unsigned int tag; /* No progress during the last interval, tag all * in-flight qcs as timed out and freeze the port. */ for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) { struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); if (qc) qc->err_mask |= AC_ERR_TIMEOUT; } ata_port_freeze(ap); } else { /* some qcs have finished, give it another chance */ ap->fastdrain_cnt = cnt; ap->fastdrain_timer.expires = jiffies + ATA_EH_FASTDRAIN_INTERVAL; add_timer(&ap->fastdrain_timer); } out_unlock: spin_unlock_irqrestore(ap->lock, flags);}/** * ata_eh_set_pending - set ATA_PFLAG_EH_PENDING and activate fast drain * @ap: target ATA port * @fastdrain: activate fast drain * * Set ATA_PFLAG_EH_PENDING and activate fast drain if @fastdrain * is non-zero and EH wasn't pending before. Fast drain ensures * that EH kicks in in timely manner. * * LOCKING: * spin_lock_irqsave(host lock) */static void ata_eh_set_pending(struct ata_port *ap, int fastdrain){ int cnt; /* already scheduled? */ if (ap->pflags & ATA_PFLAG_EH_PENDING) return; ap->pflags |= ATA_PFLAG_EH_PENDING; if (!fastdrain) return; /* do we have in-flight qcs? */ cnt = ata_eh_nr_in_flight(ap); if (!cnt) return; /* activate fast drain */ ap->fastdrain_cnt = cnt; ap->fastdrain_timer.expires = jiffies + ATA_EH_FASTDRAIN_INTERVAL; add_timer(&ap->fastdrain_timer);}/** * ata_qc_schedule_eh - schedule qc for error handling * @qc: command to schedule error handling for * * Schedule error handling for @qc. EH will kick in as soon as * other commands are drained. * * LOCKING: * spin_lock_irqsave(host lock) */void ata_qc_schedule_eh(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; WARN_ON(!ap->ops->error_handler); qc->flags |= ATA_QCFLAG_FAILED; ata_eh_set_pending(ap, 1); /* The following will fail if timeout has already expired. * ata_scsi_error() takes care of such scmds on EH entry. * Note that ATA_QCFLAG_FAILED is unconditionally set after * this function completes. */ scsi_req_abort_cmd(qc->scsicmd);}/** * ata_port_schedule_eh - schedule error handling without a qc * @ap: ATA port to schedule EH for * * Schedule error handling for @ap. EH will kick in as soon as * all commands are drained. * * LOCKING: * spin_lock_irqsave(host lock) */void ata_port_schedule_eh(struct ata_port *ap){ WARN_ON(!ap->ops->error_handler); if (ap->pflags & ATA_PFLAG_INITIALIZING) return; ata_eh_set_pending(ap, 1); scsi_schedule_eh(ap->scsi_host); DPRINTK("port EH scheduled\n");}static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link){ int tag, nr_aborted = 0; WARN_ON(!ap->ops->error_handler); /* we're gonna abort all commands, no need for fast drain */ ata_eh_set_pending(ap, 0); for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag); if (qc && (!link || qc->dev->link == link)) { qc->flags |= ATA_QCFLAG_FAILED; ata_qc_complete(qc); nr_aborted++; } } if (!nr_aborted) ata_port_schedule_eh(ap); return nr_aborted;}/** * ata_link_abort - abort all qc's on the link * @link: ATA link to abort qc's for * * Abort all active qc's active on @link and schedule EH. * * LOCKING: * spin_lock_irqsave(host lock) * * RETURNS: * Number of aborted qc's. */int ata_link_abort(struct ata_link *link){ return ata_do_link_abort(link->ap, link);}/** * ata_port_abort - abort all qc's on the port * @ap: ATA port to abort qc's for * * Abort all active qc's of @ap and schedule EH. * * LOCKING: * spin_lock_irqsave(host_set lock) * * RETURNS: * Number of aborted qc's. */int ata_port_abort(struct ata_port *ap){ return ata_do_link_abort(ap, NULL);}/** * __ata_port_freeze - freeze port * @ap: ATA port to freeze * * This function is called when HSM violation or some other * condition disrupts normal operation of the port. Frozen port * is not allowed to perform any operation until the port is * thawed, which usually follows a successful reset. * * ap->ops->freeze() callback can be used for freezing the port * hardware-wise (e.g. mask interrupt and stop DMA engine). If a * port cannot be frozen hardware-wise, the interrupt handler * must ack and clear interrupts unconditionally while the port * is frozen. * * LOCKING: * spin_lock_irqsave(host lock) */static void __ata_port_freeze(struct ata_port *ap){ WARN_ON(!ap->ops->error_handler); if (ap->ops->freeze) ap->ops->freeze(ap); ap->pflags |= ATA_PFLAG_FROZEN; DPRINTK("ata%u port frozen\n", ap->print_id);}/** * ata_port_freeze - abort & freeze port * @ap: ATA port to freeze * * Abort and freeze @ap. * * LOCKING: * spin_lock_irqsave(host lock) * * RETURNS: * Number of aborted commands. */int ata_port_freeze(struct ata_port *ap){ int nr_aborted; WARN_ON(!ap->ops->error_handler); nr_aborted = ata_port_abort(ap); __ata_port_freeze(ap); return nr_aborted;}/** * sata_async_notification - SATA async notification handler * @ap: ATA port where async notification is received * * Handler to be called when async notification via SDB FIS is * received. This function schedules EH if necessary. * * LOCKING: * spin_lock_irqsave(host lock) * * RETURNS: * 1 if EH is scheduled, 0 otherwise. */int sata_async_notification(struct ata_port *ap){ u32 sntf; int rc; if (!(ap->flags & ATA_FLAG_AN)) return 0; rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf); if (rc == 0) sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf); if (!ap->nr_pmp_links || rc) { /* PMP is not attached or SNTF is not available */ if (!ap->nr_pmp_links) { /* PMP is not attached. Check whether ATAPI * AN is configured. If so, notify media * change. */ struct ata_device *dev = ap->link.device; if ((dev->class == ATA_DEV_ATAPI) && (dev->flags & ATA_DFLAG_AN)) ata_scsi_media_change_notify(dev); return 0; } else { /* PMP is attached but SNTF is not available. * ATAPI async media change notification is * not used. The PMP must be reporting PHY * status change, schedule EH. */ ata_port_schedule_eh(ap); return 1; } } else { /* PMP is attached and SNTF is available */ struct ata_link *link; /* check and notify ATAPI AN */ ata_port_for_each_link(link, ap) { if (!(sntf & (1 << link->pmp))) continue; if ((link->device->class == ATA_DEV_ATAPI) && (link->device->flags & ATA_DFLAG_AN)) ata_scsi_media_change_notify(link->device); } /* If PMP is reporting that PHY status of some * downstream ports has changed, schedule EH. */ if (sntf & (1 << SATA_PMP_CTRL_PORT)) { ata_port_schedule_eh(ap); return 1; } return 0; }}/** * ata_eh_freeze_port - EH helper to freeze port * @ap: ATA port to freeze * * Freeze @ap. * * LOCKING: * None. */void ata_eh_freeze_port(struct ata_port *ap){ unsigned long flags; if (!ap->ops->error_handler) return; spin_lock_irqsave(ap->lock, flags); __ata_port_freeze(ap); spin_unlock_irqrestore(ap->lock, flags);}/** * ata_port_thaw_port - EH helper to thaw port * @ap: ATA port to thaw * * Thaw frozen port @ap. * * LOCKING: * None. */void ata_eh_thaw_port(struct ata_port *ap){ unsigned long flags; if (!ap->ops->error_handler) return; spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~ATA_PFLAG_FROZEN; if (ap->ops->thaw) ap->ops->thaw(ap); spin_unlock_irqrestore(ap->lock, flags); DPRINTK("ata%u port thawed\n", ap->print_id);}static void ata_eh_scsidone(struct scsi_cmnd *scmd){ /* nada */}static void __ata_eh_qc_complete(struct ata_queued_cmd *qc){ struct ata_port *ap = qc->ap; struct scsi_cmnd *scmd = qc->scsicmd; unsigned long flags; spin_lock_irqsave(ap->lock, flags); qc->scsidone = ata_eh_scsidone; __ata_qc_complete(qc); WARN_ON(ata_tag_valid(qc->tag)); spin_unlock_irqrestore(ap->lock, flags); scsi_eh_finish_cmd(scmd, &ap->eh_done_q);}/** * ata_eh_qc_complete - Complete an active ATA command from EH * @qc: Command to complete * * Indicate to the mid and upper layers that an ATA command has * completed. To be used from EH. */void ata_eh_qc_complete(struct ata_queued_cmd *qc){ struct scsi_cmnd *scmd = qc->scsicmd; scmd->retries = scmd->allowed; __ata_eh_qc_complete(qc);}/** * ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH * @qc: Command to retry * * Indicate to the mid and upper layers that an ATA command * should be retried. To be used from EH. * * SCSI midlayer limits the number of retries to scmd->allowed. * scmd->retries is decremented for commands which get retried * due to unrelated failures (qc->err_mask is zero). */void ata_eh_qc_retry(struct ata_queued_cmd *qc){ struct scsi_cmnd *scmd = qc->scsicmd; if (!qc->err_mask && scmd->retries) scmd->retries--; __ata_eh_qc_complete(qc);}/** * ata_eh_detach_dev - detach ATA device * @dev: ATA device to detach * * Detach @dev. * * LOCKING: * None. */void ata_eh_detach_dev(struct ata_device *dev){ struct ata_link *link = dev->link; struct ata_port *ap = link->ap; unsigned long flags; ata_dev_disable(dev); spin_lock_irqsave(ap->lock, flags); dev->flags &= ~ATA_DFLAG_DETACH; if (ata_scsi_offline_dev(dev)) { dev->flags |= ATA_DFLAG_DETACHED; ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; } /* clear per-dev EH actions */ ata_eh_clear_action(link, dev, &link->eh_info, ATA_EH_PERDEV_MASK); ata_eh_clear_action(link, dev, &link->eh_context.i, ATA_EH_PERDEV_MASK); spin_unlock_irqrestore(ap->lock, flags);}/** * ata_eh_about_to_do - about to perform eh_action * @link: target ATA link * @dev: target ATA dev for per-dev action (can be NULL) * @action: action about to be performed *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?