summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorXin Long <lucien.xin@gmail.com>2017-12-08 08:04:06 -0500
committerDavid S. Miller <davem@davemloft.net>2017-12-11 11:23:05 -0500
commit94014e8d871ae43d834828710c098518be44b5d9 (patch)
tree2be3b55d238b96afc9d6c2d91eeffff4d75aa2a1
parent9162e0ed9e238c1f1d738cb36ee59d96b097f8e1 (diff)
sctp: implement renege_events for sctp_stream_interleave
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>
-rw-r--r--include/net/sctp/stream_interleave.h2
-rw-r--r--include/net/sctp/ulpqueue.h9
-rw-r--r--net/sctp/sm_sideeffect.c5
-rw-r--r--net/sctp/stream_interleave.c109
-rw-r--r--net/sctp/ulpqueue.c4
5 files changed, 119 insertions, 10 deletions
diff --git a/include/net/sctp/stream_interleave.h b/include/net/sctp/stream_interleave.h
index a0f61bc08ae8..16a71cb2b098 100644
--- a/include/net/sctp/stream_interleave.h
+++ b/include/net/sctp/stream_interleave.h
@@ -43,6 +43,8 @@ struct sctp_stream_interleave {
43 struct sctp_chunk *chunk, gfp_t gfp); 43 struct sctp_chunk *chunk, gfp_t gfp);
44 int (*enqueue_event)(struct sctp_ulpq *ulpq, 44 int (*enqueue_event)(struct sctp_ulpq *ulpq,
45 struct sctp_ulpevent *event); 45 struct sctp_ulpevent *event);
46 void (*renege_events)(struct sctp_ulpq *ulpq,
47 struct sctp_chunk *chunk, gfp_t gfp);
46}; 48};
47 49
48void sctp_stream_interleave_init(struct sctp_stream *stream); 50void sctp_stream_interleave_init(struct sctp_stream *stream);
diff --git a/include/net/sctp/ulpqueue.h b/include/net/sctp/ulpqueue.h
index e0dce07b8794..eb98c7150a56 100644
--- a/include/net/sctp/ulpqueue.h
+++ b/include/net/sctp/ulpqueue.h
@@ -76,11 +76,8 @@ int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc);
76void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn); 76void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn);
77 77
78void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *, __u32); 78void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *, __u32);
79#endif /* __sctp_ulpqueue_h__ */
80
81
82
83
84
85 79
80__u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq,
81 struct sk_buff_head *list, __u16 needed);
86 82
83#endif /* __sctp_ulpqueue_h__ */
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index f4e5ecade936..2bec17ad7fc9 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1735,8 +1735,9 @@ static int sctp_cmd_interpreter(enum sctp_event event_type,
1735 break; 1735 break;
1736 1736
1737 case SCTP_CMD_RENEGE: 1737 case SCTP_CMD_RENEGE:
1738 sctp_ulpq_renege(&asoc->ulpq, cmd->obj.chunk, 1738 asoc->stream.si->renege_events(&asoc->ulpq,
1739 GFP_ATOMIC); 1739 cmd->obj.chunk,
1740 GFP_ATOMIC);
1740 break; 1741 break;
1741 1742
1742 case SCTP_CMD_SETUP_T4: 1743 case SCTP_CMD_SETUP_T4:
diff --git a/net/sctp/stream_interleave.c b/net/sctp/stream_interleave.c
index e85397200230..d62ad5c62092 100644
--- a/net/sctp/stream_interleave.c
+++ b/net/sctp/stream_interleave.c
@@ -545,6 +545,113 @@ static int sctp_ulpevent_idata(struct sctp_ulpq *ulpq,
545 return event_eor; 545 return event_eor;
546} 546}
547 547
548static struct sctp_ulpevent *sctp_intl_retrieve_first(struct sctp_ulpq *ulpq)
549{
550 struct sctp_stream_in *csin, *sin = NULL;
551 struct sk_buff *first_frag = NULL;
552 struct sk_buff *last_frag = NULL;
553 struct sctp_ulpevent *retval;
554 struct sk_buff *pos;
555 __u32 next_fsn = 0;
556 __u16 sid = 0;
557
558 skb_queue_walk(&ulpq->reasm, pos) {
559 struct sctp_ulpevent *cevent = sctp_skb2event(pos);
560
561 csin = sctp_stream_in(ulpq->asoc, cevent->stream);
562 if (csin->pd_mode)
563 continue;
564
565 switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) {
566 case SCTP_DATA_FIRST_FRAG:
567 if (first_frag)
568 goto out;
569 if (cevent->mid == csin->mid) {
570 first_frag = pos;
571 last_frag = pos;
572 next_fsn = 0;
573 sin = csin;
574 sid = cevent->stream;
575 }
576 break;
577 case SCTP_DATA_MIDDLE_FRAG:
578 if (!first_frag)
579 break;
580 if (cevent->stream == sid &&
581 cevent->mid == sin->mid &&
582 cevent->fsn == next_fsn) {
583 next_fsn++;
584 last_frag = pos;
585 } else {
586 goto out;
587 }
588 break;
589 case SCTP_DATA_LAST_FRAG:
590 if (first_frag)
591 goto out;
592 break;
593 default:
594 break;
595 }
596 }
597
598 if (!first_frag)
599 return NULL;
600
601out:
602 retval = sctp_make_reassembled_event(sock_net(ulpq->asoc->base.sk),
603 &ulpq->reasm, first_frag,
604 last_frag);
605 if (retval) {
606 sin->fsn = next_fsn;
607 sin->pd_mode = 1;
608 }
609
610 return retval;
611}
612
613static void sctp_intl_start_pd(struct sctp_ulpq *ulpq, gfp_t gfp)
614{
615 struct sctp_ulpevent *event;
616
617 if (skb_queue_empty(&ulpq->reasm))
618 return;
619
620 do {
621 event = sctp_intl_retrieve_first(ulpq);
622 if (event)
623 sctp_enqueue_event(ulpq, event);
624 } while (event);
625}
626
627static void sctp_renege_events(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk,
628 gfp_t gfp)
629{
630 struct sctp_association *asoc = ulpq->asoc;
631 __u32 freed = 0;
632 __u16 needed;
633
634 if (chunk) {
635 needed = ntohs(chunk->chunk_hdr->length);
636 needed -= sizeof(struct sctp_idata_chunk);
637 } else {
638 needed = SCTP_DEFAULT_MAXWINDOW;
639 }
640
641 if (skb_queue_empty(&asoc->base.sk->sk_receive_queue)) {
642 freed = sctp_ulpq_renege_list(ulpq, &ulpq->lobby, needed);
643 if (freed < needed)
644 freed += sctp_ulpq_renege_list(ulpq, &ulpq->reasm,
645 needed);
646 }
647
648 if (chunk && freed >= needed)
649 if (sctp_ulpevent_idata(ulpq, chunk, gfp) <= 0)
650 sctp_intl_start_pd(ulpq, gfp);
651
652 sk_mem_reclaim(asoc->base.sk);
653}
654
548static struct sctp_stream_interleave sctp_stream_interleave_0 = { 655static struct sctp_stream_interleave sctp_stream_interleave_0 = {
549 .data_chunk_len = sizeof(struct sctp_data_chunk), 656 .data_chunk_len = sizeof(struct sctp_data_chunk),
550 /* DATA process functions */ 657 /* DATA process functions */
@@ -553,6 +660,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_0 = {
553 .validate_data = sctp_validate_data, 660 .validate_data = sctp_validate_data,
554 .ulpevent_data = sctp_ulpq_tail_data, 661 .ulpevent_data = sctp_ulpq_tail_data,
555 .enqueue_event = sctp_ulpq_tail_event, 662 .enqueue_event = sctp_ulpq_tail_event,
663 .renege_events = sctp_ulpq_renege,
556}; 664};
557 665
558static struct sctp_stream_interleave sctp_stream_interleave_1 = { 666static struct sctp_stream_interleave sctp_stream_interleave_1 = {
@@ -563,6 +671,7 @@ static struct sctp_stream_interleave sctp_stream_interleave_1 = {
563 .validate_data = sctp_validate_idata, 671 .validate_data = sctp_validate_idata,
564 .ulpevent_data = sctp_ulpevent_idata, 672 .ulpevent_data = sctp_ulpevent_idata,
565 .enqueue_event = sctp_enqueue_event, 673 .enqueue_event = sctp_enqueue_event,
674 .renege_events = sctp_renege_events,
566}; 675};
567 676
568void sctp_stream_interleave_init(struct sctp_stream *stream) 677void sctp_stream_interleave_init(struct sctp_stream *stream)
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c
index 0d07f2a6cb35..76ec5149a093 100644
--- a/net/sctp/ulpqueue.c
+++ b/net/sctp/ulpqueue.c
@@ -978,8 +978,8 @@ void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn)
978 sctp_ulpq_reap_ordered(ulpq, sid); 978 sctp_ulpq_reap_ordered(ulpq, sid);
979} 979}
980 980
981static __u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq, 981__u16 sctp_ulpq_renege_list(struct sctp_ulpq *ulpq, struct sk_buff_head *list,
982 struct sk_buff_head *list, __u16 needed) 982 __u16 needed)
983{ 983{
984 __u16 freed = 0; 984 __u16 freed = 0;
985 __u32 tsn, last_tsn; 985 __u32 tsn, last_tsn;