Subject: Fwd: Re: port-i386/21531: ACPI enabled kernel enforces stressful shutdown of harddrive
To: None <tech-kern@netbsd.org>
From: Charles M. Hannum <abuse@spamalicious.com>
List: tech-kern
Date: 06/28/2004 21:21:03
--Boundary-00=_/uI4AfcFGJspr4K
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline



--Boundary-00=_/uI4AfcFGJspr4K
Content-Type: message/rfc822;
  name="forwarded message"
Content-Transfer-Encoding: 7bit
Content-Description: "Charles M. Hannum" <abuse@spamalicious.com>: Re: port-i386/21531: ACPI enabled kernel enforces stressful shutdown of harddrive
Content-Disposition: inline

From: "Charles M. Hannum" <abuse@spamalicious.com>
Organization: By Noon Software, Inc.
To: DHOYASHIKI Shinichi <clare@znet.or.jp>
Subject: Re: port-i386/21531: ACPI enabled kernel enforces stressful shutdown of harddrive
Date: Mon, 28 Jun 2004 20:53:29 +0000
User-Agent: KMail/1.6.1
Cc: gnats-bugs@netbsd.org
MIME-Version: 1.0
Content-Disposition: inline
Content-Type: Multipart/Mixed;
  boundary="Boundary-00=_JVI4ACY35gTsgDQ"
Message-Id: <200406282053.29585.abuse@spamalicious.com>


--Boundary-00=_JVI4ACY35gTsgDQ
Content-Type: text/plain;
  charset="us-ascii"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline
Subject: 

While looking at this, I noticed that we also try to issue a "flush cache" 
command to PCMCIA and CompactFlash cards that are currently powered down -- 
which is sort of guaranteed to not work.  The following patch addresses both 
of these problems.

This could probably be done better (e.g. by doing it from a shutdownhook and 
powerhook for the controller, and making PCMCIA and CardBus use these to do a 
graceful shutdown).

--Boundary-00=_JVI4ACY35gTsgDQ
Content-Type: text/x-diff;
  charset="us-ascii";
  name="ata.diff"
Content-Transfer-Encoding: 7bit
Content-Disposition: attachment;
	filename="ata.diff"

Index: ata/ata_wdc.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ata/ata_wdc.c,v
retrieving revision 1.57
diff -u -r1.57 ata_wdc.c
--- ata/ata_wdc.c	22 Jun 2004 19:20:56 -0000	1.57
+++ ata/ata_wdc.c	28 Jun 2004 20:46:54 -0000
@@ -128,6 +128,7 @@
 static int	wdc_ata_addref(struct ata_drive_datas *);
 static void	wdc_ata_delref(struct ata_drive_datas *);
 static void	wdc_ata_kill_pending(struct ata_drive_datas *);
+static int	wdc_ata_isenabled(struct ata_drive_datas *);
 
 const struct ata_bustype wdc_ata_bustype = {
 	SCSIPI_BUSTYPE_ATA,
@@ -138,6 +139,7 @@
 	wdc_ata_addref,
 	wdc_ata_delref,
 	wdc_ata_kill_pending,
+	wdc_ata_isenabled,
 };
 
 /*
@@ -828,3 +830,11 @@
 
 	wdc_delref(chp);
 }
+
+static int
+wdc_ata_isenabled(struct ata_drive_datas *drvp)
+{
+	struct wdc_channel *chp = drvp->chnl_softc;
+
+	return (wdc_isenabled(chp));
+}
Index: ata/atavar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ata/atavar.h,v
retrieving revision 1.43
diff -u -r1.43 atavar.h
--- ata/atavar.h	1 Jun 2004 19:32:30 -0000	1.43
+++ ata/atavar.h	28 Jun 2004 20:46:54 -0000
@@ -261,6 +261,7 @@
 	int	(*ata_addref)(struct ata_drive_datas *);
 	void	(*ata_delref)(struct ata_drive_datas *);
 	void	(*ata_killpending)(struct ata_drive_datas *);
+	int	(*ata_isenabled)(struct ata_drive_datas *);
 };
 
 /* bustype_type */	/* XXX XXX XXX */
