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