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