svn commit: r189121 - head/sys/netinet

Randall Stewart rrs at FreeBSD.org
Fri Feb 27 12:54:46 PST 2009


Author: rrs
Date: Fri Feb 27 20:54:45 2009
New Revision: 189121
URL: http://svn.freebsd.org/changeset/base/189121

Log:
  Fix the add stream feature of strm-reset to really work:
   - Fix the copy, we can't do a blind copy but must transfer
     the data from the old to the new.
   - Fix the ACK processing so we properly stop retransmitting
     the thing.
   - Fix it so if we get a retran we will properly reply with
     the saved response without doing anything.
  
  MFC after:	1 month

Modified:
  head/sys/netinet/sctp_crc32.c
  head/sys/netinet/sctp_input.c
  head/sys/netinet/sctp_output.c
  head/sys/netinet/sctp_output.h
  head/sys/netinet/sctp_usrreq.c

Modified: head/sys/netinet/sctp_crc32.c
==============================================================================
--- head/sys/netinet/sctp_crc32.c	Fri Feb 27 20:00:15 2009	(r189120)
+++ head/sys/netinet/sctp_crc32.c	Fri Feb 27 20:54:45 2009	(r189121)
@@ -40,7 +40,6 @@ __FBSDID("$FreeBSD$");
 #include <sys/uio.h>
 #include <sys/libkern.h>
 #include <netinet/sctp.h>
-#include <netinet/sctp_os.h>
 #include <netinet/sctp_crc32.h>
 #include <netinet/sctp_pcb.h>
 

Modified: head/sys/netinet/sctp_input.c
==============================================================================
--- head/sys/netinet/sctp_input.c	Fri Feb 27 20:00:15 2009	(r189120)
+++ head/sys/netinet/sctp_input.c	Fri Feb 27 20:54:45 2009	(r189121)
@@ -3442,6 +3442,8 @@ sctp_handle_stream_reset_response(struct
 				}
 			} else if (type == SCTP_STR_RESET_ADD_STREAMS) {
 				/* Ok we now may have more streams */
+				if (asoc->stream_reset_outstanding)
+					asoc->stream_reset_outstanding--;
 				if (action == SCTP_STREAM_RESET_PERFORMED) {
 					/* Put the new streams into effect */
 					stcb->asoc.streamoutcnt = stcb->asoc.strm_realoutsize;
@@ -3730,50 +3732,79 @@ sctp_handle_str_reset_add_strm(struct sc
 	 */
 	uint16_t num_stream, i;
 	uint32_t seq;
+	struct sctp_association *asoc = &stcb->asoc;
+	struct sctp_queued_to_read *ctl;
 
 	/* Get the number. */
 	seq = ntohl(str_add->request_seq);
 	num_stream = ntohs(str_add->number_of_streams);
 	/* Now what would be the new total? */
-	num_stream += stcb->asoc.streamincnt;
-	if (num_stream > stcb->asoc.max_inbound_streams) {
-		/* We must reject it they ask for to many */
-denied:
-		sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED);
-		stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
-		stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_DENIED;
+	if (asoc->str_reset_seq_in == seq) {
+		num_stream += stcb->asoc.streamincnt;
+		if (num_stream > stcb->asoc.max_inbound_streams) {
+			/* We must reject it they ask for to many */
+	denied:
+			sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_DENIED);
+			stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
+			stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_DENIED;
+		} else {
+			/* Ok, we can do that :-) */
+			struct sctp_stream_in *oldstrm;
+
+			/* save off the old */
+			oldstrm = stcb->asoc.strmin;
+			SCTP_MALLOC(stcb->asoc.strmin, struct sctp_stream_in *,
+			    (num_stream * sizeof(struct sctp_stream_in)),
+			    SCTP_M_STRMI);
+			if (stcb->asoc.strmin == NULL) {
+				stcb->asoc.strmin = oldstrm;
+				goto denied;
+			}
+			/* copy off the old data */
+			for (i = 0; i < stcb->asoc.streamincnt; i++) {
+				TAILQ_INIT(&stcb->asoc.strmin[i].inqueue);
+				stcb->asoc.strmin[i].stream_no = i;
+				stcb->asoc.strmin[i].last_sequence_delivered = oldstrm[i].last_sequence_delivered;
+				stcb->asoc.strmin[i].delivery_started = oldstrm[i].delivery_started;
+				/* now anything on those queues? */
+				while (TAILQ_EMPTY(&oldstrm[i].inqueue) == 0) {
+					ctl = TAILQ_FIRST(&oldstrm[i].inqueue);
+					TAILQ_REMOVE(&oldstrm[i].inqueue, ctl, next);
+					TAILQ_INSERT_TAIL(&stcb->asoc.strmin[i].inqueue, ctl, next);
+				}
+			}
+			/* Init the new streams */
+			for (i = stcb->asoc.streamincnt; i < num_stream; i++) {
+				TAILQ_INIT(&stcb->asoc.strmin[i].inqueue);
+				stcb->asoc.strmin[i].stream_no = i;
+				stcb->asoc.strmin[i].last_sequence_delivered = 0xffff;
+				stcb->asoc.strmin[i].delivery_started = 0;
+			}
+			SCTP_FREE(oldstrm, SCTP_M_STRMI);
+			/* update the size */
+			stcb->asoc.streamincnt = num_stream;
+			/* Send the ack */
+			sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED);
+			stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
+			stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED;
+			sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK, stcb,
+			    (uint32_t) stcb->asoc.streamincnt, NULL, SCTP_SO_NOT_LOCKED);
+		}
+	} else if ((asoc->str_reset_seq_in - 1) == seq) {
+		/*
+		 * one seq back, just echo back last action since my
+		 * response was lost.
+		 */
+		sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[0]);
+	} else if ((asoc->str_reset_seq_in - 2) == seq) {
+		/*
+		 * two seq back, just echo back last action since my
+		 * response was lost.
+		 */
+		sctp_add_stream_reset_result(chk, seq, asoc->last_reset_action[1]);
 	} else {
-		/* Ok, we can do that :-) */
-		struct sctp_stream_in *oldstrm;
+		sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_BAD_SEQNO);
 
