6753387 No ereports sent to root domains after rebooting control and panicking both root domains
authorMike Christensen <Michael.Christensen@Sun.COM>
Sun, 12 Oct 2008 13:47:48 -0700
changeset 7813 77e203749aae
parent 7812 f4514b3ad5a2
child 7814 fba22078b05d
6753387 No ereports sent to root domains after rebooting control and panicking both root domains 6753970 libds corrupts memory in fmd etm module 6756916 vlds driver doesn't throttle misbehaving clients 6756919 libds entry missing from usr/src/Makefile.lint
usr/src/Makefile.lint
usr/src/lib/libds/Makefile.com
usr/src/lib/libds/common/libds.c
usr/src/uts/sun4v/io/vlds.c
--- a/usr/src/Makefile.lint	Sat Oct 11 18:43:57 2008 -0700
+++ b/usr/src/Makefile.lint	Sun Oct 12 13:47:48 2008 -0700
@@ -461,6 +461,7 @@
 	cmd/prtfru \
 	cmd/sckmd \
 	cmd/vntsd \
+	lib/libds \
 	lib/libdscp \
 	lib/libpri \
 	lib/libpcp \
--- a/usr/src/lib/libds/Makefile.com	Sat Oct 11 18:43:57 2008 -0700
+++ b/usr/src/lib/libds/Makefile.com	Sun Oct 12 13:47:48 2008 -0700
@@ -44,11 +44,11 @@
 LDLIBS += -lsysevent -lnvpair -lc
 
 LINTFLAGS = -msux
-LINTFLAGS64 = -msux -Xarch=$(MACH64:sparcv9=v9)
+LINTFLAGS64 = -msux -m64
 
 $(LINTLIB) := SRCS = $(LINTSRC:%=$(SRCDIR)/%)
 $(LINTLIB) := LINTFLAGS = -nsvx -I$(ROOT)/usr/platform/sun4v/include
-$(LINTLIB) := LINTFLAGS64 = -nsvx -Xarch=$(MACH64:sparcv9=v9) \
+$(LINTLIB) := LINTFLAGS64 = -nsvx -m64 \
 	-I$(ROOT)/usr/platform/sun4v/include
 
 .KEEP_STATE:
--- a/usr/src/lib/libds/common/libds.c	Sat Oct 11 18:43:57 2008 -0700
+++ b/usr/src/lib/libds/common/libds.c	Sun Oct 12 13:47:48 2008 -0700
@@ -43,10 +43,14 @@
 typedef struct dslibentry {
 	ds_hdl_t dsl_hdl;
 	uint32_t dsl_flags;
+	uint32_t dsl_tflags;
 	char *dsl_service;
 	ds_ops_t dsl_ops;
 } dslibentry_t;
 
+/* dsl_tflags */
+#define	DSL_ENTRY_INUSE		0x0001	/* handle is currently active */
+
 #define	MIN_DSLIB_ENTRIES	64
 static dslibentry_t *dslibtab;
 static int ndslib;
@@ -69,6 +73,8 @@
  * Static functions internal to dslib.
  */
 static dslibentry_t *ds_hdl_to_dslibentry(ds_hdl_t hdl);
+static dslibentry_t *ds_new_dslibentry(void);
+static uint_t ds_service_count(char *service, boolean_t is_client);
 static dslibentry_t *ds_lookup_dslibentry(char *service, boolean_t is_client);
 static dslibentry_t *ds_register_dslibentry(ds_hdl_t hdl, char *service,
     boolean_t is_client);
@@ -110,6 +116,24 @@
 	return (dsp);
 }
 
