changeset 10037:d735c8ae153a

6621614 SCTP bundles partial data chunk
author Nick Street <Nick.Street@Sun.COM>
date Mon, 06 Jul 2009 06:01:37 -0700
parents d1aae351293e
children aeed618cdc99
files usr/src/uts/common/inet/sctp/sctp_impl.h usr/src/uts/common/inet/sctp/sctp_output.c
diffstat 2 files changed, 50 insertions(+), 17 deletions(-) [+]
line wrap: on
line diff
--- a/usr/src/uts/common/inet/sctp/sctp_impl.h	Mon Jul 06 04:04:07 2009 +0530
+++ b/usr/src/uts/common/inet/sctp/sctp_impl.h	Mon Jul 06 06:01:37 2009 -0700
@@ -982,7 +982,6 @@
 extern int	sctp_build_hdrs(sctp_t *);
 
 extern int	sctp_check_abandoned_msg(sctp_t *, mblk_t *);
-extern void	sctp_chunkify(sctp_t *, int, int);
 extern void	sctp_clean_death(sctp_t *, int);
 extern void	sctp_close_eager(sctp_t *);
 extern int	sctp_compare_faddrsets(sctp_faddr_t *, sctp_faddr_t *);
--- a/usr/src/uts/common/inet/sctp/sctp_output.c	Mon Jul 06 04:04:07 2009 +0530
+++ b/usr/src/uts/common/inet/sctp/sctp_output.c	Mon Jul 06 06:01:37 2009 -0700
@@ -79,6 +79,7 @@
 #include "sctp_impl.h"
 
 static struct kmem_cache	*sctp_kmem_ftsn_set_cache;
+static mblk_t			*sctp_chunkify(sctp_t *, int, int, int);
 
 #ifdef	DEBUG
 static boolean_t	sctp_verify_chain(mblk_t *, mblk_t *);
@@ -307,8 +308,16 @@
 	return (error);
 }
 
-void
-sctp_chunkify(sctp_t *sctp, int first_len, int bytes_to_send)
+/*
+ * While there are messages on sctp_xmit_unsent, detach each one. For each:
+ * allocate space for the chunk header, fill in the data chunk, and fill in
+ * the chunk header. Then append it to sctp_xmit_tail.
+ * Return after appending as many bytes as required (bytes_to_send).
+ * We also return if we've appended one or more chunks, and find a subsequent
+ * unsent message is too big to fit in the segment.
+ */
+mblk_t *
+sctp_chunkify(sctp_t *sctp, int mss, int firstseg_len, int bytes_to_send)
 {
 	mblk_t			*mp;
 	mblk_t			*chunk_mp;
@@ -323,7 +332,12 @@
 	sctp_faddr_t		*fp1;
 	size_t			xtralen;
 	sctp_msg_hdr_t		*msg_hdr;
-	sctp_stack_t	*sctps = sctp->sctp_sctps;
+	sctp_stack_t		*sctps = sctp->sctp_sctps;
+	sctp_msg_hdr_t		*next_msg_hdr;
+	size_t			nextlen;
+	int			remaining_len = mss - firstseg_len;
+
+	ASSERT(remaining_len >= 0);
 
 	fp = SCTP_CHUNK_DEST(mdblk);
 	if (fp == NULL)
@@ -334,14 +348,23 @@
 	else
 		xtralen = sctp->sctp_hdr6_len + sctps->sctps_wroff_xtra +
 		    sizeof (*sdc);
-	count = chunksize = first_len - sizeof (*sdc);
+	count = chunksize = remaining_len - sizeof (*sdc);
 nextmsg:
+	next_msg_hdr = (sctp_msg_hdr_t *)sctp->sctp_xmit_unsent->b_rptr;
+	nextlen = next_msg_hdr->smh_msglen;
+	/*
+	 * Will the entire next message fit in the current packet ?
+	 * if not, leave it on the unsent list.
+	 */
+	if ((firstseg_len != 0) && (nextlen > remaining_len))
+		return (NULL);
+
 	chunk_mp = mdblk->b_cont;
 
 	/*
-	 * If this partially chunked, we ignore the first_len for now
-	 * and use the one already present. For the unchunked bits, we
-	 * use the length of the last chunk.
+	 * If this partially chunked, we ignore the next one for now and
+	 * use the one already present. For the unchunked bits, we use the
+	 * length of the last chunk.
 	 */
 	if (SCTP_IS_MSG_CHUNKED(mdblk)) {
 		int	chunk_len;
@@ -405,7 +428,7 @@
 					chunk_head->b_next = mdblk->b_cont;
 					mdblk->b_cont = chunk_head;
 				}
-				return;
+				return (sctp->sctp_xmit_tail);
 			}
 			if (chunk_tail != NULL) {
 				chunk_tail->b_cont = split_mp;
@@ -439,7 +462,7 @@
 				chunk_head->b_next = mdblk->b_cont;
 				mdblk->b_cont = chunk_head;
 			}
