sctp: implement abort_pd for sctp_stream_interleave
authorXin Long <lucien.xin@gmail.com>
Fri, 8 Dec 2017 13:04:08 +0000 (21:04 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Dec 2017 16:23:05 +0000 (11:23 -0500)
abort_pd is added as a member of sctp_stream_interleave, used to abort
partial delivery for data or idata, called in sctp_cmd_assoc_failed.

Since stream interleave allows to do partial delivery for each stream
at the same time, sctp_intl_abort_pd for idata would be very different
from the old function sctp_ulpq_abort_pd for data.

Note that sctp_ulpevent_make_pdapi will support per stream in this
patch by adding pdapi_stream and pdapi_seq in sctp_pdapi_event, as
described in section 6.1.7 of RFC6458.

Signed-off-by: Xin Long <lucien.xin@gmail.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Acked-by: Neil Horman <nhorman@tuxdriver.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/sctp/stream_interleave.h
include/net/sctp/ulpevent.h
include/uapi/linux/sctp.h
net/sctp/sm_sideeffect.c
net/sctp/stream_interleave.c
net/sctp/ulpevent.c
net/sctp/ulpqueue.c

index 317d9b3a5299585dcd1456c2fed2b03e83b27ba8..501b2be049a3bdcbcd7e3bd2e01eb7b0a254e3ab 100644 (file)
@@ -46,6 +46,7 @@ struct sctp_stream_interleave {
        void    (*renege_events)(struct sctp_ulpq *ulpq,
                                 struct sctp_chunk *chunk, gfp_t gfp);
        void    (*start_pd)(struct sctp_ulpq *ulpq, gfp_t gfp);
+       void    (*abort_pd)(struct sctp_ulpq *ulpq, gfp_t gfp);
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream);
index ce4f2aa35d5665824f701e231a334e21aad032d0..51b4e0626c348c2d4cfa4d105f39914115569e92 100644 (file)
@@ -122,7 +122,8 @@ struct sctp_ulpevent *sctp_ulpevent_make_shutdown_event(
 
 struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
        const struct sctp_association *asoc,
-       __u32 indication, gfp_t gfp);
+       __u32 indication, __u32 sid, __u32 seq,
+       __u32 flags, gfp_t gfp);
 
 struct sctp_ulpevent *sctp_ulpevent_make_adaptation_indication(
        const struct sctp_association *asoc, gfp_t gfp);
index 6ed934c65a5f8fa51214406b92b57088fdb076e8..4c4db14786bd04360e1b44a7e23275d7bb9f882d 100644 (file)
@@ -460,6 +460,8 @@ struct sctp_pdapi_event {
        __u32 pdapi_length;
        __u32 pdapi_indication;
        sctp_assoc_t pdapi_assoc_id;
+       __u32 pdapi_stream;
+       __u32 pdapi_seq;
 };
 
 enum { SCTP_PARTIAL_DELIVERY_ABORTED=0, };
