1
0
mirror of https://git.FreeBSD.org/src.git synced 2024-12-13 10:02:38 +00:00

Disconnect and tagged queueing now really work. Also fix a bug that's

causing problems to slow devices.
This commit is contained in:
Luoqi Chen 1999-12-04 22:15:02 +00:00
parent 544c9788f5
commit 6f59da2daa
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=54136
3 changed files with 115 additions and 101 deletions

View File

@ -56,6 +56,7 @@ static void aic_start __P((struct aic_softc *aic));
static void aic_select __P((struct aic_softc *aic));
static void aic_selected __P((struct aic_softc *aic));
static void aic_reselected __P((struct aic_softc *aic));
static void aic_reconnect __P((struct aic_softc *aic, int tag));
static void aic_cmd __P((struct aic_softc *aic));
static void aic_msgin __P((struct aic_softc *aic));
static void aic_handle_msgin __P((struct aic_softc *aic));
@ -351,6 +352,8 @@ aic_start(struct aic_softc *aic)
}
}
CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_start: idle\n"));
aic_outb(aic, SIMODE0, ENSELDI);
aic_outb(aic, SIMODE1, ENSCSIRST);
aic_outb(aic, SCSISEQ, ENRESELI);
@ -404,14 +407,12 @@ aic_selected(struct aic_softc *aic)
if ((ti->flags & TINFO_TAG_ENB) != 0 &&
(ccb->ccb_h.flags & CAM_TAG_ACTION_VALID) != 0)
aic->msg_outq |= AIC_MSG_TAG_Q;
else
ti->lubusy |= 1 << scb->lun;
if ((ti->flags & TINFO_SDTR_NEGO) != 0)
aic->msg_outq |= AIC_MSG_SDTR;
}
/* mark target/lun busy only for untagged operations */
if ((aic->msg_outq & AIC_MSG_TAG_Q) == 0)
ti->lubusy |= 1 << scb->lun;
aic_outb(aic, CLRSINT0, CLRSELDO);
aic_outb(aic, CLRSINT1, CLRBUSFREE);
aic_outb(aic, SCSISEQ, ENAUTOATNP);
@ -428,7 +429,6 @@ static void
aic_reselected(struct aic_softc *aic)
{
u_int8_t selid;
struct aic_tinfo *ti;
CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reselected\n"));
@ -442,9 +442,10 @@ aic_reselected(struct aic_softc *aic)
aic->nexus = NULL;
}
selid = aic_inb(aic, SELID) & SCSI_ID_MASK;
selid = aic_inb(aic, SELID) & ~(1 << aic->initiator);
if (selid & (selid - 1)) {
/* this should never have happened */
printf("aic_reselected: invalid selid %x\n", selid);
aic_reset(aic, /*initiate_reset*/TRUE);
return;
}
@ -452,14 +453,13 @@ aic_reselected(struct aic_softc *aic)
aic->state = AIC_RESELECTED;
aic->target = ffs(selid) - 1;
aic->lun = -1;
ti = &aic->tinfo[aic->target];
aic_outb(aic, CLRSINT0, CLRSELDI);
aic_outb(aic, CLRSINT1, CLRBUSFREE);
aic_outb(aic, SIMODE0, 0);
aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
aic_outb(aic, SCSISEQ, ENAUTOATNP);
aic_outb(aic, SCSIRATE, ti->scsirate);
aic_outb(aic, SCSIRATE, aic->tinfo[aic->target].scsirate);
}
/*
@ -482,13 +482,46 @@ aic_sched_msgout(struct aic_softc *aic, u_int8_t msg)
static __inline int
aic_spiordy(struct aic_softc *aic)
{
int spincount = 100;
u_int8_t spiordy = 0;
while (spincount-- && !(aic_inb(aic, DMASTAT) & INTSTAT) &&
!(spiordy = aic_inb(aic, SSTAT0) & SPIORDY))
while (!(aic_inb(aic, DMASTAT) & INTSTAT) &&
!(aic_inb(aic, SSTAT0) & SPIORDY))
;
return (spiordy);
return !(aic_inb(aic, DMASTAT) & INTSTAT);
}
/*
* Reestablish a disconnected nexus.
*/
void
aic_reconnect(struct aic_softc *aic, int tag)
{
struct aic_scb *scb;
struct ccb_hdr *ccb_h;
CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_reconnect\n"));
/* Find the nexus */
TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
if (scb->target == aic->target && scb->lun == aic->lun &&
(tag == -1 || scb->tag == tag))
break;
}
/* ABORT if nothing is found */
if (!ccb_h) {
if (tag == -1)
aic_sched_msgout(aic, MSG_ABORT);
else
aic_sched_msgout(aic, MSG_ABORT_TAG);
xpt_async(AC_UNSOL_RESEL, aic->path, NULL);
return;
}
/* Reestablish the nexus */
TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
aic->nexus = scb;
scb->flags &= ~SCB_DISCONNECTED;
aic->state = AIC_HASNEXUS;
}
/*
@ -544,7 +577,7 @@ aic_msgin(struct aic_softc *aic)
aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
}
msglen += 2;
} else if (aic->msg_buf[0] & 0x20)
} else if (aic->msg_buf[0] >= 0x20 && aic->msg_buf[0] <= 0x2f)
msglen = 2;
else
msglen = 1;
@ -576,51 +609,24 @@ aic_handle_msgin(struct aic_softc *aic)
struct ccb_trans_settings neg;
if (aic->state == AIC_RESELECTED) {
int tag;
ti = &aic->tinfo[aic->target];
/*
* We expect to see an IDENTIFY message, possibly followed
* by a SIMPLE_Q_TAG message.
*/
if (MSG_ISIDENTIFY(aic->msg_buf[0])) {
aic->lun = aic->msg_buf[0] & MSG_IDENTIFY_LUNMASK;
if (ti->flags & TINFO_TAG_ENB)
return;
else
tag = -1;
} else if (aic->msg_buf[0] != MSG_SIMPLE_Q_TAG) {
tag = aic->msg_buf[1];
} else {
if (!MSG_ISIDENTIFY(aic->msg_buf[0])) {
aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
return;
}
aic->lun = aic->msg_buf[0] & MSG_IDENTIFY_LUNMASK;
if (aic->tinfo[aic->target].lubusy & (1 << aic->lun))
aic_reconnect(aic, -1);
else
aic->state = AIC_RECONNECTING;
return;
}
/* Find the nexus */
TAILQ_FOREACH(ccb_h, &aic->nexus_ccbs, sim_links.tqe) {
scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
if (ccb_h->target_id == aic->target &&
ccb_h->target_lun == aic->lun &&
(tag == -1 || scb->tag == tag))
break;
}
/* ABORT if nothing is found */
if (!ccb_h) {
if (tag == -1)
aic_sched_msgout(aic, MSG_ABORT);
else
aic_sched_msgout(aic, MSG_ABORT_TAG);
xpt_async(AC_UNSOL_RESEL, ccb_h->path, NULL);
if (aic->state == AIC_RECONNECTING) {
if (aic->msg_buf[0] != MSG_SIMPLE_Q_TAG) {
aic_sched_msgout(aic, MSG_MESSAGE_REJECT);
return;
}
/* Reestablish the nexus */
TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
aic->nexus = scb;
scb->flags &= ~SCB_DISCONNECTED;
aic->state = AIC_HASNEXUS;
CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("reconnected\n"));
aic_reconnect(aic, aic->msg_buf[1]);
return;
}
@ -630,7 +636,6 @@ aic_handle_msgin(struct aic_softc *aic)
scb = aic->nexus;
ccb_h = &scb->ccb->ccb_h;
csio = &scb->ccb->csio;
ccb_h->status &= ~CAM_STATUS_MASK;
if ((scb->flags & SCB_SENSE) != 0) {
/* auto REQUEST SENSE command */
scb->flags &= ~SCB_SENSE;
@ -704,11 +709,11 @@ aic_handle_msgin(struct aic_softc *aic)
case MSG_DISCONNECT:
scb = aic->nexus;
ccb_h = &scb->ccb->ccb_h;
TAILQ_INSERT_HEAD(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
TAILQ_INSERT_TAIL(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
scb->flags |= SCB_DISCONNECTED;
ti = &aic->tinfo[scb->target];
aic->flags |= AIC_BUSFREE_OK;
CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("disconnected\n"));
aic->nexus = NULL;
CAM_DEBUG(ccb_h->path, CAM_DEBUG_TRACE, ("disconnected\n"));
break;
case MSG_MESSAGE_REJECT:
switch (aic->msg_outq & -aic->msg_outq) {
@ -794,7 +799,7 @@ aic_msgout(struct aic_softc *aic)
ti = &aic->tinfo[scb->target];
aic->msg_buf[0] = MSG_IDENTIFY(scb->lun,
(ti->flags & TINFO_DISC_ENB) &&
(ccb->ccb_h.flags & CAM_DIS_DISCONNECT));
!(ccb->ccb_h.flags & CAM_DIS_DISCONNECT));
aic->msg_len = 1;
break;
case AIC_MSG_TAG_Q:
@ -984,6 +989,8 @@ aic_cmd(struct aic_softc *aic)
struct aic_scb *scb = aic->nexus;
struct scsi_request_sense sense_cmd;
CAM_DEBUG_PRINT(CAM_DEBUG_TRACE, ("aic_cmd\n"));
if (scb->flags & SCB_SENSE) {
/* autosense request */
sense_cmd.opcode = REQUEST_SENSE;
@ -998,6 +1005,7 @@ aic_cmd(struct aic_softc *aic)
scb->data_len = scb->ccb->csio.sense_len;
}
aic_outb(aic, SIMODE1, ENSCSIRST|ENPHASEMIS|ENBUSFREE);
aic_outb(aic, DMACNTRL0, ENDMA|WRITE);
aic_outb(aic, SXFRCTL0, SCSIEN|DMAEN|CHEN);
aic_outsw(aic, DMADATA, (u_int16_t *)scb->cmd_ptr,
@ -1006,6 +1014,7 @@ aic_cmd(struct aic_softc *aic)
(aic_inb(aic, DMASTAT) & INTSTAT) == 0)
;
aic_outb(aic, SXFRCTL0, CHEN);
aic_outb(aic, SIMODE1, ENSCSIRST|ENBUSFREE|ENREQINIT);
}
/*
@ -1034,8 +1043,10 @@ aic_done(struct aic_softc *aic, struct aic_scb *scb)
scb->target,
CAM_LUN_WILDCARD);
if (error == CAM_REQ_CMP)
if (error == CAM_REQ_CMP) {
xpt_async(AC_SENT_BDR, path, NULL);
xpt_free_path(path);
}
ccb_h = TAILQ_FIRST(&aic->pending_ccbs);
while (ccb_h != NULL) {
@ -1043,7 +1054,7 @@ aic_done(struct aic_softc *aic, struct aic_scb *scb)
pending_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
if (ccb_h->target_id == scb->target) {
ccb_h->status = CAM_BDR_SENT;
ccb_h->status |= CAM_BDR_SENT;
ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
TAILQ_REMOVE(&aic->pending_ccbs,
&pending_scb->ccb->ccb_h, sim_links.tqe);
@ -1062,7 +1073,7 @@ aic_done(struct aic_softc *aic, struct aic_scb *scb)
nexus_scb = (struct aic_scb *)ccb_h->ccb_scb_ptr;
if (ccb_h->target_id == scb->target) {
ccb_h->status = CAM_BDR_SENT;
ccb_h->status |= CAM_BDR_SENT;
ccb_h = TAILQ_NEXT(ccb_h, sim_links.tqe);
TAILQ_REMOVE(&aic->nexus_ccbs,
&nexus_scb->ccb->ccb_h, sim_links.tqe);
@ -1081,7 +1092,6 @@ aic_done(struct aic_softc *aic, struct aic_scb *scb)
if (aic->nexus == scb) {
aic->nexus = NULL;
aic->state = AIC_IDLE;
}
aic_free_scb(aic, scb);
xpt_done(ccb);
@ -1105,7 +1115,7 @@ aic_timeout(void *arg)
printf("ccb %p - timed out", ccb);
if (aic->nexus && aic->nexus != scb)
printf(", nexus %p", aic->nexus->ccb);
printf(", phase %x\n", aic_inb(aic, SCSISIGI));
printf(", phase 0x%x, state %d\n", aic_inb(aic, SCSISIGI), aic->state);
s = splcam();
@ -1140,7 +1150,6 @@ aic_timeout(void *arg)
aic_sched_msgout(aic, MSG_BUS_DEV_RESET);
} else {
if (aic->nexus == scb) {
ccb->ccb_h.status &= ~CAM_STATUS_MASK;
ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
aic_done(aic, scb);
}
@ -1186,7 +1195,7 @@ aic_intr(void *arg)
return;
}
if (aic->state != AIC_HASNEXUS) {
if (aic->state <= AIC_SELECTING) {
if ((sstat0 & SELDI) != 0) {
aic_reselected(aic);
aic_outb(aic, DMACNTRL0, INTEN);
@ -1210,6 +1219,41 @@ aic_intr(void *arg)
}
}
if ((sstat1 & BUSFREE) != 0) {
aic_outb(aic, SCSISEQ, 0);
aic_outb(aic, CLRSINT0, sstat0);
aic_outb(aic, CLRSINT1, sstat1);
if ((scb = aic->nexus)) {
if ((aic->flags & AIC_BUSFREE_OK) == 0) {
ccb = scb->ccb;
ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
aic_done(aic, scb);
} else if (scb->flags & SCB_DEVICE_RESET) {
ccb = scb->ccb;
if (ccb->ccb_h.func_code == XPT_RESET_DEV) {
xpt_async(AC_SENT_BDR,
ccb->ccb_h.path, NULL);
ccb->ccb_h.status |= CAM_REQ_CMP;
} else
ccb->ccb_h.status |= CAM_CMD_TIMEOUT;
aic_done(aic, scb);
} else if (scb->flags & SCB_SENSE) {
/* autosense request */
aic->flags &= ~AIC_BUSFREE_OK;
aic->tinfo[scb->target].lubusy &=
~(1 << scb->lun);
aic_select(aic);
aic_outb(aic, DMACNTRL0, INTEN);
return;
}
}
aic->flags &= ~AIC_BUSFREE_OK;
aic->state = AIC_IDLE;
aic_start(aic);
aic_outb(aic, DMACNTRL0, INTEN);
return;
}
if ((sstat1 & REQINIT) != 0) {
u_int8_t phase = aic_inb(aic, SCSISIGI) & PH_MASK;
aic_outb(aic, SCSISIGO, phase);
@ -1245,34 +1289,6 @@ aic_intr(void *arg)
return;
}
if ((sstat1 & BUSFREE) != 0) {
aic_outb(aic, SCSISEQ, 0);
aic_outb(aic, CLRSINT0, sstat0);
aic_outb(aic, CLRSINT1, sstat1);
if ((scb = aic->nexus)) {
if ((aic->flags & AIC_BUSFREE_OK) == 0) {
ccb = scb->ccb;
ccb->ccb_h.status = CAM_UNEXP_BUSFREE;
aic_done(aic, scb);
} else if (scb->flags & SCB_DEVICE_RESET) {
ccb = scb->ccb;
ccb->ccb_h.status = CAM_REQ_CMP;
aic_done(aic, scb);
} else if (scb->flags & SCB_SENSE) {
/* autosense request */
aic->flags &= ~AIC_BUSFREE_OK;
aic_select(aic);
aic_outb(aic, DMACNTRL0, INTEN);
return;
}
}
aic->flags &= ~AIC_BUSFREE_OK;
aic->state = AIC_IDLE;
aic_start(aic);
aic_outb(aic, DMACNTRL0, INTEN);
return;
}
printf("aic_intr: unexpected intr sstat0 %x sstat1 %x\n",
sstat0, sstat1);
aic_outb(aic, DMACNTRL0, INTEN);
@ -1351,27 +1367,23 @@ aic_reset(struct aic_softc *aic, int initiate_reset)
while ((ccb_h = TAILQ_FIRST(&aic->pending_ccbs)) != NULL) {
TAILQ_REMOVE(&aic->pending_ccbs, ccb_h, sim_links.tqe);
ccb_h->status &= ~CAM_STATUS_MASK;
ccb_h->status |= CAM_SCSI_BUS_RESET;
aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
}
while ((ccb_h = TAILQ_FIRST(&aic->nexus_ccbs)) != NULL) {
TAILQ_REMOVE(&aic->nexus_ccbs, ccb_h, sim_links.tqe);
ccb_h->status &= ~CAM_STATUS_MASK;
ccb_h->status |= CAM_SCSI_BUS_RESET;
aic_done(aic, (struct aic_scb *)ccb_h->ccb_scb_ptr);
}
if (aic->nexus) {
ccb_h = &aic->nexus->ccb->ccb_h;
ccb_h->status &= ~CAM_STATUS_MASK;
ccb_h->status |= CAM_SCSI_BUS_RESET;
aic_done(aic, aic->nexus);
}
aic->state = AIC_IDLE;
aic_outb(aic, DMACNTRL0, INTEN);
}

