* [DCCP] [PATCH 0/5]: Extend CCID interface so that CCID-2 stops polling
@ 2008-02-14 13:48 Gerrit Renker
2008-02-14 13:48 ` [PATCH 1/5] [DCCP]: Extend CCID packet dequeueing interface Gerrit Renker
0 siblings, 1 reply; 6+ messages in thread
From: Gerrit Renker @ 2008-02-14 13:48 UTC (permalink / raw)
To: acme; +Cc: dccp, netdev
This set of patches extends the packet sending/dequeuing interface, which is
currently restricted to using time intervals only. This forces CCID-2 into
a constant polling mode, which is removed in patch #4.
Patch #1: Extends the CCID packet dequeuing interface to allow CCIDs to
autonomously schedule sending. Previously it was timer-only,
so that CCID-2 polls uninterruptedly.
Patch #2: Adds a similar extension to the routine which drains the packet queue
at the end of a connection (still under congestion control).
Patch #3: Please clean up your queue when disconnecting.
Patch #4: With the previous changes, CCID-2 is taken out of its polling mode.
Patch #5: Removes the `more' argument from tx_packet_sent.
^ permalink raw reply [flat|nested] 6+ messages in thread* [PATCH 1/5] [DCCP]: Extend CCID packet dequeueing interface 2008-02-14 13:48 [DCCP] [PATCH 0/5]: Extend CCID interface so that CCID-2 stops polling Gerrit Renker @ 2008-02-14 13:48 ` Gerrit Renker 2008-02-14 13:48 ` [PATCH 2/5] [CCID]: Refine the wait-for-ccid mechanism Gerrit Renker 0 siblings, 1 reply; 6+ messages in thread From: Gerrit Renker @ 2008-02-14 13:48 UTC (permalink / raw) To: acme; +Cc: dccp, netdev, Gerrit Renker This extends the packet dequeuing interface of dccp_write_xmit() to allow 1. CCIDs to take care of timing when the next packet may be sent; 2. delayed sending (as before, with an inter-packet gap up to 65.535 seconds). The main purpose is to take CCID2 out of its polling mode (when it is network- limited, it tries every millisecond to send, without interruption). The interface can also be used to support other CCIDs. The mode of operation for (2) is as follows: * new packet is enqueued via dccp_sendmsg() => dccp_write_xmit(), * ccid_hc_tx_send_packet() detects that it may not send (e.g. window full), * it signals this condition via `CCID_PACKET_WILL_DEQUEUE_LATER', * dccp_write_xmit() returns without further action; * after some time the wait-condition for CCID becomes true, * that CCID schedules the tasklet, * tasklet function calls ccid_hc_tx_send_packet() via dccp_write_xmit(), * since the wait-condition is now true, ccid_hc_tx_packet() returns "send now", * packet is sent, and possibly more (since dccp_write_xmit() loops). Code reuse: the taskled function calls dccp_write_xmit(), the timer function reduces to a wrapper around the same code. If the tasklet finds that the socket is locked, it re-schedules the tasklet function (not the tasklet) after one jiffy. Changed DCCP_BUG to DCCP_WARN when transmit_skb returns an error (e.g. when a local qdisc is used, NET_XMIT_DROP=1 can be returned for many packets). Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> --- include/linux/dccp.h | 4 +- net/dccp/ccid.h | 37 ++++++++++++++++- net/dccp/ccids/ccid3.c | 4 +- net/dccp/output.c | 103 +++++++++++++++++++++++++++++++---------------- net/dccp/timer.c | 25 ++++++----- 5 files changed, 122 insertions(+), 51 deletions(-) --- a/include/linux/dccp.h +++ b/include/linux/dccp.h @@ -476,7 +476,8 @@ struct dccp_ackvec; * @dccps_hc_tx_insert_options - sender wants to add options when sending * @dccps_server_timewait - server holds timewait state on close (RFC 4340, 8.3) * @dccps_sync_scheduled - flag which signals "send out-of-band message soon" - * @dccps_xmit_timer - timer for when CCID is not ready to send + * @dccps_xmitlet - tasklet scheduled by the TX CCID to dequeue data packets + * @dccps_xmit_timer - used by the TX CCID to delay sending (rate-based pacing) * @dccps_syn_rtt - RTT sample from Request/Response exchange (in usecs) */ struct dccp_sock { @@ -517,6 +518,7 @@ struct dccp_sock { __u8 dccps_hc_tx_insert_options:1; __u8 dccps_server_timewait:1; __u8 dccps_sync_scheduled:1; + struct tasklet_struct dccps_xmitlet; struct timer_list dccps_xmit_timer; }; --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -124,13 +124,44 @@ static inline int ccid_get_current_id(struct dccp_sock *dp, bool rx) extern void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk); extern void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk); +/* + * Congestion control of queued data packets via CCID decision. + * + * The TX CCID performs its congestion-control by indicating whether and when a + * queued packet may be sent, using the return code of ccid_hc_tx_send_packet(). + * The following modes are supported: + * - autonomous dequeueing (CCID internally schedules dccps_xmitlet); + * - timer-based pacing (CCID returns a delay value in milliseconds). + * Modes and error handling are identified using the symbolic constants below. + */ +enum ccid_dequeueing_decision { + CCID_PACKET_SEND_AT_ONCE = 0x00000, + CCID_PACKET_DELAY = 0x10000, + CCID_PACKET_WILL_DEQUEUE_LATER = 0x20000, + CCID_PACKET_ERR = 0xF0000, +}; + +/* maximum possible number of milliseconds to delay a packet (65.535 seconds) */ +#define CCID_PACKET_DELAY_MAX 0xFFFF +#define CCID_PACKET_DELAY_MAX_USEC (CCID_PACKET_DELAY_MAX * USEC_PER_MSEC) + +static inline int ccid_packet_dequeue_eval(int return_code) +{ + if (return_code < 0) + return CCID_PACKET_ERR; + if (return_code == 0) + return CCID_PACKET_SEND_AT_ONCE; + if (return_code <= CCID_PACKET_DELAY_MAX) + return CCID_PACKET_DELAY; + return return_code; +} + static inline int ccid_hc_tx_send_packet(struct ccid *ccid, struct sock *sk, struct sk_buff *skb) { - int rc = 0; if (ccid->ccid_ops->ccid_hc_tx_send_packet != NULL) - rc = ccid->ccid_ops->ccid_hc_tx_send_packet(sk, skb); - return rc; + return ccid->ccid_ops->ccid_hc_tx_send_packet(sk, skb); + return CCID_PACKET_SEND_AT_ONCE; } static inline void ccid_hc_tx_packet_sent(struct ccid *ccid, struct sock *sk, --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -346,6 +346,8 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) case TFRC_SSTATE_FBACK: delay = ktime_us_delta(hctx->ccid3hctx_t_nom, now); ccid3_pr_debug("delay=%ld\n", (long)delay); + if (delay > CCID_PACKET_DELAY_MAX_USEC) + delay = CCID_PACKET_DELAY_MAX_USEC; /* * Scheduling of packet transmissions [RFC 3448, 4.6] * @@ -371,7 +373,7 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) /* set the nominal send time for the next following packet */ hctx->ccid3hctx_t_nom = ktime_add_us(hctx->ccid3hctx_t_nom, hctx->ccid3hctx_t_ipi); - return 0; + return CCID_PACKET_SEND_AT_ONCE; } static void ccid3_hc_tx_packet_sent(struct sock *sk, int more, --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -249,52 +249,85 @@ do_interrupted: goto out; } +/** + * dccp_xmit_packet - Send data packet under control of CCID + * Transmits next-queued payload and informs CCID to account for the packet. + */ +static void dccp_xmit_packet(struct sock *sk) +{ + int err, len; + struct dccp_sock *dp = dccp_sk(sk); + struct sk_buff *skb = skb_dequeue(&sk->sk_write_queue); + + if (unlikely(skb == NULL)) + return; + len = skb->len; + + if (sk->sk_state == DCCP_PARTOPEN) { + /* See 8.1.5. Handshake Completion */ + inet_csk_schedule_ack(sk); + inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, + inet_csk(sk)->icsk_rto, + DCCP_RTO_MAX); + DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATAACK; + } else if (dccp_ack_pending(sk)) { + DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATAACK; + } else { + DCCP_SKB_CB(skb)->dccpd_type = DCCP_PKT_DATA; + } + + err = dccp_transmit_skb(sk, skb); + if (err) + DCCP_WARN("transmit_skb() returned err=%d\n", err); + /* + * Register this one as sent even if an error occurred. To the remote + * end this error is indistinguishable from loss, so that finally (if + * the peer has no bugs) the drop is reported via receiver feedback. + */ + ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len); + + /* + * If the CCID needs to transfer additional header options out-of-band + * (e.g. Ack Vectors or feature-negotiation options), it activates the + * flag to schedule a Sync. The Sync will automatically incorporate all + * currently valid header options so that this backlog is now cleared. + */ + if (dp->dccps_sync_scheduled) + dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); +} + void dccp_write_xmit(struct sock *sk, int block) { struct dccp_sock *dp = dccp_sk(sk); struct sk_buff *skb; while ((skb = skb_peek(&sk->sk_write_queue))) { - int err = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); + int rc = ccid_hc_tx_send_packet(dp->dccps_hc_tx_ccid, sk, skb); - if (err > 0) { + switch (ccid_packet_dequeue_eval(rc)) { + case CCID_PACKET_WILL_DEQUEUE_LATER: + return; + case CCID_PACKET_DELAY: if (!block) { sk_reset_timer(sk, &dp->dccps_xmit_timer, - msecs_to_jiffies(err)+jiffies); + 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; - } else - err = dccp_wait_for_ccid(sk, skb, err); - if (err && err != -EINTR) - DCCP_BUG("err=%d after dccp_wait_for_ccid", err); - } - - skb_dequeue(&sk->sk_write_queue); - if (err == 0) { - struct dccp_skb_cb *dcb = DCCP_SKB_CB(skb); - const int len = skb->len; - - if (sk->sk_state == DCCP_PARTOPEN) { - /* See 8.1.5. Handshake Completion */ - inet_csk_schedule_ack(sk); - inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, - inet_csk(sk)->icsk_rto, - DCCP_RTO_MAX); - dcb->dccpd_type = DCCP_PKT_DATAACK; - } else if (dccp_ack_pending(sk)) - dcb->dccpd_type = DCCP_PKT_DATAACK; - else - dcb->dccpd_type = DCCP_PKT_DATA; - - err = dccp_transmit_skb(sk, skb); - ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len); - if (err) - DCCP_BUG("err=%d after ccid_hc_tx_packet_sent", - err); - if (dp->dccps_sync_scheduled) - dccp_send_sync(sk, dp->dccps_gsr, DCCP_PKT_SYNC); - } else { - dccp_pr_debug("packet discarded due to err=%d\n", err); + } + /* fall through */ + 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=%d\n", rc); } } } --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -249,32 +249,35 @@ out: sock_put(sk); } -/* Transmit-delay timer: used by the CCIDs to delay actual send time */ -static void dccp_write_xmit_timer(unsigned long data) +/** + * dccp_write_xmitlet - Workhorse for CCID packet dequeueing interface + * See the comments above %ccid_dequeueing_decision for supported modes. + */ +static void dccp_write_xmitlet(unsigned long data) { struct sock *sk = (struct sock *)data; - struct dccp_sock *dp = dccp_sk(sk); bh_lock_sock(sk); if (sock_owned_by_user(sk)) - sk_reset_timer(sk, &dp->dccps_xmit_timer, jiffies+1); + sk_reset_timer(sk, &dccp_sk(sk)->dccps_xmit_timer, jiffies + 1); else dccp_write_xmit(sk, 0); bh_unlock_sock(sk); - sock_put(sk); } -static void dccp_init_write_xmit_timer(struct sock *sk) +static void dccp_write_xmit_timer(unsigned long data) { - struct dccp_sock *dp = dccp_sk(sk); - - setup_timer(&dp->dccps_xmit_timer, dccp_write_xmit_timer, - (unsigned long)sk); + dccp_write_xmitlet(data); + sock_put((struct sock *)data); } void dccp_init_xmit_timers(struct sock *sk) { - dccp_init_write_xmit_timer(sk); + struct dccp_sock *dp = dccp_sk(sk); + + tasklet_init(&dp->dccps_xmitlet, dccp_write_xmitlet, (unsigned long)sk); + setup_timer(&dp->dccps_xmit_timer, dccp_write_xmit_timer, + (unsigned long)sk); inet_csk_init_xmit_timers(sk, &dccp_write_timer, &dccp_delack_timer, &dccp_keepalive_timer); } ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 2/5] [CCID]: Refine the wait-for-ccid mechanism 2008-02-14 13:48 ` [PATCH 1/5] [DCCP]: Extend CCID packet dequeueing interface Gerrit Renker @ 2008-02-14 13:48 ` Gerrit Renker 2008-02-14 13:48 ` [PATCH 3/5] [DCCP]: Empty the write queue when disconnecting Gerrit Renker 0 siblings, 1 reply; 6+ messages in thread From: Gerrit Renker @ 2008-02-14 13:48 UTC (permalink / raw) To: acme; +Cc: dccp, netdev, Gerrit Renker This extends the existing wait-for-ccid routine so that it may be used with different types of CCID. It further addresses the problems listed below. The code looks if the write queue is non-empty and grants the TX CCID up to `timeout' jiffies to drain the queue. It will instead purge that queue if * the delay suggested by the CCID exceeds the time budget; * a socket error occurred while waiting for the CCID; * there is a signal pending (eg. annoyed user pressed Control-C); * the CCID does not support delays (we don't know how long it will take). D e t a i l s [can be removed] ------------------------------- DCCP's sending mechanism functions a bit like non-blocking I/O: dccp_sendmsg() will enqueue up to net.dccp.default.tx_qlen packets (default=5), without waiting for them to be released to the network. Rate-based CCIDs, such as CCID3/4, can impose sending delays of up to maximally 64 seconds (t_mbi in RFC 3448). Hence the write queue may still contain packets when the application closes. Since the write queue is congestion-controlled by the CCID, draining the queue is also under control of the CCID. There are several problems that needed to be addressed: 1) The queue-drain mechanism only works with rate-based CCIDs. If CCID2 for example has a full TX queue and becomes network-limited just as the application wants to close, then waiting for CCID2 to become unblocked could lead to an indefinite delay (i.e., application "hangs"). 2) Since each TX CCID in turn uses a feedback mechanism, there may be changes in its sending policy while the queue is being drained. This can lead to further delays during which the application will not be able to terminate. 3) The minimum wait time for CCID3/4 can be expected to be the queue length times the current inter-packet delay. For example if tx_qlen=100 and a delay of 15 ms is used for each packet, then the application would have to wait for a minimum of 1.5 seconds before being allowed to exit. 4) There is no way for the user/application to control this behaviour. It would be good to use the timeout argument of dccp_close() as an upper bound. Then the maximum time that an application is willing to wait for its CCIDs to can be set via the SO_LINGER option. These problems are addressed by giving the CCID a grace period of up to the `timeout' value. The wait-for-ccid function is, as before, used when the application (a) has read all the data in its receive buffer and (b) if SO_LINGER was set with a non-zero linger time, or (c) the socket is either in the OPEN (active close) or in the PASSIVE_CLOSEREQ state (client application closes after receiving CloseReq). In addition, there is a catch-all case by calling __skb_queue_purge() after waiting for the CCID. This is necessary since the write queue may still have data when (a) the host has been passively-closed, (b) abnormal termination (unread data, zero linger time), (c) wait-for-ccid could not finish within the given time limit. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> --- net/dccp/dccp.h | 3 +- net/dccp/output.c | 122 ++++++++++++++++++++++++++++++----------------------- net/dccp/proto.c | 15 ++++++- net/dccp/timer.c | 2 +- 4 files changed, 86 insertions(+), 56 deletions(-) --- a/net/dccp/dccp.h +++ b/net/dccp/dccp.h @@ -215,8 +215,9 @@ extern void dccp_reqsk_send_ack(struct sk_buff *sk, struct request_sock *rsk); extern void dccp_send_sync(struct sock *sk, const u64 seq, const enum dccp_pkt_type pkt_type); -extern void dccp_write_xmit(struct sock *sk, int block); +extern void dccp_write_xmit(struct sock *sk); extern void dccp_write_space(struct sock *sk); +extern void dccp_flush_write_queue(struct sock *sk, long *time_budget); extern void dccp_init_xmit_timers(struct sock *sk); static inline void dccp_clear_xmit_timers(struct sock *sk) --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -204,49 +204,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; } /** @@ -296,7 +276,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"); + 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; @@ -308,19 +334,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; @@ -631,9 +647,8 @@ 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)); + skb = skb_clone(skb, prio); /* * Retransmission timer for active-close: RFC 4340, 8.3 requires * to retransmit the Close/CloseReq until the CLOSING/CLOSEREQ @@ -646,6 +661,7 @@ void dccp_send_close(struct sock *sk, const int active) */ inet_csk_reset_xmit_timer(sk, ICSK_TIME_RETRANS, DCCP_TIMEOUT_INIT, DCCP_RTO_MAX); - } else - dccp_transmit_skb(sk, skb); + } + + dccp_transmit_skb(sk, skb); } --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -756,7 +756,7 @@ int dccp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg, goto out_discard; skb_queue_tail(&sk->sk_write_queue, skb); - dccp_write_xmit(sk,0); + dccp_write_xmit(sk); out_release: release_sock(sk); return rc ? : len; @@ -979,9 +979,22 @@ void dccp_close(struct sock *sk, long timeout) /* Check zero linger _after_ checking for unread data. */ sk->sk_prot->disconnect(sk, 0); } else if (sk->sk_state != DCCP_CLOSED) { + /* + * Normal connection termination. May need to wait if there are + * still packets in the TX queue that are delayed by the CCID. + */ + dccp_flush_write_queue(sk, &timeout); dccp_terminate_connection(sk); } + /* + * Flush write queue. This may be necessary in several cases: + * - we have been closed by the peer but still have application data; + * - abortive termination (unread data or zero linger time), + * - normal termination but queue could not be flushed within time limit + */ + __skb_queue_purge(&sk->sk_write_queue); + sk_stream_wait_close(sk, timeout); adjudge_to_death: --- a/net/dccp/timer.c +++ b/net/dccp/timer.c @@ -261,7 +261,7 @@ static void dccp_write_xmitlet(unsigned long data) if (sock_owned_by_user(sk)) sk_reset_timer(sk, &dccp_sk(sk)->dccps_xmit_timer, jiffies + 1); else - dccp_write_xmit(sk, 0); + dccp_write_xmit(sk); bh_unlock_sock(sk); } ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 3/5] [DCCP]: Empty the write queue when disconnecting 2008-02-14 13:48 ` [PATCH 2/5] [CCID]: Refine the wait-for-ccid mechanism Gerrit Renker @ 2008-02-14 13:48 ` Gerrit Renker 2008-02-14 13:48 ` [PATCH 4/5] [CCID2]: Stop polling Gerrit Renker 0 siblings, 1 reply; 6+ messages in thread From: Gerrit Renker @ 2008-02-14 13:48 UTC (permalink / raw) To: acme; +Cc: dccp, netdev, Gerrit Renker dccp_disconnect() can be called due to several reasons: 1. when the connection setup failed (inet_stream_connect()); 2. when shutting down (inet_shutdown(), inet_csk_listen_stop()); 3. when aborting the connection (dccp_close() with 0 linger time). In case (1) the write queue is empty. This patch empties the write queue, if in case (2) or (3) it was not yet empty. This avoids triggering the write-queue BUG_TRAP in sk_stream_kill_queues() later on. It also seems natural to do: when breaking an association, to delete all packets that were originally intended for the soon-disconnected end (compare with call to tcp_write_queue_purge in tcp_disconnect()). Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> --- net/dccp/proto.c | 2 ++ 1 files changed, 2 insertions(+), 0 deletions(-) --- a/net/dccp/proto.c +++ b/net/dccp/proto.c @@ -277,7 +277,9 @@ int dccp_disconnect(struct sock *sk, int flags) sk->sk_err = ECONNRESET; dccp_clear_xmit_timers(sk); + __skb_queue_purge(&sk->sk_receive_queue); + __skb_queue_purge(&sk->sk_write_queue); if (sk->sk_send_head != NULL) { __kfree_skb(sk->sk_send_head); sk->sk_send_head = NULL; ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 4/5] [CCID2]: Stop polling 2008-02-14 13:48 ` [PATCH 3/5] [DCCP]: Empty the write queue when disconnecting Gerrit Renker @ 2008-02-14 13:48 ` Gerrit Renker 2008-02-14 13:48 ` [PATCH 5/5] [CCID]: Unused argument Gerrit Renker 0 siblings, 1 reply; 6+ messages in thread From: Gerrit Renker @ 2008-02-14 13:48 UTC (permalink / raw) To: acme; +Cc: dccp, netdev, Gerrit Renker This updates CCID2 to use the CCID dequeuing mechanism, converting from previous constant-polling to a now event-driven mechanism. Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> --- net/dccp/ccids/ccid2.c | 21 +++++++++++++-------- net/dccp/ccids/ccid2.h | 5 +++++ 2 files changed, 18 insertions(+), 8 deletions(-) --- a/net/dccp/ccids/ccid2.h +++ b/net/dccp/ccids/ccid2.h @@ -70,6 +70,11 @@ struct ccid2_hc_tx_sock { struct list_head ccid2hctx_parsed_ackvecs; }; +static inline bool ccid2_cwnd_network_limited(struct ccid2_hc_tx_sock *hctx) +{ + return (hctx->ccid2hctx_pipe >= hctx->ccid2hctx_cwnd); +} + struct ccid2_hc_rx_sock { int ccid2hcrx_data; }; --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -124,12 +124,9 @@ static int ccid2_hc_tx_alloc_seq(struct ccid2_hc_tx_sock *hctx) static int ccid2_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) { - struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); - - if (hctx->ccid2hctx_pipe < hctx->ccid2hctx_cwnd) - return 0; - - return 1; /* XXX CCID should dequeue when ready instead of polling */ + if (ccid2_cwnd_network_limited(ccid2_hc_tx_sk(sk))) + return CCID_PACKET_WILL_DEQUEUE_LATER; + return CCID_PACKET_SEND_AT_ONCE; } static void ccid2_change_l_ack_ratio(struct sock *sk, u32 val) @@ -169,6 +166,7 @@ static void ccid2_hc_tx_rto_expire(unsigned long data) { struct sock *sk = (struct sock *)data; struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); + const bool sender_was_blocked = ccid2_cwnd_network_limited(hctx); long s; bh_lock_sock(sk); @@ -189,8 +187,6 @@ static void ccid2_hc_tx_rto_expire(unsigned long data) if (s > 60) hctx->ccid2hctx_rto = 60 * HZ; - ccid2_start_rto_timer(sk); - /* adjust pipe, cwnd etc */ hctx->ccid2hctx_ssthresh = hctx->ccid2hctx_cwnd / 2; if (hctx->ccid2hctx_ssthresh < 2) @@ -207,6 +203,11 @@ static void ccid2_hc_tx_rto_expire(unsigned long data) hctx->ccid2hctx_rpdupack = -1; ccid2_change_l_ack_ratio(sk, 1); ccid2_hc_tx_check_sanity(hctx); + + /* if we were blocked before, we may now send cwnd=1 packet */ + if (sender_was_blocked) + tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet); + ccid2_start_rto_timer(sk); out: bh_unlock_sock(sk); sock_put(sk); @@ -461,6 +462,7 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); + const bool sender_was_blocked = ccid2_cwnd_network_limited(hctx); struct dccp_ackvec_parsed *avp; u64 ackno, seqno; struct ccid2_seq *seqp; @@ -646,6 +648,9 @@ static void ccid2_hc_tx_packet_recv(struct sock *sk, struct sk_buff *skb) ccid2_hc_tx_check_sanity(hctx); done: + /* check if incoming Acks allow pending packets to be sent */ + if (sender_was_blocked && !ccid2_cwnd_network_limited(hctx)) + tasklet_schedule(&dccp_sk(sk)->dccps_xmitlet); dccp_ackvec_parsed_cleanup(&hctx->ccid2hctx_parsed_ackvecs); } ^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH 5/5] [CCID]: Unused argument 2008-02-14 13:48 ` [PATCH 4/5] [CCID2]: Stop polling Gerrit Renker @ 2008-02-14 13:48 ` Gerrit Renker 0 siblings, 0 replies; 6+ messages in thread From: Gerrit Renker @ 2008-02-14 13:48 UTC (permalink / raw) To: acme; +Cc: dccp, netdev, Gerrit Renker This removes the argument `more' from ccid_hc_tx_packet_sent, since it was nowhere used in the entire code. (Anecdotally, this argument was not even used in the original KAME code where the function originally came from; compare the variable moreToSend in the freebsd61-dccp-kame-28.08.2006.patch now maintained by Emmanuel Lochin.) Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk> --- net/dccp/ccid.h | 6 +++--- net/dccp/ccids/ccid2.c | 2 +- net/dccp/ccids/ccid3.c | 3 +-- net/dccp/output.c | 2 +- 4 files changed, 6 insertions(+), 7 deletions(-) --- a/net/dccp/ccid.h +++ b/net/dccp/ccid.h @@ -75,7 +75,7 @@ struct ccid_operations { int (*ccid_hc_tx_send_packet)(struct sock *sk, struct sk_buff *skb); void (*ccid_hc_tx_packet_sent)(struct sock *sk, - int more, unsigned int len); + unsigned int len); void (*ccid_hc_rx_get_info)(struct sock *sk, struct tcp_info *info); void (*ccid_hc_tx_get_info)(struct sock *sk, @@ -165,10 +165,10 @@ static inline int ccid_hc_tx_send_packet(struct ccid *ccid, struct sock *sk, } static inline void ccid_hc_tx_packet_sent(struct ccid *ccid, struct sock *sk, - int more, unsigned int len) + unsigned int len) { if (ccid->ccid_ops->ccid_hc_tx_packet_sent != NULL) - ccid->ccid_ops->ccid_hc_tx_packet_sent(sk, more, len); + ccid->ccid_ops->ccid_hc_tx_packet_sent(sk, len); } static inline void ccid_hc_rx_packet_recv(struct ccid *ccid, struct sock *sk, --- a/net/dccp/ccids/ccid2.c +++ b/net/dccp/ccids/ccid2.c @@ -224,7 +224,7 @@ static void ccid2_start_rto_timer(struct sock *sk) jiffies + hctx->ccid2hctx_rto); } -static void ccid2_hc_tx_packet_sent(struct sock *sk, int more, unsigned int len) +static void ccid2_hc_tx_packet_sent(struct sock *sk, unsigned int len) { struct dccp_sock *dp = dccp_sk(sk); struct ccid2_hc_tx_sock *hctx = ccid2_hc_tx_sk(sk); --- a/net/dccp/ccids/ccid3.c +++ b/net/dccp/ccids/ccid3.c @@ -376,8 +376,7 @@ static int ccid3_hc_tx_send_packet(struct sock *sk, struct sk_buff *skb) return CCID_PACKET_SEND_AT_ONCE; } -static void ccid3_hc_tx_packet_sent(struct sock *sk, int more, - unsigned int len) +static void ccid3_hc_tx_packet_sent(struct sock *sk, unsigned int len) { struct ccid3_hc_tx_sock *hctx = ccid3_hc_tx_sk(sk); --- a/net/dccp/output.c +++ b/net/dccp/output.c @@ -264,7 +264,7 @@ static void dccp_xmit_packet(struct sock *sk) * end this error is indistinguishable from loss, so that finally (if * the peer has no bugs) the drop is reported via receiver feedback. */ - ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, 0, len); + ccid_hc_tx_packet_sent(dp->dccps_hc_tx_ccid, sk, len); /* * If the CCID needs to transfer additional header options out-of-band ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2008-02-14 13:50 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-02-14 13:48 [DCCP] [PATCH 0/5]: Extend CCID interface so that CCID-2 stops polling Gerrit Renker 2008-02-14 13:48 ` [PATCH 1/5] [DCCP]: Extend CCID packet dequeueing interface Gerrit Renker 2008-02-14 13:48 ` [PATCH 2/5] [CCID]: Refine the wait-for-ccid mechanism Gerrit Renker 2008-02-14 13:48 ` [PATCH 3/5] [DCCP]: Empty the write queue when disconnecting Gerrit Renker 2008-02-14 13:48 ` [PATCH 4/5] [CCID2]: Stop polling Gerrit Renker 2008-02-14 13:48 ` [PATCH 5/5] [CCID]: Unused argument Gerrit Renker
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox