changeset 9979:006c3455a081

6799660 panic, NULL pointer dereference, ip:ill_capability_ack_thr 6807899 system hang when running dladm delete-vnic 6816455 Panic : assertion failed: (sqp->sq_state & (SQS_DEFAULT|SQS_ILL_BOUND)) == SQS_DEFAULT
author Thirumalai Srinivasan <Thirumalai.Srinivasan@Sun.COM>
date Fri, 26 Jun 2009 12:06:35 -0700
parents ce23a6a98c42
children 13d7f3eec672
files usr/src/uts/common/inet/ip/ip.c usr/src/uts/common/inet/ip/ip_if.c usr/src/uts/common/inet/ip/ip_squeue.c usr/src/uts/common/inet/ip_stack.h usr/src/uts/common/inet/squeue.c
diffstat 5 files changed, 60 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/inet/ip/ip.c	Fri Jun 26 12:35:29 2009 -0500
+++ b/usr/src/uts/common/inet/ip/ip.c	Fri Jun 26 12:06:35 2009 -0700
@@ -5823,7 +5823,6 @@
 
 	mutex_destroy(&ipst->ips_capab_taskq_lock);
 	cv_destroy(&ipst->ips_capab_taskq_cv);
-	list_destroy(&ipst->ips_capab_taskq_list);
 
 	mutex_enter(&ipst->ips_mrt_lock);
 	while (!(ipst->ips_mrt_flags & IP_MRT_DONE))
@@ -6118,8 +6117,6 @@
 	    ill_taskq_dispatch, ipst, 0, &p0, TS_RUN, minclsyspri);
 	mutex_init(&ipst->ips_capab_taskq_lock, NULL, MUTEX_DEFAULT, NULL);
 	cv_init(&ipst->ips_capab_taskq_cv, NULL, CV_DEFAULT, NULL);
-	list_create(&ipst->ips_capab_taskq_list, sizeof (mblk_t),
-	    offsetof(mblk_t, b_next));
 
 	/*
 	 * Create the mcast_restart_timers_thread() worker thread.
--- a/usr/src/uts/common/inet/ip/ip_if.c	Fri Jun 26 12:35:29 2009 -0500
+++ b/usr/src/uts/common/inet/ip/ip_if.c	Fri Jun 26 12:06:35 2009 -0700
@@ -3229,14 +3229,18 @@
 	mutex_enter(&ipst->ips_capab_taskq_lock);
 
 	for (;;) {
-		mp = list_head(&ipst->ips_capab_taskq_list);
+		mp = ipst->ips_capab_taskq_head;
 		while (mp != NULL) {
-			list_remove(&ipst->ips_capab_taskq_list, mp);
+			ipst->ips_capab_taskq_head = mp->b_next;
+			if (ipst->ips_capab_taskq_head == NULL)
+				ipst->ips_capab_taskq_tail = NULL;
 			mutex_exit(&ipst->ips_capab_taskq_lock);
+			mp->b_next = NULL;
+
 			VERIFY(taskq_dispatch(system_taskq,
 			    ill_capability_ack_thr, mp, TQ_SLEEP) != 0);
 			mutex_enter(&ipst->ips_capab_taskq_lock);
-			mp = list_head(&ipst->ips_capab_taskq_list);
+			mp = ipst->ips_capab_taskq_head;
 		}
 
 		if (ipst->ips_capab_taskq_quit)
@@ -3245,7 +3249,8 @@
 		cv_wait(&ipst->ips_capab_taskq_cv, &ipst->ips_capab_taskq_lock);
 		CALLB_CPR_SAFE_END(&cprinfo, &ipst->ips_capab_taskq_lock);
 	}
-	VERIFY(list_head(&ipst->ips_capab_taskq_list) == NULL);
+	VERIFY(ipst->ips_capab_taskq_head == NULL);
+	VERIFY(ipst->ips_capab_taskq_tail == NULL);
 	CALLB_CPR_EXIT(&cprinfo);
 	thread_exit();
 }
@@ -3264,6 +3269,8 @@
 	boolean_t reneg;
 
 	ill = (ill_t *)mp->b_prev;
+	mp->b_prev = NULL;
+
 	VERIFY(ipsq_enter(ill, B_FALSE, CUR_OP) == B_TRUE);
 
 	if (ill->ill_dlpi_capab_state == IDCS_RESET_SENT ||
@@ -3335,6 +3342,8 @@
 	ip_stack_t	*ipst = ill->ill_ipst;
 
 	mp->b_prev = (mblk_t *)ill;
+	ASSERT(mp->b_next == NULL);
+
 	if (taskq_dispatch(system_taskq, ill_capability_ack_thr, mp,
 	    TQ_NOSLEEP) != 0)
 		return;
@@ -3344,7 +3353,14 @@
 	 * which will do the dispatch using TQ_SLEEP to guarantee success.
 	 */
 	mutex_enter(&ipst->ips_capab_taskq_lock);
-	list_insert_tail(&ipst->ips_capab_taskq_list, mp);
+	if (ipst->ips_capab_taskq_head == NULL) {
+		ASSERT(ipst->ips_capab_taskq_tail == NULL);
+		ipst->ips_capab_taskq_head = mp;
+	} else {
+		ipst->ips_capab_taskq_tail->b_next = mp;
+	}
+	ipst->ips_capab_taskq_tail = mp;
+
 	cv_signal(&ipst->ips_capab_taskq_cv);
 	mutex_exit(&ipst->ips_capab_taskq_lock);
 }
--- a/usr/src/uts/common/inet/ip/ip_squeue.c	Fri Jun 26 12:35:29 2009 -0500
+++ b/usr/src/uts/common/inet/ip/ip_squeue.c	Fri Jun 26 12:06:35 2009 -0700
@@ -203,7 +203,7 @@
 	 * default squeue, in order to support fanout of conns across
 	 * CPUs. Try to find a former default squeue that matches this
 	 * cpu id on the unbound squeue set. If no such squeue is found,
-	 * find some non-default TCP squeue and steal it. If still no such
+	 * find some non-default TCP squeue that is free. If still no such
 	 * candidate is found, create a new squeue.
 	 */
 
@@ -214,17 +214,24 @@
 	while (*lastsqp) {
 		if ((*lastsqp)->sq_bind == id &&
 		    (*lastsqp)->sq_state & SQS_DEFAULT) {
+			/*
+			 * Exact match. Former default squeue of cpu 'id'
+			 */
+			ASSERT(!((*lastsqp)->sq_state & SQS_ILL_BOUND));
 			defaultq_lastp = lastsqp;
 			break;
 		}
 		if (defaultq_lastp == NULL &&
-		    !((*lastsqp)->sq_state & SQS_DEFAULT)) {
+		    !((*lastsqp)->sq_state & (SQS_ILL_BOUND | SQS_DEFAULT))) {
+			/*
+			 * A free non-default TCP squeue
+			 */
 			defaultq_lastp = lastsqp;
 		}
 		lastsqp = &(*lastsqp)->sq_next;
+	}
 