Index: ata/wd.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ata/wd.c,v
retrieving revision 1.280
diff -u -r1.280 wd.c
--- ata/wd.c	22 Jun 2004 19:20:56 -0000	1.280
+++ ata/wd.c	28 Jun 2004 20:46:55 -0000
@@ -188,6 +188,7 @@
 void  wdrestart(void *);
 void  wddone(void *);
 int   wd_get_params(struct wd_softc *, u_int8_t, struct ataparams *);
+int   wd_standby(struct wd_softc *, int);
 int   wd_flushcache(struct wd_softc *, int);
 void  wd_shutdown(void *);
 
@@ -947,7 +948,6 @@
 
 	if (wd->sc_dk.dk_openmask == 0) {
 		wd_flushcache(wd, AT_WAIT);
-		/* XXXX Must wait for I/O to complete! */
 
 		if (! (wd->sc_flags & WDF_KLABEL))
 			wd->sc_flags &= ~WDF_LOADED;
@@ -1595,6 +1595,8 @@
 	return 0;
 }
 
+const char at_errbits[] = "\20\10ERROR\11TIMEOU\12DF";
+
 int
 wd_setcache(struct wd_softc *wd, int bits)
 {
@@ -1629,12 +1631,45 @@
 		return EIO;
 	}
 	if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
-		printf("%s: wd_setcache command error 0x%x\n",
-		    wd->sc_dev.dv_xname, wdc_c.flags);
+		char sbuf[sizeof(at_errbits) + 64];
+		bitmask_snprintf(wdc_c.flags, at_errbits, sbuf, sizeof(sbuf));
+		printf("%s: wd_setcache: status=%s\n", wd->sc_dev.dv_xname,
+		    sbuf);
+		return EIO;
+	}
+	return 0;
+}
+
+int
+wd_standby(struct wd_softc *wd, int flags)
+{
+	struct wdc_command wdc_c;
+
+printf("%s: wd_standby entered\n", wd->sc_dev.dv_xname);
+	memset(&wdc_c, 0, sizeof(struct wdc_command));
+	wdc_c.r_command = WDCC_STANDBY_IMMED;
+	wdc_c.r_st_bmask = WDCS_DRDY;
+	wdc_c.r_st_pmask = WDCS_DRDY;
+	wdc_c.flags = flags;
+	wdc_c.timeout = 30000; /* 30s timeout */
+	if (wd->atabus->ata_exec_command(wd->drvp, &wdc_c) != WDC_COMPLETE) {
+		printf("%s: standby immediate command didn't complete\n",
+		    wd->sc_dev.dv_xname);
+		return EIO;
+	}
+printf("%s: wd_standby finished, status=%x\n", wd->sc_dev.dv_xname,
+    wdc_c.flags);
+	if (wdc_c.flags & AT_ERROR) {
+		if (wdc_c.r_error == WDCE_ABRT) /* command not supported */
+			return ENODEV;
+	}
+	if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
+		char sbuf[sizeof(at_errbits) + 64];
+		bitmask_snprintf(wdc_c.flags, at_errbits, sbuf, sizeof(sbuf));
+		printf("%s: wd_standby: status=%s\n", wd->sc_dev.dv_xname,
+		    sbuf);
 		return EIO;
 	}
-	if (wdc_c.flags & ERR_NODEV)
-		return ENODEV;
 	return 0;
 }
 
@@ -1643,6 +1678,7 @@
 {
 	struct wdc_command wdc_c;
 
+printf("%s: wd_flushcache entered\n", wd->sc_dev.dv_xname);
 	if (wd->drvp->ata_vers < 4) /* WDCC_FLUSHCACHE is here since ATA-4 */
 		return ENODEV;
 	memset(&wdc_c, 0, sizeof(struct wdc_command));
@@ -1660,23 +1696,17 @@
 		    wd->sc_dev.dv_xname);
 		return EIO;
 	}
-	if (wdc_c.flags & ERR_NODEV)
-		return ENODEV;
-	if (wdc_c.flags & AT_TIMEOU) {
-		printf("%s: flush cache command timeout\n",
-		    wd->sc_dev.dv_xname);
-		return EIO;
-	}
+printf("%s: wd_flushcache finished, status=%x\n", wd->sc_dev.dv_xname,
+    wdc_c.flags);
 	if (wdc_c.flags & AT_ERROR) {
 		if (wdc_c.r_error == WDCE_ABRT) /* command not supported */
 			return ENODEV;
-		printf("%s: flush cache command: error 0x%x\n",
-		    wd->sc_dev.dv_xname, wdc_c.r_error);
-		return EIO;
 	}
