xref: /onnv-gate/usr/src/uts/common/fs/dev/sdev_netops.c (revision 10616:3be00c4a6835)
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 /*
22*10616SSebastien.Roy@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
235895Syz147064  * Use is subject to license terms.
245895Syz147064  */
255895Syz147064 
265895Syz147064 /*
275895Syz147064  * vnode ops for the /dev/net directory
285895Syz147064  *
295895Syz147064  *	The lookup is based on the internal vanity naming node table.  We also
305895Syz147064  *	override readdir in order to delete net nodes no longer	in-use.
315895Syz147064  */
325895Syz147064 
335895Syz147064 #include <sys/types.h>
345895Syz147064 #include <sys/param.h>
355895Syz147064 #include <sys/sysmacros.h>
365895Syz147064 #include <sys/sunndi.h>
375895Syz147064 #include <fs/fs_subr.h>
385895Syz147064 #include <sys/fs/dv_node.h>
395895Syz147064 #include <sys/fs/sdev_impl.h>
405895Syz147064 #include <sys/policy.h>
415895Syz147064 #include <sys/zone.h>
425895Syz147064 #include <sys/dls.h>
435895Syz147064 
445895Syz147064 struct vnodeops		*devnet_vnodeops;
455895Syz147064 
465895Syz147064 /*
475895Syz147064  * Check if a net sdev_node is still valid - i.e. it represents a current
485895Syz147064  * network link.
495895Syz147064  * This serves two purposes
505895Syz147064  *	- only valid net nodes are returned during lookup() and readdir().
515895Syz147064  *	- since net sdev_nodes are not actively destroyed when a network link
525895Syz147064  *	  goes away, we use the validator to do deferred cleanup i.e. when such
535895Syz147064  *	  nodes are encountered during subsequent lookup() and readdir().
545895Syz147064  */
555895Syz147064 int
devnet_validate(struct sdev_node * dv)565895Syz147064 devnet_validate(struct sdev_node *dv)
575895Syz147064 {
585895Syz147064 	datalink_id_t linkid;
59*10616SSebastien.Roy@Sun.COM 	zoneid_t zoneid;
605895Syz147064 
615895Syz147064 	ASSERT(!(dv->sdev_flags & SDEV_STALE));
625895Syz147064 	ASSERT(dv->sdev_state == SDEV_READY);
635895Syz147064 
64*10616SSebastien.Roy@Sun.COM 	if (dls_mgmt_get_linkid(dv->sdev_name, &linkid) != 0)
65*10616SSebastien.Roy@Sun.COM 		return (SDEV_VTOR_INVALID);
66*10616SSebastien.Roy@Sun.COM 	if (SDEV_IS_GLOBAL(dv))
67*10616SSebastien.Roy@Sun.COM 		return (SDEV_VTOR_VALID);
68*10616SSebastien.Roy@Sun.COM 	zoneid = getzoneid();
69*10616SSebastien.Roy@Sun.COM 	return (zone_check_datalink(&zoneid, linkid) == 0 ?
70*10616SSebastien.Roy@Sun.COM 	    SDEV_VTOR_VALID : SDEV_VTOR_INVALID);
715895Syz147064 }
725895Syz147064 
735895Syz147064 /*
745895Syz147064  * This callback is invoked from devname_lookup_func() to create
755895Syz147064  * a net entry when the node is not found in the cache.
765895Syz147064  */
775895Syz147064 static int
devnet_create_rvp(const char * nm,struct vattr * vap,dls_dl_handle_t * ddhp)785895Syz147064 devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp)
795895Syz147064 {
805895Syz147064 	timestruc_t now;
815895Syz147064 	dev_t dev;
825895Syz147064 	int error;
835895Syz147064 
845895Syz147064 	if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) {
855895Syz147064 		sdcmn_err12(("devnet_create_rvp: not a valid vanity name "
865895Syz147064 		    "network node: %s\n", nm));
875895Syz147064 		return (error);
885895Syz147064 	}
895895Syz147064 
905895Syz147064 	/*
915895Syz147064 	 * This is a valid network device (at least at this point in time).
925895Syz147064 	 * Create the node by setting the attribute; the rest is taken care
935895Syz147064 	 * of by devname_lookup_func().
945895Syz147064 	 */
955895Syz147064 	*vap = sdev_vattr_chr;
965895Syz147064 	vap->va_mode |= 0666;
975895Syz147064 	vap->va_rdev = dev;
985895Syz147064 
995895Syz147064 	gethrestime(&now);
1005895Syz147064 	vap->va_atime = now;
1015895Syz147064 	vap->va_mtime = now;
1025895Syz147064 	vap->va_ctime = now;
1035895Syz147064 	return (0);
1045895Syz147064 }
1055895Syz147064 
1065895Syz147064 /*
1075895Syz147064  * Lookup for /dev/net directory
1085895Syz147064  *	If the entry does not exist, the devnet_create_rvp() callback
1095895Syz147064  *	is invoked to create it.  Nodes do not persist across reboot.
1105895Syz147064  */
1115895Syz147064 /*ARGSUSED3*/
1125895Syz147064 static int
devnet_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)1135895Syz147064 devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp,
1145895Syz147064     struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred,
1155895Syz147064     caller_context_t *ct, int *direntflags, pathname_t *realpnp)
1165895Syz147064 {
1175895Syz147064 	struct sdev_node *ddv = VTOSDEV(dvp);
1185895Syz147064 	struct sdev_node *dv = NULL;
1195895Syz147064 	dls_dl_handle_t ddh = NULL;
1205895Syz147064 	struct vattr vattr;
1215895Syz147064 	int nmlen;
1225895Syz147064 	int error = ENOENT;
1235895Syz147064 
1245895Syz147064 	if (SDEVTOV(ddv)->v_type != VDIR)
1255895Syz147064 		return (ENOTDIR);
1265895Syz147064 
1275895Syz147064 	/*
1285895Syz147064 	 * Empty name or ., return node itself.
1295895Syz147064 	 */
1305895Syz147064 	nmlen = strlen(nm);
1315895Syz147064 	if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) {
1325895Syz147064 		*vpp = SDEVTOV(ddv);
1335895Syz147064 		VN_HOLD(*vpp);
1345895Syz147064 		return (0);
1355895Syz147064 	}
1365895Syz147064 
1375895Syz147064 	/*
1385895Syz147064 	 * .., return the parent directory
1395895Syz147064 	 */
1405895Syz147064 	if ((nmlen == 2) && (strcmp(nm, "..") == 0)) {
1415895Syz147064 		*vpp = SDEVTOV(ddv->sdev_dotdot);
1425895Syz147064 		VN_HOLD(*vpp);
1435895Syz147064 		return (0);
1445895Syz147064 	}
1455895Syz147064 
1465895Syz147064 	rw_enter(&ddv->sdev_contents, RW_WRITER);
1475895Syz147064 
1485895Syz147064 	/*
1495895Syz147064 	 * directory cache lookup:
1505895Syz147064 	 */
1515895Syz147064 	if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) {
1525895Syz147064 		if (dv->sdev_state == SDEV_READY) {
1535895Syz147064 			if (!(dv->sdev_flags & SDEV_ATTR_INVALID))
1545895Syz147064 				goto found;
1555895Syz147064 		} else {
1565895Syz147064 			ASSERT(dv->sdev_state == SDEV_ZOMBIE);
1575895Syz147064 			goto failed;
1585895Syz147064 		}
1595895Syz147064 	}
1605895Syz147064 
1615895Syz147064 	/*
1625895Syz147064 	 * ZOMBIED parent does not allow new node creation, bail out early.
1635895Syz147064 	 */
1645895Syz147064 	if (ddv->sdev_state == SDEV_ZOMBIE)
1655895Syz147064 		goto failed;
1665895Syz147064 
1675895Syz147064 	error = devnet_create_rvp(nm, &vattr, &ddh);
1685895Syz147064 	if (error != 0)
1695895Syz147064 		goto failed;
1705895Syz147064 
1715895Syz147064 	error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY);
1725895Syz147064 	if (error != 0) {
1735895Syz147064 		ASSERT(dv == NULL);
1745895Syz147064 		dls_devnet_close(ddh);
1755895Syz147064 		goto failed;
1765895Syz147064 	}
1775895Syz147064 
1785895Syz147064 	ASSERT(dv != NULL);
1795895Syz147064 
1805895Syz147064 	rw_enter(&dv->sdev_contents, RW_WRITER);
1815895Syz147064 	if (dv->sdev_flags & SDEV_ATTR_INVALID) {
1825895Syz147064 		/*
1835895Syz147064 		 * SDEV_ATTR_INVALID means that this device has been
1845895Syz147064 		 * detached, and its dev_t might've been changed too.
1855895Syz147064 		 * Therefore, sdev_node's 'vattr' needs to be updated.
1865895Syz147064 		 */
1875895Syz147064 		SDEVTOV(dv)->v_rdev = vattr.va_rdev;
1885895Syz147064 		ASSERT(dv->sdev_attr != NULL);
1895895Syz147064 		dv->sdev_attr->va_rdev = vattr.va_rdev;
1905895Syz147064 		dv->sdev_flags &= ~SDEV_ATTR_INVALID;
1915895Syz147064 	}
1925895Syz147064 	ASSERT(dv->sdev_private == NULL);
1935895Syz147064 	dv->sdev_private = ddh;
1945895Syz147064 	rw_exit(&dv->sdev_contents);
1955895Syz147064 
1965895Syz147064 found:
1975895Syz147064 	ASSERT(SDEV_HELD(dv));
1985895Syz147064 	rw_exit(&ddv->sdev_contents);
1995895Syz147064 	return (sdev_to_vp(dv, vpp));
2005895Syz147064 
2015895Syz147064 failed:
2025895Syz147064 	rw_exit(&ddv->sdev_contents);
2035895Syz147064 
2045895Syz147064 	if (dv != NULL)
2055895Syz147064 		SDEV_RELE(dv);
2065895Syz147064 
2075895Syz147064 	*vpp = NULL;
2085895Syz147064 	return (error);
2095895Syz147064 }
2105895Syz147064 
2115895Syz147064 static int
devnet_filldir_datalink(datalink_id_t linkid,void * arg)212*10616SSebastien.Roy@Sun.COM devnet_filldir_datalink(datalink_id_t linkid, void *arg)
2135895Syz147064 {
214*10616SSebastien.Roy@Sun.COM 	struct sdev_node	*ddv = arg;
215*10616SSebastien.Roy@Sun.COM 	struct vattr		vattr;
216*10616SSebastien.Roy@Sun.COM 	struct sdev_node	*dv;
217*10616SSebastien.Roy@Sun.COM 	dls_dl_handle_t		ddh = NULL;
218*10616SSebastien.Roy@Sun.COM 	char			link[MAXLINKNAMELEN];
2195895Syz147064 
2205895Syz147064 	ASSERT(RW_WRITE_HELD(&ddv->sdev_contents));
221*10616SSebastien.Roy@Sun.COM 
222*10616SSebastien.Roy@Sun.COM 	if (dls_mgmt_get_linkinfo(linkid, link, NULL, NULL, NULL) != 0)
223*10616SSebastien.Roy@Sun.COM 		return (0);
224*10616SSebastien.Roy@Sun.COM 
2255895Syz147064 	if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL)
2265895Syz147064 		goto found;
2275895Syz147064 
2285895Syz147064 	if (devnet_create_rvp(link, &vattr, &ddh) != 0)
2295895Syz147064 		return (0);
2305895Syz147064 
2315895Syz147064 	ASSERT(ddh != NULL);
2325895Syz147064 	dls_devnet_close(ddh);
2335895Syz147064 
2345895Syz147064 	if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred,
2355895Syz147064 	    SDEV_READY) != 0) {
2365895Syz147064 		return (0);
2375895Syz147064 	}
2385895Syz147064 
2395895Syz147064 	/*
2405895Syz147064 	 * As there is no reference holding the network device, it could be
2415895Syz147064 	 * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated
2425895Syz147064 	 * later.
2435895Syz147064 	 */
2445895Syz147064 	rw_enter(&dv->sdev_contents, RW_WRITER);
2455895Syz147064 	dv->sdev_flags |= SDEV_ATTR_INVALID;
2465895Syz147064 	rw_exit(&dv->sdev_contents);
2475895Syz147064 
2485895Syz147064 found:
2495895Syz147064 	SDEV_SIMPLE_RELE(dv);
2505895Syz147064 	return (0);
2515895Syz147064 }
2525895Syz147064 
2535895Syz147064 static void
devnet_filldir(struct sdev_node * ddv)2545895Syz147064 devnet_filldir(struct sdev_node *ddv)
2555895Syz147064 {
2565895Syz147064 	sdev_node_t	*dv, *next;
2575895Syz147064 	datalink_id_t	linkid;
2585895Syz147064 
2595895Syz147064 	ASSERT(RW_READ_HELD(&ddv->sdev_contents));
2605895Syz147064 	if (rw_tryupgrade(&ddv->sdev_contents) == NULL) {
2615895Syz147064 		rw_exit(&ddv->sdev_contents);
2625895Syz147064 		rw_enter(&ddv->sdev_contents, RW_WRITER);
2635895Syz147064 	}
2645895Syz147064 
2656260Sjg 	for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) {
2666260Sjg 		next = SDEV_NEXT_ENTRY(ddv, dv);
2675895Syz147064 
2685895Syz147064 		/* validate and prune only ready nodes */
2695895Syz147064 		if (dv->sdev_state != SDEV_READY)
2705895Syz147064 			continue;
2715895Syz147064 
2725895Syz147064 		switch (devnet_validate(dv)) {
2735895Syz147064 		case SDEV_VTOR_VALID:
2745895Syz147064 		case SDEV_VTOR_SKIP:
2755895Syz147064 			continue;
2765895Syz147064 		case SDEV_VTOR_INVALID:
2778023SPhil.Kirk@Sun.COM 		case SDEV_VTOR_STALE:
2785895Syz147064 			sdcmn_err12(("devnet_filldir: destroy invalid "
2795895Syz147064 			    "node: %s(%p)\n", dv->sdev_name, (void *)dv));
2805895Syz147064 			break;
2815895Syz147064 		}
2825895Syz147064 
2835895Syz147064 		if (SDEVTOV(dv)->v_count > 0)
2845895Syz147064 			continue;
2855895Syz147064 		SDEV_HOLD(dv);
2865895Syz147064 		/* remove the cache node */
2875895Syz147064 		(void) sdev_cache_update(ddv, &dv, dv->sdev_name,
2885895Syz147064 		    SDEV_CACHE_DELETE);
2895895Syz147064 	}
2905895Syz147064 
2915895Syz147064 	if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild())
2925895Syz147064 		goto done;
2935895Syz147064 
2945895Syz147064 	if (SDEV_IS_GLOBAL(ddv)) {
2955895Syz147064 		linkid = DATALINK_INVALID_LINKID;
2965895Syz147064 		do {
2975895Syz147064 			linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL,
2985895Syz147064 			    DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE);
299*10616SSebastien.Roy@Sun.COM 			if (linkid != DATALINK_INVALID_LINKID)
300*10616SSebastien.Roy@Sun.COM 				(void) devnet_filldir_datalink(linkid, ddv);
3015895Syz147064 		} while (linkid != DATALINK_INVALID_LINKID);
3025895Syz147064 	} else {
3035895Syz147064 		(void) zone_datalink_walk(getzoneid(),
3045895Syz147064 		    devnet_filldir_datalink, ddv);
3055895Syz147064 	}
3065895Syz147064 
3075895Syz147064 	ddv->sdev_flags &= ~SDEV_BUILD;
3085895Syz147064 
3095895Syz147064 done:
3105895Syz147064 	rw_downgrade(&ddv->sdev_contents);
3115895Syz147064 }
3125895Syz147064 
3135895Syz147064 /*
3145895Syz147064  * Display all instantiated network datalink device nodes.
3155895Syz147064  * A /dev/net entry will be created only after the first lookup of
3165895Syz147064  * the network datalink device succeeds.
3175895Syz147064  */
3185895Syz147064 /*ARGSUSED4*/
3195895Syz147064 static int
devnet_readdir(struct vnode * dvp,struct uio * uiop,struct cred * cred,int * eofp,caller_context_t * ct,int flags)3205895Syz147064 devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred,
3215895Syz147064     int *eofp, caller_context_t *ct, int flags)
3225895Syz147064 {
3235895Syz147064 	struct sdev_node *sdvp = VTOSDEV(dvp);
3245895Syz147064 
3255895Syz147064 	ASSERT(sdvp);
3265895Syz147064 
3275895Syz147064 	if (uiop->uio_offset == 0)
3285895Syz147064 		devnet_filldir(sdvp);
3295895Syz147064 
3305895Syz147064 	return (devname_readdir_func(dvp, uiop, cred, eofp, 0));
3315895Syz147064 }
3325895Syz147064 
3335895Syz147064 /*
3345895Syz147064  * This callback is invoked from devname_inactive_func() to release
3355895Syz147064  * the net entry which was held in devnet_create_rvp().
3365895Syz147064  */
3375895Syz147064 static void
devnet_inactive_callback(struct vnode * dvp)3385895Syz147064 devnet_inactive_callback(struct vnode *dvp)
3395895Syz147064 {
3405895Syz147064 	struct sdev_node *sdvp = VTOSDEV(dvp);
3415895Syz147064 	dls_dl_handle_t ddh;
3425895Syz147064 
3435895Syz147064 	if (dvp->v_type == VDIR)
3445895Syz147064 		return;
3455895Syz147064 
3465895Syz147064 	ASSERT(dvp->v_type == VCHR);
3475895Syz147064 	rw_enter(&sdvp->sdev_contents, RW_WRITER);
3485895Syz147064 	ddh = sdvp->sdev_private;
3495895Syz147064 	sdvp->sdev_private = NULL;
3505895Syz147064 	sdvp->sdev_flags |= SDEV_ATTR_INVALID;
3515895Syz147064 	rw_exit(&sdvp->sdev_contents);
3525895Syz147064 
3535895Syz147064 	/*
3545895Syz147064 	 * "ddh" (sdev_private) could be NULL if devnet_lookup fails.
3555895Syz147064 	 */
3565895Syz147064 	if (ddh != NULL)
3575895Syz147064 		dls_devnet_close(ddh);
3585895Syz147064 }
3595895Syz147064 
3605895Syz147064 /*ARGSUSED*/
3615895Syz147064 static void
devnet_inactive(struct vnode * dvp,struct cred * cred,caller_context_t * ct)3625895Syz147064 devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct)
3635895Syz147064 {
3645895Syz147064 	devname_inactive_func(dvp, cred, devnet_inactive_callback);
3655895Syz147064 }
3665895Syz147064 
3675895Syz147064 /*
3685895Syz147064  * We override lookup and readdir to build entries based on the
3695895Syz147064  * in kernel vanity naming node table.
3705895Syz147064  */
3715895Syz147064 const fs_operation_def_t devnet_vnodeops_tbl[] = {
3725895Syz147064 	VOPNAME_READDIR,	{ .vop_readdir = devnet_readdir },
3735895Syz147064 	VOPNAME_LOOKUP,		{ .vop_lookup = devnet_lookup },
3745895Syz147064 	VOPNAME_INACTIVE,	{ .vop_inactive = devnet_inactive },
3755895Syz147064 	VOPNAME_CREATE,		{ .error = fs_nosys },
3765895Syz147064 	VOPNAME_REMOVE,		{ .error = fs_nosys },
3775895Syz147064 	VOPNAME_MKDIR,		{ .error = fs_nosys },
3785895Syz147064 	VOPNAME_RMDIR,		{ .error = fs_nosys },
3795895Syz147064 	VOPNAME_SYMLINK,	{ .error = fs_nosys },
3805895Syz147064 	VOPNAME_SETSECATTR,	{ .error = fs_nosys },
3815895Syz147064 	NULL,			NULL
3825895Syz147064 };
383