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