diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-03-23 23:44:19 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-03-23 23:44:19 -0500 |
commit | 1ebbe2b20091d306453a5cf480a87e6cd28ae76f (patch) | |
tree | f5cd7a0fa69b8b1938cb5a0faed2e7b0628072a5 /net/dccp/ackvec.c | |
parent | ac58c9059da8886b5e8cde012a80266b18ca146e (diff) | |
parent | 674a396c6d2ba0341ebdd7c1c9950f32f018e2dd (diff) | |
download | talos-obmc-linux-1ebbe2b20091d306453a5cf480a87e6cd28ae76f.tar.gz talos-obmc-linux-1ebbe2b20091d306453a5cf480a87e6cd28ae76f.zip |
Merge branch 'linus'
Diffstat (limited to 'net/dccp/ackvec.c')
-rw-r--r-- | net/dccp/ackvec.c | 296 |
1 files changed, 194 insertions, 102 deletions
diff --git a/net/dccp/ackvec.c b/net/dccp/ackvec.c index 2c77dafbd091..b5981e5f6b00 100644 --- a/net/dccp/ackvec.c +++ b/net/dccp/ackvec.c @@ -13,36 +13,83 @@ #include "dccp.h" #include <linux/dccp.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/kernel.h> #include <linux/skbuff.h> +#include <linux/slab.h> #include <net/sock.h> +static kmem_cache_t *dccp_ackvec_slab; +static kmem_cache_t *dccp_ackvec_record_slab; + +static struct dccp_ackvec_record *dccp_ackvec_record_new(void) +{ + struct dccp_ackvec_record *avr = + kmem_cache_alloc(dccp_ackvec_record_slab, GFP_ATOMIC); + + if (avr != NULL) + INIT_LIST_HEAD(&avr->dccpavr_node); + + return avr; +} + +static void dccp_ackvec_record_delete(struct dccp_ackvec_record *avr) +{ + if (unlikely(avr == NULL)) + return; + /* Check if deleting a linked record */ + WARN_ON(!list_empty(&avr->dccpavr_node)); + kmem_cache_free(dccp_ackvec_record_slab, avr); +} + +static void dccp_ackvec_insert_avr(struct dccp_ackvec *av, + struct dccp_ackvec_record *avr) +{ + /* + * AVRs are sorted by seqno. Since we are sending them in order, we + * just add the AVR at the head of the list. + * -sorbo. + */ + if (!list_empty(&av->dccpav_records)) { + const struct dccp_ackvec_record *head = + list_entry(av->dccpav_records.next, + struct dccp_ackvec_record, + dccpavr_node); + BUG_ON(before48(avr->dccpavr_ack_seqno, + head->dccpavr_ack_seqno)); + } + + list_add(&avr->dccpavr_node, &av->dccpav_records); +} + int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) { struct dccp_sock *dp = dccp_sk(sk); +#ifdef CONFIG_IP_DCCP_DEBUG + const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? + "CLIENT tx: " : "server tx: "; +#endif struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; int len = av->dccpav_vec_len + 2; struct timeval now; u32 elapsed_time; unsigned char *to, *from; + struct dccp_ackvec_record *avr; + + if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) + return -1; dccp_timestamp(sk, &now); elapsed_time = timeval_delta(&now, &av->dccpav_time) / 10; - if (elapsed_time != 0) - dccp_insert_option_elapsed_time(sk, skb, elapsed_time); - - if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) + if (elapsed_time != 0 && + dccp_insert_option_elapsed_time(sk, skb, elapsed_time)) return -1; - /* - * XXX: now we have just one ack vector sent record, so - * we have to wait for it to be cleared. - * - * Of course this is not acceptable, but this is just for - * basic testing now. - */ - if (av->dccpav_ack_seqno != DCCP_MAX_SEQNO + 1) + avr = dccp_ackvec_record_new(); + if (avr == NULL) return -1; DCCP_SKB_CB(skb)->dccpd_opt_len += len; @@ -55,8 +102,8 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) from = av->dccpav_buf + av->dccpav_buf_head; /* Check if buf_head wraps */ - if ((int)av->dccpav_buf_head + len > av->dccpav_vec_len) { - const u32 tailsize = av->dccpav_vec_len - av->dccpav_buf_head; + if ((int)av->dccpav_buf_head + len > DCCP_MAX_ACKVEC_LEN) { + const u32 tailsize = DCCP_MAX_ACKVEC_LEN - av->dccpav_buf_head; memcpy(to, from, tailsize); to += tailsize; @@ -73,45 +120,37 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) * sequence number it used for the ack packet; ack_ptr will equal * buf_head; ack_ackno will equal buf_ackno; and ack_nonce will * equal buf_nonce. - * - * This implemention uses just one ack record for now. */ - av->dccpav_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq; - av->dccpav_ack_ptr = av->dccpav_buf_head; - av->dccpav_ack_ackno = av->dccpav_buf_ackno; - av->dccpav_ack_nonce = av->dccpav_buf_nonce; - av->dccpav_sent_len = av->dccpav_vec_len; + avr->dccpavr_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq; + avr->dccpavr_ack_ptr = av->dccpav_buf_head; + avr->dccpavr_ack_ackno = av->dccpav_buf_ackno; + avr->dccpavr_ack_nonce = av->dccpav_buf_nonce; + avr->dccpavr_sent_len = av->dccpav_vec_len; + + dccp_ackvec_insert_avr(av, avr); dccp_pr_debug("%sACK Vector 0, len=%d, ack_seqno=%llu, " "ack_ackno=%llu\n", - debug_prefix, av->dccpav_sent_len, - (unsigned long long)av->dccpav_ack_seqno, - (unsigned long long)av->dccpav_ack_ackno); - return -1; + debug_prefix, avr->dccpavr_sent_len, + (unsigned long long)avr->dccpavr_ack_seqno, + (unsigned long long)avr->dccpavr_ack_ackno); + return 0; } -struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len, - const gfp_t priority) +struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority) { - struct dccp_ackvec *av; - - BUG_ON(len == 0); + struct dccp_ackvec *av = kmem_cache_alloc(dccp_ackvec_slab, priority); - if (len > DCCP_MAX_ACKVEC_LEN) - return NULL; - - av = kmalloc(sizeof(*av) + len, priority); if (av != NULL) { - av->dccpav_buf_len = len; av->dccpav_buf_head = - av->dccpav_buf_tail = av->dccpav_buf_len - 1; - av->dccpav_buf_ackno = - av->dccpav_ack_ackno = av->dccpav_ack_seqno = ~0LLU; + av->dccpav_buf_tail = DCCP_MAX_ACKVEC_LEN - 1; + av->dccpav_buf_ackno = DCCP_MAX_SEQNO + 1; av->dccpav_buf_nonce = av->dccpav_buf_nonce = 0; av->dccpav_ack_ptr = 0; av->dccpav_time.tv_sec = 0; av->dccpav_time.tv_usec = 0; av->dccpav_sent_len = av->dccpav_vec_len = 0; + INIT_LIST_HEAD(&av->dccpav_records); } return av; @@ -119,7 +158,20 @@ struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len, void dccp_ackvec_free(struct dccp_ackvec *av) { - kfree(av); + if (unlikely(av == NULL)) + return; + + if (!list_empty(&av->dccpav_records)) { + struct dccp_ackvec_record *avr, *next; + + list_for_each_entry_safe(avr, next, &av->dccpav_records, + dccpavr_node) { + list_del_init(&avr->dccpavr_node); + dccp_ackvec_record_delete(avr); + } + } + + kmem_cache_free(dccp_ackvec_slab, av); } static inline u8 dccp_ackvec_state(const struct dccp_ackvec *av, @@ -146,7 +198,7 @@ static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av, unsigned int gap; long new_head; - if (av->dccpav_vec_len + packets > av->dccpav_buf_len) + if (av->dccpav_vec_len + packets > DCCP_MAX_ACKVEC_LEN) return -ENOBUFS; gap = packets - 1; @@ -158,7 +210,7 @@ static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av, gap + new_head + 1); gap = -new_head; } - new_head += av->dccpav_buf_len; + new_head += DCCP_MAX_ACKVEC_LEN; } av->dccpav_buf_head = new_head; @@ -251,7 +303,7 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, goto out_duplicate; delta -= len + 1; - if (++index == av->dccpav_buf_len) + if (++index == DCCP_MAX_ACKVEC_LEN) index = 0; } } @@ -259,7 +311,6 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, av->dccpav_buf_ackno = ackno; dccp_timestamp(sk, &av->dccpav_time); out: - dccp_pr_debug(""); return 0; out_duplicate: @@ -297,44 +348,50 @@ void dccp_ackvec_print(const struct dccp_ackvec *av) } #endif -static void dccp_ackvec_throw_away_ack_record(struct dccp_ackvec *av) +static void dccp_ackvec_throw_record(struct dccp_ackvec *av, + struct dccp_ackvec_record *avr) { - /* - * As we're keeping track of the ack vector size (dccpav_vec_len) and - * the sent ack vector size (dccpav_sent_len) we don't need - * dccpav_buf_tail at all, but keep this code here as in the future - * we'll implement a vector of ack records, as suggested in - * draft-ietf-dccp-spec-11.txt Appendix A. -acme - */ -#if 0 - u32 new_buf_tail = av->dccpav_ack_ptr + 1; - if (new_buf_tail >= av->dccpav_vec_len) - new_buf_tail -= av->dccpav_vec_len; - av->dccpav_buf_tail = new_buf_tail; -#endif - av->dccpav_vec_len -= av->dccpav_sent_len; + struct dccp_ackvec_record *next; + + av->dccpav_buf_tail = avr->dccpavr_ack_ptr - 1; + if (av->dccpav_buf_tail == 0) + av->dccpav_buf_tail = DCCP_MAX_ACKVEC_LEN - 1; + + av->dccpav_vec_len -= avr->dccpavr_sent_len; + + /* free records */ + list_for_each_entry_safe_from(avr, next, &av->dccpav_records, + dccpavr_node) { + list_del_init(&avr->dccpavr_node); + dccp_ackvec_record_delete(avr); + } } void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk, const u64 ackno) { - /* Check if we actually sent an ACK vector */ - if (av->dccpav_ack_seqno == DCCP_MAX_SEQNO + 1) - return; + struct dccp_ackvec_record *avr; - if (ackno == av->dccpav_ack_seqno) { + /* + * If we traverse backwards, it should be faster when we have large + * windows. We will be receiving ACKs for stuff we sent a while back + * -sorbo. + */ + list_for_each_entry_reverse(avr, &av->dccpav_records, dccpavr_node) { + if (ackno == avr->dccpavr_ack_seqno) { #ifdef CONFIG_IP_DCCP_DEBUG - struct dccp_sock *dp = dccp_sk(sk); - const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? - "CLIENT rx ack: " : "server rx ack: "; + struct dccp_sock *dp = dccp_sk(sk); + const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? + "CLIENT rx ack: " : "server rx ack: "; #endif - dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, " - "ack_ackno=%llu, ACKED!\n", - debug_prefix, 1, - (unsigned long long)av->dccpav_ack_seqno, - (unsigned long long)av->dccpav_ack_ackno); - dccp_ackvec_throw_away_ack_record(av); - av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1; + dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, " + "ack_ackno=%llu, ACKED!\n", + debug_prefix, 1, + (unsigned long long)avr->dccpavr_ack_seqno, + (unsigned long long)avr->dccpavr_ack_ackno); + dccp_ackvec_throw_record(av, avr); + break; + } } } @@ -344,28 +401,20 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av, const unsigned char *vector) { unsigned char i; + struct dccp_ackvec_record *avr; /* Check if we actually sent an ACK vector */ - if (av->dccpav_ack_seqno == DCCP_MAX_SEQNO + 1) - return; - /* - * We're in the receiver half connection, so if the received an ACK - * vector ackno (e.g. 50) before dccpav_ack_seqno (e.g. 52), we're - * not interested. - * - * Extra explanation with example: - * - * if we received an ACK vector with ackno 50, it can only be acking - * 50, 49, 48, etc, not 52 (the seqno for the ACK vector we sent). - */ - /* dccp_pr_debug("is %llu < %llu? ", ackno, av->dccpav_ack_seqno); */ - if (before48(ackno, av->dccpav_ack_seqno)) { - /* dccp_pr_debug_cat("yes\n"); */ + if (list_empty(&av->dccpav_records)) return; - } - /* dccp_pr_debug_cat("no\n"); */ i = len; + /* + * XXX + * I think it might be more efficient to work backwards. See comment on + * rcv_ackno. -sorbo. + */ + avr = list_entry(av->dccpav_records.next, struct dccp_ackvec_record, + dccpavr_node); while (i--) { const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK; u64 ackno_end_rl; @@ -373,14 +422,20 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av, dccp_set_seqno(&ackno_end_rl, ackno - rl); /* - * dccp_pr_debug("is %llu <= %llu <= %llu? ", ackno_end_rl, - * av->dccpav_ack_seqno, ackno); + * If our AVR sequence number is greater than the ack, go + * forward in the AVR list until it is not so. */ - if (between48(av->dccpav_ack_seqno, ackno_end_rl, ackno)) { + list_for_each_entry_from(avr, &av->dccpav_records, + dccpavr_node) { + if (!after48(avr->dccpavr_ack_seqno, ackno)) + goto found; + } + /* End of the dccpav_records list, not found, exit */ + break; +found: + if (between48(avr->dccpavr_ack_seqno, ackno_end_rl, ackno)) { const u8 state = (*vector & DCCP_ACKVEC_STATE_MASK) >> 6; - /* dccp_pr_debug_cat("yes\n"); */ - if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED) { #ifdef CONFIG_IP_DCCP_DEBUG struct dccp_sock *dp = dccp_sk(sk); @@ -393,19 +448,16 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av, "ACKED!\n", debug_prefix, len, (unsigned long long) - av->dccpav_ack_seqno, + avr->dccpavr_ack_seqno, (unsigned long long) - av->dccpav_ack_ackno); - dccp_ackvec_throw_away_ack_record(av); + avr->dccpavr_ack_ackno); + dccp_ackvec_throw_record(av, avr); } /* - * If dccpav_ack_seqno was not received, no problem - * we'll send another ACK vector. + * If it wasn't received, continue scanning... we might + * find another one. */ - av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1; - break; } - /* dccp_pr_debug_cat("no\n"); */ dccp_set_seqno(&ackno, ackno_end_rl - 1); ++vector; @@ -424,3 +476,43 @@ int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, len, value); return 0; } + +static char dccp_ackvec_slab_msg[] __initdata = + KERN_CRIT "DCCP: Unable to create ack vectors slab caches\n"; + +int __init dccp_ackvec_init(void) +{ + dccp_ackvec_slab = kmem_cache_create("dccp_ackvec", + sizeof(struct dccp_ackvec), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + if (dccp_ackvec_slab == NULL) + goto out_err; + + dccp_ackvec_record_slab = + kmem_cache_create("dccp_ackvec_record", + sizeof(struct dccp_ackvec_record), + 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + if (dccp_ackvec_record_slab == NULL) + goto out_destroy_slab; + + return 0; + +out_destroy_slab: + kmem_cache_destroy(dccp_ackvec_slab); + dccp_ackvec_slab = NULL; +out_err: + printk(dccp_ackvec_slab_msg); + return -ENOBUFS; +} + +void dccp_ackvec_exit(void) +{ + if (dccp_ackvec_slab != NULL) { + kmem_cache_destroy(dccp_ackvec_slab); + dccp_ackvec_slab = NULL; + } + if (dccp_ackvec_record_slab != NULL) { + kmem_cache_destroy(dccp_ackvec_record_slab); + dccp_ackvec_record_slab = NULL; + } +} |