diff options
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 @@ | |||
13 | #include "dccp.h" | 13 | #include "dccp.h" |
14 | 14 | ||
15 | #include <linux/dccp.h> | 15 | #include <linux/dccp.h> |
16 | #include <linux/init.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/kernel.h> | ||
16 | #include <linux/skbuff.h> | 19 | #include <linux/skbuff.h> |
20 | #include <linux/slab.h> | ||
17 | 21 | ||
18 | #include <net/sock.h> | 22 | #include <net/sock.h> |
19 | 23 | ||
24 | static kmem_cache_t *dccp_ackvec_slab; | ||
25 | static kmem_cache_t *dccp_ackvec_record_slab; | ||
26 | |||
27 | static struct dccp_ackvec_record *dccp_ackvec_record_new(void) | ||
28 | { | ||
29 | struct dccp_ackvec_record *avr = | ||
30 | kmem_cache_alloc(dccp_ackvec_record_slab, GFP_ATOMIC); | ||
31 | |||
32 | if (avr != NULL) | ||
33 | INIT_LIST_HEAD(&avr->dccpavr_node); | ||
34 | |||
35 | return avr; | ||
36 | } | ||
37 | |||
38 | static void dccp_ackvec_record_delete(struct dccp_ackvec_record *avr) | ||
39 | { | ||
40 | if (unlikely(avr == NULL)) | ||
41 | return; | ||
42 | /* Check if deleting a linked record */ | ||
43 | WARN_ON(!list_empty(&avr->dccpavr_node)); | ||
44 | kmem_cache_free(dccp_ackvec_record_slab, avr); | ||
45 | } | ||
46 | |||
47 | static void dccp_ackvec_insert_avr(struct dccp_ackvec *av, | ||
48 | struct dccp_ackvec_record *avr) | ||
49 | { | ||
50 | /* | ||
51 | * AVRs are sorted by seqno. Since we are sending them in order, we | ||
52 | * just add the AVR at the head of the list. | ||
53 | * -sorbo. | ||
54 | */ | ||
55 | if (!list_empty(&av->dccpav_records)) { | ||
56 | const struct dccp_ackvec_record *head = | ||
57 | list_entry(av->dccpav_records.next, | ||
58 | struct dccp_ackvec_record, | ||
59 | dccpavr_node); | ||
60 | BUG_ON(before48(avr->dccpavr_ack_seqno, | ||
61 | head->dccpavr_ack_seqno)); | ||
62 | } | ||
63 | |||
64 | list_add(&avr->dccpavr_node, &av->dccpav_records); | ||
65 | } | ||
66 | |||
20 | int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) | 67 | int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) |
21 | { | 68 | { |
22 | struct dccp_sock *dp = dccp_sk(sk); | 69 | struct dccp_sock *dp = dccp_sk(sk); |
70 | #ifdef CONFIG_IP_DCCP_DEBUG | ||
71 | const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? | ||
72 | "CLIENT tx: " : "server tx: "; | ||
73 | #endif | ||
23 | struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; | 74 | struct dccp_ackvec *av = dp->dccps_hc_rx_ackvec; |
24 | int len = av->dccpav_vec_len + 2; | 75 | int len = av->dccpav_vec_len + 2; |
25 | struct timeval now; | 76 | struct timeval now; |
26 | u32 elapsed_time; | 77 | u32 elapsed_time; |
27 | unsigned char *to, *from; | 78 | unsigned char *to, *from; |
79 | struct dccp_ackvec_record *avr; | ||
80 | |||
81 | if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) | ||
82 | return -1; | ||
28 | 83 | ||
29 | dccp_timestamp(sk, &now); | 84 | dccp_timestamp(sk, &now); |
30 | elapsed_time = timeval_delta(&now, &av->dccpav_time) / 10; | 85 | elapsed_time = timeval_delta(&now, &av->dccpav_time) / 10; |
31 | 86 | ||
32 | if (elapsed_time != 0) | 87 | if (elapsed_time != 0 && |
33 | dccp_insert_option_elapsed_time(sk, skb, elapsed_time); | 88 | dccp_insert_option_elapsed_time(sk, skb, elapsed_time)) |
34 | |||
35 | if (DCCP_SKB_CB(skb)->dccpd_opt_len + len > DCCP_MAX_OPT_LEN) | ||
36 | return -1; | 89 | return -1; |
37 | 90 | ||
38 | /* | 91 | avr = dccp_ackvec_record_new(); |
39 | * XXX: now we have just one ack vector sent record, so | 92 | if (avr == NULL) |
40 | * we have to wait for it to be cleared. | ||
41 | * | ||
42 | * Of course this is not acceptable, but this is just for | ||
43 | * basic testing now. | ||
44 | */ | ||
45 | if (av->dccpav_ack_seqno != DCCP_MAX_SEQNO + 1) | ||
46 | return -1; | 93 | return -1; |
47 | 94 | ||
48 | DCCP_SKB_CB(skb)->dccpd_opt_len += len; | 95 | DCCP_SKB_CB(skb)->dccpd_opt_len += len; |
@@ -55,8 +102,8 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) | |||
55 | from = av->dccpav_buf + av->dccpav_buf_head; | 102 | from = av->dccpav_buf + av->dccpav_buf_head; |
56 | 103 | ||
57 | /* Check if buf_head wraps */ | 104 | /* Check if buf_head wraps */ |
58 | if ((int)av->dccpav_buf_head + len > av->dccpav_vec_len) { | 105 | if ((int)av->dccpav_buf_head + len > DCCP_MAX_ACKVEC_LEN) { |
59 | const u32 tailsize = av->dccpav_vec_len - av->dccpav_buf_head; | 106 | const u32 tailsize = DCCP_MAX_ACKVEC_LEN - av->dccpav_buf_head; |
60 | 107 | ||
61 | memcpy(to, from, tailsize); | 108 | memcpy(to, from, tailsize); |
62 | to += tailsize; | 109 | to += tailsize; |
@@ -73,45 +120,37 @@ int dccp_insert_option_ackvec(struct sock *sk, struct sk_buff *skb) | |||
73 | * sequence number it used for the ack packet; ack_ptr will equal | 120 | * sequence number it used for the ack packet; ack_ptr will equal |
74 | * buf_head; ack_ackno will equal buf_ackno; and ack_nonce will | 121 | * buf_head; ack_ackno will equal buf_ackno; and ack_nonce will |
75 | * equal buf_nonce. | 122 | * equal buf_nonce. |
76 | * | ||
77 | * This implemention uses just one ack record for now. | ||
78 | */ | 123 | */ |
79 | av->dccpav_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq; | 124 | avr->dccpavr_ack_seqno = DCCP_SKB_CB(skb)->dccpd_seq; |
80 | av->dccpav_ack_ptr = av->dccpav_buf_head; | 125 | avr->dccpavr_ack_ptr = av->dccpav_buf_head; |
81 | av->dccpav_ack_ackno = av->dccpav_buf_ackno; | 126 | avr->dccpavr_ack_ackno = av->dccpav_buf_ackno; |
82 | av->dccpav_ack_nonce = av->dccpav_buf_nonce; | 127 | avr->dccpavr_ack_nonce = av->dccpav_buf_nonce; |
83 | av->dccpav_sent_len = av->dccpav_vec_len; | 128 | avr->dccpavr_sent_len = av->dccpav_vec_len; |
129 | |||
130 | dccp_ackvec_insert_avr(av, avr); | ||
84 | 131 | ||
85 | dccp_pr_debug("%sACK Vector 0, len=%d, ack_seqno=%llu, " | 132 | dccp_pr_debug("%sACK Vector 0, len=%d, ack_seqno=%llu, " |
86 | "ack_ackno=%llu\n", | 133 | "ack_ackno=%llu\n", |
87 | debug_prefix, av->dccpav_sent_len, | 134 | debug_prefix, avr->dccpavr_sent_len, |
88 | (unsigned long long)av->dccpav_ack_seqno, | 135 | (unsigned long long)avr->dccpavr_ack_seqno, |
89 | (unsigned long long)av->dccpav_ack_ackno); | 136 | (unsigned long long)avr->dccpavr_ack_ackno); |
90 | return -1; | 137 | return 0; |
91 | } | 138 | } |
92 | 139 | ||
93 | struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len, | 140 | struct dccp_ackvec *dccp_ackvec_alloc(const gfp_t priority) |
94 | const gfp_t priority) | ||
95 | { | 141 | { |
96 | struct dccp_ackvec *av; | 142 | struct dccp_ackvec *av = kmem_cache_alloc(dccp_ackvec_slab, priority); |
97 | |||
98 | BUG_ON(len == 0); | ||
99 | 143 | ||
100 | if (len > DCCP_MAX_ACKVEC_LEN) | ||
101 | return NULL; | ||
102 | |||
103 | av = kmalloc(sizeof(*av) + len, priority); | ||
104 | if (av != NULL) { | 144 | if (av != NULL) { |
105 | av->dccpav_buf_len = len; | ||
106 | av->dccpav_buf_head = | 145 | av->dccpav_buf_head = |
107 | av->dccpav_buf_tail = av->dccpav_buf_len - 1; | 146 | av->dccpav_buf_tail = DCCP_MAX_ACKVEC_LEN - 1; |
108 | av->dccpav_buf_ackno = | 147 | av->dccpav_buf_ackno = DCCP_MAX_SEQNO + 1; |
109 | av->dccpav_ack_ackno = av->dccpav_ack_seqno = ~0LLU; | ||
110 | av->dccpav_buf_nonce = av->dccpav_buf_nonce = 0; | 148 | av->dccpav_buf_nonce = av->dccpav_buf_nonce = 0; |
111 | av->dccpav_ack_ptr = 0; | 149 | av->dccpav_ack_ptr = 0; |
112 | av->dccpav_time.tv_sec = 0; | 150 | av->dccpav_time.tv_sec = 0; |
113 | av->dccpav_time.tv_usec = 0; | 151 | av->dccpav_time.tv_usec = 0; |
114 | av->dccpav_sent_len = av->dccpav_vec_len = 0; | 152 | av->dccpav_sent_len = av->dccpav_vec_len = 0; |
153 | INIT_LIST_HEAD(&av->dccpav_records); | ||
115 | } | 154 | } |
116 | 155 | ||
117 | return av; | 156 | return av; |
@@ -119,7 +158,20 @@ struct dccp_ackvec *dccp_ackvec_alloc(const unsigned int len, | |||
119 | 158 | ||
120 | void dccp_ackvec_free(struct dccp_ackvec *av) | 159 | void dccp_ackvec_free(struct dccp_ackvec *av) |
121 | { | 160 | { |
122 | kfree(av); | 161 | if (unlikely(av == NULL)) |
162 | return; | ||
163 | |||
164 | if (!list_empty(&av->dccpav_records)) { | ||
165 | struct dccp_ackvec_record *avr, *next; | ||
166 | |||
167 | list_for_each_entry_safe(avr, next, &av->dccpav_records, | ||
168 | dccpavr_node) { | ||
169 | list_del_init(&avr->dccpavr_node); | ||
170 | dccp_ackvec_record_delete(avr); | ||
171 | } | ||
172 | } | ||
173 | |||
174 | kmem_cache_free(dccp_ackvec_slab, av); | ||
123 | } | 175 | } |
124 | 176 | ||
125 | static inline u8 dccp_ackvec_state(const struct dccp_ackvec *av, | 177 | 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, | |||
146 | unsigned int gap; | 198 | unsigned int gap; |
147 | long new_head; | 199 | long new_head; |
148 | 200 | ||
149 | if (av->dccpav_vec_len + packets > av->dccpav_buf_len) | 201 | if (av->dccpav_vec_len + packets > DCCP_MAX_ACKVEC_LEN) |
150 | return -ENOBUFS; | 202 | return -ENOBUFS; |
151 | 203 | ||
152 | gap = packets - 1; | 204 | gap = packets - 1; |
@@ -158,7 +210,7 @@ static inline int dccp_ackvec_set_buf_head_state(struct dccp_ackvec *av, | |||
158 | gap + new_head + 1); | 210 | gap + new_head + 1); |
159 | gap = -new_head; | 211 | gap = -new_head; |
160 | } | 212 | } |
161 | new_head += av->dccpav_buf_len; | 213 | new_head += DCCP_MAX_ACKVEC_LEN; |
162 | } | 214 | } |
163 | 215 | ||
164 | av->dccpav_buf_head = new_head; | 216 | av->dccpav_buf_head = new_head; |
@@ -251,7 +303,7 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, | |||
251 | goto out_duplicate; | 303 | goto out_duplicate; |
252 | 304 | ||
253 | delta -= len + 1; | 305 | delta -= len + 1; |
254 | if (++index == av->dccpav_buf_len) | 306 | if (++index == DCCP_MAX_ACKVEC_LEN) |
255 | index = 0; | 307 | index = 0; |
256 | } | 308 | } |
257 | } | 309 | } |
@@ -259,7 +311,6 @@ int dccp_ackvec_add(struct dccp_ackvec *av, const struct sock *sk, | |||
259 | av->dccpav_buf_ackno = ackno; | 311 | av->dccpav_buf_ackno = ackno; |
260 | dccp_timestamp(sk, &av->dccpav_time); | 312 | dccp_timestamp(sk, &av->dccpav_time); |
261 | out: | 313 | out: |
262 | dccp_pr_debug(""); | ||
263 | return 0; | 314 | return 0; |
264 | 315 | ||
265 | out_duplicate: | 316 | out_duplicate: |
@@ -297,44 +348,50 @@ void dccp_ackvec_print(const struct dccp_ackvec *av) | |||
297 | } | 348 | } |
298 | #endif | 349 | #endif |
299 | 350 | ||
300 | static void dccp_ackvec_throw_away_ack_record(struct dccp_ackvec *av) | 351 | static void dccp_ackvec_throw_record(struct dccp_ackvec *av, |
352 | struct dccp_ackvec_record *avr) | ||
301 | { | 353 | { |
302 | /* | 354 | struct dccp_ackvec_record *next; |
303 | * As we're keeping track of the ack vector size (dccpav_vec_len) and | 355 | |
304 | * the sent ack vector size (dccpav_sent_len) we don't need | 356 | av->dccpav_buf_tail = avr->dccpavr_ack_ptr - 1; |
305 | * dccpav_buf_tail at all, but keep this code here as in the future | 357 | if (av->dccpav_buf_tail == 0) |
306 | * we'll implement a vector of ack records, as suggested in | 358 | av->dccpav_buf_tail = DCCP_MAX_ACKVEC_LEN - 1; |
307 | * draft-ietf-dccp-spec-11.txt Appendix A. -acme | 359 | |
308 | */ | 360 | av->dccpav_vec_len -= avr->dccpavr_sent_len; |
309 | #if 0 | 361 | |
310 | u32 new_buf_tail = av->dccpav_ack_ptr + 1; | 362 | /* free records */ |
311 | if (new_buf_tail >= av->dccpav_vec_len) | 363 | list_for_each_entry_safe_from(avr, next, &av->dccpav_records, |
312 | new_buf_tail -= av->dccpav_vec_len; | 364 | dccpavr_node) { |
313 | av->dccpav_buf_tail = new_buf_tail; | 365 | list_del_init(&avr->dccpavr_node); |
314 | #endif | 366 | dccp_ackvec_record_delete(avr); |
315 | av->dccpav_vec_len -= av->dccpav_sent_len; | 367 | } |
316 | } | 368 | } |
317 | 369 | ||
318 | void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk, | 370 | void dccp_ackvec_check_rcv_ackno(struct dccp_ackvec *av, struct sock *sk, |
319 | const u64 ackno) | 371 | const u64 ackno) |
320 | { | 372 | { |
321 | /* Check if we actually sent an ACK vector */ | 373 | struct dccp_ackvec_record *avr; |
322 | if (av->dccpav_ack_seqno == DCCP_MAX_SEQNO + 1) | ||
323 | return; | ||
324 | 374 | ||
325 | if (ackno == av->dccpav_ack_seqno) { | 375 | /* |
376 | * If we traverse backwards, it should be faster when we have large | ||
377 | * windows. We will be receiving ACKs for stuff we sent a while back | ||
378 | * -sorbo. | ||
379 | */ | ||
380 | list_for_each_entry_reverse(avr, &av->dccpav_records, dccpavr_node) { | ||
381 | if (ackno == avr->dccpavr_ack_seqno) { | ||
326 | #ifdef CONFIG_IP_DCCP_DEBUG | 382 | #ifdef CONFIG_IP_DCCP_DEBUG |
327 | struct dccp_sock *dp = dccp_sk(sk); | 383 | struct dccp_sock *dp = dccp_sk(sk); |
328 | const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? | 384 | const char *debug_prefix = dp->dccps_role == DCCP_ROLE_CLIENT ? |
329 | "CLIENT rx ack: " : "server rx ack: "; | 385 | "CLIENT rx ack: " : "server rx ack: "; |
330 | #endif | 386 | #endif |
331 | dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, " | 387 | dccp_pr_debug("%sACK packet 0, len=%d, ack_seqno=%llu, " |
332 | "ack_ackno=%llu, ACKED!\n", | 388 | "ack_ackno=%llu, ACKED!\n", |
333 | debug_prefix, 1, | 389 | debug_prefix, 1, |
334 | (unsigned long long)av->dccpav_ack_seqno, | 390 | (unsigned long long)avr->dccpavr_ack_seqno, |
335 | (unsigned long long)av->dccpav_ack_ackno); | 391 | (unsigned long long)avr->dccpavr_ack_ackno); |
336 | dccp_ackvec_throw_away_ack_record(av); | 392 | dccp_ackvec_throw_record(av, avr); |
337 | av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1; | 393 | break; |
394 | } | ||
338 | } | 395 | } |
339 | } | 396 | } |
340 | 397 | ||
@@ -344,28 +401,20 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av, | |||
344 | const unsigned char *vector) | 401 | const unsigned char *vector) |
345 | { | 402 | { |
346 | unsigned char i; | 403 | unsigned char i; |
404 | struct dccp_ackvec_record *avr; | ||
347 | 405 | ||
348 | /* Check if we actually sent an ACK vector */ | 406 | /* Check if we actually sent an ACK vector */ |
349 | if (av->dccpav_ack_seqno == DCCP_MAX_SEQNO + 1) | 407 | if (list_empty(&av->dccpav_records)) |
350 | return; | ||
351 | /* | ||
352 | * We're in the receiver half connection, so if the received an ACK | ||
353 | * vector ackno (e.g. 50) before dccpav_ack_seqno (e.g. 52), we're | ||
354 | * not interested. | ||
355 | * | ||
356 | * Extra explanation with example: | ||
357 | * | ||
358 | * if we received an ACK vector with ackno 50, it can only be acking | ||
359 | * 50, 49, 48, etc, not 52 (the seqno for the ACK vector we sent). | ||
360 | */ | ||
361 | /* dccp_pr_debug("is %llu < %llu? ", ackno, av->dccpav_ack_seqno); */ | ||
362 | if (before48(ackno, av->dccpav_ack_seqno)) { | ||
363 | /* dccp_pr_debug_cat("yes\n"); */ | ||
364 | return; | 408 | return; |
365 | } | ||
366 | /* dccp_pr_debug_cat("no\n"); */ | ||
367 | 409 | ||
368 | i = len; | 410 | i = len; |
411 | /* | ||
412 | * XXX | ||
413 | * I think it might be more efficient to work backwards. See comment on | ||
414 | * rcv_ackno. -sorbo. | ||
415 | */ | ||
416 | avr = list_entry(av->dccpav_records.next, struct dccp_ackvec_record, | ||
417 | dccpavr_node); | ||
369 | while (i--) { | 418 | while (i--) { |
370 | const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK; | 419 | const u8 rl = *vector & DCCP_ACKVEC_LEN_MASK; |
371 | u64 ackno_end_rl; | 420 | u64 ackno_end_rl; |
@@ -373,14 +422,20 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av, | |||
373 | dccp_set_seqno(&ackno_end_rl, ackno - rl); | 422 | dccp_set_seqno(&ackno_end_rl, ackno - rl); |
374 | 423 | ||
375 | /* | 424 | /* |
376 | * dccp_pr_debug("is %llu <= %llu <= %llu? ", ackno_end_rl, | 425 | * If our AVR sequence number is greater than the ack, go |
377 | * av->dccpav_ack_seqno, ackno); | 426 | * forward in the AVR list until it is not so. |
378 | */ | 427 | */ |
379 | if (between48(av->dccpav_ack_seqno, ackno_end_rl, ackno)) { | 428 | list_for_each_entry_from(avr, &av->dccpav_records, |
429 | dccpavr_node) { | ||
430 | if (!after48(avr->dccpavr_ack_seqno, ackno)) | ||
431 | goto found; | ||
432 | } | ||
433 | /* End of the dccpav_records list, not found, exit */ | ||
434 | break; | ||
435 | found: | ||
436 | if (between48(avr->dccpavr_ack_seqno, ackno_end_rl, ackno)) { | ||
380 | const u8 state = (*vector & | 437 | const u8 state = (*vector & |
381 | DCCP_ACKVEC_STATE_MASK) >> 6; | 438 | DCCP_ACKVEC_STATE_MASK) >> 6; |
382 | /* dccp_pr_debug_cat("yes\n"); */ | ||
383 | |||
384 | if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED) { | 439 | if (state != DCCP_ACKVEC_STATE_NOT_RECEIVED) { |
385 | #ifdef CONFIG_IP_DCCP_DEBUG | 440 | #ifdef CONFIG_IP_DCCP_DEBUG |
386 | struct dccp_sock *dp = dccp_sk(sk); | 441 | struct dccp_sock *dp = dccp_sk(sk); |
@@ -393,19 +448,16 @@ static void dccp_ackvec_check_rcv_ackvector(struct dccp_ackvec *av, | |||
393 | "ACKED!\n", | 448 | "ACKED!\n", |
394 | debug_prefix, len, | 449 | debug_prefix, len, |
395 | (unsigned long long) | 450 | (unsigned long long) |
396 | av->dccpav_ack_seqno, | 451 | avr->dccpavr_ack_seqno, |
397 | (unsigned long long) | 452 | (unsigned long long) |
398 | av->dccpav_ack_ackno); | 453 | avr->dccpavr_ack_ackno); |
399 | dccp_ackvec_throw_away_ack_record(av); | 454 | dccp_ackvec_throw_record(av, avr); |
400 | } | 455 | } |
401 | /* | 456 | /* |
402 | * If dccpav_ack_seqno was not received, no problem | 457 | * If it wasn't received, continue scanning... we might |
403 | * we'll send another ACK vector. | 458 | * find another one. |
404 | */ | 459 | */ |
405 | av->dccpav_ack_seqno = DCCP_MAX_SEQNO + 1; | ||
406 | break; | ||
407 | } | 460 | } |
408 | /* dccp_pr_debug_cat("no\n"); */ | ||
409 | 461 | ||
410 | dccp_set_seqno(&ackno, ackno_end_rl - 1); | 462 | dccp_set_seqno(&ackno, ackno_end_rl - 1); |
411 | ++vector; | 463 | ++vector; |
@@ -424,3 +476,43 @@ int dccp_ackvec_parse(struct sock *sk, const struct sk_buff *skb, | |||
424 | len, value); | 476 | len, value); |
425 | return 0; | 477 | return 0; |
426 | } | 478 | } |
479 | |||
480 | static char dccp_ackvec_slab_msg[] __initdata = | ||
481 | KERN_CRIT "DCCP: Unable to create ack vectors slab caches\n"; | ||
482 | |||
483 | int __init dccp_ackvec_init(void) | ||
484 | { | ||
485 | dccp_ackvec_slab = kmem_cache_create("dccp_ackvec", | ||
486 | sizeof(struct dccp_ackvec), 0, | ||
487 | SLAB_HWCACHE_ALIGN, NULL, NULL); | ||
488 | if (dccp_ackvec_slab == NULL) | ||
489 | goto out_err; | ||
490 | |||
491 | dccp_ackvec_record_slab = | ||
492 | kmem_cache_create("dccp_ackvec_record", | ||
493 | sizeof(struct dccp_ackvec_record), | ||
494 | 0, SLAB_HWCACHE_ALIGN, NULL, NULL); | ||
495 | if (dccp_ackvec_record_slab == NULL) | ||
496 | goto out_destroy_slab; | ||
497 | |||
498 | return 0; | ||
499 | |||
500 | out_destroy_slab: | ||
501 | kmem_cache_destroy(dccp_ackvec_slab); | ||
502 | dccp_ackvec_slab = NULL; | ||
503 | out_err: | ||
504 | printk(dccp_ackvec_slab_msg); | ||
505 | return -ENOBUFS; | ||
506 | } | ||
507 | |||
508 | void dccp_ackvec_exit(void) | ||
509 | { | ||
510 | if (dccp_ackvec_slab != NULL) { | ||
511 | kmem_cache_destroy(dccp_ackvec_slab); | ||
512 | dccp_ackvec_slab = NULL; | ||
513 | } | ||
514 | if (dccp_ackvec_record_slab != NULL) { | ||
515 | kmem_cache_destroy(dccp_ackvec_record_slab); | ||
516 | dccp_ackvec_record_slab = NULL; | ||
517 | } | ||
518 | } | ||