1 /* $NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $ */ 2 3 /*- 4 * Copyright (c) 2005 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Julio M. Merino Vidal, developed as part of Google's Summer of Code 9 * 2005 program. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* 34 * Efficient memory file system. 35 * 36 * tmpfs is a file system that uses NetBSD's virtual memory sub-system 37 * (the well-known UVM) to store file data and metadata in an efficient 38 * way. This means that it does not follow the structure of an on-disk 39 * file system because it simply does not need to. Instead, it uses 40 * memory-specific data structures and algorithms to automatically 41 * allocate and release resources. 42 */ 43 #include <sys/cdefs.h> 44 #include <sys/conf.h> 45 #include <sys/param.h> 46 #include <sys/limits.h> 47 #include <sys/lock.h> 48 #include <sys/mutex.h> 49 #include <sys/kernel.h> 50 #include <sys/stat.h> 51 #include <sys/systm.h> 52 #include <sys/sysctl.h> 53 #include <sys/objcache.h> 54 55 #include <vm/vm.h> 56 #include <vm/vm_object.h> 57 #include <vm/vm_param.h> 58 59 #include <vfs/tmpfs/tmpfs.h> 60 #include <vfs/tmpfs/tmpfs_vnops.h> 61 #include <vfs/tmpfs/tmpfs_args.h> 62 63 /* 64 * Default permission for root node 65 */ 66 #define TMPFS_DEFAULT_ROOT_MODE (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH) 67 68 MALLOC_DEFINE(M_TMPFSMNT, "tmpfs mount", "tmpfs mount structures"); 69 MALLOC_DEFINE(M_TMPFSNAME, "tmpfs name", "tmpfs file names"); 70 MALLOC_DEFINE(M_TMPFS_DIRENT, "tmpfs dirent", "tmpfs dirent structures"); 71 MALLOC_DEFINE(M_TMPFS_NODE, "tmpfs node", "tmpfs node structures"); 72 73 /* --------------------------------------------------------------------- */ 74 75 static int tmpfs_mount(struct mount *, char *, caddr_t, struct ucred *); 76 static int tmpfs_unmount(struct mount *, int); 77 static int tmpfs_root(struct mount *, struct vnode **); 78 static int tmpfs_fhtovp(struct mount *, struct vnode *, struct fid *, struct vnode **); 79 static int tmpfs_statfs(struct mount *, struct statfs *, struct ucred *cred); 80 81 /* --------------------------------------------------------------------- */ 82 int 83 tmpfs_node_ctor(void *obj, void *privdata, int flags) 84 { 85 struct tmpfs_node *node = (struct tmpfs_node *)obj; 86 87 node->tn_gen++; 88 node->tn_size = 0; 89 node->tn_status = 0; 90 node->tn_flags = 0; 91 node->tn_links = 0; 92 node->tn_vnode = NULL; 93 node->tn_vpstate = TMPFS_VNODE_WANT; 94 bzero(&node->tn_spec, sizeof(node->tn_spec)); 95 96 return (1); 97 } 98 99 static void 100 tmpfs_node_dtor(void *obj, void *privdata) 101 { 102 struct tmpfs_node *node = (struct tmpfs_node *)obj; 103 node->tn_type = VNON; 104 node->tn_vpstate = TMPFS_VNODE_DOOMED; 105 } 106 107 static void* 108 tmpfs_node_init(void *args, int flags) 109 { 110 struct tmpfs_node *node = (struct tmpfs_node *)objcache_malloc_alloc(args, flags); 111 node->tn_id = 0; 112 113 lockinit(&node->tn_interlock, "tmpfs node interlock", 0, LK_CANRECURSE); 114 node->tn_gen = karc4random(); 115 116 return node; 117 } 118 119 static void 120 tmpfs_node_fini(void *obj, void *args) 121 { 122 struct tmpfs_node *node = (struct tmpfs_node *)obj; 123 lockuninit(&node->tn_interlock); 124 objcache_malloc_free(obj, args); 125 } 126 127 struct objcache_malloc_args tmpfs_dirent_pool_malloc_args = 128 { sizeof(struct tmpfs_dirent), M_TMPFS_DIRENT }; 129 struct objcache_malloc_args tmpfs_node_pool_malloc_args = 130 { sizeof(struct tmpfs_node), M_TMPFS_NODE }; 131 132 static int 133 tmpfs_mount(struct mount *mp, char *path, caddr_t data, struct ucred *cred) 134 { 135 struct tmpfs_mount *tmp; 136 struct tmpfs_node *root; 137 struct tmpfs_args args; 138 vm_pindex_t pages; 139 vm_pindex_t pages_limit; 140 ino_t nodes; 141 int error; 142 /* Size counters. */ 143 ino_t nodes_max; 144 off_t size_max; 145 size_t size; 146 147 /* Root node attributes. */ 148 uid_t root_uid = cred->cr_uid; 149 gid_t root_gid = cred->cr_gid; 150 mode_t root_mode = (VREAD | VWRITE); 151 152 if (mp->mnt_flag & MNT_UPDATE) { 153 /* XXX: There is no support yet to update file system 154 * settings. Should be added. */ 155 156 return EOPNOTSUPP; 157 } 158 159 /* 160 * mount info 161 */ 162 bzero(&args, sizeof(args)); 163 size_max = 0; 164 nodes_max = 0; 165 166 if (path) { 167 if (data) { 168 error = copyin(data, &args, sizeof(args)); 169 if (error) 170 return (error); 171 } 172 size_max = args.ta_size_max; 173 nodes_max = args.ta_nodes_max; 174 } 175 176 /* 177 * If mount by non-root, then verify that user has necessary 178 * permissions on the device. 179 */ 180 if (cred->cr_uid != 0) { 181 root_mode = VREAD; 182 if ((mp->mnt_flag & MNT_RDONLY) == 0) 183 root_mode |= VWRITE; 184 } 185 186 pages_limit = vm_swap_max + vmstats.v_page_count / 2; 187 188 if (size_max == 0) 189 pages = pages_limit / 2; 190 else if (size_max < PAGE_SIZE) 191 pages = 1; 192 else if (OFF_TO_IDX(size_max) > pages_limit) 193 pages = pages_limit; 194 else 195 pages = OFF_TO_IDX(size_max); 196 197 if (nodes_max == 0) 198 nodes = 3 + pages * PAGE_SIZE / 1024; 199 else if (nodes_max < 3) 200 nodes = 3; 201 else if (nodes_max > pages) 202 nodes = pages; 203 else 204 nodes = nodes_max; 205 206 /* Allocate the tmpfs mount structure and fill it. */ 207 tmp = kmalloc(sizeof(*tmp), M_TMPFSMNT, M_WAITOK | M_ZERO); 208 209 lockinit(&(tmp->allnode_lock), "tmpfs allnode lock", 0, LK_CANRECURSE); 210 tmp->tm_nodes_max = nodes; 211 tmp->tm_nodes_inuse = 0; 212 tmp->tm_maxfilesize = IDX_TO_OFF(pages_limit); 213 LIST_INIT(&tmp->tm_nodes_used); 214 215 tmp->tm_pages_max = pages; 216 tmp->tm_pages_used = 0; 217 218 tmp->tm_dirent_pool = objcache_create( "tmpfs dirent cache", 219 0, 0, 220 NULL, NULL, NULL, 221 objcache_malloc_alloc, objcache_malloc_free, 222 &tmpfs_dirent_pool_malloc_args); 223 tmp->tm_node_pool = objcache_create( "tmpfs node cache", 224 0, 0, 225 tmpfs_node_ctor, tmpfs_node_dtor, NULL, 226 tmpfs_node_init, tmpfs_node_fini, 227 &tmpfs_node_pool_malloc_args); 228 229 /* Allocate the root node. */ 230 error = tmpfs_alloc_node(tmp, VDIR, root_uid, root_gid, 231 root_mode & ALLPERMS, NULL, NULL, 232 VNOVAL, VNOVAL, &root); 233 234 /* 235 * We are backed by swap, set snocache chflags flag so we 236 * don't trip over swapcache. 237 */ 238 root->tn_flags = SF_NOCACHE; 239 240 if (error != 0 || root == NULL) { 241 objcache_destroy(tmp->tm_node_pool); 242 objcache_destroy(tmp->tm_dirent_pool); 243 kfree(tmp, M_TMPFSMNT); 244 return error; 245 } 246 KASSERT(root->tn_id >= 0, ("tmpfs root with invalid ino: %d", (int)root->tn_id)); 247 tmp->tm_root = root; 248 249 mp->mnt_flag |= MNT_LOCAL; 250 #if 0 251 mp->mnt_kern_flag |= MNTK_RD_MPSAFE | MNTK_WR_MPSAFE | MNTK_GA_MPSAFE | 252 MNTK_IN_MPSAFE | MNTK_SG_MPSAFE; 253 #endif 254 mp->mnt_kern_flag |= MNTK_NOMSYNC; 255 mp->mnt_data = (qaddr_t)tmp; 256 vfs_getnewfsid(mp); 257 258 259 vfs_add_vnodeops(mp, &tmpfs_vnode_vops, &mp->mnt_vn_norm_ops); 260 vfs_add_vnodeops(mp, &tmpfs_fifo_vops, &mp->mnt_vn_fifo_ops); 261 262 copystr("tmpfs", mp->mnt_stat.f_mntfromname, MNAMELEN - 1, &size); 263 bzero(mp->mnt_stat.f_mntfromname +size, MNAMELEN - size); 264 bzero(mp->mnt_stat.f_mntonname, sizeof(mp->mnt_stat.f_mntonname)); 265 copyinstr(path, mp->mnt_stat.f_mntonname, 266 sizeof(mp->mnt_stat.f_mntonname) -1, 267 &size); 268 269 tmpfs_statfs(mp, &mp->mnt_stat, cred); 270 271 return 0; 272 } 273 274 /* --------------------------------------------------------------------- */ 275 276 /* ARGSUSED2 */ 277 static int 278 tmpfs_unmount(struct mount *mp, int mntflags) 279 { 280 int error; 281 int flags = 0; 282 int found; 283 struct tmpfs_mount *tmp; 284 struct tmpfs_node *node; 285 286 /* Handle forced unmounts. */ 287 if (mntflags & MNT_FORCE) 288 flags |= FORCECLOSE; 289 290 tmp = VFS_TO_TMPFS(mp); 291 292 /* 293 * Finalize all pending I/O. In the case of tmpfs we want 294 * to throw all the data away so clean out the buffer cache 295 * and vm objects before calling vflush(). 296 */ 297 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 298 if (node->tn_type == VREG && node->tn_vnode) { 299 ++node->tn_links; 300 TMPFS_NODE_LOCK(node); 301 vx_get(node->tn_vnode); 302 tmpfs_truncate(node->tn_vnode, 0); 303 vx_put(node->tn_vnode); 304 TMPFS_NODE_UNLOCK(node); 305 --node->tn_links; 306 } 307 } 308 error = vflush(mp, 0, flags); 309 if (error != 0) 310 return error; 311 312 /* 313 * First pass get rid of all the directory entries and 314 * vnode associations. The directory structure will 315 * remain via the extra link count representing tn_dir.tn_parent. 316 * 317 * No vnodes should remain after the vflush above. 318 */ 319 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 320 ++node->tn_links; 321 TMPFS_NODE_LOCK(node); 322 if (node->tn_type == VDIR) { 323 struct tmpfs_dirent *de; 324 325 while (!TAILQ_EMPTY(&node->tn_dir.tn_dirhead)) { 326 de = TAILQ_FIRST(&node->tn_dir.tn_dirhead); 327 tmpfs_dir_detach(node, de); 328 tmpfs_free_dirent(tmp, de); 329 node->tn_size -= sizeof(struct tmpfs_dirent); 330 } 331 } 332 KKASSERT(node->tn_vnode == NULL); 333 #if 0 334 vp = node->tn_vnode; 335 if (vp != NULL) { 336 tmpfs_free_vp(vp); 337 vrecycle(vp); 338 node->tn_vnode = NULL; 339 } 340 #endif 341 TMPFS_NODE_UNLOCK(node); 342 --node->tn_links; 343 } 344 345 /* 346 * Now get rid of all nodes. We can remove any node with a 347 * link count of 0 or any directory node with a link count of 348 * 1. The parents will not be destroyed until all their children 349 * have been destroyed. 350 * 351 * Recursion in tmpfs_free_node() can further modify the list so 352 * we cannot use a next pointer here. 353 * 354 * The root node will be destroyed by this loop (it will be last). 355 */ 356 while (!LIST_EMPTY(&tmp->tm_nodes_used)) { 357 found = 0; 358 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 359 if (node->tn_links == 0 || 360 (node->tn_links == 1 && node->tn_type == VDIR)) { 361 TMPFS_NODE_LOCK(node); 362 tmpfs_free_node(tmp, node); 363 /* eats lock */ 364 found = 1; 365 break; 366 } 367 } 368 if (found == 0) { 369 kprintf("tmpfs: Cannot free entire node tree!"); 370 break; 371 } 372 } 373 374 KKASSERT(tmp->tm_root == NULL); 375 376 objcache_destroy(tmp->tm_dirent_pool); 377 objcache_destroy(tmp->tm_node_pool); 378 379 lockuninit(&tmp->allnode_lock); 380 KKASSERT(tmp->tm_pages_used == 0); 381 KKASSERT(tmp->tm_nodes_inuse == 0); 382 383 /* Throw away the tmpfs_mount structure. */ 384 kfree(tmp, M_TMPFSMNT); 385 mp->mnt_data = NULL; 386 387 mp->mnt_flag &= ~MNT_LOCAL; 388 return 0; 389 } 390 391 /* --------------------------------------------------------------------- */ 392 393 static int 394 tmpfs_root(struct mount *mp, struct vnode **vpp) 395 { 396 struct tmpfs_mount *tmp; 397 int error; 398 399 tmp = VFS_TO_TMPFS(mp); 400 if (tmp->tm_root == NULL) { 401 kprintf("tmpfs_root: called without root node %p\n", mp); 402 print_backtrace(); 403 *vpp = NULL; 404 error = EINVAL; 405 } else { 406 error = tmpfs_alloc_vp(mp, tmp->tm_root, LK_EXCLUSIVE, vpp); 407 (*vpp)->v_flag |= VROOT; 408 (*vpp)->v_type = VDIR; 409 } 410 return error; 411 } 412 413 /* --------------------------------------------------------------------- */ 414 415 static int 416 tmpfs_fhtovp(struct mount *mp, struct vnode *rootvp, struct fid *fhp, struct vnode **vpp) 417 { 418 boolean_t found; 419 struct tmpfs_fid *tfhp; 420 struct tmpfs_mount *tmp; 421 struct tmpfs_node *node; 422 423 tmp = VFS_TO_TMPFS(mp); 424 425 tfhp = (struct tmpfs_fid *)fhp; 426 if (tfhp->tf_len != sizeof(struct tmpfs_fid)) 427 return EINVAL; 428 429 if (tfhp->tf_id >= tmp->tm_nodes_max) 430 return EINVAL; 431 432 found = FALSE; 433 434 TMPFS_LOCK(tmp); 435 LIST_FOREACH(node, &tmp->tm_nodes_used, tn_entries) { 436 if (node->tn_id == tfhp->tf_id && 437 node->tn_gen == tfhp->tf_gen) { 438 found = TRUE; 439 break; 440 } 441 } 442 TMPFS_UNLOCK(tmp); 443 444 if (found) 445 return (tmpfs_alloc_vp(mp, node, LK_EXCLUSIVE, vpp)); 446 447 return (EINVAL); 448 } 449 450 /* --------------------------------------------------------------------- */ 451 452 /* ARGSUSED2 */ 453 static int 454 tmpfs_statfs(struct mount *mp, struct statfs *sbp, struct ucred *cred) 455 { 456 fsfilcnt_t freenodes; 457 struct tmpfs_mount *tmp; 458 459 tmp = VFS_TO_TMPFS(mp); 460 461 sbp->f_iosize = PAGE_SIZE; 462 sbp->f_bsize = PAGE_SIZE; 463 464 sbp->f_blocks = tmp->tm_pages_max; 465 sbp->f_bavail = tmp->tm_pages_max - tmp->tm_pages_used; 466 sbp->f_bfree = sbp->f_bavail; 467 468 freenodes = tmp->tm_nodes_max - tmp->tm_nodes_inuse; 469 470 sbp->f_files = freenodes + tmp->tm_nodes_inuse; 471 sbp->f_ffree = freenodes; 472 /* sbp->f_owner = tmp->tn_uid; */ 473 474 return 0; 475 } 476 477 /* --------------------------------------------------------------------- */ 478 479 /* 480 * tmpfs vfs operations. 481 */ 482 483 static struct vfsops tmpfs_vfsops = { 484 .vfs_mount = tmpfs_mount, 485 .vfs_unmount = tmpfs_unmount, 486 .vfs_root = tmpfs_root, 487 .vfs_statfs = tmpfs_statfs, 488 .vfs_fhtovp = tmpfs_fhtovp, 489 .vfs_sync = vfs_stdsync 490 }; 491 492 VFS_SET(tmpfs_vfsops, tmpfs, 0); 493