summaryrefslogtreecommitdiffstats
path: root/sys/dev
diff options
context:
space:
mode:
authorgibbs <gibbs@FreeBSD.org>2000-11-06 20:05:38 +0000
committergibbs <gibbs@FreeBSD.org>2000-11-06 20:05:38 +0000
commit13459a7344194f03540cfd2d91e74114d63217f0 (patch)
treeee6b9de943caa6f0c590f6a368ba226cab55ea02 /sys/dev
parent0a81eeee321ee06eb5c4bdecf10ccdf38411f692 (diff)
downloadFreeBSD-src-13459a7344194f03540cfd2d91e74114d63217f0.zip
FreeBSD-src-13459a7344194f03540cfd2d91e74114d63217f0.tar.gz
aic7xxx.c:
When restarting the sequencer, ensure that the SCBCNT register is 0. A non-zero count will prevent the setting of the CCSCBDIR bit in any future dma operations. The only time CCSCBCNT would be non-zero is if we happened to halt the dma during a reset, but even that should never happen. Better safe than sorry. When a command completes before the target responds to an ATN for a recovery command, we now notify the kernel so that any recovery operation requeued in the qinfifo can be removed safely. In the past, we did this in ahc_done(), but ahc_done() may be called without the card paused. This also avoids a recursive call to ahc_search_qinifo() which could have occurred if ahc_search_qinififo() happened to be the routine to complete a recovery action. Fix 8bit math used for adjusting the qinfifo. The index must be wrapped properly within the 256 entry array. We rely on the fact that qinfifonext is a uint8_t in most cases to handle this wrap, but we missed a few spots where the resultant calculation was promoted to an int. Change the way that we deal with aborting the first or second entry from the qinfifo. We now swap the first entry in the qinfifo with the "next queued scb" to force the sequencer to see an abort collision if we ever touch the qinififo while the sequencer is mid SCB dma. aic7xxx.reg: Add new MKMSG_FAILED sequencer interrupt. This displaced the BOGUS_TAG interrupt used in some previous sequencer code debugging. aic7xxx.seq: Increment our position in the qinfifo only once the dma is complete and we have verified that the queue has not been changed during our DMA. This simplifies code in the kernel. Protect against "instruction creep" when issuing a pausing sequencer interrupt. On at least the 7890/91/96/97, the sequencer will coast after issuing the interrupt for up to two instructions. In the past we delt with this by using carefully placed nops. Now we call a routine to issue the interrupt followed by a nop and a ret. Tell the kernel should an SCB complete with the MK_MESSAGE flag still set. This means the target ignored our ATN request. Clear the channel twice as we exit the data phase. On the aic7890/91, the S/G preload logic may require the second clearing to get the last S/G out of the FIFO. aic7xxx_freebsd.c: Don't bother searching the qinfifo for a doubly queued recovery scb in ahc_done. This case is handled by the core driver now. Free the path used to issue async callbacks after the callback is complete. aic7xxx_inline.h: Split the SCB queue routine into a routine that swaps the SCB with the "next queued SCB" and a routine that calls the swapping routine and notifies the card of the new SCB. The swapping routine is now also used by ahc_search_qinfifo.
Diffstat (limited to 'sys/dev')
-rw-r--r--sys/dev/aic7xxx/aic7xxx.c79
-rw-r--r--sys/dev/aic7xxx/aic7xxx.reg7
-rw-r--r--sys/dev/aic7xxx/aic7xxx.seq59
-rw-r--r--sys/dev/aic7xxx/aic7xxx_freebsd.c10
-rw-r--r--sys/dev/aic7xxx/aic7xxx_inline.h22
-rw-r--r--sys/dev/aic7xxx/aic7xxx_osm.c10
6 files changed, 98 insertions, 89 deletions
diff --git a/sys/dev/aic7xxx/aic7xxx.c b/sys/dev/aic7xxx/aic7xxx.c
index 89224a0..452fe16 100644
--- a/sys/dev/aic7xxx/aic7xxx.c
+++ b/sys/dev/aic7xxx/aic7xxx.c
@@ -247,6 +247,7 @@ restart_sequencer(struct ahc_softc *ahc)
ahc_inb(ahc, SCSISEQ_TEMPLATE) & (ENSELI|ENRSELI|ENAUTOATNP));
if ((ahc->features & AHC_CMD_CHAN) != 0) {
/* Ensure that no DMA operations are in progress */
+ ahc_outb(ahc, CCSCBCNT, 0);
ahc_outb(ahc, CCSGCTL, 0);
ahc_outb(ahc, CCSCBCTL, 0);
}
@@ -772,12 +773,26 @@ ahc_handle_seqint(struct ahc_softc *ahc, u_int intstat)
ahc_freeze_scb(scb);
break;
}
- case BOGUS_TAG:
+ case MKMSG_FAILED:
{
- printf("%s: Somehow queued a tag value of 0xFF\n",
- ahc_name(ahc));
- ahc_dump_card_state(ahc);
- panic("For safety\n");
+ u_int scbindex;
+
+ printf("%s:%c:%d:%d: Attempt to issue message failed\n",
+ ahc_name(ahc), devinfo.channel, devinfo.target,
+ devinfo.lun);
+ scbindex = ahc_inb(ahc, SCB_TAG);
+ scb = ahc_lookup_scb(ahc, scbindex);
+ if (scb != NULL
+ && (scb->flags & SCB_RECOVERY_SCB) != 0)
+ /*
+ * Ensure that we didn't put a second instance of this
+ * SCB into the QINFIFO.
+ */
+ ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
+ SCB_GET_CHANNEL(ahc, scb),
+ SCB_GET_LUN(scb), scb->hscb->tag,
+ ROLE_INITIATOR, /*status*/0,
+ SEARCH_REMOVE);
break;
}
case ABORT_QINSCB:
@@ -4378,8 +4393,10 @@ ahc_qinfifo_requeue_tail(struct ahc_softc *ahc, struct scb *scb)
prev_scb = NULL;
if (ahc_qinfifo_count(ahc) != 0) {
u_int prev_tag;
+ uint8_t prev_pos;
- prev_tag = ahc->qinfifo[ahc->qinfifonext - 1];
+ prev_pos = ahc->qinfifonext - 1;
+ prev_tag = ahc->qinfifo[prev_pos];
prev_scb = ahc_lookup_scb(ahc, prev_tag);
}
ahc_qinfifo_requeue(ahc, prev_scb, scb);
@@ -4406,13 +4423,15 @@ static int
ahc_qinfifo_count(struct ahc_softc *ahc)
{
u_int8_t qinpos;
+ u_int8_t diff;
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
qinpos = ahc_inb(ahc, SNSCB_QOFF);
ahc_outb(ahc, SNSCB_QOFF, qinpos);
} else
qinpos = ahc_inb(ahc, QINPOS);
- return (ahc->qinfifonext - qinpos);
+ diff = ahc->qinfifonext - qinpos;
+ return (diff);
}
int
@@ -4441,16 +4460,6 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
qinstart = ahc_inb(ahc, QINPOS);
qinpos = qinstart;
- /*
- * If the next qinfifo SCB does not match the
- * entry in our qinfifo, the sequencer is in
- * the process of dmaing down the SCB that just
- * preceeds qinstart. So, start our search in
- * the qinfifo back by an entry. The sequencer
- * is smart enough to check after the SCB dma
- * completes to ensure that the newly DMAed
- * SCB is still relevant.
- */
next = ahc_inb(ahc, NEXT_QUEUED_SCB);
if (qinstart == qintail) {
if (next != ahc->next_queued_scb->hscb->tag)
@@ -4470,6 +4479,25 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
ahc_freeze_untagged_queues(ahc);
}
+ if (action != SEARCH_COUNT && (qinpos != qintail)) {
+ /*
+ * The sequencer may be in the process of dmaing
+ * down the SCB at the beginning of the queue.
+ * This could be problematic if either the first
+ * or the second SCB is removed from the queue
+ * (the first SCB includes a pointer to the "next"
+ * SCB to dma). If we have the prospect of removing
+ * any entries, swap the first element in the queue
+ * with the next HSCB so the sequencer will notice
+ * that NEXT_QUEUED_SCB has changed during its dma
+ * attempt and will retry the DMA.
+ */
+ scb = ahc_lookup_scb(ahc, ahc->qinfifo[qinpos]);
+ ahc->scb_data->scbindex[scb->hscb->tag] = NULL;
+ ahc_swap_with_next_hscb(ahc, scb);
+ ahc->qinfifo[qinpos] = scb->hscb->tag;
+ }
+
/*
* Start with an empty queue. Entries that are not chosen
* for removal will be re-added to the queue as we go.
@@ -4500,23 +4528,6 @@ ahc_search_qinfifo(struct ahc_softc *ahc, int target, char channel,
/* FALLTHROUGH */
case SEARCH_REMOVE:
- /*
- * The sequencer increments its position in
- * the qinfifo as soon as it determines that
- * an SCB needs to be DMA'ed down to the card.
- * So, if we are aborting a command that is
- * still in the process of being DMAed, we
- * must move the sequencer's qinfifo pointer
- * back as well.
- */
- if (qinpos == (qinstart - 1)) {
- if (have_qregs) {
- ahc_outb(ahc, SNSCB_QOFF,
- qinpos);
- } else {
- ahc_outb(ahc, QINPOS, qinpos);
- }
- }
break;
}
case SEARCH_COUNT:
diff --git a/sys/dev/aic7xxx/aic7xxx.reg b/sys/dev/aic7xxx/aic7xxx.reg
index ed27284..01fa704 100644
--- a/sys/dev/aic7xxx/aic7xxx.reg
+++ b/sys/dev/aic7xxx/aic7xxx.reg
@@ -801,9 +801,10 @@ register INTSTAT {
* beyond the bounds of its
* command.
*/
- mask BOGUS_TAG 0xa0|SEQINT /*
- * Sequencer given an SCB with
- * a Tag value of 0xFF.
+ mask MKMSG_FAILED 0xa0|SEQINT /*
+ * Target completed command
+ * without honoring our ATN
+ * request to issue a message.
*/
mask SCBPTR_MISMATCH 0xb0|SEQINT /*
* In the SCB paging case, our
diff --git a/sys/dev/aic7xxx/aic7xxx.seq b/sys/dev/aic7xxx/aic7xxx.seq
index f300676..399ef63 100644
--- a/sys/dev/aic7xxx/aic7xxx.seq
+++ b/sys/dev/aic7xxx/aic7xxx.seq
@@ -78,11 +78,9 @@ test_queue:
BEGIN_CRITICAL
if ((ahc->features & AHC_QUEUE_REGS) != 0) {
test QOFF_CTLSTA, SCB_AVAIL jz poll_for_work_loop;
- mov NONE, SNSCB_QOFF;
} else {
mov A, QINPOS;
cmp KERNEL_QINPOS, A je poll_for_work_loop;
- inc QINPOS;
}
mov ARG_1, NEXT_QUEUED_SCB;
END_CRITICAL
@@ -116,14 +114,19 @@ BEGIN_CRITICAL
if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
if ((ahc->flags & AHC_PAGESCBS) == 0) {
cmp SCBPTR, A je . + 2;
- mvi INTSTAT, SCBPTR_MISMATCH;
+ mvi SCBPTR_MISMATCH call set_seqint;
}
cmp SCB_TAG, A je . + 2;
- mvi INTSTAT, SCB_MISMATCH;
+ mvi SCB_MISMATCH call set_seqint;
}
mov NEXT_QUEUED_SCB, SCB_NEXT;
mov SCB_NEXT,WAITING_SCBH;
mov WAITING_SCBH, SCBPTR;
+ if ((ahc->features & AHC_QUEUE_REGS) != 0) {
+ mov NONE, SNSCB_QOFF;
+ } else {
+ inc QINPOS;
+ }
END_CRITICAL
start_waiting:
/*
@@ -134,7 +137,7 @@ start_waiting:
jmp poll_for_work_loop;
abort_qinscb:
- mvi INTSTAT, ABORT_QINSCB;
+ mvi ABORT_QINSCB call set_seqint;
call add_scb_to_free_list;
jmp poll_for_work_loop;
@@ -324,8 +327,7 @@ select_in:
* run it's own target mode message state engine.
*/
host_target_message_loop:
- mvi INTSTAT, HOST_MSG_LOOP;
- nop;
+ mvi HOST_MSG_LOOP call set_seqint;
cmp RETURN_1, EXIT_MSG_LOOP je target_ITloop;
test SSTAT0, SPIORDY jz .;
jmp host_target_message_loop;
@@ -406,10 +408,6 @@ select_out:
and SCSISEQ, TEMODE|ENSELI|ENRSELI|ENAUTOATNP, SCSISEQ;
mvi CLRSINT0, CLRSELDO;
mov SCBPTR, WAITING_SCBH;
- if ((ahc->flags & AHC_SEQUENCER_DEBUG) != 0) {
- cmp SCB_TAG, SCB_LIST_NULL jne . + 2;
- mvi INTSTAT, BOGUS_TAG;
- }
mov WAITING_SCBH,SCB_NEXT;
mov SAVED_SCSIID, SCB_SCSIID;
mov SAVED_LUN, SCB_LUN;
@@ -608,7 +606,7 @@ ITloop:
cmp A,P_STATUS je p_status;
cmp A,P_MESGIN je p_mesgin;
- mvi INTSTAT,BAD_PHASE;
+ mvi BAD_PHASE call set_seqint;
jmp ITloop; /* Try reading the bus again. */
await_busfree:
@@ -618,7 +616,7 @@ await_busfree:
and SXFRCTL0, ~SPIOEN;
test SSTAT1,REQINIT|BUSFREE jz .;
test SSTAT1, BUSFREE jnz poll_for_work;
- mvi INTSTAT, BAD_PHASE;
+ mvi BAD_PHASE call set_seqint;
}
clear_target_state:
@@ -838,7 +836,7 @@ data_phase_loop:
or SXFRCTL1,BITBUCKET;
test SSTAT1,PHASEMIS jz .;
and SXFRCTL1, ~BITBUCKET;
- mvi INTSTAT,DATA_OVERRUN;
+ mvi DATA_OVERRUN call set_seqint;
jmp ITloop;
data_phase_inbounds:
@@ -1157,6 +1155,7 @@ bmov_resid:
if ((ahc->features & AHC_ULTRA2) != 0) {
/* Clear the channel in case we return to data phase later */
or SXFRCTL0, CLRSTCNT|CLRCHN;
+ or SXFRCTL0, CLRSTCNT|CLRCHN;
}
if ((ahc->flags & AHC_TARGETROLE) != 0) {
@@ -1381,7 +1380,7 @@ p_mesgin:
* shouldn't hurt, but why do it twice...
*/
host_message_loop:
- mvi INTSTAT, HOST_MSG_LOOP;
+ mvi HOST_MSG_LOOP call set_seqint;
call phase_lock;
cmp RETURN_1, EXIT_MSG_LOOP je ITloop + 1;
jmp host_message_loop;
@@ -1394,7 +1393,7 @@ if ((ahc->features & AHC_WIDE) != 0) {
cmp ARG_1, 0x01 jne mesgin_reject;
test SCB_RESIDUAL_SGPTR[0], SG_LIST_NULL jz . + 2;
test DATA_COUNT_ODD, 0x1 jz mesgin_done;
- mvi INTSTAT, IGN_WIDE_RES;
+ mvi IGN_WIDE_RES call set_seqint;
jmp mesgin_done;
}
@@ -1419,7 +1418,13 @@ mesgin_complete:
*/
/*
- * First check for residuals
+ * See if we attempted to deliver a message but the target ingnored us.
+ */
+ test SCB_CONTROL, MK_MESSAGE jz . + 2;
+ mvi MKMSG_FAILED call set_seqint;
+
+/*
+ * Check for residuals
*/
test SCB_SGPTR, SG_LIST_NULL jnz check_status;/* No xfer */
test SCB_SGPTR, SG_FULL_RESID jnz upload_scb;/* Never xfered */
@@ -1431,8 +1436,7 @@ upload_scb:
mvi DMAPARAMS, FIFORESET;
mov SCB_TAG call dma_scb;
test SCB_SCSI_STATUS, 0xff jz complete; /* Just a residual? */
- mvi INTSTAT, BAD_STATUS; /* let driver know */
- nop;
+ mvi BAD_STATUS call set_seqint; /* let driver know */
cmp RETURN_1, SEND_SENSE jne complete;
call add_scb_to_free_list;
jmp await_busfree;
@@ -1554,9 +1558,11 @@ fetch_busy_target:
add A, -BUSY_TARGETS, SINDEX;
jc . + 2;
mvi INTSTAT, OUT_OF_RANGE;
+ nop;
add A, -(BUSY_TARGETS + 16), SINDEX;
jnc . + 2;
mvi INTSTAT, OUT_OF_RANGE;
+ nop;
}
}
mov ARG_1, SINDIR;
@@ -1643,7 +1649,7 @@ not_found_cleanup_scb:
call add_scb_to_free_list;
}
not_found:
- mvi INTSTAT, NO_MATCH;
+ mvi NO_MATCH call set_seqint;
jmp mesgin_done;
mk_mesg:
@@ -1666,7 +1672,7 @@ mk_mesg:
* use the same calling convention as inb.
*/
inb_next_wait_perr:
- mvi INTSTAT, PERR_DETECTED;
+ mvi PERR_DETECTED call set_seqint;
jmp inb_next_wait;
inb_next:
mov NONE,SCSIDATL; /*dummy read from latch to ACK*/
@@ -1736,7 +1742,7 @@ target_outb:
assert:
test SEQ_FLAGS,IDENTIFY_SEEN jnz return; /* seen IDENTIFY? */
- mvi INTSTAT,NO_IDENT ret; /* no - tell the kernel */
+ mvi NO_IDENT jmp set_seqint; /* no - tell the kernel */
/*
* Locate a disconnected SCB by SCBID. Upon return, SCBPTR and SINDEX will
@@ -1808,7 +1814,7 @@ post_byte:
}
phase_lock_perr:
- mvi INTSTAT, PERR_DETECTED;
+ mvi PERR_DETECTED call set_seqint;
phase_lock:
/*
* If there is a parity error, wait for the kernel to
@@ -1913,7 +1919,7 @@ dma_scb_tohost:
mvi CCSCBCTL, CCSCBRESET;
bmov CCSCBRAM, SCB_BASE, SCB_UPLOAD_SIZE;
or CCSCBCTL, CCSCBEN|CCSCBRESET;
- test CCSCBCTL, CCSCBDONE jz .;
+ cmp CCSCBCTL, CCSCBDONE|CCSCBEN jne .;
} else if ((ahc->bugs & AHC_SCBCHAN_UPLOAD_BUG) != 0) {
mvi CCSCBCTL, CCARREN|CCSCBRESET;
cmp CCSCBCTL, ARRDONE|CCARREN jne .;
@@ -2052,7 +2058,7 @@ get_free_or_disc_scb:
cmp FREE_SCBH, SCB_LIST_NULL jne dequeue_free_scb;
cmp DISCONNECTED_SCBH, SCB_LIST_NULL jne dequeue_disc_scb;
return_error:
- mvi INTSTAT, NO_FREE_SCB;
+ mvi NO_FREE_SCB call set_seqint;
mvi SINDEX, SCB_LIST_NULL ret;
dequeue_disc_scb:
mov SCBPTR, DISCONNECTED_SCBH;
@@ -2076,5 +2082,8 @@ BEGIN_CRITICAL
mov DISCONNECTED_SCBH, SCBPTR ret;
END_CRITICAL
}
+set_seqint:
+ mov INTSTAT, SINDEX;
+ nop;
return:
ret;
diff --git a/sys/dev/aic7xxx/aic7xxx_freebsd.c b/sys/dev/aic7xxx/aic7xxx_freebsd.c
index 6a247c0..06a4136 100644
--- a/sys/dev/aic7xxx/aic7xxx_freebsd.c
+++ b/sys/dev/aic7xxx/aic7xxx_freebsd.c
@@ -316,15 +316,6 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb)
(ccb->ccb_h.timeout * hz)/1000);
}
- /*
- * Ensure that we didn't put a second instance of this
- * SCB into the QINFIFO.
- */
- ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
- SCB_GET_CHANNEL(ahc, scb),
- SCB_GET_LUN(scb), scb->hscb->tag,
- ROLE_INITIATOR, /*status*/0,
- SEARCH_REMOVE);
if (ahc_get_transaction_status(scb) == CAM_BDR_SENT
|| ahc_get_transaction_status(scb) == CAM_REQ_ABORTED)
ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
@@ -1798,6 +1789,7 @@ ahc_send_async(struct ahc_softc *ahc, char channel, u_int target,
panic("ahc_send_async: Unexpected async event");
}
xpt_async(code, path, arg);
+ xpt_free_path(path);
}
void
diff --git a/sys/dev/aic7xxx/aic7xxx_inline.h b/sys/dev/aic7xxx/aic7xxx_inline.h
index 1a6b638..96d9c37 100644
--- a/sys/dev/aic7xxx/aic7xxx_inline.h
+++ b/sys/dev/aic7xxx/aic7xxx_inline.h
@@ -199,6 +199,8 @@ static __inline struct ahc_initiator_tinfo *
static __inline struct scb*
ahc_get_scb(struct ahc_softc *ahc);
static __inline void ahc_free_scb(struct ahc_softc *ahc, struct scb *scb);
+static __inline void ahc_swap_with_next_hscb(struct ahc_softc *ahc,
+ struct scb *scb);
static __inline void ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb);
/*
@@ -281,11 +283,8 @@ ahc_lookup_scb(struct ahc_softc *ahc, u_int tag)
}
-/*
- * Tell the sequencer about a new transaction to execute.
- */
static __inline void
-ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+ahc_swap_with_next_hscb(struct ahc_softc *ahc, struct scb *scb)
{
struct hardware_scb *q_hscb;
u_int saved_tag;
@@ -298,13 +297,9 @@ ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
* When we are called to queue "an arbitrary scb",
* we copy the contents of the incoming HSCB to the one
* the sequencer knows about, swap HSCB pointers and
- * finally assigne the SCB to the tag indexed location
+ * finally assign the SCB to the tag indexed location
* in the scb_array. This makes sure that we can still
* locate the correct SCB by SCB_TAG.
- *
- * Start by copying the payload without perterbing
- * the tag number. Also set the hscb id for the next
- * SCB to download.
*/
q_hscb = ahc->next_queued_scb->hscb;
saved_tag = q_hscb->tag;
@@ -323,6 +318,15 @@ ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
/* Now define the mapping from tag to SCB in the scbindex */
ahc->scb_data->scbindex[scb->hscb->tag] = scb;
+}
+
+/*
+ * Tell the sequencer about a new transaction to execute.
+ */
+static __inline void
+ahc_queue_scb(struct ahc_softc *ahc, struct scb *scb)
+{
+ ahc_swap_with_next_hscb(ahc, scb);
if (scb->hscb->tag == SCB_LIST_NULL
|| scb->hscb->next == SCB_LIST_NULL)
diff --git a/sys/dev/aic7xxx/aic7xxx_osm.c b/sys/dev/aic7xxx/aic7xxx_osm.c
index 6a247c0..06a4136 100644
--- a/sys/dev/aic7xxx/aic7xxx_osm.c
+++ b/sys/dev/aic7xxx/aic7xxx_osm.c
@@ -316,15 +316,6 @@ ahc_done(struct ahc_softc *ahc, struct scb *scb)
(ccb->ccb_h.timeout * hz)/1000);
}
- /*
- * Ensure that we didn't put a second instance of this
- * SCB into the QINFIFO.
- */
- ahc_search_qinfifo(ahc, SCB_GET_TARGET(ahc, scb),
- SCB_GET_CHANNEL(ahc, scb),
- SCB_GET_LUN(scb), scb->hscb->tag,
- ROLE_INITIATOR, /*status*/0,
- SEARCH_REMOVE);
if (ahc_get_transaction_status(scb) == CAM_BDR_SENT
|| ahc_get_transaction_status(scb) == CAM_REQ_ABORTED)
ahc_set_transaction_status(scb, CAM_CMD_TIMEOUT);
@@ -1798,6 +1789,7 @@ ahc_send_async(struct ahc_softc *ahc, char channel, u_int target,
panic("ahc_send_async: Unexpected async event");
}
xpt_async(code, path, arg);
+ xpt_free_path(path);
}
void
OpenPOWER on IntegriCloud