xref: /onnv-gate/usr/src/uts/common/fs/dev/sdev_netops.c (revision 5895:f251acdd9bdc)
1*5895Syz147064 /*
2*5895Syz147064  * CDDL HEADER START
3*5895Syz147064  *
4*5895Syz147064  * The contents of this file are subject to the terms of the
5*5895Syz147064  * Common Development and Distribution License (the "License").
6*5895Syz147064  * You may not use this file except in compliance with the License.
7*5895Syz147064  *
8*5895Syz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*5895Syz147064  * or http://www.opensolaris.org/os/licensing.
10*5895Syz147064  * See the License for the specific language governing permissions
11*5895Syz147064  * and limitations under the License.
12*5895Syz147064  *
13*5895Syz147064  * When distributing Covered Code, include this CDDL HEADER in each
14*5895Syz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*5895Syz147064  * If applicable, add the following below this CDDL HEADER, with the
16*5895Syz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17*5895Syz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18*5895Syz147064  *
19*5895Syz147064  * CDDL HEADER END
20*5895Syz147064  */
21*5895Syz147064 /*
22*5895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23*5895Syz147064  * Use is subject to license terms.
24*5895Syz147064  */
25*5895Syz147064 
26*5895Syz147064 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27*5895Syz147064 
28*5895Syz147064 /*
29*5895Syz147064  * vnode ops for the /dev/net directory
30*5895Syz147064  *
31*5895Syz147064  *	The lookup is based on the internal vanity naming node table.  We also
32*5895Syz147064  *	override readdir in order to delete net nodes no longer	in-use.
33*5895Syz147064  */
34*5895Syz147064 
35*5895Syz147064 #include <sys/types.h>
36*5895Syz147064 #include <sys/param.h>
37*5895Syz147064 #include <sys/sysmacros.h>
38*5895Syz147064 #include <sys/sunndi.h>
39*5895Syz147064 #include <fs/fs_subr.h>
40*5895Syz147064 #include <sys/fs/dv_node.h>
41*5895Syz147064 #include <sys/fs/sdev_impl.h>
42*5895Syz147064 #include <sys/policy.h>
43*5895Syz147064 #include <sys/zone.h>
44*5895Syz147064 #include <sys/dls.h>
45*5895Syz147064 
46*5895Syz147064 struct vnodeops		*devnet_vnodeops;
47*5895Syz147064 
48*5895Syz147064 /*
49*5895Syz147064  * Called by zone_walk_datalink() to see if the given link name belongs to the
50*5895Syz147064  * given zone.  Returns 0 to continue the walk, -1 if the link name is found.
51*5895Syz147064  */
52*5895Syz147064 static int
53*5895Syz147064 devnet_validate_name(const char *link, void *arg)
54*5895Syz147064 {
55*5895Syz147064 	return ((strcmp(link, arg) == 0) ? -1 : 0);
56*5895Syz147064 }
57*5895Syz147064 
58*5895Syz147064 /*
59*5895Syz147064  * Check if a net sdev_node is still valid - i.e. it represents a current
60*5895Syz147064  * network link.
61*5895Syz147064  * This serves two purposes
62*5895Syz147064  *	- only valid net nodes are returned during lookup() and readdir().
63*5895Syz147064  *	- since net sdev_nodes are not actively destroyed when a network link
64*5895Syz147064  *	  goes away, we use the validator to do deferred cleanup i.e. when such
65*5895Syz147064  *	  nodes are encountered during subsequent lookup() and readdir().
66*5895Syz147064  */
67*5895Syz147064 int
68*5895Syz147064 devnet_validate(struct sdev_node *dv)
69*5895Syz147064 {
70*5895Syz147064 	char *nm = dv->sdev_name;
71*5895Syz147064 	datalink_id_t linkid;
72*5895Syz147064 
73*5895Syz147064 	ASSERT(!(dv->sdev_flags & SDEV_STALE));
74*5895Syz147064 	ASSERT(dv->sdev_state == SDEV_READY);
75*5895Syz147064 
76*5895Syz147064 	if (SDEV_IS_GLOBAL(dv)) {
77*5895Syz147064 		return ((dls_mgmt_get_linkid(nm, &linkid) != 0) ?
78*5895Syz147064 		    SDEV_VTOR_INVALID : SDEV_VTOR_VALID);
79*5895Syz147064 	} else {
80*5895Syz147064 		return ((zone_datalink_walk(getzoneid(), devnet_validate_name,
81*5895Syz147064 		    nm) == -1) ? SDEV_VTOR_VALID : SDEV_VTOR_INVALID);
82*5895Syz147064 	}
83*5895Syz147064 }
84*5895Syz147064 
85*5895Syz147064 /*
86*5895Syz147064  * This callback is invoked from devname_lookup_func() to create
87*5895Syz147064  * a net entry when the node is not found in the cache.
88*5895Syz147064  */
89*5895Syz147064 static int
90*5895Syz147064 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
91*5895Syz147064 {
92*5895Syz147064 	timestruc_t now;
93*5895Syz147064 	dev_t dev;
94*5895Syz147064 	int error;
95*5895Syz147064 
96*5895Syz147064 	if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) {
97*5895Syz147064 		sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
98*5895Syz147064 		    "network node: %s\n", nm));
99*5895Syz147064 		return (error);
100*5895Syz147064 	}
101*5895Syz147064 
102*5895Syz147064 	/*
103*5895Syz147064 	 * This is a valid network device (at least at this point in time).
104*5895Syz147064 	 * Create the node by setting the attribute; the rest is taken care
105*5895Syz147064 	 * of by devname_lookup_func().
106*5895Syz147064 	 */
107*5895Syz147064 	*vap = sdev_vattr_chr;
108*5895Syz147064 	vap->va_mode |= 0666;
109*5895Syz147064 	vap->va_rdev = dev;
110*5895Syz147064 
111*5895Syz147064 	gethrestime(&now);
112*5895Syz147064 	vap->va_atime = now;
113*5895Syz147064 	vap->va_mtime = now;
114*5895Syz147064 	vap->va_ctime = now;
115*5895Syz147064 	return (0);
116*5895Syz147064 }
117*5895Syz147064 
118*5895Syz147064 /*
119*5895Syz147064  * Lookup for /dev/net directory
120*5895Syz147064  *	If the entry does not exist, the devnet_create_rvp() callback
121*5895Syz147064  *	is invoked to create it.  Nodes do not persist across reboot.
122*5895Syz147064  */
123*5895Syz147064 /*ARGSUSED3*/
124*5895Syz147064 static int
125*5895Syz147064 devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
126*5895Syz147064     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
127*5895Syz147064     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
128*5895Syz147064 {
129*5895Syz147064 	struct sdev_node *ddv = VTOSDEV(dvp);
130*5895Syz147064 	struct sdev_node *dv = NULL;
131*5895Syz147064 	dls_dl_handle_t ddh = NULL;
132*5895Syz147064 	struct vattr vattr;
133*5895Syz147064 	int nmlen;
134*5895Syz147064 	int error = ENOENT;
135*5895Syz147064 
136*5895Syz147064 	if (SDEVTOV(ddv)->v_type != VDIR)
137*5895Syz147064 		return (ENOTDIR);
138*5895Syz147064 
139*5895Syz147064 	/*
140*5895Syz147064 	 * Empty name or ., return node itself.
141*5895Syz147064 	 */
142*5895Syz147064 	nmlen = strlen(nm);
143*5895Syz147064 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
144*5895Syz147064 		*vpp = SDEVTOV(ddv);
145*5895Syz147064 		VN_HOLD(*vpp);
146*5895Syz147064 		return (0);
147*5895Syz147064 	}
148*5895Syz147064 
149*5895Syz147064 	/*
150*5895Syz147064 	 * .., return the parent directory
151*5895Syz147064 	 */
152*5895Syz147064 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
153*5895Syz147064 		*vpp = SDEVTOV(ddv->sdev_dotdot);
154*5895Syz147064 		VN_HOLD(*vpp);
155*5895Syz147064 		return (0);
156*5895Syz147064 	}
157*5895Syz147064 
158*5895Syz147064 	rw_enter(&ddv->sdev_contents, RW_WRITER);
159*5895Syz147064 
160*5895Syz147064 	/*
161*5895Syz147064 	 * directory cache lookup:
162*5895Syz147064 	 */
163*5895Syz147064 	if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) {
164*5895Syz147064 		if (dv->sdev_state == SDEV_READY) {
165*5895Syz147064 			if (!(dv->sdev_flags & SDEV_ATTR_INVALID))
166*5895Syz147064 				goto found;
167*5895Syz147064 		} else {
168*5895Syz147064 			ASSERT(dv->sdev_state == SDEV_ZOMBIE);
169*5895Syz147064 			goto failed;
170*5895Syz147064 		}
171*5895Syz147064 	}
172*5895Syz147064 
173*5895Syz147064 	/*
174*5895Syz147064 	 * ZOMBIED parent does not allow new node creation, bail out early.
175*5895Syz147064 	 */
176*5895Syz147064 	if (ddv->sdev_state == SDEV_ZOMBIE)
177*5895Syz147064 		goto failed;
178*5895Syz147064 
179*5895Syz147064 	error = devnet_create_rvp(nm, &vattr, &ddh);
180*5895Syz147064 	if (error != 0)
181*5895Syz147064 		goto failed;
182*5895Syz147064 
183*5895Syz147064 	error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY);
184*5895Syz147064 	if (error != 0) {
185*5895Syz147064 		ASSERT(dv == NULL);
186*5895Syz147064 		dls_devnet_close(ddh);
187*5895Syz147064 		goto failed;
188*5895Syz147064 	}
189*5895Syz147064 
190*5895Syz147064 	ASSERT(dv != NULL);
191*5895Syz147064 
192*5895Syz147064 	rw_enter(&dv->sdev_contents, RW_WRITER);
193*5895Syz147064 	if (dv->sdev_flags & SDEV_ATTR_INVALID) {
194*5895Syz147064 		/*
195*5895Syz147064 		 * SDEV_ATTR_INVALID means that this device has been
196*5895Syz147064 		 * detached, and its dev_t might've been changed too.
197*5895Syz147064 		 * Therefore, sdev_node's 'vattr' needs to be updated.
198*5895Syz147064 		 */
199*5895Syz147064 		SDEVTOV(dv)->v_rdev = vattr.va_rdev;
200*5895Syz147064 		ASSERT(dv->sdev_attr != NULL);
201*5895Syz147064 		dv->sdev_attr->va_rdev = vattr.va_rdev;
202*5895Syz147064 		dv->sdev_flags &= ~SDEV_ATTR_INVALID;
203*5895Syz147064 	}
204*5895Syz147064 	ASSERT(dv->sdev_private == NULL);
205*5895Syz147064 	dv->sdev_private = ddh;
206*5895Syz147064 	rw_exit(&dv->sdev_contents);
207*5895Syz147064 
208*5895Syz147064 found:
209*5895Syz147064 	ASSERT(SDEV_HELD(dv));
210*5895Syz147064 	rw_exit(&ddv->sdev_contents);
211*5895Syz147064 	return (sdev_to_vp(dv, vpp));
212*5895Syz147064 
213*5895Syz147064 failed:
214*5895Syz147064 	rw_exit(&ddv->sdev_contents);
215*5895Syz147064 
216*5895Syz147064 	if (dv != NULL)
217*5895Syz147064 		SDEV_RELE(dv);
218*5895Syz147064 
219*5895Syz147064 	*vpp = NULL;
220*5895Syz147064 	return (error);
221*5895Syz147064 }
222*5895Syz147064 
223*5895Syz147064 static int
224*5895Syz147064 devnet_filldir_datalink(const char *link, void *arg)
225*5895Syz147064 {
226*5895Syz147064 	struct sdev_node *ddv = arg;
227*5895Syz147064 	struct vattr vattr;
228*5895Syz147064 	struct sdev_node *dv;
229*5895Syz147064 	dls_dl_handle_t ddh = NULL;
230*5895Syz147064 
231*5895Syz147064 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
232*5895Syz147064 	if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
233*5895Syz147064 		goto found;
234*5895Syz147064 
235*5895Syz147064 	if (devnet_create_rvp(link, &vattr, &ddh) != 0)
236*5895Syz147064 		return (0);
237*5895Syz147064 
238*5895Syz147064 	ASSERT(ddh != NULL);
239*5895Syz147064 	dls_devnet_close(ddh);
240*5895Syz147064 
241*5895Syz147064 	if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred,
242*5895Syz147064 	    SDEV_READY) != 0) {
243*5895Syz147064 		return (0);
244*5895Syz147064 	}
245*5895Syz147064 
246*5895Syz147064 	/*
247*5895Syz147064 	 * As there is no reference holding the network device, it could be
248*5895Syz147064 	 * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated
249*5895Syz147064 	 * later.
250*5895Syz147064 	 */
251*5895Syz147064 	rw_enter(&dv->sdev_contents, RW_WRITER);
252*5895Syz147064 	dv->sdev_flags |= SDEV_ATTR_INVALID;
253*5895Syz147064 	rw_exit(&dv->sdev_contents);
254*5895Syz147064 
255*5895Syz147064 found:
256*5895Syz147064 	SDEV_SIMPLE_RELE(dv);
257*5895Syz147064 	return (0);
258*5895Syz147064 }
259*5895Syz147064 
260*5895Syz147064 static void
261*5895Syz147064 devnet_filldir(struct sdev_node *ddv)
262*5895Syz147064 {
263*5895Syz147064 	sdev_node_t	*dv, *next;
264*5895Syz147064 	char		link[MAXLINKNAMELEN];
265*5895Syz147064 	datalink_id_t	linkid;
266*5895Syz147064 
267*5895Syz147064 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
268*5895Syz147064 	if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
269*5895Syz147064 		rw_exit(&ddv->sdev_contents);
270*5895Syz147064 		rw_enter(&ddv->sdev_contents, RW_WRITER);
271*5895Syz147064 	}
272*5895Syz147064 
273*5895Syz147064 	for (dv = ddv->sdev_dot; dv; dv = next) {
274*5895Syz147064 		next = dv->sdev_next;
275*5895Syz147064 
276*5895Syz147064 		/* skip stale nodes */
277*5895Syz147064 		if (dv->sdev_flags & SDEV_STALE)
278*5895Syz147064 			continue;
279*5895Syz147064 
280*5895Syz147064 		/* validate and prune only ready nodes */
281*5895Syz147064 		if (dv->sdev_state != SDEV_READY)
282*5895Syz147064 			continue;
283*5895Syz147064 
284*5895Syz147064 		switch (devnet_validate(dv)) {
285*5895Syz147064 		case SDEV_VTOR_VALID:
286*5895Syz147064 		case SDEV_VTOR_SKIP:
287*5895Syz147064 			continue;
288*5895Syz147064 		case SDEV_VTOR_INVALID:
289*5895Syz147064 			sdcmn_err12(("devnet_filldir: destroy invalid "
290*5895Syz147064 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
291*5895Syz147064 			break;
292*5895Syz147064 		}
293*5895Syz147064 
294*5895Syz147064 		if (SDEVTOV(dv)->v_count > 0)
295*5895Syz147064 			continue;
296*5895Syz147064 		SDEV_HOLD(dv);
297*5895Syz147064 		/* remove the cache node */
298*5895Syz147064 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
299*5895Syz147064 		    SDEV_CACHE_DELETE);
300*5895Syz147064 	}
301*5895Syz147064 
302*5895Syz147064 	if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
303*5895Syz147064 		goto done;
304*5895Syz147064 
305*5895Syz147064 	if (SDEV_IS_GLOBAL(ddv)) {
306*5895Syz147064 		linkid = DATALINK_INVALID_LINKID;
307*5895Syz147064 		do {
308*5895Syz147064 			linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
309*5895Syz147064 			    DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
310*5895Syz147064 
311*5895Syz147064 			if ((linkid != DATALINK_INVALID_LINKID) &&
312*5895Syz147064 			    (dls_mgmt_get_linkinfo(linkid, link,
313*5895Syz147064 			    NULL, NULL, NULL) == 0)) {
314*5895Syz147064 				(void) devnet_filldir_datalink(link, ddv);
315*5895Syz147064 			}
316*5895Syz147064 		} while (linkid != DATALINK_INVALID_LINKID);
317*5895Syz147064 	} else {
318*5895Syz147064 		(void) zone_datalink_walk(getzoneid(),
319*5895Syz147064 		    devnet_filldir_datalink, ddv);
320*5895Syz147064 	}
321*5895Syz147064 
322*5895Syz147064 	ddv->sdev_flags &= ~SDEV_BUILD;
323*5895Syz147064 
324*5895Syz147064 done:
325*5895Syz147064 	rw_downgrade(&ddv->sdev_contents);
326*5895Syz147064 }
327*5895Syz147064 
328*5895Syz147064 /*
329*5895Syz147064  * Display all instantiated network datalink device nodes.
330*5895Syz147064  * A /dev/net entry will be created only after the first lookup of
331*5895Syz147064  * the network datalink device succeeds.
332*5895Syz147064  */
333*5895Syz147064 /*ARGSUSED4*/
334*5895Syz147064 static int
335*5895Syz147064 devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
336*5895Syz147064     int *eofp, caller_context_t *ct, int flags)
337*5895Syz147064 {
338*5895Syz147064 	struct sdev_node *sdvp = VTOSDEV(dvp);
339*5895Syz147064 
340*5895Syz147064 	ASSERT(sdvp);
341*5895Syz147064 
342*5895Syz147064 	if (uiop->uio_offset == 0)
343*5895Syz147064 		devnet_filldir(sdvp);
344*5895Syz147064 
345*5895Syz147064 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
346*5895Syz147064 }
347*5895Syz147064 
348*5895Syz147064 /*
349*5895Syz147064  * This callback is invoked from devname_inactive_func() to release
350*5895Syz147064  * the net entry which was held in devnet_create_rvp().
351*5895Syz147064  */
352*5895Syz147064 static void
353*5895Syz147064 devnet_inactive_callback(struct vnode *dvp)
354*5895Syz147064 {
355*5895Syz147064 	struct sdev_node *sdvp = VTOSDEV(dvp);
356*5895Syz147064 	dls_dl_handle_t ddh;
357*5895Syz147064 
358*5895Syz147064 	if (dvp->v_type == VDIR)
359*5895Syz147064 		return;
360*5895Syz147064 
361*5895Syz147064 	ASSERT(dvp->v_type == VCHR);
362*5895Syz147064 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
363*5895Syz147064 	ddh = sdvp->sdev_private;
364*5895Syz147064 	sdvp->sdev_private = NULL;
365*5895Syz147064 	sdvp->sdev_flags |= SDEV_ATTR_INVALID;
366*5895Syz147064 	rw_exit(&sdvp->sdev_contents);
367*5895Syz147064 
368*5895Syz147064 	/*
369*5895Syz147064 	 * "ddh" (sdev_private) could be NULL if devnet_lookup fails.
370*5895Syz147064 	 */
371*5895Syz147064 	if (ddh != NULL)
372*5895Syz147064 		dls_devnet_close(ddh);
373*5895Syz147064 }
374*5895Syz147064 
375*5895Syz147064 /*ARGSUSED*/
376*5895Syz147064 static void
377*5895Syz147064 devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct)
378*5895Syz147064 {
379*5895Syz147064 	devname_inactive_func(dvp, cred, devnet_inactive_callback);
380*5895Syz147064 }
381*5895Syz147064 
382*5895Syz147064 /*
383*5895Syz147064  * We override lookup and readdir to build entries based on the
384*5895Syz147064  * in kernel vanity naming node table.
385*5895Syz147064  */
386*5895Syz147064 const fs_operation_def_t devnet_vnodeops_tbl[] = {
387*5895Syz147064 	VOPNAME_READDIR,	{ .vop_readdir = devnet_readdir },
388*5895Syz147064 	VOPNAME_LOOKUP,		{ .vop_lookup = devnet_lookup },
389*5895Syz147064 	VOPNAME_INACTIVE,	{ .vop_inactive = devnet_inactive },
390*5895Syz147064 	VOPNAME_CREATE,		{ .error = fs_nosys },
391*5895Syz147064 	VOPNAME_REMOVE,		{ .error = fs_nosys },
392*5895Syz147064 	VOPNAME_MKDIR,		{ .error = fs_nosys },
393*5895Syz147064 	VOPNAME_RMDIR,		{ .error = fs_nosys },
394*5895Syz147064 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
395*5895Syz147064 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
396*5895Syz147064 	NULL,			NULL
397*5895Syz147064 };
398