aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp
diff options
context:
space:
mode:
authorMichele Baldessari <michele@acksyn.org>2012-11-30 23:49:42 -0500
committerDavid S. Miller <davem@davemloft.net>2012-12-03 13:32:15 -0500
commit196d67593439b03088913227093e374235596e33 (patch)
treed8d1580673e3cf878d6db230bf86b71187bf3550 /net/sctp
parent96070ae4d08eefe62ac534904ce21a01e1a5c9c4 (diff)
sctp: Add support to per-association statistics via a new SCTP_GET_ASSOC_STATS call
The current SCTP stack is lacking a mechanism to have per association statistics. This is an implementation modeled after OpenSolaris' SCTP_GET_ASSOC_STATS. Userspace part will follow on lksctp if/when there is a general ACK on this. V4: - Move ipackets++ before q->immediate.func() for consistency reasons - Move sctp_max_rto() at the end of sctp_transport_update_rto() to avoid returning bogus RTO values - return asoc->rto_min when max_obs_rto value has not changed V3: - Increase ictrlchunks in sctp_assoc_bh_rcv() as well - Move ipackets++ to sctp_inq_push() - return 0 when no rto updates took place since the last call V2: - Implement partial retrieval of stat struct to cope for future expansion - Kill the rtxpackets counter as it cannot be precise anyway - Rename outseqtsns to outofseqtsns to make it clearer that these are out of sequence unexpected TSNs - Move asoc->ipackets++ under a lock to avoid potential miscounts - Fold asoc->opackets++ into the already existing asoc check - Kill unneeded (q->asoc) test when increasing rtxchunks - Do not count octrlchunks if sending failed (SCTP_XMIT_OK != 0) - Don't count SHUTDOWNs as SACKs - Move SCTP_GET_ASSOC_STATS to the private space API - Adjust the len check in sctp_getsockopt_assoc_stats() to allow for future struct growth - Move association statistics in their own struct - Update idupchunks when we send a SACK with dup TSNs - return min_rto in max_rto when RTO has not changed. Also return the transport when max_rto last changed. Signed-off: Michele Baldessari <michele@acksyn.org> Acked-by: Vlad Yasevich <vyasevich@gmail.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/associola.c10
-rw-r--r--net/sctp/endpointola.c5
-rw-r--r--net/sctp/inqueue.c2
-rw-r--r--net/sctp/output.c14
-rw-r--r--net/sctp/outqueue.c12
-rw-r--r--net/sctp/sm_make_chunk.c5
-rw-r--r--net/sctp/sm_sideeffect.c1
-rw-r--r--net/sctp/sm_statefuns.c10
-rw-r--r--net/sctp/socket.c69
-rw-r--r--net/sctp/transport.c2
10 files changed, 117 insertions, 13 deletions
diff --git a/net/sctp/associola.c b/net/sctp/associola.c
index b1ef3bc301a5..ba3f9cc4c047 100644
--- a/net/sctp/associola.c
+++ b/net/sctp/associola.c
@@ -321,6 +321,9 @@ static struct sctp_association *sctp_association_init(struct sctp_association *a
321 asoc->default_timetolive = sp->default_timetolive; 321 asoc->default_timetolive = sp->default_timetolive;
322 asoc->default_rcv_context = sp->default_rcv_context; 322 asoc->default_rcv_context = sp->default_rcv_context;
323 323
324 /* SCTP_GET_ASSOC_STATS COUNTERS */
325 memset(&asoc->stats, 0, sizeof(struct sctp_priv_assoc_stats));
326
324 /* AUTH related initializations */ 327 /* AUTH related initializations */
325 INIT_LIST_HEAD(&asoc->endpoint_shared_keys); 328 INIT_LIST_HEAD(&asoc->endpoint_shared_keys);
326 err = sctp_auth_asoc_copy_shkeys(ep, asoc, gfp); 329 err = sctp_auth_asoc_copy_shkeys(ep, asoc, gfp);
@@ -760,6 +763,7 @@ struct sctp_transport *sctp_assoc_add_peer(struct sctp_association *asoc,
760 763
761 /* Set the transport's RTO.initial value */ 764 /* Set the transport's RTO.initial value */
762 peer->rto = asoc->rto_initial; 765 peer->rto = asoc->rto_initial;
766 sctp_max_rto(asoc, peer);
763 767
764 /* Set the peer's active state. */ 768 /* Set the peer's active state. */
765 peer->state = peer_state; 769 peer->state = peer_state;
@@ -1152,8 +1156,12 @@ static void sctp_assoc_bh_rcv(struct work_struct *work)
1152 */ 1156 */
1153 if (sctp_chunk_is_data(chunk)) 1157 if (sctp_chunk_is_data(chunk))
1154 asoc->peer.last_data_from = chunk->transport; 1158 asoc->peer.last_data_from = chunk->transport;
1155 else 1159 else {
1156 SCTP_INC_STATS(net, SCTP_MIB_INCTRLCHUNKS); 1160 SCTP_INC_STATS(net, SCTP_MIB_INCTRLCHUNKS);
1161 asoc->stats.ictrlchunks++;
1162 if (chunk->chunk_hdr->type == SCTP_CID_SACK)
1163 asoc->stats.isacks++;
1164 }
1157 1165
1158 if (chunk->transport) 1166 if (chunk->transport)
1159 chunk->transport->last_time_heard = jiffies; 1167 chunk->transport->last_time_heard = jiffies;
diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c
index 1859e2bc83d1..32ab55b18281 100644
--- a/net/sctp/endpointola.c
+++ b/net/sctp/endpointola.c
@@ -480,8 +480,11 @@ normal:
480 */ 480 */
481 if (asoc && sctp_chunk_is_data(chunk)) 481 if (asoc && sctp_chunk_is_data(chunk))
482 asoc->peer.last_data_from = chunk->transport; 482 asoc->peer.last_data_from = chunk->transport;
483 else 483 else {
484 SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_INCTRLCHUNKS); 484 SCTP_INC_STATS(sock_net(ep->base.sk), SCTP_MIB_INCTRLCHUNKS);
485 if (asoc)
486 asoc->stats.ictrlchunks++;
487 }
485 488
486 if (chunk->transport) 489 if (chunk->transport)
487 chunk->transport->last_time_heard = jiffies; 490 chunk->transport->last_time_heard = jiffies;
diff --git a/net/sctp/inqueue.c b/net/sctp/inqueue.c
index 397296fb156f..2d5ad280de38 100644
--- a/net/sctp/inqueue.c
+++ b/net/sctp/inqueue.c
@@ -104,6 +104,8 @@ void sctp_inq_push(struct sctp_inq *q, struct sctp_chunk *chunk)
104 * on the BH related data structures. 104 * on the BH related data structures.
105 */ 105 */
106 list_add_tail(&chunk->list, &q->in_chunk_list); 106 list_add_tail(&chunk->list, &q->in_chunk_list);
107 if (chunk->asoc)
108 chunk->asoc->stats.ipackets++;
107 q->immediate.func(&q->immediate); 109 q->immediate.func(&q->immediate);
108} 110}
109 111
diff --git a/net/sctp/output.c b/net/sctp/output.c
index 4e90188bf489..f5200a2ad852 100644
--- a/net/sctp/output.c
+++ b/net/sctp/output.c
@@ -311,6 +311,8 @@ static sctp_xmit_t __sctp_packet_append_chunk(struct sctp_packet *packet,
311 311
312 case SCTP_CID_SACK: 312 case SCTP_CID_SACK:
313 packet->has_sack = 1; 313 packet->has_sack = 1;
314 if (chunk->asoc)
315 chunk->asoc->stats.osacks++;
314 break; 316 break;
315 317
316 case SCTP_CID_AUTH: 318 case SCTP_CID_AUTH:
@@ -584,11 +586,13 @@ int sctp_packet_transmit(struct sctp_packet *packet)
584 */ 586 */
585 587
586 /* Dump that on IP! */ 588 /* Dump that on IP! */
587 if (asoc && asoc->peer.last_sent_to != tp) { 589 if (asoc) {
588 /* Considering the multiple CPU scenario, this is a 590 asoc->stats.opackets++;
589 * "correcter" place for last_sent_to. --xguo 591 if (asoc->peer.last_sent_to != tp)
590 */ 592 /* Considering the multiple CPU scenario, this is a
591 asoc->peer.last_sent_to = tp; 593 * "correcter" place for last_sent_to. --xguo
594 */
595 asoc->peer.last_sent_to = tp;
592 } 596 }
593 597
594 if (has_data) { 598 if (has_data) {
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 1b4a7f8ec3fd..379c81dee9d1 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -667,6 +667,7 @@ redo:
667 chunk->fast_retransmit = SCTP_DONT_FRTX; 667 chunk->fast_retransmit = SCTP_DONT_FRTX;
668 668
669 q->empty = 0; 669 q->empty = 0;
670 q->asoc->stats.rtxchunks++;
670 break; 671 break;
671 } 672 }
672 673
@@ -876,12 +877,14 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
876 if (status != SCTP_XMIT_OK) { 877 if (status != SCTP_XMIT_OK) {
877 /* put the chunk back */ 878 /* put the chunk back */
878 list_add(&chunk->list, &q->control_chunk_list); 879 list_add(&chunk->list, &q->control_chunk_list);
879 } else if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN) { 880 } else {
881 asoc->stats.octrlchunks++;
880 /* PR-SCTP C5) If a FORWARD TSN is sent, the 882 /* PR-SCTP C5) If a FORWARD TSN is sent, the
881 * sender MUST assure that at least one T3-rtx 883 * sender MUST assure that at least one T3-rtx
882 * timer is running. 884 * timer is running.
883 */ 885 */
884 sctp_transport_reset_timers(transport); 886 if (chunk->chunk_hdr->type == SCTP_CID_FWD_TSN)
887 sctp_transport_reset_timers(transport);
885 } 888 }
886 break; 889 break;
887 890
@@ -1055,6 +1058,10 @@ static int sctp_outq_flush(struct sctp_outq *q, int rtx_timeout)
1055 */ 1058 */
1056 if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING) 1059 if (asoc->state == SCTP_STATE_SHUTDOWN_PENDING)
1057 chunk->chunk_hdr->flags |= SCTP_DATA_SACK_IMM; 1060 chunk->chunk_hdr->flags |= SCTP_DATA_SACK_IMM;
1061 if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED)
1062 asoc->stats.ouodchunks++;
1063 else
1064 asoc->stats.oodchunks++;
1058 1065
1059 break; 1066 break;
1060 1067
@@ -1162,6 +1169,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk)
1162 1169
1163 sack_ctsn = ntohl(sack->cum_tsn_ack); 1170 sack_ctsn = ntohl(sack->cum_tsn_ack);
1164 gap_ack_blocks = ntohs(sack->num_gap_ack_blocks); 1171 gap_ack_blocks = ntohs(sack->num_gap_ack_blocks);
1172 asoc->stats.gapcnt += gap_ack_blocks;
1165 /* 1173 /*
1166 * SFR-CACC algorithm: 1174 * SFR-CACC algorithm:
1167 * On receipt of a SACK the sender SHOULD execute the 1175 * On receipt of a SACK the sender SHOULD execute the
diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c
index e0f01a4e8cd6..e1c5fc2be6b8 100644
--- a/net/sctp/sm_make_chunk.c
+++ b/net/sctp/sm_make_chunk.c
@@ -804,10 +804,11 @@ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc)
804 gabs); 804 gabs);
805 805
806 /* Add the duplicate TSN information. */ 806 /* Add the duplicate TSN information. */
807 if (num_dup_tsns) 807 if (num_dup_tsns) {
808 aptr->stats.idupchunks += num_dup_tsns;
808 sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns, 809 sctp_addto_chunk(retval, sizeof(__u32) * num_dup_tsns,
809 sctp_tsnmap_get_dups(map)); 810 sctp_tsnmap_get_dups(map));
810 811 }
811 /* Once we have a sack generated, check to see what our sack 812 /* Once we have a sack generated, check to see what our sack
812 * generation is, if its 0, reset the transports to 0, and reset 813 * generation is, if its 0, reset the transports to 0, and reset
813 * the association generation to 1 814 * the association generation to 1
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index c0769569b05d..c9577754a708 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -542,6 +542,7 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands,
542 */ 542 */
543 if (!is_hb || transport->hb_sent) { 543 if (!is_hb || transport->hb_sent) {
544 transport->rto = min((transport->rto * 2), transport->asoc->rto_max); 544 transport->rto = min((transport->rto * 2), transport->asoc->rto_max);
545 sctp_max_rto(asoc, transport);
545 } 546 }
546} 547}
547 548
diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c
index e92079d27eae..ebcd1eedb115 100644
--- a/net/sctp/sm_statefuns.c
+++ b/net/sctp/sm_statefuns.c
@@ -6133,6 +6133,8 @@ static int sctp_eat_data(const struct sctp_association *asoc,
6133 /* The TSN is too high--silently discard the chunk and 6133 /* The TSN is too high--silently discard the chunk and
6134 * count on it getting retransmitted later. 6134 * count on it getting retransmitted later.
6135 */ 6135 */
6136 if (chunk->asoc)
6137 chunk->asoc->stats.outofseqtsns++;
6136 return SCTP_IERROR_HIGH_TSN; 6138 return SCTP_IERROR_HIGH_TSN;
6137 } else if (tmp > 0) { 6139 } else if (tmp > 0) {
6138 /* This is a duplicate. Record it. */ 6140 /* This is a duplicate. Record it. */
@@ -6232,10 +6234,14 @@ static int sctp_eat_data(const struct sctp_association *asoc,
6232 /* Note: Some chunks may get overcounted (if we drop) or overcounted 6234 /* Note: Some chunks may get overcounted (if we drop) or overcounted
6233 * if we renege and the chunk arrives again. 6235 * if we renege and the chunk arrives again.
6234 */ 6236 */
6235 if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) 6237 if (chunk->chunk_hdr->flags & SCTP_DATA_UNORDERED) {
6236 SCTP_INC_STATS(net, SCTP_MIB_INUNORDERCHUNKS); 6238 SCTP_INC_STATS(net, SCTP_MIB_INUNORDERCHUNKS);
6237 else { 6239 if (chunk->asoc)
6240 chunk->asoc->stats.iuodchunks++;
6241 } else {
6238 SCTP_INC_STATS(net, SCTP_MIB_INORDERCHUNKS); 6242 SCTP_INC_STATS(net, SCTP_MIB_INORDERCHUNKS);
6243 if (chunk->asoc)
6244 chunk->asoc->stats.iodchunks++;
6239 ordered = 1; 6245 ordered = 1;
6240 } 6246 }
6241 6247
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index bc1624913c42..9e65758cb038 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -611,6 +611,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
611 2*asoc->pathmtu, 4380)); 611 2*asoc->pathmtu, 4380));
612 trans->ssthresh = asoc->peer.i.a_rwnd; 612 trans->ssthresh = asoc->peer.i.a_rwnd;
613 trans->rto = asoc->rto_initial; 613 trans->rto = asoc->rto_initial;
614 sctp_max_rto(asoc, trans);
614 trans->rtt = trans->srtt = trans->rttvar = 0; 615 trans->rtt = trans->srtt = trans->rttvar = 0;
615 sctp_transport_route(trans, NULL, 616 sctp_transport_route(trans, NULL,
616 sctp_sk(asoc->base.sk)); 617 sctp_sk(asoc->base.sk));
@@ -5635,6 +5636,71 @@ static int sctp_getsockopt_paddr_thresholds(struct sock *sk,
5635 return 0; 5636 return 0;
5636} 5637}
5637 5638
5639/*
5640 * SCTP_GET_ASSOC_STATS
5641 *
5642 * This option retrieves local per endpoint statistics. It is modeled
5643 * after OpenSolaris' implementation
5644 */
5645static int sctp_getsockopt_assoc_stats(struct sock *sk, int len,
5646 char __user *optval,
5647 int __user *optlen)
5648{
5649 struct sctp_assoc_stats sas;
5650 struct sctp_association *asoc = NULL;
5651
5652 /* User must provide at least the assoc id */
5653 if (len < sizeof(sctp_assoc_t))
5654 return -EINVAL;
5655
5656 if (copy_from_user(&sas, optval, len))
5657 return -EFAULT;
5658
5659 asoc = sctp_id2assoc(sk, sas.sas_assoc_id);
5660 if (!asoc)
5661 return -EINVAL;
5662
5663 sas.sas_rtxchunks = asoc->stats.rtxchunks;
5664 sas.sas_gapcnt = asoc->stats.gapcnt;
5665 sas.sas_outofseqtsns = asoc->stats.outofseqtsns;
5666 sas.sas_osacks = asoc->stats.osacks;
5667 sas.sas_isacks = asoc->stats.isacks;
5668 sas.sas_octrlchunks = asoc->stats.octrlchunks;
5669 sas.sas_ictrlchunks = asoc->stats.ictrlchunks;
5670 sas.sas_oodchunks = asoc->stats.oodchunks;
5671 sas.sas_iodchunks = asoc->stats.iodchunks;
5672 sas.sas_ouodchunks = asoc->stats.ouodchunks;
5673 sas.sas_iuodchunks = asoc->stats.iuodchunks;
5674 sas.sas_idupchunks = asoc->stats.idupchunks;
5675 sas.sas_opackets = asoc->stats.opackets;
5676 sas.sas_ipackets = asoc->stats.ipackets;
5677
5678 /* New high max rto observed, will return 0 if not a single
5679 * RTO update took place. obs_rto_ipaddr will be bogus
5680 * in such a case
5681 */
5682 sas.sas_maxrto = asoc->stats.max_obs_rto;
5683 memcpy(&sas.sas_obs_rto_ipaddr, &asoc->stats.obs_rto_ipaddr,
5684 sizeof(struct sockaddr_storage));
5685
5686 /* Mark beginning of a new observation period */
5687 asoc->stats.max_obs_rto = asoc->rto_min;
5688
5689 /* Allow the struct to grow and fill in as much as possible */
5690 len = min_t(size_t, len, sizeof(sas));
5691
5692 if (put_user(len, optlen))
5693 return -EFAULT;
5694
5695 SCTP_DEBUG_PRINTK("sctp_getsockopt_assoc_stat(%d): %d\n",
5696 len, sas.sas_assoc_id);
5697
5698 if (copy_to_user(optval, &sas, len))
5699 return -EFAULT;
5700
5701 return 0;
5702}
5703
5638SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, 5704SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
5639 char __user *optval, int __user *optlen) 5705 char __user *optval, int __user *optlen)
5640{ 5706{
@@ -5776,6 +5842,9 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname,
5776 case SCTP_PEER_ADDR_THLDS: 5842 case SCTP_PEER_ADDR_THLDS:
5777 retval = sctp_getsockopt_paddr_thresholds(sk, optval, len, optlen); 5843 retval = sctp_getsockopt_paddr_thresholds(sk, optval, len, optlen);
5778 break; 5844 break;
5845 case SCTP_GET_ASSOC_STATS:
5846 retval = sctp_getsockopt_assoc_stats(sk, len, optval, optlen);
5847 break;
5779 default: 5848 default:
5780 retval = -ENOPROTOOPT; 5849 retval = -ENOPROTOOPT;
5781 break; 5850 break;
diff --git a/net/sctp/transport.c b/net/sctp/transport.c
index 206cf5238fd3..310f11eb2206 100644
--- a/net/sctp/transport.c
+++ b/net/sctp/transport.c
@@ -363,6 +363,7 @@ void sctp_transport_update_rto(struct sctp_transport *tp, __u32 rtt)
363 if (tp->rto > tp->asoc->rto_max) 363 if (tp->rto > tp->asoc->rto_max)
364 tp->rto = tp->asoc->rto_max; 364 tp->rto = tp->asoc->rto_max;
365 365
366 sctp_max_rto(tp->asoc, tp);
366 tp->rtt = rtt; 367 tp->rtt = rtt;
367 368
368 /* Reset rto_pending so that a new RTT measurement is started when a 369 /* Reset rto_pending so that a new RTT measurement is started when a
@@ -620,6 +621,7 @@ void sctp_transport_reset(struct sctp_transport *t)
620 t->burst_limited = 0; 621 t->burst_limited = 0;
621 t->ssthresh = asoc->peer.i.a_rwnd; 622 t->ssthresh = asoc->peer.i.a_rwnd;
622 t->rto = asoc->rto_initial; 623 t->rto = asoc->rto_initial;
624 sctp_max_rto(asoc, t);
623 t->rtt = 0; 625 t->rtt = 0;
624 t->srtt = 0; 626 t->srtt = 0;
625 t->rttvar = 0; 627 t->rttvar = 0;