View File

@ -51,8 +51,8 @@ struct aic_isa_softc {
static int aic_isa_alloc_resources __P((device_t));
static void aic_isa_release_resources __P((device_t));
static int aic_isa_probe __P((device_t dev));
static int aic_isa_attach __P((device_t dev));
static int aic_isa_probe __P((device_t));
static int aic_isa_attach __P((device_t));
static u_int aic_isa_ports[] = { 0x340, 0x140 };
#define AIC_ISA_NUMPORTS (sizeof(aic_isa_ports) / sizeof(aic_isa_ports[0]))
@ -154,6 +154,7 @@ aic_isa_probe(device_t dev)
bus_set_resource(dev, SYS_RES_IRQ, 0, PORTA_IRQ(porta), 1);
if ((aic->flags & AIC_DMA_ENABLE) && isa_get_drq(dev) == -1)
bus_set_resource(dev, SYS_RES_DRQ, 0, PORTA_DRQ(porta), 1);
device_set_desc(dev, "Adaptec 6260/6360 SCSI controller");
return (0);
}

View File

@ -106,7 +106,8 @@ struct aic_softc {
#define AIC_IDLE 0x00
#define AIC_SELECTING 0x01
#define AIC_RESELECTED 0x02
#define AIC_HASNEXUS 0x03
#define AIC_RECONNECTING 0x03
#define AIC_HASNEXUS 0x04
#define AIC_MSG_IDENTIFY 0x01
#define AIC_MSG_TAG_Q 0x02