sctp: implement renege_events for sctp_stream_interleave
authorXin Long <lucien.xin@gmail.com>
Fri, 8 Dec 2017 13:04:06 +0000 (21:04 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 11 Dec 2017 16:23:05 +0000 (11:23 -0500)
renege_events is added as a member of sctp_stream_interleave, used to
renege some old data or idata in reasm or lobby queue properly to free
some memory for the new data when there's memory stress.

It defines sctp_renege_events for idata, and leaves sctp_ulpq_renege
as it is for data.

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/ulpqueue.h
net/sctp/sm_sideeffect.c
net/sctp/stream_interleave.c
net/sctp/ulpqueue.c

index a0f61bc08ae8a8d3b5e39c4239628faa3f781f6a..16a71cb2b098e9549f259f735703ec5e7e80b73b 100644 (file)
@@ -43,6 +43,8 @@ struct sctp_stream_interleave {
                                 struct sctp_chunk *chunk, gfp_t gfp);
        int     (*enqueue_event)(struct sctp_ulpq *ulpq,
                                 struct sctp_ulpevent *event);
+       void    (*renege_events)(struct sctp_ulpq *ulpq,
+                                struct sctp_chunk *chunk, gfp_t gfp);
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream);
index e0dce07b87947ca11a1af9b2c8fec10036746633..eb98c7150a56bf8b3c41ddf595c043200975cf80 100644 (file)
@@ -76,11 +76,8 @@ int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc);
 void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn);
 
 void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *, __u32);
-#endif /* __sctp_ulpqueue_h__ */
-
-
-
-
-
 
+__u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,
+                           struct sk_buff_head *list, __u16 needed);
 
+#endif /* __sctp_ulpqueue_h__ */
index f4e5ecade936748d13b7e79af1cf925ecc04c46f..2bec17ad7fc91d9bb4ad39d25226d004ff50e176 100644 (file)
@@ -1735,8 +1735,9 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
                        break;
 
                case SCTP_CMD_RENEGE:
-                       sctp_ulpq_renege(&asoc->ulpq, cmd->obj.chunk,
-                                        GFP_ATOMIC);
+                       asoc->stream.si->renege_events(&asoc->ulpq,
+                                                      cmd->obj.chunk,
+                                                      GFP_ATOMIC);
                        break;
 
                case SCTP_CMD_SETUP_T4:
index e85397200230ade71e19cc841ec0f9660cb506c7..d62ad5c6209261ae64f8cd9760a6b33c548a6e14 100644 (file)
@@ -545,6 +545,113 @@ static int sctp_ulpevent_idata(struct sctp_ulpq *ulpq,
        return event_eor;
 }
 
+static struct sctp_ulpevent *sctp_intl_retrieve_first(struct sctp_ulpq *ulpq)
+{
+       struct sctp_stream_in *csin, *sin = NULL;
+       struct sk_buff *first_frag = NULL;
+       struct sk_buff *last_frag = NULL;
+       struct sctp_ulpevent *retval;
+       struct sk_buff *pos;
+       __u32 next_fsn = 0;
+       __u16 sid = 0;
+
+       skb_queue_walk(&ulpq->reasm, pos) {
+               struct sctp_ulpevent *cevent = sctp_skb2event(pos);
+
+               csin = sctp_stream_in(ulpq->asoc, cevent->stream);
+               if (csin->pd_mode)
+                       continue;
+
+               switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
+               case SCTP_DATA_FIRST_FRAG:
+                       if (first_frag)
+                               goto out;
+                       if (cevent->mid == csin->mid) {
+                               first_frag = pos;
+                               last_frag = pos;
+                               next_fsn = 0;
+                               sin = csin;
+                               sid = cevent->stream;
+                       }
+                       break;
+               case SCTP_DATA_MIDDLE_FRAG:
+                       if (!first_frag)
+                               break;
+                       if (cevent->stream == sid &&
+                           cevent->mid == sin->mid &&
+                           cevent->fsn == next_fsn) {
+                               next_fsn++;
+                               last_frag = pos;
+                       } else {
+                               goto out;
+                       }
+                       break;
+               case SCTP_DATA_LAST_FRAG:
+                       if (first_frag)
+                               goto out;
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       if (!first_frag)
+               return NULL;
+
+out:
+       retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
+                                            &ulpq->reasm, first_frag,
+                                            last_frag);
+       if (retval) {
+               sin->fsn = next_fsn;
+               sin->pd_mode = 1;
+       }
+
+       return retval;
+}
+
+static void sctp_intl_start_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
+{
+       struct sctp_ulpevent *event;
+
+       if (skb_queue_empty(&ulpq->reasm))
+               return;
+
+       do {
+               event = sctp_intl_retrieve_first(ulpq);
+               if (event)
+                       sctp_enqueue_event(ulpq, event);
+       } while (event);
+}
+
+static void sctp_renege_events(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
+                              gfp_t gfp)
+{
+       struct sctp_association *asoc = ulpq->asoc;
+       __u32 freed = 0;
+       __u16 needed;
+
+       if (chunk) {
+               needed = ntohs(chunk->chunk_hdr->length);
+               needed -= sizeof(struct sctp_idata_chunk);
+       } else {
+               needed = SCTP_DEFAULT_MAXWINDOW;
+       }
+
+       if (skb_queue_empty(&asoc->base.sk->sk_receive_queue)) {
+               freed = sctp_ulpq_renege_list(ulpq, &ulpq->lobby, needed);
+               if (freed < needed)
+                       freed += sctp_ulpq_renege_list(ulpq, &ulpq->reasm,
+                                                      needed);
+       }
+
+       if (chunk && freed >= needed)
+               if (sctp_ulpevent_idata(ulpq, chunk, gfp) <= 0)
+                       sctp_intl_start_pd(ulpq, gfp);
+
+       sk_mem_reclaim(asoc->base.sk);
+}
+
 static struct sctp_stream_interleave sctp_stream_interleave_0 = {
        .data_chunk_len         = sizeof(struct sctp_data_chunk),
        /* DATA process functions */
@@ -553,6 +660,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_0 = {
        .validate_data          = sctp_validate_data,
        .ulpevent_data          = sctp_ulpq_tail_data,
        .enqueue_event          = sctp_ulpq_tail_event,
+       .renege_events          = sctp_ulpq_renege,
 };
 
 static struct sctp_stream_interleave sctp_stream_interleave_1 = {
@@ -563,6 +671,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_1 = {
        .validate_data          = sctp_validate_idata,
        .ulpevent_data          = sctp_ulpevent_idata,
        .enqueue_event          = sctp_enqueue_event,
+       .renege_events          = sctp_renege_events,
 };
 
 void sctp_stream_interleave_init(struct sctp_stream *stream)
index 0d07f2a6cb356871c2bc846a21959cf96c3fd329..76ec5149a093d30ee0061924fcd8b10ad3bfe4ef 100644 (file)
@@ -978,8 +978,8 @@ void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
        sctp_ulpq_reap_ordered(ulpq, sid);
 }
 
-static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,
-               struct sk_buff_head *list, __u16 needed)
+__u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq, struct sk_buff_head *list,
+                           __u16 needed)
 {
        __u16 freed = 0;
        __u32 tsn, last_tsn;