-	if (wdc_c.flags & AT_DF) {
-		printf("%s: flush cache command: drive fault\n",
-		    wd->sc_dev.dv_xname);
+	if (wdc_c.flags & (AT_ERROR | AT_TIMEOU | AT_DF)) {
+		char sbuf[sizeof(at_errbits) + 64];
+		bitmask_snprintf(wdc_c.flags, at_errbits, sbuf, sizeof(sbuf));
+		printf("%s: wd_flushcache: status=%s\n", wd->sc_dev.dv_xname,
+		    sbuf);
 		return EIO;
 	}
 	return 0;
@@ -1686,7 +1716,11 @@
 wd_shutdown(void *arg)
 {
 	struct wd_softc *wd = arg;
+
+	if (!wd->atabus->ata_isenabled(wd->drvp))
+		return;
 	wd_flushcache(wd, AT_POLL);
+	wd_standby(wd, AT_POLL);
 }
 
 /*
Index: ic/wdc.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wdc.c,v
retrieving revision 1.180
diff -u -r1.180 wdc.c
--- ic/wdc.c	1 Jun 2004 19:32:30 -0000	1.180
+++ ic/wdc.c	28 Jun 2004 20:46:55 -0000
@@ -2060,6 +2060,19 @@
 	splx(s);
 }
 
+int
+wdc_isenabled(struct wdc_channel *chp)
+{
+	struct wdc_softc *wdc = chp->ch_wdc;
+	struct scsipi_adapter *adapt = &wdc->sc_atapi_adapter._generic;
+	int s, rv;
+
+	s = splbio();
+	rv = (adapt->adapt_enable == NULL || adapt->adapt_refcnt != 0);
+	splx(s);
+	return (rv);
+}
+
 void
 wdc_print_modes(struct wdc_channel *chp)
 {
Index: ic/wdcvar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ic/wdcvar.h,v
retrieving revision 1.57
diff -u -r1.57 wdcvar.h
--- ic/wdcvar.h	25 May 2004 20:42:41 -0000	1.57
+++ ic/wdcvar.h	28 Jun 2004 20:46:55 -0000
@@ -219,6 +219,7 @@
 int	wdc_addref(struct wdc_channel *);
 void	wdc_delref(struct wdc_channel *);
 void	wdc_kill_pending(struct wdc_channel *);
+int	wdc_isenabled(struct wdc_channel *);
 
 void	wdc_print_modes (struct wdc_channel *);
 void	wdc_probe_caps(struct ata_drive_datas*);
Index: usb/umass_isdata.c
===================================================================
RCS file: /cvsroot/src/sys/dev/usb/umass_isdata.c,v
retrieving revision 1.9
diff -u -r1.9 umass_isdata.c
--- usb/umass_isdata.c	30 Dec 2003 19:30:39 -0000	1.9
+++ usb/umass_isdata.c	28 Jun 2004 20:46:56 -0000
@@ -119,6 +119,7 @@
 int  uisdata_addref(struct ata_drive_datas *);
 void uisdata_delref(struct ata_drive_datas *);
 void uisdata_kill_pending(struct ata_drive_datas *);
+int  uisdata_isenabled(struct ata_drive_datas *);
 
 void uisdata_bio_cb(struct umass_softc *, void *, int, int);
 void uisdata_exec_cb(struct umass_softc *, void *, int, int);
@@ -133,6 +134,7 @@
 	uisdata_addref,
 	uisdata_delref,
 	uisdata_kill_pending,
+	uisdata_isenabled,
 };
 
 struct ata_cmd {
@@ -478,6 +480,14 @@
 	/* Nothing to do */
 }
 
+int
+uisdata_isenabled(struct ata_drive_datas *drv)
+{
+	DPRINTF(("%s\n", __func__));
+	/* Nothing to do */
+	return (1);
+}
+
 void
 uisdata_kill_pending(struct ata_drive_datas *drv)
 {

--Boundary-00=_JVI4ACY35gTsgDQ--

--Boundary-00=_/uI4AfcFGJspr4K--