17a2de9a4SMatthew Dillon /*-
27a2de9a4SMatthew Dillon * Copyright (c) 2005, 2006 The NetBSD Foundation, Inc.
37a2de9a4SMatthew Dillon * All rights reserved.
47a2de9a4SMatthew Dillon *
57a2de9a4SMatthew Dillon * This code is derived from software contributed to The NetBSD Foundation
67a2de9a4SMatthew Dillon * by Julio M. Merino Vidal, developed as part of Google's Summer of Code
77a2de9a4SMatthew Dillon * 2005 program.
87a2de9a4SMatthew Dillon *
97a2de9a4SMatthew Dillon * Redistribution and use in source and binary forms, with or without
107a2de9a4SMatthew Dillon * modification, are permitted provided that the following conditions
117a2de9a4SMatthew Dillon * are met:
127a2de9a4SMatthew Dillon * 1. Redistributions of source code must retain the above copyright
137a2de9a4SMatthew Dillon * notice, this list of conditions and the following disclaimer.
147a2de9a4SMatthew Dillon * 2. Redistributions in binary form must reproduce the above copyright
157a2de9a4SMatthew Dillon * notice, this list of conditions and the following disclaimer in the
167a2de9a4SMatthew Dillon * documentation and/or other materials provided with the distribution.
177a2de9a4SMatthew Dillon *
187a2de9a4SMatthew Dillon * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
197a2de9a4SMatthew Dillon * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
207a2de9a4SMatthew Dillon * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
217a2de9a4SMatthew Dillon * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
227a2de9a4SMatthew Dillon * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
237a2de9a4SMatthew Dillon * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
247a2de9a4SMatthew Dillon * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
257a2de9a4SMatthew Dillon * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
267a2de9a4SMatthew Dillon * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
277a2de9a4SMatthew Dillon * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
287a2de9a4SMatthew Dillon * POSSIBILITY OF SUCH DAMAGE.
2980ae59d7SMatthew Dillon *
3080ae59d7SMatthew Dillon * $NetBSD: tmpfs_vnops.c,v 1.39 2007/07/23 15:41:01 jmmv Exp $
317a2de9a4SMatthew Dillon */
327a2de9a4SMatthew Dillon
337a2de9a4SMatthew Dillon /*
347a2de9a4SMatthew Dillon * tmpfs vnode interface.
357a2de9a4SMatthew Dillon */
367a2de9a4SMatthew Dillon
377a2de9a4SMatthew Dillon #include <sys/kernel.h>
387a2de9a4SMatthew Dillon #include <sys/kern_syscall.h>
397a2de9a4SMatthew Dillon #include <sys/param.h>
4013dd34d8Szrj #include <sys/uio.h>
417a2de9a4SMatthew Dillon #include <sys/fcntl.h>
427a2de9a4SMatthew Dillon #include <sys/lockf.h>
43*2b3f93eaSMatthew Dillon #include <sys/caps.h>
447a2de9a4SMatthew Dillon #include <sys/proc.h>
457a2de9a4SMatthew Dillon #include <sys/resourcevar.h>
467a2de9a4SMatthew Dillon #include <sys/sched.h>
477a2de9a4SMatthew Dillon #include <sys/stat.h>
487a2de9a4SMatthew Dillon #include <sys/systm.h>
49a1b829f2SMatthew Dillon #include <sys/sysctl.h>
507a2de9a4SMatthew Dillon #include <sys/unistd.h>
517a2de9a4SMatthew Dillon #include <sys/vfsops.h>
527a2de9a4SMatthew Dillon #include <sys/vnode.h>
5366fa44e7SVenkatesh Srinivas #include <sys/mountctl.h>
547a2de9a4SMatthew Dillon
557a2de9a4SMatthew Dillon #include <vm/vm.h>
56cad75e56SSascha Wildner #include <vm/vm_extern.h>
577a2de9a4SMatthew Dillon #include <vm/vm_object.h>
587a2de9a4SMatthew Dillon #include <vm/vm_page.h>
5903697313SSascha Wildner #include <vm/vm_pageout.h>
607a2de9a4SMatthew Dillon #include <vm/vm_pager.h>
61b7545cb3SAlex Hornung #include <vm/swap_pager.h>
627a2de9a4SMatthew Dillon
6354341a3bSMatthew Dillon #include <sys/buf2.h>
643c54bb74SMatthew Dillon #include <vm/vm_page2.h>
6554341a3bSMatthew Dillon
667a2de9a4SMatthew Dillon #include <vfs/fifofs/fifo.h>
677a2de9a4SMatthew Dillon #include <vfs/tmpfs/tmpfs_vnops.h>
68aa1adbf0SMatthew Dillon #include "tmpfs.h"
697a2de9a4SMatthew Dillon
708f9ba07bSMatthew Dillon static void tmpfs_strategy_done(struct bio *bio);
71cfd59c5aSMatthew Dillon static void tmpfs_move_pages(vm_object_t src, vm_object_t dst, int movflags);
72a1b829f2SMatthew Dillon
73cfd59c5aSMatthew Dillon /*
74cfd59c5aSMatthew Dillon * bufcache_mode:
75307bf766SMatthew Dillon * 0 Normal page queue operation on flush. Run through the buffer
76307bf766SMatthew Dillon * cache if free memory is under the minimum.
77108fbfcbSMatthew Dillon *
78307bf766SMatthew Dillon * 1 Try to keep in memory, but run through the buffer cache if
79307bf766SMatthew Dillon * the system is under memory pressure (though this might just
80307bf766SMatthew Dillon * require inactive cleaning).
81108fbfcbSMatthew Dillon *
82307bf766SMatthew Dillon * 2 Be a bit more aggressive when running writes through the
83307bf766SMatthew Dillon * buffer cache when the system is under memory pressure.
84307bf766SMatthew Dillon *
85307bf766SMatthew Dillon * 3 Always run tmpfs writes through the buffer cache, thus forcing
86307bf766SMatthew Dillon * them out to swap.
87cfd59c5aSMatthew Dillon */
88cfd59c5aSMatthew Dillon __read_mostly static int tmpfs_cluster_rd_enable = 1;
89cfd59c5aSMatthew Dillon __read_mostly static int tmpfs_cluster_wr_enable = 1;
90307bf766SMatthew Dillon __read_mostly int tmpfs_bufcache_mode = 0;
91a1b829f2SMatthew Dillon SYSCTL_NODE(_vfs, OID_AUTO, tmpfs, CTLFLAG_RW, 0, "TMPFS filesystem");
92cfd59c5aSMatthew Dillon SYSCTL_INT(_vfs_tmpfs, OID_AUTO, cluster_rd_enable, CTLFLAG_RW,
93cfd59c5aSMatthew Dillon &tmpfs_cluster_rd_enable, 0, "");
94cfd59c5aSMatthew Dillon SYSCTL_INT(_vfs_tmpfs, OID_AUTO, cluster_wr_enable, CTLFLAG_RW,
95cfd59c5aSMatthew Dillon &tmpfs_cluster_wr_enable, 0, "");
96cfd59c5aSMatthew Dillon SYSCTL_INT(_vfs_tmpfs, OID_AUTO, bufcache_mode, CTLFLAG_RW,
97cfd59c5aSMatthew Dillon &tmpfs_bufcache_mode, 0, "");
98cfd59c5aSMatthew Dillon
99cfd59c5aSMatthew Dillon #define TMPFS_MOVF_FROMBACKING 0x0001
100cfd59c5aSMatthew Dillon #define TMPFS_MOVF_DEACTIVATE 0x0002
101cfd59c5aSMatthew Dillon
1028f9ba07bSMatthew Dillon
10380ae59d7SMatthew Dillon static __inline
10480ae59d7SMatthew Dillon void
tmpfs_knote(struct vnode * vp,int flags)10580ae59d7SMatthew Dillon tmpfs_knote(struct vnode *vp, int flags)
10680ae59d7SMatthew Dillon {
10780ae59d7SMatthew Dillon if (flags)
10880ae59d7SMatthew Dillon KNOTE(&vp->v_pollinfo.vpi_kqinfo.ki_note, flags);
10980ae59d7SMatthew Dillon }
11080ae59d7SMatthew Dillon
11180ae59d7SMatthew Dillon
1127a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
1137a2de9a4SMatthew Dillon
1147a2de9a4SMatthew Dillon static int
tmpfs_nresolve(struct vop_nresolve_args * ap)115ba25006aSSascha Wildner tmpfs_nresolve(struct vop_nresolve_args *ap)
1167a2de9a4SMatthew Dillon {
117ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
1187a2de9a4SMatthew Dillon struct vnode *vp = NULL;
119ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
120d89ce96aSMatthew Dillon struct tmpfs_node *tnode;
1217a2de9a4SMatthew Dillon struct tmpfs_dirent *de;
1227a2de9a4SMatthew Dillon struct tmpfs_node *dnode;
123ff837cd5SMatthew Dillon int error;
1247a2de9a4SMatthew Dillon
1257a2de9a4SMatthew Dillon dnode = VP_TO_TMPFS_DIR(dvp);
1267a2de9a4SMatthew Dillon
127ff837cd5SMatthew Dillon TMPFS_NODE_LOCK_SH(dnode);
128d89a0e31SMatthew Dillon loop:
1297a2de9a4SMatthew Dillon de = tmpfs_dir_lookup(dnode, NULL, ncp);
1307a2de9a4SMatthew Dillon if (de == NULL) {
1317a2de9a4SMatthew Dillon error = ENOENT;
1327a2de9a4SMatthew Dillon } else {
133d89ce96aSMatthew Dillon /*
134d89a0e31SMatthew Dillon * Allocate a vnode for the node we found. Use
135d89a0e31SMatthew Dillon * tmpfs_alloc_vp()'s deadlock handling mode.
136d89ce96aSMatthew Dillon */
1377a2de9a4SMatthew Dillon tnode = de->td_node;
138d89a0e31SMatthew Dillon error = tmpfs_alloc_vp(dvp->v_mount, dnode, tnode,
1397a2de9a4SMatthew Dillon LK_EXCLUSIVE | LK_RETRY, &vp);
140d89a0e31SMatthew Dillon if (error == EAGAIN)
141d89a0e31SMatthew Dillon goto loop;
142974226b4STomohiro Kusumi if (error)
143974226b4STomohiro Kusumi goto out;
1447a2de9a4SMatthew Dillon KKASSERT(vp);
145d89ce96aSMatthew Dillon }
146974226b4STomohiro Kusumi
147974226b4STomohiro Kusumi out:
14881aadc82STomohiro Kusumi TMPFS_NODE_UNLOCK(dnode);
149ff837cd5SMatthew Dillon
150bed3b851STomohiro Kusumi if ((dnode->tn_status & TMPFS_NODE_ACCESSED) == 0) {
15181aadc82STomohiro Kusumi TMPFS_NODE_LOCK(dnode);
152ff837cd5SMatthew Dillon dnode->tn_status |= TMPFS_NODE_ACCESSED;
153ff837cd5SMatthew Dillon TMPFS_NODE_UNLOCK(dnode);
154bed3b851STomohiro Kusumi }
155ff837cd5SMatthew Dillon
156d89ce96aSMatthew Dillon /*
157d89ce96aSMatthew Dillon * Store the result of this lookup in the cache. Avoid this if the
1587a2de9a4SMatthew Dillon * request was for creation, as it does not improve timings on
159d89ce96aSMatthew Dillon * emprical tests.
160d89ce96aSMatthew Dillon */
1617a2de9a4SMatthew Dillon if (vp) {
1627a2de9a4SMatthew Dillon vn_unlock(vp);
163ba25006aSSascha Wildner cache_setvp(ap->a_nch, vp);
1647a2de9a4SMatthew Dillon vrele(vp);
165d89ce96aSMatthew Dillon } else if (error == ENOENT) {
166ba25006aSSascha Wildner cache_setvp(ap->a_nch, NULL);
1677a2de9a4SMatthew Dillon }
168aa1adbf0SMatthew Dillon return (error);
1697a2de9a4SMatthew Dillon }
1707a2de9a4SMatthew Dillon
1717a2de9a4SMatthew Dillon static int
tmpfs_nlookupdotdot(struct vop_nlookupdotdot_args * ap)172ba25006aSSascha Wildner tmpfs_nlookupdotdot(struct vop_nlookupdotdot_args *ap)
1737a2de9a4SMatthew Dillon {
174ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
175ba25006aSSascha Wildner struct vnode **vpp = ap->a_vpp;
1767a2de9a4SMatthew Dillon struct tmpfs_node *dnode = VP_TO_TMPFS_NODE(dvp);
177ba25006aSSascha Wildner struct ucred *cred = ap->a_cred;
1787a2de9a4SMatthew Dillon int error;
1797a2de9a4SMatthew Dillon
1807a2de9a4SMatthew Dillon *vpp = NULL;
181aa1adbf0SMatthew Dillon
1827a2de9a4SMatthew Dillon /* Check accessibility of requested node as a first step. */
1837a2de9a4SMatthew Dillon error = VOP_ACCESS(dvp, VEXEC, cred);
184ff837cd5SMatthew Dillon if (error != 0)
1857a2de9a4SMatthew Dillon return error;
1867a2de9a4SMatthew Dillon
1877a2de9a4SMatthew Dillon if (dnode->tn_dir.tn_parent != NULL) {
1887a2de9a4SMatthew Dillon /* Allocate a new vnode on the matching entry. */
189d89a0e31SMatthew Dillon error = tmpfs_alloc_vp(dvp->v_mount,
190d89a0e31SMatthew Dillon NULL, dnode->tn_dir.tn_parent,
1917a2de9a4SMatthew Dillon LK_EXCLUSIVE | LK_RETRY, vpp);
1927a2de9a4SMatthew Dillon
1937a2de9a4SMatthew Dillon if (*vpp)
1947a2de9a4SMatthew Dillon vn_unlock(*vpp);
1957a2de9a4SMatthew Dillon }
1967a2de9a4SMatthew Dillon return (*vpp == NULL) ? ENOENT : 0;
1977a2de9a4SMatthew Dillon }
1987a2de9a4SMatthew Dillon
1997a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2007a2de9a4SMatthew Dillon
2017a2de9a4SMatthew Dillon static int
tmpfs_ncreate(struct vop_ncreate_args * ap)202ba25006aSSascha Wildner tmpfs_ncreate(struct vop_ncreate_args *ap)
2037a2de9a4SMatthew Dillon {
204ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
205ba25006aSSascha Wildner struct vnode **vpp = ap->a_vpp;
206ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
207ba25006aSSascha Wildner struct vattr *vap = ap->a_vap;
208ba25006aSSascha Wildner struct ucred *cred = ap->a_cred;
2097a2de9a4SMatthew Dillon int error;
2107a2de9a4SMatthew Dillon
2117a2de9a4SMatthew Dillon KKASSERT(vap->va_type == VREG || vap->va_type == VSOCK);
2127a2de9a4SMatthew Dillon
2137a2de9a4SMatthew Dillon error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
2147a2de9a4SMatthew Dillon if (error == 0) {
215ba25006aSSascha Wildner cache_setunresolved(ap->a_nch);
216ba25006aSSascha Wildner cache_setvp(ap->a_nch, *vpp);
21780ae59d7SMatthew Dillon tmpfs_knote(dvp, NOTE_WRITE);
2187a2de9a4SMatthew Dillon }
219aa1adbf0SMatthew Dillon return (error);
2207a2de9a4SMatthew Dillon }
2217a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2227a2de9a4SMatthew Dillon
2237a2de9a4SMatthew Dillon static int
tmpfs_nmknod(struct vop_nmknod_args * ap)224ba25006aSSascha Wildner tmpfs_nmknod(struct vop_nmknod_args *ap)
2257a2de9a4SMatthew Dillon {
226ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
227ba25006aSSascha Wildner struct vnode **vpp = ap->a_vpp;
228ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
229ba25006aSSascha Wildner struct vattr *vap = ap->a_vap;
230ba25006aSSascha Wildner struct ucred *cred = ap->a_cred;
2317a2de9a4SMatthew Dillon int error;
2327a2de9a4SMatthew Dillon
2337a2de9a4SMatthew Dillon if (vap->va_type != VBLK && vap->va_type != VCHR &&
234aa1adbf0SMatthew Dillon vap->va_type != VFIFO) {
235aa1adbf0SMatthew Dillon return (EINVAL);
236aa1adbf0SMatthew Dillon }
2377a2de9a4SMatthew Dillon
2387a2de9a4SMatthew Dillon error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
2397a2de9a4SMatthew Dillon if (error == 0) {
240ba25006aSSascha Wildner cache_setunresolved(ap->a_nch);
241ba25006aSSascha Wildner cache_setvp(ap->a_nch, *vpp);
24280ae59d7SMatthew Dillon tmpfs_knote(dvp, NOTE_WRITE);
2437a2de9a4SMatthew Dillon }
2447a2de9a4SMatthew Dillon return error;
2457a2de9a4SMatthew Dillon }
2467a2de9a4SMatthew Dillon
2477a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2487a2de9a4SMatthew Dillon
2497a2de9a4SMatthew Dillon static int
tmpfs_open(struct vop_open_args * ap)250ba25006aSSascha Wildner tmpfs_open(struct vop_open_args *ap)
2517a2de9a4SMatthew Dillon {
252ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
253ba25006aSSascha Wildner int mode = ap->a_mode;
2547a2de9a4SMatthew Dillon struct tmpfs_node *node;
255aa1adbf0SMatthew Dillon int error;
2567a2de9a4SMatthew Dillon
2577a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
2587a2de9a4SMatthew Dillon
2596e0c5aabSMatthew Dillon #if 0
2607a2de9a4SMatthew Dillon /* The file is still active but all its names have been removed
2617a2de9a4SMatthew Dillon * (e.g. by a "rmdir $(pwd)"). It cannot be opened any more as
2627a2de9a4SMatthew Dillon * it is about to die. */
2637a2de9a4SMatthew Dillon if (node->tn_links < 1)
2647a2de9a4SMatthew Dillon return (ENOENT);
2656e0c5aabSMatthew Dillon #endif
2667a2de9a4SMatthew Dillon
2677a2de9a4SMatthew Dillon /* If the file is marked append-only, deny write requests. */
268630e3a33SMatthew Dillon if ((node->tn_flags & APPEND) &&
269630e3a33SMatthew Dillon (mode & (FWRITE | O_APPEND)) == FWRITE) {
2707a2de9a4SMatthew Dillon error = EPERM;
271630e3a33SMatthew Dillon } else {
272a1b829f2SMatthew Dillon if (node->tn_reg.tn_pages_in_aobj) {
273a1b829f2SMatthew Dillon TMPFS_NODE_LOCK(node);
274a1b829f2SMatthew Dillon if (node->tn_reg.tn_pages_in_aobj) {
275a1b829f2SMatthew Dillon tmpfs_move_pages(node->tn_reg.tn_aobj,
276cfd59c5aSMatthew Dillon vp->v_object,
277cfd59c5aSMatthew Dillon TMPFS_MOVF_FROMBACKING);
278a1b829f2SMatthew Dillon node->tn_reg.tn_pages_in_aobj = 0;
279a1b829f2SMatthew Dillon }
280a1b829f2SMatthew Dillon TMPFS_NODE_UNLOCK(node);
281a1b829f2SMatthew Dillon }
282a1b829f2SMatthew Dillon error = vop_stdopen(ap);
2837a2de9a4SMatthew Dillon }
284aa1adbf0SMatthew Dillon
285aa1adbf0SMatthew Dillon return (error);
2867a2de9a4SMatthew Dillon }
2877a2de9a4SMatthew Dillon
2887a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
2897a2de9a4SMatthew Dillon
2907a2de9a4SMatthew Dillon static int
tmpfs_close(struct vop_close_args * ap)291ba25006aSSascha Wildner tmpfs_close(struct vop_close_args *ap)
2927a2de9a4SMatthew Dillon {
293ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
2947a2de9a4SMatthew Dillon struct tmpfs_node *node;
295aa1adbf0SMatthew Dillon int error;
2967a2de9a4SMatthew Dillon
2977a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
2987a2de9a4SMatthew Dillon
2997a2de9a4SMatthew Dillon if (node->tn_links > 0) {
3006e0c5aabSMatthew Dillon /*
3016e0c5aabSMatthew Dillon * Update node times. No need to do it if the node has
3026e0c5aabSMatthew Dillon * been deleted, because it will vanish after we return.
3036e0c5aabSMatthew Dillon */
3047a2de9a4SMatthew Dillon tmpfs_update(vp);
3057a2de9a4SMatthew Dillon }
3067a2de9a4SMatthew Dillon
307ba25006aSSascha Wildner error = vop_stdclose(ap);
308aa1adbf0SMatthew Dillon
309aa1adbf0SMatthew Dillon return (error);
3107a2de9a4SMatthew Dillon }
3117a2de9a4SMatthew Dillon
3127a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
3137a2de9a4SMatthew Dillon
3147a2de9a4SMatthew Dillon int
tmpfs_access(struct vop_access_args * ap)315ba25006aSSascha Wildner tmpfs_access(struct vop_access_args *ap)
3167a2de9a4SMatthew Dillon {
317ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
3187a2de9a4SMatthew Dillon int error;
3197a2de9a4SMatthew Dillon struct tmpfs_node *node;
3207a2de9a4SMatthew Dillon
3217a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
3227a2de9a4SMatthew Dillon
3237a2de9a4SMatthew Dillon switch (vp->v_type) {
3247a2de9a4SMatthew Dillon case VDIR:
3257a2de9a4SMatthew Dillon /* FALLTHROUGH */
3267a2de9a4SMatthew Dillon case VLNK:
3277a2de9a4SMatthew Dillon /* FALLTHROUGH */
3287a2de9a4SMatthew Dillon case VREG:
329ba25006aSSascha Wildner if ((ap->a_mode & VWRITE) &&
330aa1adbf0SMatthew Dillon (vp->v_mount->mnt_flag & MNT_RDONLY)) {
3317a2de9a4SMatthew Dillon error = EROFS;
3327a2de9a4SMatthew Dillon goto out;
3337a2de9a4SMatthew Dillon }
3347a2de9a4SMatthew Dillon break;
3357a2de9a4SMatthew Dillon
3367a2de9a4SMatthew Dillon case VBLK:
3377a2de9a4SMatthew Dillon /* FALLTHROUGH */
3387a2de9a4SMatthew Dillon case VCHR:
3397a2de9a4SMatthew Dillon /* FALLTHROUGH */
3407a2de9a4SMatthew Dillon case VSOCK:
3417a2de9a4SMatthew Dillon /* FALLTHROUGH */
3427a2de9a4SMatthew Dillon case VFIFO:
3437a2de9a4SMatthew Dillon break;
3447a2de9a4SMatthew Dillon
3457a2de9a4SMatthew Dillon default:
3467a2de9a4SMatthew Dillon error = EINVAL;
3477a2de9a4SMatthew Dillon goto out;
3487a2de9a4SMatthew Dillon }
3497a2de9a4SMatthew Dillon
350ba25006aSSascha Wildner if ((ap->a_mode & VWRITE) && (node->tn_flags & IMMUTABLE)) {
3517a2de9a4SMatthew Dillon error = EPERM;
3527a2de9a4SMatthew Dillon goto out;
3537a2de9a4SMatthew Dillon }
3547a2de9a4SMatthew Dillon
355ba25006aSSascha Wildner error = vop_helper_access(ap, node->tn_uid, node->tn_gid,
356aa1adbf0SMatthew Dillon node->tn_mode, 0);
3577a2de9a4SMatthew Dillon out:
3587a2de9a4SMatthew Dillon return error;
3597a2de9a4SMatthew Dillon }
3607a2de9a4SMatthew Dillon
3617a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
3627a2de9a4SMatthew Dillon
3637a2de9a4SMatthew Dillon int
tmpfs_getattr(struct vop_getattr_args * ap)364ba25006aSSascha Wildner tmpfs_getattr(struct vop_getattr_args *ap)
3657a2de9a4SMatthew Dillon {
366ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
367ba25006aSSascha Wildner struct vattr *vap = ap->a_vap;
3687a2de9a4SMatthew Dillon struct tmpfs_node *node;
3697a2de9a4SMatthew Dillon
3707a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
3717a2de9a4SMatthew Dillon
3727a2de9a4SMatthew Dillon tmpfs_update(vp);
3737a2de9a4SMatthew Dillon
3747a2de9a4SMatthew Dillon vap->va_type = vp->v_type;
3757a2de9a4SMatthew Dillon vap->va_mode = node->tn_mode;
3767a2de9a4SMatthew Dillon vap->va_nlink = node->tn_links;
3777a2de9a4SMatthew Dillon vap->va_uid = node->tn_uid;
3787a2de9a4SMatthew Dillon vap->va_gid = node->tn_gid;
3797a2de9a4SMatthew Dillon vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
3807a2de9a4SMatthew Dillon vap->va_fileid = node->tn_id;
3817a2de9a4SMatthew Dillon vap->va_size = node->tn_size;
3827a2de9a4SMatthew Dillon vap->va_blocksize = PAGE_SIZE;
3837a2de9a4SMatthew Dillon vap->va_atime.tv_sec = node->tn_atime;
3847a2de9a4SMatthew Dillon vap->va_atime.tv_nsec = node->tn_atimensec;
3857a2de9a4SMatthew Dillon vap->va_mtime.tv_sec = node->tn_mtime;
3867a2de9a4SMatthew Dillon vap->va_mtime.tv_nsec = node->tn_mtimensec;
3877a2de9a4SMatthew Dillon vap->va_ctime.tv_sec = node->tn_ctime;
3887a2de9a4SMatthew Dillon vap->va_ctime.tv_nsec = node->tn_ctimensec;
3897a2de9a4SMatthew Dillon vap->va_gen = node->tn_gen;
3907a2de9a4SMatthew Dillon vap->va_flags = node->tn_flags;
391ff837cd5SMatthew Dillon if (vp->v_type == VBLK || vp->v_type == VCHR) {
3927a2de9a4SMatthew Dillon vap->va_rmajor = umajor(node->tn_rdev);
3937a2de9a4SMatthew Dillon vap->va_rminor = uminor(node->tn_rdev);
3947a2de9a4SMatthew Dillon }
3957a2de9a4SMatthew Dillon vap->va_bytes = round_page(node->tn_size);
3967a2de9a4SMatthew Dillon vap->va_filerev = 0;
397e575e508SVenkatesh Srinivas
3987a2de9a4SMatthew Dillon return 0;
3997a2de9a4SMatthew Dillon }
4007a2de9a4SMatthew Dillon
4017a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
4027a2de9a4SMatthew Dillon
4037a2de9a4SMatthew Dillon int
tmpfs_getattr_lite(struct vop_getattr_lite_args * ap)404de9bb133SMatthew Dillon tmpfs_getattr_lite(struct vop_getattr_lite_args *ap)
405fc36a10bSMatthew Dillon {
406fc36a10bSMatthew Dillon struct vnode *vp = ap->a_vp;
407de9bb133SMatthew Dillon struct vattr_lite *lvap = ap->a_lvap;
408fc36a10bSMatthew Dillon struct tmpfs_node *node;
409fc36a10bSMatthew Dillon
410fc36a10bSMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
411fc36a10bSMatthew Dillon
412fc36a10bSMatthew Dillon tmpfs_update(vp);
413fc36a10bSMatthew Dillon
414de9bb133SMatthew Dillon lvap->va_type = vp->v_type;
415de9bb133SMatthew Dillon lvap->va_mode = node->tn_mode;
416de9bb133SMatthew Dillon lvap->va_nlink = node->tn_links;
417de9bb133SMatthew Dillon lvap->va_uid = node->tn_uid;
418de9bb133SMatthew Dillon lvap->va_gid = node->tn_gid;
419de9bb133SMatthew Dillon #if 0
420fc36a10bSMatthew Dillon vap->va_fsid = vp->v_mount->mnt_stat.f_fsid.val[0];
421fc36a10bSMatthew Dillon vap->va_fileid = node->tn_id;
422de9bb133SMatthew Dillon #endif
423de9bb133SMatthew Dillon lvap->va_size = node->tn_size;
424de9bb133SMatthew Dillon #if 0
425fc36a10bSMatthew Dillon vap->va_blocksize = PAGE_SIZE;
426fc36a10bSMatthew Dillon vap->va_gen = node->tn_gen;
427de9bb133SMatthew Dillon #endif
428de9bb133SMatthew Dillon lvap->va_flags = node->tn_flags;
429de9bb133SMatthew Dillon #if 0
430fc36a10bSMatthew Dillon if (vp->v_type == VBLK || vp->v_type == VCHR) {
431fc36a10bSMatthew Dillon vap->va_rmajor = umajor(node->tn_rdev);
432fc36a10bSMatthew Dillon vap->va_rminor = uminor(node->tn_rdev);
433fc36a10bSMatthew Dillon }
434fc36a10bSMatthew Dillon vap->va_bytes = -1;
435fc36a10bSMatthew Dillon vap->va_filerev = 0;
436de9bb133SMatthew Dillon #endif
437fc36a10bSMatthew Dillon
438fc36a10bSMatthew Dillon return 0;
439fc36a10bSMatthew Dillon }
440fc36a10bSMatthew Dillon
441fc36a10bSMatthew Dillon
442fc36a10bSMatthew Dillon /* --------------------------------------------------------------------- */
443fc36a10bSMatthew Dillon
444fc36a10bSMatthew Dillon int
tmpfs_setattr(struct vop_setattr_args * ap)445ba25006aSSascha Wildner tmpfs_setattr(struct vop_setattr_args *ap)
4467a2de9a4SMatthew Dillon {
447ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
448ba25006aSSascha Wildner struct vattr *vap = ap->a_vap;
449ba25006aSSascha Wildner struct ucred *cred = ap->a_cred;
45080ae59d7SMatthew Dillon struct tmpfs_node *node = VP_TO_TMPFS_NODE(vp);
4517a2de9a4SMatthew Dillon int error = 0;
45280ae59d7SMatthew Dillon int kflags = 0;
4537a2de9a4SMatthew Dillon
454ff837cd5SMatthew Dillon TMPFS_NODE_LOCK(node);
45580ae59d7SMatthew Dillon if (error == 0 && (vap->va_flags != VNOVAL)) {
4567a2de9a4SMatthew Dillon error = tmpfs_chflags(vp, vap->va_flags, cred);
45780ae59d7SMatthew Dillon kflags |= NOTE_ATTRIB;
45880ae59d7SMatthew Dillon }
4597a2de9a4SMatthew Dillon
46080ae59d7SMatthew Dillon if (error == 0 && (vap->va_size != VNOVAL)) {
461a1b829f2SMatthew Dillon /* restore any saved pages before proceeding */
462a1b829f2SMatthew Dillon if (node->tn_reg.tn_pages_in_aobj) {
463cfd59c5aSMatthew Dillon tmpfs_move_pages(node->tn_reg.tn_aobj, vp->v_object,
464cfd59c5aSMatthew Dillon TMPFS_MOVF_FROMBACKING |
465cfd59c5aSMatthew Dillon TMPFS_MOVF_DEACTIVATE);
466a1b829f2SMatthew Dillon node->tn_reg.tn_pages_in_aobj = 0;
467a1b829f2SMatthew Dillon }
46880ae59d7SMatthew Dillon if (vap->va_size > node->tn_size)
46980ae59d7SMatthew Dillon kflags |= NOTE_WRITE | NOTE_EXTEND;
47080ae59d7SMatthew Dillon else
47180ae59d7SMatthew Dillon kflags |= NOTE_WRITE;
4727a2de9a4SMatthew Dillon error = tmpfs_chsize(vp, vap->va_size, cred);
47380ae59d7SMatthew Dillon }
4747a2de9a4SMatthew Dillon
475d89ce96aSMatthew Dillon if (error == 0 && (vap->va_uid != (uid_t)VNOVAL ||
476d89ce96aSMatthew Dillon vap->va_gid != (gid_t)VNOVAL)) {
4777a2de9a4SMatthew Dillon error = tmpfs_chown(vp, vap->va_uid, vap->va_gid, cred);
47880ae59d7SMatthew Dillon kflags |= NOTE_ATTRIB;
479d89ce96aSMatthew Dillon }
4807a2de9a4SMatthew Dillon
48180ae59d7SMatthew Dillon if (error == 0 && (vap->va_mode != (mode_t)VNOVAL)) {
4827a2de9a4SMatthew Dillon error = tmpfs_chmod(vp, vap->va_mode, cred);
48380ae59d7SMatthew Dillon kflags |= NOTE_ATTRIB;
48480ae59d7SMatthew Dillon }
4857a2de9a4SMatthew Dillon
4867a2de9a4SMatthew Dillon if (error == 0 && ((vap->va_atime.tv_sec != VNOVAL &&
4877a2de9a4SMatthew Dillon vap->va_atime.tv_nsec != VNOVAL) ||
4887a2de9a4SMatthew Dillon (vap->va_mtime.tv_sec != VNOVAL &&
489d89ce96aSMatthew Dillon vap->va_mtime.tv_nsec != VNOVAL) )) {
4907a2de9a4SMatthew Dillon error = tmpfs_chtimes(vp, &vap->va_atime, &vap->va_mtime,
4917a2de9a4SMatthew Dillon vap->va_vaflags, cred);
49280ae59d7SMatthew Dillon kflags |= NOTE_ATTRIB;
493d89ce96aSMatthew Dillon }
4947a2de9a4SMatthew Dillon
495ff837cd5SMatthew Dillon /*
496ff837cd5SMatthew Dillon * Update the node times. We give preference to the error codes
4977a2de9a4SMatthew Dillon * generated by this function rather than the ones that may arise
498ff837cd5SMatthew Dillon * from tmpfs_update.
499ff837cd5SMatthew Dillon */
5007a2de9a4SMatthew Dillon tmpfs_update(vp);
501ff837cd5SMatthew Dillon TMPFS_NODE_UNLOCK(node);
50280ae59d7SMatthew Dillon tmpfs_knote(vp, kflags);
5037a2de9a4SMatthew Dillon
504aa1adbf0SMatthew Dillon return (error);
5057a2de9a4SMatthew Dillon }
5067a2de9a4SMatthew Dillon
5077a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
5087a2de9a4SMatthew Dillon
5099fc94b5fSMatthew Dillon /*
510630e3a33SMatthew Dillon * fsync is usually a NOP, but we must take action when unmounting or
511630e3a33SMatthew Dillon * when recycling.
5129fc94b5fSMatthew Dillon */
5137a2de9a4SMatthew Dillon static int
tmpfs_fsync(struct vop_fsync_args * ap)514ba25006aSSascha Wildner tmpfs_fsync(struct vop_fsync_args *ap)
5157a2de9a4SMatthew Dillon {
516630e3a33SMatthew Dillon struct tmpfs_node *node;
517ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
5187a2de9a4SMatthew Dillon
519630e3a33SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
520630e3a33SMatthew Dillon
521d883c473SMatthew Dillon /*
522d883c473SMatthew Dillon * tmpfs vnodes typically remain dirty, avoid long syncer scans
523d883c473SMatthew Dillon * by forcing removal from the syncer list.
524d883c473SMatthew Dillon */
525d883c473SMatthew Dillon vn_syncer_remove(vp, 1);
526d883c473SMatthew Dillon
5277a2de9a4SMatthew Dillon tmpfs_update(vp);
528630e3a33SMatthew Dillon if (vp->v_type == VREG) {
529d4623db3SMatthew Dillon if (vp->v_flag & VRECLAIMED) {
530630e3a33SMatthew Dillon if (node->tn_links == 0)
531630e3a33SMatthew Dillon tmpfs_truncate(vp, 0);
532630e3a33SMatthew Dillon else
533ba25006aSSascha Wildner vfsync(ap->a_vp, ap->a_waitfor, 1, NULL, NULL);
534630e3a33SMatthew Dillon }
5359fc94b5fSMatthew Dillon }
536d883c473SMatthew Dillon
5377a2de9a4SMatthew Dillon return 0;
5387a2de9a4SMatthew Dillon }
5397a2de9a4SMatthew Dillon
5407a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
5417a2de9a4SMatthew Dillon
5427a2de9a4SMatthew Dillon static int
tmpfs_read(struct vop_read_args * ap)5437a2de9a4SMatthew Dillon tmpfs_read(struct vop_read_args *ap)
5447a2de9a4SMatthew Dillon {
5457a2de9a4SMatthew Dillon struct buf *bp;
5467a2de9a4SMatthew Dillon struct vnode *vp = ap->a_vp;
5477a2de9a4SMatthew Dillon struct uio *uio = ap->a_uio;
5487a2de9a4SMatthew Dillon struct tmpfs_node *node;
5497a2de9a4SMatthew Dillon off_t base_offset;
5509fc94b5fSMatthew Dillon size_t offset;
5517a2de9a4SMatthew Dillon size_t len;
5522446b819SMatthew Dillon size_t resid;
5539fc94b5fSMatthew Dillon int error;
554a1b829f2SMatthew Dillon int seqcount;
5557a2de9a4SMatthew Dillon
5562446b819SMatthew Dillon /*
5572446b819SMatthew Dillon * Check the basics
5582446b819SMatthew Dillon */
5597a2de9a4SMatthew Dillon if (uio->uio_offset < 0)
5607a2de9a4SMatthew Dillon return (EINVAL);
5617a2de9a4SMatthew Dillon if (vp->v_type != VREG)
5627a2de9a4SMatthew Dillon return (EINVAL);
5637a2de9a4SMatthew Dillon
5642446b819SMatthew Dillon /*
5652446b819SMatthew Dillon * Extract node, try to shortcut the operation through
5662446b819SMatthew Dillon * the VM page cache, allowing us to avoid buffer cache
5672446b819SMatthew Dillon * overheads.
5682446b819SMatthew Dillon */
5692446b819SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
5702446b819SMatthew Dillon resid = uio->uio_resid;
5710d540a44STomohiro Kusumi seqcount = ap->a_ioflag >> IO_SEQSHIFT;
5722446b819SMatthew Dillon error = vop_helper_read_shortcut(ap);
5732446b819SMatthew Dillon if (error)
5742446b819SMatthew Dillon return error;
5752446b819SMatthew Dillon if (uio->uio_resid == 0) {
5762446b819SMatthew Dillon if (resid)
5772446b819SMatthew Dillon goto finished;
5782446b819SMatthew Dillon return error;
5792446b819SMatthew Dillon }
5802446b819SMatthew Dillon
5812446b819SMatthew Dillon /*
582a1b829f2SMatthew Dillon * restore any saved pages before proceeding
583a1b829f2SMatthew Dillon */
584a1b829f2SMatthew Dillon if (node->tn_reg.tn_pages_in_aobj) {
585a1b829f2SMatthew Dillon TMPFS_NODE_LOCK(node);
586a1b829f2SMatthew Dillon if (node->tn_reg.tn_pages_in_aobj) {
587cfd59c5aSMatthew Dillon tmpfs_move_pages(node->tn_reg.tn_aobj, vp->v_object,
588cfd59c5aSMatthew Dillon TMPFS_MOVF_FROMBACKING);
589a1b829f2SMatthew Dillon node->tn_reg.tn_pages_in_aobj = 0;
590a1b829f2SMatthew Dillon }
591a1b829f2SMatthew Dillon TMPFS_NODE_UNLOCK(node);
592a1b829f2SMatthew Dillon }
593a1b829f2SMatthew Dillon
594a1b829f2SMatthew Dillon /*
5952446b819SMatthew Dillon * Fall-through to our normal read code.
5962446b819SMatthew Dillon */
5977a2de9a4SMatthew Dillon while (uio->uio_resid > 0 && uio->uio_offset < node->tn_size) {
5987a2de9a4SMatthew Dillon /*
5997a2de9a4SMatthew Dillon * Use buffer cache I/O (via tmpfs_strategy)
6007a2de9a4SMatthew Dillon */
6013c54bb74SMatthew Dillon offset = (size_t)uio->uio_offset & TMPFS_BLKMASK64;
6027a2de9a4SMatthew Dillon base_offset = (off_t)uio->uio_offset - offset;
6039cd86db5SMatthew Dillon bp = getcacheblk(vp, base_offset,
6049cd86db5SMatthew Dillon node->tn_blksize, GETBLK_KVABIO);
6059de13b88SMatthew Dillon if (bp == NULL) {
606cfd59c5aSMatthew Dillon if (tmpfs_cluster_rd_enable) {
607a1b829f2SMatthew Dillon error = cluster_readx(vp, node->tn_size,
608a1b829f2SMatthew Dillon base_offset,
6099cd86db5SMatthew Dillon node->tn_blksize,
610a1b829f2SMatthew Dillon B_NOTMETA | B_KVABIO,
611a1b829f2SMatthew Dillon uio->uio_resid,
612a1b829f2SMatthew Dillon seqcount * MAXBSIZE,
613a1b829f2SMatthew Dillon &bp);
614a1b829f2SMatthew Dillon } else {
61548db4e20SMatthew Dillon error = bread_kvabio(vp, base_offset,
6169cd86db5SMatthew Dillon node->tn_blksize, &bp);
617a1b829f2SMatthew Dillon }
6187a2de9a4SMatthew Dillon if (error) {
6197a2de9a4SMatthew Dillon brelse(bp);
6207a2de9a4SMatthew Dillon kprintf("tmpfs_read bread error %d\n", error);
6217a2de9a4SMatthew Dillon break;
6227a2de9a4SMatthew Dillon }
623527cd77dSMatthew Dillon
624527cd77dSMatthew Dillon /*
625527cd77dSMatthew Dillon * tmpfs pretty much fiddles directly with the VM
626527cd77dSMatthew Dillon * system, don't let it exhaust it or we won't play
627527cd77dSMatthew Dillon * nice with other processes.
628527cd77dSMatthew Dillon *
629527cd77dSMatthew Dillon * Only do this if the VOP is coming from a normal
630527cd77dSMatthew Dillon * read/write. The VM system handles the case for
631527cd77dSMatthew Dillon * UIO_NOCOPY.
632527cd77dSMatthew Dillon */
633527cd77dSMatthew Dillon if (uio->uio_segflg != UIO_NOCOPY)
634527cd77dSMatthew Dillon vm_wait_nominal();
6357a2de9a4SMatthew Dillon }
6363c54bb74SMatthew Dillon bp->b_flags |= B_CLUSTEROK;
637b3f55d88SMatthew Dillon bkvasync(bp);
6387a2de9a4SMatthew Dillon
6397a2de9a4SMatthew Dillon /*
6407a2de9a4SMatthew Dillon * Figure out how many bytes we can actually copy this loop.
6417a2de9a4SMatthew Dillon */
6429cd86db5SMatthew Dillon len = node->tn_blksize - offset;
6437a2de9a4SMatthew Dillon if (len > uio->uio_resid)
6447a2de9a4SMatthew Dillon len = uio->uio_resid;
6457a2de9a4SMatthew Dillon if (len > node->tn_size - uio->uio_offset)
6467a2de9a4SMatthew Dillon len = (size_t)(node->tn_size - uio->uio_offset);
6477a2de9a4SMatthew Dillon
64844480e31SMatthew Dillon error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio);
6497a2de9a4SMatthew Dillon bqrelse(bp);
6507a2de9a4SMatthew Dillon if (error) {
6517a2de9a4SMatthew Dillon kprintf("tmpfs_read uiomove error %d\n", error);
6527a2de9a4SMatthew Dillon break;
6537a2de9a4SMatthew Dillon }
6547a2de9a4SMatthew Dillon }
6557a2de9a4SMatthew Dillon
6562446b819SMatthew Dillon finished:
657bed3b851STomohiro Kusumi if ((node->tn_status & TMPFS_NODE_ACCESSED) == 0) {
6587a2de9a4SMatthew Dillon TMPFS_NODE_LOCK(node);
6597a2de9a4SMatthew Dillon node->tn_status |= TMPFS_NODE_ACCESSED;
6607a2de9a4SMatthew Dillon TMPFS_NODE_UNLOCK(node);
661bed3b851STomohiro Kusumi }
6627a2de9a4SMatthew Dillon return (error);
6637a2de9a4SMatthew Dillon }
6647a2de9a4SMatthew Dillon
6657a2de9a4SMatthew Dillon static int
tmpfs_write(struct vop_write_args * ap)6667a2de9a4SMatthew Dillon tmpfs_write(struct vop_write_args *ap)
6677a2de9a4SMatthew Dillon {
6687a2de9a4SMatthew Dillon struct buf *bp;
6697a2de9a4SMatthew Dillon struct vnode *vp = ap->a_vp;
6707a2de9a4SMatthew Dillon struct uio *uio = ap->a_uio;
6717a2de9a4SMatthew Dillon struct thread *td = uio->uio_td;
6727a2de9a4SMatthew Dillon struct tmpfs_node *node;
6737a2de9a4SMatthew Dillon boolean_t extended;
6747a2de9a4SMatthew Dillon off_t oldsize;
6757a2de9a4SMatthew Dillon int error;
6767a2de9a4SMatthew Dillon off_t base_offset;
6779fc94b5fSMatthew Dillon size_t offset;
6787a2de9a4SMatthew Dillon size_t len;
6797a2de9a4SMatthew Dillon struct rlimit limit;
6807a2de9a4SMatthew Dillon int trivial = 0;
68180ae59d7SMatthew Dillon int kflags = 0;
6823c54bb74SMatthew Dillon int seqcount;
6837a2de9a4SMatthew Dillon
6847a2de9a4SMatthew Dillon error = 0;
6857a2de9a4SMatthew Dillon if (uio->uio_resid == 0) {
6867a2de9a4SMatthew Dillon return error;
6877a2de9a4SMatthew Dillon }
6887a2de9a4SMatthew Dillon
6897a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
6907a2de9a4SMatthew Dillon
6917a2de9a4SMatthew Dillon if (vp->v_type != VREG)
6927a2de9a4SMatthew Dillon return (EINVAL);
6930d540a44STomohiro Kusumi seqcount = ap->a_ioflag >> IO_SEQSHIFT;
6947a2de9a4SMatthew Dillon
695b37a7c00SMatthew Dillon TMPFS_NODE_LOCK(node);
696b37a7c00SMatthew Dillon
697a1b829f2SMatthew Dillon /*
698a1b829f2SMatthew Dillon * restore any saved pages before proceeding
699a1b829f2SMatthew Dillon */
700a1b829f2SMatthew Dillon if (node->tn_reg.tn_pages_in_aobj) {
701cfd59c5aSMatthew Dillon tmpfs_move_pages(node->tn_reg.tn_aobj, vp->v_object,
702cfd59c5aSMatthew Dillon TMPFS_MOVF_FROMBACKING);
703a1b829f2SMatthew Dillon node->tn_reg.tn_pages_in_aobj = 0;
704a1b829f2SMatthew Dillon }
705a1b829f2SMatthew Dillon
7067a2de9a4SMatthew Dillon oldsize = node->tn_size;
7077a2de9a4SMatthew Dillon if (ap->a_ioflag & IO_APPEND)
7087a2de9a4SMatthew Dillon uio->uio_offset = node->tn_size;
7097a2de9a4SMatthew Dillon
7107a2de9a4SMatthew Dillon /*
7117a2de9a4SMatthew Dillon * Check for illegal write offsets.
7127a2de9a4SMatthew Dillon */
7137a2de9a4SMatthew Dillon if (uio->uio_offset + uio->uio_resid >
7141be4932cSVenkatesh Srinivas VFS_TO_TMPFS(vp->v_mount)->tm_maxfilesize) {
715b37a7c00SMatthew Dillon error = EFBIG;
716b37a7c00SMatthew Dillon goto done;
7171be4932cSVenkatesh Srinivas }
7187a2de9a4SMatthew Dillon
7194c5cdd6bSMatthew Dillon /*
7204c5cdd6bSMatthew Dillon * NOTE: Ignore if UIO does not come from a user thread (e.g. VN).
7214c5cdd6bSMatthew Dillon */
7224c5cdd6bSMatthew Dillon if (vp->v_type == VREG && td != NULL && td->td_lwp != NULL) {
7237a2de9a4SMatthew Dillon error = kern_getrlimit(RLIMIT_FSIZE, &limit);
724b37a7c00SMatthew Dillon if (error)
725b37a7c00SMatthew Dillon goto done;
7267a2de9a4SMatthew Dillon if (uio->uio_offset + uio->uio_resid > limit.rlim_cur) {
7277a2de9a4SMatthew Dillon ksignal(td->td_proc, SIGXFSZ);
728b37a7c00SMatthew Dillon error = EFBIG;
729b37a7c00SMatthew Dillon goto done;
7307a2de9a4SMatthew Dillon }
7317a2de9a4SMatthew Dillon }
7327a2de9a4SMatthew Dillon
7337a2de9a4SMatthew Dillon /*
7347a2de9a4SMatthew Dillon * Extend the file's size if necessary
7357a2de9a4SMatthew Dillon */
7369fc94b5fSMatthew Dillon extended = ((uio->uio_offset + uio->uio_resid) > node->tn_size);
7377a2de9a4SMatthew Dillon
7387a2de9a4SMatthew Dillon while (uio->uio_resid > 0) {
7397a2de9a4SMatthew Dillon /*
7403c54bb74SMatthew Dillon * Don't completely blow out running buffer I/O
7413c54bb74SMatthew Dillon * when being hit from the pageout daemon.
7423c54bb74SMatthew Dillon */
7433c54bb74SMatthew Dillon if (uio->uio_segflg == UIO_NOCOPY &&
7443c54bb74SMatthew Dillon (ap->a_ioflag & IO_RECURSE) == 0) {
7459cd86db5SMatthew Dillon bwillwrite(node->tn_blksize);
7463c54bb74SMatthew Dillon }
7473c54bb74SMatthew Dillon
7483c54bb74SMatthew Dillon /*
7497a2de9a4SMatthew Dillon * Use buffer cache I/O (via tmpfs_strategy)
7509cd86db5SMatthew Dillon *
7519cd86db5SMatthew Dillon * Calculate the maximum bytes we can write to the buffer at
7529cd86db5SMatthew Dillon * this offset (after resizing).
7537a2de9a4SMatthew Dillon */
7543c54bb74SMatthew Dillon offset = (size_t)uio->uio_offset & TMPFS_BLKMASK64;
7557a2de9a4SMatthew Dillon base_offset = (off_t)uio->uio_offset - offset;
7567a2de9a4SMatthew Dillon len = uio->uio_resid;
7579cd86db5SMatthew Dillon if (len > TMPFS_BLKSIZE - offset)
7589cd86db5SMatthew Dillon len = TMPFS_BLKSIZE - offset;
7597a2de9a4SMatthew Dillon
7607a2de9a4SMatthew Dillon if ((uio->uio_offset + len) > node->tn_size) {
7619fc94b5fSMatthew Dillon trivial = (uio->uio_offset <= node->tn_size);
762b37a7c00SMatthew Dillon error = tmpfs_reg_resize(vp, uio->uio_offset + len,
763b37a7c00SMatthew Dillon trivial);
7647a2de9a4SMatthew Dillon if (error)
7657a2de9a4SMatthew Dillon break;
7667a2de9a4SMatthew Dillon }
7677a2de9a4SMatthew Dillon
7689fc94b5fSMatthew Dillon /*
7699fc94b5fSMatthew Dillon * Read to fill in any gaps. Theoretically we could
7709fc94b5fSMatthew Dillon * optimize this if the write covers the entire buffer
7719fc94b5fSMatthew Dillon * and is not a UIO_NOCOPY write, however this can lead
7729fc94b5fSMatthew Dillon * to a security violation exposing random kernel memory
7739fc94b5fSMatthew Dillon * (whatever junk was in the backing VM pages before).
7749fc94b5fSMatthew Dillon *
7759fc94b5fSMatthew Dillon * So just use bread() to do the right thing.
7769fc94b5fSMatthew Dillon */
7779cd86db5SMatthew Dillon error = bread_kvabio(vp, base_offset, node->tn_blksize, &bp);
778b3f55d88SMatthew Dillon bkvasync(bp);
77944480e31SMatthew Dillon error = uiomovebp(bp, (char *)bp->b_data + offset, len, uio);
7807a2de9a4SMatthew Dillon if (error) {
7817a2de9a4SMatthew Dillon kprintf("tmpfs_write uiomove error %d\n", error);
7827a2de9a4SMatthew Dillon brelse(bp);
7837a2de9a4SMatthew Dillon break;
7847a2de9a4SMatthew Dillon }
7857a2de9a4SMatthew Dillon
78680ae59d7SMatthew Dillon if (uio->uio_offset > node->tn_size) {
7877a2de9a4SMatthew Dillon node->tn_size = uio->uio_offset;
78880ae59d7SMatthew Dillon kflags |= NOTE_EXTEND;
78980ae59d7SMatthew Dillon }
79080ae59d7SMatthew Dillon kflags |= NOTE_WRITE;
7917a2de9a4SMatthew Dillon
7927a2de9a4SMatthew Dillon /*
7934eb0bb82SMatthew Dillon * UIO_NOCOPY is a sensitive state due to potentially being
7944eb0bb82SMatthew Dillon * issued from the pageout daemon while in a low-memory
7954eb0bb82SMatthew Dillon * situation. However, in order to cluster the I/O nicely
7964eb0bb82SMatthew Dillon * (e.g. 64KB+ writes instead of 16KB writes), we still try
7974eb0bb82SMatthew Dillon * to follow the same semantics that any other filesystem
7984eb0bb82SMatthew Dillon * might use.
7997a2de9a4SMatthew Dillon *
800d86d27a8SMatthew Dillon * For the normal case we buwrite(), dirtying the underlying
801d86d27a8SMatthew Dillon * VM pages instead of dirtying the buffer and releasing the
802d86d27a8SMatthew Dillon * buffer as a clean buffer. This allows tmpfs to use
803d86d27a8SMatthew Dillon * essentially all available memory to cache file data.
804d86d27a8SMatthew Dillon * If we used bdwrite() the buffer cache would wind up
805d86d27a8SMatthew Dillon * flushing the data to swap too quickly.
806527cd77dSMatthew Dillon *
8073c54bb74SMatthew Dillon * But because tmpfs can seriously load the VM system we
8083c54bb74SMatthew Dillon * fall-back to using bdwrite() when free memory starts
8093c54bb74SMatthew Dillon * to get low. This shifts the load away from the VM system
8103c54bb74SMatthew Dillon * and makes tmpfs act more like a normal filesystem with
8113c54bb74SMatthew Dillon * regards to disk activity.
8123c54bb74SMatthew Dillon *
813527cd77dSMatthew Dillon * tmpfs pretty much fiddles directly with the VM
814527cd77dSMatthew Dillon * system, don't let it exhaust it or we won't play
815527cd77dSMatthew Dillon * nice with other processes. Only do this if the
816527cd77dSMatthew Dillon * VOP is coming from a normal read/write. The VM system
817527cd77dSMatthew Dillon * handles the case for UIO_NOCOPY.
8187a2de9a4SMatthew Dillon */
8193c54bb74SMatthew Dillon bp->b_flags |= B_CLUSTEROK;
820d86d27a8SMatthew Dillon if (uio->uio_segflg == UIO_NOCOPY) {
8216ff1f162SMatthew Dillon /*
822108fbfcbSMatthew Dillon * Flush from the pageout daemon, deal with potentially
823108fbfcbSMatthew Dillon * very heavy tmpfs write activity causing long stalls
824108fbfcbSMatthew Dillon * in the pageout daemon before pages get to free/cache.
825108fbfcbSMatthew Dillon *
826108fbfcbSMatthew Dillon * We have to be careful not to bypass the page queues
827108fbfcbSMatthew Dillon * entirely or we can cause write-read thrashing and
828108fbfcbSMatthew Dillon * delay the paging of data that is more pageable then
829108fbfcbSMatthew Dillon * our current data.
8306ff1f162SMatthew Dillon *
8316ff1f162SMatthew Dillon * (a) Under severe pressure setting B_DIRECT will
8326ff1f162SMatthew Dillon * cause a buffer release to try to free the
8336ff1f162SMatthew Dillon * underlying pages.
8346ff1f162SMatthew Dillon *
835108fbfcbSMatthew Dillon * (b) Under modest memory pressure the B_AGE flag
836108fbfcbSMatthew Dillon * we retire the buffer and its underlying pages
837108fbfcbSMatthew Dillon * more quickly than normal.
838108fbfcbSMatthew Dillon *
839108fbfcbSMatthew Dillon * We could also force this by setting B_NOTMETA
840108fbfcbSMatthew Dillon * but that might have other unintended side-
841108fbfcbSMatthew Dillon * effects (e.g. setting PG_NOTMETA on the VM page).
8426ff1f162SMatthew Dillon *
8434eb0bb82SMatthew Dillon * (c) For the pageout->putpages->generic_putpages->
8444eb0bb82SMatthew Dillon * UIO_NOCOPY-write (here), issuing an immediate
8454eb0bb82SMatthew Dillon * write prevents any real clustering from
8464eb0bb82SMatthew Dillon * happening because the buffers probably aren't
8474eb0bb82SMatthew Dillon * (yet) marked dirty, or lost due to prior use
8484eb0bb82SMatthew Dillon * of buwrite(). Try to use the normal
8494eb0bb82SMatthew Dillon * cluster_write() mechanism for performance.
8504eb0bb82SMatthew Dillon *
8516ff1f162SMatthew Dillon * Hopefully this will unblock the VM system more
8526ff1f162SMatthew Dillon * quickly under extreme tmpfs write load.
8536ff1f162SMatthew Dillon */
854307bf766SMatthew Dillon if (tmpfs_bufcache_mode >= 2) {
855e91e64c7SMatthew Dillon if (vm_paging_min_dnc(vm_page_free_hysteresis))
856108fbfcbSMatthew Dillon bp->b_flags |= B_DIRECT | B_TTC;
857e91e64c7SMatthew Dillon if (vm_pages_needed || vm_paging_start(0))
858108fbfcbSMatthew Dillon bp->b_flags |= B_AGE;
859108fbfcbSMatthew Dillon }
860307bf766SMatthew Dillon bp->b_flags |= B_RELBUF;
8613c54bb74SMatthew Dillon bp->b_act_count = 0; /* buffer->deactivate pgs */
8624eb0bb82SMatthew Dillon if (tmpfs_cluster_wr_enable &&
8634eb0bb82SMatthew Dillon (ap->a_ioflag & (IO_SYNC | IO_DIRECT)) == 0) {
8644eb0bb82SMatthew Dillon cluster_write(bp, node->tn_size,
8659cd86db5SMatthew Dillon node->tn_blksize, seqcount);
8664eb0bb82SMatthew Dillon } else {
8673c54bb74SMatthew Dillon cluster_awrite(bp);
8684eb0bb82SMatthew Dillon }
869e91e64c7SMatthew Dillon } else if (vm_paging_min() ||
870e91e64c7SMatthew Dillon ((vm_pages_needed || vm_paging_start(0)) &&
871307bf766SMatthew Dillon tmpfs_bufcache_mode >= 1)) {
8726ff1f162SMatthew Dillon /*
873ca1197e2SMatthew Dillon * If the pageout daemon is running we cycle the
874ca1197e2SMatthew Dillon * write through the buffer cache normally to
875ca1197e2SMatthew Dillon * pipeline the flush, thus avoiding adding any
876ca1197e2SMatthew Dillon * more memory pressure to the pageout daemon.
8776ff1f162SMatthew Dillon */
8783c54bb74SMatthew Dillon bp->b_act_count = 0; /* buffer->deactivate pgs */
879cfd59c5aSMatthew Dillon if (tmpfs_cluster_wr_enable) {
880cfd59c5aSMatthew Dillon cluster_write(bp, node->tn_size,
8819cd86db5SMatthew Dillon node->tn_blksize, seqcount);
882cfd59c5aSMatthew Dillon } else {
8833c54bb74SMatthew Dillon bdwrite(bp);
884cfd59c5aSMatthew Dillon }
885d89ce96aSMatthew Dillon } else {
8866ff1f162SMatthew Dillon /*
8876ff1f162SMatthew Dillon * Otherwise run the buffer directly through to the
888cfd59c5aSMatthew Dillon * backing VM store, leaving the buffer clean so
889cfd59c5aSMatthew Dillon * buffer limits do not force early flushes to swap.
8906ff1f162SMatthew Dillon */
8919fc94b5fSMatthew Dillon buwrite(bp);
8923c54bb74SMatthew Dillon /*vm_wait_nominal();*/
893d89ce96aSMatthew Dillon }
8949fc94b5fSMatthew Dillon
8957a2de9a4SMatthew Dillon if (bp->b_error) {
8962cd8c774SRumko kprintf("tmpfs_write bwrite error %d\n", bp->b_error);
8977a2de9a4SMatthew Dillon break;
8987a2de9a4SMatthew Dillon }
8997a2de9a4SMatthew Dillon }
9007a2de9a4SMatthew Dillon
9017a2de9a4SMatthew Dillon if (error) {
90280ae59d7SMatthew Dillon if (extended) {
9037a2de9a4SMatthew Dillon (void)tmpfs_reg_resize(vp, oldsize, trivial);
90480ae59d7SMatthew Dillon kflags &= ~NOTE_EXTEND;
90580ae59d7SMatthew Dillon }
90680ae59d7SMatthew Dillon goto done;
9077a2de9a4SMatthew Dillon }
9087a2de9a4SMatthew Dillon
909fc1a9118SMatthew Dillon /*
910fc1a9118SMatthew Dillon * Currently we don't set the mtime on files modified via mmap()
911fc1a9118SMatthew Dillon * because we can't tell the difference between those modifications
912fc1a9118SMatthew Dillon * and an attempt by the pageout daemon to flush tmpfs pages to
913fc1a9118SMatthew Dillon * swap.
914fc1a9118SMatthew Dillon *
915fc1a9118SMatthew Dillon * This is because in order to defer flushes as long as possible
916fc1a9118SMatthew Dillon * buwrite() works by marking the underlying VM pages dirty in
917fc1a9118SMatthew Dillon * order to be able to dispose of the buffer cache buffer without
918fc1a9118SMatthew Dillon * flushing it.
919fc1a9118SMatthew Dillon */
920fa4a12c4SMatthew Dillon if (uio->uio_segflg == UIO_NOCOPY) {
921fa4a12c4SMatthew Dillon if (vp->v_flag & VLASTWRITETS) {
922723be147SMatthew Dillon node->tn_mtime = vp->v_lastwrite_ts.tv_sec;
923723be147SMatthew Dillon node->tn_mtimensec = vp->v_lastwrite_ts.tv_nsec;
924723be147SMatthew Dillon }
925fa4a12c4SMatthew Dillon } else {
926fa4a12c4SMatthew Dillon node->tn_status |= TMPFS_NODE_MODIFIED;
927fa4a12c4SMatthew Dillon vclrflags(vp, VLASTWRITETS);
928fa4a12c4SMatthew Dillon }
929723be147SMatthew Dillon
930fc1a9118SMatthew Dillon if (extended)
931fc1a9118SMatthew Dillon node->tn_status |= TMPFS_NODE_CHANGED;
9327a2de9a4SMatthew Dillon
9337a2de9a4SMatthew Dillon if (node->tn_mode & (S_ISUID | S_ISGID)) {
934*2b3f93eaSMatthew Dillon if (caps_priv_check(ap->a_cred, SYSCAP_NOVFS_RETAINSUGID))
9357a2de9a4SMatthew Dillon node->tn_mode &= ~(S_ISUID | S_ISGID);
9367a2de9a4SMatthew Dillon }
93780ae59d7SMatthew Dillon done:
938b37a7c00SMatthew Dillon TMPFS_NODE_UNLOCK(node);
939b37a7c00SMatthew Dillon if (kflags)
94080ae59d7SMatthew Dillon tmpfs_knote(vp, kflags);
9411be4932cSVenkatesh Srinivas
9427a2de9a4SMatthew Dillon return(error);
9437a2de9a4SMatthew Dillon }
9447a2de9a4SMatthew Dillon
9457a2de9a4SMatthew Dillon static int
tmpfs_advlock(struct vop_advlock_args * ap)9467a2de9a4SMatthew Dillon tmpfs_advlock(struct vop_advlock_args *ap)
9477a2de9a4SMatthew Dillon {
9487a2de9a4SMatthew Dillon struct tmpfs_node *node;
9497a2de9a4SMatthew Dillon struct vnode *vp = ap->a_vp;
950aa1adbf0SMatthew Dillon int error;
9517a2de9a4SMatthew Dillon
9527a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
953aa1adbf0SMatthew Dillon error = (lf_advlock(ap, &node->tn_advlock, node->tn_size));
954aa1adbf0SMatthew Dillon
955aa1adbf0SMatthew Dillon return (error);
9567a2de9a4SMatthew Dillon }
9577a2de9a4SMatthew Dillon
958fc1a9118SMatthew Dillon /*
959fc1a9118SMatthew Dillon * The strategy function is typically only called when memory pressure
960fc1a9118SMatthew Dillon * forces the system to attempt to pageout pages. It can also be called
961fc1a9118SMatthew Dillon * by [n]vtruncbuf() when a truncation cuts a page in half. Normal write
962fc1a9118SMatthew Dillon * operations
96348db4e20SMatthew Dillon *
96448db4e20SMatthew Dillon * We set VKVABIO for VREG files so bp->b_data may not be synchronized to
96548db4e20SMatthew Dillon * our cpu. swap_pager_strategy() is all we really use, and it directly
96648db4e20SMatthew Dillon * supports this.
967fc1a9118SMatthew Dillon */
9687a2de9a4SMatthew Dillon static int
tmpfs_strategy(struct vop_strategy_args * ap)9697a2de9a4SMatthew Dillon tmpfs_strategy(struct vop_strategy_args *ap)
9707a2de9a4SMatthew Dillon {
9717a2de9a4SMatthew Dillon struct bio *bio = ap->a_bio;
9728f9ba07bSMatthew Dillon struct bio *nbio;
9739fc94b5fSMatthew Dillon struct buf *bp = bio->bio_buf;
9747a2de9a4SMatthew Dillon struct vnode *vp = ap->a_vp;
9757a2de9a4SMatthew Dillon struct tmpfs_node *node;
9767a2de9a4SMatthew Dillon vm_object_t uobj;
9778f9ba07bSMatthew Dillon vm_page_t m;
9788f9ba07bSMatthew Dillon int i;
9797a2de9a4SMatthew Dillon
9809fc94b5fSMatthew Dillon if (vp->v_type != VREG) {
9819fc94b5fSMatthew Dillon bp->b_resid = bp->b_bcount;
9829fc94b5fSMatthew Dillon bp->b_flags |= B_ERROR | B_INVAL;
9839fc94b5fSMatthew Dillon bp->b_error = EINVAL;
9849fc94b5fSMatthew Dillon biodone(bio);
9859fc94b5fSMatthew Dillon return(0);
9869fc94b5fSMatthew Dillon }
9877a2de9a4SMatthew Dillon
9887a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
9897a2de9a4SMatthew Dillon
9907a2de9a4SMatthew Dillon uobj = node->tn_reg.tn_aobj;
9919fc94b5fSMatthew Dillon
9927a2de9a4SMatthew Dillon /*
993fc1a9118SMatthew Dillon * Don't bother flushing to swap if there is no swap, just
994fc1a9118SMatthew Dillon * ensure that the pages are marked as needing a commit (still).
9957a2de9a4SMatthew Dillon */
9968f9ba07bSMatthew Dillon if (bp->b_cmd == BUF_CMD_WRITE && vm_swap_size == 0) {
9978f9ba07bSMatthew Dillon for (i = 0; i < bp->b_xio.xio_npages; ++i) {
9988f9ba07bSMatthew Dillon m = bp->b_xio.xio_pages[i];
999fc1a9118SMatthew Dillon vm_page_need_commit(m);
10008f9ba07bSMatthew Dillon }
10018f9ba07bSMatthew Dillon bp->b_resid = 0;
10028f9ba07bSMatthew Dillon bp->b_error = 0;
10038f9ba07bSMatthew Dillon biodone(bio);
10048f9ba07bSMatthew Dillon } else {
1005108fbfcbSMatthew Dillon #if 0
1006cfd59c5aSMatthew Dillon /*
1007108fbfcbSMatthew Dillon * XXX removed, this does not work well because under heavy
1008108fbfcbSMatthew Dillon * filesystem loads it often
1009108fbfcbSMatthew Dillon * forces the data to be read right back in again after
1010108fbfcbSMatthew Dillon * being written due to bypassing normal LRU operation.
1011108fbfcbSMatthew Dillon *
1012cfd59c5aSMatthew Dillon * Tell the buffer cache to try to recycle the pages
1013cfd59c5aSMatthew Dillon * to PQ_CACHE on release.
1014cfd59c5aSMatthew Dillon */
1015cfd59c5aSMatthew Dillon if (tmpfs_bufcache_mode >= 2 ||
1016cfd59c5aSMatthew Dillon (tmpfs_bufcache_mode == 1 && vm_paging_needed(0))) {
1017cfd59c5aSMatthew Dillon bp->b_flags |= B_TTC;
1018cfd59c5aSMatthew Dillon }
1019108fbfcbSMatthew Dillon #endif
10208f9ba07bSMatthew Dillon nbio = push_bio(bio);
10218f9ba07bSMatthew Dillon nbio->bio_done = tmpfs_strategy_done;
10228f9ba07bSMatthew Dillon nbio->bio_offset = bio->bio_offset;
10238f9ba07bSMatthew Dillon swap_pager_strategy(uobj, nbio);
10248f9ba07bSMatthew Dillon }
10257a2de9a4SMatthew Dillon return 0;
10267a2de9a4SMatthew Dillon }
10277a2de9a4SMatthew Dillon
10288f9ba07bSMatthew Dillon /*
1029fc1a9118SMatthew Dillon * If we were unable to commit the pages to swap make sure they are marked
1030fc1a9118SMatthew Dillon * as needing a commit (again). If we were, clear the flag to allow the
1031fc1a9118SMatthew Dillon * pages to be freed.
1032a1b829f2SMatthew Dillon *
1033a1b829f2SMatthew Dillon * Do not error-out the buffer. In particular, vinvalbuf() needs to
1034a1b829f2SMatthew Dillon * always work.
10358f9ba07bSMatthew Dillon */
10368f9ba07bSMatthew Dillon static void
tmpfs_strategy_done(struct bio * bio)10378f9ba07bSMatthew Dillon tmpfs_strategy_done(struct bio *bio)
10388f9ba07bSMatthew Dillon {
10398f9ba07bSMatthew Dillon struct buf *bp;
10408f9ba07bSMatthew Dillon vm_page_t m;
10418f9ba07bSMatthew Dillon int i;
10428f9ba07bSMatthew Dillon
10438f9ba07bSMatthew Dillon bp = bio->bio_buf;
10448f9ba07bSMatthew Dillon
1045fc1a9118SMatthew Dillon if (bp->b_flags & B_ERROR) {
10468f9ba07bSMatthew Dillon bp->b_flags &= ~B_ERROR;
10478f9ba07bSMatthew Dillon bp->b_error = 0;
10488f9ba07bSMatthew Dillon bp->b_resid = 0;
10498f9ba07bSMatthew Dillon for (i = 0; i < bp->b_xio.xio_npages; ++i) {
10508f9ba07bSMatthew Dillon m = bp->b_xio.xio_pages[i];
1051fc1a9118SMatthew Dillon vm_page_need_commit(m);
1052fc1a9118SMatthew Dillon }
1053fc1a9118SMatthew Dillon } else {
1054fc1a9118SMatthew Dillon for (i = 0; i < bp->b_xio.xio_npages; ++i) {
1055fc1a9118SMatthew Dillon m = bp->b_xio.xio_pages[i];
1056fc1a9118SMatthew Dillon vm_page_clear_commit(m);
10578f9ba07bSMatthew Dillon }
10588f9ba07bSMatthew Dillon }
10598f9ba07bSMatthew Dillon bio = pop_bio(bio);
10608f9ba07bSMatthew Dillon biodone(bio);
10618f9ba07bSMatthew Dillon }
10628f9ba07bSMatthew Dillon
10634eb0bb82SMatthew Dillon /*
10644eb0bb82SMatthew Dillon * To make write clustering work well make the backing store look
10654eb0bb82SMatthew Dillon * contiguous to the cluster_*() code. The swap_strategy() function
10664eb0bb82SMatthew Dillon * will take it from there.
10674eb0bb82SMatthew Dillon *
10684eb0bb82SMatthew Dillon * Use MAXBSIZE-sized chunks as a micro-optimization to make random
10694eb0bb82SMatthew Dillon * flushes leave full-sized gaps.
10704eb0bb82SMatthew Dillon */
10717a2de9a4SMatthew Dillon static int
tmpfs_bmap(struct vop_bmap_args * ap)10727a2de9a4SMatthew Dillon tmpfs_bmap(struct vop_bmap_args *ap)
10737a2de9a4SMatthew Dillon {
10747a2de9a4SMatthew Dillon if (ap->a_doffsetp != NULL)
10757a2de9a4SMatthew Dillon *ap->a_doffsetp = ap->a_loffset;
10767a2de9a4SMatthew Dillon if (ap->a_runp != NULL)
10774eb0bb82SMatthew Dillon *ap->a_runp = MAXBSIZE - (ap->a_loffset & (MAXBSIZE - 1));
10787a2de9a4SMatthew Dillon if (ap->a_runb != NULL)
10794eb0bb82SMatthew Dillon *ap->a_runb = ap->a_loffset & (MAXBSIZE - 1);
10807a2de9a4SMatthew Dillon
10817a2de9a4SMatthew Dillon return 0;
10827a2de9a4SMatthew Dillon }
10839fc94b5fSMatthew Dillon
10847a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
10857a2de9a4SMatthew Dillon
10867a2de9a4SMatthew Dillon static int
tmpfs_nremove(struct vop_nremove_args * ap)1087ba25006aSSascha Wildner tmpfs_nremove(struct vop_nremove_args *ap)
10887a2de9a4SMatthew Dillon {
1089ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
1090ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
10919fc94b5fSMatthew Dillon struct vnode *vp;
10927a2de9a4SMatthew Dillon int error;
10937a2de9a4SMatthew Dillon struct tmpfs_dirent *de;
10947a2de9a4SMatthew Dillon struct tmpfs_mount *tmp;
10957a2de9a4SMatthew Dillon struct tmpfs_node *dnode;
10967a2de9a4SMatthew Dillon struct tmpfs_node *node;
1097aa1adbf0SMatthew Dillon
10989fc94b5fSMatthew Dillon /*
1099ba25006aSSascha Wildner * We have to acquire the vp from ap->a_nch because we will likely
1100a1fa5d8dSMatthew Dillon * unresolve the namecache entry, and a vrele/vput is needed to
1101a1fa5d8dSMatthew Dillon * trigger the tmpfs_inactive/tmpfs_reclaim sequence.
1102a1fa5d8dSMatthew Dillon *
1103a1fa5d8dSMatthew Dillon * We have to use vget to clear any inactive state on the vnode,
1104a1fa5d8dSMatthew Dillon * otherwise the vnode may remain inactive and thus tmpfs_inactive
1105a1fa5d8dSMatthew Dillon * will not get called when we release it.
11069fc94b5fSMatthew Dillon */
1107ba25006aSSascha Wildner error = cache_vget(ap->a_nch, ap->a_cred, LK_SHARED, &vp);
1108aa1adbf0SMatthew Dillon KKASSERT(vp->v_mount == dvp->v_mount);
11099fc94b5fSMatthew Dillon KKASSERT(error == 0);
1110a1fa5d8dSMatthew Dillon vn_unlock(vp);
11117a2de9a4SMatthew Dillon
11127a2de9a4SMatthew Dillon if (vp->v_type == VDIR) {
11137a2de9a4SMatthew Dillon error = EISDIR;
1114ff837cd5SMatthew Dillon goto out2;
11157a2de9a4SMatthew Dillon }
11167a2de9a4SMatthew Dillon
11177a2de9a4SMatthew Dillon dnode = VP_TO_TMPFS_DIR(dvp);
11187a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
11197a2de9a4SMatthew Dillon tmp = VFS_TO_TMPFS(vp->v_mount);
1120ff837cd5SMatthew Dillon
1121ff837cd5SMatthew Dillon TMPFS_NODE_LOCK(dnode);
112278ce1036SMatthew Dillon TMPFS_NODE_LOCK(node);
11237a2de9a4SMatthew Dillon de = tmpfs_dir_lookup(dnode, node, ncp);
11247a2de9a4SMatthew Dillon if (de == NULL) {
11257a2de9a4SMatthew Dillon error = ENOENT;
112678ce1036SMatthew Dillon TMPFS_NODE_UNLOCK(node);
1127837afe1aSMatthew Dillon TMPFS_NODE_UNLOCK(dnode);
11287a2de9a4SMatthew Dillon goto out;
11297a2de9a4SMatthew Dillon }
11307a2de9a4SMatthew Dillon
11317a2de9a4SMatthew Dillon /* Files marked as immutable or append-only cannot be deleted. */
11327a2de9a4SMatthew Dillon if ((node->tn_flags & (IMMUTABLE | APPEND | NOUNLINK)) ||
11337a2de9a4SMatthew Dillon (dnode->tn_flags & APPEND)) {
11347a2de9a4SMatthew Dillon error = EPERM;
113578ce1036SMatthew Dillon TMPFS_NODE_UNLOCK(node);
1136837afe1aSMatthew Dillon TMPFS_NODE_UNLOCK(dnode);
11377a2de9a4SMatthew Dillon goto out;
11387a2de9a4SMatthew Dillon }
11397a2de9a4SMatthew Dillon
11407a2de9a4SMatthew Dillon /* Remove the entry from the directory; as it is a file, we do not
11417a2de9a4SMatthew Dillon * have to change the number of hard links of the directory. */
1142307bf766SMatthew Dillon tmpfs_dir_detach_locked(dnode, de);
1143837afe1aSMatthew Dillon TMPFS_NODE_UNLOCK(dnode);
11447a2de9a4SMatthew Dillon
11457a2de9a4SMatthew Dillon /* Free the directory entry we just deleted. Note that the node
11467a2de9a4SMatthew Dillon * referred by it will not be removed until the vnode is really
11477a2de9a4SMatthew Dillon * reclaimed. */
11480786baf1SMatthew Dillon tmpfs_free_dirent(tmp, de);
11497a2de9a4SMatthew Dillon
115078ce1036SMatthew Dillon if (node->tn_links > 0)
115191321284SMatthew Dillon node->tn_status |= TMPFS_NODE_CHANGED;
11527a2de9a4SMatthew Dillon TMPFS_NODE_UNLOCK(node);
11537a2de9a4SMatthew Dillon
1154ba25006aSSascha Wildner cache_unlink(ap->a_nch);
115580ae59d7SMatthew Dillon tmpfs_knote(vp, NOTE_DELETE);
11567a2de9a4SMatthew Dillon error = 0;
11577a2de9a4SMatthew Dillon
11587a2de9a4SMatthew Dillon out:
1159ff837cd5SMatthew Dillon if (error == 0)
1160ff837cd5SMatthew Dillon tmpfs_knote(dvp, NOTE_WRITE);
1161ff837cd5SMatthew Dillon out2:
11629fc94b5fSMatthew Dillon vrele(vp);
11637a2de9a4SMatthew Dillon
11647a2de9a4SMatthew Dillon return error;
11657a2de9a4SMatthew Dillon }
11667a2de9a4SMatthew Dillon
11677a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
11687a2de9a4SMatthew Dillon
11697a2de9a4SMatthew Dillon static int
tmpfs_nlink(struct vop_nlink_args * ap)1170ba25006aSSascha Wildner tmpfs_nlink(struct vop_nlink_args *ap)
11717a2de9a4SMatthew Dillon {
1172ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
1173ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
1174307bf766SMatthew Dillon struct tmpfs_mount *tmp = VFS_TO_TMPFS(vp->v_mount);
1175ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
11767a2de9a4SMatthew Dillon struct tmpfs_dirent *de;
11777a2de9a4SMatthew Dillon struct tmpfs_node *node;
117822d3b394SMatthew Dillon struct tmpfs_node *dnode;
117922d3b394SMatthew Dillon int error;
11807a2de9a4SMatthew Dillon
11817a2de9a4SMatthew Dillon KKASSERT(dvp != vp); /* XXX When can this be false? */
11827a2de9a4SMatthew Dillon
11837a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
118422d3b394SMatthew Dillon dnode = VP_TO_TMPFS_NODE(dvp);
1185ff837cd5SMatthew Dillon TMPFS_NODE_LOCK(dnode);
11867a2de9a4SMatthew Dillon
11877a2de9a4SMatthew Dillon /* XXX: Why aren't the following two tests done by the caller? */
11887a2de9a4SMatthew Dillon
11897a2de9a4SMatthew Dillon /* Hard links of directories are forbidden. */
11907a2de9a4SMatthew Dillon if (vp->v_type == VDIR) {
11917a2de9a4SMatthew Dillon error = EPERM;
11927a2de9a4SMatthew Dillon goto out;
11937a2de9a4SMatthew Dillon }
11947a2de9a4SMatthew Dillon
11957a2de9a4SMatthew Dillon /* Cannot create cross-device links. */
11967a2de9a4SMatthew Dillon if (dvp->v_mount != vp->v_mount) {
11977a2de9a4SMatthew Dillon error = EXDEV;
11987a2de9a4SMatthew Dillon goto out;
11997a2de9a4SMatthew Dillon }
12007a2de9a4SMatthew Dillon
1201307bf766SMatthew Dillon /* Cannot hard-link into a deleted directory */
1202307bf766SMatthew Dillon if (dnode != tmp->tm_root && dnode->tn_dir.tn_parent == NULL) {
1203307bf766SMatthew Dillon error = ENOENT;
1204307bf766SMatthew Dillon goto out;
1205307bf766SMatthew Dillon }
1206307bf766SMatthew Dillon
12077a2de9a4SMatthew Dillon /* Ensure that we do not overflow the maximum number of links imposed
12087a2de9a4SMatthew Dillon * by the system. */
12097a2de9a4SMatthew Dillon KKASSERT(node->tn_links <= LINK_MAX);
1210ff837cd5SMatthew Dillon if (node->tn_links >= LINK_MAX) {
12117a2de9a4SMatthew Dillon error = EMLINK;
12127a2de9a4SMatthew Dillon goto out;
12137a2de9a4SMatthew Dillon }
12147a2de9a4SMatthew Dillon
12157a2de9a4SMatthew Dillon /* We cannot create links of files marked immutable or append-only. */
12167a2de9a4SMatthew Dillon if (node->tn_flags & (IMMUTABLE | APPEND)) {
12177a2de9a4SMatthew Dillon error = EPERM;
12187a2de9a4SMatthew Dillon goto out;
12197a2de9a4SMatthew Dillon }
12207a2de9a4SMatthew Dillon
12217a2de9a4SMatthew Dillon /* Allocate a new directory entry to represent the node. */
12227a2de9a4SMatthew Dillon error = tmpfs_alloc_dirent(VFS_TO_TMPFS(vp->v_mount), node,
12237a2de9a4SMatthew Dillon ncp->nc_name, ncp->nc_nlen, &de);
12247a2de9a4SMatthew Dillon if (error != 0)
12257a2de9a4SMatthew Dillon goto out;
12267a2de9a4SMatthew Dillon
12277a2de9a4SMatthew Dillon /* Insert the new directory entry into the appropriate directory. */
1228307bf766SMatthew Dillon tmpfs_dir_attach_locked(dnode, de);
12297a2de9a4SMatthew Dillon
12307a2de9a4SMatthew Dillon /* vp link count has changed, so update node times. */
12317a2de9a4SMatthew Dillon
12327a2de9a4SMatthew Dillon TMPFS_NODE_LOCK(node);
12337a2de9a4SMatthew Dillon node->tn_status |= TMPFS_NODE_CHANGED;
12347a2de9a4SMatthew Dillon TMPFS_NODE_UNLOCK(node);
12357a2de9a4SMatthew Dillon tmpfs_update(vp);
12367a2de9a4SMatthew Dillon
123780ae59d7SMatthew Dillon tmpfs_knote(vp, NOTE_LINK);
1238ba25006aSSascha Wildner cache_setunresolved(ap->a_nch);
1239ba25006aSSascha Wildner cache_setvp(ap->a_nch, vp);
12407a2de9a4SMatthew Dillon error = 0;
12417a2de9a4SMatthew Dillon
12427a2de9a4SMatthew Dillon out:
1243ff837cd5SMatthew Dillon TMPFS_NODE_UNLOCK(dnode);
1244ff837cd5SMatthew Dillon if (error == 0)
1245ff837cd5SMatthew Dillon tmpfs_knote(dvp, NOTE_WRITE);
12467a2de9a4SMatthew Dillon return error;
12477a2de9a4SMatthew Dillon }
12487a2de9a4SMatthew Dillon
12497a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
12507a2de9a4SMatthew Dillon
12517a2de9a4SMatthew Dillon static int
tmpfs_nrename(struct vop_nrename_args * ap)1252ba25006aSSascha Wildner tmpfs_nrename(struct vop_nrename_args *ap)
12537a2de9a4SMatthew Dillon {
1254ba25006aSSascha Wildner struct vnode *fdvp = ap->a_fdvp;
1255ba25006aSSascha Wildner struct namecache *fncp = ap->a_fnch->ncp;
12567a2de9a4SMatthew Dillon struct vnode *fvp = fncp->nc_vp;
1257ba25006aSSascha Wildner struct vnode *tdvp = ap->a_tdvp;
1258ba25006aSSascha Wildner struct namecache *tncp = ap->a_tnch->ncp;
1259a1fa5d8dSMatthew Dillon struct vnode *tvp;
1260fd67ed24SMatthew Dillon struct tmpfs_dirent *de, *tde, *de2;
12617a2de9a4SMatthew Dillon struct tmpfs_mount *tmp;
12627a2de9a4SMatthew Dillon struct tmpfs_node *fdnode;
12634c154053SMatthew Dillon struct tmpfs_node *tdnode;
12647a2de9a4SMatthew Dillon struct tmpfs_node *fnode;
12657a2de9a4SMatthew Dillon struct tmpfs_node *tnode;
126622d3b394SMatthew Dillon char *newname;
1267dca262fbSMatthew Dillon char *oldname;
126822d3b394SMatthew Dillon int error;
12697a2de9a4SMatthew Dillon
1270aa1adbf0SMatthew Dillon KKASSERT(fdvp->v_mount == fvp->v_mount);
1271aa1adbf0SMatthew Dillon
1272a1fa5d8dSMatthew Dillon /*
1273a1fa5d8dSMatthew Dillon * Because tvp can get overwritten we have to vget it instead of
1274a1fa5d8dSMatthew Dillon * just vref or use it, otherwise it's VINACTIVE flag may not get
1275a1fa5d8dSMatthew Dillon * cleared and the node won't get destroyed.
1276a1fa5d8dSMatthew Dillon */
1277ba25006aSSascha Wildner error = cache_vget(ap->a_tnch, ap->a_cred, LK_SHARED, &tvp);
1278a1fa5d8dSMatthew Dillon if (error == 0) {
1279a1fa5d8dSMatthew Dillon tnode = VP_TO_TMPFS_NODE(tvp);
1280a1fa5d8dSMatthew Dillon vn_unlock(tvp);
1281a1fa5d8dSMatthew Dillon } else {
1282a1fa5d8dSMatthew Dillon tnode = NULL;
1283a1fa5d8dSMatthew Dillon }
12847a2de9a4SMatthew Dillon
12857a2de9a4SMatthew Dillon /* Disallow cross-device renames.
12867a2de9a4SMatthew Dillon * XXX Why isn't this done by the caller? */
12877a2de9a4SMatthew Dillon if (fvp->v_mount != tdvp->v_mount ||
12887a2de9a4SMatthew Dillon (tvp != NULL && fvp->v_mount != tvp->v_mount)) {
12897a2de9a4SMatthew Dillon error = EXDEV;
12907a2de9a4SMatthew Dillon goto out;
12917a2de9a4SMatthew Dillon }
12927a2de9a4SMatthew Dillon
12937a2de9a4SMatthew Dillon tmp = VFS_TO_TMPFS(tdvp->v_mount);
12947a2de9a4SMatthew Dillon tdnode = VP_TO_TMPFS_DIR(tdvp);
12957a2de9a4SMatthew Dillon
12967a2de9a4SMatthew Dillon /* If source and target are the same file, there is nothing to do. */
12977a2de9a4SMatthew Dillon if (fvp == tvp) {
12987a2de9a4SMatthew Dillon error = 0;
12997a2de9a4SMatthew Dillon goto out;
13007a2de9a4SMatthew Dillon }
13017a2de9a4SMatthew Dillon
13027a2de9a4SMatthew Dillon fdnode = VP_TO_TMPFS_DIR(fdvp);
13037a2de9a4SMatthew Dillon fnode = VP_TO_TMPFS_NODE(fvp);
13044c154053SMatthew Dillon
13054c154053SMatthew Dillon tmpfs_lock4(fdnode, tdnode, fnode, tnode);
13064c154053SMatthew Dillon
1307307bf766SMatthew Dillon /*
1308307bf766SMatthew Dillon * Cannot rename into a deleted directory
1309307bf766SMatthew Dillon */
1310307bf766SMatthew Dillon if (tdnode != tmp->tm_root && tdnode->tn_dir.tn_parent == NULL) {
1311307bf766SMatthew Dillon error = ENOENT;
1312307bf766SMatthew Dillon goto out_locked;
1313307bf766SMatthew Dillon }
13147a2de9a4SMatthew Dillon
13157a2de9a4SMatthew Dillon /* Avoid manipulating '.' and '..' entries. */
1316307bf766SMatthew Dillon de = tmpfs_dir_lookup(fdnode, fnode, fncp);
13177a2de9a4SMatthew Dillon if (de == NULL) {
13187a2de9a4SMatthew Dillon error = ENOENT;
13197a2de9a4SMatthew Dillon goto out_locked;
13207a2de9a4SMatthew Dillon }
13217a2de9a4SMatthew Dillon KKASSERT(de->td_node == fnode);
13227a2de9a4SMatthew Dillon
1323dca262fbSMatthew Dillon /*
1324dca262fbSMatthew Dillon * If replacing an entry in the target directory and that entry
1325dca262fbSMatthew Dillon * is a directory, it must be empty.
1326dca262fbSMatthew Dillon *
13277a2de9a4SMatthew Dillon * Kern_rename gurantees the destination to be a directory
1328dca262fbSMatthew Dillon * if the source is one (it does?).
1329dca262fbSMatthew Dillon */
13307a2de9a4SMatthew Dillon if (tvp != NULL) {
13317a2de9a4SMatthew Dillon KKASSERT(tnode != NULL);
13327a2de9a4SMatthew Dillon
13337a2de9a4SMatthew Dillon if ((tnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
13347a2de9a4SMatthew Dillon (tdnode->tn_flags & (APPEND | IMMUTABLE))) {
13357a2de9a4SMatthew Dillon error = EPERM;
13367a2de9a4SMatthew Dillon goto out_locked;
13377a2de9a4SMatthew Dillon }
13387a2de9a4SMatthew Dillon
13397a2de9a4SMatthew Dillon if (fnode->tn_type == VDIR && tnode->tn_type == VDIR) {
13407a2de9a4SMatthew Dillon if (tnode->tn_size > 0) {
13417a2de9a4SMatthew Dillon error = ENOTEMPTY;
13427a2de9a4SMatthew Dillon goto out_locked;
13437a2de9a4SMatthew Dillon }
13447a2de9a4SMatthew Dillon } else if (fnode->tn_type == VDIR && tnode->tn_type != VDIR) {
13457a2de9a4SMatthew Dillon error = ENOTDIR;
13467a2de9a4SMatthew Dillon goto out_locked;
13477a2de9a4SMatthew Dillon } else if (fnode->tn_type != VDIR && tnode->tn_type == VDIR) {
13487a2de9a4SMatthew Dillon error = EISDIR;
13497a2de9a4SMatthew Dillon goto out_locked;
13507a2de9a4SMatthew Dillon } else {
13517a2de9a4SMatthew Dillon KKASSERT(fnode->tn_type != VDIR &&
13527a2de9a4SMatthew Dillon tnode->tn_type != VDIR);
13537a2de9a4SMatthew Dillon }
13547a2de9a4SMatthew Dillon }
13557a2de9a4SMatthew Dillon
1356dca262fbSMatthew Dillon if ((fnode->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) ||
1357dca262fbSMatthew Dillon (fdnode->tn_flags & (APPEND | IMMUTABLE))) {
13587a2de9a4SMatthew Dillon error = EPERM;
13597a2de9a4SMatthew Dillon goto out_locked;
13607a2de9a4SMatthew Dillon }
13617a2de9a4SMatthew Dillon
1362dca262fbSMatthew Dillon /*
1363dca262fbSMatthew Dillon * Ensure that we have enough memory to hold the new name, if it
1364dca262fbSMatthew Dillon * has to be changed.
1365dca262fbSMatthew Dillon */
13667a2de9a4SMatthew Dillon if (fncp->nc_nlen != tncp->nc_nlen ||
13677a2de9a4SMatthew Dillon bcmp(fncp->nc_name, tncp->nc_name, fncp->nc_nlen) != 0) {
1368d00cd01cSVenkatesh Srinivas newname = kmalloc(tncp->nc_nlen + 1, tmp->tm_name_zone,
136942f6f6b1SVenkatesh Srinivas M_WAITOK | M_NULLOK);
137042f6f6b1SVenkatesh Srinivas if (newname == NULL) {
137142f6f6b1SVenkatesh Srinivas error = ENOSPC;
137242f6f6b1SVenkatesh Srinivas goto out_locked;
137342f6f6b1SVenkatesh Srinivas }
13747a2de9a4SMatthew Dillon bcopy(tncp->nc_name, newname, tncp->nc_nlen);
13757a2de9a4SMatthew Dillon newname[tncp->nc_nlen] = '\0';
1376dca262fbSMatthew Dillon } else {
1377dca262fbSMatthew Dillon newname = NULL;
1378dca262fbSMatthew Dillon }
1379dca262fbSMatthew Dillon
1380dca262fbSMatthew Dillon /*
1381dca262fbSMatthew Dillon * Unlink entry from source directory. Note that the kernel has
1382dca262fbSMatthew Dillon * already checked for illegal recursion cases (renaming a directory
1383dca262fbSMatthew Dillon * into a subdirectory of itself).
1384dca262fbSMatthew Dillon */
13856e0c5aabSMatthew Dillon if (fdnode != tdnode) {
1386307bf766SMatthew Dillon tmpfs_dir_detach_locked(fdnode, de);
13876e0c5aabSMatthew Dillon } else {
1388e195a8feSMatthew Dillon /* XXX depend on namecache lock */
1389e195a8feSMatthew Dillon KKASSERT(de == tmpfs_dir_lookup(fdnode, fnode, fncp));
139029ca4fd6SJohannes Hofmann RB_REMOVE(tmpfs_dirtree, &fdnode->tn_dir.tn_dirtree, de);
1391f5f22af6SMatthew Dillon RB_REMOVE(tmpfs_dirtree_cookie,
1392f5f22af6SMatthew Dillon &fdnode->tn_dir.tn_cookietree, de);
139329ca4fd6SJohannes Hofmann }
1394dca262fbSMatthew Dillon
1395dca262fbSMatthew Dillon /*
1396dca262fbSMatthew Dillon * Handle any name change. Swap with newname, we will
1397dca262fbSMatthew Dillon * deallocate it at the end.
1398dca262fbSMatthew Dillon */
1399dca262fbSMatthew Dillon if (newname != NULL) {
1400dca262fbSMatthew Dillon oldname = de->td_name;
14017a2de9a4SMatthew Dillon de->td_name = newname;
1402dca262fbSMatthew Dillon de->td_namelen = (uint16_t)tncp->nc_nlen;
1403dca262fbSMatthew Dillon newname = oldname;
1404dca262fbSMatthew Dillon }
1405dca262fbSMatthew Dillon
1406dca262fbSMatthew Dillon /*
140729ca4fd6SJohannes Hofmann * If we are overwriting an entry, we have to remove the old one
140829ca4fd6SJohannes Hofmann * from the target directory.
140929ca4fd6SJohannes Hofmann */
141029ca4fd6SJohannes Hofmann if (tvp != NULL) {
141129ca4fd6SJohannes Hofmann /* Remove the old entry from the target directory. */
141229ca4fd6SJohannes Hofmann tde = tmpfs_dir_lookup(tdnode, tnode, tncp);
1413307bf766SMatthew Dillon tmpfs_dir_detach_locked(tdnode, tde);
141429ca4fd6SJohannes Hofmann tmpfs_knote(tdnode->tn_vnode, NOTE_DELETE);
141529ca4fd6SJohannes Hofmann
141629ca4fd6SJohannes Hofmann /*
141729ca4fd6SJohannes Hofmann * Free the directory entry we just deleted. Note that the
141829ca4fd6SJohannes Hofmann * node referred by it will not be removed until the vnode is
141929ca4fd6SJohannes Hofmann * really reclaimed.
142029ca4fd6SJohannes Hofmann */
142129ca4fd6SJohannes Hofmann tmpfs_free_dirent(VFS_TO_TMPFS(tvp->v_mount), tde);
142229ca4fd6SJohannes Hofmann /*cache_inval_vp(tvp, CINV_DESTROY);*/
142329ca4fd6SJohannes Hofmann }
142429ca4fd6SJohannes Hofmann
142529ca4fd6SJohannes Hofmann /*
1426dca262fbSMatthew Dillon * Link entry to target directory. If the entry
1427dca262fbSMatthew Dillon * represents a directory move the parent linkage
1428dca262fbSMatthew Dillon * as well.
1429dca262fbSMatthew Dillon */
1430dca262fbSMatthew Dillon if (fdnode != tdnode) {
1431dca262fbSMatthew Dillon if (de->td_node->tn_type == VDIR) {
1432dca262fbSMatthew Dillon TMPFS_VALIDATE_DIR(fnode);
1433dca262fbSMatthew Dillon }
1434307bf766SMatthew Dillon tmpfs_dir_attach_locked(tdnode, de);
1435dca262fbSMatthew Dillon } else {
1436dca262fbSMatthew Dillon tdnode->tn_status |= TMPFS_NODE_MODIFIED;
1437fd67ed24SMatthew Dillon de2 = RB_INSERT(tmpfs_dirtree, &tdnode->tn_dir.tn_dirtree, de);
1438fd67ed24SMatthew Dillon KASSERT(de2 == NULL,
1439fd67ed24SMatthew Dillon ("tmpfs_nrenameA: duplicate insertion of %p, has %p\n",
1440fd67ed24SMatthew Dillon de, de2));
1441fd67ed24SMatthew Dillon de2 = RB_INSERT(tmpfs_dirtree_cookie,
1442f5f22af6SMatthew Dillon &tdnode->tn_dir.tn_cookietree, de);
1443fd67ed24SMatthew Dillon KASSERT(de2 == NULL,
1444fd67ed24SMatthew Dillon ("tmpfs_nrenameB: duplicate insertion of %p, has %p\n",
1445fd67ed24SMatthew Dillon de, de2));
14467a2de9a4SMatthew Dillon }
14474c154053SMatthew Dillon tmpfs_unlock4(fdnode, tdnode, fnode, tnode);
14487a2de9a4SMatthew Dillon
1449dca262fbSMatthew Dillon /*
1450dca262fbSMatthew Dillon * Finish up
1451dca262fbSMatthew Dillon */
1452dca262fbSMatthew Dillon if (newname) {
1453d00cd01cSVenkatesh Srinivas kfree(newname, tmp->tm_name_zone);
1454dca262fbSMatthew Dillon newname = NULL;
1455dca262fbSMatthew Dillon }
1456ba25006aSSascha Wildner cache_rename(ap->a_fnch, ap->a_tnch);
1457ba25006aSSascha Wildner tmpfs_knote(ap->a_fdvp, NOTE_WRITE);
1458ba25006aSSascha Wildner tmpfs_knote(ap->a_tdvp, NOTE_WRITE);
145980ae59d7SMatthew Dillon if (fnode->tn_vnode)
146080ae59d7SMatthew Dillon tmpfs_knote(fnode->tn_vnode, NOTE_RENAME);
14614c154053SMatthew Dillon if (tvp)
14624c154053SMatthew Dillon vrele(tvp);
14634c154053SMatthew Dillon return 0;
14647a2de9a4SMatthew Dillon
14657a2de9a4SMatthew Dillon out_locked:
14664c154053SMatthew Dillon tmpfs_unlock4(fdnode, tdnode, fnode, tnode);
14677a2de9a4SMatthew Dillon out:
1468a1fa5d8dSMatthew Dillon if (tvp)
1469a1fa5d8dSMatthew Dillon vrele(tvp);
14707a2de9a4SMatthew Dillon return error;
14717a2de9a4SMatthew Dillon }
14727a2de9a4SMatthew Dillon
14737a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
14747a2de9a4SMatthew Dillon
14757a2de9a4SMatthew Dillon static int
tmpfs_nmkdir(struct vop_nmkdir_args * ap)1476ba25006aSSascha Wildner tmpfs_nmkdir(struct vop_nmkdir_args *ap)
14777a2de9a4SMatthew Dillon {
1478ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
1479ba25006aSSascha Wildner struct vnode **vpp = ap->a_vpp;
1480ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
1481ba25006aSSascha Wildner struct vattr *vap = ap->a_vap;
1482ba25006aSSascha Wildner struct ucred *cred = ap->a_cred;
14837a2de9a4SMatthew Dillon int error;
14847a2de9a4SMatthew Dillon
14857a2de9a4SMatthew Dillon KKASSERT(vap->va_type == VDIR);
14867a2de9a4SMatthew Dillon
14877a2de9a4SMatthew Dillon error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, NULL);
14887a2de9a4SMatthew Dillon if (error == 0) {
1489ba25006aSSascha Wildner cache_setunresolved(ap->a_nch);
1490ba25006aSSascha Wildner cache_setvp(ap->a_nch, *vpp);
149180ae59d7SMatthew Dillon tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
14927a2de9a4SMatthew Dillon }
14937a2de9a4SMatthew Dillon return error;
14947a2de9a4SMatthew Dillon }
14957a2de9a4SMatthew Dillon
14967a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
14977a2de9a4SMatthew Dillon
14987a2de9a4SMatthew Dillon static int
tmpfs_nrmdir(struct vop_nrmdir_args * ap)1499ba25006aSSascha Wildner tmpfs_nrmdir(struct vop_nrmdir_args *ap)
15007a2de9a4SMatthew Dillon {
1501ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
1502ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
15039fc94b5fSMatthew Dillon struct vnode *vp;
15047a2de9a4SMatthew Dillon struct tmpfs_dirent *de;
15057a2de9a4SMatthew Dillon struct tmpfs_mount *tmp;
15067a2de9a4SMatthew Dillon struct tmpfs_node *dnode;
15077a2de9a4SMatthew Dillon struct tmpfs_node *node;
150838e5e604SMatthew Dillon int error;
150938e5e604SMatthew Dillon
151038e5e604SMatthew Dillon /*
1511ba25006aSSascha Wildner * We have to acquire the vp from ap->a_nch because we will likely
1512a1fa5d8dSMatthew Dillon * unresolve the namecache entry, and a vrele/vput is needed to
1513a1fa5d8dSMatthew Dillon * trigger the tmpfs_inactive/tmpfs_reclaim sequence.
1514a1fa5d8dSMatthew Dillon *
1515a1fa5d8dSMatthew Dillon * We have to use vget to clear any inactive state on the vnode,
1516a1fa5d8dSMatthew Dillon * otherwise the vnode may remain inactive and thus tmpfs_inactive
1517a1fa5d8dSMatthew Dillon * will not get called when we release it.
15189fc94b5fSMatthew Dillon */
1519ba25006aSSascha Wildner error = cache_vget(ap->a_nch, ap->a_cred, LK_SHARED, &vp);
15209fc94b5fSMatthew Dillon KKASSERT(error == 0);
1521a1fa5d8dSMatthew Dillon vn_unlock(vp);
15227a2de9a4SMatthew Dillon
1523e527fb6bSMatthew Dillon /*
1524e527fb6bSMatthew Dillon * Prevalidate so we don't hit an assertion later
1525e527fb6bSMatthew Dillon */
1526e527fb6bSMatthew Dillon if (vp->v_type != VDIR) {
1527e527fb6bSMatthew Dillon error = ENOTDIR;
1528e527fb6bSMatthew Dillon goto out;
1529e527fb6bSMatthew Dillon }
1530e527fb6bSMatthew Dillon
15317a2de9a4SMatthew Dillon tmp = VFS_TO_TMPFS(dvp->v_mount);
15327a2de9a4SMatthew Dillon dnode = VP_TO_TMPFS_DIR(dvp);
15337a2de9a4SMatthew Dillon node = VP_TO_TMPFS_DIR(vp);
15347a2de9a4SMatthew Dillon
1535ff837cd5SMatthew Dillon /*
15364c154053SMatthew Dillon *
15374c154053SMatthew Dillon */
15384c154053SMatthew Dillon TMPFS_NODE_LOCK(dnode);
15394c154053SMatthew Dillon TMPFS_NODE_LOCK(node);
15404c154053SMatthew Dillon
15414c154053SMatthew Dillon /*
1542a44ecf5cSMatthew Dillon * Only empty directories can be removed.
1543ff837cd5SMatthew Dillon */
15447a2de9a4SMatthew Dillon if (node->tn_size > 0) {
15457a2de9a4SMatthew Dillon error = ENOTEMPTY;
15464c154053SMatthew Dillon goto out_locked;
15477a2de9a4SMatthew Dillon }
15487a2de9a4SMatthew Dillon
15497a2de9a4SMatthew Dillon if ((dnode->tn_flags & APPEND)
15507a2de9a4SMatthew Dillon || (node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND))) {
15517a2de9a4SMatthew Dillon error = EPERM;
15524c154053SMatthew Dillon goto out_locked;
15537a2de9a4SMatthew Dillon }
15547a2de9a4SMatthew Dillon
1555ff837cd5SMatthew Dillon /*
1556ff837cd5SMatthew Dillon * This invariant holds only if we are not trying to
1557ff837cd5SMatthew Dillon * remove "..". We checked for that above so this is safe now.
1558ff837cd5SMatthew Dillon */
15597a2de9a4SMatthew Dillon KKASSERT(node->tn_dir.tn_parent == dnode);
15607a2de9a4SMatthew Dillon
1561ff837cd5SMatthew Dillon /*
15624c154053SMatthew Dillon * Get the directory entry associated with node (vp)
1563ff837cd5SMatthew Dillon */
15647a2de9a4SMatthew Dillon de = tmpfs_dir_lookup(dnode, node, ncp);
1565ff837cd5SMatthew Dillon KKASSERT(TMPFS_DIRENT_MATCHES(de, ncp->nc_name, ncp->nc_nlen));
15667a2de9a4SMatthew Dillon
15677a2de9a4SMatthew Dillon /* Check flags to see if we are allowed to remove the directory. */
1568b7fe63afSMatthew Dillon if ((dnode->tn_flags & APPEND) ||
1569b7fe63afSMatthew Dillon node->tn_flags & (NOUNLINK | IMMUTABLE | APPEND)) {
15707a2de9a4SMatthew Dillon error = EPERM;
15714c154053SMatthew Dillon goto out_locked;
15727a2de9a4SMatthew Dillon }
15737a2de9a4SMatthew Dillon
15747a2de9a4SMatthew Dillon /* Detach the directory entry from the directory (dnode). */
1575307bf766SMatthew Dillon tmpfs_dir_detach_locked(dnode, de);
15767a2de9a4SMatthew Dillon
15776e0c5aabSMatthew Dillon /*
15786e0c5aabSMatthew Dillon * Must set parent linkage to NULL (tested by ncreate to disallow
15796e0c5aabSMatthew Dillon * the creation of new files/dirs in a deleted directory)
15806e0c5aabSMatthew Dillon */
158191321284SMatthew Dillon node->tn_status |= TMPFS_NODE_CHANGED;
15827a2de9a4SMatthew Dillon
1583ff837cd5SMatthew Dillon dnode->tn_status |= TMPFS_NODE_ACCESSED | TMPFS_NODE_CHANGED |
1584ff837cd5SMatthew Dillon TMPFS_NODE_MODIFIED;
15857a2de9a4SMatthew Dillon
15867a2de9a4SMatthew Dillon /* Free the directory entry we just deleted. Note that the node
15877a2de9a4SMatthew Dillon * referred by it will not be removed until the vnode is really
15887a2de9a4SMatthew Dillon * reclaimed. */
15890786baf1SMatthew Dillon tmpfs_free_dirent(tmp, de);
15907a2de9a4SMatthew Dillon
15917a2de9a4SMatthew Dillon /* Release the deleted vnode (will destroy the node, notify
15927a2de9a4SMatthew Dillon * interested parties and clean it from the cache). */
15937a2de9a4SMatthew Dillon
15947a2de9a4SMatthew Dillon dnode->tn_status |= TMPFS_NODE_CHANGED;
15957a2de9a4SMatthew Dillon
15964c154053SMatthew Dillon TMPFS_NODE_UNLOCK(node);
15974c154053SMatthew Dillon TMPFS_NODE_UNLOCK(dnode);
15984c154053SMatthew Dillon
15994c154053SMatthew Dillon tmpfs_update(dvp);
1600ba25006aSSascha Wildner cache_unlink(ap->a_nch);
1601b9f715bcSMatthew Dillon tmpfs_knote(vp, NOTE_DELETE);
160280ae59d7SMatthew Dillon tmpfs_knote(dvp, NOTE_WRITE | NOTE_LINK);
16034c154053SMatthew Dillon vrele(vp);
16044c154053SMatthew Dillon return 0;
16054c154053SMatthew Dillon
16064c154053SMatthew Dillon out_locked:
16074c154053SMatthew Dillon TMPFS_NODE_UNLOCK(node);
16084c154053SMatthew Dillon TMPFS_NODE_UNLOCK(dnode);
16097a2de9a4SMatthew Dillon
16107a2de9a4SMatthew Dillon out:
16119fc94b5fSMatthew Dillon vrele(vp);
16127a2de9a4SMatthew Dillon
16137a2de9a4SMatthew Dillon return error;
16147a2de9a4SMatthew Dillon }
16157a2de9a4SMatthew Dillon
16167a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
16177a2de9a4SMatthew Dillon
16187a2de9a4SMatthew Dillon static int
tmpfs_nsymlink(struct vop_nsymlink_args * ap)1619ba25006aSSascha Wildner tmpfs_nsymlink(struct vop_nsymlink_args *ap)
16207a2de9a4SMatthew Dillon {
1621ba25006aSSascha Wildner struct vnode *dvp = ap->a_dvp;
1622ba25006aSSascha Wildner struct vnode **vpp = ap->a_vpp;
1623ba25006aSSascha Wildner struct namecache *ncp = ap->a_nch->ncp;
1624ba25006aSSascha Wildner struct vattr *vap = ap->a_vap;
1625ba25006aSSascha Wildner struct ucred *cred = ap->a_cred;
1626ba25006aSSascha Wildner char *target = ap->a_target;
16277a2de9a4SMatthew Dillon int error;
16287a2de9a4SMatthew Dillon
16297a2de9a4SMatthew Dillon vap->va_type = VLNK;
16307a2de9a4SMatthew Dillon error = tmpfs_alloc_file(dvp, vpp, vap, ncp, cred, target);
16317a2de9a4SMatthew Dillon if (error == 0) {
163280ae59d7SMatthew Dillon tmpfs_knote(*vpp, NOTE_WRITE);
1633ba25006aSSascha Wildner cache_setunresolved(ap->a_nch);
1634ba25006aSSascha Wildner cache_setvp(ap->a_nch, *vpp);
16357a2de9a4SMatthew Dillon }
16367a2de9a4SMatthew Dillon return error;
16377a2de9a4SMatthew Dillon }
16387a2de9a4SMatthew Dillon
16397a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
16407a2de9a4SMatthew Dillon
16417a2de9a4SMatthew Dillon static int
tmpfs_readdir(struct vop_readdir_args * ap)1642ba25006aSSascha Wildner tmpfs_readdir(struct vop_readdir_args *ap)
16437a2de9a4SMatthew Dillon {
1644ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
1645ba25006aSSascha Wildner struct uio *uio = ap->a_uio;
1646ba25006aSSascha Wildner int *eofflag = ap->a_eofflag;
1647ba25006aSSascha Wildner off_t **cookies = ap->a_cookies;
1648ba25006aSSascha Wildner int *ncookies = ap->a_ncookies;
164922d3b394SMatthew Dillon struct tmpfs_mount *tmp;
16507a2de9a4SMatthew Dillon int error;
16517a2de9a4SMatthew Dillon off_t startoff;
16527a2de9a4SMatthew Dillon off_t cnt = 0;
16537a2de9a4SMatthew Dillon struct tmpfs_node *node;
16547a2de9a4SMatthew Dillon
16557a2de9a4SMatthew Dillon /* This operation only makes sense on directory nodes. */
1656aa1adbf0SMatthew Dillon if (vp->v_type != VDIR) {
16577a2de9a4SMatthew Dillon return ENOTDIR;
1658aa1adbf0SMatthew Dillon }
16597a2de9a4SMatthew Dillon
166022d3b394SMatthew Dillon tmp = VFS_TO_TMPFS(vp->v_mount);
16617a2de9a4SMatthew Dillon node = VP_TO_TMPFS_DIR(vp);
16627a2de9a4SMatthew Dillon startoff = uio->uio_offset;
16637a2de9a4SMatthew Dillon
16647a2de9a4SMatthew Dillon if (uio->uio_offset == TMPFS_DIRCOOKIE_DOT) {
16657a2de9a4SMatthew Dillon error = tmpfs_dir_getdotdent(node, uio);
1666e91c5b26SMatthew Dillon if (error && error != EINVAL) {
1667ff837cd5SMatthew Dillon TMPFS_NODE_LOCK_SH(node);
16687a2de9a4SMatthew Dillon goto outok;
1669ff837cd5SMatthew Dillon }
16707a2de9a4SMatthew Dillon cnt++;
16717a2de9a4SMatthew Dillon }
16727a2de9a4SMatthew Dillon
16737a2de9a4SMatthew Dillon if (uio->uio_offset == TMPFS_DIRCOOKIE_DOTDOT) {
1674ff837cd5SMatthew Dillon /* may lock parent, cannot hold node lock */
167522d3b394SMatthew Dillon error = tmpfs_dir_getdotdotdent(tmp, node, uio);
1676e91c5b26SMatthew Dillon if (error && error != EINVAL) {
1677ff837cd5SMatthew Dillon TMPFS_NODE_LOCK_SH(node);
16787a2de9a4SMatthew Dillon goto outok;
1679ff837cd5SMatthew Dillon }
16807a2de9a4SMatthew Dillon cnt++;
16817a2de9a4SMatthew Dillon }
16827a2de9a4SMatthew Dillon
1683ff837cd5SMatthew Dillon TMPFS_NODE_LOCK_SH(node);
16847a2de9a4SMatthew Dillon error = tmpfs_dir_getdents(node, uio, &cnt);
16857a2de9a4SMatthew Dillon
16867a2de9a4SMatthew Dillon outok:
16877a2de9a4SMatthew Dillon KKASSERT(error >= -1);
16887a2de9a4SMatthew Dillon
16897a2de9a4SMatthew Dillon if (error == -1)
16907a2de9a4SMatthew Dillon error = 0;
16917a2de9a4SMatthew Dillon
16927a2de9a4SMatthew Dillon if (eofflag != NULL)
16937a2de9a4SMatthew Dillon *eofflag =
16947a2de9a4SMatthew Dillon (error == 0 && uio->uio_offset == TMPFS_DIRCOOKIE_EOF);
16957a2de9a4SMatthew Dillon
16967a2de9a4SMatthew Dillon /* Update NFS-related variables. */
16977a2de9a4SMatthew Dillon if (error == 0 && cookies != NULL && ncookies != NULL) {
16987a2de9a4SMatthew Dillon off_t i;
16997a2de9a4SMatthew Dillon off_t off = startoff;
17007a2de9a4SMatthew Dillon struct tmpfs_dirent *de = NULL;
17017a2de9a4SMatthew Dillon
17027a2de9a4SMatthew Dillon *ncookies = cnt;
17037a2de9a4SMatthew Dillon *cookies = kmalloc(cnt * sizeof(off_t), M_TEMP, M_WAITOK);
17047a2de9a4SMatthew Dillon
17057a2de9a4SMatthew Dillon for (i = 0; i < cnt; i++) {
17067a2de9a4SMatthew Dillon KKASSERT(off != TMPFS_DIRCOOKIE_EOF);
17077a2de9a4SMatthew Dillon if (off == TMPFS_DIRCOOKIE_DOT) {
17087a2de9a4SMatthew Dillon off = TMPFS_DIRCOOKIE_DOTDOT;
17097a2de9a4SMatthew Dillon } else {
17107a2de9a4SMatthew Dillon if (off == TMPFS_DIRCOOKIE_DOTDOT) {
1711f5f22af6SMatthew Dillon de = RB_MIN(tmpfs_dirtree_cookie,
1712f5f22af6SMatthew Dillon &node->tn_dir.tn_cookietree);
17137a2de9a4SMatthew Dillon } else if (de != NULL) {
1714f5f22af6SMatthew Dillon de = RB_NEXT(tmpfs_dirtree_cookie,
1715f5f22af6SMatthew Dillon &node->tn_dir.tn_cookietree, de);
17167a2de9a4SMatthew Dillon } else {
1717e91c5b26SMatthew Dillon de = tmpfs_dir_lookupbycookie(node, off,
1718e91c5b26SMatthew Dillon 1);
17197a2de9a4SMatthew Dillon KKASSERT(de != NULL);
1720f5f22af6SMatthew Dillon de = RB_NEXT(tmpfs_dirtree_cookie,
1721f5f22af6SMatthew Dillon &node->tn_dir.tn_cookietree, de);
17227a2de9a4SMatthew Dillon }
17237a2de9a4SMatthew Dillon if (de == NULL)
17247a2de9a4SMatthew Dillon off = TMPFS_DIRCOOKIE_EOF;
17257a2de9a4SMatthew Dillon else
17267a2de9a4SMatthew Dillon off = tmpfs_dircookie(de);
17277a2de9a4SMatthew Dillon }
17287a2de9a4SMatthew Dillon (*cookies)[i] = off;
17297a2de9a4SMatthew Dillon }
17307a2de9a4SMatthew Dillon KKASSERT(uio->uio_offset == off);
17317a2de9a4SMatthew Dillon }
173281aadc82STomohiro Kusumi TMPFS_NODE_UNLOCK(node);
17337a2de9a4SMatthew Dillon
1734bed3b851STomohiro Kusumi if ((node->tn_status & TMPFS_NODE_ACCESSED) == 0) {
173581aadc82STomohiro Kusumi TMPFS_NODE_LOCK(node);
1736ff837cd5SMatthew Dillon node->tn_status |= TMPFS_NODE_ACCESSED;
1737ff837cd5SMatthew Dillon TMPFS_NODE_UNLOCK(node);
1738bed3b851STomohiro Kusumi }
17397a2de9a4SMatthew Dillon return error;
17407a2de9a4SMatthew Dillon }
17417a2de9a4SMatthew Dillon
17427a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
17437a2de9a4SMatthew Dillon
17447a2de9a4SMatthew Dillon static int
tmpfs_readlink(struct vop_readlink_args * ap)1745ba25006aSSascha Wildner tmpfs_readlink(struct vop_readlink_args *ap)
17467a2de9a4SMatthew Dillon {
1747ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
1748ba25006aSSascha Wildner struct uio *uio = ap->a_uio;
17497a2de9a4SMatthew Dillon int error;
17507a2de9a4SMatthew Dillon struct tmpfs_node *node;
17517a2de9a4SMatthew Dillon
17527a2de9a4SMatthew Dillon KKASSERT(uio->uio_offset == 0);
17537a2de9a4SMatthew Dillon KKASSERT(vp->v_type == VLNK);
17547a2de9a4SMatthew Dillon
17557a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
1756ff837cd5SMatthew Dillon TMPFS_NODE_LOCK_SH(node);
1757ff837cd5SMatthew Dillon error = uiomove(node->tn_link,
1758ff837cd5SMatthew Dillon MIN(node->tn_size, uio->uio_resid), uio);
175981aadc82STomohiro Kusumi TMPFS_NODE_UNLOCK(node);
1760bed3b851STomohiro Kusumi if ((node->tn_status & TMPFS_NODE_ACCESSED) == 0) {
176181aadc82STomohiro Kusumi TMPFS_NODE_LOCK(node);
17627a2de9a4SMatthew Dillon node->tn_status |= TMPFS_NODE_ACCESSED;
17637a2de9a4SMatthew Dillon TMPFS_NODE_UNLOCK(node);
1764bed3b851STomohiro Kusumi }
17657a2de9a4SMatthew Dillon return error;
17667a2de9a4SMatthew Dillon }
17677a2de9a4SMatthew Dillon
17687a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
17697a2de9a4SMatthew Dillon
17707a2de9a4SMatthew Dillon static int
tmpfs_inactive(struct vop_inactive_args * ap)1771ba25006aSSascha Wildner tmpfs_inactive(struct vop_inactive_args *ap)
17727a2de9a4SMatthew Dillon {
1773ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
17747a2de9a4SMatthew Dillon struct tmpfs_node *node;
1775aa1adbf0SMatthew Dillon struct mount *mp;
17767a2de9a4SMatthew Dillon
1777aa1adbf0SMatthew Dillon mp = vp->v_mount;
1778aa1adbf0SMatthew Dillon lwkt_gettoken(&mp->mnt_token);
17797a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
17807a2de9a4SMatthew Dillon
17819fc94b5fSMatthew Dillon /*
1782a1fa5d8dSMatthew Dillon * Degenerate case
1783a1fa5d8dSMatthew Dillon */
1784a1fa5d8dSMatthew Dillon if (node == NULL) {
1785a1fa5d8dSMatthew Dillon vrecycle(vp);
1786aa1adbf0SMatthew Dillon lwkt_reltoken(&mp->mnt_token);
1787a1fa5d8dSMatthew Dillon return(0);
1788a1fa5d8dSMatthew Dillon }
1789a1fa5d8dSMatthew Dillon
1790a1fa5d8dSMatthew Dillon /*
17919fc94b5fSMatthew Dillon * Get rid of unreferenced deleted vnodes sooner rather than
17929fc94b5fSMatthew Dillon * later so the data memory can be recovered immediately.
1793f96f2f39SMatthew Dillon *
1794f96f2f39SMatthew Dillon * We must truncate the vnode to prevent the normal reclamation
1795f96f2f39SMatthew Dillon * path from flushing the data for the removed file to disk.
17969fc94b5fSMatthew Dillon */
17977a2de9a4SMatthew Dillon TMPFS_NODE_LOCK(node);
1798a44ecf5cSMatthew Dillon if (node->tn_links == 0) {
17999fc94b5fSMatthew Dillon node->tn_vpstate = TMPFS_VNODE_DOOMED;
18007a2de9a4SMatthew Dillon TMPFS_NODE_UNLOCK(node);
1801f96f2f39SMatthew Dillon if (node->tn_type == VREG)
1802f96f2f39SMatthew Dillon tmpfs_truncate(vp, 0);
18037a2de9a4SMatthew Dillon vrecycle(vp);
18049fc94b5fSMatthew Dillon } else {
1805a1b829f2SMatthew Dillon /*
1806a1b829f2SMatthew Dillon * We must retain any VM pages belonging to the vnode's
1807a1b829f2SMatthew Dillon * object as the vnode will destroy the object during a
1808a1b829f2SMatthew Dillon * later reclaim. We call vinvalbuf(V_SAVE) to clean
1809a1b829f2SMatthew Dillon * out the buffer cache.
1810a1b829f2SMatthew Dillon *
1811a1b829f2SMatthew Dillon * On DragonFlyBSD, vnodes are not immediately deactivated
1812a1b829f2SMatthew Dillon * on the 1->0 refs, so this is a relatively optimal
1813a1b829f2SMatthew Dillon * operation. We have to do this in tmpfs_inactive()
1814a1b829f2SMatthew Dillon * because the pages will have already been thrown away
1815a1b829f2SMatthew Dillon * at the time tmpfs_reclaim() is called.
1816a1b829f2SMatthew Dillon */
1817a1b829f2SMatthew Dillon if (node->tn_type == VREG &&
1818a1b829f2SMatthew Dillon node->tn_reg.tn_pages_in_aobj == 0) {
1819a1b829f2SMatthew Dillon vinvalbuf(vp, V_SAVE, 0, 0);
1820a1b829f2SMatthew Dillon KKASSERT(RB_EMPTY(&vp->v_rbdirty_tree));
1821a1b829f2SMatthew Dillon KKASSERT(RB_EMPTY(&vp->v_rbclean_tree));
1822cfd59c5aSMatthew Dillon tmpfs_move_pages(vp->v_object, node->tn_reg.tn_aobj,
1823cfd59c5aSMatthew Dillon TMPFS_MOVF_DEACTIVATE);
1824a1b829f2SMatthew Dillon node->tn_reg.tn_pages_in_aobj = 1;
1825a1b829f2SMatthew Dillon }
1826a1b829f2SMatthew Dillon
18277a2de9a4SMatthew Dillon TMPFS_NODE_UNLOCK(node);
18289fc94b5fSMatthew Dillon }
1829aa1adbf0SMatthew Dillon lwkt_reltoken(&mp->mnt_token);
18307a2de9a4SMatthew Dillon
18317a2de9a4SMatthew Dillon return 0;
18327a2de9a4SMatthew Dillon }
18337a2de9a4SMatthew Dillon
18347a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
18357a2de9a4SMatthew Dillon
18367a2de9a4SMatthew Dillon int
tmpfs_reclaim(struct vop_reclaim_args * ap)1837ba25006aSSascha Wildner tmpfs_reclaim(struct vop_reclaim_args *ap)
18387a2de9a4SMatthew Dillon {
1839ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
18407a2de9a4SMatthew Dillon struct tmpfs_mount *tmp;
18417a2de9a4SMatthew Dillon struct tmpfs_node *node;
1842aa1adbf0SMatthew Dillon struct mount *mp;
1843aa1adbf0SMatthew Dillon
1844aa1adbf0SMatthew Dillon mp = vp->v_mount;
1845aa1adbf0SMatthew Dillon lwkt_gettoken(&mp->mnt_token);
18467a2de9a4SMatthew Dillon
18477a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
18487a2de9a4SMatthew Dillon tmp = VFS_TO_TMPFS(vp->v_mount);
1849aa1adbf0SMatthew Dillon KKASSERT(mp == tmp->tm_mount);
18507a2de9a4SMatthew Dillon
185189984f3dSMatthew Dillon TMPFS_NODE_LOCK(node);
185289984f3dSMatthew Dillon KKASSERT(node->tn_vnode == vp);
185389984f3dSMatthew Dillon node->tn_vnode = NULL;
185489984f3dSMatthew Dillon vp->v_data = NULL;
18557a2de9a4SMatthew Dillon
1856b7fe63afSMatthew Dillon /*
1857b7fe63afSMatthew Dillon * If the node referenced by this vnode was deleted by the
1858b7fe63afSMatthew Dillon * user, we must free its associated data structures now that
1859b7fe63afSMatthew Dillon * the vnode is being reclaimed.
1860b7fe63afSMatthew Dillon *
1861b7fe63afSMatthew Dillon * Directories have an extra link ref.
1862b7fe63afSMatthew Dillon */
1863a44ecf5cSMatthew Dillon if (node->tn_links == 0) {
18647a2de9a4SMatthew Dillon node->tn_vpstate = TMPFS_VNODE_DOOMED;
18657a2de9a4SMatthew Dillon tmpfs_free_node(tmp, node);
18660786baf1SMatthew Dillon /* eats the lock */
18679fc94b5fSMatthew Dillon } else {
18687a2de9a4SMatthew Dillon TMPFS_NODE_UNLOCK(node);
18699fc94b5fSMatthew Dillon }
1870aa1adbf0SMatthew Dillon lwkt_reltoken(&mp->mnt_token);
18717a2de9a4SMatthew Dillon
18727a2de9a4SMatthew Dillon KKASSERT(vp->v_data == NULL);
18737a2de9a4SMatthew Dillon return 0;
18747a2de9a4SMatthew Dillon }
18757a2de9a4SMatthew Dillon
18767a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
18777a2de9a4SMatthew Dillon
18787a2de9a4SMatthew Dillon static int
tmpfs_mountctl(struct vop_mountctl_args * ap)187966fa44e7SVenkatesh Srinivas tmpfs_mountctl(struct vop_mountctl_args *ap)
188066fa44e7SVenkatesh Srinivas {
188166fa44e7SVenkatesh Srinivas struct tmpfs_mount *tmp;
188266fa44e7SVenkatesh Srinivas struct mount *mp;
188366fa44e7SVenkatesh Srinivas int rc;
188466fa44e7SVenkatesh Srinivas
1885aa1adbf0SMatthew Dillon mp = ap->a_head.a_ops->head.vv_mount;
1886aa1adbf0SMatthew Dillon lwkt_gettoken(&mp->mnt_token);
1887aa1adbf0SMatthew Dillon
188866fa44e7SVenkatesh Srinivas switch (ap->a_op) {
188966fa44e7SVenkatesh Srinivas case (MOUNTCTL_SET_EXPORT):
189066fa44e7SVenkatesh Srinivas tmp = (struct tmpfs_mount *) mp->mnt_data;
189166fa44e7SVenkatesh Srinivas
189266fa44e7SVenkatesh Srinivas if (ap->a_ctllen != sizeof(struct export_args))
189366fa44e7SVenkatesh Srinivas rc = (EINVAL);
189466fa44e7SVenkatesh Srinivas else
189566fa44e7SVenkatesh Srinivas rc = vfs_export(mp, &tmp->tm_export,
189666fa44e7SVenkatesh Srinivas (const struct export_args *) ap->a_ctl);
189766fa44e7SVenkatesh Srinivas break;
189866fa44e7SVenkatesh Srinivas default:
189966fa44e7SVenkatesh Srinivas rc = vop_stdmountctl(ap);
190066fa44e7SVenkatesh Srinivas break;
190166fa44e7SVenkatesh Srinivas }
1902aa1adbf0SMatthew Dillon
1903aa1adbf0SMatthew Dillon lwkt_reltoken(&mp->mnt_token);
190466fa44e7SVenkatesh Srinivas return (rc);
190566fa44e7SVenkatesh Srinivas }
190666fa44e7SVenkatesh Srinivas
190766fa44e7SVenkatesh Srinivas /* --------------------------------------------------------------------- */
190866fa44e7SVenkatesh Srinivas
190966fa44e7SVenkatesh Srinivas static int
tmpfs_print(struct vop_print_args * ap)1910ba25006aSSascha Wildner tmpfs_print(struct vop_print_args *ap)
19117a2de9a4SMatthew Dillon {
1912ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
19137a2de9a4SMatthew Dillon
19147a2de9a4SMatthew Dillon struct tmpfs_node *node;
19157a2de9a4SMatthew Dillon
19167a2de9a4SMatthew Dillon node = VP_TO_TMPFS_NODE(vp);
19177a2de9a4SMatthew Dillon
19187a2de9a4SMatthew Dillon kprintf("tag VT_TMPFS, tmpfs_node %p, flags 0x%x, links %d\n",
19197a2de9a4SMatthew Dillon node, node->tn_flags, node->tn_links);
19207a2de9a4SMatthew Dillon kprintf("\tmode 0%o, owner %d, group %d, size %ju, status 0x%x\n",
19217a2de9a4SMatthew Dillon node->tn_mode, node->tn_uid, node->tn_gid,
19227a2de9a4SMatthew Dillon (uintmax_t)node->tn_size, node->tn_status);
19237a2de9a4SMatthew Dillon
19247a2de9a4SMatthew Dillon if (vp->v_type == VFIFO)
19257a2de9a4SMatthew Dillon fifo_printinfo(vp);
19267a2de9a4SMatthew Dillon
19277a2de9a4SMatthew Dillon kprintf("\n");
19287a2de9a4SMatthew Dillon
19297a2de9a4SMatthew Dillon return 0;
19307a2de9a4SMatthew Dillon }
19317a2de9a4SMatthew Dillon
19327a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
19337a2de9a4SMatthew Dillon
19347a2de9a4SMatthew Dillon static int
tmpfs_pathconf(struct vop_pathconf_args * ap)1935ba25006aSSascha Wildner tmpfs_pathconf(struct vop_pathconf_args *ap)
19367a2de9a4SMatthew Dillon {
1937ba25006aSSascha Wildner struct vnode *vp = ap->a_vp;
1938ba25006aSSascha Wildner int name = ap->a_name;
1939ba25006aSSascha Wildner register_t *retval = ap->a_retval;
194036799b8eSSascha Wildner struct tmpfs_mount *tmp;
19417a2de9a4SMatthew Dillon int error;
19427a2de9a4SMatthew Dillon
19437a2de9a4SMatthew Dillon error = 0;
19447a2de9a4SMatthew Dillon
19457a2de9a4SMatthew Dillon switch (name) {
1946b3b8fc63SSascha Wildner case _PC_CHOWN_RESTRICTED:
1947b3b8fc63SSascha Wildner *retval = 1;
1948b3b8fc63SSascha Wildner break;
1949b3b8fc63SSascha Wildner
1950b3b8fc63SSascha Wildner case _PC_FILESIZEBITS:
195136799b8eSSascha Wildner tmp = VFS_TO_TMPFS(vp->v_mount);
195236799b8eSSascha Wildner *retval = max(32, flsll(tmp->tm_pages_max * PAGE_SIZE) + 1);
1953b3b8fc63SSascha Wildner break;
1954b3b8fc63SSascha Wildner
19557a2de9a4SMatthew Dillon case _PC_LINK_MAX:
19567a2de9a4SMatthew Dillon *retval = LINK_MAX;
19577a2de9a4SMatthew Dillon break;
19587a2de9a4SMatthew Dillon
19597a2de9a4SMatthew Dillon case _PC_NAME_MAX:
19607a2de9a4SMatthew Dillon *retval = NAME_MAX;
19617a2de9a4SMatthew Dillon break;
19627a2de9a4SMatthew Dillon
1963b3b8fc63SSascha Wildner case _PC_NO_TRUNC:
1964b3b8fc63SSascha Wildner *retval = 1;
1965b3b8fc63SSascha Wildner break;
1966b3b8fc63SSascha Wildner
19677a2de9a4SMatthew Dillon case _PC_PATH_MAX:
19687a2de9a4SMatthew Dillon *retval = PATH_MAX;
19697a2de9a4SMatthew Dillon break;
19707a2de9a4SMatthew Dillon
19717a2de9a4SMatthew Dillon case _PC_PIPE_BUF:
19727a2de9a4SMatthew Dillon *retval = PIPE_BUF;
19737a2de9a4SMatthew Dillon break;
19747a2de9a4SMatthew Dillon
19757a2de9a4SMatthew Dillon case _PC_SYNC_IO:
19767a2de9a4SMatthew Dillon *retval = 1;
19777a2de9a4SMatthew Dillon break;
19787a2de9a4SMatthew Dillon
197909789cfdSSascha Wildner case _PC_2_SYMLINKS:
198009789cfdSSascha Wildner *retval = 1;
198109789cfdSSascha Wildner break;
198209789cfdSSascha Wildner
19837a2de9a4SMatthew Dillon default:
19847a2de9a4SMatthew Dillon error = EINVAL;
19857a2de9a4SMatthew Dillon }
19867a2de9a4SMatthew Dillon
19877a2de9a4SMatthew Dillon return error;
19887a2de9a4SMatthew Dillon }
19897a2de9a4SMatthew Dillon
199080ae59d7SMatthew Dillon /************************************************************************
199180ae59d7SMatthew Dillon * KQFILTER OPS *
199280ae59d7SMatthew Dillon ************************************************************************/
199380ae59d7SMatthew Dillon
199480ae59d7SMatthew Dillon static void filt_tmpfsdetach(struct knote *kn);
199580ae59d7SMatthew Dillon static int filt_tmpfsread(struct knote *kn, long hint);
199680ae59d7SMatthew Dillon static int filt_tmpfswrite(struct knote *kn, long hint);
199780ae59d7SMatthew Dillon static int filt_tmpfsvnode(struct knote *kn, long hint);
199880ae59d7SMatthew Dillon
199980ae59d7SMatthew Dillon static struct filterops tmpfsread_filtops =
2000ff837cd5SMatthew Dillon { FILTEROP_ISFD | FILTEROP_MPSAFE,
2001ff837cd5SMatthew Dillon NULL, filt_tmpfsdetach, filt_tmpfsread };
200280ae59d7SMatthew Dillon static struct filterops tmpfswrite_filtops =
2003ff837cd5SMatthew Dillon { FILTEROP_ISFD | FILTEROP_MPSAFE,
2004ff837cd5SMatthew Dillon NULL, filt_tmpfsdetach, filt_tmpfswrite };
200580ae59d7SMatthew Dillon static struct filterops tmpfsvnode_filtops =
2006ff837cd5SMatthew Dillon { FILTEROP_ISFD | FILTEROP_MPSAFE,
2007ff837cd5SMatthew Dillon NULL, filt_tmpfsdetach, filt_tmpfsvnode };
200880ae59d7SMatthew Dillon
200980ae59d7SMatthew Dillon static int
tmpfs_kqfilter(struct vop_kqfilter_args * ap)201080ae59d7SMatthew Dillon tmpfs_kqfilter (struct vop_kqfilter_args *ap)
201180ae59d7SMatthew Dillon {
201280ae59d7SMatthew Dillon struct vnode *vp = ap->a_vp;
201380ae59d7SMatthew Dillon struct knote *kn = ap->a_kn;
201480ae59d7SMatthew Dillon
201580ae59d7SMatthew Dillon switch (kn->kn_filter) {
201680ae59d7SMatthew Dillon case EVFILT_READ:
201780ae59d7SMatthew Dillon kn->kn_fop = &tmpfsread_filtops;
201880ae59d7SMatthew Dillon break;
201980ae59d7SMatthew Dillon case EVFILT_WRITE:
202080ae59d7SMatthew Dillon kn->kn_fop = &tmpfswrite_filtops;
202180ae59d7SMatthew Dillon break;
202280ae59d7SMatthew Dillon case EVFILT_VNODE:
202380ae59d7SMatthew Dillon kn->kn_fop = &tmpfsvnode_filtops;
202480ae59d7SMatthew Dillon break;
202580ae59d7SMatthew Dillon default:
202680ae59d7SMatthew Dillon return (EOPNOTSUPP);
202780ae59d7SMatthew Dillon }
202880ae59d7SMatthew Dillon
202980ae59d7SMatthew Dillon kn->kn_hook = (caddr_t)vp;
203080ae59d7SMatthew Dillon
203180ae59d7SMatthew Dillon knote_insert(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
203280ae59d7SMatthew Dillon
203380ae59d7SMatthew Dillon return(0);
203480ae59d7SMatthew Dillon }
203580ae59d7SMatthew Dillon
203680ae59d7SMatthew Dillon static void
filt_tmpfsdetach(struct knote * kn)203780ae59d7SMatthew Dillon filt_tmpfsdetach(struct knote *kn)
203880ae59d7SMatthew Dillon {
203980ae59d7SMatthew Dillon struct vnode *vp = (void *)kn->kn_hook;
204080ae59d7SMatthew Dillon
204180ae59d7SMatthew Dillon knote_remove(&vp->v_pollinfo.vpi_kqinfo.ki_note, kn);
204280ae59d7SMatthew Dillon }
204380ae59d7SMatthew Dillon
204480ae59d7SMatthew Dillon static int
filt_tmpfsread(struct knote * kn,long hint)204580ae59d7SMatthew Dillon filt_tmpfsread(struct knote *kn, long hint)
204680ae59d7SMatthew Dillon {
204780ae59d7SMatthew Dillon struct vnode *vp = (void *)kn->kn_hook;
204880ae59d7SMatthew Dillon struct tmpfs_node *node = VP_TO_TMPFS_NODE(vp);
204980ae59d7SMatthew Dillon off_t off;
205080ae59d7SMatthew Dillon
205180ae59d7SMatthew Dillon if (hint == NOTE_REVOKE) {
20523bcb6e5eSSepherosa Ziehau kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
205380ae59d7SMatthew Dillon return(1);
205480ae59d7SMatthew Dillon }
205580ae59d7SMatthew Dillon
205680ae59d7SMatthew Dillon /*
2057f79d9cc9SMatthew Dillon * Interlock against MP races when performing this function.
205880ae59d7SMatthew Dillon */
2059ff837cd5SMatthew Dillon TMPFS_NODE_LOCK_SH(node);
2060f79d9cc9SMatthew Dillon off = node->tn_size - kn->kn_fp->f_offset;
2061f79d9cc9SMatthew Dillon kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
2062f79d9cc9SMatthew Dillon if (kn->kn_sfflags & NOTE_OLDAPI) {
2063ff837cd5SMatthew Dillon TMPFS_NODE_UNLOCK(node);
2064f79d9cc9SMatthew Dillon return(1);
2065f79d9cc9SMatthew Dillon }
206680ae59d7SMatthew Dillon if (kn->kn_data == 0) {
206780ae59d7SMatthew Dillon kn->kn_data = (off < INTPTR_MAX) ? off : INTPTR_MAX;
206880ae59d7SMatthew Dillon }
2069ff837cd5SMatthew Dillon TMPFS_NODE_UNLOCK(node);
207080ae59d7SMatthew Dillon return (kn->kn_data != 0);
207180ae59d7SMatthew Dillon }
207280ae59d7SMatthew Dillon
207380ae59d7SMatthew Dillon static int
filt_tmpfswrite(struct knote * kn,long hint)207480ae59d7SMatthew Dillon filt_tmpfswrite(struct knote *kn, long hint)
207580ae59d7SMatthew Dillon {
207680ae59d7SMatthew Dillon if (hint == NOTE_REVOKE)
20773bcb6e5eSSepherosa Ziehau kn->kn_flags |= (EV_EOF | EV_NODATA | EV_ONESHOT);
207880ae59d7SMatthew Dillon kn->kn_data = 0;
207980ae59d7SMatthew Dillon return (1);
208080ae59d7SMatthew Dillon }
208180ae59d7SMatthew Dillon
208280ae59d7SMatthew Dillon static int
filt_tmpfsvnode(struct knote * kn,long hint)208380ae59d7SMatthew Dillon filt_tmpfsvnode(struct knote *kn, long hint)
208480ae59d7SMatthew Dillon {
208580ae59d7SMatthew Dillon if (kn->kn_sfflags & hint)
208680ae59d7SMatthew Dillon kn->kn_fflags |= hint;
208780ae59d7SMatthew Dillon if (hint == NOTE_REVOKE) {
20883bcb6e5eSSepherosa Ziehau kn->kn_flags |= (EV_EOF | EV_NODATA);
208980ae59d7SMatthew Dillon return (1);
209080ae59d7SMatthew Dillon }
209180ae59d7SMatthew Dillon return (kn->kn_fflags != 0);
209280ae59d7SMatthew Dillon }
209380ae59d7SMatthew Dillon
2094a1b829f2SMatthew Dillon /*
2095a1b829f2SMatthew Dillon * Helper to move VM pages between objects
2096a1b829f2SMatthew Dillon *
2097a1b829f2SMatthew Dillon * NOTE: The vm_page_rename() dirties the page, so we can clear the
2098a1b829f2SMatthew Dillon * PG_NEED_COMMIT flag. If the pages are being moved into tn_aobj,
2099a1b829f2SMatthew Dillon * the pageout daemon will be able to page them out.
2100a1b829f2SMatthew Dillon */
2101a1b829f2SMatthew Dillon static int
tmpfs_move_pages_callback(vm_page_t p,void * data)2102a1b829f2SMatthew Dillon tmpfs_move_pages_callback(vm_page_t p, void *data)
2103a1b829f2SMatthew Dillon {
2104a1b829f2SMatthew Dillon struct rb_vm_page_scan_info *info = data;
2105a1b829f2SMatthew Dillon vm_pindex_t pindex;
2106a1b829f2SMatthew Dillon
2107965c458cSMatthew Dillon /*
2108965c458cSMatthew Dillon * Take control of the page
2109965c458cSMatthew Dillon */
2110a1b829f2SMatthew Dillon pindex = p->pindex;
2111a1b829f2SMatthew Dillon if (vm_page_busy_try(p, TRUE)) {
2112a1b829f2SMatthew Dillon vm_page_sleep_busy(p, TRUE, "tpgmov");
2113a1b829f2SMatthew Dillon info->error = -1;
2114a1b829f2SMatthew Dillon return -1;
2115a1b829f2SMatthew Dillon }
2116a1b829f2SMatthew Dillon if (p->object != info->object || p->pindex != pindex) {
2117a1b829f2SMatthew Dillon vm_page_wakeup(p);
2118a1b829f2SMatthew Dillon info->error = -1;
2119a1b829f2SMatthew Dillon return -1;
2120a1b829f2SMatthew Dillon }
2121cfd59c5aSMatthew Dillon
2122965c458cSMatthew Dillon /*
2123965c458cSMatthew Dillon * Make sure the page is not mapped. These flags might also still be
2124965c458cSMatthew Dillon * set heuristically even if we know the page is not mapped and must
2125965c458cSMatthew Dillon * be properly cleaned up.
2126965c458cSMatthew Dillon */
2127965c458cSMatthew Dillon if (__predict_false((p->flags & (PG_MAPPED|PG_WRITEABLE)) != 0))
2128965c458cSMatthew Dillon vm_page_protect(p, VM_PROT_NONE);
2129965c458cSMatthew Dillon
2130965c458cSMatthew Dillon /*
2131965c458cSMatthew Dillon * Free or rename the page as appropriate
2132965c458cSMatthew Dillon */
2133cfd59c5aSMatthew Dillon if ((info->pagerflags & TMPFS_MOVF_FROMBACKING) &&
2134cfd59c5aSMatthew Dillon (p->flags & PG_SWAPPED) &&
2135cfd59c5aSMatthew Dillon (p->flags & PG_NEED_COMMIT) == 0 &&
2136cfd59c5aSMatthew Dillon p->dirty == 0) {
2137cfd59c5aSMatthew Dillon /*
2138cfd59c5aSMatthew Dillon * If the page in the backing aobj was paged out to swap
2139cfd59c5aSMatthew Dillon * it will be clean and it is better to free it rather
2140cfd59c5aSMatthew Dillon * than re-dirty it. We will assume that the page was
2141cfd59c5aSMatthew Dillon * paged out to swap for a reason!
2142cfd59c5aSMatthew Dillon *
2143cfd59c5aSMatthew Dillon * This helps avoid unnecessary swap thrashing on the page.
2144cfd59c5aSMatthew Dillon */
2145cfd59c5aSMatthew Dillon vm_page_free(p);
2146cfd59c5aSMatthew Dillon } else if ((info->pagerflags & TMPFS_MOVF_FROMBACKING) == 0 &&
2147cfd59c5aSMatthew Dillon (p->flags & PG_NEED_COMMIT) == 0 &&
2148cfd59c5aSMatthew Dillon p->dirty == 0) {
2149cfd59c5aSMatthew Dillon /*
2150cfd59c5aSMatthew Dillon * If the page associated with the vnode was cleaned via
2151cfd59c5aSMatthew Dillon * a tmpfs_strategy() call, it exists as a swap block in
2152cfd59c5aSMatthew Dillon * aobj and it is again better to free it rather than
2153cfd59c5aSMatthew Dillon * re-dirty it. We will assume that the page was
2154cfd59c5aSMatthew Dillon * paged out to swap for a reason!
2155cfd59c5aSMatthew Dillon *
2156cfd59c5aSMatthew Dillon * This helps avoid unnecessary swap thrashing on the page.
2157cfd59c5aSMatthew Dillon */
2158cfd59c5aSMatthew Dillon vm_page_free(p);
2159cfd59c5aSMatthew Dillon } else {
2160cfd59c5aSMatthew Dillon /*
2161cfd59c5aSMatthew Dillon * Rename the page, which will also ensure that it is flagged
2162cfd59c5aSMatthew Dillon * as dirty and check whether a swap block association exists
2163cfd59c5aSMatthew Dillon * in the target object or not, setting appropriate flags if
2164cfd59c5aSMatthew Dillon * it does.
2165cfd59c5aSMatthew Dillon */
2166530e94fcSMatthew Dillon vm_page_rename(p, info->dest_object, pindex);
2167a1b829f2SMatthew Dillon vm_page_clear_commit(p);
2168cfd59c5aSMatthew Dillon if (info->pagerflags & TMPFS_MOVF_DEACTIVATE)
2169cfd59c5aSMatthew Dillon vm_page_deactivate(p);
2170a1b829f2SMatthew Dillon vm_page_wakeup(p);
2171a1b829f2SMatthew Dillon /* page automaticaly made dirty */
2172cfd59c5aSMatthew Dillon }
2173a1b829f2SMatthew Dillon
2174a1b829f2SMatthew Dillon return 0;
2175a1b829f2SMatthew Dillon }
2176a1b829f2SMatthew Dillon
2177a1b829f2SMatthew Dillon static
2178a1b829f2SMatthew Dillon void
tmpfs_move_pages(vm_object_t src,vm_object_t dst,int movflags)2179cfd59c5aSMatthew Dillon tmpfs_move_pages(vm_object_t src, vm_object_t dst, int movflags)
2180a1b829f2SMatthew Dillon {
2181a1b829f2SMatthew Dillon struct rb_vm_page_scan_info info;
2182a1b829f2SMatthew Dillon
2183a1b829f2SMatthew Dillon vm_object_hold(src);
2184a1b829f2SMatthew Dillon vm_object_hold(dst);
2185a1b829f2SMatthew Dillon info.object = src;
2186530e94fcSMatthew Dillon info.dest_object = dst;
2187cfd59c5aSMatthew Dillon info.pagerflags = movflags;
2188a1b829f2SMatthew Dillon do {
218969940345SMatthew Dillon if (src->paging_in_progress)
219069940345SMatthew Dillon vm_object_pip_wait(src, "objtfs");
2191a1b829f2SMatthew Dillon info.error = 1;
2192a1b829f2SMatthew Dillon vm_page_rb_tree_RB_SCAN(&src->rb_memq, NULL,
2193a1b829f2SMatthew Dillon tmpfs_move_pages_callback, &info);
219469940345SMatthew Dillon } while (info.error < 0 || !RB_EMPTY(&src->rb_memq) ||
219569940345SMatthew Dillon src->paging_in_progress);
2196a1b829f2SMatthew Dillon vm_object_drop(dst);
2197a1b829f2SMatthew Dillon vm_object_drop(src);
2198a1b829f2SMatthew Dillon }
219980ae59d7SMatthew Dillon
22007a2de9a4SMatthew Dillon /* --------------------------------------------------------------------- */
22017a2de9a4SMatthew Dillon
22027a2de9a4SMatthew Dillon /*
22037a2de9a4SMatthew Dillon * vnode operations vector used for files stored in a tmpfs file system.
22047a2de9a4SMatthew Dillon */
22057a2de9a4SMatthew Dillon struct vop_ops tmpfs_vnode_vops = {
22067a2de9a4SMatthew Dillon .vop_default = vop_defaultop,
22077a2de9a4SMatthew Dillon .vop_getpages = vop_stdgetpages,
22087a2de9a4SMatthew Dillon .vop_putpages = vop_stdputpages,
22097a2de9a4SMatthew Dillon .vop_ncreate = tmpfs_ncreate,
22107a2de9a4SMatthew Dillon .vop_nresolve = tmpfs_nresolve,
22117a2de9a4SMatthew Dillon .vop_nlookupdotdot = tmpfs_nlookupdotdot,
22127a2de9a4SMatthew Dillon .vop_nmknod = tmpfs_nmknod,
22137a2de9a4SMatthew Dillon .vop_open = tmpfs_open,
22147a2de9a4SMatthew Dillon .vop_close = tmpfs_close,
22157a2de9a4SMatthew Dillon .vop_access = tmpfs_access,
22167a2de9a4SMatthew Dillon .vop_getattr = tmpfs_getattr,
2217de9bb133SMatthew Dillon .vop_getattr_lite = tmpfs_getattr_lite,
22187a2de9a4SMatthew Dillon .vop_setattr = tmpfs_setattr,
22197a2de9a4SMatthew Dillon .vop_read = tmpfs_read,
22207a2de9a4SMatthew Dillon .vop_write = tmpfs_write,
22217a2de9a4SMatthew Dillon .vop_fsync = tmpfs_fsync,
222266fa44e7SVenkatesh Srinivas .vop_mountctl = tmpfs_mountctl,
22237a2de9a4SMatthew Dillon .vop_nremove = tmpfs_nremove,
22247a2de9a4SMatthew Dillon .vop_nlink = tmpfs_nlink,
22257a2de9a4SMatthew Dillon .vop_nrename = tmpfs_nrename,
22267a2de9a4SMatthew Dillon .vop_nmkdir = tmpfs_nmkdir,
22277a2de9a4SMatthew Dillon .vop_nrmdir = tmpfs_nrmdir,
22287a2de9a4SMatthew Dillon .vop_nsymlink = tmpfs_nsymlink,
22297a2de9a4SMatthew Dillon .vop_readdir = tmpfs_readdir,
22307a2de9a4SMatthew Dillon .vop_readlink = tmpfs_readlink,
22317a2de9a4SMatthew Dillon .vop_inactive = tmpfs_inactive,
22327a2de9a4SMatthew Dillon .vop_reclaim = tmpfs_reclaim,
22337a2de9a4SMatthew Dillon .vop_print = tmpfs_print,
22347a2de9a4SMatthew Dillon .vop_pathconf = tmpfs_pathconf,
22359fc94b5fSMatthew Dillon .vop_bmap = tmpfs_bmap,
22367a2de9a4SMatthew Dillon .vop_strategy = tmpfs_strategy,
22377a2de9a4SMatthew Dillon .vop_advlock = tmpfs_advlock,
223880ae59d7SMatthew Dillon .vop_kqfilter = tmpfs_kqfilter
22397a2de9a4SMatthew Dillon };
2240