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