1 /* $NetBSD: tmpfs_vfsops.c,v 1.43 2008/07/28 18:00:20 pooka Exp $ */
4 * Copyright (c) 2005, 2006, 2007 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.
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: tmpfs_vfsops.c,v 1.43 2008/07/28 18:00:20 pooka Exp $");
47 #include <sys/param.h>
48 #include <sys/types.h>
50 #include <sys/mount.h>
52 #include <sys/systm.h>
53 #include <sys/vnode.h>
55 #include <sys/module.h>
57 #include <miscfs/genfs/genfs.h>
58 #include <fs/tmpfs/tmpfs.h>
59 #include <fs/tmpfs/tmpfs_args.h>
61 MODULE(MODULE_CLASS_VFS
, tmpfs
, NULL
);
63 /* --------------------------------------------------------------------- */
65 static int tmpfs_mount(struct mount
*, const char *, void *, size_t *);
66 static int tmpfs_start(struct mount
*, int);
67 static int tmpfs_unmount(struct mount
*, int);
68 static int tmpfs_root(struct mount
*, struct vnode
**);
69 static int tmpfs_vget(struct mount
*, ino_t
, struct vnode
**);
70 static int tmpfs_fhtovp(struct mount
*, struct fid
*, struct vnode
**);
71 static int tmpfs_vptofh(struct vnode
*, struct fid
*, size_t *);
72 static int tmpfs_statvfs(struct mount
*, struct statvfs
*);
73 static int tmpfs_sync(struct mount
*, int, kauth_cred_t
);
74 static void tmpfs_init(void);
75 static void tmpfs_done(void);
76 static int tmpfs_snapshot(struct mount
*, struct vnode
*,
79 /* --------------------------------------------------------------------- */
82 tmpfs_mount(struct mount
*mp
, const char *path
, void *data
, size_t *data_len
)
84 struct lwp
*l
= curlwp
;
88 struct tmpfs_mount
*tmp
;
89 struct tmpfs_node
*root
;
90 struct tmpfs_args
*args
= data
;
92 if (*data_len
< sizeof *args
)
95 /* Handle retrieval of mount point arguments. */
96 if (mp
->mnt_flag
& MNT_GETARGS
) {
97 if (mp
->mnt_data
== NULL
)
99 tmp
= VFS_TO_TMPFS(mp
);
101 args
->ta_version
= TMPFS_ARGS_VERSION
;
102 args
->ta_nodes_max
= tmp
->tm_nodes_max
;
103 args
->ta_size_max
= tmp
->tm_pages_max
* PAGE_SIZE
;
106 args
->ta_root_uid
= root
->tn_uid
;
107 args
->ta_root_gid
= root
->tn_gid
;
108 args
->ta_root_mode
= root
->tn_mode
;
110 *data_len
= sizeof *args
;
114 if (mp
->mnt_flag
& MNT_UPDATE
) {
115 /* XXX: There is no support yet to update file system
116 * settings. Should be added. */
121 if (args
->ta_version
!= TMPFS_ARGS_VERSION
)
124 /* Do not allow mounts if we do not have enough memory to preserve
125 * the minimum reserved pages. */
126 if (tmpfs_mem_info(true) < TMPFS_PAGES_RESERVED
)
129 /* Get the maximum number of memory pages this file system is
130 * allowed to use, based on the maximum size the user passed in
131 * the mount structure. A value of zero is treated as if the
132 * maximum available space was requested. */
133 if (args
->ta_size_max
< PAGE_SIZE
|| args
->ta_size_max
>= SIZE_MAX
)
136 pages
= args
->ta_size_max
/ PAGE_SIZE
+
137 (args
->ta_size_max
% PAGE_SIZE
== 0 ? 0 : 1);
142 if (args
->ta_nodes_max
<= 3)
143 nodes
= 3 + pages
* PAGE_SIZE
/ 1024;
145 nodes
= args
->ta_nodes_max
;
150 /* Allocate the tmpfs mount structure and fill it. */
151 tmp
= kmem_alloc(sizeof(struct tmpfs_mount
), KM_SLEEP
);
155 tmp
->tm_nodes_max
= nodes
;
156 tmp
->tm_nodes_cnt
= 0;
157 LIST_INIT(&tmp
->tm_nodes
);
159 mutex_init(&tmp
->tm_lock
, MUTEX_DEFAULT
, IPL_NONE
);
161 tmp
->tm_pages_max
= pages
;
162 tmp
->tm_pages_used
= 0;
163 tmpfs_pool_init(&tmp
->tm_dirent_pool
, sizeof(struct tmpfs_dirent
),
165 tmpfs_pool_init(&tmp
->tm_node_pool
, sizeof(struct tmpfs_node
),
167 tmpfs_str_pool_init(&tmp
->tm_str_pool
, tmp
);
169 /* Allocate the root node. */
170 error
= tmpfs_alloc_node(tmp
, VDIR
, args
->ta_root_uid
,
171 args
->ta_root_gid
, args
->ta_root_mode
& ALLPERMS
, NULL
, NULL
,
173 KASSERT(error
== 0 && root
!= NULL
);
178 mp
->mnt_flag
|= MNT_LOCAL
;
179 mp
->mnt_stat
.f_namemax
= MAXNAMLEN
;
180 mp
->mnt_fs_bshift
= PAGE_SHIFT
;
181 mp
->mnt_dev_bshift
= DEV_BSHIFT
;
182 mp
->mnt_iflag
|= IMNT_MPSAFE
;
185 return set_statvfs_info(path
, UIO_USERSPACE
, "tmpfs", UIO_SYSSPACE
,
186 mp
->mnt_op
->vfs_name
, mp
, l
);
189 /* --------------------------------------------------------------------- */
192 tmpfs_start(struct mount
*mp
, int flags
)
198 /* --------------------------------------------------------------------- */
202 tmpfs_unmount(struct mount
*mp
, int mntflags
)
206 struct tmpfs_mount
*tmp
;
207 struct tmpfs_node
*node
;
209 /* Handle forced unmounts. */
210 if (mntflags
& MNT_FORCE
)
213 /* Finalize all pending I/O. */
214 error
= vflush(mp
, NULL
, flags
);
218 tmp
= VFS_TO_TMPFS(mp
);
220 /* Free all associated data. The loop iterates over the linked list
221 * we have containing all used nodes. For each of them that is
222 * a directory, we free all its directory entries. Note that after
223 * freeing a node, it will automatically go to the available list,
224 * so we will later have to iterate over it to release its items. */
225 node
= LIST_FIRST(&tmp
->tm_nodes
);
226 while (node
!= NULL
) {
227 struct tmpfs_node
*next
;
229 if (node
->tn_type
== VDIR
) {
230 struct tmpfs_dirent
*de
;
232 de
= TAILQ_FIRST(&node
->tn_spec
.tn_dir
.tn_dir
);
234 struct tmpfs_dirent
*nde
;
236 nde
= TAILQ_NEXT(de
, td_entries
);
237 tmpfs_free_dirent(tmp
, de
, false);
239 node
->tn_size
-= sizeof(struct tmpfs_dirent
);
243 next
= LIST_NEXT(node
, tn_entries
);
244 tmpfs_free_node(tmp
, node
);
248 tmpfs_pool_destroy(&tmp
->tm_dirent_pool
);
249 tmpfs_pool_destroy(&tmp
->tm_node_pool
);
250 tmpfs_str_pool_destroy(&tmp
->tm_str_pool
);
252 KASSERT(tmp
->tm_pages_used
== 0);
254 /* Throw away the tmpfs_mount structure. */
255 mutex_destroy(&tmp
->tm_lock
);
256 kmem_free(tmp
, sizeof(*tmp
));
262 /* --------------------------------------------------------------------- */
265 tmpfs_root(struct mount
*mp
, struct vnode
**vpp
)
268 return tmpfs_alloc_vp(mp
, VFS_TO_TMPFS(mp
)->tm_root
, vpp
);
271 /* --------------------------------------------------------------------- */
274 tmpfs_vget(struct mount
*mp
, ino_t ino
,
278 printf("tmpfs_vget called; need for it unknown yet\n");
282 /* --------------------------------------------------------------------- */
285 tmpfs_fhtovp(struct mount
*mp
, struct fid
*fhp
, struct vnode
**vpp
)
288 struct tmpfs_fid tfh
;
289 struct tmpfs_mount
*tmp
;
290 struct tmpfs_node
*node
;
292 tmp
= VFS_TO_TMPFS(mp
);
294 if (fhp
->fid_len
!= sizeof(struct tmpfs_fid
))
297 memcpy(&tfh
, fhp
, sizeof(struct tmpfs_fid
));
299 if (tfh
.tf_id
>= tmp
->tm_nodes_max
)
303 mutex_enter(&tmp
->tm_lock
);
304 LIST_FOREACH(node
, &tmp
->tm_nodes
, tn_entries
) {
305 if (node
->tn_id
== tfh
.tf_id
&&
306 node
->tn_gen
== tfh
.tf_gen
) {
311 mutex_exit(&tmp
->tm_lock
);
313 /* XXXAD nothing to prevent 'node' from being removed. */
314 return found
? tmpfs_alloc_vp(mp
, node
, vpp
) : EINVAL
;
317 /* --------------------------------------------------------------------- */
320 tmpfs_vptofh(struct vnode
*vp
, struct fid
*fhp
, size_t *fh_size
)
322 struct tmpfs_fid tfh
;
323 struct tmpfs_node
*node
;
325 if (*fh_size
< sizeof(struct tmpfs_fid
)) {
326 *fh_size
= sizeof(struct tmpfs_fid
);
329 *fh_size
= sizeof(struct tmpfs_fid
);
330 node
= VP_TO_TMPFS_NODE(vp
);
332 memset(&tfh
, 0, sizeof(tfh
));
333 tfh
.tf_len
= sizeof(struct tmpfs_fid
);
334 tfh
.tf_gen
= node
->tn_gen
;
335 tfh
.tf_id
= node
->tn_id
;
336 memcpy(fhp
, &tfh
, sizeof(tfh
));
341 /* --------------------------------------------------------------------- */
345 tmpfs_statvfs(struct mount
*mp
, struct statvfs
*sbp
)
347 fsfilcnt_t freenodes
;
348 struct tmpfs_mount
*tmp
;
350 tmp
= VFS_TO_TMPFS(mp
);
352 sbp
->f_iosize
= sbp
->f_frsize
= sbp
->f_bsize
= PAGE_SIZE
;
354 sbp
->f_blocks
= TMPFS_PAGES_MAX(tmp
);
355 sbp
->f_bavail
= sbp
->f_bfree
= TMPFS_PAGES_AVAIL(tmp
);
358 freenodes
= MIN(tmp
->tm_nodes_max
- tmp
->tm_nodes_cnt
,
359 TMPFS_PAGES_AVAIL(tmp
) * PAGE_SIZE
/ sizeof(struct tmpfs_node
));
361 sbp
->f_files
= tmp
->tm_nodes_cnt
+ freenodes
;
362 sbp
->f_favail
= sbp
->f_ffree
= freenodes
;
365 copy_statvfs_info(sbp
, mp
);
370 /* --------------------------------------------------------------------- */
374 tmpfs_sync(struct mount
*mp
, int waitfor
,
381 /* --------------------------------------------------------------------- */
389 /* --------------------------------------------------------------------- */
397 /* --------------------------------------------------------------------- */
400 tmpfs_snapshot(struct mount
*mp
, struct vnode
*vp
,
401 struct timespec
*ctime
)
407 /* --------------------------------------------------------------------- */
410 * tmpfs vfs operations.
413 extern const struct vnodeopv_desc tmpfs_fifoop_opv_desc
;
414 extern const struct vnodeopv_desc tmpfs_specop_opv_desc
;
415 extern const struct vnodeopv_desc tmpfs_vnodeop_opv_desc
;
417 const struct vnodeopv_desc
* const tmpfs_vnodeopv_descs
[] = {
418 &tmpfs_fifoop_opv_desc
,
419 &tmpfs_specop_opv_desc
,
420 &tmpfs_vnodeop_opv_desc
,
424 struct vfsops tmpfs_vfsops
= {
425 MOUNT_TMPFS
, /* vfs_name */
426 sizeof (struct tmpfs_args
),
427 tmpfs_mount
, /* vfs_mount */
428 tmpfs_start
, /* vfs_start */
429 tmpfs_unmount
, /* vfs_unmount */
430 tmpfs_root
, /* vfs_root */
431 (void *)eopnotsupp
, /* vfs_quotactl */
432 tmpfs_statvfs
, /* vfs_statvfs */
433 tmpfs_sync
, /* vfs_sync */
434 tmpfs_vget
, /* vfs_vget */
435 tmpfs_fhtovp
, /* vfs_fhtovp */
436 tmpfs_vptofh
, /* vfs_vptofh */
437 tmpfs_init
, /* vfs_init */
438 NULL
, /* vfs_reinit */
439 tmpfs_done
, /* vfs_done */
440 NULL
, /* vfs_mountroot */
441 tmpfs_snapshot
, /* vfs_snapshot */
442 vfs_stdextattrctl
, /* vfs_extattrctl */
443 (void *)eopnotsupp
, /* vfs_suspendctl */
444 genfs_renamelock_enter
,
445 genfs_renamelock_exit
,
447 tmpfs_vnodeopv_descs
,
448 0, /* vfs_refcount */
453 tmpfs_modcmd(modcmd_t cmd
, void *arg
)
457 case MODULE_CMD_INIT
:
458 return vfs_attach(&tmpfs_vfsops
);
459 case MODULE_CMD_FINI
:
460 return vfs_detach(&tmpfs_vfsops
);