Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 14 Apr 2011 07:49:46 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r220615 - head/sys/dev/mvs
Message-ID:  <201104140749.p3E7nk2u025136@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Thu Apr 14 07:49:45 2011
New Revision: 220615
URL: http://svn.freebsd.org/changeset/base/220615

Log:
  Refactor hard-reset implementation in mvs(4).
  
  Instead of spinning in a tight loop for up to 15 seconds, polling for device
  readiness while it spins up, return reset completion just after PHY reports
  "connect well" or 100ms connection timeout. If device was found, use callout
  for checking device readiness with 100ms period up to full 31 second timeout.
  
  This fixes system freeze for 5-10 seconds on drives hot plug-in.

Modified:
  head/sys/dev/mvs/mvs.c
  head/sys/dev/mvs/mvs.h

Modified: head/sys/dev/mvs/mvs.c
==============================================================================
--- head/sys/dev/mvs/mvs.c	Thu Apr 14 07:14:22 2011	(r220614)
+++ head/sys/dev/mvs/mvs.c	Thu Apr 14 07:49:45 2011	(r220615)
@@ -33,6 +33,7 @@ __FBSDID("$FreeBSD$");
 #include <sys/kernel.h>
 #include <sys/ata.h>
 #include <sys/bus.h>
+#include <sys/conf.h>
 #include <sys/endian.h>
 #include <sys/malloc.h>
 #include <sys/lock.h>
@@ -75,7 +76,7 @@ static int mvs_sata_phy_reset(device_t d
 static int mvs_wait(device_t dev, u_int s, u_int c, int t);
 static void mvs_tfd_read(device_t dev, union ccb *ccb);
 static void mvs_tfd_write(device_t dev, union ccb *ccb);
-static void mvs_legacy_intr(device_t dev);
+static void mvs_legacy_intr(device_t dev, int poll);
 static void mvs_crbq_intr(device_t dev);
 static void mvs_begin_transaction(device_t dev, union ccb *ccb);
 static void mvs_legacy_execute_transaction(struct mvs_slot *slot);
@@ -125,6 +126,7 @@ mvs_ch_attach(device_t dev)
 	    device_get_unit(dev), "pm_level", &ch->pm_level);
 	if (ch->pm_level > 3)
 		callout_init_mtx(&ch->pm_timer, &ch->mtx, 0);
+	callout_init_mtx(&ch->reset_timer, &ch->mtx, 0);
 	resource_int_value(device_get_name(dev),
 	    device_get_unit(dev), "sata_rev", &sata_rev);
 	for (i = 0; i < 16; i++) {
@@ -218,6 +220,11 @@ mvs_ch_detach(device_t dev)
 
 	mtx_lock(&ch->mtx);
 	xpt_async(AC_LOST_DEVICE, ch->path, NULL);
+	/* Forget about reset. */
+	if (ch->resetting) {
+		ch->resetting = 0;
+		xpt_release_simq(ch->sim, TRUE);
+	}
 	xpt_free_path(ch->path);
 	xpt_bus_deregister(cam_sim_path(ch->sim));
 	cam_sim_free(ch->sim, /*free_devq*/TRUE);
@@ -225,6 +232,7 @@ mvs_ch_detach(device_t dev)
 
 	if (ch->pm_level > 3)
 		callout_drain(&ch->pm_timer);
+	callout_drain(&ch->reset_timer);
 	bus_teardown_intr(dev, ch->r_irq, ch->ih);
 	bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq);
 
@@ -286,6 +294,12 @@ mvs_ch_suspend(device_t dev)
 	xpt_freeze_simq(ch->sim, 1);
 	while (ch->oslots)
 		msleep(ch, &ch->mtx, PRIBIO, "mvssusp", hz/100);
+	/* Forget about reset. */
+	if (ch->resetting) {
+		ch->resetting = 0;
+		callout_stop(&ch->reset_timer);
+		xpt_release_simq(ch->sim, TRUE);
+	}
 	mvs_ch_deinit(dev);
 	mtx_unlock(&ch->mtx);
 	return (0);
