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