xref: /illumos-gate/usr/src/lib/varpd/libvarpd/common/libvarpd.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  * varpd library
18  */
19 
20 #include <stdlib.h>
21 #include <errno.h>
22 #include <umem.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25 #include <sys/avl.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <strings.h>
29 
30 #include <libvarpd_impl.h>
31 
32 static int
33 libvarpd_instance_comparator(const void *lp, const void *rp)
34 {
35 	const varpd_instance_t *lpp, *rpp;
36 	lpp = lp;
37 	rpp = rp;
38 
39 	if (lpp->vri_id > rpp->vri_id)
40 		return (1);
41 	if (lpp->vri_id < rpp->vri_id)
42 		return (-1);
43 	return (0);
44 }
45 
46 static int
47 libvarpd_instance_lcomparator(const void *lp, const void *rp)
48 {
49 	const varpd_instance_t *lpp, *rpp;
50 	lpp = lp;
51 	rpp = rp;
52 
53 	if (lpp->vri_linkid > rpp->vri_linkid)
54 		return (1);
55 	if (lpp->vri_linkid < rpp->vri_linkid)
56 		return (-1);
57 	return (0);
58 }
59 
60 int
61 libvarpd_create(varpd_handle_t **vphp)
62 {
63 	int ret;
64 	varpd_impl_t *vip;
65 	char buf[32];
66 
67 	if (vphp == NULL)
68 		return (EINVAL);
69 
70 	*vphp = NULL;
71 	vip = umem_alloc(sizeof (varpd_impl_t), UMEM_DEFAULT);
72 	if (vip == NULL)
73 		return (errno);
74 
75 	bzero(vip, sizeof (varpd_impl_t));
76 	(void) snprintf(buf, sizeof (buf), "varpd_%p", vip);
77 	vip->vdi_idspace = id_space_create(buf, LIBVARPD_ID_MIN,
78 	    LIBVARPD_ID_MAX);
79 	if (vip->vdi_idspace == NULL) {
80 		int ret = errno;
81 		umem_free(vip, sizeof (varpd_impl_t));
82 		return (ret);
83 	}
84 
85 	vip->vdi_qcache = umem_cache_create("query", sizeof (varpd_query_t), 0,
86 	    NULL, NULL, NULL, NULL, NULL, 0);
87 	if (vip->vdi_qcache == NULL) {
88 		int ret = errno;
89 		id_space_destroy(vip->vdi_idspace);
90 		umem_free(vip, sizeof (varpd_impl_t));
91 		return (ret);
92 	}
93 
94 	if ((ret = libvarpd_overlay_init(vip)) != 0) {
95 		umem_cache_destroy(vip->vdi_qcache);
96 		id_space_destroy(vip->vdi_idspace);
97 		umem_free(vip, sizeof (varpd_impl_t));
98 		return (ret);
99 	}
100 
101 	libvarpd_persist_init(vip);
102 
103 	avl_create(&vip->vdi_plugins, libvarpd_plugin_comparator,
104 	    sizeof (varpd_plugin_t), offsetof(varpd_plugin_t, vpp_node));
105 
106 	avl_create(&vip->vdi_instances, libvarpd_instance_comparator,
107 	    sizeof (varpd_instance_t), offsetof(varpd_instance_t, vri_inode));
108 	avl_create(&vip->vdi_linstances, libvarpd_instance_lcomparator,
109 	    sizeof (varpd_instance_t), offsetof(varpd_instance_t, vri_lnode));
110 
111 	if (mutex_init(&vip->vdi_lock, USYNC_THREAD | LOCK_ERRORCHECK,
112 	    NULL) != 0)
113 		libvarpd_panic("failed to create mutex: %d", errno);
114 
115 	vip->vdi_doorfd = -1;
116 	*vphp = (varpd_handle_t *)vip;
117 	return (0);
118 }
119 
120 void
121 libvarpd_destroy(varpd_handle_t *vhp)
122 {
123 	varpd_impl_t *vip = (varpd_impl_t *)vhp;
124 
125 	libvarpd_overlay_lookup_quiesce(vhp);
126 	if (mutex_destroy(&vip->vdi_lock) != 0)
127 		libvarpd_panic("failed to destroy mutex: %d", errno);
128 	libvarpd_persist_fini(vip);
129 	libvarpd_overlay_fini(vip);
130 	umem_cache_destroy(vip->vdi_qcache);
131 	id_space_destroy(vip->vdi_idspace);
132 	umem_free(vip, sizeof (varpd_impl_t));
133 }
134 
135 int
136 libvarpd_instance_create(varpd_handle_t *vhp, datalink_id_t linkid,
137     const char *pname, varpd_instance_handle_t **outp)
138 {
139 	int ret;
140 	varpd_impl_t *vip = (varpd_impl_t *)vhp;
141 	varpd_plugin_t *plugin;
142 	varpd_instance_t *inst, lookup;
143 	overlay_plugin_dest_t dest;
144 	uint64_t vid;
145 
146 	/*
147 	 * We should really have our own errnos.
148 	 */
149 	plugin = libvarpd_plugin_lookup(vip, pname);
150 	if (plugin == NULL)
151 		return (ENOENT);
152 
153 	if ((ret = libvarpd_overlay_info(vip, linkid, &dest, NULL, &vid)) != 0)
154 		return (ret);
155 
156 	inst = umem_alloc(sizeof (varpd_instance_t), UMEM_DEFAULT);
157 	if (inst == NULL)
158 		return (ENOMEM);
159 
160 	inst->vri_id = id_alloc(vip->vdi_idspace);
161 	if (inst->vri_id == -1)
162 		libvarpd_panic("failed to allocate id from vdi_idspace: %d",
163 		    errno);
164 	inst->vri_linkid = linkid;
165 	inst->vri_vnetid = vid;
166 	inst->vri_mode = plugin->vpp_mode;
167 	inst->vri_dest = dest;
168 	inst->vri_plugin = plugin;
169 	inst->vri_impl = vip;
170 	inst->vri_flags = 0;
171 	if ((ret = plugin->vpp_ops->vpo_create((varpd_provider_handle_t *)inst,
172 	    &inst->vri_private, dest)) != 0) {
173 		id_free(vip->vdi_idspace, inst->vri_id);
174 		umem_free(inst, sizeof (varpd_instance_t));
175 		return (ret);
176 	}
177 
178 	if (mutex_init(&inst->vri_lock, USYNC_THREAD | LOCK_ERRORCHECK,
179 	    NULL) != 0)
180 		libvarpd_panic("failed to create mutex: %d", errno);
181 
182 	mutex_enter(&vip->vdi_lock);
183 	lookup.vri_id = inst->vri_id;
184 	if (avl_find(&vip->vdi_instances, &lookup, NULL) != NULL)
185 		libvarpd_panic("found duplicate instance with id %d",
186 		    lookup.vri_id);
187 	avl_add(&vip->vdi_instances, inst);
188 	lookup.vri_linkid = inst->vri_linkid;
189 	if (avl_find(&vip->vdi_linstances, &lookup, NULL) != NULL)
190 		libvarpd_panic("found duplicate linstance with id %d",
191 		    lookup.vri_linkid);
192 	avl_add(&vip->vdi_linstances, inst);
193 	mutex_exit(&vip->vdi_lock);
194 	*outp = (varpd_instance_handle_t *)inst;
195 	return (0);
196 }
197 
198 uint64_t
199 libvarpd_instance_id(varpd_instance_handle_t *ihp)
200 {
201 	varpd_instance_t *inst = (varpd_instance_t *)ihp;
202 	return (inst->vri_id);
203 }
204 
205 uint64_t
206 libvarpd_plugin_vnetid(varpd_provider_handle_t *vhp)
207 {
208 	varpd_instance_t *inst = (varpd_instance_t *)vhp;
209 	return (inst->vri_vnetid);
210 }
211 
212 varpd_instance_handle_t *
213 libvarpd_instance_lookup(varpd_handle_t *vhp, uint64_t id)
214 {
215 	varpd_impl_t *vip = (varpd_impl_t *)vhp;
216 	varpd_instance_t lookup, *retp;
217 
218 	lookup.vri_id = id;
219 	mutex_enter(&vip->vdi_lock);
220 	retp = avl_find(&vip->vdi_instances, &lookup, NULL);
221 	mutex_exit(&vip->vdi_lock);
222 	return ((varpd_instance_handle_t *)retp);
223 }
224 
225 /*
226  * If this function becomes external to varpd, we need to change it to return a
227  * varpd_instance_handle_t.
228  */
229 varpd_instance_t *
230 libvarpd_instance_lookup_by_dlid(varpd_impl_t *vip, datalink_id_t linkid)
231 {
232 	varpd_instance_t lookup, *retp;
233 
234 	lookup.vri_linkid = linkid;
235 	mutex_enter(&vip->vdi_lock);
236 	retp = avl_find(&vip->vdi_linstances, &lookup, NULL);
237 	mutex_exit(&vip->vdi_lock);
238 	return (retp);
239 }
240 
241 /*
242  * When an instance is being destroyed, that means we should deactivate it, as
243  * well as clean it up. That means here, the proper order is calling the plug-in
244  * stop and then the destroy function.
245  */
246 void
247 libvarpd_instance_destroy(varpd_instance_handle_t *ihp)
248 {
249 	varpd_instance_t *inst = (varpd_instance_t *)ihp;
250 	varpd_impl_t *vip = inst->vri_impl;
251 
252 	/*
253 	 * First things first, remove it from global visibility.
254 	 */
255 	mutex_enter(&vip->vdi_lock);
256 	avl_remove(&vip->vdi_instances, inst);
257 	avl_remove(&vip->vdi_linstances, inst);
258 	mutex_exit(&vip->vdi_lock);
259 
260 	mutex_enter(&inst->vri_lock);
261 
262 	/*
263 	 * We need to clean up this instance, that means remove it from
264 	 * persistence and stopping it. Then finally we'll have to clean it up
265 	 * entirely.
266 	 */
267 	if (inst->vri_flags & VARPD_INSTANCE_F_ACTIVATED) {
268 		inst->vri_flags &= ~VARPD_INSTANCE_F_ACTIVATED;
269 		libvarpd_torch_instance(vip, inst);
270 		inst->vri_plugin->vpp_ops->vpo_stop(inst->vri_private);
271 		inst->vri_plugin->vpp_ops->vpo_destroy(inst->vri_private);
272 		inst->vri_private = NULL;
273 	}
274 	mutex_exit(&inst->vri_lock);
275 
276 	/* Do the full clean up of the instance */
277 	if (mutex_destroy(&inst->vri_lock) != 0)
278 		libvarpd_panic("failed to destroy instance vri_lock");
279 	id_free(vip->vdi_idspace, inst->vri_id);
280 	umem_free(inst, sizeof (varpd_instance_t));
281 }
282 
283 int
284 libvarpd_instance_activate(varpd_instance_handle_t *ihp)
285 {
286 	int ret;
287 	varpd_instance_t *inst = (varpd_instance_t *)ihp;
288 
289 	mutex_enter(&inst->vri_lock);
290 
291 	if (inst->vri_flags & VARPD_INSTANCE_F_ACTIVATED) {
292 		ret = EEXIST;
293 		goto out;
294 	}
295 
296 	if ((ret = inst->vri_plugin->vpp_ops->vpo_start(inst->vri_private)) !=
297 	    0)
298 		goto out;
299 
300 	if ((ret = libvarpd_persist_instance(inst->vri_impl, inst)) != 0)
301 		goto out;
302 
303 	/*
304 	 * If this fails, we don't need to call stop, as the caller should end
305 	 * up calling destroy on the instance, which takes care of calling stop
306 	 * and destroy.
307 	 */
308 	if ((ret = libvarpd_overlay_associate(inst)) != 0)
309 		goto out;
310 
311 	inst->vri_flags |= VARPD_INSTANCE_F_ACTIVATED;
312 
313 out:
314 	mutex_exit(&inst->vri_lock);
315 	return (ret);
316 }
317 
318 static void
319 libvarpd_prefork(void)
320 {
321 	libvarpd_plugin_prefork();
322 }
323 
324 static void
325 libvarpd_postfork(void)
326 {
327 	libvarpd_plugin_postfork();
328 }
329 
330 #pragma init(libvarpd_init)
331 static void
332 libvarpd_init(void)
333 {
334 	libvarpd_plugin_init();
335 	if (pthread_atfork(libvarpd_prefork, libvarpd_postfork,
336 	    libvarpd_postfork) != 0)
337 		libvarpd_panic("failed to create varpd atfork: %d", errno);
338 }
339 
340 #pragma fini(libvarpd_fini)
341 static void
342 libvarpd_fini(void)
343 {
344 	libvarpd_plugin_fini();
345 }
346