changeset 3952:e932b8e8ca41

6494669 N2 NIU leaf driver: hot lock in nxge_start affecting specweb performance
author ml29623
date Mon, 02 Apr 2007 18:15:26 -0700
parents 7c819a7ade91
children f116008e2db3
files usr/src/uts/common/Makefile.files usr/src/uts/common/io/nxge/nxge_main.c usr/src/uts/common/io/nxge/nxge_send.c usr/src/uts/common/io/nxge/nxge_serialize.c usr/src/uts/common/io/nxge/nxge_txdma.c usr/src/uts/common/sys/nxge/nxge_serialize.h usr/src/uts/common/sys/nxge/nxge_txdma.h
diffstat 7 files changed, 609 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/Makefile.files	Mon Apr 02 17:39:35 2007 -0700
+++ b/usr/src/uts/common/Makefile.files	Mon Apr 02 18:15:26 2007 -0700
@@ -1379,7 +1379,8 @@
 		nxge_hw.o nxge_fzc.o nxge_virtual.o		\
 		nxge_send.o nxge_classify.o nxge_fflp.o		\
 		nxge_fflp_hash.o nxge_ndd.o nxge_kstats.o	\
-		nxge_zcp.o nxge_fm.o nxge_espc.o
+		nxge_zcp.o nxge_fm.o nxge_espc.o		\
+		nxge_serialize.o
 
 NXGE_NPI_OBJS =	\
 		npi.o npi_mac.o	npi_ipp.o			\
--- a/usr/src/uts/common/io/nxge/nxge_main.c	Mon Apr 02 17:39:35 2007 -0700
+++ b/usr/src/uts/common/io/nxge/nxge_main.c	Mon Apr 02 18:15:26 2007 -0700
@@ -56,6 +56,7 @@
 boolean_t	nxge_jumbo_enable = B_FALSE;
 uint16_t	nxge_rcr_timeout = NXGE_RDC_RCR_TIMEOUT;
 uint16_t	nxge_rcr_threshold = NXGE_RDC_RCR_THRESHOLD;
+nxge_tx_mode_t	nxge_tx_scheme = NXGE_USE_SERIAL;
 
 /*
  * Debugging flags:
@@ -2282,6 +2283,7 @@
 	size_t			tx_buf_alloc_size;
 	size_t			tx_cntl_alloc_size;
 	uint32_t		*num_chunks; /* per dma */
+	uint32_t		bcopy_thresh;
 
 	NXGE_DEBUG_MSG((nxgep, MEM_CTL, "==> nxge_alloc_tx_mem_pool"));
 
@@ -2330,7 +2332,12 @@
 	 * (For packet payload over this limit, packets will not be
 	 *  copied.)
 	 */
-	tx_buf_alloc_size = (nxge_bcopy_thresh * nxge_tx_ring_size);
+	if (nxgep->niu_type == N2_NIU) {
+		bcopy_thresh = TX_BCOPY_SIZE;
+	} else {
+		bcopy_thresh = nxge_bcopy_thresh;
+	}
+	tx_buf_alloc_size = (bcopy_thresh * nxge_tx_ring_size);
 
 	/*
 	 * Addresses of transmit descriptor ring and the
@@ -2378,7 +2385,7 @@
 		num_chunks[i] = 0;
 		status = nxge_alloc_tx_buf_dma(nxgep, st_tdc, &dma_buf_p[i],
 					tx_buf_alloc_size,
-					nxge_bcopy_thresh, &num_chunks[i]);
+					bcopy_thresh, &num_chunks[i]);
 		if (status != NXGE_OK) {
 			break;
 		}
--- a/usr/src/uts/common/io/nxge/nxge_send.c	Mon Apr 02 17:39:35 2007 -0700
+++ b/usr/src/uts/common/io/nxge/nxge_send.c	Mon Apr 02 18:15:26 2007 -0700
@@ -38,6 +38,7 @@
 extern uint32_t		nxge_tx_use_bcopy;
 extern uint32_t		nxge_tx_lb_policy;
 extern uint32_t		nxge_no_tx_lb;
+extern nxge_tx_mode_t	nxge_tx_scheme;
 
 typedef struct _mac_tx_hint {
 	uint16_t	sap;
@@ -223,7 +224,7 @@
 	 * for header. No padding will be used.
 	 */
 	pkt_len = pack_len = boff = TX_PKT_HEADER_SIZE;
