Allow polled commands to be queued, if ata_exec_xfer() is called in thread

context. For this introduce 3 new xfer tags:
- C_WAIT, equivalent of AT_WAIT
- C_WAITACT, when there is a thread waiting on this xfer to become the active
  one (that is, to be at the head of the queue)
- C_FREE, set by ata_free_xfer() when it can't free the xfer because it's
  still in use. The holder should then free the xfer ASAP.

If ata_exec_xfer() is called with (C_POLL | C_WAIT), and there is already
xfers to be processed, assert C_WAITACT and sleep.
atastart() checks for C_WAITACT, and wakeup the thread waiting for this xfer
to become active if set. atastart() won't process this xfer, it's the
responsability of the thread waked up to handle it.

Fix (the right way) kern/27421 by Martin Husemann.
This commit is contained in:
bouyer 2004-10-30 23:10:37 +00:00
parent 58d3abc94f
commit c31b572a47
3 changed files with 46 additions and 7 deletions

View File

@ -1,4 +1,4 @@
/* $NetBSD: ata.c,v 1.61 2004/10/28 20:11:41 bouyer Exp $ */
/* $NetBSD: ata.c,v 1.62 2004/10/30 23:10:37 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer. All rights reserved.
@ -30,7 +30,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.61 2004/10/28 20:11:41 bouyer Exp $");
__KERNEL_RCSID(0, "$NetBSD: ata.c,v 1.62 2004/10/30 23:10:37 bouyer Exp $");
#ifndef ATADEBUG
#define ATADEBUG
@ -702,6 +702,21 @@ ata_exec_xfer(struct ata_channel *chp, struct ata_xfer *xfer)
TAILQ_INSERT_TAIL(&chp->ch_queue->queue_xfer, xfer, c_xferchain);
ATADEBUG_PRINT(("atastart from ata_exec_xfer, flags 0x%x\n",
chp->ch_flags), DEBUG_XFERS);
/*
* if polling and can sleep, wait for the xfer to be at head of queue
*/
if ((xfer->c_flags & (C_POLL | C_WAIT)) == (C_POLL | C_WAIT)) {
while (chp->ch_queue->active_xfer != NULL ||
TAILQ_FIRST(&chp->ch_queue->queue_xfer) != xfer) {
xfer->c_flags |= C_WAITACT;
tsleep(xfer, PRIBIO, "ataact", 0);
xfer->c_flags &= ~C_WAITACT;
if (xfer->c_flags & C_FREE) {
ata_free_xfer(chp, xfer);
return;
}
}
}
atastart(chp);
}
@ -744,6 +759,17 @@ atastart(struct ata_channel *chp)
if (__predict_false(chp->ch_queue->queue_freeze > 0)) {
return; /* queue froozen */
}
/*
* if someone is waiting for the command to be active, wake it up
* and let it process the command
*/
if (xfer->c_flags & C_WAITACT) {
ATADEBUG_PRINT(("atastart: xfer %p channel %d drive %d "
"wait active\n", xfer, chp->ch_channel, xfer->c_drive),
DEBUG_XFERS);
wakeup(xfer);
return;
}
#ifdef DIAGNOSTIC
if ((chp->ch_flags & ATACH_IRQ_WAIT) != 0)
panic("atastart: channel waiting for irq");
@ -763,6 +789,7 @@ atastart(struct ata_channel *chp)
if (atac->atac_cap & ATAC_CAP_NOIRQ)
KASSERT(xfer->c_flags & C_POLL);
xfer->c_start(chp, xfer);
}
@ -788,6 +815,13 @@ ata_free_xfer(struct ata_channel *chp, struct ata_xfer *xfer)
struct atac_softc *atac = chp->ch_atac;
int s;
if (xfer->c_flags & C_WAITACT) {
/* Someone is waiting for this xfer, so we can't free now */
xfer->c_flags |= C_FREE;
wakeup(xfer);
return;
}
if (atac->atac_free_hw)
(*atac->atac_free_hw)(chp);
s = splbio();

View File

@ -1,4 +1,4 @@
/* $NetBSD: atavar.h,v 1.64 2004/08/20 23:26:54 thorpej Exp $ */
/* $NetBSD: atavar.h,v 1.65 2004/10/30 23:10:37 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001 Manuel Bouyer.
@ -70,11 +70,14 @@ struct ata_xfer {
void (*c_kill_xfer)(struct ata_channel *, struct ata_xfer *, int);
};
/* vlags in c_flags */
/* flags in c_flags */
#define C_ATAPI 0x0001 /* xfer is ATAPI request */
#define C_TIMEOU 0x0002 /* xfer processing timed out */
#define C_POLL 0x0004 /* command is polled */
#define C_DMA 0x0008 /* command uses DMA */
#define C_WAIT 0x0010 /* can use tsleep */
#define C_WAITACT 0x0020 /* wakeup when active */
#define C_FREE 0x0040 /* call ata_free_xfer() asap */
/* reasons for c_kill_xfer() */
#define KILL_GONE 1 /* device is gone */

View File

@ -1,4 +1,4 @@
/* $NetBSD: wdc.c,v 1.216 2004/10/30 17:17:21 bouyer Exp $ */
/* $NetBSD: wdc.c,v 1.217 2004/10/30 23:10:37 bouyer Exp $ */
/*
* Copyright (c) 1998, 2001, 2003 Manuel Bouyer. All rights reserved.
@ -70,7 +70,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.216 2004/10/30 17:17:21 bouyer Exp $");
__KERNEL_RCSID(0, "$NetBSD: wdc.c,v 1.217 2004/10/30 23:10:37 bouyer Exp $");
#ifndef ATADEBUG
#define ATADEBUG
@ -1274,6 +1274,8 @@ wdc_exec_command(struct ata_drive_datas *drvp, struct ata_command *ata_c)
ata_c->flags |= AT_POLL;
if (ata_c->flags & AT_POLL)
xfer->c_flags |= C_POLL;
if (ata_c->flags & AT_WAIT)
xfer->c_flags |= C_WAIT;
xfer->c_drive = drvp->drive;
xfer->c_databuf = ata_c->data;
xfer->c_bcount = ata_c->bcount;
@ -1657,7 +1659,7 @@ static void
__wdcerror(struct ata_channel *chp, char *msg)
{
struct atac_softc *atac = chp->ch_atac;
struct ata_xfer *xfer = TAILQ_FIRST(&chp->ch_queue->queue_xfer);
struct ata_xfer *xfer = chp->ch_queue->active_xfer;
if (xfer == NULL)
printf("%s:%d: %s\n", atac->atac_dev.dv_xname, chp->ch_channel,