sctp: add SCTP_PR_STREAM_STATUS sockopt for prsctp
authorXin Long <lucien.xin@gmail.com>
Sat, 1 Apr 2017 09:07:46 +0000 (17:07 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 3 Apr 2017 21:52:35 +0000 (14:52 -0700)
Before when implementing sctp prsctp, SCTP_PR_STREAM_STATUS wasn't
added, as it needs to save abandoned_(un)sent for every stream.

After sctp stream reconf is added in sctp, assoc has structure
sctp_stream_out to save per stream info.

This patch is to add SCTP_PR_STREAM_STATUS by putting the prsctp
per stream statistics into sctp_stream_out.

v1->v2:
  fix an indent issue.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/structs.h
include/uapi/linux/sctp.h
net/sctp/chunk.c
net/sctp/outqueue.c
net/sctp/socket.c

index 592decebac752ffca16fddbc75cbf14f9283d125..3e61a54424a177281b3338c67051580fa9a1610d 100644 (file)
@@ -1315,6 +1315,8 @@ struct sctp_inithdr_host {
 struct sctp_stream_out {
        __u16   ssn;
        __u8    state;
+       __u64   abandoned_unsent[SCTP_PR_INDEX(MAX) + 1];
+       __u64   abandoned_sent[SCTP_PR_INDEX(MAX) + 1];
 };
 
 struct sctp_stream_in {
index 7212870ef5d795ecf5cdebc414a8d97e607cac42..ced9d8b974268ed270661c3e2da77165e3a24784 100644 (file)
@@ -115,6 +115,7 @@ typedef __s32 sctp_assoc_t;
 #define SCTP_PR_SUPPORTED      113
 #define SCTP_DEFAULT_PRINFO    114
 #define SCTP_PR_ASSOC_STATUS   115
+#define SCTP_PR_STREAM_STATUS  116
 #define SCTP_RECONFIG_SUPPORTED        117
 #define SCTP_ENABLE_STREAM_RESET       118
 #define SCTP_RESET_STREAMS     119
index e3621cb4827fadb5f5cb41ebe8455dfa3300a765..697721a7a3f1761373aa66b847bd744ea1b42d10 100644 (file)
@@ -306,14 +306,24 @@ int sctp_chunk_abandoned(struct sctp_chunk *chunk)
 
        if (SCTP_PR_TTL_ENABLED(chunk->sinfo.sinfo_flags) &&
            time_after(jiffies, chunk->msg->expires_at)) {
-               if (chunk->sent_count)
+               struct sctp_stream_out *streamout =
+                       &chunk->asoc->stream->out[chunk->sinfo.sinfo_stream];
+
+               if (chunk->sent_count) {
                        chunk->asoc->abandoned_sent[SCTP_PR_INDEX(TTL)]++;
-               else
+                       streamout->abandoned_sent[SCTP_PR_INDEX(TTL)]++;
+               } else {
                        chunk->asoc->abandoned_unsent[SCTP_PR_INDEX(TTL)]++;
+                       streamout->abandoned_unsent[SCTP_PR_INDEX(TTL)]++;
+               }
                return 1;
        } else if (SCTP_PR_RTX_ENABLED(chunk->sinfo.sinfo_flags) &&
                   chunk->sent_count > chunk->sinfo.sinfo_timetolive) {
+               struct sctp_stream_out *streamout =
+                       &chunk->asoc->stream->out[chunk->sinfo.sinfo_stream];
+
                chunk->asoc->abandoned_sent[SCTP_PR_INDEX(RTX)]++;
+               streamout->abandoned_sent[SCTP_PR_INDEX(RTX)]++;
                return 1;
        } else if (!SCTP_PR_POLICY(chunk->sinfo.sinfo_flags) &&
                   chunk->msg->expires_at &&
index 025ccff670720bd38b9cc9bd995b87e134c9f931..3f78d7f06e14780f2bfe88e18a73e658b0e307e1 100644 (file)
@@ -353,6 +353,8 @@ static int sctp_prsctp_prune_sent(struct sctp_association *asoc,
        struct sctp_chunk *chk, *temp;
 
        list_for_each_entry_safe(chk, temp, queue, transmitted_list) {
+               struct sctp_stream_out *streamout;
+
                if (!SCTP_PR_PRIO_ENABLED(chk->sinfo.sinfo_flags) ||
                    chk->sinfo.sinfo_timetolive <= sinfo->sinfo_timetolive)
                        continue;
@@ -361,8 +363,10 @@ static int sctp_prsctp_prune_sent(struct sctp_association *asoc,
                sctp_insert_list(&asoc->outqueue.abandoned,
                                 &chk->transmitted_list);
 
+               streamout = &asoc->stream->out[chk->sinfo.sinfo_stream];
                asoc->sent_cnt_removable--;
                asoc->abandoned_sent[SCTP_PR_INDEX(PRIO)]++;
+               streamout->abandoned_sent[SCTP_PR_INDEX(PRIO)]++;
 
                if (!chk->tsn_gap_acked) {
                        if (chk->transport)
@@ -396,6 +400,12 @@ static int sctp_prsctp_prune_unsent(struct sctp_association *asoc,
                q->out_qlen -= chk->skb->len;
                asoc->sent_cnt_removable--;
                asoc->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
+               if (chk->sinfo.sinfo_stream < asoc->stream->outcnt) {
+                       struct sctp_stream_out *streamout =
+                               &asoc->stream->out[chk->sinfo.sinfo_stream];
+
+                       streamout->abandoned_unsent[SCTP_PR_INDEX(PRIO)]++;
+               }
 
                msg_len -= SCTP_DATA_SNDSIZE(chk) +
                           sizeof(struct sk_buff) +
index ccc08fc39722a7fb82af7c1d9be326c96735cfcd..6489446925e60bec6e31b1dab277b0f1316d7a6f 100644 (file)
@@ -6576,6 +6576,61 @@ out:
        return retval;
 }
 
+static int sctp_getsockopt_pr_streamstatus(struct sock *sk, int len,
+                                          char __user *optval,
+                                          int __user *optlen)
+{
+       struct sctp_stream_out *streamout;
+       struct sctp_association *asoc;
+       struct sctp_prstatus params;
+       int retval = -EINVAL;
+       int policy;
+
+       if (len < sizeof(params))
+               goto out;
+
+       len = sizeof(params);
+       if (copy_from_user(&params, optval, len)) {
+               retval = -EFAULT;
+               goto out;
+       }
+
+       policy = params.sprstat_policy;
+       if (policy & ~SCTP_PR_SCTP_MASK)
+               goto out;
+
+       asoc = sctp_id2assoc(sk, params.sprstat_assoc_id);
+       if (!asoc || params.sprstat_sid >= asoc->stream->outcnt)
+               goto out;
+
+       streamout = &asoc->stream->out[params.sprstat_sid];
+       if (policy == SCTP_PR_SCTP_NONE) {
+               params.sprstat_abandoned_unsent = 0;
+               params.sprstat_abandoned_sent = 0;
+               for (policy = 0; policy <= SCTP_PR_INDEX(MAX); policy++) {
+                       params.sprstat_abandoned_unsent +=
+                               streamout->abandoned_unsent[policy];
+                       params.sprstat_abandoned_sent +=
+                               streamout->abandoned_sent[policy];
+               }
+       } else {
+               params.sprstat_abandoned_unsent =
+                       streamout->abandoned_unsent[__SCTP_PR_INDEX(policy)];
+               params.sprstat_abandoned_sent =
+                       streamout->abandoned_sent[__SCTP_PR_INDEX(policy)];
+       }
+
+       if (put_user(len, optlen) || copy_to_user(optval, &params, len)) {
+               retval = -EFAULT;
+               goto out;
+       }
+
+       retval = 0;
+
+out:
+       return retval;
+}
+
 static int sctp_getsockopt_reconfig_supported(struct sock *sk, int len,
                                              char __user *optval,
                                              int __user *optlen)
@@ -6825,6 +6880,10 @@ static int sctp_getsockopt(struct sock *sk, int level, int optname,
                retval = sctp_getsockopt_pr_assocstatus(sk, len, optval,
                                                        optlen);
                break;
+       case SCTP_PR_STREAM_STATUS:
+               retval = sctp_getsockopt_pr_streamstatus(sk, len, optval,
+                                                        optlen);
+               break;
        case SCTP_RECONFIG_SUPPORTED:
                retval = sctp_getsockopt_reconfig_supported(sk, len, optval,
                                                            optlen);