+static uint_t
+ds_service_count(char *service, boolean_t is_client)
+{
+	int i;
+	dslibentry_t *dsp;
+	uint_t is_client_flag = is_client ? VLDS_REG_CLIENT : 0;
+	uint_t count = 0;
+
+	for (i = 0, dsp = dslibtab; i < ndslib; i++, dsp++) {
+		if (dsp->dsl_hdl != NULL &&
+		    strcmp(dsp->dsl_service, service) == 0 &&
+		    (dsp->dsl_flags & VLDS_REG_CLIENT) == is_client_flag) {
+			count++;
+		}
+	}
+	return (count);
+}
+
 static dslibentry_t *
 ds_lookup_dslibentry(char *service, boolean_t is_client)
 {
@@ -131,36 +155,29 @@
 ds_register_dslibentry(ds_hdl_t hdl, char *service, boolean_t is_client)
 {
 	dslibentry_t *dsp, *orig_dsp;
-	uint_t nhdls;
 
-	if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL)
+	if ((dsp = ds_hdl_to_dslibentry(hdl)) != NULL) {
+		dsp->dsl_tflags |= DSL_ENTRY_INUSE;
 		return (dsp);
+	}
 
 	if ((orig_dsp = ds_lookup_dslibentry(service, is_client)) == NULL) {
 		return (NULL);
 	}
 
-	/*
-	 * Find out if we have 1 or 2 or more handles.  Having one implies
-	 * that we can reuse the current one handle for this service.
-	 * Having two or more implies we need to allocate a new handle.
-	 */
-	if (ds_hdl_lookup(service, is_client, NULL, 2, &nhdls) != 0)
+	if ((orig_dsp->dsl_tflags & DSL_ENTRY_INUSE) == 0) {
+		/* use the original structure entry */
+		orig_dsp->dsl_tflags |= DSL_ENTRY_INUSE;
+		orig_dsp->dsl_hdl = hdl;
+		return (orig_dsp);
+	}
+
+	/* allocate a new structure entry */
+	if ((dsp = ds_new_dslibentry()) == NULL)
 		return (NULL);
 
-	if (nhdls == 1) {
-		/* reuse the original structure entry */
-		dsp = orig_dsp;
-	} else if (nhdls == 2) {
-		/* allocate a new structure entry */
-		if ((dsp = ds_new_dslibentry()) == NULL)
-			return (NULL);
-		*dsp = *orig_dsp;
-		dsp->dsl_service = strdup(orig_dsp->dsl_service);
-	} else {
-		/* can't happen... */
-		return (NULL);
-	}
+	*dsp = *orig_dsp;
+	dsp->dsl_service = strdup(orig_dsp->dsl_service);
 	dsp->dsl_hdl = hdl;
 	return (dsp);
 }
@@ -175,22 +192,22 @@
 	uint_t nhdls;
 
 	/*
-	 * Find out if we have 1 or 2 or more handles.  Having one implies
-	 * that we want to leave the entry alone unless this is a ds_unreg_hdl
+	 * Find out if we have 1 or 2 or more handles for the given
+	 * service.  Having one implies that we want to leave the entry
+	 * intact but marked as not in use unless this is a ds_unreg_hdl
 	 * (force_unreg is true).
 	 */
-	if (ds_hdl_lookup(dsp->dsl_service,
-	    (dsp->dsl_flags & VLDS_REG_CLIENT) != 0, NULL, 2, &nhdls) != 0) {
-		/* should never happen */
-		return;
-	}
+	nhdls = ds_service_count(dsp->dsl_service,
+	    (dsp->dsl_flags & VLDS_REG_CLIENT) != 0);
 
-	if ((nhdls == 1 && force_unreg) || nhdls == 2) {
+	if ((nhdls == 1 && force_unreg) || nhdls >= 2) {
 		dsp->dsl_hdl = NULL;
 		if (dsp->dsl_service) {
 			free(dsp->dsl_service);
 		}
 		(void) memset(dsp, 0, sizeof (dslibentry_t));
+	} else if (nhdls == 1) {
+		dsp->dsl_tflags &= ~DSL_ENTRY_INUSE;
 	}
 }
 
@@ -316,7 +333,8 @@
 		return (errno);
 
 	if (dslibtab == NULL) {
-		if ((dslibtab = malloc(sizeof (dslibentry_t) * ndslib)) == NULL)
+		dslibtab = malloc(sizeof (dslibentry_t) * MIN_DSLIB_ENTRIES);
+		if (dslibtab == NULL)
 			return (errno = ENOMEM);
 		ndslib = MIN_DSLIB_ENTRIES;
 		(void) memset(dslibtab, 0, sizeof (dslibentry_t) * ndslib);
@@ -415,6 +433,7 @@
 	 */
 	dsp->dsl_hdl = hdl;
 	dsp->dsl_flags = flags;
+	dsp->dsl_tflags = 0;
 	dsp->dsl_service = strdup(cap->svc_id);
 	dsp->dsl_ops = *ops;
 	(void) mutex_unlock(&dslib_lock);
--- a/usr/src/uts/sun4v/io/vlds.c	Sat Oct 11 18:43:57 2008 -0700
+++ b/usr/src/uts/sun4v/io/vlds.c	Sun Oct 12 13:47:48 2008 -0700
@@ -77,6 +77,7 @@
 	vlds_recv_hdr_t	*recv_headp;	/* ptr to head of recv queue */
 	vlds_recv_hdr_t	*recv_tailp;	/* ptr to tail of recv queue */
 	size_t		recv_size;	/* no. of bytes in recv queue */
+	uint_t		recv_cnt;	/* no of messages in recv queue */
 	kmutex_t	recv_lock;	/* lock for recv queue */
 	kcondvar_t	recv_cv;	/* condition variable for recv queue */
 	int		recv_nreaders;	/* no of currently waiting readers */
@@ -84,6 +85,7 @@
 
 #define	VLDS_RECV_OK		1
 #define	VLDS_RECV_UNREG_PENDING	2
+#define	VLDS_RECV_OVERFLOW	3
 
 static int vlds_ports_inited = 0;
 
@@ -179,6 +181,9 @@
 	NULL				/* ds_ucap_init will fill in */
 };
 
