xref: /onnv-gate/usr/src/uts/common/fs/dev/sdev_netops.c (revision 6260:0ca1934af628)
15895Syz147064 /*
25895Syz147064  * CDDL HEADER START
35895Syz147064  *
45895Syz147064  * The contents of this file are subject to the terms of the
55895Syz147064  * Common Development and Distribution License (the "License").
65895Syz147064  * You may not use this file except in compliance with the License.
75895Syz147064  *
85895Syz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
95895Syz147064  * or http://www.opensolaris.org/os/licensing.
105895Syz147064  * See the License for the specific language governing permissions
115895Syz147064  * and limitations under the License.
125895Syz147064  *
135895Syz147064  * When distributing Covered Code, include this CDDL HEADER in each
145895Syz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
155895Syz147064  * If applicable, add the following below this CDDL HEADER, with the
165895Syz147064  * fields enclosed by brackets "[]" replaced with your own identifying
175895Syz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
185895Syz147064  *
195895Syz147064  * CDDL HEADER END
205895Syz147064  */
215895Syz147064 /*
225895Syz147064  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
235895Syz147064  * Use is subject to license terms.
245895Syz147064  */
255895Syz147064 
265895Syz147064 #pragma ident	"%Z%%M%	%I%	%E% SMI"
275895Syz147064 
285895Syz147064 /*
295895Syz147064  * vnode ops for the /dev/net directory
305895Syz147064  *
315895Syz147064  *	The lookup is based on the internal vanity naming node table.  We also
325895Syz147064  *	override readdir in order to delete net nodes no longer	in-use.
335895Syz147064  */
345895Syz147064 
355895Syz147064 #include <sys/types.h>
365895Syz147064 #include <sys/param.h>
375895Syz147064 #include <sys/sysmacros.h>
385895Syz147064 #include <sys/sunndi.h>
395895Syz147064 #include <fs/fs_subr.h>
405895Syz147064 #include <sys/fs/dv_node.h>
415895Syz147064 #include <sys/fs/sdev_impl.h>
425895Syz147064 #include <sys/policy.h>
435895Syz147064 #include <sys/zone.h>
445895Syz147064 #include <sys/dls.h>
455895Syz147064 
465895Syz147064 struct vnodeops		*devnet_vnodeops;
475895Syz147064 
485895Syz147064 /*
495895Syz147064  * Called by zone_walk_datalink() to see if the given link name belongs to the
505895Syz147064  * given zone.  Returns 0 to continue the walk, -1 if the link name is found.
515895Syz147064  */
525895Syz147064 static int
535895Syz147064 devnet_validate_name(const char *link, void *arg)
545895Syz147064 {
555895Syz147064 	return ((strcmp(link, arg) == 0) ? -1 : 0);
565895Syz147064 }
575895Syz147064 
585895Syz147064 /*
595895Syz147064  * Check if a net sdev_node is still valid - i.e. it represents a current
605895Syz147064  * network link.
615895Syz147064  * This serves two purposes
625895Syz147064  *	- only valid net nodes are returned during lookup() and readdir().
635895Syz147064  *	- since net sdev_nodes are not actively destroyed when a network link
645895Syz147064  *	  goes away, we use the validator to do deferred cleanup i.e. when such
655895Syz147064  *	  nodes are encountered during subsequent lookup() and readdir().
665895Syz147064  */
675895Syz147064 int
685895Syz147064 devnet_validate(struct sdev_node *dv)
695895Syz147064 {
705895Syz147064 	char *nm = dv->sdev_name;
715895Syz147064 	datalink_id_t linkid;
725895Syz147064 
735895Syz147064 	ASSERT(!(dv->sdev_flags & SDEV_STALE));
745895Syz147064 	ASSERT(dv->sdev_state == SDEV_READY);
755895Syz147064 
765895Syz147064 	if (SDEV_IS_GLOBAL(dv)) {
775895Syz147064 		return ((dls_mgmt_get_linkid(nm, &linkid) != 0) ?
785895Syz147064 		    SDEV_VTOR_INVALID : SDEV_VTOR_VALID);
795895Syz147064 	} else {
805895Syz147064 		return ((zone_datalink_walk(getzoneid(), devnet_validate_name,
815895Syz147064 		    nm) == -1) ? SDEV_VTOR_VALID : SDEV_VTOR_INVALID);
825895Syz147064 	}
835895Syz147064 }
845895Syz147064 
855895Syz147064 /*
865895Syz147064  * This callback is invoked from devname_lookup_func() to create
875895Syz147064  * a net entry when the node is not found in the cache.
885895Syz147064  */
895895Syz147064 static int
905895Syz147064 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
915895Syz147064 {
925895Syz147064 	timestruc_t now;
935895Syz147064 	dev_t dev;
945895Syz147064 	int error;
955895Syz147064 
965895Syz147064 	if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) {
975895Syz147064 		sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
985895Syz147064 		    "network node: %s\n", nm));
995895Syz147064 		return (error);
1005895Syz147064 	}
1015895Syz147064 
1025895Syz147064 	/*
1035895Syz147064 	 * This is a valid network device (at least at this point in time).
1045895Syz147064 	 * Create the node by setting the attribute; the rest is taken care
1055895Syz147064 	 * of by devname_lookup_func().
1065895Syz147064 	 */
1075895Syz147064 	*vap = sdev_vattr_chr;
1085895Syz147064 	vap->va_mode |= 0666;
1095895Syz147064 	vap->va_rdev = dev;
1105895Syz147064 
1115895Syz147064 	gethrestime(&now);
1125895Syz147064 	vap->va_atime = now;
1135895Syz147064 	vap->va_mtime = now;
1145895Syz147064 	vap->va_ctime = now;
1155895Syz147064 	return (0);
1165895Syz147064 }
1175895Syz147064 
1185895Syz147064 /*
1195895Syz147064  * Lookup for /dev/net directory
1205895Syz147064  *	If the entry does not exist, the devnet_create_rvp() callback
1215895Syz147064  *	is invoked to create it.  Nodes do not persist across reboot.
1225895Syz147064  */
1235895Syz147064 /*ARGSUSED3*/
1245895Syz147064 static int
1255895Syz147064 devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
1265895Syz147064     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
1275895Syz147064     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
1285895Syz147064 {
1295895Syz147064 	struct sdev_node *ddv = VTOSDEV(dvp);
1305895Syz147064 	struct sdev_node *dv = NULL;
1315895Syz147064 	dls_dl_handle_t ddh = NULL;
1325895Syz147064 	struct vattr vattr;
1335895Syz147064 	int nmlen;
1345895Syz147064 	int error = ENOENT;
1355895Syz147064 
1365895Syz147064 	if (SDEVTOV(ddv)->v_type != VDIR)
1375895Syz147064 		return (ENOTDIR);
1385895Syz147064 
1395895Syz147064 	/*
1405895Syz147064 	 * Empty name or ., return node itself.
1415895Syz147064 	 */
1425895Syz147064 	nmlen = strlen(nm);
1435895Syz147064 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
1445895Syz147064 		*vpp = SDEVTOV(ddv);
1455895Syz147064 		VN_HOLD(*vpp);
1465895Syz147064 		return (0);
1475895Syz147064 	}
1485895Syz147064 
1495895Syz147064 	/*
1505895Syz147064 	 * .., return the parent directory
1515895Syz147064 	 */
1525895Syz147064 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
1535895Syz147064 		*vpp = SDEVTOV(ddv->sdev_dotdot);
1545895Syz147064 		VN_HOLD(*vpp);
1555895Syz147064 		return (0);
1565895Syz147064 	}
1575895Syz147064 
1585895Syz147064 	rw_enter(&ddv->sdev_contents, RW_WRITER);
1595895Syz147064 
1605895Syz147064 	/*
1615895Syz147064 	 * directory cache lookup:
1625895Syz147064 	 */
1635895Syz147064 	if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) {
1645895Syz147064 		if (dv->sdev_state == SDEV_READY) {
1655895Syz147064 			if (!(dv->sdev_flags & SDEV_ATTR_INVALID))
1665895Syz147064 				goto found;
1675895Syz147064 		} else {
1685895Syz147064 			ASSERT(dv->sdev_state == SDEV_ZOMBIE);
1695895Syz147064 			goto failed;
1705895Syz147064 		}
1715895Syz147064 	}
1725895Syz147064 
1735895Syz147064 	/*
1745895Syz147064 	 * ZOMBIED parent does not allow new node creation, bail out early.
1755895Syz147064 	 */
1765895Syz147064 	if (ddv->sdev_state == SDEV_ZOMBIE)
1775895Syz147064 		goto failed;
1785895Syz147064 
1795895Syz147064 	error = devnet_create_rvp(nm, &vattr, &ddh);
1805895Syz147064 	if (error != 0)
1815895Syz147064 		goto failed;
1825895Syz147064 
1835895Syz147064 	error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY);
1845895Syz147064 	if (error != 0) {
1855895Syz147064 		ASSERT(dv == NULL);
1865895Syz147064 		dls_devnet_close(ddh);
1875895Syz147064 		goto failed;
1885895Syz147064 	}
1895895Syz147064 
1905895Syz147064 	ASSERT(dv != NULL);
1915895Syz147064 
1925895Syz147064 	rw_enter(&dv->sdev_contents, RW_WRITER);
1935895Syz147064 	if (dv->sdev_flags & SDEV_ATTR_INVALID) {
1945895Syz147064 		/*
1955895Syz147064 		 * SDEV_ATTR_INVALID means that this device has been
1965895Syz147064 		 * detached, and its dev_t might've been changed too.
1975895Syz147064 		 * Therefore, sdev_node's 'vattr' needs to be updated.
1985895Syz147064 		 */
1995895Syz147064 		SDEVTOV(dv)->v_rdev = vattr.va_rdev;
2005895Syz147064 		ASSERT(dv->sdev_attr != NULL);
2015895Syz147064 		dv->sdev_attr->va_rdev = vattr.va_rdev;
2025895Syz147064 		dv->sdev_flags &= ~SDEV_ATTR_INVALID;
2035895Syz147064 	}
2045895Syz147064 	ASSERT(dv->sdev_private == NULL);
2055895Syz147064 	dv->sdev_private = ddh;
2065895Syz147064 	rw_exit(&dv->sdev_contents);
2075895Syz147064 
2085895Syz147064 found:
2095895Syz147064 	ASSERT(SDEV_HELD(dv));
2105895Syz147064 	rw_exit(&ddv->sdev_contents);
2115895Syz147064 	return (sdev_to_vp(dv, vpp));
2125895Syz147064 
2135895Syz147064 failed:
2145895Syz147064 	rw_exit(&ddv->sdev_contents);
2155895Syz147064 
2165895Syz147064 	if (dv != NULL)
2175895Syz147064 		SDEV_RELE(dv);
2185895Syz147064 
2195895Syz147064 	*vpp = NULL;
2205895Syz147064 	return (error);
2215895Syz147064 }
2225895Syz147064 
2235895Syz147064 static int
2245895Syz147064 devnet_filldir_datalink(const char *link, void *arg)
2255895Syz147064 {
2265895Syz147064 	struct sdev_node *ddv = arg;
2275895Syz147064 	struct vattr vattr;
2285895Syz147064 	struct sdev_node *dv;
2295895Syz147064 	dls_dl_handle_t ddh = NULL;
2305895Syz147064 
2315895Syz147064 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
2325895Syz147064 	if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
2335895Syz147064 		goto found;
2345895Syz147064 
2355895Syz147064 	if (devnet_create_rvp(link, &vattr, &ddh) != 0)
2365895Syz147064 		return (0);
2375895Syz147064 
2385895Syz147064 	ASSERT(ddh != NULL);
2395895Syz147064 	dls_devnet_close(ddh);
2405895Syz147064 
2415895Syz147064 	if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred,
2425895Syz147064 	    SDEV_READY) != 0) {
2435895Syz147064 		return (0);
2445895Syz147064 	}
2455895Syz147064 
2465895Syz147064 	/*
2475895Syz147064 	 * As there is no reference holding the network device, it could be
2485895Syz147064 	 * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated
2495895Syz147064 	 * later.
2505895Syz147064 	 */
2515895Syz147064 	rw_enter(&dv->sdev_contents, RW_WRITER);
2525895Syz147064 	dv->sdev_flags |= SDEV_ATTR_INVALID;
2535895Syz147064 	rw_exit(&dv->sdev_contents);
2545895Syz147064 
2555895Syz147064 found:
2565895Syz147064 	SDEV_SIMPLE_RELE(dv);
2575895Syz147064 	return (0);
2585895Syz147064 }
2595895Syz147064 
2605895Syz147064 static void
2615895Syz147064 devnet_filldir(struct sdev_node *ddv)
2625895Syz147064 {
2635895Syz147064 	sdev_node_t	*dv, *next;
2645895Syz147064 	char		link[MAXLINKNAMELEN];
2655895Syz147064 	datalink_id_t	linkid;
2665895Syz147064 
2675895Syz147064 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2685895Syz147064 	if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
2695895Syz147064 		rw_exit(&ddv->sdev_contents);
2705895Syz147064 		rw_enter(&ddv->sdev_contents, RW_WRITER);
2715895Syz147064 	}
2725895Syz147064 
273*6260Sjg 	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
274*6260Sjg 		next = SDEV_NEXT_ENTRY(ddv, dv);
2755895Syz147064 
2765895Syz147064 		/* skip stale nodes */
2775895Syz147064 		if (dv->sdev_flags & SDEV_STALE)
2785895Syz147064 			continue;
2795895Syz147064 
2805895Syz147064 		/* validate and prune only ready nodes */
2815895Syz147064 		if (dv->sdev_state != SDEV_READY)
2825895Syz147064 			continue;
2835895Syz147064 
2845895Syz147064 		switch (devnet_validate(dv)) {
2855895Syz147064 		case SDEV_VTOR_VALID:
2865895Syz147064 		case SDEV_VTOR_SKIP:
2875895Syz147064 			continue;
2885895Syz147064 		case SDEV_VTOR_INVALID:
2895895Syz147064 			sdcmn_err12(("devnet_filldir: destroy invalid "
2905895Syz147064 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
2915895Syz147064 			break;
2925895Syz147064 		}
2935895Syz147064 
2945895Syz147064 		if (SDEVTOV(dv)->v_count > 0)
2955895Syz147064 			continue;
2965895Syz147064 		SDEV_HOLD(dv);
2975895Syz147064 		/* remove the cache node */
2985895Syz147064 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
2995895Syz147064 		    SDEV_CACHE_DELETE);
3005895Syz147064 	}
3015895Syz147064 
3025895Syz147064 	if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
3035895Syz147064 		goto done;
3045895Syz147064 
3055895Syz147064 	if (SDEV_IS_GLOBAL(ddv)) {
3065895Syz147064 		linkid = DATALINK_INVALID_LINKID;
3075895Syz147064 		do {
3085895Syz147064 			linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
3095895Syz147064 			    DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
3105895Syz147064 
3115895Syz147064 			if ((linkid != DATALINK_INVALID_LINKID) &&
3125895Syz147064 			    (dls_mgmt_get_linkinfo(linkid, link,
3135895Syz147064 			    NULL, NULL, NULL) == 0)) {
3145895Syz147064 				(void) devnet_filldir_datalink(link, ddv);
3155895Syz147064 			}
3165895Syz147064 		} while (linkid != DATALINK_INVALID_LINKID);
3175895Syz147064 	} else {
3185895Syz147064 		(void) zone_datalink_walk(getzoneid(),
3195895Syz147064 		    devnet_filldir_datalink, ddv);
3205895Syz147064 	}
3215895Syz147064 
3225895Syz147064 	ddv->sdev_flags &= ~SDEV_BUILD;
3235895Syz147064 
3245895Syz147064 done:
3255895Syz147064 	rw_downgrade(&ddv->sdev_contents);
3265895Syz147064 }
3275895Syz147064 
3285895Syz147064 /*
3295895Syz147064  * Display all instantiated network datalink device nodes.
3305895Syz147064  * A /dev/net entry will be created only after the first lookup of
3315895Syz147064  * the network datalink device succeeds.
3325895Syz147064  */
3335895Syz147064 /*ARGSUSED4*/
3345895Syz147064 static int
3355895Syz147064 devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
3365895Syz147064     int *eofp, caller_context_t *ct, int flags)
3375895Syz147064 {
3385895Syz147064 	struct sdev_node *sdvp = VTOSDEV(dvp);
3395895Syz147064 
3405895Syz147064 	ASSERT(sdvp);
3415895Syz147064 
3425895Syz147064 	if (uiop->uio_offset == 0)
3435895Syz147064 		devnet_filldir(sdvp);
3445895Syz147064 
3455895Syz147064 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
3465895Syz147064 }
3475895Syz147064 
3485895Syz147064 /*
3495895Syz147064  * This callback is invoked from devname_inactive_func() to release
3505895Syz147064  * the net entry which was held in devnet_create_rvp().
3515895Syz147064  */
3525895Syz147064 static void
3535895Syz147064 devnet_inactive_callback(struct vnode *dvp)
3545895Syz147064 {
3555895Syz147064 	struct sdev_node *sdvp = VTOSDEV(dvp);
3565895Syz147064 	dls_dl_handle_t ddh;
3575895Syz147064 
3585895Syz147064 	if (dvp->v_type == VDIR)
3595895Syz147064 		return;
3605895Syz147064 
3615895Syz147064 	ASSERT(dvp->v_type == VCHR);
3625895Syz147064 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
3635895Syz147064 	ddh = sdvp->sdev_private;
3645895Syz147064 	sdvp->sdev_private = NULL;
3655895Syz147064 	sdvp->sdev_flags |= SDEV_ATTR_INVALID;
3665895Syz147064 	rw_exit(&sdvp->sdev_contents);
3675895Syz147064 
3685895Syz147064 	/*
3695895Syz147064 	 * "ddh" (sdev_private) could be NULL if devnet_lookup fails.
3705895Syz147064 	 */
3715895Syz147064 	if (ddh != NULL)
3725895Syz147064 		dls_devnet_close(ddh);
3735895Syz147064 }
3745895Syz147064 
3755895Syz147064 /*ARGSUSED*/
3765895Syz147064 static void
3775895Syz147064 devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct)
3785895Syz147064 {
3795895Syz147064 	devname_inactive_func(dvp, cred, devnet_inactive_callback);
3805895Syz147064 }
3815895Syz147064 
3825895Syz147064 /*
3835895Syz147064  * We override lookup and readdir to build entries based on the
3845895Syz147064  * in kernel vanity naming node table.
3855895Syz147064  */
3865895Syz147064 const fs_operation_def_t devnet_vnodeops_tbl[] = {
3875895Syz147064 	VOPNAME_READDIR,	{ .vop_readdir = devnet_readdir },
3885895Syz147064 	VOPNAME_LOOKUP,		{ .vop_lookup = devnet_lookup },
3895895Syz147064 	VOPNAME_INACTIVE,	{ .vop_inactive = devnet_inactive },
3905895Syz147064 	VOPNAME_CREATE,		{ .error = fs_nosys },
3915895Syz147064 	VOPNAME_REMOVE,		{ .error = fs_nosys },
3925895Syz147064 	VOPNAME_MKDIR,		{ .error = fs_nosys },
3935895Syz147064 	VOPNAME_RMDIR,		{ .error = fs_nosys },
3945895Syz147064 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
3955895Syz147064 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
3965895Syz147064 	NULL,			NULL
3975895Syz147064 };
398