diff options
author | Gerrit Renker <gerrit@erg.abdn.ac.uk> | 2009-01-05 00:42:53 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-01-05 00:42:53 -0500 |
commit | ddebc973c56b51b4e5d84d606f0430d81b895d67 (patch) | |
tree | cebe0e4461346072b2063132fc1d9cf8c3e148f1 /net/dccp/ccid.c | |
parent | 6ea2fde13abd3444008ab5e9585f9ed249e6047e (diff) |
dccp: Lockless integration of CCID congestion-control plugins
Based on Arnaldo's earlier patch, this patch integrates the standardised
CCID congestion control plugins (CCID-2 and CCID-3) of DCCP with dccp.ko:
* enables a faster connection path by eliminating the need to always go
through the CCID registration lock;
* updates the implementation to use only a single array whose size equals
the number of configured CCIDs instead of the maximum (256);
* since the CCIDs are now fixed array elements, synchronization is no
longer needed, simplifying use and implementation.
CCID-2 is suggested as minimum for a basic DCCP implementation (RFC 4340, 10);
CCID-3 is a standards-track CCID supported by RFC 4342 and RFC 5348.
Signed-off-by: Gerrit Renker <gerrit@erg.abdn.ac.uk>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/dccp/ccid.c')
-rw-r--r-- | net/dccp/ccid.c | 156 |
1 files changed, 100 insertions, 56 deletions
diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c index bcc643f992ae..538d3b1da623 100644 --- a/net/dccp/ccid.c +++ b/net/dccp/ccid.c | |||
@@ -13,6 +13,70 @@ | |||
13 | 13 | ||
14 | #include "ccid.h" | 14 | #include "ccid.h" |
15 | 15 | ||
16 | static struct ccid_operations *ccids[] = { | ||
17 | &ccid2_ops, | ||
18 | #ifdef CONFIG_IP_DCCP_CCID3 | ||
19 | &ccid3_ops, | ||
20 | #endif | ||
21 | }; | ||
22 | |||
23 | static struct ccid_operations *ccid_by_number(const u8 id) | ||
24 | { | ||
25 | int i; | ||
26 | |||
27 | for (i = 0; i < ARRAY_SIZE(ccids); i++) | ||
28 | if (ccids[i]->ccid_id == id) | ||
29 | return ccids[i]; | ||
30 | return NULL; | ||
31 | } | ||
32 | |||
33 | /* check that up to @array_len members in @ccid_array are supported */ | ||
34 | bool ccid_support_check(u8 const *ccid_array, u8 array_len) | ||
35 | { | ||
36 | while (array_len > 0) | ||
37 | if (ccid_by_number(ccid_array[--array_len]) == NULL) | ||
38 | return false; | ||
39 | return true; | ||
40 | } | ||
41 | |||
42 | /** | ||
43 | * ccid_get_builtin_ccids - Populate a list of built-in CCIDs | ||
44 | * @ccid_array: pointer to copy into | ||
45 | * @array_len: value to return length into | ||
46 | * This function allocates memory - caller must see that it is freed after use. | ||
47 | */ | ||
48 | int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) | ||
49 | { | ||
50 | *ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any()); | ||
51 | if (*ccid_array == NULL) | ||
52 | return -ENOBUFS; | ||
53 | |||
54 | for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1) | ||
55 | (*ccid_array)[*array_len] = ccids[*array_len]->ccid_id; | ||
56 | return 0; | ||
57 | } | ||
58 | |||
59 | int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, | ||
60 | char __user *optval, int __user *optlen) | ||
61 | { | ||
62 | u8 *ccid_array, array_len; | ||
63 | int err = 0; | ||
64 | |||
65 | if (len < ARRAY_SIZE(ccids)) | ||
66 | return -EINVAL; | ||
67 | |||
68 | if (ccid_get_builtin_ccids(&ccid_array, &array_len)) | ||
69 | return -ENOBUFS; | ||
70 | |||
71 | if (put_user(array_len, optlen) || | ||
72 | copy_to_user(optval, ccid_array, array_len)) | ||
73 | err = -EFAULT; | ||
74 | |||
75 | kfree(ccid_array); | ||
76 | return err; | ||
77 | } | ||
78 | |||
79 | #ifdef ___OLD_INTERFACE_TO_BE_REMOVED___ | ||
16 | static u8 builtin_ccids[] = { | 80 | static u8 builtin_ccids[] = { |
17 | DCCPC_CCID2, /* CCID2 is supported by default */ | 81 | DCCPC_CCID2, /* CCID2 is supported by default */ |
18 | #if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE) | 82 | #if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE) |
@@ -62,6 +126,7 @@ static inline void ccids_read_unlock(void) | |||
62 | #define ccids_read_lock() do { } while(0) | 126 | #define ccids_read_lock() do { } while(0) |
63 | #define ccids_read_unlock() do { } while(0) | 127 | #define ccids_read_unlock() do { } while(0) |
64 | #endif | 128 | #endif |
129 | #endif /* ___OLD_INTERFACE_TO_BE_REMOVED___ */ | ||
65 | 130 | ||
66 | static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...) | 131 | static struct kmem_cache *ccid_kmem_cache_create(int obj_size, const char *fmt,...) |
67 | { | 132 | { |
@@ -93,6 +158,7 @@ static void ccid_kmem_cache_destroy(struct kmem_cache *slab) | |||
93 | } | 158 | } |
94 | } | 159 | } |
95 | 160 | ||
161 | #ifdef ___OLD_INTERFACE_TO_BE_REMOVED___ | ||
96 | /* check that up to @array_len members in @ccid_array are supported */ | 162 | /* check that up to @array_len members in @ccid_array are supported */ |
97 | bool ccid_support_check(u8 const *ccid_array, u8 array_len) | 163 | bool ccid_support_check(u8 const *ccid_array, u8 array_len) |
98 | { | 164 | { |
@@ -133,8 +199,9 @@ int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, | |||
133 | return -EFAULT; | 199 | return -EFAULT; |
134 | return 0; | 200 | return 0; |
135 | } | 201 | } |
202 | #endif /* ___OLD_INTERFACE_TO_BE_REMOVED___ */ | ||
136 | 203 | ||
137 | int ccid_register(struct ccid_operations *ccid_ops) | 204 | static int ccid_activate(struct ccid_operations *ccid_ops) |
138 | { | 205 | { |
139 | int err = -ENOBUFS; | 206 | int err = -ENOBUFS; |
140 | 207 | ||
@@ -152,79 +219,40 @@ int ccid_register(struct ccid_operations *ccid_ops) | |||
152 | if (ccid_ops->ccid_hc_tx_slab == NULL) | 219 | if (ccid_ops->ccid_hc_tx_slab == NULL) |
153 | goto out_free_rx_slab; | 220 | goto out_free_rx_slab; |
154 | 221 | ||
155 | ccids_write_lock(); | 222 | pr_info("CCID: Activated CCID %d (%s)\n", |
156 | err = -EEXIST; | ||
157 | if (ccids[ccid_ops->ccid_id] == NULL) { | ||
158 | ccids[ccid_ops->ccid_id] = ccid_ops; | ||
159 | err = 0; | ||
160 | } | ||
161 | ccids_write_unlock(); | ||
162 | if (err != 0) | ||
163 | goto out_free_tx_slab; | ||
164 | |||
165 | pr_info("CCID: Registered CCID %d (%s)\n", | ||
166 | ccid_ops->ccid_id, ccid_ops->ccid_name); | 223 | ccid_ops->ccid_id, ccid_ops->ccid_name); |
224 | err = 0; | ||
167 | out: | 225 | out: |
168 | return err; | 226 | return err; |
169 | out_free_tx_slab: | ||
170 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); | ||
171 | ccid_ops->ccid_hc_tx_slab = NULL; | ||
172 | goto out; | ||
173 | out_free_rx_slab: | 227 | out_free_rx_slab: |
174 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); | 228 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); |
175 | ccid_ops->ccid_hc_rx_slab = NULL; | 229 | ccid_ops->ccid_hc_rx_slab = NULL; |
176 | goto out; | 230 | goto out; |
177 | } | 231 | } |
178 | 232 | ||
179 | EXPORT_SYMBOL_GPL(ccid_register); | 233 | static void ccid_deactivate(struct ccid_operations *ccid_ops) |
180 | |||
181 | int ccid_unregister(struct ccid_operations *ccid_ops) | ||
182 | { | 234 | { |
183 | ccids_write_lock(); | ||
184 | ccids[ccid_ops->ccid_id] = NULL; | ||
185 | ccids_write_unlock(); | ||
186 | |||
187 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); | 235 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab); |
188 | ccid_ops->ccid_hc_tx_slab = NULL; | 236 | ccid_ops->ccid_hc_tx_slab = NULL; |
189 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); | 237 | ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab); |
190 | ccid_ops->ccid_hc_rx_slab = NULL; | 238 | ccid_ops->ccid_hc_rx_slab = NULL; |
191 | 239 | ||
192 | pr_info("CCID: Unregistered CCID %d (%s)\n", | 240 | pr_info("CCID: Deactivated CCID %d (%s)\n", |
193 | ccid_ops->ccid_id, ccid_ops->ccid_name); | 241 | ccid_ops->ccid_id, ccid_ops->ccid_name); |
194 | return 0; | ||
195 | } | 242 | } |
196 | 243 | ||
197 | EXPORT_SYMBOL_GPL(ccid_unregister); | ||
198 | |||
199 | struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) | 244 | struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) |
200 | { | 245 | { |
201 | struct ccid_operations *ccid_ops; | 246 | struct ccid_operations *ccid_ops = ccid_by_number(id); |
202 | struct ccid *ccid = NULL; | 247 | struct ccid *ccid = NULL; |
203 | 248 | ||
204 | ccids_read_lock(); | ||
205 | #ifdef CONFIG_MODULES | ||
206 | if (ccids[id] == NULL) { | ||
207 | /* We only try to load if in process context */ | ||
208 | ccids_read_unlock(); | ||
209 | if (gfp & GFP_ATOMIC) | ||
210 | goto out; | ||
211 | request_module("net-dccp-ccid-%d", id); | ||
212 | ccids_read_lock(); | ||
213 | } | ||
214 | #endif | ||
215 | ccid_ops = ccids[id]; | ||
216 | if (ccid_ops == NULL) | 249 | if (ccid_ops == NULL) |
217 | goto out_unlock; | 250 | goto out; |
218 | |||
219 | if (!try_module_get(ccid_ops->ccid_owner)) | ||
220 | goto out_unlock; | ||
221 | |||
222 | ccids_read_unlock(); | ||
223 | 251 | ||
224 | ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab : | 252 | ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab : |
225 | ccid_ops->ccid_hc_tx_slab, gfp); | 253 | ccid_ops->ccid_hc_tx_slab, gfp); |
226 | if (ccid == NULL) | 254 | if (ccid == NULL) |
227 | goto out_module_put; | 255 | goto out; |
228 | ccid->ccid_ops = ccid_ops; | 256 | ccid->ccid_ops = ccid_ops; |
229 | if (rx) { | 257 | if (rx) { |
230 | memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size); | 258 | memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size); |
@@ -239,15 +267,10 @@ struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) | |||
239 | } | 267 | } |
240 | out: | 268 | out: |
241 | return ccid; | 269 | return ccid; |
242 | out_unlock: | ||
243 | ccids_read_unlock(); | ||
244 | goto out; | ||
245 | out_free_ccid: | 270 | out_free_ccid: |
246 | kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab : | 271 | kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab : |
247 | ccid_ops->ccid_hc_tx_slab, ccid); | 272 | ccid_ops->ccid_hc_tx_slab, ccid); |
248 | ccid = NULL; | 273 | ccid = NULL; |
249 | out_module_put: | ||
250 | module_put(ccid_ops->ccid_owner); | ||
251 | goto out; | 274 | goto out; |
252 | } | 275 | } |
253 | 276 | ||
@@ -270,10 +293,6 @@ static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) | |||
270 | ccid_ops->ccid_hc_tx_exit(sk); | 293 | ccid_ops->ccid_hc_tx_exit(sk); |
271 | kmem_cache_free(ccid_ops->ccid_hc_tx_slab, ccid); | 294 | kmem_cache_free(ccid_ops->ccid_hc_tx_slab, ccid); |
272 | } | 295 | } |
273 | ccids_read_lock(); | ||
274 | if (ccids[ccid_ops->ccid_id] != NULL) | ||
275 | module_put(ccid_ops->ccid_owner); | ||
276 | ccids_read_unlock(); | ||
277 | } | 296 | } |
278 | 297 | ||
279 | void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) | 298 | void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk) |
@@ -289,3 +308,28 @@ void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk) | |||
289 | } | 308 | } |
290 | 309 | ||
291 | EXPORT_SYMBOL_GPL(ccid_hc_tx_delete); | 310 | EXPORT_SYMBOL_GPL(ccid_hc_tx_delete); |
311 | |||
312 | int __init ccid_initialize_builtins(void) | ||
313 | { | ||
314 | int i, err; | ||
315 | |||
316 | for (i = 0; i < ARRAY_SIZE(ccids); i++) { | ||
317 | err = ccid_activate(ccids[i]); | ||
318 | if (err) | ||
319 | goto unwind_registrations; | ||
320 | } | ||
321 | return 0; | ||
322 | |||
323 | unwind_registrations: | ||
324 | while(--i >= 0) | ||
325 | ccid_deactivate(ccids[i]); | ||
326 | return err; | ||
327 | } | ||
328 | |||
329 | void ccid_cleanup_builtins(void) | ||
330 | { | ||
331 | int i; | ||
332 | |||
333 | for (i = 0; i < ARRAY_SIZE(ccids); i++) | ||
334 | ccid_deactivate(ccids[i]); | ||
335 | } | ||