index 36710549a4ca807b81c0424ff91940ff6767f486..8adde71fdb318c68b37c0fa3923fb15b9890ab13 100644 (file)
@@ -632,7 +632,7 @@ static void sctp_cmd_assoc_failed(struct sctp_cmd_seq *commands,
        struct sctp_chunk *abort;
 
        /* Cancel any partial delivery in progress. */
-       sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC);
+       asoc->stream.si->abort_pd(&asoc->ulpq, GFP_ATOMIC);
 
        if (event_type == SCTP_EVENT_T_CHUNK && subtype.chunk == SCTP_CID_ABORT)
                event = sctp_ulpevent_make_assoc_change(asoc, 0, SCTP_COMM_LOST,
index 4dce8d33c5ab790330774c26181a8cab326e6712..d15645ea338b6162d9f7193669eae5f0b1940f4f 100644 (file)
@@ -652,6 +652,103 @@ static void sctp_renege_events(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
        sk_mem_reclaim(asoc->base.sk);
 }
 
+static void sctp_intl_stream_abort_pd(struct sctp_ulpq *ulpq, __u16 sid,
+                                     __u32 mid, __u16 flags, gfp_t gfp)
+{
+       struct sock *sk = ulpq->asoc->base.sk;
+       struct sctp_ulpevent *ev = NULL;
+
+       if (!sctp_ulpevent_type_enabled(SCTP_PARTIAL_DELIVERY_EVENT,
+                                       &sctp_sk(sk)->subscribe))
+               return;
+
+       ev = sctp_ulpevent_make_pdapi(ulpq->asoc, SCTP_PARTIAL_DELIVERY_ABORTED,
+                                     sid, mid, flags, gfp);
+       if (ev) {
+               __skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));
+
+               if (!sctp_sk(sk)->data_ready_signalled) {
+                       sctp_sk(sk)->data_ready_signalled = 1;
+                       sk->sk_data_ready(sk);
+               }
+       }
+}
+
+static void sctp_intl_reap_ordered(struct sctp_ulpq *ulpq, __u16 sid)
+{
+       struct sctp_stream *stream = &ulpq->asoc->stream;
+       struct sctp_ulpevent *cevent, *event = NULL;
+       struct sk_buff_head *lobby = &ulpq->lobby;
+       struct sk_buff *pos, *tmp;
+       struct sk_buff_head temp;
+       __u16 csid;
+       __u32 cmid;
+
+       skb_queue_head_init(&temp);
+       sctp_skb_for_each(pos, lobby, tmp) {
+               cevent = (struct sctp_ulpevent *)pos->cb;
+               csid = cevent->stream;
+               cmid = cevent->mid;
+
+               if (csid > sid)
+                       break;
+
+               if (csid < sid)
+                       continue;
+
+               if (!MID_lt(cmid, sctp_mid_peek(stream, in, csid)))
+                       break;
+
+               __skb_unlink(pos, lobby);
+               if (!event)
+                       event = sctp_skb2event(pos);
+
+               __skb_queue_tail(&temp, pos);
+       }
+
+       if (!event && pos != (struct sk_buff *)lobby) {
+               cevent = (struct sctp_ulpevent *)pos->cb;
+               csid = cevent->stream;
+               cmid = cevent->mid;
+
+               if (csid == sid && cmid == sctp_mid_peek(stream, in, csid)) {
+                       sctp_mid_next(stream, in, csid);
+                       __skb_unlink(pos, lobby);
+                       __skb_queue_tail(&temp, pos);
+                       event = sctp_skb2event(pos);
+               }
+       }
+
+       if (event) {
+               sctp_intl_retrieve_ordered(ulpq, event);
+               sctp_enqueue_event(ulpq, event);
+       }
+}
+
+static void sctp_intl_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
+{
+       struct sctp_stream *stream = &ulpq->asoc->stream;
+       __u16 sid;
+
+       for (sid = 0; sid < stream->incnt; sid++) {
+               struct sctp_stream_in *sin = &stream->in[sid];
+               __u32 mid;
+
+               if (sin->pd_mode) {
+                       sin->pd_mode = 0;
+
+                       mid = sin->mid;
+                       sctp_intl_stream_abort_pd(ulpq, sid, mid, 0, gfp);
+                       sctp_mid_skip(stream, in, sid, mid);
+
+                       sctp_intl_reap_ordered(ulpq, sid);
+               }
+       }
+
+       /* intl abort pd happens only when all data needs to be cleaned */
+       sctp_ulpq_flush(ulpq);
+}
+
 static struct sctp_stream_interleave sctp_stream_interleave_0 = {
        .data_chunk_len         = sizeof(struct sctp_data_chunk),
        /* DATA process functions */
@@ -662,6 +759,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_0 = {
        .enqueue_event          = sctp_ulpq_tail_event,
        .renege_events          = sctp_ulpq_renege,
        .start_pd               = sctp_ulpq_partial_delivery,
+       .abort_pd               = sctp_ulpq_abort_pd,
 };
 
 static struct sctp_stream_interleave sctp_stream_interleave_1 = {
@@ -674,6 +772,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_1 = {
        .enqueue_event          = sctp_enqueue_event,
        .renege_events          = sctp_renege_events,
        .start_pd               = sctp_intl_start_pd,
+       .abort_pd               = sctp_intl_abort_pd,
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream)
index d3218f3e9cf72484244a4f20549fa4b47ab73337..84207ad33e8e9270d0fa1ddf61a514cc9315656d 100644 (file)
@@ -730,8 +730,9 @@ fail:
  *   various events.
  */
 struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
-       const struct sctp_association *asoc, __u32 indication,
-       gfp_t gfp)
+                                       const struct sctp_association *asoc,
+                                       __u32 indication, __u32 sid, __u32 seq,
+                                       __u32 flags, gfp_t gfp)
 {
        struct sctp_ulpevent *event;
        struct sctp_pdapi_event *pd;
@@ -752,7 +753,9 @@ struct sctp_ulpevent *sctp_ulpevent_make_pdapi(
         *   Currently unused.
         */
        pd->pdapi_type = SCTP_PARTIAL_DELIVERY_EVENT;
-       pd->pdapi_flags = 0;
+       pd->pdapi_flags = flags;
+       pd->pdapi_stream = sid;
+       pd->pdapi_seq = seq;
 
        /* pdapi_length: 32 bits (unsigned integer)
         *
index 76ec5149a093d30ee0061924fcd8b10ad3bfe4ef..dd53daab4a25fef3a97f63ba3b3c222a65bcf882 100644 (file)
@@ -1144,7 +1144,7 @@ void sctp_ulpq_abort_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
                                       &sctp_sk(sk)->subscribe))
                ev = sctp_ulpevent_make_pdapi(ulpq->asoc,
                                              SCTP_PARTIAL_DELIVERY_ABORTED,
-                                             gfp);
+                                             0, 0, 0, gfp);
        if (ev)
                __skb_queue_tail(&sk->sk_receive_queue, sctp_event2skb(ev));