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