diff options
Diffstat (limited to 'net/dccp/ccid.c')
-rw-r--r-- | net/dccp/ccid.c | 101 |
1 files changed, 77 insertions, 24 deletions
diff --git a/net/dccp/ccid.c b/net/dccp/ccid.c index 4809753d12ae..e3fb52b4f5c6 100644 --- a/net/dccp/ccid.c +++ b/net/dccp/ccid.c | |||
@@ -13,6 +13,13 @@ | |||
13 | 13 | ||
14 | #include "ccid.h" | 14 | #include "ccid.h" |
15 | 15 | ||
16 | static u8 builtin_ccids[] = { | ||
17 | DCCPC_CCID2, /* CCID2 is supported by default */ | ||
18 | #if defined(CONFIG_IP_DCCP_CCID3) || defined(CONFIG_IP_DCCP_CCID3_MODULE) | ||
19 | DCCPC_CCID3, | ||
20 | #endif | ||
21 | }; | ||
22 | |||
16 | static struct ccid_operations *ccids[CCID_MAX]; | 23 | static struct ccid_operations *ccids[CCID_MAX]; |
17 | #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) | 24 | #if defined(CONFIG_SMP) || defined(CONFIG_PREEMPT) |
18 | static atomic_t ccids_lockct = ATOMIC_INIT(0); | 25 | static atomic_t ccids_lockct = ATOMIC_INIT(0); |
@@ -86,6 +93,47 @@ static void ccid_kmem_cache_destroy(struct kmem_cache *slab) | |||
86 | } | 93 | } |
87 | } | 94 | } |
88 | 95 | ||
96 | /* check that up to @array_len members in @ccid_array are supported */ | ||
97 | bool ccid_support_check(u8 const *ccid_array, u8 array_len) | ||
98 | { | ||
99 | u8 i, j, found; | ||
100 | |||
101 | for (i = 0, found = 0; i < array_len; i++, found = 0) { | ||
102 | for (j = 0; !found && j < ARRAY_SIZE(builtin_ccids); j++) | ||
103 | found = (ccid_array[i] == builtin_ccids[j]); | ||
104 | if (!found) | ||
105 | return false; | ||
106 | } | ||
107 | return true; | ||
108 | } | ||
109 | |||
110 | /** | ||
111 | * ccid_get_builtin_ccids - Provide copy of `builtin' CCID array | ||
112 | * @ccid_array: pointer to copy into | ||
113 | * @array_len: value to return length into | ||
114 | * This function allocates memory - caller must see that it is freed after use. | ||
115 | */ | ||
116 | int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len) | ||
117 | { | ||
118 | *ccid_array = kmemdup(builtin_ccids, sizeof(builtin_ccids), gfp_any()); | ||
119 | if (*ccid_array == NULL) | ||
120 | return -ENOBUFS; | ||
121 | *array_len = ARRAY_SIZE(builtin_ccids); | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | int ccid_getsockopt_builtin_ccids(struct sock *sk, int len, | ||
126 | char __user *optval, int __user *optlen) | ||
127 | { | ||
128 | if (len < sizeof(builtin_ccids)) | ||
129 | return -EINVAL; | ||
130 | |||
131 | if (put_user(sizeof(builtin_ccids), optlen) || | ||
132 | copy_to_user(optval, builtin_ccids, sizeof(builtin_ccids))) | ||
133 | return -EFAULT; | ||
134 | return 0; | ||
135 | } | ||
136 | |||
89 | int ccid_register(struct ccid_operations *ccid_ops) | 137 | int ccid_register(struct ccid_operations *ccid_ops) |
90 | { | 138 | { |
91 | int err = -ENOBUFS; | 139 | int err = -ENOBUFS; |
@@ -148,22 +196,41 @@ int ccid_unregister(struct ccid_operations *ccid_ops) | |||
148 | 196 | ||
149 | EXPORT_SYMBOL_GPL(ccid_unregister); | 197 | EXPORT_SYMBOL_GPL(ccid_unregister); |
150 | 198 | ||
199 | /** | ||
200 | * ccid_request_module - Pre-load CCID module for later use | ||
201 | * This should be called only from process context (e.g. during connection | ||
202 | * setup) and is necessary for later calls to ccid_new (typically in software | ||
203 | * interrupt), so that it has the modules available when they are needed. | ||
204 | */ | ||
205 | static int ccid_request_module(u8 id) | ||
206 | { | ||
207 | if (!in_atomic()) { | ||
208 | ccids_read_lock(); | ||
209 | if (ccids[id] == NULL) { | ||
210 | ccids_read_unlock(); | ||
211 | return request_module("net-dccp-ccid-%d", id); | ||
212 | } | ||
213 | ccids_read_unlock(); | ||
214 | } | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | int ccid_request_modules(u8 const *ccid_array, u8 array_len) | ||
219 | { | ||
220 | #ifdef CONFIG_KMOD | ||
221 | while (array_len--) | ||
222 | if (ccid_request_module(ccid_array[array_len])) | ||
223 | return -1; | ||
224 | #endif | ||
225 | return 0; | ||
226 | } | ||
227 | |||
151 | struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) | 228 | struct ccid *ccid_new(unsigned char id, struct sock *sk, int rx, gfp_t gfp) |
152 | { | 229 | { |
153 | struct ccid_operations *ccid_ops; | 230 | struct ccid_operations *ccid_ops; |
154 | struct ccid *ccid = NULL; | 231 | struct ccid *ccid = NULL; |
155 | 232 | ||
156 | ccids_read_lock(); | 233 | ccids_read_lock(); |
157 | #ifdef CONFIG_KMOD | ||
158 | if (ccids[id] == NULL) { | ||
159 | /* We only try to load if in process context */ | ||
160 | ccids_read_unlock(); | ||
161 | if (gfp & GFP_ATOMIC) | ||
162 | goto out; | ||
163 | request_module("net-dccp-ccid-%d", id); | ||
164 | ccids_read_lock(); | ||
165 | } | ||
166 | #endif | ||
167 | ccid_ops = ccids[id]; | 234 | ccid_ops = ccids[id]; |
168 | if (ccid_ops == NULL) | 235 | if (ccid_ops == NULL) |
169 | goto out_unlock; | 236 | goto out_unlock; |
@@ -205,20 +272,6 @@ out_module_put: | |||
205 | 272 | ||
206 | EXPORT_SYMBOL_GPL(ccid_new); | 273 | EXPORT_SYMBOL_GPL(ccid_new); |
207 | 274 | ||
208 | struct ccid *ccid_hc_rx_new(unsigned char id, struct sock *sk, gfp_t gfp) | ||
209 | { | ||
210 | return ccid_new(id, sk, 1, gfp); | ||
211 | } | ||
212 | |||
213 | EXPORT_SYMBOL_GPL(ccid_hc_rx_new); | ||
214 | |||
215 | struct ccid *ccid_hc_tx_new(unsigned char id,struct sock *sk, gfp_t gfp) | ||
216 | { | ||
217 | return ccid_new(id, sk, 0, gfp); | ||
218 | } | ||
219 | |||
220 | EXPORT_SYMBOL_GPL(ccid_hc_tx_new); | ||
221 | |||
222 | static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) | 275 | static void ccid_delete(struct ccid *ccid, struct sock *sk, int rx) |
223 | { | 276 | { |
224 | struct ccid_operations *ccid_ops; | 277 | struct ccid_operations *ccid_ops; |