Skip to contentSkip to conversation

libata: add @disable_on_err to ata_set_mode()

PATCH New Tejun Heo 2 participants 10 messages Mar 13, 2006 – Mar 21, 2006

Subsystems (2)
lore
Share & tools
lore

Tejun Heo

author
#0 lore raw

PATCH 1/1 libata: add @disable_on_err to ata_set_mode()

Hello.

This patchset adds @disable_on_err to ata_set_mode(). If zero, ata_set_mode() returns error code on failure; otherwise, only the failing devices get disabled. Note that the approach is slightly different here because ATA xfer mode configuration is port-wide, so ata_set_mode() acts as the final decision maker regarding device offlining advised by @disable_on_err.

This patchset is against
[1] the current upstream
+ [2] implement-per-dev-xfermask patchset

Thanks.

--
tejun

[1] fb21f0d0ec7e31cc814165e1a9d2662d9c9dd980
[2] http://article.gmane.org/gmane.linux.ide/8705

author
#1 lore raw

PATCH 1/4 libata: implement ata_dev_disable()

This patch implements ata_dev_disable() which prints a warning message and takes @dev offline. Currently, this is done by explicitly incrementing dev->class with case-by-case warning messages. Giving user clear indication when libata gives up will be more important as libata will be doing more retries.

---

 drivers/scsi/libata-core.c |    9 +++++++++
 1 files changed, 9 insertions(+), 0 deletions(-)

b0e8e3c30b12f3adf35a7047031c9a6e8258e289

drivers/scsi/libata-core.c +9 -0
index 7b1150d..92c7c53 100644
@@ -394,6 +394,15 @@ static const char *ata_mode_string(unsig
 	return "<n/a>";
 }
 
+static void ata_dev_disable(struct ata_port *ap, struct ata_device *dev)
+{
+	if (ata_dev_present(dev)) {
+		printk(KERN_WARNING "ata%u: dev %u disabled\n",
+		       ap->id, dev->devno);
+		dev->class++;
+	}
+}
+
 /**
  *	ata_pio_devchk - PATA device presence detection
  *	@ap: ATA channel to examine
Signature · 1 line
1.2.4
author
#2 lore raw

PATCH 2/4 libata: use ata_dev_disable() in ata_bus_probe()

In reply to Tejun Heo

We may or may not disable a device after ata_dev_configure() fails. Kill 'not supported, ignoring' message in ata_dev_configure() and use ata_dev_disable() in ata_bus_probe().

---

 drivers/scsi/libata-core.c |    4 +---
 1 files changed, 1 insertions(+), 3 deletions(-)

54e2f80b5190b3f94779c06cbfe722b4bfe9204a

drivers/scsi/libata-core.c +1 -3
index 92c7c53..b7595bf 100644
@@ -1373,8 +1373,6 @@ static int ata_dev_configure(struct ata_
 	return 0;
 
 err_out_nosup:
-	printk(KERN_WARNING "ata%u: dev %u not supported, ignoring\n",
-	       ap->id, dev->devno);
 	DPRINTK("EXIT, err\n");
 	return rc;
 }
@@ -1443,7 +1441,7 @@ static int ata_bus_probe(struct ata_port
 		}
 
 		if (ata_dev_configure(ap, dev, 1)) {
-			dev->class++;	/* disable device */
+			ata_dev_disable(ap, dev);
 			continue;
 		}
 
Signature · 1 line
1.2.4
author
#3 lore raw

PATCH 3/4 libata: make ata_set_mode() responsible for failure handling

In reply to Tejun Heo

Make ata_set_mode() responsible for determining whether to take port or device offline on failure. ata_dev_set_xfermode() and ata_dev_set_mode() indicate error to the caller instead of disabling port directly on failure. Also, for consistency, ata_dev_present() check is done in ata_set_mode() instead of ata_dev_set_mode().

---

 drivers/scsi/libata-core.c |   56 ++++++++++++++++++++++++++++----------------
 1 files changed, 36 insertions(+), 20 deletions(-)

07934616ecb83d7bac2adfe9eb32f5173feb32ff

drivers/scsi/libata-core.c +36 -20
index b7595bf..6826181 100644
@@ -64,7 +64,8 @@
 static unsigned int ata_dev_init_params(struct ata_port *ap,
 					struct ata_device *dev);
 static void ata_set_mode(struct ata_port *ap);
