dccp: Refine the wait-for-ccid mechanism
[deliverable/linux.git] / net / dccp / output.c
index 9afd58e39e239136cbb926af965a90925e1f735a..39056dc61355fc4ab170b781b617573c06a45e07 100644 (file)
@@ -206,49 +206,29 @@ void dccp_write_space(struct sock *sk)
 }
 
 /**
- * dccp_wait_for_ccid - Wait for ccid to tell us we can send a packet
+ * dccp_wait_for_ccid  -  Await CCID send permission
  * @sk:    socket to wait for
- * @skb:   current skb to pass on for waiting
- * @delay: sleep timeout in milliseconds (> 0)
- * This function is called by default when the socket is closed, and
- * when a non-zero linger time is set on the socket. For consistency
+ * @delay: timeout in jiffies
+ * This is used by CCIDs which need to delay the send time in process context.
  */
-static int dccp_wait_for_ccid(struct sock *sk, struct sk_buff *skb, int delay)
+static int dccp_wait_for_ccid(struct sock *sk, unsigned long delay)
 {
-       struct dccp_sock *dp = dccp_sk(sk);
        DEFINE_WAIT(wait);
-       unsigned long jiffdelay;
-       int rc;
-
-       do {
-               dccp_pr_debug("delayed send by %d msec\n", delay);
-               jiffdelay = msecs_to_jiffies(delay);
-
-               prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+       long remaining;
 
-               sk->sk_write_pending++;
-               release_sock(sk);
-               schedule_timeout(jiffdelay);
-               lock_sock(sk);
-               sk->sk_write_pending--;
+       prepare_to_wait(sk->sk_sleep, &wait, TASK_INTERRUPTIBLE);
+       sk->sk_write_pending++;
+       release_sock(sk);
 
-               if (sk->sk_err)
-                       goto do_error;
-               if (signal_pending(current))
-                       goto do_interrupted;
+       remaining = schedule_timeout(delay);
 
-               rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb);
-       } while ((delay = rc) > 0);
-out:
+       lock_sock(sk);
+       sk->sk_write_pending--;
        finish_wait(sk->sk_sleep, &wait);
-       return rc;
-
-do_error:
-       rc = -EPIPE;
-       goto out;
-do_interrupted:
-       rc = -EINTR;
-       goto out;
+
+       if (signal_pending(current) || sk->sk_err)
+               return -1;
+       return remaining;
 }
 
 /**
@@ -311,7 +291,53 @@ static void dccp_xmit_packet(struct sock *sk)
                dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC);
 }
 
-void dccp_write_xmit(struct sock *sk, int block)
+/**
+ * dccp_flush_write_queue  -  Drain queue at end of connection
+ * Since dccp_sendmsg queues packets without waiting for them to be sent, it may
+ * happen that the TX queue is not empty at the end of a connection. We give the
+ * HC-sender CCID a grace period of up to @time_budget jiffies. If this function
+ * returns with a non-empty write queue, it will be purged later.
+ */
+void dccp_flush_write_queue(struct sock *sk, long *time_budget)
+{
+       struct dccp_sock *dp = dccp_sk(sk);
+       struct sk_buff *skb;
+       long delay, rc;
+
+       while (*time_budget > 0 && (skb = skb_peek(&sk->sk_write_queue))) {
+               rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb);
+
+               switch (ccid_packet_dequeue_eval(rc)) {
+               case CCID_PACKET_WILL_DEQUEUE_LATER:
+                       /*
+                        * If the CCID determines when to send, the next sending
+                        * time is unknown or the CCID may not even send again
+                        * (e.g. remote host crashes or lost Ack packets).
+                        */
+                       DCCP_WARN("CCID did not manage to send all packets\n");
+                       return;
+               case CCID_PACKET_DELAY:
+                       delay = msecs_to_jiffies(rc);
+                       if (delay > *time_budget)
+                               return;
+                       rc = dccp_wait_for_ccid(sk, delay);
+                       if (rc < 0)
+                               return;
+                       *time_budget -= (delay - rc);
+                       /* check again if we can send now */
+                       break;
+               case CCID_PACKET_SEND_AT_ONCE:
+                       dccp_xmit_packet(sk);
+                       break;
+               case CCID_PACKET_ERR:
+                       skb_dequeue(&sk->sk_write_queue);
+                       kfree_skb(skb);
+                       dccp_pr_debug("packet discarded due to err=%ld\n", rc);
+               }
+       }
+}
+
+void dccp_write_xmit(struct sock *sk)
 {
        struct dccp_sock *dp = dccp_sk(sk);
        struct sk_buff *skb;
@@ -323,19 +349,9 @@ void dccp_write_xmit(struct sock *sk, int block)
                case CCID_PACKET_WILL_DEQUEUE_LATER:
                        return;
                case CCID_PACKET_DELAY:
-                       if (!block) {
-                               sk_reset_timer(sk, &dp->dccps_xmit_timer,
-                                               msecs_to_jiffies(rc)+jiffies);
-                               return;
-                       }
-                       rc = dccp_wait_for_ccid(sk, skb, rc);
-                       if (rc && rc != -EINTR) {
-                               DCCP_BUG("err=%d after dccp_wait_for_ccid", rc);
-                               skb_dequeue(&sk->sk_write_queue);
-                               kfree_skb(skb);
-                               break;
-                       }
-                       /* fall through */
+                       sk_reset_timer(sk, &dp->dccps_xmit_timer,
+                                      jiffies + msecs_to_jiffies(rc));
+                       return;
                case CCID_PACKET_SEND_AT_ONCE:
                        dccp_xmit_packet(sk);
                        break;
@@ -660,7 +676,6 @@ void dccp_send_close(struct sock *sk, const int active)
                DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_CLOSE;
 
        if (active) {
-               dccp_write_xmit(sk, 1);
                dccp_skb_entail(sk, skb);
                dccp_transmit_skb(sk, skb_clone(skb, prio));
                /*
This page took 0.036304 seconds and 5 git commands to generate.