-	if (nxge_tx_use_bcopy) {
+	if (nxge_tx_use_bcopy && (nxgep->niu_type != N2_NIU)) {
 		bcopy_thresh = (nxge_bcopy_thresh - TX_PKT_HEADER_SIZE);
 	} else {
 		bcopy_thresh = (TX_BCOPY_SIZE - TX_PKT_HEADER_SIZE);
@@ -841,11 +842,21 @@
 	return (status);
 }
 
+int
+nxge_serial_tx(mblk_t *mp, void *arg)
+{
+	p_tx_ring_t		tx_ring_p = (p_tx_ring_t)arg;
+	p_nxge_t		nxgep = tx_ring_p->nxgep;
+
+	return (nxge_start(nxgep, tx_ring_p, mp));
+}
+
 boolean_t
 nxge_send(p_nxge_t nxgep, mblk_t *mp, p_mac_tx_hint_t hp)
 {
 	p_tx_ring_t 		*tx_rings;
 	uint8_t			ring_index;
+	p_tx_ring_t		tx_ring_p;
 
 	NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_send"));
 
@@ -858,10 +869,20 @@
 	NXGE_DEBUG_MSG((nxgep, TX_CTL, "==> nxge_tx_msg: max_tdcs %d "
 		"ring_index %d", nxgep->max_tdcs, ring_index));
 
-	if (nxge_start(nxgep, tx_rings[ring_index], mp)) {
-		NXGE_DEBUG_MSG((nxgep, TX_CTL, "<== nxge_send: failed "
-			"ring index %d", ring_index));
-		return (B_FALSE);
+	switch (nxge_tx_scheme) {
+	case NXGE_USE_START:
+		if (nxge_start(nxgep, tx_rings[ring_index], mp)) {
+			NXGE_DEBUG_MSG((nxgep, TX_CTL, "<== nxge_send: failed "
+				"ring index %d", ring_index));
+			return (B_FALSE);
+		}
+		break;
+
+	case NXGE_USE_SERIAL:
+	default:
+		tx_ring_p = tx_rings[ring_index];
+		nxge_serialize_enter(tx_ring_p->serial, mp);
+		break;
 	}
 
 	NXGE_DEBUG_MSG((nxgep, TX_CTL, "<== nxge_send: ring index %d",
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/io/nxge/nxge_serialize.c	Mon Apr 02 18:15:26 2007 -0700
@@ -0,0 +1,406 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+#include <sys/nxge/nxge_impl.h>
+#include <sys/proc.h>
+
+uint32_t nxge_maxhrs = MAXHRS;
+
+extern pri_t maxclsyspri;
+extern proc_t p0;
+
+extern int servicing_interrupt(void);
+extern void bzero(void *, size_t);
+
+#ifdef _KERNEL
+static void nxge_onetrack(void *p);
+#else
+static void *nxge_onetrack(void *p);
+#endif
+
+static int nxge_serial_put(nxge_serialize_t *, void *);
+static int nxge_serial_getn(nxge_serialize_t *, mblk_t **, mblk_t **);
+static void nxge_serial_ungetn(nxge_serialize_t *, mblk_t *, mblk_t *, int);
+static int nxge_freelance(nxge_serialize_t *);
+static caddr_t nxge_tx_s_begin(nxge_serialize_t *);
+static void nxge_tx_s_end(nxge_serialize_t *);
+
+nxge_serialize_t *
+nxge_serialize_create(int length, onetrack_t *proc, void *cookie)
+{
+	nxge_serialize_t *p;
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"==> nxge_serialize_create:"));
+
+	p = (nxge_serialize_t *)kmem_alloc(sizeof (nxge_serialize_t), KM_SLEEP);
+	mutex_init(&p->lock, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&p->serial, NULL, MUTEX_DEFAULT, NULL);
+	mutex_init(&p->timelock, NULL, MUTEX_DEFAULT, NULL);
+	cv_init(&p->serial_cv, NULL, CV_DRIVER, NULL);
+	cv_init(&p->timecv, NULL, CV_DRIVER, NULL);
+	p->count = 0;
+	p->cookie = cookie;
+	p->serialop = proc;
+	p->owned = 0;
+	p->head = NULL;
+	p->tail = NULL;
+	p->totaltime = 0;
+	p->totalcount = 0;
+	/*
+	 * An initial estimate of the avg time spent in the serializer function.
+	 * Any non-zero value is fine. A large value will induce unnecessary
+	 * delays.
+	 */
+	p->avg = 1;
+	p->length = length;
+	p->s_state = NXGE_TX_STHREAD_RUNNING;
+
+	p->tx_sthread = thread_create(NULL, 0,
+			nxge_onetrack, p, 0, &p0, TS_RUN, maxclsyspri);
+	if (p->tx_sthread == NULL) {
+		cv_destroy(&p->serial_cv);
+		cv_destroy(&p->timecv);
+		mutex_destroy(&p->lock);
+		mutex_destroy(&p->serial);
+		mutex_destroy(&p->timelock);
+		kmem_free(p, sizeof (nxge_serialize_t));
+
+		NXGE_DEBUG_MSG((NULL, TX_CTL,
+			"<== nxge_serialize_create: (NULL)"));
+
+		return (NULL);
+	}
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"<== nxge_serialize_create: s %p thread %p",
+		p, p->tx_sthread));
+
+	return (p);
+}
+
+void
+nxge_serialize_destroy(nxge_serialize_t *p)
+{
+	int n, i;
+	mblk_t *mp, *nmp, *t;
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"==> nxge_serialize_destroy: s %p", p));
+	if (p == NULL) {
+		NXGE_DEBUG_MSG((NULL, TX_CTL,
+			"<== nxge_serialize_destroy:"));
+		return;
+	}
+
+	mutex_enter(&p->serial);
+	p->s_state |= NXGE_TX_STHREAD_DESTROY;
+	cv_signal(&p->serial_cv);
+	cv_signal(&p->timecv);
+	while (p->s_state & NXGE_TX_STHREAD_DESTROY) {
+		cv_wait(&p->serial_cv, &p->serial);
+		NXGE_DEBUG_MSG((NULL, TX_CTL,
+			"==> nxge_serialize_destroy: s %p state %d",
+			p, p->s_state));
+		if (p->s_state & NXGE_TX_STHREAD_EXIT) {
+			break;
+		}
+	}
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"==> nxge_serialize_destroy: s %p state %d",
+		p, p->s_state));
+
+	n = nxge_serial_getn(p, &mp, &t);
+	for (i = 0; i < n; i++) {
+		NXGE_DEBUG_MSG((NULL, TX_CTL,
+		    "==> nxge_serialize_destroy: s %p mp %p", p, mp));
+
+		nmp = mp->b_next;
+		mp->b_next = NULL;
+		freemsg(mp);
+
+		mp = nmp;
+	}
+
+	mutex_exit(&p->serial);
+
+	cv_destroy(&p->serial_cv);
+	cv_destroy(&p->timecv);
+	mutex_destroy(&p->lock);
+	mutex_destroy(&p->serial);
+	mutex_destroy(&p->timelock);
+	kmem_free(p, sizeof (nxge_serialize_t));
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"<== nxge_serialize_destroy: s %p", p));
+}
+
+/*
+ * Return values:
+ * 0 means put succeeded
+ * 1 means we have exclusive access
+ */
+static int
+nxge_serial_put(nxge_serialize_t *p, void *mp)
+{
+	mblk_t *t;
+	int r = 0;
+	int block = 0;
+	hrtime_t tns;
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"==> nxge_serial_put: s %p mp %p", p, mp));
+
+	mutex_enter(&p->lock);
+	/*
+	 * If the time required to drain all the queued up packets
+	 * is greater than a tick, we need to block.
+	 */
+	if ((tns = (p->count++) * p->avg) > NXGE_TX_AVG_CNT) {
+		/*
+		 * Sanity check that we will sleep only for less than ~a second
+		 */
+		if (tns > ONESEC) {
+			p->count--;
+			mutex_exit(&p->lock);
+			freemsg(mp);
+			return (0);
+		}
+		block = 1;
+	}
+	if (p->owned == 0) {
+		r = p->owned = 1;
+		block = 0;
+	}
+	if ((t = p->tail) != NULL) {
+		t->b_next = mp;
+		p->tail = mp;
+	} else {
+		p->head = p->tail = mp;
+	}
+	mutex_exit(&p->lock);
+
+	/*
+	 * Block for the number of ticks required to drain half
+	 * the queued up packets - but only if we are not within
+	 * an interrupt thread.
+	 */
+	if (block) {
+		if (!servicing_interrupt()) {
+			long wait = lbolt + drv_usectohz(tns/NXGE_TX_AVG_RES);
+			mutex_enter(&p->timelock);
+			(void) cv_timedwait(&p->timecv, &p->timelock, wait);
+			mutex_exit(&p->timelock);
+		}
+	}
+
+
+	return (r);
+}
+
+static int
+nxge_serial_getn(nxge_serialize_t *p, mblk_t **head, mblk_t **tail)
+{
+	int c;
+
+	mutex_enter(&p->lock);
+	if ((c = p->count) != 0) {
+		*head = p->head;
+		*tail = p->tail;
+		p->head = p->tail = NULL;
+		p->count = 0;
+	} else {
+		p->owned = 0;
+	}
+	mutex_exit(&p->lock);
+
+	return (c);
+}
+
+static void
+nxge_serial_ungetn(nxge_serialize_t *p, mblk_t *head, mblk_t *tail, int n)
+{
+	mutex_enter(&p->lock);
+	if (p->tail != NULL) {
+		tail->b_next = p->head;
+		p->head = head;
+	} else {
+		p->head = head;
+		p->tail = tail;
+	}
+	p->count += n;
+	mutex_exit(&p->lock);
+}
+
+#ifdef _KERNEL
+static void
+#else
+static void *
+#endif
+nxge_onetrack(void *s)
+{
+	int		k, i;
+	mblk_t		*mp, *ignore;
+	nxge_serialize_t *p = (nxge_serialize_t *)s;
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"==> nxge_onetrack: s %p", s));
+	(void) nxge_tx_s_begin(p);
+	mutex_enter(&p->serial);
+	while (p->s_state & NXGE_TX_STHREAD_RUNNING) {
+		CALLB_CPR_SAFE_BEGIN(&p->s_cprinfo);
+		if (p->s_state & NXGE_TX_STHREAD_DESTROY) {
+			break;
+		}
+		cv_wait(&p->serial_cv, &p->serial);
+		if (p->s_state & NXGE_TX_STHREAD_DESTROY) {
+			break;
+		}
+		CALLB_CPR_SAFE_END(&p->s_cprinfo,
+					&p->serial)
+		while (k = nxge_serial_getn(p, &mp, &ignore)) {
+			hrtime_t t0 = gethrtime();
+			for (i = 0; i < k; i++) {
+				mblk_t *n = mp->b_next;
+				mp->b_next = NULL;
+
+				NXGE_DEBUG_MSG((NULL, TX_CTL,
+				    "==> nxge_onetrack: s %p mp %p", s, mp));
+
+				/*
+				 * The queue is full, block and wait for half of
+				 * it to drain.
+				 */
+				while (p->serialop(mp, p->cookie)) {
+					hrtime_t tns = p->avg * p->length;
+					long wait = lbolt +
+					    drv_usectohz(tns / NXGE_TX_AVG_RES);
+					(void) cv_timedwait(&p->timecv,
+					    &p->serial, wait);
+				}
+				mp = n;
+			}
+
+			ASSERT(mp == NULL);
+
+			/*
+			 * Update the total time and count of the serializer
+			 * function and * generate the avg time required to
+			 * process a packet.
+			 */
+			p->totaltime += (gethrtime() - t0);
+			p->totalcount += k;
+			p->avg = p->totaltime/p->totalcount;
+		}
+	}
+
+	mutex_exit(&p->serial);
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"<== nxge_onetrack: s %p", s));
+	nxge_tx_s_end(s);
+}
+
+
+/*
+ * Return values:
+ * 0 : don't need to signal worker
+ * 1 : worker needs to be signalled
+ */
+static int
+nxge_freelance(nxge_serialize_t *s)
+{
+	int i, n, c = 0;
+	mblk_t *mp, *t;
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"==> nxge_freelance: s %p", s));
+	while (n = nxge_serial_getn(s, &mp, &t)) {
+		if ((n > nxge_maxhrs) || ((c += n) > nxge_maxhrs)) {
+			nxge_serial_ungetn(s, mp, t, n);
+			return (1);
+		}
+		for (i = 0; i < n; i++) {
+			mblk_t *next = mp->b_next;
+			mp->b_next = NULL;
+			if (s->serialop(mp, s->cookie)) {
+				mp->b_next = next;
+				nxge_serial_ungetn(s, mp, t, n - i);
+				return (1);
+			}
+
+			NXGE_DEBUG_MSG((NULL, TX_CTL,
+				"==> nxge_freelance: s %p mp %p", s, mp));
+
+			mp = next;
+		}
+	}
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"<== nxge_freelance: s %p", s));
+
+	return (0);
+}
+
+void
+nxge_serialize_enter(nxge_serialize_t *s, mblk_t *mp)
+{
+	if (nxge_serial_put(s, mp)) {
+		if (nxge_freelance(s)) {
+			mutex_enter(&s->serial);
+			cv_signal(&s->serial_cv);
+			mutex_exit(&s->serial);
+		}
+	}
+}
+
+static caddr_t
+nxge_tx_s_begin(nxge_serialize_t *s)
+{
+	CALLB_CPR_INIT(&s->s_cprinfo, &s->serial,
+			callb_generic_cpr, "nxge_tx_serialize");
+	return (s->cookie);
+}
+
+static void
+nxge_tx_s_end(nxge_serialize_t *s)
+{
+	callb_cpr_t cprinfo;
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"==> nxge_tx_s_end: s %p", s));
+	cprinfo = s->s_cprinfo;
+	mutex_enter(&s->serial);
+	s->s_state |= NXGE_TX_STHREAD_EXIT;
+	cv_signal(&s->serial_cv);
+
+	CALLB_CPR_EXIT(&cprinfo);
+
+	NXGE_DEBUG_MSG((NULL, TX_CTL,
+		"<== nxge_tx_s_end: s %p", s));
+
+	thread_exit();
+}
--- a/usr/src/uts/common/io/nxge/nxge_txdma.c	Mon Apr 02 17:39:35 2007 -0700
+++ b/usr/src/uts/common/io/nxge/nxge_txdma.c	Mon Apr 02 18:15:26 2007 -0700
@@ -51,6 +51,8 @@
 extern ddi_dma_attr_t nxge_desc_dma_attr;
 extern ddi_dma_attr_t nxge_tx_dma_attr;
 