-			return;
+			return (sctp->sctp_xmit_tail);
 		}
 		chunk_hdr->b_rptr += xtralen - sizeof (*sdc);
 		chunk_hdr->b_wptr = chunk_hdr->b_rptr + sizeof (*sdc);
@@ -523,6 +546,7 @@
 		}
 		goto nextmsg;
 	}
+	return (sctp->sctp_xmit_tail);
 }
 
 void
@@ -788,16 +812,17 @@
  * skipped. A message can be abandoned if it has a non-zero timetolive and
  * transmission has not yet started or if it is a partially reliable
  * message and its time is up (assuming we are PR-SCTP aware).
+ * We only return a chunk if it will fit entirely in the current packet.
  * 'cansend' is used to determine if need to try and chunkify messages from
  * the unsent list, if any, and also as an input to sctp_chunkify() if so.
  *
- * firstseg indicates the space already used, cansend represents remaining
- * space in the window, ((sfa_pmss - firstseg) can therefore reasonably
+ * firstseg_len indicates the space already used, cansend represents remaining
+ * space in the window, ((sfa_pmss - firstseg_len) can therefore reasonably
  * be used to compute the cansend arg).
  */
 mblk_t *
 sctp_get_msg_to_send(sctp_t *sctp, mblk_t **mp, mblk_t *meta, int  *error,
-    int32_t firstseg, uint32_t cansend, sctp_faddr_t *fp)
+    int32_t firstseg_len, uint32_t cansend, sctp_faddr_t *fp)
 {
 	mblk_t		*mp1;
 	sctp_msg_hdr_t	*msg_hdr;
@@ -903,8 +928,8 @@
 				goto chunk_done;
 			}
 		}
-		sctp_chunkify(sctp, fp->sfa_pmss - firstseg, cansend);
-		if ((meta = sctp->sctp_xmit_tail) == NULL)
+		meta = sctp_chunkify(sctp, fp->sfa_pmss, firstseg_len, cansend);
+		if (meta == NULL)
 			goto chunk_done;
 		/*
 		 * sctp_chunkify() won't advance sctp_xmit_tail if it adds
@@ -1171,7 +1196,13 @@
 				goto unsent_data;
 			}
 		}
-		/* See if we can bundle more. */
+		/*
+		 * Bundle chunks. We linkb() the chunks together to send
+		 * downstream in a single packet.
+		 * Partial chunks MUST NOT be bundled with full chunks, so we
+		 * rely on sctp_get_msg_to_send() to only return messages that
+		 * will fit entirely in the current packet.
+		 */
 		while (seglen < pathmax) {
 			int32_t		new_len;
 			int32_t		new_xtralen;
@@ -1186,7 +1217,10 @@
 				    meta->b_next, &error, seglen,
 				    (seglen - xtralen) >= cansend ? 0 :
 				    cansend - seglen, fp);
-				if (error != 0 || meta == NULL)
+				if (error != 0)
+					break;
+				/* If no more eligible chunks, cease bundling */
+				if (meta == NULL)
 					break;
 				sctp->sctp_xmit_tail =  meta;
 			}