summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorrrs <rrs@FreeBSD.org>2007-12-07 01:32:14 +0000
committerrrs <rrs@FreeBSD.org>2007-12-07 01:32:14 +0000
commitfad90600c19b80ff5cafb5fd89a160912b9a6e81 (patch)
tree28675f5e9591ab08a3868a32b2e938383f8959ab /sys
parente85902857b909623d774f2b8773d681b4116c23a (diff)
downloadFreeBSD-src-fad90600c19b80ff5cafb5fd89a160912b9a6e81.zip
FreeBSD-src-fad90600c19b80ff5cafb5fd89a160912b9a6e81.tar.gz
- More fixes for lock misses on the transfer of data to
the sent_queue. Sometimes I wonder why any code ever works :-) - Fix the pad of the last mbuf routine, It was working improperly on non-4 byte aligned chunks which could cause memory overruns. MFC after: 1 week
Diffstat (limited to 'sys')
-rw-r--r--sys/netinet/sctp_output.c222
-rw-r--r--sys/netinet/sctp_usrreq.c5
-rw-r--r--sys/netinet/sctputil.c4
3 files changed, 126 insertions, 105 deletions
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index 7299702..be5349e 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -6210,7 +6210,7 @@ sctp_clean_up_ctl(struct sctp_tcb *stcb, struct sctp_association *asoc)
static int
sctp_can_we_split_this(struct sctp_tcb *stcb,
- struct sctp_stream_queue_pending *sp,
+ uint32_t length,
uint32_t goal_mtu, uint32_t frag_point, int eeor_on)
{
/*
@@ -6223,7 +6223,7 @@ sctp_can_we_split_this(struct sctp_tcb *stcb,
* entire thing, since it might be all the guy is putting in
* the hopper.
*/
- if (goal_mtu >= sp->length) {
+ if (goal_mtu >= length) {
/*-
* If we have data outstanding,
* we get another chance when the sack
@@ -6234,7 +6234,7 @@ sctp_can_we_split_this(struct sctp_tcb *stcb,
* If nothing is in flight, we zero the
* packet counter.
*/
- return (sp->length);
+ return (length);
}
return (0);
@@ -6249,16 +6249,16 @@ sctp_can_we_split_this(struct sctp_tcb *stcb,
* get a full msg in so we have to allow splitting.
*/
if (SCTP_SB_LIMIT_SND(stcb->sctp_socket) < frag_point) {
- return (sp->length);
+ return (length);
}
- if ((sp->length <= goal_mtu) ||
- ((sp->length - goal_mtu) < sctp_min_residual)) {
+ if ((length <= goal_mtu) ||
+ ((length - goal_mtu) < sctp_min_residual)) {
/* Sub-optimial residual don't split in non-eeor mode. */
return (0);
}
/*
- * If we reach here sp->length is larger than the goal_mtu. Do we
- * wish to split it for the sake of packet putting together?
+ * If we reach here length is larger than the goal_mtu. Do we wish
+ * to split it for the sake of packet putting together?
*/
if (goal_mtu >= min(sctp_min_split_point, frag_point)) {
/* Its ok to split it */
@@ -6284,7 +6284,7 @@ sctp_move_to_outqueue(struct sctp_tcb *stcb, struct sctp_nets *net,
struct sctp_stream_queue_pending *sp;
struct sctp_tmit_chunk *chk;
struct sctp_data_chunk *dchkh;
- uint32_t to_move;
+ uint32_t to_move, length;
uint8_t rcv_flags = 0;
uint8_t some_taken;
uint8_t send_lock_up = 0;
@@ -6296,10 +6296,12 @@ one_more_time:
sp = TAILQ_FIRST(&strq->outqueue);
if (sp == NULL) {
*locked = 0;
- SCTP_TCB_SEND_LOCK(stcb);
+ if (send_lock_up == 0) {
+ SCTP_TCB_SEND_LOCK(stcb);
+ send_lock_up = 1;
+ }
sp = TAILQ_FIRST(&strq->outqueue);
if (sp) {
- SCTP_TCB_SEND_UNLOCK(stcb);
goto one_more_time;
}
if (strq->last_msg_incomplete) {
@@ -6308,57 +6310,60 @@ one_more_time:
strq->last_msg_incomplete);
strq->last_msg_incomplete = 0;
}
- SCTP_TCB_SEND_UNLOCK(stcb);
- return (0);
+ to_move = 0;
+ if (send_lock_up) {
+ SCTP_TCB_SEND_UNLOCK(stcb);
+ send_lock_up = 0;
+ }
+ goto out_of;
}
- if (sp->msg_is_complete) {
- if (sp->length == 0) {
- if (sp->sender_all_done) {
- /*
- * We are doing differed cleanup. Last time
- * through when we took all the data the
- * sender_all_done was not set.
- */
- if (sp->put_last_out == 0) {
- SCTP_PRINTF("Gak, put out entire msg with NO end!-1\n");
- SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d send_lock:%d\n",
- sp->sender_all_done,
- sp->length,
- sp->msg_is_complete,
- sp->put_last_out,
- send_lock_up);
- }
- if (TAILQ_NEXT(sp, next) == NULL) {
- SCTP_TCB_SEND_LOCK(stcb);
- send_lock_up = 1;
- }
- atomic_subtract_int(&asoc->stream_queue_cnt, 1);
- TAILQ_REMOVE(&strq->outqueue, sp, next);
- sctp_free_remote_addr(sp->net);
- if (sp->data) {
- sctp_m_freem(sp->data);
- sp->data = NULL;
- }
- sctp_free_a_strmoq(stcb, sp);
+ if ((sp->msg_is_complete) && (sp->length == 0)) {
+ if (sp->sender_all_done) {
+ /*
+ * We are doing differed cleanup. Last time through
+ * when we took all the data the sender_all_done was
+ * not set.
+ */
+ if (sp->put_last_out == 0) {
+ SCTP_PRINTF("Gak, put out entire msg with NO end!-1\n");
+ SCTP_PRINTF("sender_done:%d len:%d msg_comp:%d put_last_out:%d send_lock:%d\n",
+ sp->sender_all_done,
+ sp->length,
+ sp->msg_is_complete,
+ sp->put_last_out,
+ send_lock_up);
+ }
+ if ((TAILQ_NEXT(sp, next) == NULL) && (send_lock_up == 0)) {
+ SCTP_TCB_SEND_LOCK(stcb);
+ send_lock_up = 1;
+ }
+ atomic_subtract_int(&asoc->stream_queue_cnt, 1);
+ TAILQ_REMOVE(&strq->outqueue, sp, next);
+ sctp_free_remote_addr(sp->net);
+ if (sp->data) {
+ sctp_m_freem(sp->data);
+ sp->data = NULL;
+ }
+ sctp_free_a_strmoq(stcb, sp);
- /* we can't be locked to it */
- *locked = 0;
- stcb->asoc.locked_on_sending = NULL;
- if (send_lock_up) {
- SCTP_TCB_SEND_UNLOCK(stcb);
- send_lock_up = 0;
- }
- /* back to get the next msg */
- goto one_more_time;
- } else {
- /*
- * sender just finished this but still holds
- * a reference
- */
- *locked = 1;
- *giveup = 1;
- return (0);
+ /* we can't be locked to it */
+ *locked = 0;
+ stcb->asoc.locked_on_sending = NULL;
+ if (send_lock_up) {
+ SCTP_TCB_SEND_UNLOCK(stcb);
+ send_lock_up = 0;
}
+ /* back to get the next msg */
+ goto one_more_time;
+ } else {
+ /*
+ * sender just finished this but still holds a
+ * reference
+ */
+ *locked = 1;
+ *giveup = 1;
+ to_move = 0;
+ goto out_of;
}
} else {
/* is there some to get */
@@ -6366,7 +6371,8 @@ one_more_time:
/* no */
*locked = 1;
*giveup = 1;
- return (0);
+ to_move = 0;
+ goto out_of;
}
}
some_taken = sp->some_taken;
@@ -6374,10 +6380,11 @@ one_more_time:
sp->msg_is_complete = 1;
}
re_look:
+ length = sp->length;
if (sp->msg_is_complete) {
/* The message is complete */
- to_move = min(sp->length, frag_point);
- if (to_move == sp->length) {
+ to_move = min(length, frag_point);
+ if (to_move == length) {
/* All of it fits in the MTU */
if (sp->some_taken) {
rcv_flags |= SCTP_DATA_LAST_FRAG;
@@ -6394,23 +6401,23 @@ re_look:
sp->some_taken = 1;
}
} else {
- to_move = sctp_can_we_split_this(stcb, sp, goal_mtu,
+ to_move = sctp_can_we_split_this(stcb, length, goal_mtu,
frag_point, eeor_mode);
if (to_move) {
/*-
- * We use a snapshot of length in case it
- * is expanding during the compare.
- */
+ * We use a snapshot of length in case it
+ * is expanding during the compare.
+ */
uint32_t llen;
- llen = sp->length;
+ llen = length;
if (to_move >= llen) {
to_move = llen;
if (send_lock_up == 0) {
/*-
- * We are taking all of an incomplete msg
- * thus we need a send lock.
- */
+ * We are taking all of an incomplete msg
+ * thus we need a send lock.
+ */
SCTP_TCB_SEND_LOCK(stcb);
send_lock_up = 1;
if (sp->msg_is_complete) {
@@ -6432,7 +6439,8 @@ re_look:
*locked = 1;
}
*giveup = 1;
- return (0);
+ to_move = 0;
+ goto out_of;
}
}
@@ -6440,14 +6448,9 @@ re_look:
sctp_alloc_a_chunk(stcb, chk);
if (chk == NULL) {
/* No chunk memory */
-out_gu:
- if (send_lock_up) {
- /* sa_ignore NO_NULL_CHK */
- SCTP_TCB_SEND_UNLOCK(stcb);
- send_lock_up = 0;
- }
*giveup = 1;
- return (0);
+ to_move = 0;
+ goto out_of;
}
/*
* Setup for unordered if needed by looking at the user sent info
@@ -6459,17 +6462,17 @@ out_gu:
/* clear out the chunk before setting up */
memset(chk, 0, sizeof(*chk));
chk->rec.data.rcv_flags = rcv_flags;
- if ((send_lock_up == 0) && (sp->msg_is_complete == 0)) {
- SCTP_TCB_SEND_LOCK(stcb);
- send_lock_up = 1;
- }
- if (SCTP_BUF_IS_EXTENDED(sp->data)) {
- chk->copy_by_ref = 1;
- } else {
- chk->copy_by_ref = 0;
- }
- if (to_move >= sp->length) {
- /* we can steal the whole thing */
+
+ if (to_move >= length) {
+ /* we think we can steal the whole thing */
+ if ((sp->sender_all_done == 0) && (send_lock_up == 0)) {
+ SCTP_TCB_SEND_LOCK(stcb);
+ send_lock_up = 1;
+ }
+ if (to_move < sp->length) {
+ /* bail, it changed */
+ goto dont_do_it;
+ }
chk->data = sp->data;
chk->last_mbuf = sp->tail_mbuf;
/* register the stealing */
@@ -6477,13 +6480,15 @@ out_gu:
} else {
struct mbuf *m;
+dont_do_it:
chk->data = SCTP_M_COPYM(sp->data, 0, to_move, M_DONTWAIT);
chk->last_mbuf = NULL;
if (chk->data == NULL) {
sp->some_taken = some_taken;
sctp_free_a_chunk(stcb, chk);
*bail = 1;
- goto out_gu;
+ to_move = 0;
+ goto out_of;
}
/* Pull off the data */
m_adj(sp->data, to_move);
@@ -6511,7 +6516,22 @@ out_gu:
m = sp->data;
}
}
- if (to_move > sp->length) {
+ if (SCTP_BUF_IS_EXTENDED(chk->data)) {
+ chk->copy_by_ref = 1;
+ } else {
+ chk->copy_by_ref = 0;
+ }
+ /*
+ * get last_mbuf and counts of mb useage This is ugly but hopefully
+ * its only one mbuf.
+ */
+ if (chk->last_mbuf == NULL) {
+ chk->last_mbuf = chk->data;
+ while (SCTP_BUF_NEXT(chk->last_mbuf) != NULL) {
+ chk->last_mbuf = SCTP_BUF_NEXT(chk->last_mbuf);
+ }
+ }
+ if (to_move > length) {
/*- This should not happen either
* since we always lower to_move to the size
* of sp->length if its larger.
@@ -6525,12 +6545,6 @@ out_gu:
} else {
atomic_subtract_int(&sp->length, to_move);
}
- if (chk->last_mbuf == NULL) {
- chk->last_mbuf = chk->data;
- while (SCTP_BUF_NEXT(chk->last_mbuf) != NULL) {
- chk->last_mbuf = SCTP_BUF_NEXT(chk->last_mbuf);
- }
- }
if (M_LEADINGSPACE(chk->data) < (int)sizeof(struct sctp_data_chunk)) {
/* Not enough room for a chunk header, get some */
struct mbuf *m;
@@ -6563,7 +6577,8 @@ out_gu:
chk->data = NULL;
*bail = 1;
sctp_free_a_chunk(stcb, chk);
- goto out_gu;
+ to_move = 0;
+ goto out_of;
} else {
SCTP_BUF_LEN(m) = 0;
SCTP_BUF_NEXT(m) = chk->data;
@@ -6581,7 +6596,8 @@ out_gu:
sctp_free_a_chunk(stcb, chk);
#endif
*bail = 1;
- goto out_gu;
+ to_move = 0;
+ goto out_of;
}
sctp_snd_sb_alloc(stcb, sizeof(struct sctp_data_chunk));
chk->book_size = chk->send_size = (to_move +
@@ -6589,10 +6605,6 @@ out_gu:
chk->book_size_scale = 0;
chk->sent = SCTP_DATAGRAM_UNSENT;
- /*
- * get last_mbuf and counts of mb useage This is ugly but hopefully
- * its only one mbuf.
- */
chk->flags = 0;
chk->asoc = &stcb->asoc;
chk->pad_inplace = 0;
@@ -6706,6 +6718,7 @@ out_gu:
asoc->chunks_on_out_queue++;
TAILQ_INSERT_TAIL(&asoc->send_queue, chk, sctp_next);
asoc->send_queue_cnt++;
+out_of:
if (send_lock_up) {
SCTP_TCB_SEND_UNLOCK(stcb);
send_lock_up = 0;
@@ -6739,6 +6752,7 @@ done_it:
}
+
static void
sctp_fill_outqueue(struct sctp_tcb *stcb,
struct sctp_nets *net, int frag_point, int eeor_mode, int *quit_now)
@@ -11899,6 +11913,7 @@ skip_preblock:
}
if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) &&
(stcb->asoc.total_flight > 0) &&
+ (stcb->asoc.stream_queue_cnt < SCTP_MAX_DATA_BUNDLING) &&
(un_sent < (int)(stcb->asoc.smallest_mtu - SCTP_MIN_OVERHEAD))
) {
@@ -12181,6 +12196,7 @@ skip_out_eof:
}
if ((sctp_is_feature_off(inp, SCTP_PCB_FLAGS_NODELAY)) &&
(stcb->asoc.total_flight > 0) &&
+ (stcb->asoc.stream_queue_cnt < SCTP_MAX_DATA_BUNDLING) &&
(un_sent < (int)(stcb->asoc.smallest_mtu - SCTP_MIN_OVERHEAD))
) {
/*-
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index aea2db5..0c6bf6f 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -4152,6 +4152,11 @@ sctp_listen(struct socket *so, int backlog, struct thread *p)
}
SOCK_LOCK(so);
} else {
+ if (backlog != 0) {
+ inp->sctp_flags |= SCTP_PCB_FLAGS_LISTENING;
+ } else {
+ inp->sctp_flags &= ~SCTP_PCB_FLAGS_LISTENING;
+ }
SCTP_INP_RUNLOCK(inp);
}
/* It appears for 7.0 and on, we must always call this. */
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index 906ad6e..89b5e4f 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -2825,7 +2825,7 @@ sctp_add_pad_tombuf(struct mbuf *m, int padlen)
SCTP_LTRACE_ERR_RET_PKT(m, NULL, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOBUFS);
return (ENOBUFS);
}
- if (M_TRAILINGSPACE(m)) {
+ if (padlen <= M_TRAILINGSPACE(m)) {
/*
* The easy way. We hope the majority of the time we hit
* here :)
@@ -2843,8 +2843,8 @@ sctp_add_pad_tombuf(struct mbuf *m, int padlen)
return (ENOSPC);
}
/* setup and insert in middle */
- SCTP_BUF_NEXT(tmp) = SCTP_BUF_NEXT(m);
SCTP_BUF_LEN(tmp) = padlen;
+ SCTP_BUF_NEXT(tmp) = NULL;
SCTP_BUF_NEXT(m) = tmp;
dp = mtod(tmp, uint8_t *);
}
OpenPOWER on IntegriCloud