Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/net/dccp/ccid.c
15109 views
1
/*
2
* net/dccp/ccid.c
3
*
4
* An implementation of the DCCP protocol
5
* Arnaldo Carvalho de Melo <[email protected]>
6
*
7
* CCID infrastructure
8
*
9
* This program is free software; you can redistribute it and/or modify it
10
* under the terms of the GNU General Public License version 2 as
11
* published by the Free Software Foundation.
12
*/
13
14
#include <linux/slab.h>
15
16
#include "ccid.h"
17
#include "ccids/lib/tfrc.h"
18
19
static struct ccid_operations *ccids[] = {
20
&ccid2_ops,
21
#ifdef CONFIG_IP_DCCP_CCID3
22
&ccid3_ops,
23
#endif
24
};
25
26
static struct ccid_operations *ccid_by_number(const u8 id)
27
{
28
int i;
29
30
for (i = 0; i < ARRAY_SIZE(ccids); i++)
31
if (ccids[i]->ccid_id == id)
32
return ccids[i];
33
return NULL;
34
}
35
36
/* check that up to @array_len members in @ccid_array are supported */
37
bool ccid_support_check(u8 const *ccid_array, u8 array_len)
38
{
39
while (array_len > 0)
40
if (ccid_by_number(ccid_array[--array_len]) == NULL)
41
return false;
42
return true;
43
}
44
45
/**
46
* ccid_get_builtin_ccids - Populate a list of built-in CCIDs
47
* @ccid_array: pointer to copy into
48
* @array_len: value to return length into
49
* This function allocates memory - caller must see that it is freed after use.
50
*/
51
int ccid_get_builtin_ccids(u8 **ccid_array, u8 *array_len)
52
{
53
*ccid_array = kmalloc(ARRAY_SIZE(ccids), gfp_any());
54
if (*ccid_array == NULL)
55
return -ENOBUFS;
56
57
for (*array_len = 0; *array_len < ARRAY_SIZE(ccids); *array_len += 1)
58
(*ccid_array)[*array_len] = ccids[*array_len]->ccid_id;
59
return 0;
60
}
61
62
int ccid_getsockopt_builtin_ccids(struct sock *sk, int len,
63
char __user *optval, int __user *optlen)
64
{
65
u8 *ccid_array, array_len;
66
int err = 0;
67
68
if (ccid_get_builtin_ccids(&ccid_array, &array_len))
69
return -ENOBUFS;
70
71
if (put_user(array_len, optlen))
72
err = -EFAULT;
73
else if (len > 0 && copy_to_user(optval, ccid_array,
74
len > array_len ? array_len : len))
75
err = -EFAULT;
76
77
kfree(ccid_array);
78
return err;
79
}
80
81
static struct kmem_cache *ccid_kmem_cache_create(int obj_size, char *slab_name_fmt, const char *fmt,...)
82
{
83
struct kmem_cache *slab;
84
va_list args;
85
86
va_start(args, fmt);
87
vsnprintf(slab_name_fmt, CCID_SLAB_NAME_LENGTH, fmt, args);
88
va_end(args);
89
90
slab = kmem_cache_create(slab_name_fmt, sizeof(struct ccid) + obj_size, 0,
91
SLAB_HWCACHE_ALIGN, NULL);
92
return slab;
93
}
94
95
static void ccid_kmem_cache_destroy(struct kmem_cache *slab)
96
{
97
if (slab != NULL)
98
kmem_cache_destroy(slab);
99
}
100
101
static int ccid_activate(struct ccid_operations *ccid_ops)
102
{
103
int err = -ENOBUFS;
104
105
ccid_ops->ccid_hc_rx_slab =
106
ccid_kmem_cache_create(ccid_ops->ccid_hc_rx_obj_size,
107
ccid_ops->ccid_hc_rx_slab_name,
108
"ccid%u_hc_rx_sock",
109
ccid_ops->ccid_id);
110
if (ccid_ops->ccid_hc_rx_slab == NULL)
111
goto out;
112
113
ccid_ops->ccid_hc_tx_slab =
114
ccid_kmem_cache_create(ccid_ops->ccid_hc_tx_obj_size,
115
ccid_ops->ccid_hc_tx_slab_name,
116
"ccid%u_hc_tx_sock",
117
ccid_ops->ccid_id);
118
if (ccid_ops->ccid_hc_tx_slab == NULL)
119
goto out_free_rx_slab;
120
121
pr_info("CCID: Activated CCID %d (%s)\n",
122
ccid_ops->ccid_id, ccid_ops->ccid_name);
123
err = 0;
124
out:
125
return err;
126
out_free_rx_slab:
127
ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
128
ccid_ops->ccid_hc_rx_slab = NULL;
129
goto out;
130
}
131
132
static void ccid_deactivate(struct ccid_operations *ccid_ops)
133
{
134
ccid_kmem_cache_destroy(ccid_ops->ccid_hc_tx_slab);
135
ccid_ops->ccid_hc_tx_slab = NULL;
136
ccid_kmem_cache_destroy(ccid_ops->ccid_hc_rx_slab);
137
ccid_ops->ccid_hc_rx_slab = NULL;
138
139
pr_info("CCID: Deactivated CCID %d (%s)\n",
140
ccid_ops->ccid_id, ccid_ops->ccid_name);
141
}
142
143
struct ccid *ccid_new(const u8 id, struct sock *sk, bool rx)
144
{
145
struct ccid_operations *ccid_ops = ccid_by_number(id);
146
struct ccid *ccid = NULL;
147
148
if (ccid_ops == NULL)
149
goto out;
150
151
ccid = kmem_cache_alloc(rx ? ccid_ops->ccid_hc_rx_slab :
152
ccid_ops->ccid_hc_tx_slab, gfp_any());
153
if (ccid == NULL)
154
goto out;
155
ccid->ccid_ops = ccid_ops;
156
if (rx) {
157
memset(ccid + 1, 0, ccid_ops->ccid_hc_rx_obj_size);
158
if (ccid->ccid_ops->ccid_hc_rx_init != NULL &&
159
ccid->ccid_ops->ccid_hc_rx_init(ccid, sk) != 0)
160
goto out_free_ccid;
161
} else {
162
memset(ccid + 1, 0, ccid_ops->ccid_hc_tx_obj_size);
163
if (ccid->ccid_ops->ccid_hc_tx_init != NULL &&
164
ccid->ccid_ops->ccid_hc_tx_init(ccid, sk) != 0)
165
goto out_free_ccid;
166
}
167
out:
168
return ccid;
169
out_free_ccid:
170
kmem_cache_free(rx ? ccid_ops->ccid_hc_rx_slab :
171
ccid_ops->ccid_hc_tx_slab, ccid);
172
ccid = NULL;
173
goto out;
174
}
175
176
void ccid_hc_rx_delete(struct ccid *ccid, struct sock *sk)
177
{
178
if (ccid != NULL) {
179
if (ccid->ccid_ops->ccid_hc_rx_exit != NULL)
180
ccid->ccid_ops->ccid_hc_rx_exit(sk);
181
kmem_cache_free(ccid->ccid_ops->ccid_hc_rx_slab, ccid);
182
}
183
}
184
185
void ccid_hc_tx_delete(struct ccid *ccid, struct sock *sk)
186
{
187
if (ccid != NULL) {
188
if (ccid->ccid_ops->ccid_hc_tx_exit != NULL)
189
ccid->ccid_ops->ccid_hc_tx_exit(sk);
190
kmem_cache_free(ccid->ccid_ops->ccid_hc_tx_slab, ccid);
191
}
192
}
193
194
int __init ccid_initialize_builtins(void)
195
{
196
int i, err = tfrc_lib_init();
197
198
if (err)
199
return err;
200
201
for (i = 0; i < ARRAY_SIZE(ccids); i++) {
202
err = ccid_activate(ccids[i]);
203
if (err)
204
goto unwind_registrations;
205
}
206
return 0;
207
208
unwind_registrations:
209
while(--i >= 0)
210
ccid_deactivate(ccids[i]);
211
tfrc_lib_exit();
212
return err;
213
}
214
215
void ccid_cleanup_builtins(void)
216
{
217
int i;
218
219
for (i = 0; i < ARRAY_SIZE(ccids); i++)
220
ccid_deactivate(ccids[i]);
221
tfrc_lib_exit();
222
}
223
224