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