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>