1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2015 Joyent, Inc.
14 */
15
16 /*
17 * Overlay device encapsulation plugin management
18 *
19 * For more information, see the big theory statement in
20 * uts/common/io/overlay/overlay.c
21 */
22
23 #include <sys/types.h>
24 #include <sys/kmem.h>
25 #include <sys/ddi.h>
26 #include <sys/sunddi.h>
27 #include <sys/errno.h>
28 #include <sys/sysmacros.h>
29 #include <sys/modctl.h>
30
31 #include <sys/overlay_impl.h>
32
33 static kmem_cache_t *overlay_plugin_cache;
34 static kmutex_t overlay_plugin_lock;
35 static list_t overlay_plugin_list;
36
37 #define OVERLAY_MODDIR "overlay"
38
39 /* ARGSUSED */
40 static int
overlay_plugin_cache_constructor(void * buf,void * arg,int kmflags)41 overlay_plugin_cache_constructor(void *buf, void *arg, int kmflags)
42 {
43 overlay_plugin_t *opp = buf;
44
45 mutex_init(&opp->ovp_mutex, NULL, MUTEX_DRIVER, NULL);
46 list_link_init(&opp->ovp_link);
47
48 return (0);
49 }
50
51 /* ARGSUSED */
52 static void
overlay_plugin_cache_destructor(void * buf,void * arg)53 overlay_plugin_cache_destructor(void *buf, void *arg)
54 {
55 overlay_plugin_t *opp = buf;
56 ASSERT(list_link_active(&opp->ovp_link) == 0);
57 mutex_destroy(&opp->ovp_mutex);
58 }
59
60 void
overlay_plugin_init(void)61 overlay_plugin_init(void)
62 {
63 mutex_init(&overlay_plugin_lock, NULL, MUTEX_DRIVER, 0);
64
65 /*
66 * In the future we may want to have a reaper to unload unused modules
67 * to help the kernel be able to reclaim memory.
68 */
69 overlay_plugin_cache = kmem_cache_create("overlay_plugin_cache",
70 sizeof (overlay_plugin_t), 0, overlay_plugin_cache_constructor,
71 overlay_plugin_cache_destructor, NULL, NULL, NULL, 0);
72 list_create(&overlay_plugin_list, sizeof (overlay_plugin_t),
73 offsetof(overlay_plugin_t, ovp_link));
74 }
75
76 void
overlay_plugin_fini(void)77 overlay_plugin_fini(void)
78 {
79 mutex_enter(&overlay_plugin_lock);
80 VERIFY(list_is_empty(&overlay_plugin_list));
81 mutex_exit(&overlay_plugin_lock);
82
83 list_destroy(&overlay_plugin_list);
84 kmem_cache_destroy(overlay_plugin_cache);
85 mutex_destroy(&overlay_plugin_lock);
86 }
87
88 overlay_plugin_register_t *
overlay_plugin_alloc(uint_t version)89 overlay_plugin_alloc(uint_t version)
90 {
91 overlay_plugin_register_t *ovrp;
92 /* Version 1 is the only one that exists */
93 if (version != OVEP_VERSION_ONE)
94 return (NULL);
95
96 ovrp = kmem_zalloc(sizeof (overlay_plugin_register_t), KM_SLEEP);
97 ovrp->ovep_version = version;
98 return (ovrp);
99 }
100
101 void
overlay_plugin_free(overlay_plugin_register_t * ovrp)102 overlay_plugin_free(overlay_plugin_register_t *ovrp)
103 {
104 kmem_free(ovrp, sizeof (overlay_plugin_register_t));
105 }
106
107 int
overlay_plugin_register(overlay_plugin_register_t * ovrp)108 overlay_plugin_register(overlay_plugin_register_t *ovrp)
109 {
110 overlay_plugin_t *opp, *ipp;
111
112 /* Sanity check parameters of the registration */
113 if (ovrp->ovep_version != OVEP_VERSION_ONE)
114 return (EINVAL);
115
116 if (ovrp->ovep_name == NULL || ovrp->ovep_ops == NULL)
117 return (EINVAL);
118
119 if ((ovrp->ovep_flags & ~(OVEP_F_VLAN_TAG)) != 0)
120 return (EINVAL);
121
122 if (ovrp->ovep_id_size < 1)
123 return (EINVAL);
124
125 /* Don't support anything that has an id size larger than 8 bytes */
126 if (ovrp->ovep_id_size > 8)
127 return (ENOTSUP);
128
129 if (ovrp->ovep_dest == OVERLAY_PLUGIN_D_INVALID)
130 return (EINVAL);
131
132 if ((ovrp->ovep_dest & ~OVERLAY_PLUGIN_D_MASK) != 0)
133 return (EINVAL);
134
135 if (ovrp->ovep_ops->ovpo_callbacks != 0)
136 return (EINVAL);
137 if (ovrp->ovep_ops->ovpo_init == NULL)
138 return (EINVAL);
139 if (ovrp->ovep_ops->ovpo_fini == NULL)
140 return (EINVAL);
141 if (ovrp->ovep_ops->ovpo_encap == NULL)
142 return (EINVAL);
143 if (ovrp->ovep_ops->ovpo_decap == NULL)
144 return (EINVAL);
145 if (ovrp->ovep_ops->ovpo_socket == NULL)
146 return (EINVAL);
147 if (ovrp->ovep_ops->ovpo_getprop == NULL)
148 return (EINVAL);
149 if (ovrp->ovep_ops->ovpo_setprop == NULL)
150 return (EINVAL);
151 if (ovrp->ovep_ops->ovpo_propinfo == NULL)
152 return (EINVAL);
153
154
155 opp = kmem_cache_alloc(overlay_plugin_cache, KM_SLEEP);
156 opp->ovp_active = 0;
157 opp->ovp_name = ovrp->ovep_name;
158 opp->ovp_ops = ovrp->ovep_ops;
159 opp->ovp_props = ovrp->ovep_props;
160 opp->ovp_id_size = ovrp->ovep_id_size;
161 opp->ovp_flags = ovrp->ovep_flags;
162 opp->ovp_dest = ovrp->ovep_dest;
163
164 opp->ovp_nprops = 0;
165 if (ovrp->ovep_props != NULL) {
166 while (ovrp->ovep_props[opp->ovp_nprops] != NULL) {
167 if (strlen(ovrp->ovep_props[opp->ovp_nprops]) >=
168 OVERLAY_PROP_NAMELEN) {
169 mutex_exit(&overlay_plugin_lock);
170 kmem_cache_free(overlay_plugin_cache, opp);
171 return (EINVAL);
172 }
173 opp->ovp_nprops++;
174 }
175 }
176
177 mutex_enter(&overlay_plugin_lock);
178 for (ipp = list_head(&overlay_plugin_list); ipp != NULL;
179 ipp = list_next(&overlay_plugin_list, ipp)) {
180 if (strcmp(ipp->ovp_name, opp->ovp_name) == 0) {
181 mutex_exit(&overlay_plugin_lock);
182 kmem_cache_free(overlay_plugin_cache, opp);
183 return (EEXIST);
184 }
185 }
186 list_insert_tail(&overlay_plugin_list, opp);
187 mutex_exit(&overlay_plugin_lock);
188
189 return (0);
190 }
191
192 int
overlay_plugin_unregister(const char * name)193 overlay_plugin_unregister(const char *name)
194 {
195 overlay_plugin_t *opp;
196
197 mutex_enter(&overlay_plugin_lock);
198 for (opp = list_head(&overlay_plugin_list); opp != NULL;
199 opp = list_next(&overlay_plugin_list, opp)) {
200 if (strcmp(opp->ovp_name, name) == 0)
201 break;
202 }
203
204 if (opp == NULL) {
205 mutex_exit(&overlay_plugin_lock);
206 return (ENOENT);
207 }
208
209 mutex_enter(&opp->ovp_mutex);
210 if (opp->ovp_active > 0) {
211 mutex_exit(&opp->ovp_mutex);
212 mutex_exit(&overlay_plugin_lock);
213 return (EBUSY);
214 }
215 mutex_exit(&opp->ovp_mutex);
216
217 list_remove(&overlay_plugin_list, opp);
218 mutex_exit(&overlay_plugin_lock);
219
220 kmem_cache_free(overlay_plugin_cache, opp);
221 return (0);
222 }
223
224 overlay_plugin_t *
overlay_plugin_lookup(const char * name)225 overlay_plugin_lookup(const char *name)
226 {
227 overlay_plugin_t *opp;
228 boolean_t trymodload = B_FALSE;
229
230 for (;;) {
231 mutex_enter(&overlay_plugin_lock);
232 for (opp = list_head(&overlay_plugin_list); opp != NULL;
233 opp = list_next(&overlay_plugin_list, opp)) {
234 if (strcmp(name, opp->ovp_name) == 0) {
235 mutex_enter(&opp->ovp_mutex);
236 opp->ovp_active++;
237 mutex_exit(&opp->ovp_mutex);
238 mutex_exit(&overlay_plugin_lock);
239 return (opp);
240 }
241 }
242 mutex_exit(&overlay_plugin_lock);
243
244 if (trymodload == B_TRUE)
245 return (NULL);
246
247 /*
248 * If we didn't find it, it may still exist, but just not have
249 * been a loaded module. In that case, we'll do one attempt to
250 * load it.
251 */
252 if (modload(OVERLAY_MODDIR, (char *)name) == -1)
253 return (NULL);
254 trymodload = B_TRUE;
255 }
256
257 }
258
259 void
overlay_plugin_rele(overlay_plugin_t * opp)260 overlay_plugin_rele(overlay_plugin_t *opp)
261 {
262 mutex_enter(&opp->ovp_mutex);
263 ASSERT(opp->ovp_active > 0);
264 opp->ovp_active--;
265 mutex_exit(&opp->ovp_mutex);
266 }
267
268 void
overlay_plugin_walk(overlay_plugin_walk_f func,void * arg)269 overlay_plugin_walk(overlay_plugin_walk_f func, void *arg)
270 {
271 overlay_plugin_t *opp;
272 mutex_enter(&overlay_plugin_lock);
273 for (opp = list_head(&overlay_plugin_list); opp != NULL;
274 opp = list_next(&overlay_plugin_list, opp)) {
275 if (func(opp, arg) != 0) {
276 mutex_exit(&overlay_plugin_lock);
277 return;
278 }
279 }
280 mutex_exit(&overlay_plugin_lock);
281 }
282