aboutsummaryrefslogtreecommitdiffstats
path: root/net/sctp
diff options
context:
space:
mode:
authorVlad Yasevich <vladislav.yasevich@hp.com>2007-03-23 14:32:00 -0400
committerDavid S. Miller <davem@sunset.davemloft.net>2007-04-26 01:28:00 -0400
commitd49d91d79a8dc5e85108a5ae1c8eef23dec135c1 (patch)
treee98b53a69b310128a03a06fcc1dd9f94f7aa34b2 /net/sctp
parentb6e1331f3ce25a56edb956054eaf8011654686cb (diff)
[SCTP]: Implement SCTP_PARTIAL_DELIVERY_POINT option.
This option induces partial delivery to run as soon as the specified amount of data has been accumulated on the association. However, we give preference to fully reassembled messages over PD messages. In any case, window and buffer is freed up. Signed-off-by: Vlad Yasevich <vladislav.yasevich@.hp.com> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/sctp')
-rw-r--r--net/sctp/socket.c57
-rw-r--r--net/sctp/ulpqueue.c64
2 files changed, 117 insertions, 4 deletions
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 }