@@ -804,7 +818,7 @@ mvs_ch_intr(void *data)
 	}
 	/* Legacy mode device interrupt. */
 	if ((arg->cause & 2) && !edma)
-		mvs_legacy_intr(dev);
+		mvs_legacy_intr(dev, arg->cause & 4);
 }
 
 static uint8_t
@@ -823,7 +837,7 @@ mvs_getstatus(device_t dev, int clear)
 }
 
 static void
-mvs_legacy_intr(device_t dev)
+mvs_legacy_intr(device_t dev, int poll)
 {
 	struct mvs_channel *ch = device_get_softc(dev);
 	struct mvs_slot *slot = &ch->slot[0]; /* PIO is always in slot 0. */
@@ -841,6 +855,8 @@ mvs_legacy_intr(device_t dev)
 	port = ccb->ccb_h.target_id & 0x0f;
 	/* Wait a bit for late !BUSY status update. */
 	if (status & ATA_S_BUSY) {
+		if (poll)
+			return;
 		DELAY(100);
 		if ((status = mvs_getstatus(dev, 1)) & ATA_S_BUSY) {
 			DELAY(1000);
@@ -1317,7 +1333,7 @@ mvs_legacy_execute_transaction(struct mv
 			    DELAY(10);
 			    ccb->ataio.res.status = ATA_INB(ch->r_mem, ATA_STATUS);
 			} while (ccb->ataio.res.status & ATA_S_BUSY && timeout--);
-			mvs_legacy_intr(dev);
+			mvs_legacy_intr(dev, 1);
 			return;
 		}
 		ch->donecount = 0;
@@ -1910,11 +1926,13 @@ mvs_wait(device_t dev, u_int s, u_int c,
 	uint8_t st;
 
 	while (((st =  mvs_getstatus(dev, 0)) & (s | c)) != s) {
-		DELAY(1000);
-		if (timeout++ > t) {
-			device_printf(dev, "Wait status %02x\n", st);
+		if (timeout >= t) {
+			if (t != 0)
+				device_printf(dev, "Wait status %02x\n", st);
 			return (-1);
 		}
+		DELAY(1000);
+		timeout++;
 	} 
 	return (timeout);
 }
@@ -1937,6 +1955,35 @@ mvs_requeue_frozen(device_t dev)
 }
 
 static void
+mvs_reset_to(void *arg)
+{
+	device_t dev = arg;
+	struct mvs_channel *ch = device_get_softc(dev);
+	int t;
+
+	if (ch->resetting == 0)
+		return;
+	ch->resetting--;
+	if ((t = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, 0)) >= 0) {
+		if (bootverbose) {
+			device_printf(dev,
+			    "MVS reset: device ready after %dms\n",
+			    (310 - ch->resetting) * 100);
+		}
+		ch->resetting = 0;
+		xpt_release_simq(ch->sim, TRUE);
+		return;
+	}
+	if (ch->resetting == 0) {
+		device_printf(dev,
+		    "MVS reset: device not ready after 31000ms\n");
+		xpt_release_simq(ch->sim, TRUE);
+		return;
+	}
+	callout_schedule(&ch->reset_timer, hz / 10);
+}
+
+static void
 mvs_reset(device_t dev)
 {
 	struct mvs_channel *ch = device_get_softc(dev);
@@ -1945,6 +1992,12 @@ mvs_reset(device_t dev)
 	xpt_freeze_simq(ch->sim, 1);
 	if (bootverbose)
 		device_printf(dev, "MVS reset...\n");
+	/* Forget about previous reset. */
+	if (ch->resetting) {
+		ch->resetting = 0;
+		callout_stop(&ch->reset_timer);
+		xpt_release_simq(ch->sim, TRUE);
+	}
 	/* Requeue freezed command. */
 	mvs_requeue_frozen(dev);
 	/* Kill the engine and requeue all running commands. */
@@ -1969,6 +2022,7 @@ mvs_reset(device_t dev)
 	ch->eslots = 0;
 	ch->toslots = 0;
 	ch->fatalerr = 0;
+	ch->fake_busy = 0;
 	/* Tell the XPT about the event */
 	xpt_async(AC_BUS_RESET, ch->path, NULL);
 	ATA_OUTL(ch->r_mem, EDMA_IEM, 0);
@@ -1978,8 +2032,7 @@ mvs_reset(device_t dev)
 	/* Reset and reconnect PHY, */
 	if (!mvs_sata_phy_reset(dev)) {
 		if (bootverbose)
-			device_printf(dev,
-			    "MVS reset done: phy reset found no device\n");
+			device_printf(dev, "MVS reset: device not found\n");
 		ch->devices = 0;
 		ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff);
 		ATA_OUTL(ch->r_mem, EDMA_IEC, 0);
@@ -1987,18 +2040,26 @@ mvs_reset(device_t dev)
 		xpt_release_simq(ch->sim, TRUE);
 		return;
 	}
+	if (bootverbose)
+		device_printf(dev, "MVS reset: device found\n");
 	/* Wait for clearing busy status. */
-	if ((i = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ, 15000)) < 0)
-		device_printf(dev, "device is not ready\n");
-	else if (bootverbose)                                                        
-		device_printf(dev, "ready wait time=%dms\n", i);
+	if ((i = mvs_wait(dev, 0, ATA_S_BUSY | ATA_S_DRQ,
+	    dumping ? 31000 : 0)) < 0) {
+		if (dumping) {
+			device_printf(dev,
+			    "MVS reset: device not ready after 31000ms\n");
+		} else
+			ch->resetting = 310;
+	} else if (bootverbose)
+		device_printf(dev, "MVS reset: device ready after %dms\n", i);
 	ch->devices = 1;
 	ATA_OUTL(ch->r_mem, SATA_SE, 0xffffffff);
 	ATA_OUTL(ch->r_mem, EDMA_IEC, 0);
 	ATA_OUTL(ch->r_mem, EDMA_IEM, ~EDMA_IE_TRANSIENT);
