libata-eh.c
来自「linux 内核源代码」· C语言 代码 · 共 2,561 行 · 第 1/5 页
C
2,561 行
} /* Determine which reset to use and record in ehc->i.action. * prereset() may examine and modify it. */ if (softreset && (!hardreset || (!(lflags & ATA_LFLAG_NO_SRST) && !sata_set_spd_needed(link) && !(ehc->i.action & ATA_EH_HARDRESET)))) tmp_action = ATA_EH_SOFTRESET; else tmp_action = ATA_EH_HARDRESET; ehc->i.action = (ehc->i.action & ~ATA_EH_RESET_MASK) | tmp_action; if (prereset) { rc = prereset(link, jiffies + ATA_EH_PRERESET_TIMEOUT); if (rc) { if (rc == -ENOENT) { ata_link_printk(link, KERN_DEBUG, "port disabled. ignoring.\n"); ehc->i.action &= ~ATA_EH_RESET_MASK; ata_link_for_each_dev(dev, link) classes[dev->devno] = ATA_DEV_NONE; rc = 0; } else ata_link_printk(link, KERN_ERR, "prereset failed (errno=%d)\n", rc); goto out; } } /* prereset() might have modified ehc->i.action */ if (ehc->i.action & ATA_EH_HARDRESET) reset = hardreset; else if (ehc->i.action & ATA_EH_SOFTRESET) reset = softreset; else { /* prereset told us not to reset, bang classes and return */ ata_link_for_each_dev(dev, link) classes[dev->devno] = ATA_DEV_NONE; rc = 0; goto out; } /* did prereset() screw up? if so, fix up to avoid oopsing */ if (!reset) { if (softreset) reset = softreset; else reset = hardreset; } retry: deadline = jiffies + ata_eh_reset_timeouts[try++]; /* shut up during boot probing */ if (verbose) ata_link_printk(link, KERN_INFO, "%s resetting link\n", reset == softreset ? "soft" : "hard"); /* mark that this EH session started with reset */ if (reset == hardreset) ehc->i.flags |= ATA_EHI_DID_HARDRESET; else ehc->i.flags |= ATA_EHI_DID_SOFTRESET; rc = ata_do_reset(link, reset, classes, deadline); if (reset == hardreset && ata_eh_followup_srst_needed(link, rc, classify, classes)) { /* okay, let's do follow-up softreset */ reset = softreset; if (!reset) { ata_link_printk(link, KERN_ERR, "follow-up softreset required " "but no softreset avaliable\n"); rc = -EINVAL; goto fail; } ata_eh_about_to_do(link, NULL, ATA_EH_RESET_MASK); rc = ata_do_reset(link, reset, classes, deadline); } /* -EAGAIN can happen if we skipped followup SRST */ if (rc && rc != -EAGAIN) goto fail; /* was classification successful? */ if (classify && classes[0] == ATA_DEV_UNKNOWN && !(lflags & ATA_LFLAG_ASSUME_CLASS)) { if (try < max_tries) { ata_link_printk(link, KERN_WARNING, "classification failed\n"); rc = -EINVAL; goto fail; } ata_link_printk(link, KERN_WARNING, "classfication failed, assuming ATA\n"); lflags |= ATA_LFLAG_ASSUME_ATA; } ata_link_for_each_dev(dev, link) { /* After the reset, the device state is PIO 0 and the * controller state is undefined. Reset also wakes up * drives from sleeping mode. */ dev->pio_mode = XFER_PIO_0; dev->flags &= ~ATA_DFLAG_SLEEPING; if (ata_link_offline(link)) continue; /* apply class override */ if (lflags & ATA_LFLAG_ASSUME_ATA) classes[dev->devno] = ATA_DEV_ATA; else if (lflags & ATA_LFLAG_ASSUME_SEMB) classes[dev->devno] = ATA_DEV_SEMB_UNSUP; /* not yet */ } /* record current link speed */ if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0) link->sata_spd = (sstatus >> 4) & 0xf; if (postreset) postreset(link, classes); /* reset successful, schedule revalidation */ ata_eh_done(link, NULL, ehc->i.action & ATA_EH_RESET_MASK); ehc->i.action |= ATA_EH_REVALIDATE; rc = 0; out: /* clear hotplug flag */ ehc->i.flags &= ~ATA_EHI_HOTPLUGGED; spin_lock_irqsave(ap->lock, flags); ap->pflags &= ~ATA_PFLAG_RESETTING; spin_unlock_irqrestore(ap->lock, flags); return rc; fail: if (rc == -ERESTART || try >= max_tries) goto out; now = jiffies; if (time_before(now, deadline)) { unsigned long delta = deadline - now; ata_link_printk(link, KERN_WARNING, "reset failed " "(errno=%d), retrying in %u secs\n", rc, (jiffies_to_msecs(delta) + 999) / 1000); while (delta) delta = schedule_timeout_uninterruptible(delta); } if (rc == -EPIPE || try == max_tries - 1) sata_down_spd_limit(link); if (hardreset) reset = hardreset; goto retry;}static int ata_eh_revalidate_and_attach(struct ata_link *link, struct ata_device **r_failed_dev){ struct ata_port *ap = link->ap; struct ata_eh_context *ehc = &link->eh_context; struct ata_device *dev; unsigned int new_mask = 0; unsigned long flags; int rc = 0; DPRINTK("ENTER\n"); /* For PATA drive side cable detection to work, IDENTIFY must * be done backwards such that PDIAG- is released by the slave * device before the master device is identified. */ ata_link_for_each_dev_reverse(dev, link) { unsigned int action = ata_eh_dev_action(dev); unsigned int readid_flags = 0; if (ehc->i.flags & ATA_EHI_DID_RESET) readid_flags |= ATA_READID_POSTRESET; if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) { WARN_ON(dev->class == ATA_DEV_PMP); if (ata_link_offline(link)) { rc = -EIO; goto err; } ata_eh_about_to_do(link, dev, ATA_EH_REVALIDATE); rc = ata_dev_revalidate(dev, ehc->classes[dev->devno], readid_flags); if (rc) goto err; ata_eh_done(link, dev, ATA_EH_REVALIDATE); /* Configuration may have changed, reconfigure * transfer mode. */ ehc->i.flags |= ATA_EHI_SETMODE; /* schedule the scsi_rescan_device() here */ queue_work(ata_aux_wq, &(ap->scsi_rescan_task)); } else if (dev->class == ATA_DEV_UNKNOWN && ehc->tries[dev->devno] && ata_class_enabled(ehc->classes[dev->devno])) { dev->class = ehc->classes[dev->devno]; if (dev->class == ATA_DEV_PMP) rc = sata_pmp_attach(dev); else rc = ata_dev_read_id(dev, &dev->class, readid_flags, dev->id); switch (rc) { case 0: new_mask |= 1 << dev->devno; break; case -ENOENT: /* IDENTIFY was issued to non-existent * device. No need to reset. Just * thaw and kill the device. */ ata_eh_thaw_port(ap); dev->class = ATA_DEV_UNKNOWN; break; default: dev->class = ATA_DEV_UNKNOWN; goto err; } } } /* PDIAG- should have been released, ask cable type if post-reset */ if (ata_is_host_link(link) && ap->ops->cable_detect && (ehc->i.flags & ATA_EHI_DID_RESET)) ap->cbl = ap->ops->cable_detect(ap); /* Configure new devices forward such that user doesn't see * device detection messages backwards. */ ata_link_for_each_dev(dev, link) { if (!(new_mask & (1 << dev->devno)) || dev->class == ATA_DEV_PMP) continue; ehc->i.flags |= ATA_EHI_PRINTINFO; rc = ata_dev_configure(dev); ehc->i.flags &= ~ATA_EHI_PRINTINFO; if (rc) goto err; spin_lock_irqsave(ap->lock, flags); ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG; spin_unlock_irqrestore(ap->lock, flags); /* new device discovered, configure xfermode */ ehc->i.flags |= ATA_EHI_SETMODE; } return 0; err: *r_failed_dev = dev; DPRINTK("EXIT rc=%d\n", rc); return rc;}static int ata_link_nr_enabled(struct ata_link *link){ struct ata_device *dev; int cnt = 0; ata_link_for_each_dev(dev, link) if (ata_dev_enabled(dev)) cnt++; return cnt;}static int ata_link_nr_vacant(struct ata_link *link){ struct ata_device *dev; int cnt = 0; ata_link_for_each_dev(dev, link) if (dev->class == ATA_DEV_UNKNOWN) cnt++; return cnt;}static int ata_eh_skip_recovery(struct ata_link *link){ struct ata_eh_context *ehc = &link->eh_context; struct ata_device *dev; /* skip disabled links */ if (link->flags & ATA_LFLAG_DISABLED) return 1; /* thaw frozen port, resume link and recover failed devices */ if ((link->ap->pflags & ATA_PFLAG_FROZEN) || (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_link_nr_enabled(link)) return 0; /* skip if class codes for all vacant slots are ATA_DEV_NONE */ ata_link_for_each_dev(dev, link) { if (dev->class == ATA_DEV_UNKNOWN && ehc->classes[dev->devno] != ATA_DEV_NONE) return 0; } return 1;}static int ata_eh_handle_dev_fail(struct ata_device *dev, int err){ struct ata_eh_context *ehc = &dev->link->eh_context; ehc->tries[dev->devno]--; switch (err) { case -ENODEV: /* device missing or wrong IDENTIFY data, schedule probing */ ehc->i.probe_mask |= (1 << dev->devno); case -EINVAL: /* give it just one more chance */ ehc->tries[dev->devno] = min(ehc->tries[dev->devno], 1); case -EIO: if (ehc->tries[dev->devno] == 1 && dev->pio_mode > XFER_PIO_0) { /* This is the last chance, better to slow * down than lose it. */ sata_down_spd_limit(dev->link); ata_down_xfermask_limit(dev, ATA_DNXFER_PIO); } } if (ata_dev_enabled(dev) && !ehc->tries[dev->devno]) { /* disable device if it has used up all its chances */ ata_dev_disable(dev); /* detach if offline */ if (ata_link_offline(dev->link)) ata_eh_detach_dev(dev); /* probe if requested */ if ((ehc->i.probe_mask & (1 << dev->devno)) && !(ehc->did_probe_mask & (1 << dev->devno))) { ata_eh_detach_dev(dev); ata_dev_init(dev); ehc->tries[dev->devno] = ATA_EH_DEV_TRIES; ehc->did_probe_mask |= (1 << dev->devno); ehc->i.action |= ATA_EH_SOFTRESET; } return 1; } else { /* soft didn't work? be haaaaard */ if (ehc->i.flags & ATA_EHI_DID_RESET) ehc->i.action |= ATA_EH_HARDRESET; else ehc->i.action |= ATA_EH_SOFTRESET; return 0; }}/** * ata_eh_recover - recover host port after error * @ap: host port to recover * @prereset: prereset method (can be NULL) * @softreset: softreset method (can be NULL) * @hardreset: hardreset method (can be NULL) * @postreset: postreset method (can be NULL) * @r_failed_link: out parameter for failed link * * This is the alpha and omega, eum and yang, heart and soul of * libata exception handling. On entry, actions required to * recover each link and hotplug requests are recorded in the * link's eh_context. This function executes all the operations * with appropriate retrials and fallbacks to resurrect failed * devices, detach goners and greet newcomers. * * LOCKING: * Kernel thread context (may sleep). * * RETURNS: * 0 on success, -errno on failure. */int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset, struct ata_link **r_failed_link){ struct ata_link *link; struct ata_device *dev; int nr_failed_devs, nr_disabled_devs; int reset, rc; unsigned long flags; DPRINTK("ENTER\n"); /* prep for recovery */ ata_port_for_each_link(link, ap) { struct ata_eh_context *ehc = &link->eh_context; /* re-enable link? */ if (ehc->i.action & ATA_EH_ENABLE_LINK) { ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK); spin_lock_irqsave(ap->lock, flags); link->flags &= ~ATA_LFLAG_DISABLED; spin_unlock_irqrestore(ap->lock, flags); ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK); } ata_link_for_each_dev(dev, link) { if (link->flags & ATA_LFLAG_NO_RETRY) ehc->tries[dev->devno] = 1; else ehc->tries[dev->devno] = ATA_EH_DEV_TRIES; /* collect port action mask recorded in dev actions */ ehc->i.action |= ehc->i.dev_action[dev->devno] & ~ATA_EH_PERDEV_MASK; ehc->i.dev_action[dev->devno] &= ATA_EH_PERDEV_MASK; /* process hotplug request */ if (dev->flags & ATA_DFLAG_DETACH) ata_eh_detach_dev(dev); if (!ata_dev_enabled(dev) && ((ehc->i.probe_mask & (1 << dev->devno)) && !(ehc->did_probe_mask & (1 << dev->devno)))) { ata_eh_detach_dev(dev); ata_dev_init(dev); ehc->did_probe_mask |= (1 << dev->devno); ehc->i.action |= ATA_EH_SOFTRESET; } } } retry: rc = 0; nr_failed_devs = 0; nr_disabled_devs = 0; reset = 0; /* if UNLOADING, finish immediately */ if (ap->pflags & ATA_PFLAG_UNLOADING) goto out; /* prep for EH */ ata_port_for_each_link(link, ap) { struct ata_eh_context *ehc = &link->eh_context; /* skip EH if possible. */ if (ata_eh_skip_recovery(link)) ehc->i.action = 0; /* do we need to reset? */ if (ehc->i.action & ATA_EH_RESET_MASK) reset = 1; ata_link_for_each_dev(dev, link) ehc->classes[dev->devno] = ATA_DEV_UNKNOWN; } /* reset */ if (reset) { /* if PMP is attached, this function only deals with * downstream links, port should stay thawed. */ if (!ap->nr_pmp_links) ata_eh_freeze_port(ap); ata_port_for_each_link(link, ap) { struct ata_eh_context *ehc = &link->eh_context; if (!(ehc->i.action & ATA_EH_RESET_MASK)) continue; rc = ata_eh_reset(link, ata_link_nr_vacant(link), prereset, softreset, hardreset, postreset); if (rc) { ata_link_printk(link, KERN_ERR, "reset failed, giving up\n"); goto out; } } if (!ap->nr_pmp_links) ata_eh_thaw_port(ap); } /* the rest */ ata_port_for_each_
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?