⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 libata-pmp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	spin_lock_irqsave(ap->lock, flags);	WARN_ON(ap->nr_pmp_links);	ap->nr_pmp_links = sata_pmp_gscr_ports(dev->gscr);	spin_unlock_irqrestore(ap->lock, flags);	sata_pmp_quirks(ap);	if (ap->ops->pmp_attach)		ap->ops->pmp_attach(ap);	ata_port_for_each_link(tlink, ap)		sata_link_init_spd(tlink);	ata_acpi_associate_sata_port(ap);	return 0; fail:	link->pmp = 0;	return rc;}/** *	sata_pmp_detach - detach a SATA PMP device *	@dev: SATA PMP device to detach * *	Detach SATA PMP device @dev.  This function is also *	responsible for deconfiguring PMP links. * *	LOCKING: *	Kernel thread context (may sleep). */static void sata_pmp_detach(struct ata_device *dev){	struct ata_link *link = dev->link;	struct ata_port *ap = link->ap;	struct ata_link *tlink;	unsigned long flags;	ata_dev_printk(dev, KERN_INFO, "Port Multiplier detaching\n");	WARN_ON(!ata_is_host_link(link) || dev->devno ||		link->pmp != SATA_PMP_CTRL_PORT);	if (ap->ops->pmp_detach)		ap->ops->pmp_detach(ap);	ata_port_for_each_link(tlink, ap)		ata_eh_detach_dev(tlink->device);	spin_lock_irqsave(ap->lock, flags);	ap->nr_pmp_links = 0;	link->pmp = 0;	spin_unlock_irqrestore(ap->lock, flags);	ata_acpi_associate_sata_port(ap);}/** *	sata_pmp_same_pmp - does new GSCR matches the configured PMP? *	@dev: PMP device to compare against *	@new_gscr: GSCR block of the new device * *	Compare @new_gscr against @dev and determine whether @dev is *	the PMP described by @new_gscr. * *	LOCKING: *	None. * *	RETURNS: *	1 if @dev matches @new_gscr, 0 otherwise. */static int sata_pmp_same_pmp(struct ata_device *dev, const u32 *new_gscr){	const u32 *old_gscr = dev->gscr;	u16 old_vendor, new_vendor, old_devid, new_devid;	int old_nr_ports, new_nr_ports;	old_vendor = sata_pmp_gscr_vendor(old_gscr);	new_vendor = sata_pmp_gscr_vendor(new_gscr);	old_devid = sata_pmp_gscr_devid(old_gscr);	new_devid = sata_pmp_gscr_devid(new_gscr);	old_nr_ports = sata_pmp_gscr_ports(old_gscr);	new_nr_ports = sata_pmp_gscr_ports(new_gscr);	if (old_vendor != new_vendor) {		ata_dev_printk(dev, KERN_INFO, "Port Multiplier "			       "vendor mismatch '0x%x' != '0x%x'\n",			       old_vendor, new_vendor);		return 0;	}	if (old_devid != new_devid) {		ata_dev_printk(dev, KERN_INFO, "Port Multiplier "			       "device ID mismatch '0x%x' != '0x%x'\n",			       old_devid, new_devid);		return 0;	}	if (old_nr_ports != new_nr_ports) {		ata_dev_printk(dev, KERN_INFO, "Port Multiplier "			       "nr_ports mismatch '0x%x' != '0x%x'\n",			       old_nr_ports, new_nr_ports);		return 0;	}	return 1;}/** *	sata_pmp_revalidate - revalidate SATA PMP *	@dev: PMP device to revalidate *	@new_class: new class code * *	Re-read GSCR block and make sure @dev is still attached to the *	port and properly configured. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno otherwise. */static int sata_pmp_revalidate(struct ata_device *dev, unsigned int new_class){	struct ata_link *link = dev->link;	struct ata_port *ap = link->ap;	u32 *gscr = (void *)ap->sector_buf;	int rc;	DPRINTK("ENTER\n");	ata_eh_about_to_do(link, NULL, ATA_EH_REVALIDATE);	if (!ata_dev_enabled(dev)) {		rc = -ENODEV;		goto fail;	}	/* wrong class? */	if (ata_class_enabled(new_class) && new_class != ATA_DEV_PMP) {		rc = -ENODEV;		goto fail;	}	/* read GSCR */	rc = sata_pmp_read_gscr(dev, gscr);	if (rc)		goto fail;	/* is the pmp still there? */	if (!sata_pmp_same_pmp(dev, gscr)) {		rc = -ENODEV;		goto fail;	}	memcpy(dev->gscr, gscr, sizeof(gscr[0]) * SATA_PMP_GSCR_DWORDS);	rc = sata_pmp_configure(dev, 0);	if (rc)		goto fail;	ata_eh_done(link, NULL, ATA_EH_REVALIDATE);	DPRINTK("EXIT, rc=0\n");	return 0; fail:	ata_dev_printk(dev, KERN_ERR,		       "PMP revalidation failed (errno=%d)\n", rc);	DPRINTK("EXIT, rc=%d\n", rc);	return rc;}/** *	sata_pmp_revalidate_quick - revalidate SATA PMP quickly *	@dev: PMP device to revalidate * *	Make sure the attached PMP is accessible. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno otherwise. */static int sata_pmp_revalidate_quick(struct ata_device *dev){	unsigned int err_mask;	u32 prod_id;	err_mask = sata_pmp_read(dev->link, SATA_PMP_GSCR_PROD_ID, &prod_id);	if (err_mask) {		ata_dev_printk(dev, KERN_ERR, "failed to read PMP product ID "			       "(Emask=0x%x)\n", err_mask);		return -EIO;	}	if (prod_id != dev->gscr[SATA_PMP_GSCR_PROD_ID]) {		ata_dev_printk(dev, KERN_ERR, "PMP product ID mismatch\n");		/* something weird is going on, request full PMP recovery */		return -EIO;	}	return 0;}/** *	sata_pmp_eh_recover_pmp - recover PMP *	@ap: ATA port PMP is attached to *	@prereset: prereset method (can be NULL) *	@softreset: softreset method *	@hardreset: hardreset method *	@postreset: postreset method (can be NULL) * *	Recover PMP attached to @ap.  Recovery procedure is somewhat *	similar to that of ata_eh_recover() except that reset should *	always be performed in hard->soft sequence and recovery *	failure results in PMP detachment. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno on failure. */static int sata_pmp_eh_recover_pmp(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 *link = &ap->link;	struct ata_eh_context *ehc = &link->eh_context;	struct ata_device *dev = link->device;	int tries = ATA_EH_PMP_TRIES;	int detach = 0, rc = 0;	int reval_failed = 0;	DPRINTK("ENTER\n");	if (dev->flags & ATA_DFLAG_DETACH) {		detach = 1;		goto fail;	} retry:	ehc->classes[0] = ATA_DEV_UNKNOWN;	if (ehc->i.action & ATA_EH_RESET_MASK) {		struct ata_link *tlink;		ata_eh_freeze_port(ap);		/* reset */		ehc->i.action = ATA_EH_HARDRESET;		rc = ata_eh_reset(link, 0, prereset, softreset, hardreset,				  postreset);		if (rc) {			ata_link_printk(link, KERN_ERR,					"failed to reset PMP, giving up\n");			goto fail;		}		ata_eh_thaw_port(ap);		/* PMP is reset, SErrors cannot be trusted, scan all */		ata_port_for_each_link(tlink, ap)			ata_ehi_schedule_probe(&tlink->eh_context.i);	}	/* If revalidation is requested, revalidate and reconfigure;	 * otherwise, do quick revalidation.	 */	if (ehc->i.action & ATA_EH_REVALIDATE)		rc = sata_pmp_revalidate(dev, ehc->classes[0]);	else		rc = sata_pmp_revalidate_quick(dev);	if (rc) {		tries--;		if (rc == -ENODEV) {			ehc->i.probe_mask |= 1;			detach = 1;			/* give it just two more chances */			tries = min(tries, 2);		}		if (tries) {			int sleep = ehc->i.flags & ATA_EHI_DID_RESET;			/* consecutive revalidation failures? speed down */			if (reval_failed)				sata_down_spd_limit(link);			else				reval_failed = 1;			ata_dev_printk(dev, KERN_WARNING,				       "retrying hardreset%s\n",				       sleep ? " in 5 secs" : "");			if (sleep)				ssleep(5);			ehc->i.action |= ATA_EH_HARDRESET;			goto retry;		} else {			ata_dev_printk(dev, KERN_ERR, "failed to recover PMP "				       "after %d tries, giving up\n",				       ATA_EH_PMP_TRIES);			goto fail;		}	}	/* okay, PMP resurrected */	ehc->i.flags = 0;	DPRINTK("EXIT, rc=0\n");	return 0; fail:	sata_pmp_detach(dev);	if (detach)		ata_eh_detach_dev(dev);	else		ata_dev_disable(dev);	DPRINTK("EXIT, rc=%d\n", rc);	return rc;}static int sata_pmp_eh_handle_disabled_links(struct ata_port *ap){	struct ata_link *link;	unsigned long flags;	int rc;	spin_lock_irqsave(ap->lock, flags);	ata_port_for_each_link(link, ap) {		if (!(link->flags & ATA_LFLAG_DISABLED))			continue;		spin_unlock_irqrestore(ap->lock, flags);		/* Some PMPs require hardreset sequence to get		 * SError.N working.		 */		if ((link->flags & ATA_LFLAG_HRST_TO_RESUME) &&		    (link->eh_context.i.flags & ATA_EHI_RESUME_LINK))			sata_link_hardreset(link, sata_deb_timing_normal,					    jiffies + ATA_TMOUT_INTERNAL_QUICK);		/* unconditionally clear SError.N */		rc = sata_scr_write(link, SCR_ERROR, SERR_PHYRDY_CHG);		if (rc) {			ata_link_printk(link, KERN_ERR, "failed to clear "					"SError.N (errno=%d)\n", rc);			return rc;		}		spin_lock_irqsave(ap->lock, flags);	}	spin_unlock_irqrestore(ap->lock, flags);	return 0;}static int sata_pmp_handle_link_fail(struct ata_link *link, int *link_tries){	struct ata_port *ap = link->ap;	unsigned long flags;	if (link_tries[link->pmp] && --link_tries[link->pmp])		return 1;	/* disable this link */	if (!(link->flags & ATA_LFLAG_DISABLED)) {		ata_link_printk(link, KERN_WARNING,			"failed to recover link after %d tries, disabling\n",			ATA_EH_PMP_LINK_TRIES);		spin_lock_irqsave(ap->lock, flags);		link->flags |= ATA_LFLAG_DISABLED;		spin_unlock_irqrestore(ap->lock, flags);	}	ata_dev_disable(link->device);	link->eh_context.i.action = 0;	return 0;}/** *	sata_pmp_eh_recover - recover PMP-enabled port *	@ap: ATA port to recover *	@prereset: prereset method (can be NULL) *	@softreset: softreset method *	@hardreset: hardreset method *	@postreset: postreset method (can be NULL) *	@pmp_prereset: PMP prereset method (can be NULL) *	@pmp_softreset: PMP softreset method (can be NULL) *	@pmp_hardreset: PMP hardreset method (can be NULL) *	@pmp_postreset: PMP postreset method (can be NULL) * *	Drive EH recovery operation for PMP enabled port @ap.  This *	function recovers host and PMP ports with proper retrials and *	fallbacks.  Actual recovery operations are performed using *	ata_eh_recover() and sata_pmp_eh_recover_pmp(). * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno on failure. */static int sata_pmp_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,		ata_prereset_fn_t pmp_prereset, ata_reset_fn_t pmp_softreset,		ata_reset_fn_t pmp_hardreset, ata_postreset_fn_t pmp_postreset){	int pmp_tries, link_tries[SATA_PMP_MAX_PORTS];	struct ata_link *pmp_link = &ap->link;	struct ata_device *pmp_dev = pmp_link->device;	struct ata_eh_context *pmp_ehc = &pmp_link->eh_context;	struct ata_link *link;	struct ata_device *dev;	unsigned int err_mask;	u32 gscr_error, sntf;	int cnt, rc;	pmp_tries = ATA_EH_PMP_TRIES;	ata_port_for_each_link(link, ap)		link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES; retry:	/* PMP attached? */	if (!ap->nr_pmp_links) {		rc = ata_eh_recover(ap, prereset, softreset, hardreset,				    postreset, NULL);		if (rc) {			ata_link_for_each_dev(dev, &ap->link)				ata_dev_disable(dev);			return rc;		}		if (pmp_dev->class != ATA_DEV_PMP)			return 0;		/* new PMP online */		ata_port_for_each_link(link, ap)			link_tries[link->pmp] = ATA_EH_PMP_LINK_TRIES;		/* fall through */	}	/* recover pmp */	rc = sata_pmp_eh_recover_pmp(ap, prereset, softreset, hardreset,				     postreset);	if (rc)		goto pmp_fail;	/* handle disabled links */	rc = sata_pmp_eh_handle_disabled_links(ap);	if (rc)		goto pmp_fail;	/* recover links */	rc = ata_eh_recover(ap, pmp_prereset, pmp_softreset, pmp_hardreset,			    pmp_postreset, &link);	if (rc)		goto link_fail;	/* Connection status might have changed while resetting other	 * links, check SATA_PMP_GSCR_ERROR before returning.	 */	/* clear SNotification */	rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);	if (rc == 0)		sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);	/* enable notification */	if (pmp_dev->flags & ATA_DFLAG_AN) {		pmp_dev->gscr[SATA_PMP_GSCR_FEAT_EN] |= SATA_PMP_FEAT_NOTIFY;		err_mask = sata_pmp_write(pmp_dev->link, SATA_PMP_GSCR_FEAT_EN,					  pmp_dev->gscr[SATA_PMP_GSCR_FEAT_EN]);		if (err_mask) {			ata_dev_printk(pmp_dev, KERN_ERR, "failed to write "				       "PMP_FEAT_EN (Emask=0x%x)\n", err_mask);			rc = -EIO;			goto pmp_fail;		}	}	/* check GSCR_ERROR */	err_mask = sata_pmp_read(pmp_link, SATA_PMP_GSCR_ERROR, &gscr_error);	if (err_mask) {		ata_dev_printk(pmp_dev, KERN_ERR, "failed to read "			       "PMP_GSCR_ERROR (Emask=0x%x)\n", err_mask);		rc = -EIO;		goto pmp_fail;	}	cnt = 0;	ata_port_for_each_link(link, ap) {		if (!(gscr_error & (1 << link->pmp)))			continue;		if (sata_pmp_handle_link_fail(link, link_tries)) {			ata_ehi_hotplugged(&link->eh_context.i);			cnt++;		} else {			ata_link_printk(link, KERN_WARNING,				"PHY status changed but maxed out on retries, "				"giving up\n");			ata_link_printk(link, KERN_WARNING,				"Manully issue scan to resume this link\n");		}	}	if (cnt) {		ata_port_printk(ap, KERN_INFO, "PMP SError.N set for some "				"ports, repeating recovery\n");		goto retry;	}	return 0; link_fail:	if (sata_pmp_handle_link_fail(link, link_tries)) {		pmp_ehc->i.action |= ATA_EH_HARDRESET;		goto retry;	}	/* fall through */ pmp_fail:	/* Control always ends up here after detaching PMP.  Shut up	 * and return if we're unloading.	 */	if (ap->pflags & ATA_PFLAG_UNLOADING)		return rc;	if (!ap->nr_pmp_links)		goto retry;	if (--pmp_tries) {		ata_port_printk(ap, KERN_WARNING,				"failed to recover PMP, retrying in 5 secs\n");		pmp_ehc->i.action |= ATA_EH_HARDRESET;		ssleep(5);		goto retry;	}	ata_port_printk(ap, KERN_ERR,			"failed to recover PMP after %d tries, giving up\n",			ATA_EH_PMP_TRIES);	sata_pmp_detach(pmp_dev);	ata_dev_disable(pmp_dev);	return rc;}/** *	sata_pmp_do_eh - do standard error handling for PMP-enabled host *	@ap: host port to handle error for *	@prereset: prereset method (can be NULL) *	@softreset: softreset method *	@hardreset: hardreset method *	@postreset: postreset method (can be NULL) *	@pmp_prereset: PMP prereset method (can be NULL) *	@pmp_softreset: PMP softreset method (can be NULL) *	@pmp_hardreset: PMP hardreset method (can be NULL) *	@pmp_postreset: PMP postreset method (can be NULL) * *	Perform standard error handling sequence for PMP-enabled host *	@ap. * *	LOCKING: *	Kernel thread context (may sleep). */void sata_pmp_do_eh(struct ata_port *ap,		ata_prereset_fn_t prereset, ata_reset_fn_t softreset,		ata_reset_fn_t hardreset, ata_postreset_fn_t postreset,		ata_prereset_fn_t pmp_prereset, ata_reset_fn_t pmp_softreset,		ata_reset_fn_t pmp_hardreset, ata_postreset_fn_t pmp_postreset){	ata_eh_autopsy(ap);	ata_eh_report(ap);	sata_pmp_eh_recover(ap, prereset, softreset, hardreset, postreset,			    pmp_prereset, pmp_softreset, pmp_hardreset,			    pmp_postreset);	ata_eh_finish(ap);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -