summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordg <dg@FreeBSD.org>1998-08-04 08:53:12 +0000
committerdg <dg@FreeBSD.org>1998-08-04 08:53:12 +0000
commit7480325b78dd2b3189429e53dcfacbdce7241bfb (patch)
treeb55afd2f0cb5d14f328b9cc850d985c3f809b727
parent7c635b975bab48695edca3a2785b4511a4194fe7 (diff)
downloadFreeBSD-src-7480325b78dd2b3189429e53dcfacbdce7241bfb.zip
FreeBSD-src-7480325b78dd2b3189429e53dcfacbdce7241bfb.tar.gz
Implemented a more sophisticated mechanism for handling transmitter
interrupts which now defers them until the transmit queue if filled up with completed buffers. This has two advantages: first, it reduces the number of transmitter interrupts to just 1/120th of the rate that they occured previously, and two, running down many buffers at once has much improved cache effects.
-rw-r--r--sys/dev/fxp/if_fxp.c148
-rw-r--r--sys/pci/if_fxp.c148
2 files changed, 206 insertions, 90 deletions
diff --git a/sys/dev/fxp/if_fxp.c b/sys/dev/fxp/if_fxp.c
index 92897c3..faf83fb 100644
--- a/sys/dev/fxp/if_fxp.c
+++ b/sys/dev/fxp/if_fxp.c
@@ -27,7 +27,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: if_fxp.c,v 1.53 1998/08/02 00:28:45 dg Exp $
+ * $Id: if_fxp.c,v 1.54 1998/08/02 00:33:38 dg Exp $
*/
/*
@@ -244,6 +244,15 @@ static int tx_threshold = 64;
#define FXP_NTXCB 128
/*
+ * Number of completed TX commands at which point an interrupt
+ * will be generated to garbage collect the attached buffers.
+ * Must be at least one less than FXP_NTXCB, and should be
+ * enough less so that the transmitter doesn't becomes idle
+ * during the buffer rundown (which would reduce performance).
+ */
+#define FXP_CXINT_THRESH 120
+
+/*
* TxCB list index mask. This is used to do list wrap-around.
*/
#define FXP_TXCB_MASK (FXP_NTXCB - 1)
@@ -394,9 +403,10 @@ fxp_attach(parent, self, aux)
*/
if_attach(ifp);
/*
- * Let the system queue as many packets as we have TX descriptors.
+ * Let the system queue as many packets as we have available
+ * TX descriptors.
*/
- ifp->if_snd.ifq_maxlen = FXP_NTXCB;
+ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1;
ether_ifattach(ifp, enaddr);
#if NBPFILTER > 0
bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB,
@@ -566,9 +576,10 @@ fxp_attach(config_id, unit)
*/
if_attach(ifp);
/*
- * Let the system queue as many packets as we have TX descriptors.
+ * Let the system queue as many packets as we have available
+ * TX descriptors.
*/
- ifp->if_snd.ifq_maxlen = FXP_NTXCB;
+ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1;
ether_ifattach(ifp);
#if NBPFILTER > 0
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
@@ -797,8 +808,10 @@ fxp_start(ifp)
/*
* We're finished if there is nothing more to add to the list or if
* we're all filled up with buffers to transmit.
+ * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add
+ * a NOP command when needed.
*/
- while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB) {
+ while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) {
struct mbuf *m, *mb_head;
int segment;
@@ -859,8 +872,18 @@ tbdinit:
txp->tbd_number = segment;
txp->mb_head = mb_head;
txp->cb_status = 0;
- txp->cb_command =
- FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S;
+ if (sc->tx_queued != FXP_CXINT_THRESH - 1) {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S;
+ } else {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Set a 5 second timer just in case we don't hear from the
+ * card again.
+ */
+ ifp->if_timer = 5;
+ }
txp->tx_threshold = tx_threshold;
/*
@@ -895,12 +918,6 @@ tbdinit:
if (txp != NULL) {
fxp_scb_wait(sc);
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME);
-
- /*
- * Set a 5 second timer just in case we don't hear from the
- * card again.
- */
- ifp->if_timer = 5;
}
}
@@ -928,6 +945,33 @@ fxp_intr(arg)
CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack);
/*
+ * Free any finished transmit mbuf chains.
+ */
+ if (statack & FXP_SCB_STATACK_CXTNO) {
+ struct fxp_cb_tx *txp;
+
+ for (txp = sc->cbl_first; sc->tx_queued &&
+ (txp->cb_status & FXP_CB_STATUS_C) != 0;
+ txp = txp->next) {
+ if (txp->mb_head != NULL) {
+ m_freem(txp->mb_head);
+ txp->mb_head = NULL;
+ }
+ sc->tx_queued--;
+ }
+ sc->cbl_first = txp;
+ ifp->if_timer = 0;
+ if (sc->tx_queued == 0) {
+ if (sc->need_mcsetup)
+ fxp_mc_setup(sc);
+ }
+ /*
+ * Try to start more packets transmitting.
+ */
+ if (ifp->if_snd.ifq_head != NULL)
+ fxp_start(ifp);
+ }
+ /*
* Process receiver interrupts. If a no-resource (RNR)
* condition exists, get whatever packets we can and
* re-start the receiver.
@@ -1003,33 +1047,6 @@ rcvloop:
FXP_SCB_COMMAND_RU_START);
}
}
- /*
- * Free any finished transmit mbuf chains.
- */
- if (statack & FXP_SCB_STATACK_CNA) {
- struct fxp_cb_tx *txp;
-
- for (txp = sc->cbl_first; sc->tx_queued &&
- (txp->cb_status & FXP_CB_STATUS_C) != 0;
- txp = txp->next) {
- if (txp->mb_head != NULL) {
- m_freem(txp->mb_head);
- txp->mb_head = NULL;
- }
- sc->tx_queued--;
- }
- sc->cbl_first = txp;
- if (sc->tx_queued == 0) {
- ifp->if_timer = 0;
- if (sc->need_mcsetup)
- fxp_mc_setup(sc);
- }
- /*
- * Try to start more packets transmitting.
- */
- if (ifp->if_snd.ifq_head != NULL)
- fxp_start(ifp);
- }
}
#if defined(__NetBSD__)
return (claimed);
@@ -1262,7 +1279,7 @@ fxp_init(xsc)
cbp->dma_bce = 0; /* (disable) dma max counters */
cbp->late_scb = 0; /* (don't) defer SCB update */
cbp->tno_int = 0; /* (disable) tx not okay interrupt */
- cbp->ci_int = 0; /* interrupt on CU not active */
+ cbp->ci_int = 1; /* interrupt on CU idle */
cbp->save_bf = prm; /* save bad frames */
cbp->disc_short_rx = !prm; /* discard short packets */
cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */
@@ -1691,7 +1708,7 @@ fxp_ioctl(ifp, command, data)
*
* We have an artificial restriction that the multicast setup command
* must be the first command in the chain, so we take steps to ensure
- * that. By requiring this, it allows us to keep the performance of
+ * this. By requiring this, it allows us to keep up the performance of
* the pre-initialized command ring (esp. link pointers) by not actually
* inserting the mcsetup command in the ring - i.e. its link pointer
* points to the TxCB ring, but the mcsetup descriptor itself is not part
@@ -1709,8 +1726,49 @@ fxp_mc_setup(sc)
struct ifmultiaddr *ifma;
int nmcasts;
+ /*
+ * If there are queued commands, we must wait until they are all
+ * completed. If we are already waiting, then add a NOP command
+ * with interrupt option so that we're notified when all commands
+ * have been completed - fxp_start() ensures that no additional
+ * TX commands will be added when need_mcsetup is true.
+ */
if (sc->tx_queued) {
+ struct fxp_cb_tx *txp;
+
+ /*
+ * need_mcsetup will be true if we are already waiting for the
+ * NOP command to be completed (see below). In this case, bail.
+ */
+ if (sc->need_mcsetup)
+ return;
sc->need_mcsetup = 1;
+
+ /*
+ * Add a NOP command with interrupt so that we are notified when all
+ * TX commands have been processed.
+ */
+ txp = sc->cbl_last->next;
+ txp->mb_head = NULL;
+ txp->cb_status = 0;
+ txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Advance the end of list forward.
+ */
+ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+ sc->cbl_last = txp;
+ sc->tx_queued++;
+ /*
+ * Issue a resume in case the CU has just suspended.
+ */
+ fxp_scb_wait(sc);
+ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME);
+ /*
+ * Set a 5 second timer just in case we don't hear from the
+ * card again.
+ */
+ ifp->if_timer = 5;
+
return;
}
sc->need_mcsetup = 0;
@@ -1721,7 +1779,7 @@ fxp_mc_setup(sc)
mcsp->next = sc->cbl_base;
mcsp->mb_head = NULL;
mcsp->cb_status = 0;
- mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S;
+ mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
mcsp->link_addr = vtophys(&sc->cbl_base->cb_status);
nmcasts = 0;
@@ -1758,6 +1816,6 @@ fxp_mc_setup(sc)
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
- ifp->if_timer = 5;
+ ifp->if_timer = 2;
return;
}
diff --git a/sys/pci/if_fxp.c b/sys/pci/if_fxp.c
index 92897c3..faf83fb 100644
--- a/sys/pci/if_fxp.c
+++ b/sys/pci/if_fxp.c
@@ -27,7 +27,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: if_fxp.c,v 1.53 1998/08/02 00:28:45 dg Exp $
+ * $Id: if_fxp.c,v 1.54 1998/08/02 00:33:38 dg Exp $
*/
/*
@@ -244,6 +244,15 @@ static int tx_threshold = 64;
#define FXP_NTXCB 128
/*
+ * Number of completed TX commands at which point an interrupt
+ * will be generated to garbage collect the attached buffers.
+ * Must be at least one less than FXP_NTXCB, and should be
+ * enough less so that the transmitter doesn't becomes idle
+ * during the buffer rundown (which would reduce performance).
+ */
+#define FXP_CXINT_THRESH 120
+
+/*
* TxCB list index mask. This is used to do list wrap-around.
*/
#define FXP_TXCB_MASK (FXP_NTXCB - 1)
@@ -394,9 +403,10 @@ fxp_attach(parent, self, aux)
*/
if_attach(ifp);
/*
- * Let the system queue as many packets as we have TX descriptors.
+ * Let the system queue as many packets as we have available
+ * TX descriptors.
*/
- ifp->if_snd.ifq_maxlen = FXP_NTXCB;
+ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1;
ether_ifattach(ifp, enaddr);
#if NBPFILTER > 0
bpfattach(&sc->sc_ethercom.ec_if.if_bpf, ifp, DLT_EN10MB,
@@ -566,9 +576,10 @@ fxp_attach(config_id, unit)
*/
if_attach(ifp);
/*
- * Let the system queue as many packets as we have TX descriptors.
+ * Let the system queue as many packets as we have available
+ * TX descriptors.
*/
- ifp->if_snd.ifq_maxlen = FXP_NTXCB;
+ ifp->if_snd.ifq_maxlen = FXP_NTXCB - 1;
ether_ifattach(ifp);
#if NBPFILTER > 0
bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
@@ -797,8 +808,10 @@ fxp_start(ifp)
/*
* We're finished if there is nothing more to add to the list or if
* we're all filled up with buffers to transmit.
+ * NOTE: One TxCB is reserved to guarantee that fxp_mc_setup() can add
+ * a NOP command when needed.
*/
- while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB) {
+ while (ifp->if_snd.ifq_head != NULL && sc->tx_queued < FXP_NTXCB - 1) {
struct mbuf *m, *mb_head;
int segment;
@@ -859,8 +872,18 @@ tbdinit:
txp->tbd_number = segment;
txp->mb_head = mb_head;
txp->cb_status = 0;
- txp->cb_command =
- FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S;
+ if (sc->tx_queued != FXP_CXINT_THRESH - 1) {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S;
+ } else {
+ txp->cb_command =
+ FXP_CB_COMMAND_XMIT | FXP_CB_COMMAND_SF | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Set a 5 second timer just in case we don't hear from the
+ * card again.
+ */
+ ifp->if_timer = 5;
+ }
txp->tx_threshold = tx_threshold;
/*
@@ -895,12 +918,6 @@ tbdinit:
if (txp != NULL) {
fxp_scb_wait(sc);
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME);
-
- /*
- * Set a 5 second timer just in case we don't hear from the
- * card again.
- */
- ifp->if_timer = 5;
}
}
@@ -928,6 +945,33 @@ fxp_intr(arg)
CSR_WRITE_1(sc, FXP_CSR_SCB_STATACK, statack);
/*
+ * Free any finished transmit mbuf chains.
+ */
+ if (statack & FXP_SCB_STATACK_CXTNO) {
+ struct fxp_cb_tx *txp;
+
+ for (txp = sc->cbl_first; sc->tx_queued &&
+ (txp->cb_status & FXP_CB_STATUS_C) != 0;
+ txp = txp->next) {
+ if (txp->mb_head != NULL) {
+ m_freem(txp->mb_head);
+ txp->mb_head = NULL;
+ }
+ sc->tx_queued--;
+ }
+ sc->cbl_first = txp;
+ ifp->if_timer = 0;
+ if (sc->tx_queued == 0) {
+ if (sc->need_mcsetup)
+ fxp_mc_setup(sc);
+ }
+ /*
+ * Try to start more packets transmitting.
+ */
+ if (ifp->if_snd.ifq_head != NULL)
+ fxp_start(ifp);
+ }
+ /*
* Process receiver interrupts. If a no-resource (RNR)
* condition exists, get whatever packets we can and
* re-start the receiver.
@@ -1003,33 +1047,6 @@ rcvloop:
FXP_SCB_COMMAND_RU_START);
}
}
- /*
- * Free any finished transmit mbuf chains.
- */
- if (statack & FXP_SCB_STATACK_CNA) {
- struct fxp_cb_tx *txp;
-
- for (txp = sc->cbl_first; sc->tx_queued &&
- (txp->cb_status & FXP_CB_STATUS_C) != 0;
- txp = txp->next) {
- if (txp->mb_head != NULL) {
- m_freem(txp->mb_head);
- txp->mb_head = NULL;
- }
- sc->tx_queued--;
- }
- sc->cbl_first = txp;
- if (sc->tx_queued == 0) {
- ifp->if_timer = 0;
- if (sc->need_mcsetup)
- fxp_mc_setup(sc);
- }
- /*
- * Try to start more packets transmitting.
- */
- if (ifp->if_snd.ifq_head != NULL)
- fxp_start(ifp);
- }
}
#if defined(__NetBSD__)
return (claimed);
@@ -1262,7 +1279,7 @@ fxp_init(xsc)
cbp->dma_bce = 0; /* (disable) dma max counters */
cbp->late_scb = 0; /* (don't) defer SCB update */
cbp->tno_int = 0; /* (disable) tx not okay interrupt */
- cbp->ci_int = 0; /* interrupt on CU not active */
+ cbp->ci_int = 1; /* interrupt on CU idle */
cbp->save_bf = prm; /* save bad frames */
cbp->disc_short_rx = !prm; /* discard short packets */
cbp->underrun_retry = 1; /* retry mode (1) on DMA underrun */
@@ -1691,7 +1708,7 @@ fxp_ioctl(ifp, command, data)
*
* We have an artificial restriction that the multicast setup command
* must be the first command in the chain, so we take steps to ensure
- * that. By requiring this, it allows us to keep the performance of
+ * this. By requiring this, it allows us to keep up the performance of
* the pre-initialized command ring (esp. link pointers) by not actually
* inserting the mcsetup command in the ring - i.e. its link pointer
* points to the TxCB ring, but the mcsetup descriptor itself is not part
@@ -1709,8 +1726,49 @@ fxp_mc_setup(sc)
struct ifmultiaddr *ifma;
int nmcasts;
+ /*
+ * If there are queued commands, we must wait until they are all
+ * completed. If we are already waiting, then add a NOP command
+ * with interrupt option so that we're notified when all commands
+ * have been completed - fxp_start() ensures that no additional
+ * TX commands will be added when need_mcsetup is true.
+ */
if (sc->tx_queued) {
+ struct fxp_cb_tx *txp;
+
+ /*
+ * need_mcsetup will be true if we are already waiting for the
+ * NOP command to be completed (see below). In this case, bail.
+ */
+ if (sc->need_mcsetup)
+ return;
sc->need_mcsetup = 1;
+
+ /*
+ * Add a NOP command with interrupt so that we are notified when all
+ * TX commands have been processed.
+ */
+ txp = sc->cbl_last->next;
+ txp->mb_head = NULL;
+ txp->cb_status = 0;
+ txp->cb_command = FXP_CB_COMMAND_NOP | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
+ /*
+ * Advance the end of list forward.
+ */
+ sc->cbl_last->cb_command &= ~FXP_CB_COMMAND_S;
+ sc->cbl_last = txp;
+ sc->tx_queued++;
+ /*
+ * Issue a resume in case the CU has just suspended.
+ */
+ fxp_scb_wait(sc);
+ CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_RESUME);
+ /*
+ * Set a 5 second timer just in case we don't hear from the
+ * card again.
+ */
+ ifp->if_timer = 5;
+
return;
}
sc->need_mcsetup = 0;
@@ -1721,7 +1779,7 @@ fxp_mc_setup(sc)
mcsp->next = sc->cbl_base;
mcsp->mb_head = NULL;
mcsp->cb_status = 0;
- mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S;
+ mcsp->cb_command = FXP_CB_COMMAND_MCAS | FXP_CB_COMMAND_S | FXP_CB_COMMAND_I;
mcsp->link_addr = vtophys(&sc->cbl_base->cb_status);
nmcasts = 0;
@@ -1758,6 +1816,6 @@ fxp_mc_setup(sc)
CSR_WRITE_4(sc, FXP_CSR_SCB_GENERAL, vtophys(&mcsp->cb_status));
CSR_WRITE_1(sc, FXP_CSR_SCB_COMMAND, FXP_SCB_COMMAND_CU_START);
- ifp->if_timer = 5;
+ ifp->if_timer = 2;
return;
}
OpenPOWER on IntegriCloud