1 /* $NetBSD: puffs_node.c,v 1.6 2007/10/11 19:41:13 pooka Exp $ */ 2 3 /* 4 * Copyright (c) 2005, 2006, 2007 Antti Kantee. All Rights Reserved. 5 * 6 * Development of this software was supported by the 7 * Google Summer of Code program, the Ulla Tuominen Foundation 8 * and the Finnish Cultural Foundation. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 20 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 25 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: puffs_node.c,v 1.6 2007/10/11 19:41:13 pooka Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/hash.h> 37 #include <sys/kmem.h> 38 #include <sys/malloc.h> 39 #include <sys/mount.h> 40 #include <sys/namei.h> 41 #include <sys/vnode.h> 42 43 #include <fs/puffs/puffs_msgif.h> 44 #include <fs/puffs/puffs_sys.h> 45 46 #include <miscfs/genfs/genfs_node.h> 47 #include <miscfs/specfs/specdev.h> 48 49 static const struct genfs_ops puffs_genfsops = { 50 .gop_size = puffs_gop_size, 51 .gop_write = genfs_gop_write, 52 .gop_markupdate = puffs_gop_markupdate, 53 #if 0 54 .gop_alloc, should ask userspace 55 #endif 56 }; 57 58 static __inline struct puffs_node_hashlist 59 *puffs_cookie2hashlist(struct puffs_mount *, void *); 60 static struct puffs_node *puffs_cookie2pnode(struct puffs_mount *, void *); 61 62 struct pool puffs_pnpool; 63 64 /* 65 * Grab a vnode, intialize all the puffs-dependant stuff. 66 */ 67 int 68 puffs_getvnode(struct mount *mp, void *cookie, enum vtype type, 69 voff_t vsize, dev_t rdev, struct vnode **vpp) 70 { 71 struct puffs_mount *pmp; 72 struct puffs_newcookie *pnc; 73 struct vnode *vp, *nvp; 74 struct puffs_node *pnode; 75 struct puffs_node_hashlist *plist; 76 int error; 77 78 pmp = MPTOPUFFSMP(mp); 79 80 error = EPROTO; 81 if (type <= VNON || type >= VBAD) { 82 puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EINVAL, 83 "bad node type", cookie); 84 goto bad; 85 } 86 if (vsize == VSIZENOTSET) { 87 puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EINVAL, 88 "VSIZENOTSET is not a valid size", cookie); 89 goto bad; 90 } 91 92 /* 93 * XXX: there is a deadlock condition between vfs_busy() and 94 * vnode locks. For an unmounting file system the mountpoint 95 * is frozen, but in unmount(FORCE) vflush() wants to access all 96 * of the vnodes. If we are here waiting for the mountpoint 97 * lock while holding on to a vnode lock, well, we ain't 98 * just pining for the fjords anymore. If we release the 99 * vnode lock, we will be in the situation "mount point 100 * is dying" and panic() will ensue in insmntque. So as a 101 * temporary workaround, get a vnode without putting it on 102 * the mount point list, check if mount point is still alive 103 * and kicking and only then add the vnode to the list. 104 */ 105 error = getnewvnode(VT_PUFFS, NULL, puffs_vnodeop_p, &vp); 106 if (error) 107 goto bad; 108 vp->v_vnlock = NULL; 109 vp->v_type = type; 110 111 /* 112 * Check what mount point isn't going away. This will work 113 * until we decide to remove biglock or make the kernel 114 * preemptive. But hopefully the real problem will be fixed 115 * by then. 116 * 117 * XXX: yes, should call vfs_busy(), but thar be rabbits with 118 * vicious streaks a mile wide ... 119 * 120 * XXX: there is a transient failure here: if someone is unmounting 121 * the file system but doesn't succeed (due to it being busy), 122 * we incorrectly fail new vnode allocation. This is *very* 123 * hard to fix with the current structure of file system unmounting. 124 */ 125 if (mp->mnt_iflag & IMNT_UNMOUNT) { 126 DPRINTF(("puffs_getvnode: mp %p unmount, unable to create " 127 "vnode for cookie %p\n", mp, cookie)); 128 ungetnewvnode(vp); 129 error = ENXIO; 130 goto bad; 131 } 132 133 /* 134 * Creation should not fail after this point. Or if it does, 135 * care must be taken so that VOP_INACTIVE() isn't called. 136 */ 137 138 /* So mp is not dead yet.. good.. inform new vnode of its master */ 139 simple_lock(&mntvnode_slock); 140 TAILQ_INSERT_TAIL(&mp->mnt_vnodelist, vp, v_mntvnodes); 141 simple_unlock(&mntvnode_slock); 142 vp->v_mount = mp; 143 144 /* 145 * clerical tasks & footwork 146 */ 147 148 /* default size */ 149 uvm_vnp_setsize(vp, 0); 150 151 /* dances based on vnode type. almost ufs_vinit(), but not quite */ 152 switch (type) { 153 case VCHR: 154 case VBLK: 155 /* 156 * replace vnode operation vector with the specops vector. 157 * our user server has very little control over the node 158 * if it decides its a character or block special file 159 */ 160 vp->v_op = puffs_specop_p; 161 162 /* do the standard checkalias-dance */ 163 if ((nvp = checkalias(vp, rdev, mp)) != NULL) { 164 /* 165 * found: release & unallocate aliased 166 * old (well, actually, new) node 167 */ 168 vp->v_op = spec_vnodeop_p; 169 vp->v_vflag &= ~VV_LOCKSWORK; 170 vrele(vp); 171 vgone(vp); /* cya */ 172 173 /* init "new" vnode */ 174 vp = nvp; 175 vp->v_vnlock = NULL; 176 vp->v_mount = mp; 177 } 178 break; 179 180 case VFIFO: 181 vp->v_op = puffs_fifoop_p; 182 break; 183 184 case VREG: 185 uvm_vnp_setsize(vp, vsize); 186 break; 187 188 case VDIR: 189 case VLNK: 190 case VSOCK: 191 break; 192 default: 193 panic("puffs_getvnode: invalid vtype %d", type); 194 } 195 196 pnode = pool_get(&puffs_pnpool, PR_WAITOK); 197 memset(pnode, 0, sizeof(struct puffs_node)); 198 199 pnode->pn_cookie = cookie; 200 pnode->pn_refcount = 1; 201 202 /* insert cookie on list, take off of interlock list */ 203 mutex_init(&pnode->pn_mtx, MUTEX_DEFAULT, IPL_NONE); 204 SLIST_INIT(&pnode->pn_sel.sel_klist); 205 plist = puffs_cookie2hashlist(pmp, cookie); 206 mutex_enter(&pmp->pmp_lock); 207 LIST_INSERT_HEAD(plist, pnode, pn_hashent); 208 if (cookie != pmp->pmp_root_cookie) { 209 LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) { 210 if (pnc->pnc_cookie == cookie) { 211 LIST_REMOVE(pnc, pnc_entries); 212 kmem_free(pnc, sizeof(struct puffs_newcookie)); 213 break; 214 } 215 } 216 KASSERT(pnc != NULL); 217 } 218 mutex_exit(&pmp->pmp_lock); 219 220 vp->v_data = pnode; 221 vp->v_type = type; 222 pnode->pn_vp = vp; 223 pnode->pn_serversize = vsize; 224 225 genfs_node_init(vp, &puffs_genfsops); 226 *vpp = vp; 227 228 DPRINTF(("new vnode at %p, pnode %p, cookie %p\n", vp, 229 pnode, pnode->pn_cookie)); 230 231 return 0; 232 233 bad: 234 /* remove staging cookie from list */ 235 if (cookie != pmp->pmp_root_cookie) { 236 mutex_enter(&pmp->pmp_lock); 237 LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) { 238 if (pnc->pnc_cookie == cookie) { 239 LIST_REMOVE(pnc, pnc_entries); 240 kmem_free(pnc, sizeof(struct puffs_newcookie)); 241 break; 242 } 243 } 244 KASSERT(pnc != NULL); 245 mutex_exit(&pmp->pmp_lock); 246 } 247 248 return error; 249 } 250 251 /* new node creating for creative vop ops (create, symlink, mkdir, mknod) */ 252 int 253 puffs_newnode(struct mount *mp, struct vnode *dvp, struct vnode **vpp, 254 void *cookie, struct componentname *cnp, enum vtype type, dev_t rdev) 255 { 256 struct puffs_mount *pmp = MPTOPUFFSMP(mp); 257 struct puffs_newcookie *pnc; 258 struct vnode *vp; 259 int error; 260 261 /* userspace probably has this as a NULL op */ 262 if (cookie == NULL) { 263 error = EOPNOTSUPP; 264 return error; 265 } 266 267 /* 268 * Check for previous node with the same designation. 269 * Explicitly check the root node cookie, since it might be 270 * reclaimed from the kernel when this check is made. 271 */ 272 mutex_enter(&pmp->pmp_lock); 273 if (cookie == pmp->pmp_root_cookie 274 || puffs_cookie2pnode(pmp, cookie) != NULL) { 275 mutex_exit(&pmp->pmp_lock); 276 puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EEXIST, 277 "cookie exists", cookie); 278 return EPROTO; 279 } 280 281 LIST_FOREACH(pnc, &pmp->pmp_newcookie, pnc_entries) { 282 if (pnc->pnc_cookie == cookie) { 283 mutex_exit(&pmp->pmp_lock); 284 puffs_msg_errnotify(pmp, PUFFS_ERR_MAKENODE, EEXIST, 285 "cookie exists", cookie); 286 return EPROTO; 287 } 288 } 289 pnc = kmem_alloc(sizeof(struct puffs_newcookie), KM_SLEEP); 290 pnc->pnc_cookie = cookie; 291 LIST_INSERT_HEAD(&pmp->pmp_newcookie, pnc, pnc_entries); 292 mutex_exit(&pmp->pmp_lock); 293 294 error = puffs_getvnode(dvp->v_mount, cookie, type, 0, rdev, &vp); 295 if (error) 296 return error; 297 298 vp->v_type = type; 299 vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 300 *vpp = vp; 301 302 if ((cnp->cn_flags & MAKEENTRY) && PUFFS_USE_NAMECACHE(pmp)) 303 cache_enter(dvp, vp, cnp); 304 305 return 0; 306 } 307 308 void 309 puffs_putvnode(struct vnode *vp) 310 { 311 struct puffs_mount *pmp; 312 struct puffs_node *pnode; 313 314 pmp = VPTOPUFFSMP(vp); 315 pnode = VPTOPP(vp); 316 317 #ifdef DIAGNOSTIC 318 if (vp->v_tag != VT_PUFFS) 319 panic("puffs_putvnode: %p not a puffs vnode", vp); 320 #endif 321 322 LIST_REMOVE(pnode, pn_hashent); 323 genfs_node_destroy(vp); 324 puffs_releasenode(pnode); 325 vp->v_data = NULL; 326 327 return; 328 } 329 330 static __inline struct puffs_node_hashlist * 331 puffs_cookie2hashlist(struct puffs_mount *pmp, void *cookie) 332 { 333 uint32_t hash; 334 335 hash = hash32_buf(&cookie, sizeof(void *), HASH32_BUF_INIT); 336 return &pmp->pmp_pnodehash[hash % pmp->pmp_npnodehash]; 337 } 338 339 /* 340 * Translate cookie to puffs_node. Caller must hold pmp_lock 341 * and it will be held upon return. 342 */ 343 static struct puffs_node * 344 puffs_cookie2pnode(struct puffs_mount *pmp, void *cookie) 345 { 346 struct puffs_node_hashlist *plist; 347 struct puffs_node *pnode; 348 349 plist = puffs_cookie2hashlist(pmp, cookie); 350 LIST_FOREACH(pnode, plist, pn_hashent) { 351 if (pnode->pn_cookie == cookie) 352 break; 353 } 354 355 return pnode; 356 } 357 358 /* 359 * Make sure root vnode exists and reference it. Does NOT lock. 360 */ 361 static int 362 puffs_makeroot(struct puffs_mount *pmp) 363 { 364 struct vnode *vp; 365 int rv; 366 367 /* 368 * pmp_lock must be held if vref()'ing or vrele()'ing the 369 * root vnode. the latter is controlled by puffs_inactive(). 370 * 371 * pmp_root is set here and cleared in puffs_reclaim(). 372 */ 373 retry: 374 mutex_enter(&pmp->pmp_lock); 375 vp = pmp->pmp_root; 376 if (vp) { 377 simple_lock(&vp->v_interlock); 378 mutex_exit(&pmp->pmp_lock); 379 if (vget(vp, LK_INTERLOCK) == 0) 380 return 0; 381 } else 382 mutex_exit(&pmp->pmp_lock); 383 384 /* 385 * So, didn't have the magic root vnode available. 386 * No matter, grab another and stuff it with the cookie. 387 */ 388 if ((rv = puffs_getvnode(pmp->pmp_mp, pmp->pmp_root_cookie, 389 pmp->pmp_root_vtype, pmp->pmp_root_vsize, pmp->pmp_root_rdev, &vp))) 390 return rv; 391 392 /* 393 * Someone magically managed to race us into puffs_getvnode? 394 * Put our previous new vnode back and retry. 395 */ 396 mutex_enter(&pmp->pmp_lock); 397 if (pmp->pmp_root) { 398 mutex_exit(&pmp->pmp_lock); 399 puffs_putvnode(vp); 400 goto retry; 401 } 402 403 /* store cache */ 404 vp->v_vflag |= VV_ROOT; 405 pmp->pmp_root = vp; 406 mutex_exit(&pmp->pmp_lock); 407 408 return 0; 409 } 410 411 /* 412 * Locate the in-kernel vnode based on the cookie received given 413 * from userspace. Returns a vnode, if found, NULL otherwise. 414 * The parameter "lock" control whether to lock the possible or 415 * not. Locking always might cause us to lock against ourselves 416 * in situations where we want the vnode but don't care for the 417 * vnode lock, e.g. file server issued putpages. 418 */ 419 int 420 puffs_cookie2vnode(struct puffs_mount *pmp, void *cookie, int lock, 421 int willcreate, struct vnode **vpp) 422 { 423 struct puffs_node *pnode; 424 struct puffs_newcookie *pnc; 425 struct vnode *vp; 426 int vgetflags, rv; 427 428 /* 429 * Handle root in a special manner, since we want to make sure 430 * pmp_root is properly set. 431 */ 432 if (cookie == pmp->pmp_root_cookie) { 433 if ((rv = puffs_makeroot(pmp))) 434 return rv; 435 if (lock) 436 vn_lock(pmp->pmp_root, LK_EXCLUSIVE | LK_RETRY); 437 438 *vpp = pmp->pmp_root; 439 return 0; 440 } 441 442 mutex_enter(&pmp->pmp_lock); 443 pnode = puffs_cookie2pnode(pmp, cookie); 444 if (pnode == NULL) { 445 if (willcreate) { 446 pnc = kmem_alloc(sizeof(struct puffs_newcookie), 447 KM_SLEEP); 448 pnc->pnc_cookie = cookie; 449 LIST_INSERT_HEAD(&pmp->pmp_newcookie, pnc, pnc_entries); 450 } 451 mutex_exit(&pmp->pmp_lock); 452 return PUFFS_NOSUCHCOOKIE; 453 } 454 vp = pnode->pn_vp; 455 simple_lock(&vp->v_interlock); 456 mutex_exit(&pmp->pmp_lock); 457 458 vgetflags = LK_INTERLOCK; 459 if (lock) 460 vgetflags |= LK_EXCLUSIVE | LK_RETRY; 461 if ((rv = vget(vp, vgetflags))) 462 return rv; 463 464 *vpp = vp; 465 return 0; 466 } 467 468 void 469 puffs_updatenode(struct vnode *vp, int flags) 470 { 471 struct puffs_node *pn; 472 struct timespec ts; 473 474 if (flags == 0) 475 return; 476 477 pn = VPTOPP(vp); 478 nanotime(&ts); 479 480 if (flags & PUFFS_UPDATEATIME) { 481 pn->pn_mc_atime = ts; 482 pn->pn_stat |= PNODE_METACACHE_ATIME; 483 } 484 if (flags & PUFFS_UPDATECTIME) { 485 pn->pn_mc_ctime = ts; 486 pn->pn_stat |= PNODE_METACACHE_CTIME; 487 } 488 if (flags & PUFFS_UPDATEMTIME) { 489 pn->pn_mc_mtime = ts; 490 pn->pn_stat |= PNODE_METACACHE_MTIME; 491 } 492 if (flags & PUFFS_UPDATESIZE) { 493 pn->pn_mc_size = vp->v_size; 494 pn->pn_stat |= PNODE_METACACHE_SIZE; 495 } 496 } 497 498 /* 499 * Add reference to node. 500 * mutex held on entry and return 501 */ 502 void 503 puffs_referencenode(struct puffs_node *pn) 504 { 505 506 KASSERT(mutex_owned(&pn->pn_mtx)); 507 pn->pn_refcount++; 508 } 509 510 /* 511 * Release pnode structure which dealing with references to the 512 * puffs_node instead of the vnode. Can't use vref()/vrele() on 513 * the vnode there, since that causes the lovely VOP_INACTIVE(), 514 * which in turn causes the lovely deadlock when called by the one 515 * who is supposed to handle it. 516 */ 517 void 518 puffs_releasenode(struct puffs_node *pn) 519 { 520 521 mutex_enter(&pn->pn_mtx); 522 if (--pn->pn_refcount == 0) { 523 mutex_exit(&pn->pn_mtx); 524 mutex_destroy(&pn->pn_mtx); 525 pool_put(&puffs_pnpool, pn); 526 } else { 527 mutex_exit(&pn->pn_mtx); 528 } 529 } 530