Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/dev/ata/ata_subr.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/dev/ata/ata_subr.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.4 retrieving revision 1.4.6.2 diff -u -p -r1.4 -r1.4.6.2 --- src/sys/dev/ata/ata_subr.c 2017/10/20 07:06:07 1.4 +++ src/sys/dev/ata/ata_subr.c 2020/04/08 14:08:03 1.4.6.2 @@ -1,4 +1,4 @@ -/* $NetBSD: ata_subr.c,v 1.4 2017/10/20 07:06:07 jdolecek Exp $ */ +/* $NetBSD: ata_subr.c,v 1.4.6.2 2020/04/08 14:08:03 martin Exp $ */ /* * Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved. @@ -25,14 +25,13 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: ata_subr.c,v 1.4 2017/10/20 07:06:07 jdolecek Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ata_subr.c,v 1.4.6.2 2020/04/08 14:08:03 martin Exp $"); #include "opt_ata.h" #include #include #include -#include #include #include #include @@ -66,11 +65,11 @@ extern int atadebug_mask; #define ATADEBUG_PRINT(args, level) #endif -void +static void ata_queue_reset(struct ata_queue *chq) { /* make sure that we can use polled commands */ - TAILQ_INIT(&chq->queue_xfer); + SIMPLEQ_INIT(&chq->queue_xfer); TAILQ_INIT(&chq->active_xfers); chq->queue_freeze = 0; chq->queue_active = 0; @@ -158,30 +157,6 @@ ata_queue_drive_active_xfer(struct ata_c return xfer; } -static void -ata_xfer_init(struct ata_xfer *xfer, uint8_t slot) -{ - memset(xfer, 0, sizeof(*xfer)); - - xfer->c_slot = slot; - - cv_init(&xfer->c_active, "ataact"); - cv_init(&xfer->c_finish, "atafin"); - callout_init(&xfer->c_timo_callout, 0); /* XXX MPSAFE */ - callout_init(&xfer->c_retry_callout, 0); /* XXX MPSAFE */ -} - -static void -ata_xfer_destroy(struct ata_xfer *xfer) -{ - callout_halt(&xfer->c_timo_callout, NULL); /* XXX MPSAFE */ - callout_destroy(&xfer->c_timo_callout); - callout_halt(&xfer->c_retry_callout, NULL); /* XXX MPSAFE */ - callout_destroy(&xfer->c_retry_callout); - cv_destroy(&xfer->c_active); - cv_destroy(&xfer->c_finish); -} - struct ata_queue * ata_queue_alloc(uint8_t openings) { @@ -191,18 +166,16 @@ ata_queue_alloc(uint8_t openings) if (openings > ATA_MAX_OPENINGS) openings = ATA_MAX_OPENINGS; - struct ata_queue *chq = malloc(offsetof(struct ata_queue, queue_xfers[openings]), - M_DEVBUF, M_WAITOK | M_ZERO); + struct ata_queue *chq = kmem_zalloc(sizeof(*chq), KM_SLEEP); chq->queue_openings = openings; ata_queue_reset(chq); - cv_init(&chq->queue_busy, "ataqbusy"); cv_init(&chq->queue_drain, "atdrn"); cv_init(&chq->queue_idle, "qidl"); - for (uint8_t i = 0; i < openings; i++) - ata_xfer_init(&chq->queue_xfers[i], i); + cv_init(&chq->c_active, "ataact"); + cv_init(&chq->c_cmd_finish, "atafin"); return chq; } @@ -210,14 +183,13 @@ ata_queue_alloc(uint8_t openings) void ata_queue_free(struct ata_queue *chq) { - for (uint8_t i = 0; i < chq->queue_openings; i++) - ata_xfer_destroy(&chq->queue_xfers[i]); - - cv_destroy(&chq->queue_busy); cv_destroy(&chq->queue_drain); cv_destroy(&chq->queue_idle); - free(chq, M_DEVBUF); + cv_destroy(&chq->c_active); + cv_destroy(&chq->c_cmd_finish); + + kmem_free(chq, sizeof(*chq)); } void @@ -226,6 +198,8 @@ ata_channel_init(struct ata_channel *chp mutex_init(&chp->ch_lock, MUTEX_DEFAULT, IPL_BIO); cv_init(&chp->ch_thr_idle, "atath"); + callout_init(&chp->c_timo_callout, 0); /* XXX MPSAFE */ + /* Optionally setup the queue, too */ if (chp->ch_queue == NULL) { chp->ch_queue = ata_queue_alloc(1); @@ -240,199 +214,169 @@ ata_channel_destroy(struct ata_channel * chp->ch_queue = NULL; } + mutex_enter(&chp->ch_lock); + callout_halt(&chp->c_timo_callout, &chp->ch_lock); + callout_destroy(&chp->c_timo_callout); + mutex_exit(&chp->ch_lock); + mutex_destroy(&chp->ch_lock); cv_destroy(&chp->ch_thr_idle); } -/* - * Does it's own locking, does not require splbio(). - * flags - whether to block waiting for free xfer - * openings - limit of openings supported by device, <= 0 means tag not - * relevant, and any available xfer can be returned - */ -struct ata_xfer * -ata_get_xfer_ext(struct ata_channel *chp, int flags, uint8_t openings) +void +ata_timeout(void *v) { + struct ata_channel *chp = v; struct ata_queue *chq = chp->ch_queue; - struct ata_xfer *xfer = NULL; - uint32_t avail, slot, mask; - int error; + struct ata_xfer *xfer, *nxfer; + int s; - ATADEBUG_PRINT(("%s: channel %d fl 0x%x op %d qavail 0x%x qact %d", - __func__, chp->ch_channel, flags, openings, - chq->queue_xfers_avail, chq->queue_active), - DEBUG_XFERS); + s = splbio(); /* XXX MPSAFE */ - ata_channel_lock(chp); + callout_ack(&chp->c_timo_callout); - /* - * When openings is just 1, can't reserve anything for - * recovery. KASSERT() here is to catch code which naively - * relies on C_RECOVERY to work under this condition. - */ - KASSERT((flags & C_RECOVERY) == 0 || chq->queue_openings > 1); - - if (flags & C_RECOVERY) { - mask = UINT32_MAX; - } else { - if (openings <= 0 || openings > chq->queue_openings) - openings = chq->queue_openings; - - if (openings > 1) { - mask = __BIT(openings - 1) - 1; - } else { - mask = UINT32_MAX; - } + if (chp->ch_flags & ATACH_RECOVERING) { + /* Do nothing, recovery will requeue the xfers */ + return; } -retry: - avail = ffs32(chq->queue_xfers_avail & mask); - if (avail == 0) { - /* - * Catch code which tries to get another recovery xfer while - * already holding one (wrong recursion). - */ - KASSERTMSG((flags & C_RECOVERY) == 0, - "recovery xfer busy openings %d mask %x avail %x", - openings, mask, chq->queue_xfers_avail); - - if (flags & C_WAIT) { - chq->queue_flags |= QF_NEED_XFER; - error = cv_wait_sig(&chq->queue_busy, &chp->ch_lock); - if (error == 0) - goto retry; + /* + * If there is a timeout, means the last enqueued command + * timed out, and thus all commands timed out. + * XXX locking + */ + TAILQ_FOREACH_SAFE(xfer, &chq->active_xfers, c_activechain, nxfer) { + ATADEBUG_PRINT(("%s: slot %d\n", __func__, xfer->c_slot), + DEBUG_FUNCS|DEBUG_XFERS); + + if (ata_timo_xfer_check(xfer)) { + /* Already logged */ + continue; } - goto out; + /* Mark as timed out. Do not print anything, wd(4) will. */ + xfer->c_flags |= C_TIMEOU; + xfer->ops->c_intr(xfer->c_chp, xfer, 0); } - slot = avail - 1; - xfer = &chq->queue_xfers[slot]; - chq->queue_xfers_avail &= ~__BIT(slot); - - KASSERT((chq->active_xfers_used & __BIT(slot)) == 0); + splx(s); +} - /* zero everything after the callout member */ - memset(&xfer->c_startzero, 0, - sizeof(struct ata_xfer) - offsetof(struct ata_xfer, c_startzero)); +void +ata_channel_lock(struct ata_channel *chp) +{ + mutex_enter(&chp->ch_lock); +} -out: - ata_channel_unlock(chp); +void +ata_channel_unlock(struct ata_channel *chp) +{ + mutex_exit(&chp->ch_lock); +} - ATADEBUG_PRINT((" xfer %p\n", xfer), DEBUG_XFERS); - return xfer; +void +ata_channel_lock_owned(struct ata_channel *chp) +{ + KASSERT(mutex_owned(&chp->ch_lock)); } -/* - * ata_deactivate_xfer() must be always called prior to ata_free_xfer() - */ +#ifdef ATADEBUG void -ata_free_xfer(struct ata_channel *chp, struct ata_xfer *xfer) +atachannel_debug(struct ata_channel *chp) { struct ata_queue *chq = chp->ch_queue; - ata_channel_lock(chp); + printf(" ch %s flags 0x%x ndrives %d\n", + device_xname(chp->atabus), chp->ch_flags, chp->ch_ndrives); + printf(" que: flags 0x%x avail 0x%x used 0x%x\n", + chq->queue_flags, chq->queue_xfers_avail, chq->active_xfers_used); + printf(" act %d freez %d open %u\n", + chq->queue_active, chq->queue_freeze, chq->queue_openings); +} +#endif /* ATADEBUG */ + +bool +ata_queue_alloc_slot(struct ata_channel *chp, uint8_t *c_slot, + uint8_t drv_openings) +{ + struct ata_queue *chq = chp->ch_queue; + uint32_t avail, mask; - if (xfer->c_flags & (C_WAITACT|C_WAITTIMO)) { - /* Someone is waiting for this xfer, so we can't free now */ - xfer->c_flags |= C_FREE; - cv_signal(&xfer->c_active); - goto out; - } + KASSERT(mutex_owned(&chp->ch_lock)); + KASSERT(chq->queue_active < chq->queue_openings); -#if NATA_PIOBM /* XXX wdc dependent code */ - if (xfer->c_flags & C_PIOBM) { - struct wdc_softc *wdc = CHAN_TO_WDC(chp); - - /* finish the busmastering PIO */ - (*wdc->piobm_done)(wdc->dma_arg, - chp->ch_channel, xfer->c_drive); - chp->ch_flags &= ~(ATACH_DMA_WAIT | ATACH_PIOBM_WAIT | ATACH_IRQ_WAIT); - } -#endif + ATADEBUG_PRINT(("%s: channel %d qavail 0x%x qact %d", + __func__, chp->ch_channel, + chq->queue_xfers_avail, chq->queue_active), + DEBUG_XFERS); - if (chp->ch_atac->atac_free_hw) - chp->ch_atac->atac_free_hw(chp); - - KASSERT((chq->active_xfers_used & __BIT(xfer->c_slot)) == 0); - KASSERT((chq->queue_xfers_avail & __BIT(xfer->c_slot)) == 0); - chq->queue_xfers_avail |= __BIT(xfer->c_slot); - -out: - if (chq->queue_flags & QF_NEED_XFER) { - chq->queue_flags &= ~QF_NEED_XFER; - cv_broadcast(&chq->queue_busy); - } + mask = __BIT(MIN(chq->queue_openings, drv_openings)) - 1; - ata_channel_unlock(chp); + avail = ffs32(chq->queue_xfers_avail & mask); + if (avail == 0) + return false; - ATADEBUG_PRINT(("%s: channel %d xfer %p qavail 0x%x qact %d\n", - __func__, chp->ch_channel, xfer, chq->queue_xfers_avail, - chq->queue_active), - DEBUG_XFERS); + KASSERT(avail > 0); + KASSERT(avail <= drv_openings); + + *c_slot = avail - 1; + chq->queue_xfers_avail &= ~__BIT(*c_slot); + + KASSERT((chq->active_xfers_used & __BIT(*c_slot)) == 0); + return true; } -/* - * Must be called without any locks, i.e. with both drive and channel locks - * released. - */ -void -ata_channel_start(struct ata_channel *chp, int drive) -{ - int i, s; - struct ata_drive_datas *drvp; - - s = splbio(); - - KASSERT(chp->ch_ndrives > 0); - -#define ATA_DRIVE_START(chp, drive) \ - do { \ - KASSERT(drive < chp->ch_ndrives); \ - drvp = &chp->ch_drive[drive]; \ - \ - if (drvp->drive_type != ATA_DRIVET_ATA && \ - drvp->drive_type != ATA_DRIVET_ATAPI && \ - drvp->drive_type != ATA_DRIVET_OLD) \ - continue; \ - \ - if (drvp->drv_start != NULL) \ - (*drvp->drv_start)(drvp->drv_softc); \ - } while (0) +void +ata_queue_free_slot(struct ata_channel *chp, uint8_t c_slot) +{ + struct ata_queue *chq = chp->ch_queue; - /* - * Process drives in round robin fashion starting with next one after - * the one which finished transfer. Thus no single drive would - * completely starve other drives on same channel. - * This loop processes all but the current drive, so won't do anything - * if there is only one drive in channel. - */ - for (i = (drive + 1) % chp->ch_ndrives; i != drive; - i = (i + 1) % chp->ch_ndrives) { - ATA_DRIVE_START(chp, i); - } + KASSERT(mutex_owned(&chp->ch_lock)); - /* Now try to kick off xfers on the current drive */ - ATA_DRIVE_START(chp, drive); + KASSERT((chq->active_xfers_used & __BIT(c_slot)) == 0); + KASSERT((chq->queue_xfers_avail & __BIT(c_slot)) == 0); - splx(s); -#undef ATA_DRIVE_START + chq->queue_xfers_avail |= __BIT(c_slot); } void -ata_channel_lock(struct ata_channel *chp) +ata_queue_hold(struct ata_channel *chp) { - mutex_enter(&chp->ch_lock); + struct ata_queue *chq = chp->ch_queue; + + KASSERT(mutex_owned(&chp->ch_lock)); + + chq->queue_hold |= chq->active_xfers_used; + chq->active_xfers_used = 0; } void -ata_channel_unlock(struct ata_channel *chp) +ata_queue_unhold(struct ata_channel *chp) { - mutex_exit(&chp->ch_lock); + struct ata_queue *chq = chp->ch_queue; + + KASSERT(mutex_owned(&chp->ch_lock)); + + chq->active_xfers_used |= chq->queue_hold; + chq->queue_hold = 0; } -void -ata_channel_lock_owned(struct ata_channel *chp) +/* + * Must be called with interrupts blocked. + */ +uint32_t +ata_queue_active(struct ata_channel *chp) { - KASSERT(mutex_owned(&chp->ch_lock)); + struct ata_queue *chq = chp->ch_queue; + + if (chp->ch_flags & ATACH_DETACHED) + return 0; + + return chq->active_xfers_used; +} + +uint8_t +ata_queue_openings(struct ata_channel *chp) +{ + return chp->ch_queue->queue_openings; }