diff options
-rw-r--r-- | include/net/sctp/structs.h | 1 | ||||
-rw-r--r-- | include/net/sctp/user.h | 2 | ||||
-rw-r--r-- | net/sctp/socket.c | 57 | ||||
-rw-r--r-- | net/sctp/ulpqueue.c | 64 |
4 files changed, 120 insertions, 4 deletions
diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index fe7f5ae1c513..37b4a24e589b 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h | |||
@@ -307,6 +307,7 @@ struct sctp_sock { | |||
307 | __u8 v4mapped; | 307 | __u8 v4mapped; |
308 | __u8 frag_interleave; | 308 | __u8 frag_interleave; |
309 | __u32 adaptation_ind; | 309 | __u32 adaptation_ind; |
310 | __u32 pd_point; | ||
310 | 311 | ||
311 | atomic_t pd_mode; | 312 | atomic_t pd_mode; |
312 | /* Receive to here while partial delivery is in effect. */ | 313 | /* Receive to here while partial delivery is in effect. */ |
diff --git a/include/net/sctp/user.h b/include/net/sctp/user.h index e77316088dc7..9a8352710631 100644 --- a/include/net/sctp/user.h +++ b/include/net/sctp/user.h | |||
@@ -99,6 +99,8 @@ enum sctp_optname { | |||
99 | #define SCTP_CONTEXT SCTP_CONTEXT | 99 | #define SCTP_CONTEXT SCTP_CONTEXT |
100 | SCTP_FRAGMENT_INTERLEAVE, | 100 | SCTP_FRAGMENT_INTERLEAVE, |
101 | #define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE | 101 | #define SCTP_FRAGMENT_INTERLEAVE SCTP_FRAGMENT_INTERLEAVE |
102 | SCTP_PARTIAL_DELIVERY_POINT, /* Set/Get partial delivery point */ | ||
103 | #define SCTP_PARTIAL_DELIVERY_POINT SCTP_PARTIAL_DELIVERY_POINT | ||
102 | 104 | ||
103 | /* Internal Socket Options. Some of the sctp library functions are | 105 | /* Internal Socket Options. Some of the sctp library functions are |
104 | * implemented using these socket options. | 106 | * implemented using these socket options. |
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index b4be473c68b0..1e787a2d0b5f 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -2832,6 +2832,32 @@ static int sctp_setsockopt_fragment_interleave(struct sock *sk, | |||
2832 | return 0; | 2832 | return 0; |
2833 | } | 2833 | } |
2834 | 2834 | ||
2835 | /* | ||
2836 | * 7.1.25. Set or Get the sctp partial delivery point | ||
2837 | * (SCTP_PARTIAL_DELIVERY_POINT) | ||
2838 | * This option will set or get the SCTP partial delivery point. This | ||
2839 | * point is the size of a message where the partial delivery API will be | ||
2840 | * invoked to help free up rwnd space for the peer. Setting this to a | ||
2841 | * lower value will cause partial delivery's to happen more often. The | ||
2842 | * calls argument is an integer that sets or gets the partial delivery | ||
2843 | * point. | ||
2844 | */ | ||
2845 | static int sctp_setsockopt_partial_delivery_point(struct sock *sk, | ||
2846 | char __user *optval, | ||
2847 | int optlen) | ||
2848 | { | ||
2849 | u32 val; | ||
2850 | |||
2851 | if (optlen != sizeof(u32)) | ||
2852 | return -EINVAL; | ||
2853 | if (get_user(val, (int __user *)optval)) | ||
2854 | return -EFAULT; | ||
2855 | |||
2856 | sctp_sk(sk)->pd_point = val; | ||
2857 | |||
2858 | return 0; /* is this the right error code? */ | ||
2859 | } | ||
2860 | |||
2835 | /* API 6.2 setsockopt(), getsockopt() | 2861 | /* API 6.2 setsockopt(), getsockopt() |
2836 | * | 2862 | * |
2837 | * Applications use setsockopt() and getsockopt() to set or retrieve | 2863 | * Applications use setsockopt() and getsockopt() to set or retrieve |
@@ -2911,6 +2937,9 @@ SCTP_STATIC int sctp_setsockopt(struct sock *sk, int level, int optname, | |||
2911 | case SCTP_DELAYED_ACK_TIME: | 2937 | case SCTP_DELAYED_ACK_TIME: |
2912 | retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen); | 2938 | retval = sctp_setsockopt_delayed_ack_time(sk, optval, optlen); |
2913 | break; | 2939 | break; |
2940 | case SCTP_PARTIAL_DELIVERY_POINT: | ||
2941 | retval = sctp_setsockopt_partial_delivery_point(sk, optval, optlen); | ||
2942 | break; | ||
2914 | 2943 | ||
2915 | case SCTP_INITMSG: | 2944 | case SCTP_INITMSG: |
2916 | retval = sctp_setsockopt_initmsg(sk, optval, optlen); | 2945 | retval = sctp_setsockopt_initmsg(sk, optval, optlen); |
@@ -4602,6 +4631,30 @@ static int sctp_getsockopt_fragment_interleave(struct sock *sk, int len, | |||
4602 | return 0; | 4631 | return 0; |
4603 | } | 4632 | } |
4604 | 4633 | ||
4634 | /* | ||
4635 | * 7.1.25. Set or Get the sctp partial delivery point | ||
4636 | * (chapter and verse is quoted at sctp_setsockopt_partial_delivery_point()) | ||
4637 | */ | ||
4638 | static int sctp_getsockopt_partial_delivery_point(struct sock *sk, int len, | ||
4639 | char __user *optval, | ||
4640 | int __user *optlen) | ||
4641 | { | ||
4642 | u32 val; | ||
4643 | |||
4644 | if (len < sizeof(u32)) | ||
4645 | return -EINVAL; | ||
4646 | |||
4647 | len = sizeof(u32); | ||
4648 | |||
4649 | val = sctp_sk(sk)->pd_point; | ||
4650 | if (put_user(len, optlen)) | ||
4651 | return -EFAULT; | ||
4652 | if (copy_to_user(optval, &val, len)) | ||
4653 | return -EFAULT; | ||
4654 | |||
4655 | return -ENOTSUPP; | ||
4656 | } | ||
4657 | |||
4605 | SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, | 4658 | SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, |
4606 | char __user *optval, int __user *optlen) | 4659 | char __user *optval, int __user *optlen) |
4607 | { | 4660 | { |
@@ -4718,6 +4771,10 @@ SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, | |||
4718 | retval = sctp_getsockopt_fragment_interleave(sk, len, optval, | 4771 | retval = sctp_getsockopt_fragment_interleave(sk, len, optval, |
4719 | optlen); | 4772 | optlen); |
4720 | break; | 4773 | break; |
4774 | case SCTP_PARTIAL_DELIVERY_POINT: | ||
4775 | retval = sctp_getsockopt_partial_delivery_point(sk, len, optval, | ||
4776 | optlen); | ||
4777 | break; | ||
4721 | default: | 4778 | default: |
4722 | retval = -ENOPROTOOPT; | 4779 | retval = -ENOPROTOOPT; |
4723 | break; | 4780 | break; |
diff --git a/net/sctp/ulpqueue.c b/net/sctp/ulpqueue.c index ac80c34f6c2c..0fa4d4d4df17 100644 --- a/net/sctp/ulpqueue.c +++ b/net/sctp/ulpqueue.c | |||
@@ -177,6 +177,15 @@ int sctp_clear_pd(struct sock *sk, struct sctp_association *asoc) | |||
177 | return 0; | 177 | return 0; |
178 | } | 178 | } |
179 | 179 | ||
180 | /* Set the pd_mode on the socket and ulpq */ | ||
181 | static void sctp_ulpq_set_pd(struct sctp_ulpq *ulpq) | ||
182 | { | ||
183 | struct sctp_sock *sp = sctp_sk(ulpq->asoc->base.sk); | ||
184 | |||
185 | atomic_inc(&sp->pd_mode); | ||
186 | ulpq->pd_mode = 1; | ||
187 | } | ||
188 | |||
180 | /* Clear the pd_mode and restart any pending messages waiting for delivery. */ | 189 | /* Clear the pd_mode and restart any pending messages waiting for delivery. */ |
181 | static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq) | 190 | static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq) |
182 | { | 191 | { |
@@ -401,6 +410,11 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u | |||
401 | struct sk_buff *first_frag = NULL; | 410 | struct sk_buff *first_frag = NULL; |
402 | __u32 ctsn, next_tsn; | 411 | __u32 ctsn, next_tsn; |
403 | struct sctp_ulpevent *retval = NULL; | 412 | struct sctp_ulpevent *retval = NULL; |
413 | struct sk_buff *pd_first = NULL; | ||
414 | struct sk_buff *pd_last = NULL; | ||
415 | size_t pd_len = 0; | ||
416 | struct sctp_association *asoc; | ||
417 | u32 pd_point; | ||
404 | 418 | ||
405 | /* Initialized to 0 just to avoid compiler warning message. Will | 419 | /* Initialized to 0 just to avoid compiler warning message. Will |
406 | * never be used with this value. It is referenced only after it | 420 | * never be used with this value. It is referenced only after it |
@@ -416,6 +430,10 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u | |||
416 | * we expect to find the remaining middle fragments and the last | 430 | * we expect to find the remaining middle fragments and the last |
417 | * fragment in order. If not, first_frag is reset to NULL and we | 431 | * fragment in order. If not, first_frag is reset to NULL and we |
418 | * start the next pass when we find another first fragment. | 432 | * start the next pass when we find another first fragment. |
433 | * | ||
434 | * There is a potential to do partial delivery if user sets | ||
435 | * SCTP_PARTIAL_DELIVERY_POINT option. Lets count some things here | ||
436 | * to see if can do PD. | ||
419 | */ | 437 | */ |
420 | skb_queue_walk(&ulpq->reasm, pos) { | 438 | skb_queue_walk(&ulpq->reasm, pos) { |
421 | cevent = sctp_skb2event(pos); | 439 | cevent = sctp_skb2event(pos); |
@@ -423,14 +441,32 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u | |||
423 | 441 | ||
424 | switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) { | 442 | switch (cevent->msg_flags & SCTP_DATA_FRAG_MASK) { |
425 | case SCTP_DATA_FIRST_FRAG: | 443 | case SCTP_DATA_FIRST_FRAG: |
444 | /* If this "FIRST_FRAG" is the first | ||
445 | * element in the queue, then count it towards | ||
446 | * possible PD. | ||
447 | */ | ||
448 | if (pos == ulpq->reasm.next) { | ||
449 | pd_first = pos; | ||
450 | pd_last = pos; | ||
451 | pd_len = pos->len; | ||
452 | } else { | ||
453 | pd_first = NULL; | ||
454 | pd_last = NULL; | ||
455 | pd_len = 0; | ||
456 | } | ||
457 | |||
426 | first_frag = pos; | 458 | first_frag = pos; |
427 | next_tsn = ctsn + 1; | 459 | next_tsn = ctsn + 1; |
428 | break; | 460 | break; |
429 | 461 | ||
430 | case SCTP_DATA_MIDDLE_FRAG: | 462 | case SCTP_DATA_MIDDLE_FRAG: |
431 | if ((first_frag) && (ctsn == next_tsn)) | 463 | if ((first_frag) && (ctsn == next_tsn)) { |
432 | next_tsn++; | 464 | next_tsn++; |
433 | else | 465 | if (pd_first) { |
466 | pd_last = pos; | ||
467 | pd_len += pos->len; | ||
468 | } | ||
469 | } else | ||
434 | first_frag = NULL; | 470 | first_frag = NULL; |
435 | break; | 471 | break; |
436 | 472 | ||
@@ -441,7 +477,28 @@ static inline struct sctp_ulpevent *sctp_ulpq_retrieve_reassembled(struct sctp_u | |||
441 | first_frag = NULL; | 477 | first_frag = NULL; |
442 | break; | 478 | break; |
443 | }; | 479 | }; |
480 | } | ||
481 | |||
482 | asoc = ulpq->asoc; | ||
483 | if (pd_first) { | ||
484 | /* Make sure we can enter partial deliver. | ||
485 | * We can trigger partial delivery only if framgent | ||
486 | * interleave is set, or the socket is not already | ||
487 | * in partial delivery. | ||
488 | */ | ||
489 | if (!sctp_sk(asoc->base.sk)->frag_interleave && | ||
490 | atomic_read(&sctp_sk(asoc->base.sk)->pd_mode)) | ||
491 | goto done; | ||
444 | 492 | ||
493 | cevent = sctp_skb2event(pd_first); | ||
494 | pd_point = sctp_sk(asoc->base.sk)->pd_point; | ||
495 | if (pd_point && pd_point <= pd_len) { | ||
496 | retval = sctp_make_reassembled_event(&ulpq->reasm, | ||
497 | pd_first, | ||
498 | pd_last); | ||
499 | if (retval) | ||
500 | sctp_ulpq_set_pd(ulpq); | ||
501 | } | ||
445 | } | 502 | } |
446 | done: | 503 | done: |
447 | return retval; | 504 | return retval; |
@@ -882,8 +939,7 @@ void sctp_ulpq_partial_delivery(struct sctp_ulpq *ulpq, | |||
882 | /* Send event to the ULP. */ | 939 | /* Send event to the ULP. */ |
883 | if (event) { | 940 | if (event) { |
884 | sctp_ulpq_tail_event(ulpq, event); | 941 | sctp_ulpq_tail_event(ulpq, event); |
885 | atomic_inc(&sp->pd_mode); | 942 | sctp_ulpq_set_pd(ulpq); |
886 | ulpq->pd_mode = 1; | ||
887 | return; | 943 | return; |
888 | } | 944 | } |
889 | } | 945 | } |