diff options
author | Xin Long <lucien.xin@gmail.com> | 2017-12-08 08:04:06 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-12-11 11:23:05 -0500 |
commit | 94014e8d871ae43d834828710c098518be44b5d9 (patch) | |
tree | 2be3b55d238b96afc9d6c2d91eeffff4d75aa2a1 | |
parent | 9162e0ed9e238c1f1d738cb36ee59d96b097f8e1 (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.h | 2 | ||||
-rw-r--r-- | include/net/sctp/ulpqueue.h | 9 | ||||
-rw-r--r-- | net/sctp/sm_sideeffect.c | 5 | ||||
-rw-r--r-- | net/sctp/stream_interleave.c | 109 | ||||
-rw-r--r-- | net/sctp/ulpqueue.c | 4 |
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 | ||
48 | void sctp_stream_interleave_init(struct sctp_stream *stream); | 50 | void 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); | |||
76 | void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn); | 76 | void sctp_ulpq_skip(struct sctp_ulpq *ulpq, __u16 sid, __u16 ssn); |
77 | 77 | ||
78 | void sctp_ulpq_reasm_flushtsn(struct sctp_ulpq *, __u32); | 78 | void 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 | ||
548 | static 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 | |||
601 | out: | ||
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 | |||
613 | static 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 | |||
627 | static 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 | |||
548 | static struct sctp_stream_interleave sctp_stream_interleave_0 = { | 655 | static 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 | ||
558 | static struct sctp_stream_interleave sctp_stream_interleave_1 = { | 666 | static 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 | ||
568 | void sctp_stream_interleave_init(struct sctp_stream *stream) | 677 | void 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 | ||
981 | static __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; |