-static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev);
+static unsigned int ata_dev_set_xfermode(struct ata_port *ap,
+					 struct ata_device *dev);
 static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev);
 
 static unsigned int ata_unique_id = 1;
@@ -1754,20 +1755,28 @@ int ata_timing_compute(struct ata_device
 	return 0;
 }
 
-static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
 {
-	if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
-		return;
+	unsigned int err_mask;
+	int rc;
 
 	if (dev->xfer_shift == ATA_SHIFT_PIO)
 		dev->flags |= ATA_DFLAG_PIO;
 
-	ata_dev_set_xfermode(ap, dev);
+	err_mask = ata_dev_set_xfermode(ap, dev);
+	if (err_mask) {
+		printk(KERN_ERR
+		       "ata%u: failed to set xfermode (err_mask=0x%x)\n",
+		       ap->id, err_mask);
+		return -EIO;
+	}
 
-	if (ata_dev_revalidate(ap, dev, 0)) {
-		printk(KERN_ERR "ata%u: failed to revalidate after set "
-		       "xfermode, disabled\n", ap->id);
-		ata_port_disable(ap);
+	rc = ata_dev_revalidate(ap, dev, 0);
+	if (rc) {
+		printk(KERN_ERR
+		       "ata%u: failed to revalidate after set xfermode\n",
+		       ap->id);
+		return rc;
 	}
 
 	DPRINTK("xfer_shift=%u, xfer_mode=0x%x\n",
@@ -1776,6 +1785,7 @@ static void ata_dev_set_mode(struct ata_
 	printk(KERN_INFO "ata%u: dev %u configured for %s\n",
 	       ap->id, dev->devno,
 	       ata_mode_string(ata_xfer_mode2mask(dev->xfer_mode)));
+	return 0;
 }
 
 static int ata_host_set_pio(struct ata_port *ap)
@@ -1859,11 +1869,15 @@ static void ata_set_mode(struct ata_port
 	ata_host_set_dma(ap);
 
 	/* step 4: update devices' xfer mode */
-	for (i = 0; i < ATA_MAX_DEVICES; i++)
-		ata_dev_set_mode(ap, &ap->device[i]);
+	for (i = 0; i < ATA_MAX_DEVICES; i++) {
+		struct ata_device *dev = &ap->device[i];
 
-	if (ap->flags & ATA_FLAG_PORT_DISABLED)
-		return;
+		if (!ata_dev_present(dev))
+			continue;
+
+		if (ata_dev_set_mode(ap, dev))
+			goto err_out;
+	}
 
 	if (ap->ops->post_set_mode)
 		ap->ops->post_set_mode(ap);
@@ -2702,11 +2716,16 @@ static void ata_dev_xfermask(struct ata_
  *
  *	LOCKING:
  *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	0 on success, AC_ERR_* mask otherwise.
  */
 
-static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev)
+static unsigned int ata_dev_set_xfermode(struct ata_port *ap,
+					 struct ata_device *dev)
 {
 	struct ata_taskfile tf;
+	unsigned int err_mask;
 
 	/* set up set-features taskfile */
 	DPRINTK("set features - xfer mode\n");
@@ -2718,13 +2737,10 @@ static void ata_dev_set_xfermode(struct 
 	tf.protocol = ATA_PROT_NODATA;
 	tf.nsect = dev->xfer_mode;
 
-	if (ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0)) {
-		printk(KERN_ERR "ata%u: failed to set xfermode, disabled\n",
-		       ap->id);
-		ata_port_disable(ap);
-	}
+	err_mask = ata_exec_internal(ap, dev, &tf, DMA_NONE, NULL, 0);
 
-	DPRINTK("EXIT\n");
+	DPRINTK("EXIT, err_mask=%x\n", err_mask);
+	return err_mask;
 }
 
 /**
Signature · 1 line
1.2.4
#4 lore raw

Re: [PATCH 3/4] libata: make ata_set_mode() responsible for failure handling

Quoted 29 lines · Tejun Heo

Make ata_set_mode() responsible for determining whether to take port or device offline on failure. ata_dev_set_xfermode() and ata_dev_set_mode() indicate error to the caller instead of disabling port directly on failure. Also, for consistency, ata_dev_present() check is done in ata_set_mode() instead of ata_dev_set_mode().

---

 drivers/scsi/libata-core.c |   56 ++++++++++++++++++++++++++++----------------
 1 files changed, 36 insertions(+), 20 deletions(-)

07934616ecb83d7bac2adfe9eb32f5173feb32ff

drivers/scsi/libata-core.c +2 -1
index b7595bf..6826181 100644
@@ -64,7 +64,8 @@
 static unsigned int ata_dev_init_params(struct ata_port *ap,
 					struct ata_device *dev);
 static void ata_set_mode(struct ata_port *ap);
-static void ata_dev_set_xfermode(struct ata_port *ap, struct ata_device *dev);
+static unsigned int ata_dev_set_xfermode(struct ata_port *ap,
+					 struct ata_device *dev);
 static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev);
 
 static unsigned int ata_unique_id = 1;

drivers/scsi/libata-core.c int ata_timing_compute(struct ata_device · line 1756

Quoted 2 lines
drivers/scsi/libata-core.c +0 -0
@@ -1754,20 +1755,28 @@ int ata_timing_compute(struct ata_device
 	return 0;
 }

ACK 1-4 in this patchset, but dropping due to dropped patches in previous patchset.

I have the following concern with this patch (#3) however:

drivers/scsi/libata-core.c int ata_timing_compute(struct ata_device · line 1760

Quoted 5 lines
drivers/scsi/libata-core.c +1 -3
-static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
 {
-	if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
-		return;

I think you drop too many ATA_FLAG_PORT_DISABLED tests in this patch, leading the code to potentially miss a previously-flagged PORT_DISABLED (perhaps by an LLDD).

Jeff

author
#5 lore raw

Re: [PATCH 3/4] libata: make ata_set_mode() responsible for failure handling

Quoted 13 lines · Jeff Garzik

I have the following concern with this patch (#3) however:

drivers/scsi/libata-core.c int ata_timing_compute(struct ata_device · line 1760

Quoted 5 lines
drivers/scsi/libata-core.c +1 -1
-static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)

{

drivers/scsi/libata-core.c +0 -2
-	if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
-		return;

I think you drop too many ATA_FLAG_PORT_DISABLED tests in this patch, leading the code to potentially miss a previously-flagged PORT_DISABLED (perhaps by an LLDD).

Hmmm... the plan is to disallow LLDD's take ports or devices offline from low level callbacks. They should just let upper layer know by returning failure code.

Currently, during probing, the only place where disabling a port is allowed is ->phy_reset (for compatibility), which ata_bus_probe() interprets as no active device and reenables the port. Later, ->error_handler() would have that power too but I don't think any other callback should be allowed to do that. Also, no current in-kernel LLDD does that during probing process.

If we're gonna allow some callbacks to disable ports or devices. I think we need to come up with clear rules such that checks can be performed exactly where needed.

Thanks.

Signature · 1 line
tejun
#6 lore raw

Re: [PATCH 3/4] libata: make ata_set_mode() responsible for failure handling

Quoted 15 lines · Tejun Heo
Quoted 11 lines · Jeff Garzik

I have the following concern with this patch (#3) however:

drivers/scsi/libata-core.c int ata_timing_compute(struct ata_device · line 1760

Quoted 5 lines
drivers/scsi/libata-core.c +1 -1
-static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)

{

drivers/scsi/libata-core.c +0 -2
-	if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
-		return;

I think you drop too many ATA_FLAG_PORT_DISABLED tests in this patch, leading the code to potentially miss a previously-flagged PORT_DISABLED (perhaps by an LLDD).

Quoted 3 lines

Hmmm... the plan is to disallow LLDD's take ports or devices offline from low level callbacks. They should just let upper layer know by returning failure code.

Long term that plan is fine, but you still have to deal with the existing API one way or another. ata_port_disable() is called directly by a bunch of Alan's PATA drivers, and by ata_piix and sata_mv.

Thus you would either need to keep the PORT_DISABLED checks or convert the drivers in question to a better API.

So just check those ata_port_disable() cases...

Jeff

author
#7 lore raw

Re: [PATCH 3/4] libata: make ata_set_mode() responsible for failure handling

Quoted 23 lines · Jeff Garzik
Quoted 18 lines · Tejun Heo
Quoted 14 lines · Jeff Garzik

I have the following concern with this patch (#3) however:

drivers/scsi/libata-core.c int ata_timing_compute(struct ata_device · line 1760

Quoted 6 lines

-static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev) {

drivers/scsi/libata-core.c +0 -2
-	if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
-		return;

I think you drop too many ATA_FLAG_PORT_DISABLED tests in this patch, leading the code to potentially miss a previously-flagged PORT_DISABLED (perhaps by an LLDD).

Quoted 3 lines

Hmmm... the plan is to disallow LLDD's take ports or devices offline from low level callbacks. They should just let upper layer know by returning failure code.

Quoted 9 lines

Long term that plan is fine, but you still have to deal with the existing API one way or another. ata_port_disable() is called directly by a bunch of Alan's PATA drivers, and by ata_piix and sata_mv.

Thus you would either need to keep the PORT_DISABLED checks or convert the drivers in question to a better API.

So just check those ata_port_disable() cases...

AFAICS, ata_piix doesn't call ata_port_disable() directly, but sata_mv() does, through mv_host_intr() -> mv_err_intr() ->mv_stop_and_reset() -> __mv_phy_reset(). I'll check the code path such that disabled ports are handled properly. Thanks for pointing out.

Signature · 1 line
tejun
#8 lore raw

Re: [PATCH 3/4] libata: make ata_set_mode() responsible for failure handling

Quoted 36 lines · Tejun Heo
Quoted 24 lines · Jeff Garzik
Quoted 21 lines · Tejun Heo
Quoted 4 lines · Jeff Garzik

I have the following concern with this patch (#3) however:

Quoted 9 lines

drivers/scsi/libata-core.c int ata_timing_compute(struct ata_device · line 1760

Quoted 6 lines

-static void ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev)
+static int ata_dev_set_mode(struct ata_port *ap, struct ata_device *dev) {

drivers/scsi/libata-core.c +0 -2
-	if (!ata_dev_present(dev) || (ap->flags & ATA_FLAG_PORT_DISABLED))
-		return;

I think you drop too many ATA_FLAG_PORT_DISABLED tests in this patch, leading the code to potentially miss a previously-flagged PORT_DISABLED (perhaps by an LLDD).

Hmmm... the plan is to disallow LLDD's take ports or devices offline

from low level callbacks. They should just let upper layer know by

Quoted 8 lines
Quoted 1 line

returning failure code.

Long term that plan is fine, but you still have to deal with the existing API one way or another. ata_port_disable() is called directly by a bunch of Alan's PATA drivers, and by ata_piix and sata_mv.

Thus you would either need to keep the PORT_DISABLED checks or convert the drivers in question to a better API.

So just check those ata_port_disable() cases...

Quoted 5 lines

AFAICS, ata_piix doesn't call ata_port_disable() directly, but sata_mv() does, through mv_host_intr() -> mv_err_intr() ->mv_stop_and_reset() -> __mv_phy_reset(). I'll check the code path such that disabled ports are handled properly. Thanks for pointing out.

Yeah, sorry, for ata_piix I was looking at vanilla linux-2.6.git rather than libata-dev.git#upstream... :)

Jeff

author
#9 lore raw

PATCH 4/4 libata: add @disable_on_err argument to ata_set_mode()

In reply to Tejun Heo

ata_set_mode() used to disable whole port on failure. This patch adds @disable_on_err which makes ata_set_mode() disable failing devices when non-zero, and simply return when zero. Due to the port-wide characteristic of ATA xfer mode configuration, ata_mode_set() is the final place to determine device offlining; thus, the @disable_on_err mechanism to tell it which action to take on failure.

Now port is disabled only if all devices on the port is disabled. This behavior change is intentional.

---

 drivers/scsi/libata-core.c |   61 +++++++++++++++++++++++++++-----------------
 1 files changed, 37 insertions(+), 24 deletions(-)

4eb4d225ccfc6b5401b338c92c142f30b761db2e

drivers/scsi/libata-core.c +37 -24
index 6826181..f620595 100644
@@ -63,7 +63,7 @@
 
 static unsigned int ata_dev_init_params(struct ata_port *ap,
 					struct ata_device *dev);
-static void ata_set_mode(struct ata_port *ap);
+static int ata_set_mode(struct ata_port *ap, int disable_on_err);
 static unsigned int ata_dev_set_xfermode(struct ata_port *ap,
 					 struct ata_device *dev);
 static void ata_dev_xfermask(struct ata_port *ap, struct ata_device *dev);
@@ -1449,16 +1449,16 @@ static int ata_bus_probe(struct ata_port
 		found = 1;
 	}
 
-	if (!found)
-		goto err_out_disable;
-
-	ata_set_mode(ap);
-	if (ap->flags & ATA_FLAG_PORT_DISABLED)
-		goto err_out_disable;
-
-	return 0;
+	/* configure transfer mode */
+	if (found) {
+		ata_set_mode(ap, 1);
+		for (i = 0; i < ATA_MAX_DEVICES; i++)
+			if (ata_dev_present(&ap->device[i]))
+				return 0;
+	}
 
-err_out_disable:
+	/* no device present, disable port */
+	ata_port_disable(ap);
 	ap->ops->port_disable(ap);
 	return -1;
 }
@@ -1788,7 +1788,7 @@ static int ata_dev_set_mode(struct ata_p
 	return 0;
 }
 
-static int ata_host_set_pio(struct ata_port *ap)
+static int ata_host_set_pio(struct ata_port *ap, int disable_on_err)
 {
 	int i;
 
@@ -1799,8 +1799,13 @@ static int ata_host_set_pio(struct ata_p
 			continue;
 
 		if (!dev->pio_mode) {
-			printk(KERN_WARNING "ata%u: no PIO support\n", ap->id);
-			return -1;
+			printk(KERN_WARNING "ata%u: dev %u no PIO support\n",
+			       ap->id, dev->devno);
+			if (disable_on_err) {
+				ata_dev_disable(ap, dev);
+				continue;
+			} else
+				return -EINVAL;
 		}
 
 		dev->xfer_mode = dev->pio_mode;
@@ -1832,13 +1837,19 @@ static void ata_host_set_dma(struct ata_
 /**
  *	ata_set_mode - Program timings and issue SET FEATURES - XFER
  *	@ap: port on which timings will be programmed
+ *	@disable_on_err: disable device on error
  *
- *	Set ATA device disk transfer mode (PIO3, UDMA6, etc.).
+ *	Set ATA device disk transfer mode (PIO3, UDMA6, etc.).  If
+ *	@disable_on_err is non-zero, devices which fail to configure
+ *	are taken offline and this function always succeeds.
  *
  *	LOCKING:
  *	PCI/etc. bus probe sem.
+ *
+ *	RETURNS:
+ *	0 on success, negative errno otherwise
  */
-static void ata_set_mode(struct ata_port *ap)
+static int ata_set_mode(struct ata_port *ap, int disable_on_err)
 {
 	int i, rc;
 
@@ -1861,9 +1872,9 @@ static void ata_set_mode(struct ata_port
 	}
 
 	/* step 2: always set host PIO timings */
-	rc = ata_host_set_pio(ap);
+	rc = ata_host_set_pio(ap, disable_on_err);
 	if (rc)
-		goto err_out;
+		return rc;
 
 	/* step 3: set host DMA timings */
 	ata_host_set_dma(ap);
@@ -1875,17 +1886,19 @@ static void ata_set_mode(struct ata_port
 		if (!ata_dev_present(dev))
 			continue;
 
-		if (ata_dev_set_mode(ap, dev))
-			goto err_out;
+		rc = ata_dev_set_mode(ap, dev);
+		if (rc) {
+			if (disable_on_err)
+				ata_dev_disable(ap, dev);
+			else
+				return rc;
+		}
 	}
 
 	if (ap->ops->post_set_mode)
 		ap->ops->post_set_mode(ap);
 
-	return;
-
-err_out:
-	ata_port_disable(ap);
+	return 0;
 }
 
 /**
@@ -4526,7 +4539,7 @@ int ata_device_resume(struct ata_port *a
 {
 	if (ap->flags & ATA_FLAG_SUSPENDED) {
 		ap->flags &= ~ATA_FLAG_SUSPENDED;
-		ata_set_mode(ap);
+		ata_set_mode(ap, 1);
 	}
 	if (!ata_dev_present(dev))
 		return 0;
Signature · 1 line
1.2.4