summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--sys/netinet/sctp.h16
-rw-r--r--sys/netinet/sctp_output.c167
-rw-r--r--sys/netinet/sctp_structs.h1
-rw-r--r--sys/netinet/sctputil.c17
4 files changed, 115 insertions, 86 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h
index c302606..ad5e84c 100644
--- a/sys/netinet/sctp.h
+++ b/sys/netinet/sctp.h
@@ -519,20 +519,22 @@ __attribute__((packed));
#define SCTP_MBCNT_LOGGING_ENABLE 0x00000200
#define SCTP_MBUF_LOGGING_ENABLE 0x00000400
#define SCTP_NAGLE_LOGGING_ENABLE 0x00000800
-#define SCTP_RECV_RWND_LOGGING_ENABLE 0x00001000
+#define SCTP_RECV_RWND_LOGGING_ENABLE 0x00001000
#define SCTP_RTTVAR_LOGGING_ENABLE 0x00002000
#define SCTP_SACK_LOGGING_ENABLE 0x00004000
-#define SCTP_SACK_RWND_LOGGING_ENABLE 0x00008000
+#define SCTP_SACK_RWND_LOGGING_ENABLE 0x00008000
#define SCTP_SB_LOGGING_ENABLE 0x00010000
#define SCTP_STR_LOGGING_ENABLE 0x00020000
#define SCTP_WAKE_LOGGING_ENABLE 0x00040000
#define SCTP_LOG_MAXBURST_ENABLE 0x00080000
#define SCTP_LOG_RWND_ENABLE 0x00100000
-#define SCTP_LOG_SACK_ARRIVALS_ENABLE 0x00200000
-#define SCTP_LTRACE_CHUNK_ENABLE 0x00400000
-#define SCTP_LTRACE_ERROR_ENABLE 0x00800000
-#define SCTP_LAST_PACKET_TRACING 0x01000000
-#define SCTP_THRESHOLD_LOGGING 0x02000000
+#define SCTP_LOG_SACK_ARRIVALS_ENABLE 0x00200000
+#define SCTP_LTRACE_CHUNK_ENABLE 0x00400000
+#define SCTP_LTRACE_ERROR_ENABLE 0x00800000
+#define SCTP_LAST_PACKET_TRACING 0x01000000
+#define SCTP_THRESHOLD_LOGGING 0x02000000
+#define SCTP_LOG_AT_SEND_2_SCTP 0x04000000
+#define SCTP_LOG_AT_SEND_2_OUTQ 0x08000000
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index fccd2bd..ce6a7b8 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -6608,13 +6608,12 @@ out_gu:
atomic_add_int(&chk->whoTo->ref_count, 1);
chk->rec.data.TSN_seq = atomic_fetchadd_int(&asoc->sending_seq, 1);
-#ifdef SCTP_LOG_SENDING_STR
- sctp_misc_ints(SCTP_STRMOUT_LOG_SEND,
- (uintptr_t) stcb, (uintptr_t) sp,
- (uint32_t) ((chk->rec.data.stream_number << 16) | chk->rec.data.stream_seq),
- chk->rec.data.TSN_seq);
-#endif
-
+ if (sctp_logging_level & SCTP_LOG_AT_SEND_2_OUTQ) {
+ sctp_misc_ints(SCTP_STRMOUT_LOG_SEND,
+ (uintptr_t) stcb, sp->length,
+ (uint32_t) ((chk->rec.data.stream_number << 16) | chk->rec.data.stream_seq),
+ chk->rec.data.TSN_seq);
+ }
dchkh = mtod(chk->data, struct sctp_data_chunk *);
/*
* Put the rest of the things in place now. Size was done earlier in
@@ -10960,6 +10959,7 @@ sctp_lower_sosend(struct socket *so,
int free_cnt_applied = 0;
int un_sent = 0;
int now_filled = 0;
+ int inqueue_bytes = 0;
struct sctp_block_entry be;
struct sctp_inpcb *inp;
struct sctp_tcb *stcb = NULL;
@@ -10975,7 +10975,7 @@ sctp_lower_sosend(struct socket *so,
int hold_tcblock = 0;
int non_blocking = 0;
int temp_flags = 0;
- uint32_t local_add_more;
+ uint32_t local_add_more, local_soresv = 0;
error = 0;
net = NULL;
@@ -11011,9 +11011,9 @@ sctp_lower_sosend(struct socket *so,
addr,
sndlen);
/*-
- * Pre-screen address, if one is given the sin-len
- * must be set correctly!
- */
+ * Pre-screen address, if one is given the sin-len
+ * must be set correctly!
+ */
if (addr) {
if ((addr->sa_family == AF_INET) &&
(addr->sa_len != sizeof(struct sockaddr_in))) {
@@ -11306,20 +11306,20 @@ sctp_lower_sosend(struct socket *so,
}
for (i = 0; i < asoc->streamoutcnt; i++) {
/*-
- * inbound side must be set
- * to 0xffff, also NOTE when
- * we get the INIT-ACK back
- * (for INIT sender) we MUST
- * reduce the count
- * (streamoutcnt) but first
- * check if we sent to any
- * of the upper streams that
- * were dropped (if some
- * were). Those that were
- * dropped must be notified
- * to the upper layer as
- * failed to send.
- */
+ * inbound side must be set
+ * to 0xffff, also NOTE when
+ * we get the INIT-ACK back
+ * (for INIT sender) we MUST
+ * reduce the count
+ * (streamoutcnt) but first
+ * check if we sent to any
+ * of the upper streams that
+ * were dropped (if some
+ * were). Those that were
+ * dropped must be notified
+ * to the upper layer as
+ * failed to send.
+ */
asoc->strmout[i].next_sequence_sent = 0x0;
TAILQ_INIT(&asoc->strmout[i].outqueue);
asoc->strmout[i].stream_no = i;
@@ -11334,11 +11334,11 @@ sctp_lower_sosend(struct socket *so,
/* out with the INIT */
queue_only_for_init = 1;
/*-
- * we may want to dig in after this call and adjust the MTU
- * value. It defaulted to 1500 (constant) but the ro
- * structure may now have an update and thus we may need to
- * change it BEFORE we append the message.
- */
+ * we may want to dig in after this call and adjust the MTU
+ * value. It defaulted to 1500 (constant) but the ro
+ * structure may now have an update and thus we may need to
+ * change it BEFORE we append the message.
+ */
net = stcb->asoc.primary_destination;
asoc = &stcb->asoc;
}
@@ -11359,8 +11359,13 @@ sctp_lower_sosend(struct socket *so,
}
/* would we block? */
if (non_blocking) {
+ if (hold_tcblock == 0) {
+ SCTP_TCB_LOCK(stcb);
+ hold_tcblock = 1;
+ }
+ inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk));
if ((SCTP_SB_LIMIT_SND(so) <
- (sndlen + stcb->asoc.total_output_queue_size)) ||
+ (sndlen + inqueue_bytes + stcb->asoc.sb_send_resv)) ||
(stcb->asoc.chunks_on_out_queue >
sctp_max_chunks_on_queue)) {
SCTP_LTRACE_ERR_RET(inp, stcb, net, SCTP_FROM_SCTP_OUTPUT, EWOULDBLOCK);
@@ -11368,11 +11373,15 @@ sctp_lower_sosend(struct socket *so,
error = EMSGSIZE;
else
error = EWOULDBLOCK;
-
- atomic_add_int(&stcb->sctp_ep->total_nospaces, 1);
goto out_unlocked;
}
+ stcb->asoc.sb_send_resv += sndlen;
+ SCTP_TCB_UNLOCK(stcb);
+ hold_tcblock = 0;
+ } else {
+ atomic_add_int(&stcb->asoc.sb_send_resv, sndlen);
}
+ local_soresv = sndlen;
/* Keep the stcb from being freed under our feet */
if (free_cnt_applied) {
#ifdef INVARIANTS
@@ -11526,10 +11535,10 @@ sctp_lower_sosend(struct socket *so,
error = uiomove((caddr_t)ph, (int)tot_out, uio);
if (error) {
/*-
- * Here if we can't get his data we
- * still abort we just don't get to
- * send the users note :-0
- */
+ * Here if we can't get his data we
+ * still abort we just don't get to
+ * send the users note :-0
+ */
sctp_m_freem(mm);
mm = NULL;
}
@@ -11563,8 +11572,14 @@ sctp_lower_sosend(struct socket *so,
goto out_unlocked;
}
/* Calculate the maximum we can send */
- if (SCTP_SB_LIMIT_SND(so) > stcb->asoc.total_output_queue_size) {
- max_len = SCTP_SB_LIMIT_SND(so) - stcb->asoc.total_output_queue_size;
+ inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk));
+ if (SCTP_SB_LIMIT_SND(so) > inqueue_bytes) {
+ if (non_blocking) {
+ /* we already checked for non-blocking above. */
+ max_len = sndlen;
+ } else {
+ max_len = SCTP_SB_LIMIT_SND(so) - stcb->asoc.total_output_queue_size;
+ }
} else {
max_len = 0;
}
@@ -11612,13 +11627,17 @@ sctp_lower_sosend(struct socket *so,
local_add_more = sndlen;
}
len = 0;
- if (((max_len < local_add_more) &&
+ if (non_blocking == 0) {
+ goto skip_preblock;
+ }
+ if (((max_len <= local_add_more) &&
(SCTP_SB_LIMIT_SND(so) > local_add_more)) ||
- ((stcb->asoc.chunks_on_out_queue + stcb->asoc.stream_queue_cnt) > sctp_max_chunks_on_queue)) {
+ ((stcb->asoc.chunks_on_out_queue + stcb->asoc.stream_queue_cnt) > sctp_max_chunks_on_queue)) { /* if */
/* No room right no ! */
SOCKBUF_LOCK(&so->so_snd);
- while ((SCTP_SB_LIMIT_SND(so) < (stcb->asoc.total_output_queue_size + sctp_add_more_threshold)) ||
- ((stcb->asoc.stream_queue_cnt + stcb->asoc.chunks_on_out_queue) > sctp_max_chunks_on_queue)) {
+ inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk));
+ while ((SCTP_SB_LIMIT_SND(so) < (inqueue_bytes + sctp_add_more_threshold)) ||
+ ((stcb->asoc.stream_queue_cnt + stcb->asoc.chunks_on_out_queue) > sctp_max_chunks_on_queue /* while */ )) {
if (sctp_logging_level & SCTP_BLK_LOGGING_ENABLE) {
sctp_log_block(SCTP_BLOCK_LOG_INTO_BLKA,
@@ -11646,6 +11665,7 @@ sctp_lower_sosend(struct socket *so,
if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
goto out_unlocked;
}
+ inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk));
}
if (SCTP_SB_LIMIT_SND(so) > stcb->asoc.total_output_queue_size) {
max_len = SCTP_SB_LIMIT_SND(so) - stcb->asoc.total_output_queue_size;
@@ -11654,6 +11674,7 @@ sctp_lower_sosend(struct socket *so,
}
SOCKBUF_UNLOCK(&so->so_snd);
}
+skip_preblock:
if (stcb->asoc.state & SCTP_STATE_ABOUT_TO_BE_FREED) {
goto out_unlocked;
}
@@ -11714,11 +11735,11 @@ sctp_lower_sosend(struct socket *so,
atomic_add_int(&asoc->stream_queue_cnt, 1);
if ((srcv->sinfo_flags & SCTP_UNORDERED) == 0) {
sp->strseq = strm->next_sequence_sent;
-#ifdef SCTP_LOG_SENDING_STR
- sctp_misc_ints(SCTP_STRMOUT_LOG_ASSIGN,
- (uintptr_t) stcb, (uintptr_t) sp,
- (uint32_t) ((srcv->sinfo_stream << 16) | sp->strseq), 0);
-#endif
+ if (sctp_logging_level & SCTP_LOG_AT_SEND_2_SCTP) {
+ sctp_misc_ints(SCTP_STRMOUT_LOG_ASSIGN,
+ (uintptr_t) stcb, sp->length,
+ (uint32_t) ((srcv->sinfo_stream << 16) | sp->strseq), 0);
+ }
strm->next_sequence_sent++;
} else {
SCTP_STAT_INCR(sctps_sends_with_unord);
@@ -11827,8 +11848,9 @@ sctp_lower_sosend(struct socket *so,
hold_tcblock = 1;
}
sctp_prune_prsctp(stcb, asoc, srcv, sndlen);
+ inqueue_bytes = stcb->asoc.total_output_queue_size - (stcb->asoc.chunks_on_out_queue * sizeof(struct sctp_data_chunk));
if (SCTP_SB_LIMIT_SND(so) > stcb->asoc.total_output_queue_size)
- max_len = SCTP_SB_LIMIT_SND(so) - stcb->asoc.total_output_queue_size;
+ max_len = SCTP_SB_LIMIT_SND(so) - inqueue_bytes;
else
max_len = 0;
if (max_len > 0) {
@@ -11946,19 +11968,19 @@ sctp_lower_sosend(struct socket *so,
}
SOCKBUF_LOCK(&so->so_snd);
/*-
- * This is a bit strange, but I think it will
- * work. The total_output_queue_size is locked and
- * protected by the TCB_LOCK, which we just released.
- * There is a race that can occur between releasing it
- * above, and me getting the socket lock, where sacks
- * come in but we have not put the SB_WAIT on the
- * so_snd buffer to get the wakeup. After the LOCK
- * is applied the sack_processing will also need to
- * LOCK the so->so_snd to do the actual sowwakeup(). So
- * once we have the socket buffer lock if we recheck the
- * size we KNOW we will get to sleep safely with the
- * wakeup flag in place.
- */
+ * This is a bit strange, but I think it will
+ * work. The total_output_queue_size is locked and
+ * protected by the TCB_LOCK, which we just released.
+ * There is a race that can occur between releasing it
+ * above, and me getting the socket lock, where sacks
+ * come in but we have not put the SB_WAIT on the
+ * so_snd buffer to get the wakeup. After the LOCK
+ * is applied the sack_processing will also need to
+ * LOCK the so->so_snd to do the actual sowwakeup(). So
+ * once we have the socket buffer lock if we recheck the
+ * size we KNOW we will get to sleep safely with the
+ * wakeup flag in place.
+ */
if (SCTP_SB_LIMIT_SND(so) <= (stcb->asoc.total_output_queue_size +
min(sctp_add_more_threshold, SCTP_SB_LIMIT_SND(so)))
) {
@@ -12068,15 +12090,15 @@ dataless_eof:
}
} else {
/*-
- * we still got (or just got) data to send, so set
- * SHUTDOWN_PENDING
- */
+ * we still got (or just got) data to send, so set
+ * SHUTDOWN_PENDING
+ */
/*-
- * XXX sockets draft says that SCTP_EOF should be
- * sent with no data. currently, we will allow user
- * data to be sent first and move to
- * SHUTDOWN-PENDING
- */
+ * XXX sockets draft says that SCTP_EOF should be
+ * sent with no data. currently, we will allow user
+ * data to be sent first and move to
+ * SHUTDOWN-PENDING
+ */
if ((SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_SENT) &&
(SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_RECEIVED) &&
(SCTP_GET_STATE(asoc) != SCTP_STATE_SHUTDOWN_ACK_SENT)) {
@@ -12233,7 +12255,10 @@ skip_out_eof:
out:
out_unlocked:
-
+ if (local_soresv && stcb) {
+ atomic_subtract_int(&stcb->asoc.sb_send_resv, sndlen);
+ local_soresv = 0;
+ }
if (create_lock_applied) {
SCTP_ASOC_CREATE_UNLOCK(inp);
create_lock_applied = 0;
diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h
index 1d145e2..69b6f92 100644
--- a/sys/netinet/sctp_structs.h
+++ b/sys/netinet/sctp_structs.h
@@ -800,6 +800,7 @@ struct sctp_association {
uint32_t total_output_queue_size;
uint32_t sb_cc; /* shadow of sb_cc */
+ uint32_t sb_send_resv; /* amount reserved on a send */
uint32_t my_rwnd_control_len; /* shadow of sb_mbcnt used for rwnd
* control */
/* 32 bit nonce stuff */
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index 801107f..fb660c8 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -904,6 +904,7 @@ sctp_init_asoc(struct sctp_inpcb *m, struct sctp_tcb *stcb,
#else
asoc->default_flowlabel = 0;
#endif
+ asoc->sb_send_resv = 0;
if (override_tag) {
struct timeval now;
@@ -3363,6 +3364,7 @@ sctp_notify_shutdown_event(struct sctp_tcb *stcb)
}
#endif
socantsendmore(stcb->sctp_socket);
+ socantrcvmore(stcb->sctp_socket);
#if defined (__APPLE__) || defined(SCTP_SO_LOCK_TESTING)
SCTP_SOCKET_UNLOCK(so, 1);
#endif
@@ -4948,10 +4950,6 @@ sctp_sorecvmsg(struct socket *so,
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
return (EINVAL);
}
- if (from && fromlen <= 0) {
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, EINVAL);
- return (EINVAL);
- }
if (msg_flags) {
in_flags = *msg_flags;
if (in_flags & MSG_PEEK)
@@ -5017,12 +5015,15 @@ restart_nosblocks:
error = so->so_error;
if ((in_flags & MSG_PEEK) == 0)
so->so_error = 0;
+ goto out;
} else {
- SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOTCONN);
- /* indicate EOF */
- error = 0;
+ if (so->so_rcv.sb_cc == 0) {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTPUTIL, ENOTCONN);
+ /* indicate EOF */
+ error = 0;
+ goto out;
+ }
}
- goto out;
}
if ((so->so_rcv.sb_cc <= held_length) && block_allowed) {
/* we need to wait for data */
OpenPOWER on IntegriCloud