-		/* save off the old */
-		oldstrm = stcb->asoc.strmin;
-		SCTP_MALLOC(stcb->asoc.strmin, struct sctp_stream_in *,
-		    (num_stream * sizeof(struct sctp_stream_in)),
-		    SCTP_M_STRMI);
-		if (stcb->asoc.strmin == NULL) {
-			stcb->asoc.strmin = oldstrm;
-			goto denied;
-		}
-		/* copy off the old data */
-		memcpy(stcb->asoc.strmin, oldstrm,
-		    (stcb->asoc.streamincnt * sizeof(struct sctp_stream_in)));
-		/* Init the new streams */
-		for (i = stcb->asoc.streamincnt; i < num_stream; i++) {
-			TAILQ_INIT(&stcb->asoc.strmin[i].inqueue);
-			stcb->asoc.strmin[i].stream_no = i;
-			stcb->asoc.strmin[i].last_sequence_delivered = 0xffff;
-			stcb->asoc.strmin[i].delivery_started = 0;
-		}
-		SCTP_FREE(oldstrm, SCTP_M_STRMI);
-		/* update the size */
-		stcb->asoc.streamincnt = num_stream;
-		/* Send the ack */
-		sctp_add_stream_reset_result(chk, seq, SCTP_STREAM_RESET_PERFORMED);
-		stcb->asoc.last_reset_action[1] = stcb->asoc.last_reset_action[0];
-		stcb->asoc.last_reset_action[0] = SCTP_STREAM_RESET_PERFORMED;
-		sctp_ulp_notify(SCTP_NOTIFY_STR_RESET_INSTREAM_ADD_OK, stcb,
-		    (uint32_t) stcb->asoc.streamincnt, NULL, SCTP_SO_NOT_LOCKED);
 	}
 }
 

Modified: head/sys/netinet/sctp_output.c
==============================================================================
--- head/sys/netinet/sctp_output.c	Fri Feb 27 20:00:15 2009	(r189120)
+++ head/sys/netinet/sctp_output.c	Fri Feb 27 20:54:45 2009	(r189121)
@@ -7445,22 +7445,26 @@ outof_here:
 	}
 }
 
-static void
+void
 sctp_remove_from_wheel(struct sctp_tcb *stcb,
     struct sctp_association *asoc,
-    struct sctp_stream_out *strq)
+    struct sctp_stream_out *strq,
+    int holds_lock)
 {
 	/* take off and then setup so we know it is not on the wheel */
-	SCTP_TCB_SEND_LOCK(stcb);
+	if (holds_lock == 0)
+		SCTP_TCB_SEND_LOCK(stcb);
 	if (TAILQ_FIRST(&strq->outqueue)) {
 		/* more was added */
-		SCTP_TCB_SEND_UNLOCK(stcb);
+		if (holds_lock == 0)
+			SCTP_TCB_SEND_UNLOCK(stcb);
 		return;
 	}
 	TAILQ_REMOVE(&asoc->out_wheel, strq, next_spoke);
 	strq->next_spoke.tqe_next = NULL;
 	strq->next_spoke.tqe_prev = NULL;
-	SCTP_TCB_SEND_UNLOCK(stcb);
+	if (holds_lock == 0)
+		SCTP_TCB_SEND_UNLOCK(stcb);
 }
 
 static void
@@ -9083,7 +9087,7 @@ sctp_fill_outqueue(struct sctp_tcb *stcb
 						}
 					}
 				}
