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