+extern int nxge_serial_tx(mblk_t *mp, void *arg);
+
 static nxge_status_t nxge_map_txdma(p_nxge_t);
 static void nxge_unmap_txdma(p_nxge_t);
 
@@ -2251,6 +2253,10 @@
 		KMEM_ZALLOC(sizeof (tx_ring_t), KM_SLEEP);
 	MUTEX_INIT(&tx_ring_p->lock, NULL, MUTEX_DRIVER,
 		(void *)nxgep->interrupt_cookie);
+
+	tx_ring_p->nxgep = nxgep;
+	tx_ring_p->serial = nxge_serialize_create(nmsgs,
+				nxge_serial_tx, tx_ring_p);
 	/*
 	 * Allocate transmit message rings and handles for packets
 	 * not to be copied to premapped buffers.
@@ -2334,6 +2340,11 @@
 	goto nxge_map_txdma_channel_buf_ring_exit;
 
 nxge_map_txdma_channel_buf_ring_fail1:
+	if (tx_ring_p->serial) {
+		nxge_serialize_destroy(tx_ring_p->serial);
+		tx_ring_p->serial = NULL;
+	}
+
 	index--;
 	for (; index >= 0; index--) {
 		if (tx_msg_ring[i].dma_handle != NULL) {
@@ -2409,6 +2420,11 @@
 		}
 	}
 
+	if (tx_ring_p->serial) {
+		nxge_serialize_destroy(tx_ring_p->serial);
+		tx_ring_p->serial = NULL;
+	}
+
 	MUTEX_DESTROY(&tx_ring_p->lock);
 	KMEM_FREE(tx_msg_ring, sizeof (tx_msg_t) * tx_ring_p->tx_ring_size);
 	KMEM_FREE(tx_ring_p, sizeof (tx_ring_t));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/usr/src/uts/common/sys/nxge/nxge_serialize.h	Mon Apr 02 18:15:26 2007 -0700
@@ -0,0 +1,142 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#ifndef	_SYS_NXGE_NXGE_SERIALIZE_H
+#define	_SYS_NXGE_NXGE_SERIALIZE_H
+
+#pragma ident	"%Z%%M%	%I%	%E% SMI"
+
+#ifdef	__cplusplus
+extern "C" {
+#endif
+
+#define	NXGE_TX_AVG_CNT		200000000
+#define	NXGE_TX_AVG_RES		2000		/* sleep at least a tick */
+#define	MAXHRS			3		/* # of packets to process */
+#define	ONESEC			1000000000	/* one second */
+
+#ifdef _KERNEL
+
+#include <sys/stream.h>
+#include <sys/mutex.h>
+#include <sys/condvar.h>
+#include <sys/kmem.h>
+#include <sys/ddi.h>
+#include <sys/callb.h>
+
+/*
+ * Thread state flags
+ */
+#define	NXGE_TX_STHREAD_RUNNING	0x0001	/* thread started */
+#define	NXGE_TX_STHREAD_DESTROY	0x0002	/* thread is being destroyed */
+#define	NXGE_TX_STHREAD_EXIT	0x0003	/* thread exits */
+
+#else
+
+static int p0 = 0;
+#define	TS_RUN	0
+static int maxclsyspri = 99;
+
+#include <atomic.h>
+#include <thread.h>
+#include <synch.h>
+#include <assert.h>
+#include <errno.h>
+typedef void mblk_t;
+typedef mutex_t kmutex_t;
+typedef cond_t kcondvar_t;
+
+#define	MUTEX_DEFAULT USYNC_THREAD
+
+#define	mutex_init(a, b, c, d) mutex_init(a, b, c)
+#define	mutex_enter(a) mutex_lock(a)
+#define	mutex_tryenter(a) mutex_trylock(a)
+#define	mutex_exit(a) mutex_unlock(a)
+
+#define	cv_init(c, n, t, m) cond_init(c, USYNC_THREAD, NULL)
+#define	cv_wait(c, m) cond_wait(c, m)
+#define	cv_signal(c) cond_signal(c)
+#define	cv_timedwait(c, m, t) cond_timedwait(c, m, &t)
+
+#define	kmem_alloc(a, b) malloc(a)
+#define	kmem_free(a, b) free(a)
+
+#define	cas32(a, b, c) atomic_cas_32(a, b, c)
+
+#define	thread_create(a, b, c, d, e, f, g, h)	\
+		thr_create(a, b, c, d, THR_BOUND, NULL)
+
+#endif
+
+
+typedef int (onetrack_t)(mblk_t *, void *);
+
+typedef struct {
+	kmutex_t	lock;
+	int		count;
+	mblk_t		*head;
+	mblk_t		*tail;
+	void		*cookie;
+	onetrack_t	*serialop;
+	int		owned;
+	/* Counter tracks the total time spent in serializer function */
+	hrtime_t	totaltime;
+	/*
+	 * Counter tracks the total number of time the serializer
+	 * function was called.
+	 */
+	long		totalcount;
+	/*
+	 * Counter maintains the average time spent in the serializer function
+	 * and is derived as (totaltime/totalcount).
+	 */
+	int		avg;
+	/*
+	 * The lenght of the queue to which the serializer function
+	 * will append data.
+	 */
+	int		length;
+	kcondvar_t	serial_cv;
+	kcondvar_t	timecv;
+	kmutex_t	serial;
+	uint32_t	s_state;
+	boolean_t	s_need_signal;
+	callb_cpr_t 	s_cprinfo;
+	kthread_t 	*tx_sthread;
+	kmutex_t	timelock;
+} nxge_serialize_t;
+
+/*
+ * Prototypes definitions
+ */
+nxge_serialize_t *nxge_serialize_create(int, onetrack_t *, void *);
+void nxge_serialize_destroy(nxge_serialize_t *);
+void nxge_serialize_enter(nxge_serialize_t *, mblk_t *);
+
+#ifdef	__cplusplus
+}
+#endif
+
+#endif	/* _SYS_NXGE_NXGE_SERIALIZE_H */
--- a/usr/src/uts/common/sys/nxge/nxge_txdma.h	Mon Apr 02 17:39:35 2007 -0700
+++ b/usr/src/uts/common/sys/nxge/nxge_txdma.h	Mon Apr 02 18:15:26 2007 -0700
@@ -19,7 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
+ * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  */
 
@@ -34,6 +34,7 @@
 
 #include <sys/nxge/nxge_txdma_hw.h>
 #include <npi_txdma.h>
+#include <sys/nxge/nxge_serialize.h>
 
 #define	TXDMA_PORT_BITMAP(nxgep)		(nxgep->pt_config.tx_dma_map)
 
@@ -67,6 +68,11 @@
 
 #define	TXDMA_DRR_WEIGHT_DEFAULT	0x001f
 
+typedef enum {
+	NXGE_USE_SERIAL	= 0,
+	NXGE_USE_START,
+} nxge_tx_mode_t;
+
 typedef struct _tx_msg_t {
 	nxge_os_block_mv_t 	flags;		/* DMA, BCOPY, DVMA (?) */
 	nxge_os_dma_common_t	buf_dma;	/* premapped buffer blocks */
@@ -163,7 +169,7 @@
 	boolean_t 		queueing;
 
 	nxge_os_mutex_t		sq_lock;
-
+	nxge_serialize_t 	*serial;
 	p_mblk_t 		head;
 	p_mblk_t 		tail;