Subject: Re: kern/30194: unrecoverable wd(4) error after suspend/resume
To: None <gnats-bugs@NetBSD.org>
From: Manuel Bouyer <bouyer@antioche.eu.org>
List: netbsd-bugs
Date: 05/16/2005 20:02:05
--17pEHd4RhPHOinZp
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

> >Description:
> i am unable to resume from suspend with APM in the kernel.  when resuming, the
> system comes up but disk seems to be hung immediately (sometimes the disk
> activity led is constantly on), processes hang in vnlock (sometimes in
> getblk).  kernel prints error:
> 
>  atabus0: resuming...
>  atabus1: resuming...
>  wd0a: error writing fsbn 15042624 of 15042624-15042655 (wd0 bn 15042687; cn 14923 tn 4 sn 51), retrying
>  wd0: (aborted command)

Hi,
can you try the attached patch ? I've not been able to reproduce your problem,
but the atabus power hook needs to be smarter than it is actually.

-- 
Manuel Bouyer <bouyer@antioche.eu.org>
     NetBSD: 26 ans d'experience feront toujours la difference
--

--17pEHd4RhPHOinZp
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename=diff

Index: ata.c
===================================================================
RCS file: /cvsroot/src/sys/dev/ata/ata.c,v
retrieving revision 1.67
diff -u -r1.67 ata.c
--- ata.c	11 Apr 2005 04:24:54 -0000	1.67
+++ ata.c	16 May 2005 17:42:45 -0000
@@ -158,6 +158,7 @@
 
 	TAILQ_INIT(&chp->ch_queue->queue_xfer);
 	chp->ch_queue->queue_freeze = 0;
+	chp->ch_queue->queue_flags = 0;
 	chp->ch_queue->active_xfer = NULL;
 
 	chp->atabus = config_found(&chp->ch_atac->atac_dev, chp, atabusprint);
@@ -322,11 +323,7 @@
 	splx(s);
 
 	/* Configure the devices on the bus. */
-	if (sc->sc_sleeping == 1) {
-		printf("%s: resuming...\n", sc->sc_dev.dv_xname);
-		sc->sc_sleeping = 0;
-	} else
-		atabusconfig(sc);
+	atabusconfig(sc);
 
 	for (;;) {
 		s = splbio();
@@ -430,7 +427,6 @@
 	config_pending_incr();
 	kthread_create(atabus_create_thread, sc);
 
-	sc->sc_sleeping = 0;
 	sc->sc_powerhook = powerhook_establish(atabus_powerhook, sc);
 	if (sc->sc_powerhook == NULL)
 		printf("%s: WARNING: unable to establish power hook\n",
@@ -699,6 +695,22 @@
 }
 
 /*
+ * freeze the queue and wait for the controller to be idle. Caller has to
+ * unfreeze/restart the queue
+ */
+void
+ata_queue_idle(struct ata_queue *queue)
+{
+	int s = splbio();
+	queue->queue_freeze++;
+	while (queue->active_xfer != NULL) {
+		queue->queue_flags |= QF_IDLE_WAIT;
+		tsleep(&queue->queue_flags, PRIBIO, "qidl", 0);
+	}
+	splx(s);
+}
+
+/*
  * Add a command to the queue and start controller.
  *
  * MUST BE CALLED AT splbio()!
@@ -772,6 +784,10 @@
 		return; /* channel aleady active */
 	}
 	if (__predict_false(chp->ch_queue->queue_freeze > 0)) {
+		if (queue->queue_flags & QF_IDLE_WAIT) {
+			queue->queue_flags &= ~QF_IDLE_WAIT;
+			wakeup(&queue->queue_flags);
+		}
 		return; /* queue froozen */
 	}
 	/*
@@ -1422,17 +1438,17 @@
 	switch (why) {
 	case PWR_SOFTSUSPEND:
 	case PWR_SOFTSTANDBY:
-		sc->sc_sleeping = 1;
-		s = splbio();
-		chp->ch_flags = ATACH_SHUTDOWN;
-		splx(s);
-		wakeup(&chp->ch_thread);
-		while (chp->ch_thread != NULL)
-			(void) tsleep((void *)&chp->ch_flags, PRIBIO,
-			    "atadown", 0);
+		/* freeze the queue and wait for the controller to be idle */
+		ata_queue_idle(chp->ch_queue);
 		break;
 	case PWR_RESUME:
-		atabus_create_thread(sc);
+		printf("%s: resuming...\n", sc->sc_dev.dv_xname);
+		s = splbio();
+		KASSERT(chp->ch_queue->queue_freeze > 0);
+		/* unfreeze the queue and reset drives (to wake them up) */
+		chp->ch_queue->queue_freeze--;
+		ata_reset_channel(chp, AT_WAIT);
+		splx(s);
 		break;
 	case PWR_SUSPEND:
 	case PWR_STANDBY:
Index: atavar.h
===================================================================
RCS file: /cvsroot/src/sys/dev/ata/atavar.h,v
retrieving revision 1.67
diff -u -r1.67 atavar.h
--- atavar.h	27 Feb 2005 00:26:59 -0000	1.67
+++ atavar.h	16 May 2005 17:42:45 -0000
@@ -88,6 +88,8 @@
 	TAILQ_HEAD(, ata_xfer) queue_xfer; /* queue of pending commands */
 	int queue_freeze; /* freeze count for the queue */
 	struct ata_xfer *active_xfer; /* active command */
+	int queue_flags;	/* flags for this queue */
+#define QF_IDLE_WAIT   0x01    /* someone is wants the controller idle */
 };
 
 /* ATA bus instance state information. */
@@ -97,7 +99,6 @@
 	int sc_flags;
 #define ATABUSCF_OPEN	0x01
 	void *sc_powerhook;
-	int sc_sleeping;
 };
 
 /*
@@ -441,6 +442,7 @@
 void	ata_probe_caps(struct ata_drive_datas *);
 
 void	ata_dmaerr(struct ata_drive_datas *, int);
+void	ata_queue_idle(struct ata_queue *);
 #endif /* _KERNEL */
 
 #endif /* _DEV_ATA_ATAVAR_H_ */

--17pEHd4RhPHOinZp--