-				sctp_remove_from_wheel(stcb, asoc, strq);
+				sctp_remove_from_wheel(stcb, asoc, strq, 0);
 			}
 			if ((giveup) || bail) {
 				break;

Modified: head/sys/netinet/sctp_output.h
==============================================================================
--- head/sys/netinet/sctp_output.h	Fri Feb 27 20:00:15 2009	(r189120)
+++ head/sys/netinet/sctp_output.h	Fri Feb 27 20:54:45 2009	(r189121)
@@ -101,6 +101,11 @@ void
 sctp_send_heartbeat_ack(struct sctp_tcb *, struct mbuf *, int, int,
     struct sctp_nets *);
 
+void
+sctp_remove_from_wheel(struct sctp_tcb *stcb,
+    struct sctp_association *asoc,
+    struct sctp_stream_out *strq, int holds_lock);
+
 
 void sctp_send_shutdown(struct sctp_tcb *, struct sctp_nets *);
 

Modified: head/sys/netinet/sctp_usrreq.c
==============================================================================
--- head/sys/netinet/sctp_usrreq.c	Fri Feb 27 20:00:15 2009	(r189120)
+++ head/sys/netinet/sctp_usrreq.c	Fri Feb 27 20:54:45 2009	(r189121)
@@ -3326,6 +3326,8 @@ sctp_setopt(struct socket *so, int optna
 				if ((stcb->asoc.strm_realoutsize - stcb->asoc.streamoutcnt) < addstrmcnt) {
 					/* Need to allocate more */
 					struct sctp_stream_out *oldstream;
+					struct sctp_stream_queue_pending *sp;
+					int removed;
 
 					oldstream = stcb->asoc.strmout;
 					/* get some more */
@@ -3342,20 +3344,63 @@ sctp_setopt(struct socket *so, int optna
 					 * the old out stuff and
 					 * initializing the new stuff.
 					 */
-					memcpy(stcb->asoc.strmout, oldstream,
-					    (stcb->asoc.streamoutcnt * sizeof(struct sctp_stream_out)));
+					SCTP_TCB_SEND_LOCK(stcb);
+					for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
+						TAILQ_INIT(&stcb->asoc.strmout[i].outqueue);
+						stcb->asoc.strmout[i].next_sequence_sent = oldstream[i].next_sequence_sent;
+						stcb->asoc.strmout[i].last_msg_incomplete = oldstream[i].last_msg_incomplete;
+						stcb->asoc.strmout[i].stream_no = i;
+						if (oldstream[i].next_spoke.tqe_next) {
+							sctp_remove_from_wheel(stcb, &stcb->asoc, &oldstream[i], 1);
+							stcb->asoc.strmout[i].next_spoke.tqe_next = NULL;
+							stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL;
+							removed = 1;
+						} else {
+							/* not on out wheel */
+							stcb->asoc.strmout[i].next_spoke.tqe_next = NULL;
+							stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL;
+							removed = 0;
+						}
+						/*
+						 * now anything on those
+						 * queues?
+						 */
+						while (TAILQ_EMPTY(&oldstream[i].outqueue) == 0) {
+							sp = TAILQ_FIRST(&oldstream[i].outqueue);
+							TAILQ_REMOVE(&oldstream[i].outqueue, sp, next);
+							TAILQ_INSERT_TAIL(&stcb->asoc.strmout[i].outqueue, sp, next);
+						}
+						/* Did we disrupt the wheel? */
+						if (removed) {
+							sctp_insert_on_wheel(stcb,
+							    &stcb->asoc,
+							    &stcb->asoc.strmout[i],
+							    1);
+						}
+						/*
+						 * Now move assoc pointers
+						 * too
+						 */
+						if (stcb->asoc.last_out_stream == &oldstream[i]) {
+							stcb->asoc.last_out_stream = &stcb->asoc.strmout[i];
+						}
+						if (stcb->asoc.locked_on_sending == &oldstream[i]) {
+							stcb->asoc.locked_on_sending = &stcb->asoc.strmout[i];
+						}
+					}
 					/* now the new streams */
 					for (i = stcb->asoc.streamoutcnt; i < (stcb->asoc.streamoutcnt + addstrmcnt); i++) {
 						stcb->asoc.strmout[i].next_sequence_sent = 0x0;
 						TAILQ_INIT(&stcb->asoc.strmout[i].outqueue);
 						stcb->asoc.strmout[i].stream_no = i;
 						stcb->asoc.strmout[i].last_msg_incomplete = 0;
-						stcb->asoc.strmout[i].next_spoke.tqe_next = 0;
-						stcb->asoc.strmout[i].next_spoke.tqe_prev = 0;
+						stcb->asoc.strmout[i].next_spoke.tqe_next = NULL;
+						stcb->asoc.strmout[i].next_spoke.tqe_prev = NULL;
 					}
 					stcb->asoc.strm_realoutsize = stcb->asoc.streamoutcnt + addstrmcnt;
 					SCTP_FREE(oldstream, SCTP_M_STRMO);
 				}
+				SCTP_TCB_SEND_UNLOCK(stcb);
 				goto skip_stuff;
 			} else {
 				SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);


More information about the svn-src-all mailing list