xref: /illumos-gate/usr/src/uts/common/io/overlay/overlay_plugin.c (revision dd72704bd9e794056c558153663c739e2012d721)
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
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
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
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
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 *
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
102 overlay_plugin_free(overlay_plugin_register_t *ovrp)
103 {
104 	kmem_free(ovrp, sizeof (overlay_plugin_register_t));
105 }
106 
107 int
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
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 *
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
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
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