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