xref: /illumos-gate/usr/src/uts/common/fs/dev/sdev_plugin.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
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 2019 Joyent, Inc.
14  */
15 
16 /*
17  * Dynamic directory plugin interface for sdev.
18  *
19  * The sdev plugin interfaces provides a means for a dynamic directory based on
20  * in-kernel state to be simply created. Traditionally, dynamic directories were
21  * built into sdev itself. While these legacy plugins are useful, it makes more
22  * sense for these pieces of functionality to live with the individual drivers.
23  *
24  * The plugin interface requires folks to implement three interfaces and
25  * provides a series of callbacks that can be made in the context of those
26  * interfaces to interrogate the sdev_node_t without having to leak
27  * implementation details of the sdev_node_t. These interfaces are:
28  *
29  *   o spo_validate
30  *
31  *   Given a particular node, answer the question as to whether or not this
32  *   entry is still valid. Here, plugins should use the name and the dev_t
33  *   associated with the node to verify that it matches something that still
34  *   exists.
35  *
36  *   o spo_filldir
37  *
38  *   Fill all the entries inside of a directory. Note that some of these entries
39  *   may already exist.
40  *
41  *   o spo_inactive
42  *
43  *   The given node is no longer being used. This allows the consumer to
44  *   potentially tear down anything that was being held open related to this.
45  *   Note that this only fires when the given sdev_node_t becomes a zombie.
46  *
47  * During these callbacks a consumer is not allowed to register or unregister a
48  * plugin, especially their own. They may call the sdev_ctx style functions. All
49  * callbacks fire in a context where blocking is allowed (eg. the spl is below
50  * LOCK_LEVEL).
51  *
52  * When a plugin is added, we create its directory in the global zone. By doing
53  * that, we ensure that something isn't already there and that nothing else can
54  * come along and try and create something without our knowledge. We only have
55  * to create it in the GZ and not for all other instances of sdev because an
56  * instance of sdev that isn't at /dev does not have dynamic directories, and
57  * second, any instance of sdev present in a non-global zone cannot create
58  * anything, therefore we know that by it not being in the global zone's
59  * instance of sdev that we're good to go.
60  *
61  * Lock Ordering
62  * -------------
63  *
64  * The global sdev_plugin_lock must be held before any of the individual
65  * sdev_plugin_t`sp_lock. Further, once any plugin related lock has been held,
66  * it is not legal to take any holds on any sdev_node_t or to grab the
67  * sdev_node_t`contents_lock in any way.
68  */
69 
70 #include <sys/types.h>
71 #include <sys/stat.h>
72 #include <sys/fs/sdev_impl.h>
73 #include <sys/fs/sdev_plugin.h>
74 #include <fs/fs_subr.h>
75 #include <sys/ddi.h>
76 #include <sys/sunddi.h>
77 #include <sys/ksynch.h>
78 #include <sys/sysmacros.h>
79 #include <sys/list.h>
80 #include <sys/ctype.h>
81 
82 kmutex_t sdev_plugin_lock;
83 list_t sdev_plugin_list;
84 kmem_cache_t *sdev_plugin_cache;
85 struct vnodeops *sdev_plugin_vnops;
86 
87 #define	SDEV_PLUGIN_NAMELEN	64
88 
89 typedef struct sdev_plugin {
90 	list_node_t sp_link;
91 	char sp_name[SDEV_PLUGIN_NAMELEN];	/* E */
92 	int sp_nflags;				/* E */
93 	struct vnodeops *sp_vnops;		/* E */
94 	sdev_plugin_ops_t *sp_pops;		/* E */
95 	boolean_t sp_islegacy;			/* E */
96 	int (*sp_lvtor)(sdev_node_t *);		/* E */
97 	kmutex_t sp_lock;			/* Protects everything below */
98 	kcondvar_t sp_nodecv;
99 	size_t sp_nnodes;
100 } sdev_plugin_t;
101 
102 /* ARGSUSED */
103 static int
104 sdev_plugin_cache_constructor(void *buf, void *arg, int tags)
105 {
106 	sdev_plugin_t *spp = buf;
107 	mutex_init(&spp->sp_lock, NULL, MUTEX_DRIVER, 0);
108 	cv_init(&spp->sp_nodecv, NULL, CV_DRIVER, NULL);
109 	return (0);
110 }
111 
112 /* ARGSUSED */
113 static void
114 sdev_plugin_cache_destructor(void *buf, void *arg)
115 {
116 	sdev_plugin_t *spp = buf;
117 	cv_destroy(&spp->sp_nodecv);
118 	mutex_destroy(&spp->sp_lock);
119 }
120 
121 enum vtype
122 sdev_ctx_vtype(sdev_ctx_t ctx)
123 {
124 	sdev_node_t *sdp = (sdev_node_t *)ctx;
125 
126 	ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
127 	return (sdp->sdev_vnode->v_type);
128 }
129 
130 const char *
131 sdev_ctx_path(sdev_ctx_t ctx)
132 {
133 	sdev_node_t *sdp = (sdev_node_t *)ctx;
134 
135 	ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
136 	return (sdp->sdev_path);
137 }
138 
139 const char *
140 sdev_ctx_name(sdev_ctx_t ctx)
141 {
142 	sdev_node_t *sdp = (sdev_node_t *)ctx;
143 
144 	ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
145 	return (sdp->sdev_name);
146 }
147 
148 int
149 sdev_ctx_minor(sdev_ctx_t ctx, minor_t *minorp)
150 {
151 	sdev_node_t *sdp = (sdev_node_t *)ctx;
152 
153 	ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
154 	ASSERT(minorp != NULL);
155 	if (sdp->sdev_vnode->v_type == VCHR ||
156 	    sdp->sdev_vnode->v_type == VBLK) {
157 		*minorp = getminor(sdp->sdev_vnode->v_rdev);
158 		return (0);
159 	}
160 
161 	return (ENODEV);
162 }
163 
164 /*
165  * Currently we only support psasing through a single flag -- SDEV_IS_GLOBAL.
166  */
167 sdev_ctx_flags_t
168 sdev_ctx_flags(sdev_ctx_t ctx)
169 {
170 	sdev_node_t *sdp = (sdev_node_t *)ctx;
171 
172 	ASSERT(RW_LOCK_HELD(&sdp->sdev_contents));
173 	return (sdp->sdev_flags & SDEV_GLOBAL);
174 }
175 
176 /*
177  * Use the same rules as zones for a name. isalphanum + '-', '_', and '.'.
178  */
179 static int
180 sdev_plugin_name_isvalid(const char *c, int buflen)
181 {
182 	int i;
183 
184 	for (i = 0; i < buflen; i++, c++) {
185 		if (*c == '\0')
186 			return (1);
187 
188 		if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.')
189 			return (0);
190 	}
191 	/* Never found a null terminator */
192 	return (0);
193 }
194 
195 static int
196 sdev_plugin_mknode(sdev_plugin_t *spp, sdev_node_t *sdvp, char *name,
197     vattr_t *vap)
198 {
199 	int ret;
200 	sdev_node_t *svp;
201 
202 	ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
203 	ASSERT(spp != NULL);
204 	svp = sdev_cache_lookup(sdvp, name);
205 	if (svp != NULL) {
206 		SDEV_SIMPLE_RELE(svp);
207 		return (EEXIST);
208 	}
209 
210 	ret = sdev_mknode(sdvp, name, &svp, vap, NULL, NULL, kcred,
211 	    SDEV_READY);
212 	if (ret != 0)
213 		return (ret);
214 	SDEV_SIMPLE_RELE(svp);
215 
216 	return (0);
217 }
218 
219 /*
220  * Plugin node creation callbacks
221  */
222 int
223 sdev_plugin_mkdir(sdev_ctx_t ctx, char *name)
224 {
225 	sdev_node_t *sdvp;
226 	timestruc_t now;
227 	struct vattr vap;
228 
229 	if (sdev_plugin_name_isvalid(name, SDEV_PLUGIN_NAMELEN) == 0)
230 		return (EINVAL);
231 
232 	sdvp = (sdev_node_t *)ctx;
233 	ASSERT(sdvp->sdev_private != NULL);
234 	ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
235 
236 	vap = *sdev_getdefault_attr(VDIR);
237 	gethrestime(&now);
238 	vap.va_atime = now;
239 	vap.va_mtime = now;
240 	vap.va_ctime = now;
241 
242 	return (sdev_plugin_mknode(sdvp->sdev_private, sdvp, name, &vap));
243 }
244 
245 int
246 sdev_plugin_mknod(sdev_ctx_t ctx, char *name, mode_t mode, dev_t dev)
247 {
248 	sdev_node_t *sdvp;
249 	timestruc_t now;
250 	struct vattr vap;
251 	mode_t type = mode & S_IFMT;
252 	mode_t access = mode & S_IAMB;
253 
254 	if (sdev_plugin_name_isvalid(name, SDEV_PLUGIN_NAMELEN) == 0)
255 		return (EINVAL);
256 
257 	sdvp = (sdev_node_t *)ctx;
258 	ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
259 
260 	/*
261 	 * Ensure only type and user/group/other permission bits are present.
262 	 * Do not allow setuid, setgid, etc.
263 	 */
264 	if ((mode & ~(S_IFMT | S_IAMB)) != 0)
265 		return (EINVAL);
266 
267 	/* Disallow types other than character and block devices */
268 	if (type != S_IFCHR && type != S_IFBLK)
269 		return (EINVAL);
270 
271 	/* Disallow execute bits */
272 	if ((access & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)
273 		return (EINVAL);
274 
275 	/* No bits other than 0666 in access */
276 	ASSERT((access &
277 	    ~(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)) == 0);
278 
279 	/* Default to relatively safe access bits if none specified. */
280 	if (access == 0)
281 		access = 0600;
282 
283 	ASSERT(sdvp->sdev_private != NULL);
284 
285 	vap = *sdev_getdefault_attr(type == S_IFCHR ? VCHR : VBLK);
286 	gethrestime(&now);
287 	vap.va_atime = now;
288 	vap.va_mtime = now;
289 	vap.va_ctime = now;
290 	vap.va_rdev = dev;
291 	vap.va_mode = type | access;
292 
293 	/* Despite the similar name, this is in fact a different function */
294 	return (sdev_plugin_mknode(sdvp->sdev_private, sdvp, name, &vap));
295 }
296 
297 static int
298 sdev_plugin_validate(sdev_node_t *sdp)
299 {
300 	int ret;
301 	sdev_plugin_t *spp;
302 
303 	ASSERT(sdp->sdev_private != NULL);
304 	spp = sdp->sdev_private;
305 	ASSERT(spp->sp_islegacy == B_FALSE);
306 	ASSERT(spp->sp_pops != NULL);
307 	rw_enter(&sdp->sdev_contents, RW_READER);
308 	ret = spp->sp_pops->spo_validate((uintptr_t)sdp);
309 	rw_exit(&sdp->sdev_contents);
310 	return (ret);
311 }
312 
313 static void
314 sdev_plugin_validate_dir(sdev_node_t *sdvp)
315 {
316 	int ret;
317 	sdev_node_t *svp, *next;
318 
319 	ASSERT(RW_WRITE_HELD(&sdvp->sdev_contents));
320 
321 	for (svp = SDEV_FIRST_ENTRY(sdvp); svp != NULL; svp = next) {
322 
323 		next = SDEV_NEXT_ENTRY(sdvp, svp);
324 		ASSERT(svp->sdev_state != SDEV_ZOMBIE);
325 		/* skip nodes that aren't ready */
326 		if (svp->sdev_state == SDEV_INIT)
327 			continue;
328 
329 		switch (sdev_plugin_validate(svp)) {
330 		case SDEV_VTOR_VALID:
331 		case SDEV_VTOR_SKIP:
332 			continue;
333 		case SDEV_VTOR_INVALID:
334 		case SDEV_VTOR_STALE:
335 			break;
336 		}
337 
338 		SDEV_HOLD(svp);
339 
340 		/*
341 		 * Clean out everything underneath this node before we
342 		 * remove it.
343 		 */
344 		if (svp->sdev_vnode->v_type == VDIR) {
345 			ret = sdev_cleandir(svp, NULL, 0);
346 			ASSERT(ret == 0);
347 		}
348 		/* remove the cache node */
349 		(void) sdev_cache_update(sdvp, &svp, svp->sdev_name,
350 		    SDEV_CACHE_DELETE);
351 		SDEV_RELE(svp);
352 	}
353 }
354 
355 /* ARGSUSED */
356 static int
357 sdev_plugin_vop_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
358     int *eofp, caller_context_t *ct_unused, int flags_unused)
359 {
360 	int ret;
361 	sdev_node_t *sdvp = VTOSDEV(dvp);
362 	sdev_plugin_t *spp;
363 
364 	ASSERT(RW_READ_HELD(&sdvp->sdev_contents));
365 
366 	/* Sanity check we're not a zombie before we do anyting else */
367 	if (sdvp->sdev_state == SDEV_ZOMBIE)
368 		return (ENOENT);
369 
370 	spp = sdvp->sdev_private;
371 	ASSERT(spp != NULL);
372 	ASSERT(spp->sp_islegacy == B_FALSE);
373 	ASSERT(spp->sp_pops != NULL);
374 
375 	if (crgetzoneid(cred) == GLOBAL_ZONEID && !SDEV_IS_GLOBAL(sdvp))
376 		return (EPERM);
377 
378 	if (uiop->uio_offset == 0) {
379 		/*
380 		 * We upgrade to a write lock and grab the plugin's lock along
381 		 * the way. We're almost certainly going to get creation
382 		 * callbacks, so this is the only safe way to go.
383 		 */
384 		if (rw_tryupgrade(&sdvp->sdev_contents) == 0) {
385 			rw_exit(&sdvp->sdev_contents);
386 			rw_enter(&sdvp->sdev_contents, RW_WRITER);
387 			if (sdvp->sdev_state == SDEV_ZOMBIE) {
388 				rw_downgrade(&sdvp->sdev_contents);
389 				return (ENOENT);
390 			}
391 		}
392 
393 		sdev_plugin_validate_dir(sdvp);
394 		ret = spp->sp_pops->spo_filldir((uintptr_t)sdvp);
395 		rw_downgrade(&sdvp->sdev_contents);
396 		if (ret != 0)
397 			return (ret);
398 	}
399 
400 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
401 }
402 
403 /*
404  * If we don't have a callback function that returns a failure, then sdev will
405  * try to create a node for us which violates all of our basic assertions. To
406  * work around that we create our own callback for devname_lookup_func which
407  * always returns ENOENT as at this point either it was created with the filldir
408  * callback or it was not.
409  */
410 /*ARGSUSED*/
411 static int
412 sdev_plugin_vop_lookup_cb(sdev_node_t *ddv, char *nm, void **arg, cred_t *cred,
413     void *unused, char *unused2)
414 {
415 	return (ENOENT);
416 }
417 
418 /* ARGSUSED */
419 static int
420 sdev_plugin_vop_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
421     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
422     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
423 {
424 	int ret;
425 	sdev_node_t *sdvp;
426 	sdev_plugin_t *spp;
427 
428 	/* execute access is required to search the directory */
429 	if ((ret = VOP_ACCESS(dvp, VEXEC, 0, cred, ct)) != 0)
430 		return (ret);
431 
432 	sdvp = VTOSDEV(dvp);
433 	spp = sdvp->sdev_private;
434 	ASSERT(spp != NULL);
435 	ASSERT(spp->sp_islegacy == B_FALSE);
436 	ASSERT(spp->sp_pops != NULL);
437 
438 	if (crgetzoneid(cred) == GLOBAL_ZONEID && !SDEV_IS_GLOBAL(sdvp))
439 		return (EPERM);
440 
441 	/*
442 	 * Go straight for the write lock.
443 	 */
444 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
445 	if (sdvp->sdev_state == SDEV_ZOMBIE) {
446 		rw_exit(&sdvp->sdev_contents);
447 		return (ENOENT);
448 	}
449 	sdev_plugin_validate_dir(sdvp);
450 	ret = spp->sp_pops->spo_filldir((uintptr_t)sdvp);
451 	rw_exit(&sdvp->sdev_contents);
452 	if (ret != 0)
453 		return (ret);
454 
455 	return (devname_lookup_func(sdvp, nm, vpp, cred,
456 	    sdev_plugin_vop_lookup_cb, SDEV_VATTR));
457 }
458 
459 /*
460  * sdev is not a good citizen. We get inactive callbacks whenever a vnode goes
461  * to zero, but isn't necessairily a zombie yet. As such, to make things easier
462  * for users, we only fire the inactive callback when the node becomes a zombie
463  * and thus will be torn down here.
464  */
465 static void
466 sdev_plugin_vop_inactive_cb(struct vnode *dvp)
467 {
468 	sdev_node_t *sdp = VTOSDEV(dvp);
469 	sdev_plugin_t *spp = sdp->sdev_private;
470 
471 	rw_enter(&sdp->sdev_contents, RW_READER);
472 	if (sdp->sdev_state != SDEV_ZOMBIE) {
473 		rw_exit(&sdp->sdev_contents);
474 		return;
475 	}
476 	spp->sp_pops->spo_inactive((uintptr_t)sdp);
477 	mutex_enter(&spp->sp_lock);
478 	VERIFY(spp->sp_nnodes > 0);
479 	spp->sp_nnodes--;
480 	cv_signal(&spp->sp_nodecv);
481 	mutex_exit(&spp->sp_lock);
482 	rw_exit(&sdp->sdev_contents);
483 }
484 
485 /*ARGSUSED*/
486 static void
487 sdev_plugin_vop_inactive(struct vnode *dvp, struct cred *cred,
488     caller_context_t *ct)
489 {
490 	sdev_node_t *sdp = VTOSDEV(dvp);
491 	sdev_plugin_t *spp = sdp->sdev_private;
492 	ASSERT(sdp->sdev_private != NULL);
493 	ASSERT(spp->sp_islegacy == B_FALSE);
494 	devname_inactive_func(dvp, cred, sdev_plugin_vop_inactive_cb);
495 }
496 
497 const fs_operation_def_t sdev_plugin_vnodeops_tbl[] = {
498 	VOPNAME_READDIR,	{ .vop_readdir = sdev_plugin_vop_readdir },
499 	VOPNAME_LOOKUP,		{ .vop_lookup = sdev_plugin_vop_lookup },
500 	VOPNAME_INACTIVE,	{ .vop_inactive = sdev_plugin_vop_inactive },
501 	VOPNAME_CREATE,		{ .error = fs_nosys },
502 	VOPNAME_REMOVE,		{ .error = fs_nosys },
503 	VOPNAME_MKDIR,		{ .error = fs_nosys },
504 	VOPNAME_RMDIR,		{ .error = fs_nosys },
505 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
506 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
507 	NULL,			NULL
508 };
509 
510 /*
511  * construct a new template with overrides from vtab
512  */
513 static fs_operation_def_t *
514 sdev_merge_vtab(const fs_operation_def_t tab[])
515 {
516 	fs_operation_def_t *new;
517 	const fs_operation_def_t *tab_entry;
518 
519 	/* make a copy of standard vnode ops table */
520 	new = kmem_alloc(sdev_vnodeops_tbl_size, KM_SLEEP);
521 	bcopy((void *)sdev_vnodeops_tbl, new, sdev_vnodeops_tbl_size);
522 
523 	/* replace the overrides from tab */
524 	for (tab_entry = tab; tab_entry->name != NULL; tab_entry++) {
525 		fs_operation_def_t *std_entry = new;
526 		while (std_entry->name) {
527 			if (strcmp(tab_entry->name, std_entry->name) == 0) {
528 				std_entry->func = tab_entry->func;
529 				break;
530 			}
531 			std_entry++;
532 		}
533 	}
534 
535 	return (new);
536 }
537 
538 /* free memory allocated by sdev_merge_vtab */
539 static void
540 sdev_free_vtab(fs_operation_def_t *new)
541 {
542 	kmem_free(new, sdev_vnodeops_tbl_size);
543 }
544 
545 /*
546  * Register a new plugin.
547  */
548 sdev_plugin_hdl_t
549 sdev_plugin_register(const char *name, sdev_plugin_ops_t *ops, int *errp)
550 {
551 	char buf[sizeof ("dev")] = "";
552 	struct pathname pn = { 0 };
553 	sdev_plugin_t *spp, *iter;
554 	vnode_t *vp, *nvp;
555 	sdev_node_t *sdp, *slp;
556 	timestruc_t now;
557 	struct vattr vap;
558 	int ret, err;
559 
560 	/*
561 	 * Some consumers don't care about why they failed. To keep the code
562 	 * simple, we'll just pretend they gave us something.
563 	 */
564 	if (errp == NULL)
565 		errp = &err;
566 
567 	if (sdev_plugin_name_isvalid(name, SDEV_PLUGIN_NAMELEN) == 0) {
568 		*errp = EINVAL;
569 		return ((sdev_plugin_hdl_t)NULL);
570 	}
571 
572 	if (ops->spo_version != 1) {
573 		*errp = EINVAL;
574 		return ((sdev_plugin_hdl_t)NULL);
575 	}
576 
577 	if (ops->spo_validate == NULL || ops->spo_filldir == NULL ||
578 	    ops->spo_inactive == NULL) {
579 		*errp = EINVAL;
580 		return ((sdev_plugin_hdl_t)NULL);
581 	}
582 
583 	if ((ops->spo_flags & ~SDEV_PLUGIN_FLAGS_MASK) != 0) {
584 		*errp = EINVAL;
585 		return ((sdev_plugin_hdl_t)NULL);
586 	}
587 
588 	spp = kmem_cache_alloc(sdev_plugin_cache, KM_SLEEP);
589 	(void) strlcpy(spp->sp_name, name, SDEV_PLUGIN_NAMELEN);
590 
591 	spp->sp_pops = ops;
592 	spp->sp_nflags = SDEV_DYNAMIC | SDEV_VTOR;
593 	if (ops->spo_flags & SDEV_PLUGIN_NO_NCACHE)
594 		spp->sp_nflags |= SDEV_NO_NCACHE;
595 	if (ops->spo_flags & SDEV_PLUGIN_SUBDIR)
596 		spp->sp_nflags |= SDEV_SUBDIR;
597 	spp->sp_vnops = sdev_plugin_vnops;
598 	spp->sp_islegacy = B_FALSE;
599 	spp->sp_lvtor = NULL;
600 	spp->sp_nnodes = 0;
601 
602 	/*
603 	 * Make sure our /dev entry is unique and install it.  We also need to
604 	 * go through and grab the sdev root node as we cannot grab any sdev
605 	 * node locks once we've grabbed the sdev_plugin_lock. We effectively
606 	 * assert that if a directory is not present in the GZ's /dev, then it
607 	 * doesn't exist in any of the local zones.
608 	 *
609 	 * Note that we may be in NGZ context: during a prof_filldir(".../dev/")
610 	 * enumeration, for example. So we have to dig as deep as lookuppnvp()
611 	 * to make sure we really get to the global /dev (i.e.  escape both
612 	 * CRED() and ->u_rdir).
613 	 */
614 	(void) pn_get_buf("dev", UIO_SYSSPACE, &pn, buf, sizeof (buf));
615 	VN_HOLD(rootdir);
616 	ret = lookuppnvp(&pn, NULL, NO_FOLLOW, NULLVPP,
617 	    &vp, rootdir, rootdir, kcred);
618 
619 	if (ret != 0) {
620 		*errp = ret;
621 		kmem_cache_free(sdev_plugin_cache, spp);
622 		return ((sdev_plugin_hdl_t)NULL);
623 	}
624 	/* Make sure we have the real vnode */
625 	if (VOP_REALVP(vp, &nvp, NULL) == 0) {
626 		VN_HOLD(nvp);
627 		VN_RELE(vp);
628 		vp = nvp;
629 		nvp = NULL;
630 	}
631 	VERIFY(vp->v_op == sdev_vnodeops);
632 	sdp = VTOSDEV(vp);
633 	rw_enter(&sdp->sdev_contents, RW_WRITER);
634 	slp = sdev_cache_lookup(sdp, spp->sp_name);
635 	if (slp != NULL) {
636 		SDEV_RELE(slp);
637 		rw_exit(&sdp->sdev_contents);
638 		VN_RELE(vp);
639 		*errp = EEXIST;
640 		kmem_cache_free(sdev_plugin_cache, spp);
641 		return ((sdev_plugin_hdl_t)NULL);
642 	}
643 
644 	mutex_enter(&sdev_plugin_lock);
645 	for (iter = list_head(&sdev_plugin_list); iter != NULL;
646 	    iter = list_next(&sdev_plugin_list, iter)) {
647 		if (strcmp(spp->sp_name, iter->sp_name) == 0) {
648 			mutex_exit(&sdev_plugin_lock);
649 			rw_exit(&sdp->sdev_contents);
650 			VN_RELE(vp);
651 			*errp = EEXIST;
652 			kmem_cache_free(sdev_plugin_cache, spp);
653 			return ((sdev_plugin_hdl_t)NULL);
654 		}
655 	}
656 
657 	list_insert_tail(&sdev_plugin_list, spp);
658 	mutex_exit(&sdev_plugin_lock);
659 
660 	/*
661 	 * Now go ahead and create the top level directory for the global zone.
662 	 */
663 	vap = *sdev_getdefault_attr(VDIR);
664 	gethrestime(&now);
665 	vap.va_atime = now;
666 	vap.va_mtime = now;
667 	vap.va_ctime = now;
668 
669 	(void) sdev_plugin_mknode(spp, sdp, spp->sp_name, &vap);
670 
671 	rw_exit(&sdp->sdev_contents);
672 	VN_RELE(vp);
673 
674 	*errp = 0;
675 
676 	return ((sdev_plugin_hdl_t)spp);
677 }
678 
679 static void
680 sdev_plugin_unregister_cb(sdev_node_t *rdp, void *arg)
681 {
682 	sdev_plugin_t *spp = arg;
683 	sdev_node_t *sdp;
684 
685 	rw_enter(&rdp->sdev_contents, RW_WRITER);
686 	sdp = sdev_cache_lookup(rdp, spp->sp_name);
687 	/* If it doesn't exist, we're done here */
688 	if (sdp == NULL) {
689 		rw_exit(&rdp->sdev_contents);
690 		return;
691 	}
692 
693 	/*
694 	 * We first delete the directory before recursively marking everything
695 	 * else stale. This ordering should ensure that we don't accidentally
696 	 * miss anything.
697 	 */
698 	sdev_cache_update(rdp, &sdp, spp->sp_name, SDEV_CACHE_DELETE);
699 	sdev_stale(sdp);
700 	SDEV_RELE(sdp);
701 	rw_exit(&rdp->sdev_contents);
702 }
703 
704 int sdev_plugin_unregister_allowed;
705 
706 /*
707  * Remove a plugin. This will block until everything has become a zombie, thus
708  * guaranteeing the caller that nothing will call into them again once this call
709  * returns. While the call is ongoing, it could be called into. Note that while
710  * this is ongoing, it will block other mounts.
711  *
712  * NB: this is not safe when used from detach() context - we will be DEVI_BUSY,
713  * and other sdev threads may be waiting for this.  Only use the over-ride if
714  * willing to risk it.
715  */
716 int
717 sdev_plugin_unregister(sdev_plugin_hdl_t hdl)
718 {
719 	sdev_plugin_t *spp = (sdev_plugin_t *)hdl;
720 	if (spp->sp_islegacy)
721 		return (EINVAL);
722 
723 	if (!sdev_plugin_unregister_allowed)
724 		return (EBUSY);
725 
726 	mutex_enter(&sdev_plugin_lock);
727 	list_remove(&sdev_plugin_list, spp);
728 	mutex_exit(&sdev_plugin_lock);
729 
730 	sdev_mnt_walk(sdev_plugin_unregister_cb, spp);
731 	mutex_enter(&spp->sp_lock);
732 	while (spp->sp_nnodes > 0)
733 		cv_wait(&spp->sp_nodecv, &spp->sp_lock);
734 	mutex_exit(&spp->sp_lock);
735 	kmem_cache_free(sdev_plugin_cache, spp);
736 	return (0);
737 }
738 
739 /*
740  * Register an old sdev style plugin to deal with what used to be in the vtab.
741  */
742 static int
743 sdev_plugin_register_legacy(struct sdev_vop_table *vtp)
744 {
745 	sdev_plugin_t *spp;
746 
747 	spp = kmem_cache_alloc(sdev_plugin_cache, KM_SLEEP);
748 	(void) strlcpy(spp->sp_name, vtp->vt_name, SDEV_PLUGIN_NAMELEN);
749 	spp->sp_islegacy = B_TRUE;
750 	spp->sp_pops = NULL;
751 	spp->sp_nflags = vtp->vt_flags;
752 	spp->sp_lvtor = vtp->vt_vtor;
753 	spp->sp_nnodes = 0;
754 
755 	if (vtp->vt_service != NULL) {
756 		fs_operation_def_t *templ;
757 		templ = sdev_merge_vtab(vtp->vt_service);
758 		if (vn_make_ops(vtp->vt_name,
759 		    (const fs_operation_def_t *)templ,
760 		    &spp->sp_vnops) != 0) {
761 			cmn_err(CE_WARN, "%s: malformed vnode ops\n",
762 			    vtp->vt_name);
763 			sdev_free_vtab(templ);
764 			kmem_cache_free(sdev_plugin_cache, spp);
765 			return (1);
766 		}
767 
768 		if (vtp->vt_global_vops) {
769 			*(vtp->vt_global_vops) = spp->sp_vnops;
770 		}
771 
772 		sdev_free_vtab(templ);
773 	} else {
774 		spp->sp_vnops = sdev_vnodeops;
775 	}
776 
777 	/*
778 	 * No need to check for EEXIST here. These are loaded as a part of the
779 	 * sdev's initialization function. Further, we don't have to create them
780 	 * as that's taken care of in sdev's mount for the GZ.
781 	 */
782 	mutex_enter(&sdev_plugin_lock);
783 	list_insert_tail(&sdev_plugin_list, spp);
784 	mutex_exit(&sdev_plugin_lock);
785 
786 	return (0);
787 }
788 
789 /*
790  * We need to match off of the sdev_path, not the sdev_name. We are only allowed
791  * to exist directly under /dev.
792  */
793 static sdev_plugin_t *
794 sdev_match(sdev_node_t *dv)
795 {
796 	int vlen;
797 	const char *path;
798 	sdev_plugin_t *spp;
799 
800 	if (strlen(dv->sdev_path) <= 5)
801 		return (NULL);
802 
803 	if (strncmp(dv->sdev_path, "/dev/", 5) != 0)
804 		return (NULL);
805 	path = dv->sdev_path + 5;
806 
807 	mutex_enter(&sdev_plugin_lock);
808 
809 	for (spp = list_head(&sdev_plugin_list); spp != NULL;
810 	    spp = list_next(&sdev_plugin_list, spp)) {
811 		if (strcmp(spp->sp_name, path) == 0) {
812 			mutex_exit(&sdev_plugin_lock);
813 			return (spp);
814 		}
815 
816 		if (spp->sp_nflags & SDEV_SUBDIR) {
817 			vlen = strlen(spp->sp_name);
818 			if ((strncmp(spp->sp_name, path,
819 			    vlen - 1) == 0) && path[vlen] == '/') {
820 				mutex_exit(&sdev_plugin_lock);
821 				return (spp);
822 			}
823 
824 		}
825 	}
826 
827 	mutex_exit(&sdev_plugin_lock);
828 	return (NULL);
829 }
830 
831 void
832 sdev_set_no_negcache(sdev_node_t *dv)
833 {
834 	char *path;
835 	sdev_plugin_t *spp;
836 
837 	ASSERT(dv->sdev_path);
838 	path = dv->sdev_path + strlen("/dev/");
839 
840 	mutex_enter(&sdev_plugin_lock);
841 	for (spp = list_head(&sdev_plugin_list); spp != NULL;
842 	    spp = list_next(&sdev_plugin_list, spp)) {
843 		if (strcmp(spp->sp_name, path) == 0) {
844 			if (spp->sp_nflags & SDEV_NO_NCACHE)
845 				dv->sdev_flags |= SDEV_NO_NCACHE;
846 			break;
847 		}
848 	}
849 	mutex_exit(&sdev_plugin_lock);
850 }
851 
852 struct vnodeops *
853 sdev_get_vop(sdev_node_t *dv)
854 {
855 	char *path;
856 	sdev_plugin_t *spp;
857 
858 	path = dv->sdev_path;
859 	ASSERT(path);
860 
861 	/* gets the relative path to /dev/ */
862 	path += 5;
863 
864 	if ((spp = sdev_match(dv)) != NULL) {
865 		dv->sdev_flags |= spp->sp_nflags;
866 		if (SDEV_IS_PERSIST(dv->sdev_dotdot) &&
867 		    (SDEV_IS_PERSIST(dv) || !SDEV_IS_DYNAMIC(dv)))
868 			dv->sdev_flags |= SDEV_PERSIST;
869 		return (spp->sp_vnops);
870 	}
871 
872 	/* child inherits the persistence of the parent */
873 	if (SDEV_IS_PERSIST(dv->sdev_dotdot))
874 		dv->sdev_flags |= SDEV_PERSIST;
875 	return (sdev_vnodeops);
876 }
877 
878 void *
879 sdev_get_vtor(sdev_node_t *dv)
880 {
881 	sdev_plugin_t *spp;
882 
883 	if (dv->sdev_private == NULL) {
884 		spp = sdev_match(dv);
885 		if (spp == NULL)
886 			return (NULL);
887 	} else {
888 		spp = dv->sdev_private;
889 	}
890 
891 	if (spp->sp_islegacy)
892 		return ((void *)spp->sp_lvtor);
893 	else
894 		return ((void *)sdev_plugin_validate);
895 }
896 
897 void
898 sdev_plugin_nodeready(sdev_node_t *sdp)
899 {
900 	sdev_plugin_t *spp;
901 
902 	ASSERT(RW_WRITE_HELD(&sdp->sdev_contents));
903 	ASSERT(sdp->sdev_private == NULL);
904 
905 	spp = sdev_match(sdp);
906 	if (spp == NULL)
907 		return;
908 	if (spp->sp_islegacy)
909 		return;
910 	sdp->sdev_private = spp;
911 	mutex_enter(&spp->sp_lock);
912 	spp->sp_nnodes++;
913 	mutex_exit(&spp->sp_lock);
914 }
915 
916 int
917 sdev_plugin_init(void)
918 {
919 	sdev_vop_table_t *vtp;
920 	fs_operation_def_t *templ;
921 
922 	sdev_plugin_cache = kmem_cache_create("sdev_plugin",
923 	    sizeof (sdev_plugin_t), 0, sdev_plugin_cache_constructor,
924 	    sdev_plugin_cache_destructor, NULL, NULL, NULL, 0);
925 	if (sdev_plugin_cache == NULL)
926 		return (1);
927 	mutex_init(&sdev_plugin_lock, NULL, MUTEX_DRIVER, NULL);
928 	list_create(&sdev_plugin_list, sizeof (sdev_plugin_t),
929 	    offsetof(sdev_plugin_t, sp_link));
930 
931 	/*
932 	 * Register all of the legacy vnops
933 	 */
934 	for (vtp = &vtab[0]; vtp->vt_name != NULL; vtp++)
935 		if (sdev_plugin_register_legacy(vtp) != 0)
936 			return (1);
937 
938 	templ = sdev_merge_vtab(sdev_plugin_vnodeops_tbl);
939 	if (vn_make_ops("sdev_plugin",
940 	    (const fs_operation_def_t *)templ,
941 	    &sdev_plugin_vnops) != 0) {
942 		sdev_free_vtab(templ);
943 		return (1);
944 	}
945 
946 	sdev_free_vtab(templ);
947 	return (0);
948 }
949