1 /* $NetBSD: tmpfs_vfsops.c,v 1.10 2005/12/11 12:24:29 christos Exp $ */
4 * Copyright (c) 2005 The NetBSD Foundation, Inc.
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
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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.
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.
34 * Efficient memory file system.
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.
43 #include <sys/cdefs.h>
45 #include <sys/param.h>
46 #include <sys/limits.h>
48 #include <sys/mutex.h>
49 #include <sys/kernel.h>
51 #include <sys/systm.h>
52 #include <sys/sysctl.h>
53 #include <sys/objcache.h>
56 #include <vm/vm_object.h>
57 #include <vm/vm_param.h>
59 #include <vfs/tmpfs/tmpfs.h>
60 #include <vfs/tmpfs/tmpfs_vnops.h>
61 #include <vfs/tmpfs/tmpfs_args.h>
64 * Default permission for root node
66 #define TMPFS_DEFAULT_ROOT_MODE (S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH)
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");
73 /* --------------------------------------------------------------------- */
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
);
81 /* --------------------------------------------------------------------- */
83 tmpfs_node_ctor(void *obj
, void *privdata
, int flags
)
85 struct tmpfs_node
*node
= (struct tmpfs_node
*)obj
;
92 node
->tn_vnode
= NULL
;
93 node
->tn_vpstate
= TMPFS_VNODE_WANT
;
94 bzero(&node
->tn_spec
, sizeof(node
->tn_spec
));
100 tmpfs_node_dtor(void *obj
, void *privdata
)
102 struct tmpfs_node
*node
= (struct tmpfs_node
*)obj
;
103 node
->tn_type
= VNON
;
104 node
->tn_vpstate
= TMPFS_VNODE_DOOMED
;
108 tmpfs_node_init(void *args
, int flags
)
110 struct tmpfs_node
*node
= (struct tmpfs_node
*)objcache_malloc_alloc(args
, flags
);
113 lockinit(&node
->tn_interlock
, "tmpfs node interlock", 0, LK_CANRECURSE
);
114 node
->tn_gen
= karc4random();
120 tmpfs_node_fini(void *obj
, void *args
)
122 struct tmpfs_node
*node
= (struct tmpfs_node
*)obj
;
123 lockuninit(&node
->tn_interlock
);
124 objcache_malloc_free(obj
, args
);
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
};
133 tmpfs_mount(struct mount
*mp
, char *path
, caddr_t data
, struct ucred
*cred
)
135 struct tmpfs_mount
*tmp
;
136 struct tmpfs_node
*root
;
137 struct tmpfs_args args
;
139 vm_pindex_t pages_limit
;
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
);
152 if (mp
->mnt_flag
& MNT_UPDATE
) {
153 /* XXX: There is no support yet to update file system
154 * settings. Should be added. */
162 bzero(&args
, sizeof(args
));
168 error
= copyin(data
, &args
, sizeof(args
));
172 size_max
= args
.ta_size_max
;
173 nodes_max
= args
.ta_nodes_max
;
177 * If mount by non-root, then verify that user has necessary
178 * permissions on the device.
180 if (cred
->cr_uid
!= 0) {
182 if ((mp
->mnt_flag
& MNT_RDONLY
) == 0)
186 pages_limit
= vm_swap_max
+ vmstats
.v_page_count
/ 2;
189 pages
= pages_limit
/ 2;
190 else if (size_max
< PAGE_SIZE
)
192 else if (OFF_TO_IDX(size_max
) > pages_limit
)
195 pages
= OFF_TO_IDX(size_max
);
198 nodes
= 3 + pages
* PAGE_SIZE
/ 1024;
199 else if (nodes_max
< 3)
201 else if (nodes_max
> pages
)
206 /* Allocate the tmpfs mount structure and fill it. */
207 tmp
= kmalloc(sizeof(*tmp
), M_TMPFSMNT
, M_WAITOK
| M_ZERO
);
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
);
215 tmp
->tm_pages_max
= pages
;
216 tmp
->tm_pages_used
= 0;
218 tmp
->tm_dirent_pool
= objcache_create( "tmpfs dirent cache",
221 objcache_malloc_alloc
, objcache_malloc_free
,
222 &tmpfs_dirent_pool_malloc_args
);
223 tmp
->tm_node_pool
= objcache_create( "tmpfs node cache",
225 tmpfs_node_ctor
, tmpfs_node_dtor
, NULL
,
226 tmpfs_node_init
, tmpfs_node_fini
,
227 &tmpfs_node_pool_malloc_args
);
229 /* Allocate the root node. */
230 error
= tmpfs_alloc_node(tmp
, VDIR
, root_uid
,
231 root_gid
, root_mode
& ALLPERMS
, NULL
, NULL
,
232 VNOVAL
, VNOVAL
, &root
);
234 if (error
!= 0 || root
== NULL
) {
235 objcache_destroy(tmp
->tm_node_pool
);
236 objcache_destroy(tmp
->tm_dirent_pool
);
237 kfree(tmp
, M_TMPFSMNT
);
240 KASSERT(root
->tn_id
>= 0, ("tmpfs root with invalid ino: %d", (int)root
->tn_id
));
243 mp
->mnt_flag
|= MNT_LOCAL
;
245 mp
->mnt_kern_flag
|= MNTK_RD_MPSAFE
| MNTK_WR_MPSAFE
| MNTK_GA_MPSAFE
|
246 MNTK_IN_MPSAFE
| MNTK_SG_MPSAFE
;
248 mp
->mnt_data
= (qaddr_t
)tmp
;
252 vfs_add_vnodeops(mp
, &tmpfs_vnode_vops
, &mp
->mnt_vn_norm_ops
);
253 vfs_add_vnodeops(mp
, &tmpfs_fifo_vops
, &mp
->mnt_vn_fifo_ops
);
255 copystr("tmpfs", mp
->mnt_stat
.f_mntfromname
, MNAMELEN
- 1, &size
);
256 bzero(mp
->mnt_stat
.f_mntfromname
+size
, MNAMELEN
- size
);
257 bzero(mp
->mnt_stat
.f_mntonname
, sizeof(mp
->mnt_stat
.f_mntonname
));
258 copyinstr(path
, mp
->mnt_stat
.f_mntonname
,
259 sizeof(mp
->mnt_stat
.f_mntonname
) -1,
262 tmpfs_statfs(mp
, &mp
->mnt_stat
, cred
);
267 /* --------------------------------------------------------------------- */
271 tmpfs_unmount(struct mount
*mp
, int mntflags
)
276 struct tmpfs_mount
*tmp
;
277 struct tmpfs_node
*node
;
279 /* Handle forced unmounts. */
280 if (mntflags
& MNT_FORCE
)
283 /* Tell vflush->vinvalbuf->fsync to throw away data */
284 tmp
= VFS_TO_TMPFS(mp
);
285 tmp
->tm_flags
|= TMPFS_FLAG_UNMOUNTING
;
287 /* Finalize all pending I/O. */
288 error
= vflush(mp
, 0, flags
);
293 * First pass get rid of all the directory entries and
294 * vnode associations. The directory structure will
295 * remain via the extra link count representing tn_dir.tn_parent.
297 * No vnodes should remain after the vflush above.
299 LIST_FOREACH(node
, &tmp
->tm_nodes_used
, tn_entries
) {
301 TMPFS_NODE_LOCK(node
);
302 if (node
->tn_type
== VDIR
) {
303 struct tmpfs_dirent
*de
;
305 while (!TAILQ_EMPTY(&node
->tn_dir
.tn_dirhead
)) {
306 de
= TAILQ_FIRST(&node
->tn_dir
.tn_dirhead
);
307 tmpfs_dir_detach(node
, de
);
308 tmpfs_free_dirent(tmp
, de
);
309 node
->tn_size
-= sizeof(struct tmpfs_dirent
);
312 KKASSERT(node
->tn_vnode
== NULL
);
318 node
->tn_vnode
= NULL
;
321 TMPFS_NODE_UNLOCK(node
);
326 * Now get rid of all nodes. We can remove any node with a
327 * link count of 0 or any directory node with a link count of
328 * 1. The parents will not be destroyed until all their children
329 * have been destroyed.
331 * Recursion in tmpfs_free_node() can further modify the list so
332 * we cannot use a next pointer here.
334 * The root node will be destroyed by this loop (it will be last).
336 while (!LIST_EMPTY(&tmp
->tm_nodes_used
)) {
338 LIST_FOREACH(node
, &tmp
->tm_nodes_used
, tn_entries
) {
339 if (node
->tn_links
== 0 ||
340 (node
->tn_links
== 1 && node
->tn_type
== VDIR
)) {
341 TMPFS_NODE_LOCK(node
);
342 tmpfs_free_node(tmp
, node
);
349 kprintf("tmpfs: Cannot free entire node tree!");
354 KKASSERT(tmp
->tm_root
== NULL
);
356 objcache_destroy(tmp
->tm_dirent_pool
);
357 objcache_destroy(tmp
->tm_node_pool
);
359 lockuninit(&tmp
->allnode_lock
);
360 KKASSERT(tmp
->tm_pages_used
== 0);
361 KKASSERT(tmp
->tm_nodes_inuse
== 0);
363 /* Throw away the tmpfs_mount structure. */
364 kfree(tmp
, M_TMPFSMNT
);
367 mp
->mnt_flag
&= ~MNT_LOCAL
;
371 /* --------------------------------------------------------------------- */
374 tmpfs_root(struct mount
*mp
, struct vnode
**vpp
)
376 struct tmpfs_mount
*tmp
;
379 tmp
= VFS_TO_TMPFS(mp
);
380 if (tmp
->tm_root
== NULL
) {
381 kprintf("tmpfs_root: called without root node %p\n", mp
);
386 error
= tmpfs_alloc_vp(mp
, tmp
->tm_root
, LK_EXCLUSIVE
, vpp
);
387 (*vpp
)->v_flag
|= VROOT
;
388 (*vpp
)->v_type
= VDIR
;
393 /* --------------------------------------------------------------------- */
396 tmpfs_fhtovp(struct mount
*mp
, struct vnode
*rootvp
, struct fid
*fhp
, struct vnode
**vpp
)
399 struct tmpfs_fid
*tfhp
;
400 struct tmpfs_mount
*tmp
;
401 struct tmpfs_node
*node
;
403 tmp
= VFS_TO_TMPFS(mp
);
405 tfhp
= (struct tmpfs_fid
*)fhp
;
406 if (tfhp
->tf_len
!= sizeof(struct tmpfs_fid
))
409 if (tfhp
->tf_id
>= tmp
->tm_nodes_max
)
415 LIST_FOREACH(node
, &tmp
->tm_nodes_used
, tn_entries
) {
416 if (node
->tn_id
== tfhp
->tf_id
&&
417 node
->tn_gen
== tfhp
->tf_gen
) {
425 return (tmpfs_alloc_vp(mp
, node
, LK_EXCLUSIVE
, vpp
));
430 /* --------------------------------------------------------------------- */
434 tmpfs_statfs(struct mount
*mp
, struct statfs
*sbp
, struct ucred
*cred
)
436 fsfilcnt_t freenodes
;
437 struct tmpfs_mount
*tmp
;
439 tmp
= VFS_TO_TMPFS(mp
);
441 sbp
->f_iosize
= PAGE_SIZE
;
442 sbp
->f_bsize
= PAGE_SIZE
;
444 sbp
->f_blocks
= tmp
->tm_pages_max
;
445 sbp
->f_bavail
= tmp
->tm_pages_max
- tmp
->tm_pages_used
;
446 sbp
->f_bfree
= sbp
->f_bavail
;
448 freenodes
= tmp
->tm_nodes_max
- tmp
->tm_nodes_inuse
;
450 sbp
->f_files
= freenodes
+ tmp
->tm_nodes_inuse
;
451 sbp
->f_ffree
= freenodes
;
452 /* sbp->f_owner = tmp->tn_uid; */
457 /* --------------------------------------------------------------------- */
460 * tmpfs vfs operations.
463 static struct vfsops tmpfs_vfsops
= {
464 .vfs_mount
= tmpfs_mount
,
465 .vfs_unmount
= tmpfs_unmount
,
466 .vfs_root
= tmpfs_root
,
467 .vfs_statfs
= tmpfs_statfs
,
468 .vfs_fhtovp
= tmpfs_fhtovp
,
469 .vfs_sync
= vfs_stdsync
472 VFS_SET(tmpfs_vfsops
, tmpfs
, 0);