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 /* 22*2b24ab6bSSebastien 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; 59*2b24ab6bSSebastien Roy zoneid_t zoneid; 60d62bc4baSyz 61d62bc4baSyz ASSERT(!(dv->sdev_flags & SDEV_STALE)); 62d62bc4baSyz ASSERT(dv->sdev_state == SDEV_READY); 63d62bc4baSyz 64*2b24ab6bSSebastien Roy if (dls_mgmt_get_linkid(dv->sdev_name, &linkid) != 0) 65*2b24ab6bSSebastien Roy return (SDEV_VTOR_INVALID); 66*2b24ab6bSSebastien Roy if (SDEV_IS_GLOBAL(dv)) 67*2b24ab6bSSebastien Roy return (SDEV_VTOR_VALID); 68*2b24ab6bSSebastien Roy zoneid = getzoneid(); 69*2b24ab6bSSebastien Roy return (zone_check_datalink(&zoneid, linkid) == 0 ? 70*2b24ab6bSSebastien Roy SDEV_VTOR_VALID : SDEV_VTOR_INVALID); 71d62bc4baSyz } 72d62bc4baSyz 73d62bc4baSyz /* 74d62bc4baSyz * This callback is invoked from devname_lookup_func() to create 75d62bc4baSyz * a net entry when the node is not found in the cache. 76d62bc4baSyz */ 77d62bc4baSyz static int 78d62bc4baSyz devnet_create_rvp(const char *nm, struct vattr *vap, dls_dl_handle_t *ddhp) 79d62bc4baSyz { 80d62bc4baSyz timestruc_t now; 81d62bc4baSyz dev_t dev; 82d62bc4baSyz int error; 83d62bc4baSyz 84d62bc4baSyz if ((error = dls_devnet_open(nm, ddhp, &dev)) != 0) { 85d62bc4baSyz sdcmn_err12(("devnet_create_rvp: not a valid vanity name " 86d62bc4baSyz "network node: %s\n", nm)); 87d62bc4baSyz return (error); 88d62bc4baSyz } 89d62bc4baSyz 90d62bc4baSyz /* 91d62bc4baSyz * This is a valid network device (at least at this point in time). 92d62bc4baSyz * Create the node by setting the attribute; the rest is taken care 93d62bc4baSyz * of by devname_lookup_func(). 94d62bc4baSyz */ 95d62bc4baSyz *vap = sdev_vattr_chr; 96d62bc4baSyz vap->va_mode |= 0666; 97d62bc4baSyz vap->va_rdev = dev; 98d62bc4baSyz 99d62bc4baSyz gethrestime(&now); 100d62bc4baSyz vap->va_atime = now; 101d62bc4baSyz vap->va_mtime = now; 102d62bc4baSyz vap->va_ctime = now; 103d62bc4baSyz return (0); 104d62bc4baSyz } 105d62bc4baSyz 106d62bc4baSyz /* 107d62bc4baSyz * Lookup for /dev/net directory 108d62bc4baSyz * If the entry does not exist, the devnet_create_rvp() callback 109d62bc4baSyz * is invoked to create it. Nodes do not persist across reboot. 110d62bc4baSyz */ 111d62bc4baSyz /*ARGSUSED3*/ 112d62bc4baSyz static int 113d62bc4baSyz devnet_lookup(struct vnode *dvp, char *nm, struct vnode **vpp, 114d62bc4baSyz struct pathname *pnp, int flags, struct vnode *rdir, struct cred *cred, 115d62bc4baSyz caller_context_t *ct, int *direntflags, pathname_t *realpnp) 116d62bc4baSyz { 117d62bc4baSyz struct sdev_node *ddv = VTOSDEV(dvp); 118d62bc4baSyz struct sdev_node *dv = NULL; 119d62bc4baSyz dls_dl_handle_t ddh = NULL; 120d62bc4baSyz struct vattr vattr; 121d62bc4baSyz int nmlen; 122d62bc4baSyz int error = ENOENT; 123d62bc4baSyz 124d62bc4baSyz if (SDEVTOV(ddv)->v_type != VDIR) 125d62bc4baSyz return (ENOTDIR); 126d62bc4baSyz 127d62bc4baSyz /* 128d62bc4baSyz * Empty name or ., return node itself. 129d62bc4baSyz */ 130d62bc4baSyz nmlen = strlen(nm); 131d62bc4baSyz if ((nmlen == 0) || ((nmlen == 1) && (nm[0] == '.'))) { 132d62bc4baSyz *vpp = SDEVTOV(ddv); 133d62bc4baSyz VN_HOLD(*vpp); 134d62bc4baSyz return (0); 135d62bc4baSyz } 136d62bc4baSyz 137d62bc4baSyz /* 138d62bc4baSyz * .., return the parent directory 139d62bc4baSyz */ 140d62bc4baSyz if ((nmlen == 2) && (strcmp(nm, "..") == 0)) { 141d62bc4baSyz *vpp = SDEVTOV(ddv->sdev_dotdot); 142d62bc4baSyz VN_HOLD(*vpp); 143d62bc4baSyz return (0); 144d62bc4baSyz } 145d62bc4baSyz 146d62bc4baSyz rw_enter(&ddv->sdev_contents, RW_WRITER); 147d62bc4baSyz 148d62bc4baSyz /* 149d62bc4baSyz * directory cache lookup: 150d62bc4baSyz */ 151d62bc4baSyz if ((dv = sdev_cache_lookup(ddv, nm)) != NULL) { 152d62bc4baSyz if (dv->sdev_state == SDEV_READY) { 153d62bc4baSyz if (!(dv->sdev_flags & SDEV_ATTR_INVALID)) 154d62bc4baSyz goto found; 155d62bc4baSyz } else { 156d62bc4baSyz ASSERT(dv->sdev_state == SDEV_ZOMBIE); 157d62bc4baSyz goto failed; 158d62bc4baSyz } 159d62bc4baSyz } 160d62bc4baSyz 161d62bc4baSyz /* 162d62bc4baSyz * ZOMBIED parent does not allow new node creation, bail out early. 163d62bc4baSyz */ 164d62bc4baSyz if (ddv->sdev_state == SDEV_ZOMBIE) 165d62bc4baSyz goto failed; 166d62bc4baSyz 167d62bc4baSyz error = devnet_create_rvp(nm, &vattr, &ddh); 168d62bc4baSyz if (error != 0) 169d62bc4baSyz goto failed; 170d62bc4baSyz 171d62bc4baSyz error = sdev_mknode(ddv, nm, &dv, &vattr, NULL, NULL, cred, SDEV_READY); 172d62bc4baSyz if (error != 0) { 173d62bc4baSyz ASSERT(dv == NULL); 174d62bc4baSyz dls_devnet_close(ddh); 175d62bc4baSyz goto failed; 176d62bc4baSyz } 177d62bc4baSyz 178d62bc4baSyz ASSERT(dv != NULL); 179d62bc4baSyz 180d62bc4baSyz rw_enter(&dv->sdev_contents, RW_WRITER); 181d62bc4baSyz if (dv->sdev_flags & SDEV_ATTR_INVALID) { 182d62bc4baSyz /* 183d62bc4baSyz * SDEV_ATTR_INVALID means that this device has been 184d62bc4baSyz * detached, and its dev_t might've been changed too. 185d62bc4baSyz * Therefore, sdev_node's 'vattr' needs to be updated. 186d62bc4baSyz */ 187d62bc4baSyz SDEVTOV(dv)->v_rdev = vattr.va_rdev; 188d62bc4baSyz ASSERT(dv->sdev_attr != NULL); 189d62bc4baSyz dv->sdev_attr->va_rdev = vattr.va_rdev; 190d62bc4baSyz dv->sdev_flags &= ~SDEV_ATTR_INVALID; 191d62bc4baSyz } 192d62bc4baSyz ASSERT(dv->sdev_private == NULL); 193d62bc4baSyz dv->sdev_private = ddh; 194d62bc4baSyz rw_exit(&dv->sdev_contents); 195d62bc4baSyz 196d62bc4baSyz found: 197d62bc4baSyz ASSERT(SDEV_HELD(dv)); 198d62bc4baSyz rw_exit(&ddv->sdev_contents); 199d62bc4baSyz return (sdev_to_vp(dv, vpp)); 200d62bc4baSyz 201d62bc4baSyz failed: 202d62bc4baSyz rw_exit(&ddv->sdev_contents); 203d62bc4baSyz 204d62bc4baSyz if (dv != NULL) 205d62bc4baSyz SDEV_RELE(dv); 206d62bc4baSyz 207d62bc4baSyz *vpp = NULL; 208d62bc4baSyz return (error); 209d62bc4baSyz } 210d62bc4baSyz 211d62bc4baSyz static int 212*2b24ab6bSSebastien Roy devnet_filldir_datalink(datalink_id_t linkid, void *arg) 213d62bc4baSyz { 214*2b24ab6bSSebastien Roy struct sdev_node *ddv = arg; 215*2b24ab6bSSebastien Roy struct vattr vattr; 216*2b24ab6bSSebastien Roy struct sdev_node *dv; 217*2b24ab6bSSebastien Roy dls_dl_handle_t ddh = NULL; 218*2b24ab6bSSebastien Roy char link[MAXLINKNAMELEN]; 219d62bc4baSyz 220d62bc4baSyz ASSERT(RW_WRITE_HELD(&ddv->sdev_contents)); 221*2b24ab6bSSebastien Roy 222*2b24ab6bSSebastien Roy if (dls_mgmt_get_linkinfo(linkid, link, NULL, NULL, NULL) != 0) 223*2b24ab6bSSebastien Roy return (0); 224*2b24ab6bSSebastien Roy 225d62bc4baSyz if ((dv = sdev_cache_lookup(ddv, (char *)link)) != NULL) 226d62bc4baSyz goto found; 227d62bc4baSyz 228d62bc4baSyz if (devnet_create_rvp(link, &vattr, &ddh) != 0) 229d62bc4baSyz return (0); 230d62bc4baSyz 231d62bc4baSyz ASSERT(ddh != NULL); 232d62bc4baSyz dls_devnet_close(ddh); 233d62bc4baSyz 234d62bc4baSyz if (sdev_mknode(ddv, (char *)link, &dv, &vattr, NULL, NULL, kcred, 235d62bc4baSyz SDEV_READY) != 0) { 236d62bc4baSyz return (0); 237d62bc4baSyz } 238d62bc4baSyz 239d62bc4baSyz /* 240d62bc4baSyz * As there is no reference holding the network device, it could be 241d62bc4baSyz * detached. Set SDEV_ATTR_INVALID so that the 'vattr' will be updated 242d62bc4baSyz * later. 243d62bc4baSyz */ 244d62bc4baSyz rw_enter(&dv->sdev_contents, RW_WRITER); 245d62bc4baSyz dv->sdev_flags |= SDEV_ATTR_INVALID; 246d62bc4baSyz rw_exit(&dv->sdev_contents); 247d62bc4baSyz 248d62bc4baSyz found: 249d62bc4baSyz SDEV_SIMPLE_RELE(dv); 250d62bc4baSyz return (0); 251d62bc4baSyz } 252d62bc4baSyz 253d62bc4baSyz static void 254d62bc4baSyz devnet_filldir(struct sdev_node *ddv) 255d62bc4baSyz { 256d62bc4baSyz sdev_node_t *dv, *next; 257d62bc4baSyz datalink_id_t linkid; 258d62bc4baSyz 259d62bc4baSyz ASSERT(RW_READ_HELD(&ddv->sdev_contents)); 260d62bc4baSyz if (rw_tryupgrade(&ddv->sdev_contents) == NULL) { 261d62bc4baSyz rw_exit(&ddv->sdev_contents); 262d62bc4baSyz rw_enter(&ddv->sdev_contents, RW_WRITER); 263d62bc4baSyz } 264d62bc4baSyz 265aac43a5fSjg for (dv = SDEV_FIRST_ENTRY(ddv); dv; dv = next) { 266aac43a5fSjg next = SDEV_NEXT_ENTRY(ddv, dv); 267d62bc4baSyz 268d62bc4baSyz /* validate and prune only ready nodes */ 269d62bc4baSyz if (dv->sdev_state != SDEV_READY) 270d62bc4baSyz continue; 271d62bc4baSyz 272d62bc4baSyz switch (devnet_validate(dv)) { 273d62bc4baSyz case SDEV_VTOR_VALID: 274d62bc4baSyz case SDEV_VTOR_SKIP: 275d62bc4baSyz continue; 276d62bc4baSyz case SDEV_VTOR_INVALID: 277b127ac41SPhilip Kirk case SDEV_VTOR_STALE: 278d62bc4baSyz sdcmn_err12(("devnet_filldir: destroy invalid " 279d62bc4baSyz "node: %s(%p)\n", dv->sdev_name, (void *)dv)); 280d62bc4baSyz break; 281d62bc4baSyz } 282d62bc4baSyz 283d62bc4baSyz if (SDEVTOV(dv)->v_count > 0) 284d62bc4baSyz continue; 285d62bc4baSyz SDEV_HOLD(dv); 286d62bc4baSyz /* remove the cache node */ 287d62bc4baSyz (void) sdev_cache_update(ddv, &dv, dv->sdev_name, 288d62bc4baSyz SDEV_CACHE_DELETE); 289d62bc4baSyz } 290d62bc4baSyz 291d62bc4baSyz if (((ddv->sdev_flags & SDEV_BUILD) == 0) && !dls_devnet_rebuild()) 292d62bc4baSyz goto done; 293d62bc4baSyz 294d62bc4baSyz if (SDEV_IS_GLOBAL(ddv)) { 295d62bc4baSyz linkid = DATALINK_INVALID_LINKID; 296d62bc4baSyz do { 297d62bc4baSyz linkid = dls_mgmt_get_next(linkid, DATALINK_CLASS_ALL, 298d62bc4baSyz DATALINK_ANY_MEDIATYPE, DLMGMT_ACTIVE); 299*2b24ab6bSSebastien Roy if (linkid != DATALINK_INVALID_LINKID) 300*2b24ab6bSSebastien Roy (void) devnet_filldir_datalink(linkid, ddv); 301d62bc4baSyz } while (linkid != DATALINK_INVALID_LINKID); 302d62bc4baSyz } else { 303d62bc4baSyz (void) zone_datalink_walk(getzoneid(), 304d62bc4baSyz devnet_filldir_datalink, ddv); 305d62bc4baSyz } 306d62bc4baSyz 307d62bc4baSyz ddv->sdev_flags &= ~SDEV_BUILD; 308d62bc4baSyz 309d62bc4baSyz done: 310d62bc4baSyz rw_downgrade(&ddv->sdev_contents); 311d62bc4baSyz } 312d62bc4baSyz 313d62bc4baSyz /* 314d62bc4baSyz * Display all instantiated network datalink device nodes. 315d62bc4baSyz * A /dev/net entry will be created only after the first lookup of 316d62bc4baSyz * the network datalink device succeeds. 317d62bc4baSyz */ 318d62bc4baSyz /*ARGSUSED4*/ 319d62bc4baSyz static int 320d62bc4baSyz devnet_readdir(struct vnode *dvp, struct uio *uiop, struct cred *cred, 321d62bc4baSyz int *eofp, caller_context_t *ct, int flags) 322d62bc4baSyz { 323d62bc4baSyz struct sdev_node *sdvp = VTOSDEV(dvp); 324d62bc4baSyz 325d62bc4baSyz ASSERT(sdvp); 326d62bc4baSyz 327d62bc4baSyz if (uiop->uio_offset == 0) 328d62bc4baSyz devnet_filldir(sdvp); 329d62bc4baSyz 330d62bc4baSyz return (devname_readdir_func(dvp, uiop, cred, eofp, 0)); 331d62bc4baSyz } 332d62bc4baSyz 333d62bc4baSyz /* 334d62bc4baSyz * This callback is invoked from devname_inactive_func() to release 335d62bc4baSyz * the net entry which was held in devnet_create_rvp(). 336d62bc4baSyz */ 337d62bc4baSyz static void 338d62bc4baSyz devnet_inactive_callback(struct vnode *dvp) 339d62bc4baSyz { 340d62bc4baSyz struct sdev_node *sdvp = VTOSDEV(dvp); 341d62bc4baSyz dls_dl_handle_t ddh; 342d62bc4baSyz 343d62bc4baSyz if (dvp->v_type == VDIR) 344d62bc4baSyz return; 345d62bc4baSyz 346d62bc4baSyz ASSERT(dvp->v_type == VCHR); 347d62bc4baSyz rw_enter(&sdvp->sdev_contents, RW_WRITER); 348d62bc4baSyz ddh = sdvp->sdev_private; 349d62bc4baSyz sdvp->sdev_private = NULL; 350d62bc4baSyz sdvp->sdev_flags |= SDEV_ATTR_INVALID; 351d62bc4baSyz rw_exit(&sdvp->sdev_contents); 352d62bc4baSyz 353d62bc4baSyz /* 354d62bc4baSyz * "ddh" (sdev_private) could be NULL if devnet_lookup fails. 355d62bc4baSyz */ 356d62bc4baSyz if (ddh != NULL) 357d62bc4baSyz dls_devnet_close(ddh); 358d62bc4baSyz } 359d62bc4baSyz 360d62bc4baSyz /*ARGSUSED*/ 361d62bc4baSyz static void 362d62bc4baSyz devnet_inactive(struct vnode *dvp, struct cred *cred, caller_context_t *ct) 363d62bc4baSyz { 364d62bc4baSyz devname_inactive_func(dvp, cred, devnet_inactive_callback); 365d62bc4baSyz } 366d62bc4baSyz 367d62bc4baSyz /* 368d62bc4baSyz * We override lookup and readdir to build entries based on the 369d62bc4baSyz * in kernel vanity naming node table. 370d62bc4baSyz */ 371d62bc4baSyz const fs_operation_def_t devnet_vnodeops_tbl[] = { 372d62bc4baSyz VOPNAME_READDIR, { .vop_readdir = devnet_readdir }, 373d62bc4baSyz VOPNAME_LOOKUP, { .vop_lookup = devnet_lookup }, 374d62bc4baSyz VOPNAME_INACTIVE, { .vop_inactive = devnet_inactive }, 375d62bc4baSyz VOPNAME_CREATE, { .error = fs_nosys }, 376d62bc4baSyz VOPNAME_REMOVE, { .error = fs_nosys }, 377d62bc4baSyz VOPNAME_MKDIR, { .error = fs_nosys }, 378d62bc4baSyz VOPNAME_RMDIR, { .error = fs_nosys }, 379d62bc4baSyz VOPNAME_SYMLINK, { .error = fs_nosys }, 380d62bc4baSyz VOPNAME_SETSECATTR, { .error = fs_nosys }, 381d62bc4baSyz NULL, NULL 382d62bc4baSyz }; 383