+static size_t vlds_recvq_maxsize = DS_STREAM_MTU * 8;
+static uint_t vlds_recvq_maxmsg = 16;
+
 #define	VLDS_MINOR_MAX			SHRT_MAX
 
 /* Definitions for binding handle array */
@@ -1393,6 +1398,7 @@
 	dpsp->recv_headp = NULL;
 	dpsp->recv_tailp = NULL;
 	dpsp->recv_size = 0;
+	dpsp->recv_cnt = 0;
 }
 
 static void
@@ -1400,6 +1406,7 @@
 {
 	ASSERT(dpsp->state == VLDS_RECV_UNREG_PENDING);
 	ASSERT(dpsp->recv_size == 0);
+	ASSERT(dpsp->recv_cnt == 0);
 	ASSERT(dpsp->recv_headp == NULL);
 	ASSERT(dpsp->recv_tailp == NULL);
 
@@ -1418,8 +1425,17 @@
 
 	mutex_enter(&dpsp->recv_lock);
 	while (dpsp->recv_size == 0) {
+		ASSERT(dpsp->recv_cnt == 0);
 		if (dpsp->state == VLDS_RECV_UNREG_PENDING)
 			break;
+
+		if (dpsp->state == VLDS_RECV_OVERFLOW) {
+			DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow",
+			    __func__);
+			dpsp->state = VLDS_RECV_OK;
+			mutex_exit(&dpsp->recv_lock);
+			return (ENOBUFS);
+		}
 		/*
 		 * Passing in a buflen of 0 allows user to poll for msgs.
 		 */
@@ -1462,7 +1478,9 @@
 		dpsp->recv_headp = rhp->next;
 		ASSERT(dpsp->recv_headp != NULL);
 	}
+	ASSERT(dpsp->recv_cnt > 0);
 	dpsp->recv_size -= rhp->datasz;
+	dpsp->recv_cnt -= 1;
 	mutex_exit(&dpsp->recv_lock);
 
 	msglen = rhp->datasz;
@@ -1503,6 +1521,7 @@
 	dpsp->recv_headp = NULL;
 	dpsp->recv_tailp = NULL;
 	dpsp->recv_size = 0;
+	dpsp->recv_cnt = 0;
 
 	/*
 	 * Make sure other readers have exited.
@@ -1524,6 +1543,26 @@
 
 	mutex_enter(&dpsp->recv_lock);
 	if (dpsp->state != VLDS_RECV_UNREG_PENDING) {
+		/*
+		 * If we've already encountered an overflow, or there
+		 * are pending messages and either queue size and
+		 * message limits will be exceeded with this message,
+		 * we mark the recvq as overflowed and return an ENOBUFS
+		 * error.  This allows the enqueuing of one big message
+		 * or several little messages.
+		 */
+		if ((dpsp->state == VLDS_RECV_OVERFLOW) ||
+		    ((dpsp->recv_cnt != 0) &&
+		    ((dpsp->recv_size + buflen) > vlds_recvq_maxsize) ||
+		    ((dpsp->recv_cnt + 1) > vlds_recvq_maxmsg))) {
+			DS_DBG_RCVQ(CE_NOTE, "%s: user data queue overflow",
+			    __func__);
+			dpsp->state = VLDS_RECV_OVERFLOW;
+			cv_broadcast(&dpsp->recv_cv);
+			mutex_exit(&dpsp->recv_lock);
+			return (ENOBUFS);
+		}
+
 		DS_DBG_RCVQ(CE_NOTE, "%s: user data enqueued msglen: %ld",
 		    __func__, buflen);
 		DS_DUMP_MSG(DS_DBG_FLAG_RCVQ, buf, buflen);
@@ -1540,6 +1579,7 @@
 			dpsp->recv_tailp = rhp;
 		}
 		dpsp->recv_size += rhp->datasz;
+		dpsp->recv_cnt += 1;
 		cv_broadcast(&dpsp->recv_cv);
 	}
 	mutex_exit(&dpsp->recv_lock);