summaryrefslogtreecommitdiffstats
path: root/sys/netinet
diff options
context:
space:
mode:
authortuexen <tuexen@FreeBSD.org>2014-08-22 20:26:20 +0000
committertuexen <tuexen@FreeBSD.org>2014-08-22 20:26:20 +0000
commit9db28b75f0aec8ddfe352de0c5b8e13dd51c5cb1 (patch)
tree64ab7500bf3f6439dd3918dce4d107163abc3e86 /sys/netinet
parentfcd4ba9eaabfa53f01ae9c54fc6bdbfd9a91ffa1 (diff)
downloadFreeBSD-src-9db28b75f0aec8ddfe352de0c5b8e13dd51c5cb1.zip
FreeBSD-src-9db28b75f0aec8ddfe352de0c5b8e13dd51c5cb1.tar.gz
MFC r269945:
Add support for the SCTP_PR_STREAM_STATUS and SCTP_PR_ASSOC_STATUS socket options. This includes managing the correspoing stat counters. Add the SCTP_DETAILED_STR_STATS kernel option to control per policy counters on every stream. The default is off and only an aggregated counter is available. This is sufficient for the RTCWeb usecase.
Diffstat (limited to 'sys/netinet')
-rw-r--r--sys/netinet/sctp.h2
-rw-r--r--sys/netinet/sctp_input.c14
-rw-r--r--sys/netinet/sctp_output.c28
-rw-r--r--sys/netinet/sctp_structs.h10
-rw-r--r--sys/netinet/sctp_uio.h19
-rw-r--r--sys/netinet/sctp_usrreq.c66
-rw-r--r--sys/netinet/sctputil.c33
7 files changed, 169 insertions, 3 deletions
diff --git a/sys/netinet/sctp.h b/sys/netinet/sctp.h
index c2497a7..9b795ed 100644
--- a/sys/netinet/sctp.h
+++ b/sys/netinet/sctp.h
@@ -140,6 +140,8 @@ struct sctp_paramhdr {
#define SCTP_GET_ASSOC_NUMBER 0x00000104 /* ro */
#define SCTP_GET_ASSOC_ID_LIST 0x00000105 /* ro */
#define SCTP_TIMEOUTS 0x00000106
+#define SCTP_PR_STREAM_STATUS 0x00000107
+#define SCTP_PR_ASSOC_STATUS 0x00000108
/*
* user socket options: BSD implementation specific
diff --git a/sys/netinet/sctp_input.c b/sys/netinet/sctp_input.c
index 49503c5..420148d 100644
--- a/sys/netinet/sctp_input.c
+++ b/sys/netinet/sctp_input.c
@@ -1469,6 +1469,11 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
int spec_flag = 0;
uint32_t how_indx;
+#if defined(SCTP_DETAILED_STR_STATS)
+ int j;
+
+#endif
+
net = *netp;
/* I know that the TCB is non-NULL from the caller */
asoc = &stcb->asoc;
@@ -1931,6 +1936,15 @@ sctp_process_cookie_existing(struct mbuf *m, int iphlen, int offset,
sctp_report_all_outbound(stcb, 0, 1, SCTP_SO_LOCKED);
for (i = 0; i < stcb->asoc.streamoutcnt; i++) {
stcb->asoc.strmout[i].chunks_on_queues = 0;
+#if defined(SCTP_DETAILED_STR_STATS)
+ for (j = 0; j < SCTP_PR_SCTP_MAX + 1; j++) {
+ asoc->strmout[i].abandoned_sent[j] = 0;
+ asoc->strmout[i].abandoned_unsent[j] = 0;
+ }
+#else
+ asoc->strmout[i].abandoned_sent[0] = 0;
+ asoc->strmout[i].abandoned_unsent[0] = 0;
+#endif
stcb->asoc.strmout[i].stream_no = i;
stcb->asoc.strmout[i].next_sequence_send = 0;
stcb->asoc.strmout[i].last_msg_incomplete = 0;
diff --git a/sys/netinet/sctp_output.c b/sys/netinet/sctp_output.c
index b9b8573..e1a6a03 100644
--- a/sys/netinet/sctp_output.c
+++ b/sys/netinet/sctp_output.c
@@ -3618,6 +3618,11 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
struct sctp_stream_out *tmp_str;
unsigned int i;
+#if defined(SCTP_DETAILED_STR_STATS)
+ int j;
+
+#endif
+
/* Default is NOT correct */
SCTPDBG(SCTP_DEBUG_OUTPUT1, "Ok, default:%d pre_open:%d\n",
stcb->asoc.streamoutcnt, stcb->asoc.pre_open_streams);
@@ -3638,6 +3643,15 @@ sctp_process_cmsgs_for_init(struct sctp_tcb *stcb, struct mbuf *control, int *er
TAILQ_INIT(&stcb->asoc.strmout[i].outqueue);
stcb->asoc.strmout[i].chunks_on_queues = 0;
stcb->asoc.strmout[i].next_sequence_send = 0;
+#if defined(SCTP_DETAILED_STR_STATS)
+ for (j = 0; j < SCTP_PR_SCTP_MAX + 1; j++) {
+ stcb->asoc.strmout[i].abandoned_sent[j] = 0;
+ stcb->asoc.strmout[i].abandoned_unsent[j] = 0;
+ }
+#else
+ stcb->asoc.strmout[i].abandoned_sent[0] = 0;
+ stcb->asoc.strmout[i].abandoned_unsent[0] = 0;
+#endif
stcb->asoc.strmout[i].stream_no = i;
stcb->asoc.strmout[i].last_msg_incomplete = 0;
stcb->asoc.ss_functions.sctp_ss_init_stream(&stcb->asoc.strmout[i], NULL);
@@ -11923,6 +11937,11 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
struct sctp_stream_queue_pending *sp, *nsp;
int i;
+#if defined(SCTP_DETAILED_STR_STATS)
+ int j;
+
+#endif
+
oldstream = stcb->asoc.strmout;
/* get some more */
SCTP_MALLOC(stcb->asoc.strmout, struct sctp_stream_out *,
@@ -11968,6 +11987,15 @@ sctp_send_str_reset_req(struct sctp_tcb *stcb,
for (i = stcb->asoc.streamoutcnt; i < (stcb->asoc.streamoutcnt + adding_o); i++) {
TAILQ_INIT(&stcb->asoc.strmout[i].outqueue);
stcb->asoc.strmout[i].chunks_on_queues = 0;
+#if defined(SCTP_DETAILED_STR_STATS)
+ for (j = 0; j < SCTP_PR_SCTP_MAX + 1; j++) {
+ stcb->asoc.strmout[i].abandoned_sent[j] = 0;
+ stcb->asoc.strmout[i].abandoned_unsent[j] = 0;
+ }
+#else
+ stcb->asoc.strmout[i].abandoned_sent[0] = 0;
+ stcb->asoc.strmout[i].abandoned_unsent[0] = 0;
+#endif
stcb->asoc.strmout[i].next_sequence_send = 0x0;
stcb->asoc.strmout[i].stream_no = i;
stcb->asoc.strmout[i].last_msg_incomplete = 0;
diff --git a/sys/netinet/sctp_structs.h b/sys/netinet/sctp_structs.h
index 9be8614..24c456c 100644
--- a/sys/netinet/sctp_structs.h
+++ b/sys/netinet/sctp_structs.h
@@ -587,6 +587,14 @@ struct sctp_stream_out {
struct sctp_streamhead outqueue;
union scheduling_parameters ss_params;
uint32_t chunks_on_queues;
+#if defined(SCTP_DETAILED_STR_STATS)
+ uint32_t abandoned_unsent[SCTP_PR_SCTP_MAX + 1];
+ uint32_t abandoned_sent[SCTP_PR_SCTP_MAX + 1];
+#else
+ /* Only the aggregation */
+ uint32_t abandoned_unsent[1];
+ uint32_t abandoned_sent[1];
+#endif
uint16_t stream_no;
uint16_t next_sequence_send; /* next one I expect to send out */
uint8_t last_msg_incomplete;
@@ -1211,6 +1219,8 @@ struct sctp_association {
uint32_t timoshutdownack;
struct timeval start_time;
struct timeval discontinuity_time;
+ uint64_t abandoned_unsent[SCTP_PR_SCTP_MAX + 1];
+ uint64_t abandoned_sent[SCTP_PR_SCTP_MAX + 1];
};
#endif
diff --git a/sys/netinet/sctp_uio.h b/sys/netinet/sctp_uio.h
index 1295391..300062a 100644
--- a/sys/netinet/sctp_uio.h
+++ b/sys/netinet/sctp_uio.h
@@ -249,18 +249,23 @@ struct sctp_snd_all_completes {
SCTP_SACK_IMMEDIATELY)) != 0)
/* for the endpoint */
-/* The lower byte is an enumeration of PR-SCTP policies */
+/* The lower four bits is an enumeration of PR-SCTP policies */
#define SCTP_PR_SCTP_NONE 0x0000/* Reliable transfer */
#define SCTP_PR_SCTP_TTL 0x0001/* Time based PR-SCTP */
#define SCTP_PR_SCTP_BUF 0x0002/* Buffer based PR-SCTP */
#define SCTP_PR_SCTP_RTX 0x0003/* Number of retransmissions based PR-SCTP */
+#define SCTP_PR_SCTP_MAX SCTP_PR_SCTP_RTX
+#define SCTP_PR_SCTP_ALL 0x000f/* Used for aggregated stats */
#define PR_SCTP_POLICY(x) ((x) & 0x0f)
-#define PR_SCTP_ENABLED(x) (PR_SCTP_POLICY(x) != SCTP_PR_SCTP_NONE)
+#define PR_SCTP_ENABLED(x) ((PR_SCTP_POLICY(x) != SCTP_PR_SCTP_NONE) && \
+ (PR_SCTP_POLICY(x) != SCTP_PR_SCTP_ALL))
#define PR_SCTP_TTL_ENABLED(x) (PR_SCTP_POLICY(x) == SCTP_PR_SCTP_TTL)
#define PR_SCTP_BUF_ENABLED(x) (PR_SCTP_POLICY(x) == SCTP_PR_SCTP_BUF)
#define PR_SCTP_RTX_ENABLED(x) (PR_SCTP_POLICY(x) == SCTP_PR_SCTP_RTX)
-#define PR_SCTP_INVALID_POLICY(x) (PR_SCTP_POLICY(x) > SCTP_PR_SCTP_RTX)
+#define PR_SCTP_INVALID_POLICY(x) (PR_SCTP_POLICY(x) > SCTP_PR_SCTP_MAX)
+#define PR_SCTP_VALID_POLICY(x) (PR_SCTP_POLICY(x) <= SCTP_PR_SCTP_MAX)
+
/* Stat's */
struct sctp_pcbinfo {
uint32_t ep_count;
@@ -720,6 +725,14 @@ struct sctp_udpencaps {
uint16_t sue_port;
};
+struct sctp_prstatus {
+ sctp_assoc_t sprstat_assoc_id;
+ uint16_t sprstat_sid;
+ uint16_t sprstat_policy;
+ uint64_t sprstat_abandoned_unsent;
+ uint64_t sprstat_abandoned_sent;
+};
+
struct sctp_cwnd_args {
struct sctp_nets *net; /* network to *//* FIXME: LP64 issue */
uint32_t cwnd_new_value;/* cwnd in k */
diff --git a/sys/netinet/sctp_usrreq.c b/sys/netinet/sctp_usrreq.c
index 1d49ef5..da22c1f 100644
--- a/sys/netinet/sctp_usrreq.c
+++ b/sys/netinet/sctp_usrreq.c
@@ -3510,6 +3510,72 @@ flags_out:
}
break;
}
+ case SCTP_PR_STREAM_STATUS:
+ {
+ struct sctp_prstatus *sprstat;
+ uint16_t sid;
+ uint16_t policy;
+
+ SCTP_CHECK_AND_CAST(sprstat, optval, struct sctp_prstatus, *optsize);
+ SCTP_FIND_STCB(inp, stcb, sprstat->sprstat_assoc_id);
+
+ sid = sprstat->sprstat_sid;
+ policy = sprstat->sprstat_policy;
+#if defined(SCTP_DETAILED_STR_STATS)
+ if ((stcb != NULL) &&
+ (policy != SCTP_PR_SCTP_NONE) &&
+ (sid < stcb->asoc.streamoutcnt) &&
+ ((policy == SCTP_PR_SCTP_ALL) ||
+ (PR_SCTP_VALID_POLICY(policy)))) {
+#else
+ if ((stcb != NULL) &&
+ (policy != SCTP_PR_SCTP_NONE) &&
+ (sid < stcb->asoc.streamoutcnt) &&
+ (policy == SCTP_PR_SCTP_ALL)) {
+#endif
+ if (policy == SCTP_PR_SCTP_ALL) {
+ sprstat->sprstat_abandoned_unsent = stcb->asoc.strmout[sid].abandoned_unsent[0];
+ sprstat->sprstat_abandoned_sent = stcb->asoc.strmout[sid].abandoned_sent[0];
+ } else {
+ sprstat->sprstat_abandoned_unsent = stcb->asoc.strmout[sid].abandoned_unsent[policy];
+ sprstat->sprstat_abandoned_sent = stcb->asoc.strmout[sid].abandoned_sent[policy];
+ }
+ SCTP_TCB_UNLOCK(stcb);
+ *optsize = sizeof(struct sctp_prstatus);
+ } else {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ error = EINVAL;
+ }
+ break;
+ }
+ case SCTP_PR_ASSOC_STATUS:
+ {
+ struct sctp_prstatus *sprstat;
+ uint16_t policy;
+
+ SCTP_CHECK_AND_CAST(sprstat, optval, struct sctp_prstatus, *optsize);
+ SCTP_FIND_STCB(inp, stcb, sprstat->sprstat_assoc_id);
+
+ policy = sprstat->sprstat_policy;
+ if ((stcb != NULL) &&
+ (policy != SCTP_PR_SCTP_NONE) &&
+ ((policy == SCTP_PR_SCTP_ALL) ||
+ (PR_SCTP_VALID_POLICY(policy)))) {
+ if (policy == SCTP_PR_SCTP_ALL) {
+ sprstat->sprstat_abandoned_unsent = stcb->asoc.abandoned_unsent[0];
+ sprstat->sprstat_abandoned_sent = stcb->asoc.abandoned_sent[0];
+ } else {
+ sprstat->sprstat_abandoned_unsent = stcb->asoc.abandoned_unsent[policy];
+ sprstat->sprstat_abandoned_sent = stcb->asoc.abandoned_sent[policy];
+ }
+ SCTP_TCB_UNLOCK(stcb);
+ *optsize = sizeof(struct sctp_prstatus);
+ } else {
+ SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, EINVAL);
+ error = EINVAL;
+ }
+ break;
+ }
default:
SCTP_LTRACE_ERR_RET(inp, NULL, NULL, SCTP_FROM_SCTP_USRREQ, ENOPROTOOPT);
error = ENOPROTOOPT;
diff --git a/sys/netinet/sctputil.c b/sys/netinet/sctputil.c
index d2a1f8f..6bb1001 100644
--- a/sys/netinet/sctputil.c
+++ b/sys/netinet/sctputil.c
@@ -896,6 +896,11 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
*/
int i;
+#if defined(SCTP_DETAILED_STR_STATS)
+ int j;
+
+#endif
+
asoc = &stcb->asoc;
/* init all variables to a known value. */
SCTP_SET_STATE(&stcb->asoc, SCTP_STATE_INUSE);
@@ -1056,6 +1061,15 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
asoc->strmout[i].next_sequence_send = 0x0;
TAILQ_INIT(&asoc->strmout[i].outqueue);
asoc->strmout[i].chunks_on_queues = 0;
+#if defined(SCTP_DETAILED_STR_STATS)
+ for (j = 0; j < SCTP_PR_SCTP_MAX + 1; j++) {
+ asoc->strmout[i].abandoned_sent[j] = 0;
+ asoc->strmout[i].abandoned_unsent[j] = 0;
+ }
+#else
+ asoc->strmout[i].abandoned_sent[0] = 0;
+ asoc->strmout[i].abandoned_unsent[0] = 0;
+#endif
asoc->strmout[i].stream_no = i;
asoc->strmout[i].last_msg_incomplete = 0;
asoc->ss_functions.sctp_ss_init_stream(&asoc->strmout[i], NULL);
@@ -1111,6 +1125,10 @@ sctp_init_asoc(struct sctp_inpcb *inp, struct sctp_tcb *stcb,
asoc->timoshutdownack = 0;
(void)SCTP_GETTIME_TIMEVAL(&asoc->start_time);
asoc->discontinuity_time = asoc->start_time;
+ for (i = 0; i < SCTP_PR_SCTP_MAX + 1; i++) {
+ asoc->abandoned_unsent[i] = 0;
+ asoc->abandoned_sent[i] = 0;
+ }
/*
* sa_ignore MEMLEAK {memory is put in the assoc mapping array and
* freed later when the association is freed.
@@ -4713,6 +4731,21 @@ sctp_release_pr_sctp_chunk(struct sctp_tcb *stcb, struct sctp_tmit_chunk *tp1,
stream = tp1->rec.data.stream_number;
seq = tp1->rec.data.stream_seq;
+ if (sent || !(tp1->rec.data.rcv_flags & SCTP_DATA_FIRST_FRAG)) {
+ stcb->asoc.abandoned_sent[0]++;
+ stcb->asoc.abandoned_sent[PR_SCTP_POLICY(tp1->flags)]++;
+ stcb->asoc.strmout[stream].abandoned_sent[0]++;
+#if defined(SCTP_DETAILED_STR_STATS)
+ stcb->asoc.strmout[stream].abandoned_sent[PR_SCTP_POLICY(tp1->flags)]++;
+#endif
+ } else {
+ stcb->asoc.abandoned_unsent[0]++;
+ stcb->asoc.abandoned_unsent[PR_SCTP_POLICY(tp1->flags)]++;
+ stcb->asoc.strmout[stream].abandoned_unsent[0]++;
+#if defined(SCTP_DETAILED_STR_STATS)
+ stcb->asoc.strmout[stream].abandoned_unsent[PR_SCTP_POLICY(tp1->flags)]++;
+#endif
+ }
do {
ret_sz += tp1->book_size;
if (tp1->data != NULL) {
OpenPOWER on IntegriCloud