aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/net/sctp/structs.h1
-rw-r--r--include/net/sctp/user.h2
-rw-r--r--net/sctp/socket.c57
-rw-r--r--net/sctp/ulpqueue.c64
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 */
2845static 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 */
4638static 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
4605SCTP_STATIC int sctp_getsockopt(struct sock *sk, int level, int optname, 4658SCTP_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 */
181static 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. */
181static int sctp_ulpq_clear_pd(struct sctp_ulpq *ulpq) 190static 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 }
446done: 503done:
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 }