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