-	}
-	if (defaultq_lastp) {
+	if (defaultq_lastp != NULL) {
 		/* Remove from src set and set SQS_DEFAULT */
 		sq = *defaultq_lastp;
 		*defaultq_lastp = sq->sq_next;
@@ -262,7 +269,8 @@
 	mutex_enter(&sqset_lock);
 	for (sq = sqs->sqs_head; sq != NULL; sq = sq->sq_next) {
 		/*
-		 * Select a non-default squeue
+		 * Select a non-default TCP squeue that is free i.e. not
+		 * bound to any ill.
 		 */
 		if (!(sq->sq_state & (SQS_DEFAULT | SQS_ILL_BOUND)))
 			break;
@@ -564,7 +572,7 @@
 	mutex_exit(&ill->ill_lock);
 	while (!(sqp->sq_state & SQS_POLL_CLEANUP_DONE))
 		cv_wait(&sqp->sq_ctrlop_done_cv, &sqp->sq_lock);
-	sqp->sq_state &= ~(SQS_POLL_CLEANUP_DONE | SQS_ILL_BOUND);
+	sqp->sq_state &= ~SQS_POLL_CLEANUP_DONE;
 
 	ASSERT(!(sqp->sq_state & (SQS_POLL_THR_CONTROL |
 	    SQS_WORKER_THR_CONTROL | SQS_POLL_QUIESCE_DONE |
@@ -574,13 +582,25 @@
 	mutex_exit(&sqp->sq_lock);
 
 	/*
-	 * Logically free the squeue. It goes back to the set of unused
-	 * squeues
+	 * Move the squeue to sqset_global_list[0] which holds the set of
+	 * squeues not bound to any cpu. Note that the squeue is still
+	 * considered bound to an ill as long as SQS_ILL_BOUND is set.
 	 */
 	mutex_enter(&sqset_lock);
 	ip_squeue_set_move(sqp, sqset_global_list[0]);
 	mutex_exit(&sqset_lock);
 
+	/*
+	 * CPU going offline can also trigger a move of the squeue to the
+	 * unbound set sqset_global_list[0]. However the squeue won't be
+	 * recycled for the next use as long as the SQS_ILL_BOUND flag
+	 * is set. Hence we clear the SQS_ILL_BOUND flag only towards the
+	 * end after the move.
+	 */
+	mutex_enter(&sqp->sq_lock);
+	sqp->sq_state &= ~SQS_ILL_BOUND;
+	mutex_exit(&sqp->sq_lock);
+
 	mutex_enter(&ill->ill_lock);
 	rx_ring->rr_ring_state = RR_FREE;
 	mutex_exit(&ill->ill_lock);
@@ -702,7 +722,7 @@
 	/* Also move default squeue to unbound set */
 
 	sqp = sqs->sqs_default;
-	ASSERT(sqp);
+	ASSERT(sqp != NULL);
 	ASSERT((sqp->sq_state & (SQS_DEFAULT|SQS_ILL_BOUND)) == SQS_DEFAULT);
 
 	sqp->sq_next = unbound->sqs_head;
--- a/usr/src/uts/common/inet/ip_stack.h	Fri Jun 26 12:35:29 2009 -0500
+++ b/usr/src/uts/common/inet/ip_stack.h	Fri Jun 26 12:06:35 2009 -0700
@@ -188,7 +188,8 @@
 	/* Taskq dispatcher for capability operations */
 	kmutex_t	ips_capab_taskq_lock;
 	kcondvar_t	ips_capab_taskq_cv;
-	list_t		ips_capab_taskq_list;
+	mblk_t		*ips_capab_taskq_head;
+	mblk_t		*ips_capab_taskq_tail;
 	kthread_t	*ips_capab_taskq_thread;
 	boolean_t	ips_capab_taskq_quit;
 
--- a/usr/src/uts/common/inet/squeue.c	Fri Jun 26 12:35:29 2009 -0500
+++ b/usr/src/uts/common/inet/squeue.c	Fri Jun 26 12:06:35 2009 -0700
@@ -1055,6 +1055,14 @@
 			sqp->sq_state &= ~(SQS_PROC|SQS_GET_PKTS);
 			/* LINTED: constant in conditional context */
 			SQS_POLLING_OFF(sqp, B_TRUE, sq_rx_ring);
+
+			/*
+			 * If there is a pending control operation
+			 * wake up the worker, since it is currently
+			 * not running.
+			 */
+			if (sqp->sq_state & SQS_WORKER_THR_CONTROL)
+				cv_signal(&sqp->sq_worker_cv);
 		} else {
 			/*
 			 * Worker thread is already running. We don't need