-	if (bootverbose)
-		device_printf(dev, "MVS reset done: device found\n");
-	xpt_release_simq(ch->sim, TRUE);
+	if (ch->resetting)
+		callout_reset(&ch->reset_timer, hz / 10, mvs_reset_to, dev);
+	else
+		xpt_release_simq(ch->sim, TRUE);
 }
 
 static void
@@ -2307,7 +2368,12 @@ mvspoll(struct cam_sim *sim)
 	struct mvs_intr_arg arg;
 
 	arg.arg = ch->dev;
-	arg.cause = 2; /* XXX */
+	arg.cause = 2 | 4; /* XXX */
 	mvs_ch_intr(&arg);
+	if (ch->resetting != 0 &&
+	    (--ch->resetpolldiv <= 0 || !callout_pending(&ch->reset_timer))) {
+		ch->resetpolldiv = 1000;
+		mvs_reset_to(ch->dev);
+	}
 }
 

Modified: head/sys/dev/mvs/mvs.h
==============================================================================
--- head/sys/dev/mvs/mvs.h	Thu Apr 14 07:14:22 2011	(r220614)
+++ head/sys/dev/mvs/mvs.h	Thu Apr 14 07:49:45 2011	(r220615)
@@ -561,6 +561,8 @@ struct mvs_channel {
 	int			fatalerr;	/* Fatal error happend */
 	int			lastslot;	/* Last used slot */
 	int			taggedtarget;	/* Last tagged target */
+	int			resetting;	/* Hard-reset in progress. */
+	int			resetpolldiv;	/* Hard-reset poll divider. */
 	int			out_idx;	/* Next written CRQB */
 	int			in_idx;		/* Next read CRPB */
 	u_int			transfersize;	/* PIO transfer size */
@@ -569,6 +571,7 @@ struct mvs_channel {
 	u_int			fake_busy;	/* Fake busy bit after command submission */
 	union ccb		*frozen;	/* Frozen command */
 	struct callout		pm_timer;	/* Power management events */
+	struct callout		reset_timer;	/* Hard-reset timeout */
 
 	struct mvs_device	user[16];	/* User-specified settings */
 	struct mvs_device	curr[16];	/* Current settings */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201104140749.p3E7nk2u025136>