]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_node.c
xnu-2050.22.13.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_node.c
CommitLineData
1c79356b 1/*
6d2010ae 2 * Copyright (c) 2000-2011 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
1c79356b 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
8f6c56a5 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
8f6c56a5 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
1c79356b
A
27 */
28/* Copyright (c) 1995 NeXT Computer, Inc. All Rights Reserved */
29/*
30 * Copyright (c) 1989, 1993
31 * The Regents of the University of California. All rights reserved.
32 *
33 * This code is derived from software contributed to Berkeley by
34 * Rick Macklem at The University of Guelph.
35 *
36 * Redistribution and use in source and binary forms, with or without
37 * modification, are permitted provided that the following conditions
38 * are met:
39 * 1. Redistributions of source code must retain the above copyright
40 * notice, this list of conditions and the following disclaimer.
41 * 2. Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in the
43 * documentation and/or other materials provided with the distribution.
44 * 3. All advertising materials mentioning features or use of this software
45 * must display the following acknowledgement:
46 * This product includes software developed by the University of
47 * California, Berkeley and its contributors.
48 * 4. Neither the name of the University nor the names of its contributors
49 * may be used to endorse or promote products derived from this software
50 * without specific prior written permission.
51 *
52 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
53 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
54 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
55 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
56 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
57 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
58 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
59 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
60 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
61 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
62 * SUCH DAMAGE.
63 *
64 * @(#)nfs_node.c 8.6 (Berkeley) 5/22/95
65 * FreeBSD-Id: nfs_node.c,v 1.22 1997/10/28 14:06:20 bde Exp $
66 */
67
68
69#include <sys/param.h>
b0d623f7 70#include <sys/kernel.h>
1c79356b
A
71#include <sys/systm.h>
72#include <sys/proc.h>
91447636
A
73#include <sys/kauth.h>
74#include <sys/mount_internal.h>
1c79356b 75#include <sys/vnode.h>
91447636 76#include <sys/ubc.h>
1c79356b 77#include <sys/malloc.h>
6d2010ae 78#include <sys/fcntl.h>
316670eb 79#include <sys/time.h>
1c79356b
A
80
81#include <nfs/rpcv2.h>
82#include <nfs/nfsproto.h>
83#include <nfs/nfs.h>
84#include <nfs/nfsnode.h>
2d21ac55 85#include <nfs/nfs_gss.h>
1c79356b
A
86#include <nfs/nfsmount.h>
87
2d21ac55
A
88#define NFSNOHASH(fhsum) \
89 (&nfsnodehashtbl[(fhsum) & nfsnodehash])
90static LIST_HEAD(nfsnodehashhead, nfsnode) *nfsnodehashtbl;
91static u_long nfsnodehash;
1c79356b 92
2d21ac55
A
93static lck_grp_t *nfs_node_hash_lck_grp;
94static lck_grp_t *nfs_node_lck_grp;
b0d623f7 95static lck_grp_t *nfs_data_lck_grp;
91447636 96lck_mtx_t *nfs_node_hash_mutex;
1c79356b
A
97
98/*
99 * Initialize hash links for nfsnodes
100 * and build nfsnode free list.
101 */
102void
e5568f75 103nfs_nhinit(void)
1c79356b 104{
2d21ac55
A
105 nfs_node_hash_lck_grp = lck_grp_alloc_init("nfs_node_hash", LCK_GRP_ATTR_NULL);
106 nfs_node_hash_mutex = lck_mtx_alloc_init(nfs_node_hash_lck_grp, LCK_ATTR_NULL);
107 nfs_node_lck_grp = lck_grp_alloc_init("nfs_node", LCK_GRP_ATTR_NULL);
b0d623f7 108 nfs_data_lck_grp = lck_grp_alloc_init("nfs_data", LCK_GRP_ATTR_NULL);
2d21ac55 109}
91447636 110
2d21ac55
A
111void
112nfs_nhinit_finish(void)
113{
114 lck_mtx_lock(nfs_node_hash_mutex);
115 if (!nfsnodehashtbl)
116 nfsnodehashtbl = hashinit(desiredvnodes, M_NFSNODE, &nfsnodehash);
117 lck_mtx_unlock(nfs_node_hash_mutex);
1c79356b
A
118}
119
120/*
121 * Compute an entry in the NFS hash table structure
122 */
123u_long
91447636 124nfs_hash(u_char *fhp, int fhsize)
1c79356b 125{
91447636
A
126 u_long fhsum;
127 int i;
1c79356b 128
1c79356b
A
129 fhsum = 0;
130 for (i = 0; i < fhsize; i++)
91447636 131 fhsum += *fhp++;
1c79356b
A
132 return (fhsum);
133}
134
135/*
136 * Look up a vnode/nfsnode by file handle.
137 * Callers must check for mount points!!
138 * In all cases, a pointer to a
139 * nfsnode structure is returned.
140 */
1c79356b 141int
91447636 142nfs_nget(
2d21ac55
A
143 mount_t mp,
144 nfsnode_t dnp,
91447636
A
145 struct componentname *cnp,
146 u_char *fhp,
147 int fhsize,
148 struct nfs_vattr *nvap,
149 u_int64_t *xidp,
6d2010ae 150 uint32_t auth,
91447636 151 int flags,
2d21ac55 152 nfsnode_t *npp)
1c79356b 153{
2d21ac55 154 nfsnode_t np;
1c79356b 155 struct nfsnodehashhead *nhpp;
2d21ac55
A
156 vnode_t vp;
157 int error, nfsvers;
158 mount_t mp2;
91447636
A
159 struct vnode_fsparam vfsp;
160 uint32_t vid;
1c79356b 161
2d21ac55
A
162 FSDBG_TOP(263, mp, dnp, flags, npp);
163
1c79356b 164 /* Check for unmount in progress */
2d21ac55
A
165 if (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT)) {
166 *npp = NULL;
167 error = ENXIO;
168 FSDBG_BOT(263, mp, dnp, 0xd1e, error);
169 return (error);
1c79356b 170 }
2d21ac55 171 nfsvers = VFSTONFS(mp)->nm_vers;
1c79356b
A
172
173 nhpp = NFSNOHASH(nfs_hash(fhp, fhsize));
174loop:
91447636 175 lck_mtx_lock(nfs_node_hash_mutex);
1c79356b 176 for (np = nhpp->lh_first; np != 0; np = np->n_hash.le_next) {
2d21ac55
A
177 mp2 = (np->n_hflag & NHINIT) ? np->n_mount : NFSTOMP(np);
178 if (mp != mp2 || np->n_fhsize != fhsize ||
91447636 179 bcmp(fhp, np->n_fhp, fhsize))
1c79356b 180 continue;
6d2010ae
A
181 if (nvap && (nvap->nva_flags & NFS_FFLAG_TRIGGER_REFERRAL) &&
182 cnp && (cnp->cn_namelen > (fhsize - (int)sizeof(dnp)))) {
183 /* The name was too long to fit in the file handle. Check it against the node's name. */
184 int namecmp = 0;
185 const char *vname = vnode_getname(NFSTOV(np));
186 if (vname) {
187 if (cnp->cn_namelen != (int)strlen(vname))
188 namecmp = 1;
189 else
190 namecmp = strncmp(vname, cnp->cn_nameptr, cnp->cn_namelen);
191 vnode_putname(vname);
192 }
193 if (namecmp) /* full name didn't match */
194 continue;
195 }
2d21ac55
A
196 FSDBG(263, dnp, np, np->n_flag, 0xcace0000);
197 /* if the node is locked, sleep on it */
b0d623f7 198 if ((np->n_hflag & NHLOCKED) && !(flags & NG_NOCREATE)) {
2d21ac55
A
199 np->n_hflag |= NHLOCKWANT;
200 FSDBG(263, dnp, np, np->n_flag, 0xcace2222);
201 msleep(np, nfs_node_hash_mutex, PDROP | PINOD, "nfs_nget", NULL);
202 FSDBG(263, dnp, np, np->n_flag, 0xcace3333);
55e303ae
A
203 goto loop;
204 }
1c79356b 205 vp = NFSTOV(np);
91447636
A
206 vid = vnode_vid(vp);
207 lck_mtx_unlock(nfs_node_hash_mutex);
208 if ((error = vnode_getwithvid(vp, vid))) {
209 /*
210 * If vnode is being reclaimed or has already
211 * changed identity, no need to wait.
212 */
2d21ac55 213 FSDBG_BOT(263, dnp, *npp, 0xcace0d1e, error);
91447636 214 return (error);
2d21ac55 215 }
b0d623f7 216 if ((error = nfs_node_lock(np))) {
2d21ac55
A
217 /* this only fails if the node is now unhashed */
218 /* so let's see if we can find/create it again */
219 FSDBG(263, dnp, *npp, 0xcaced1e2, error);
220 vnode_put(vp);
b0d623f7
A
221 if (flags & NG_NOCREATE) {
222 *npp = 0;
223 FSDBG_BOT(263, dnp, *npp, 0xcaced1e0, ENOENT);
224 return (ENOENT);
225 }
2d21ac55
A
226 goto loop;
227 }
91447636 228 /* update attributes */
b0d623f7
A
229 if (nvap)
230 error = nfs_loadattrcache(np, nvap, xidp, 0);
91447636 231 if (error) {
b0d623f7 232 nfs_node_unlock(np);
91447636
A
233 vnode_put(vp);
234 } else {
2d21ac55
A
235 if (dnp && cnp && (flags & NG_MAKEENTRY))
236 cache_enter(NFSTOV(dnp), vp, cnp);
91447636 237 *npp = np;
1c79356b 238 }
2d21ac55 239 FSDBG_BOT(263, dnp, *npp, 0xcace0000, error);
91447636 240 return(error);
1c79356b 241 }
1c79356b 242
2d21ac55
A
243 FSDBG(263, mp, dnp, npp, 0xaaaaaaaa);
244
b0d623f7
A
245 if (flags & NG_NOCREATE) {
246 lck_mtx_unlock(nfs_node_hash_mutex);
247 *npp = 0;
248 FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOENT);
249 return (ENOENT);
250 }
251
1c79356b 252 /*
55e303ae
A
253 * allocate and initialize nfsnode and stick it in the hash
254 * before calling getnewvnode(). Anyone finding it in the
255 * hash before initialization is complete will wait for it.
1c79356b 256 */
2d21ac55 257 MALLOC_ZONE(np, nfsnode_t, sizeof *np, M_NFSNODE, M_WAITOK);
91447636
A
258 if (!np) {
259 lck_mtx_unlock(nfs_node_hash_mutex);
260 *npp = 0;
2d21ac55 261 FSDBG_BOT(263, dnp, *npp, 0x80000001, ENOMEM);
91447636
A
262 return (ENOMEM);
263 }
2d21ac55
A
264 bzero(np, sizeof *np);
265 np->n_hflag |= (NHINIT | NHLOCKED);
266 np->n_mount = mp;
6d2010ae 267 np->n_auth = auth;
b0d623f7
A
268 TAILQ_INIT(&np->n_opens);
269 TAILQ_INIT(&np->n_lock_owners);
270 TAILQ_INIT(&np->n_locks);
271 np->n_dlink.tqe_next = NFSNOLIST;
6d2010ae
A
272 np->n_dreturn.tqe_next = NFSNOLIST;
273 np->n_monlink.le_next = NFSNOLIST;
274
275 /* ugh... need to keep track of ".zfs" directories to workaround server bugs */
276 if ((nvap->nva_type == VDIR) && cnp && (cnp->cn_namelen == 4) &&
277 (cnp->cn_nameptr[0] == '.') && (cnp->cn_nameptr[1] == 'z') &&
278 (cnp->cn_nameptr[2] == 'f') && (cnp->cn_nameptr[3] == 's'))
279 np->n_flag |= NISDOTZFS;
280 if (dnp && (dnp->n_flag & NISDOTZFS))
281 np->n_flag |= NISDOTZFSCHILD;
2d21ac55
A
282
283 if (dnp && cnp && ((cnp->cn_namelen != 2) ||
284 (cnp->cn_nameptr[0] != '.') || (cnp->cn_nameptr[1] != '.'))) {
285 vnode_t dvp = NFSTOV(dnp);
286 if (!vnode_get(dvp)) {
287 if (!vnode_ref(dvp))
288 np->n_parent = dvp;
289 vnode_put(dvp);
290 }
291 }
55e303ae 292
91447636 293 /* setup node's file handle */
1c79356b 294 if (fhsize > NFS_SMALLFH) {
91447636 295 MALLOC_ZONE(np->n_fhp, u_char *,
1c79356b 296 fhsize, M_NFSBIGFH, M_WAITOK);
91447636
A
297 if (!np->n_fhp) {
298 lck_mtx_unlock(nfs_node_hash_mutex);
299 FREE_ZONE(np, sizeof *np, M_NFSNODE);
300 *npp = 0;
2d21ac55 301 FSDBG_BOT(263, dnp, *npp, 0x80000002, ENOMEM);
91447636
A
302 return (ENOMEM);
303 }
304 } else {
305 np->n_fhp = &np->n_fh[0];
306 }
307 bcopy(fhp, np->n_fhp, fhsize);
1c79356b 308 np->n_fhsize = fhsize;
91447636
A
309
310 /* Insert the nfsnode in the hash queue for its new file handle */
91447636 311 LIST_INSERT_HEAD(nhpp, np, n_hash);
2d21ac55
A
312 np->n_hflag |= NHHASHED;
313 FSDBG(266, 0, np, np->n_flag, np->n_hflag);
314
315 /* lock the new nfsnode */
b0d623f7
A
316 lck_mtx_init(&np->n_lock, nfs_node_lck_grp, LCK_ATTR_NULL);
317 lck_rw_init(&np->n_datalock, nfs_data_lck_grp, LCK_ATTR_NULL);
318 lck_mtx_init(&np->n_openlock, nfs_open_grp, LCK_ATTR_NULL);
319 lck_mtx_lock(&np->n_lock);
1c79356b 320
55e303ae 321 /* release lock on hash table */
91447636
A
322 lck_mtx_unlock(nfs_node_hash_mutex);
323
324 /* do initial loading of attributes */
6d2010ae
A
325 NACLINVALIDATE(np);
326 NACCESSINVALIDATE(np);
91447636
A
327 error = nfs_loadattrcache(np, nvap, xidp, 1);
328 if (error) {
2d21ac55 329 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
b0d623f7 330 nfs_node_unlock(np);
91447636
A
331 lck_mtx_lock(nfs_node_hash_mutex);
332 LIST_REMOVE(np, n_hash);
2d21ac55
A
333 np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
334 if (np->n_hflag & NHLOCKWANT) {
335 np->n_hflag &= ~NHLOCKWANT;
336 wakeup(np);
91447636
A
337 }
338 lck_mtx_unlock(nfs_node_hash_mutex);
2d21ac55
A
339 if (np->n_parent) {
340 if (!vnode_get(np->n_parent)) {
341 vnode_rele(np->n_parent);
342 vnode_put(np->n_parent);
343 }
344 np->n_parent = NULL;
345 }
b0d623f7
A
346 lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp);
347 lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp);
348 lck_mtx_destroy(&np->n_openlock, nfs_open_grp);
91447636
A
349 if (np->n_fhsize > NFS_SMALLFH)
350 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
351 FREE_ZONE(np, sizeof *np, M_NFSNODE);
352 *npp = 0;
2d21ac55 353 FSDBG_BOT(263, dnp, *npp, 0x80000003, error);
91447636
A
354 return (error);
355 }
2d21ac55 356 NFS_CHANGED_UPDATE(nfsvers, np, nvap);
91447636 357 if (nvap->nva_type == VDIR)
2d21ac55 358 NFS_CHANGED_UPDATE_NC(nfsvers, np, nvap);
1c79356b 359
55e303ae 360 /* now, attempt to get a new vnode */
2d21ac55 361 vfsp.vnfs_mp = mp;
91447636
A
362 vfsp.vnfs_vtype = nvap->nva_type;
363 vfsp.vnfs_str = "nfs";
2d21ac55 364 vfsp.vnfs_dvp = dnp ? NFSTOV(dnp) : NULL;
91447636 365 vfsp.vnfs_fsnode = np;
2d21ac55
A
366 if (nfsvers == NFS_VER4) {
367#if FIFO
368 if (nvap->nva_type == VFIFO)
369 vfsp.vnfs_vops = fifo_nfsv4nodeop_p;
370 else
371#endif /* FIFO */
372 if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
373 vfsp.vnfs_vops = spec_nfsv4nodeop_p;
374 else
375 vfsp.vnfs_vops = nfsv4_vnodeop_p;
376 } else {
377#if FIFO
378 if (nvap->nva_type == VFIFO)
379 vfsp.vnfs_vops = fifo_nfsv2nodeop_p;
380 else
381#endif /* FIFO */
382 if (nvap->nva_type == VBLK || nvap->nva_type == VCHR)
383 vfsp.vnfs_vops = spec_nfsv2nodeop_p;
384 else
385 vfsp.vnfs_vops = nfsv2_vnodeop_p;
386 }
91447636
A
387 vfsp.vnfs_markroot = (flags & NG_MARKROOT) ? 1 : 0;
388 vfsp.vnfs_marksystem = 0;
389 vfsp.vnfs_rdev = 0;
390 vfsp.vnfs_filesize = nvap->nva_size;
391 vfsp.vnfs_cnp = cnp;
2d21ac55
A
392 vfsp.vnfs_flags = VNFS_ADDFSREF;
393 if (!dnp || !cnp || !(flags & NG_MAKEENTRY))
394 vfsp.vnfs_flags |= VNFS_NOCACHE;
395
6d2010ae
A
396#if CONFIG_TRIGGERS
397 if ((nfsvers >= NFS_VER4) && (nvap->nva_type == VDIR) && (np->n_vattr.nva_flags & NFS_FFLAG_TRIGGER)) {
398 struct vnode_trigger_param vtp;
399 bzero(&vtp, sizeof(vtp));
400 bcopy(&vfsp, &vtp.vnt_params, sizeof(vfsp));
401 vtp.vnt_resolve_func = nfs_mirror_mount_trigger_resolve;
402 vtp.vnt_unresolve_func = nfs_mirror_mount_trigger_unresolve;
403 vtp.vnt_rearm_func = nfs_mirror_mount_trigger_rearm;
404 vtp.vnt_flags = VNT_AUTO_REARM;
405 error = vnode_create(VNCREATE_TRIGGER, VNCREATE_TRIGGER_SIZE, &vtp, &np->n_vnode);
406 } else
407#endif
408 {
409 error = vnode_create(VNCREATE_FLAVOR, VCREATESIZE, &vfsp, &np->n_vnode);
410 }
55e303ae 411 if (error) {
2d21ac55 412 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
b0d623f7 413 nfs_node_unlock(np);
91447636 414 lck_mtx_lock(nfs_node_hash_mutex);
55e303ae 415 LIST_REMOVE(np, n_hash);
2d21ac55
A
416 np->n_hflag &= ~(NHHASHED|NHINIT|NHLOCKED);
417 if (np->n_hflag & NHLOCKWANT) {
418 np->n_hflag &= ~NHLOCKWANT;
419 wakeup(np);
91447636
A
420 }
421 lck_mtx_unlock(nfs_node_hash_mutex);
2d21ac55
A
422 if (np->n_parent) {
423 if (!vnode_get(np->n_parent)) {
424 vnode_rele(np->n_parent);
425 vnode_put(np->n_parent);
426 }
427 np->n_parent = NULL;
428 }
b0d623f7
A
429 lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp);
430 lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp);
431 lck_mtx_destroy(&np->n_openlock, nfs_open_grp);
55e303ae 432 if (np->n_fhsize > NFS_SMALLFH)
91447636 433 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
55e303ae
A
434 FREE_ZONE(np, sizeof *np, M_NFSNODE);
435 *npp = 0;
2d21ac55 436 FSDBG_BOT(263, dnp, *npp, 0x80000004, error);
55e303ae
A
437 return (error);
438 }
2d21ac55
A
439 vp = np->n_vnode;
440 vnode_settag(vp, VT_NFS);
91447636 441 /* node is now initialized */
55e303ae 442
91447636
A
443 /* check if anyone's waiting on this node */
444 lck_mtx_lock(nfs_node_hash_mutex);
2d21ac55
A
445 np->n_hflag &= ~(NHINIT|NHLOCKED);
446 if (np->n_hflag & NHLOCKWANT) {
447 np->n_hflag &= ~NHLOCKWANT;
448 wakeup(np);
55e303ae 449 }
91447636 450 lck_mtx_unlock(nfs_node_hash_mutex);
1c79356b 451
2d21ac55
A
452 *npp = np;
453
454 FSDBG_BOT(263, dnp, vp, *npp, error);
fa4905b1 455 return (error);
1c79356b
A
456}
457
91447636 458
1c79356b 459int
2d21ac55 460nfs_vnop_inactive(ap)
91447636
A
461 struct vnop_inactive_args /* {
462 struct vnodeop_desc *a_desc;
463 vnode_t a_vp;
464 vfs_context_t a_context;
1c79356b
A
465 } */ *ap;
466{
b0d623f7
A
467 vnode_t vp = ap->a_vp;
468 vfs_context_t ctx = ap->a_context;
469 nfsnode_t np = VTONFS(ap->a_vp);
2d21ac55
A
470 struct nfs_sillyrename *nsp;
471 struct nfs_vattr nvattr;
6d2010ae 472 int unhash, attrerr, busyerror, error, inuse, busied, force;
b0d623f7 473 struct nfs_open_file *nofp;
b0d623f7
A
474 struct componentname cn;
475 struct nfsmount *nmp = NFSTONMP(np);
6d2010ae 476 mount_t mp = vnode_mount(vp);
b0d623f7
A
477
478restart:
6d2010ae 479 force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT));
b0d623f7 480 error = 0;
6d2010ae 481 inuse = (nfs_mount_state_in_use_start(nmp, NULL) == 0);
b0d623f7
A
482
483 /* There shouldn't be any open or lock state at this point */
484 lck_mtx_lock(&np->n_openlock);
6d2010ae
A
485 if (np->n_openrefcnt && !force)
486 NP(np, "nfs_vnop_inactive: still open: %d", np->n_openrefcnt);
b0d623f7
A
487 TAILQ_FOREACH(nofp, &np->n_opens, nof_link) {
488 lck_mtx_lock(&nofp->nof_lock);
489 if (nofp->nof_flags & NFS_OPEN_FILE_BUSY) {
6d2010ae
A
490 if (!force)
491 NP(np, "nfs_vnop_inactive: open file busy");
b0d623f7
A
492 busied = 0;
493 } else {
494 nofp->nof_flags |= NFS_OPEN_FILE_BUSY;
495 busied = 1;
496 }
497 lck_mtx_unlock(&nofp->nof_lock);
6d2010ae
A
498 if ((np->n_flag & NREVOKE) || (nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
499 if (busied)
500 nfs_open_file_clear_busy(nofp);
501 continue;
502 }
b0d623f7
A
503 /*
504 * If we just created the file, we already had it open in
505 * anticipation of getting a subsequent open call. If the
506 * node has gone inactive without being open, we need to
507 * clean up (close) the open done in the create.
508 */
6d2010ae 509 if ((nofp->nof_flags & NFS_OPEN_FILE_CREATE) && nofp->nof_creator && !force) {
b0d623f7
A
510 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) {
511 lck_mtx_unlock(&np->n_openlock);
512 if (busied)
513 nfs_open_file_clear_busy(nofp);
514 if (inuse)
515 nfs_mount_state_in_use_end(nmp, 0);
6d2010ae
A
516 if (!nfs4_reopen(nofp, NULL))
517 goto restart;
b0d623f7
A
518 }
519 nofp->nof_flags &= ~NFS_OPEN_FILE_CREATE;
520 lck_mtx_unlock(&np->n_openlock);
6d2010ae 521 error = nfs_close(np, nofp, NFS_OPEN_SHARE_ACCESS_BOTH, NFS_OPEN_SHARE_DENY_NONE, ctx);
b0d623f7 522 if (error) {
6d2010ae 523 NP(np, "nfs_vnop_inactive: create close error: %d", error);
b0d623f7
A
524 nofp->nof_flags |= NFS_OPEN_FILE_CREATE;
525 }
526 if (busied)
527 nfs_open_file_clear_busy(nofp);
528 if (inuse)
529 nfs_mount_state_in_use_end(nmp, error);
530 goto restart;
531 }
532 if (nofp->nof_flags & NFS_OPEN_FILE_NEEDCLOSE) {
533 /*
534 * If the file is marked as needing reopen, but this was the only
535 * open on the file, just drop the open.
536 */
537 nofp->nof_flags &= ~NFS_OPEN_FILE_NEEDCLOSE;
538 if ((nofp->nof_flags & NFS_OPEN_FILE_REOPEN) && (nofp->nof_opencnt == 1)) {
539 nofp->nof_flags &= ~NFS_OPEN_FILE_REOPEN;
540 nofp->nof_r--;
541 nofp->nof_opencnt--;
542 nofp->nof_access = 0;
6d2010ae 543 } else if (!force) {
b0d623f7
A
544 lck_mtx_unlock(&np->n_openlock);
545 if (nofp->nof_flags & NFS_OPEN_FILE_REOPEN) {
546 if (busied)
547 nfs_open_file_clear_busy(nofp);
548 if (inuse)
549 nfs_mount_state_in_use_end(nmp, 0);
6d2010ae
A
550 if (!nfs4_reopen(nofp, NULL))
551 goto restart;
b0d623f7 552 }
6d2010ae 553 error = nfs_close(np, nofp, NFS_OPEN_SHARE_ACCESS_READ, NFS_OPEN_SHARE_DENY_NONE, ctx);
b0d623f7 554 if (error) {
6d2010ae 555 NP(np, "nfs_vnop_inactive: need close error: %d", error);
b0d623f7
A
556 nofp->nof_flags |= NFS_OPEN_FILE_NEEDCLOSE;
557 }
558 if (busied)
559 nfs_open_file_clear_busy(nofp);
560 if (inuse)
561 nfs_mount_state_in_use_end(nmp, error);
562 goto restart;
563 }
564 }
6d2010ae
A
565 if (nofp->nof_opencnt && !force)
566 NP(np, "nfs_vnop_inactive: file still open: %d", nofp->nof_opencnt);
567 if (!force && (nofp->nof_access || nofp->nof_deny ||
b0d623f7
A
568 nofp->nof_mmap_access || nofp->nof_mmap_deny ||
569 nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
570 nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
6d2010ae
A
571 nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw ||
572 nofp->nof_d_r || nofp->nof_d_w || nofp->nof_d_rw ||
573 nofp->nof_d_r_dw || nofp->nof_d_w_dw || nofp->nof_d_rw_dw ||
574 nofp->nof_d_r_drw || nofp->nof_d_w_drw || nofp->nof_d_rw_drw)) {
575 NP(np, "nfs_vnop_inactive: non-zero access: %d %d %d %d # %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u",
b0d623f7
A
576 nofp->nof_access, nofp->nof_deny,
577 nofp->nof_mmap_access, nofp->nof_mmap_deny,
6d2010ae
A
578 nofp->nof_r, nofp->nof_d_r,
579 nofp->nof_w, nofp->nof_d_w,
580 nofp->nof_rw, nofp->nof_d_rw,
581 nofp->nof_r_dw, nofp->nof_d_r_dw,
582 nofp->nof_w_dw, nofp->nof_d_w_dw,
583 nofp->nof_rw_dw, nofp->nof_d_rw_dw,
584 nofp->nof_r_drw, nofp->nof_d_r_drw,
585 nofp->nof_w_drw, nofp->nof_d_w_drw,
586 nofp->nof_rw_drw, nofp->nof_d_rw_drw);
b0d623f7
A
587 }
588 if (busied)
589 nfs_open_file_clear_busy(nofp);
590 }
591 lck_mtx_unlock(&np->n_openlock);
1c79356b 592
b0d623f7
A
593 if (inuse && nfs_mount_state_in_use_end(nmp, error))
594 goto restart;
2d21ac55 595
b0d623f7 596 nfs_node_lock_force(np);
2d21ac55
A
597
598 if (vnode_vtype(vp) != VDIR) {
b0d623f7 599 nsp = np->n_sillyrename;
2d21ac55 600 np->n_sillyrename = NULL;
b0d623f7 601 } else {
2d21ac55 602 nsp = NULL;
b0d623f7 603 }
1c79356b 604
2d21ac55
A
605 FSDBG_TOP(264, vp, np, np->n_flag, nsp);
606
607 if (!nsp) {
608 /* no silly file to clean up... */
609 /* clear all flags other than these */
610 np->n_flag &= (NMODIFIED);
b0d623f7 611 nfs_node_unlock(np);
2d21ac55
A
612 FSDBG_BOT(264, vp, np, np->n_flag, 0);
613 return (0);
614 }
b0d623f7 615 nfs_node_unlock(np);
2d21ac55
A
616
617 /* Remove the silly file that was rename'd earlier */
618
619 /* flush all the buffers */
b0d623f7 620 nfs_vinvalbuf2(vp, V_SAVE, vfs_context_thread(ctx), nsp->nsr_cred, 1);
2d21ac55
A
621
622 /* try to get the latest attributes */
b0d623f7 623 attrerr = nfs_getattr(np, &nvattr, ctx, NGA_UNCACHED);
2d21ac55
A
624
625 /* Check if we should remove it from the node hash. */
626 /* Leave it if inuse or it has multiple hard links. */
627 if (vnode_isinuse(vp, 0) || (!attrerr && (nvattr.nva_nlink > 1))) {
628 unhash = 0;
629 } else {
630 unhash = 1;
631 ubc_setsize(vp, 0);
632 }
633
b0d623f7
A
634 /* mark this node and the directory busy while we do the remove */
635 busyerror = nfs_node_set_busy2(nsp->nsr_dnp, np, vfs_context_thread(ctx));
2d21ac55
A
636
637 /* lock the node while we remove the silly file */
638 lck_mtx_lock(nfs_node_hash_mutex);
639 while (np->n_hflag & NHLOCKED) {
640 np->n_hflag |= NHLOCKWANT;
641 msleep(np, nfs_node_hash_mutex, PINOD, "nfs_inactive", NULL);
642 }
643 np->n_hflag |= NHLOCKED;
644 lck_mtx_unlock(nfs_node_hash_mutex);
645
b0d623f7
A
646 /* purge the name cache to deter others from finding it */
647 bzero(&cn, sizeof(cn));
648 cn.cn_nameptr = nsp->nsr_name;
649 cn.cn_namelen = nsp->nsr_namlen;
650 nfs_name_cache_purge(nsp->nsr_dnp, np, &cn, ctx);
2d21ac55
A
651
652 FSDBG(264, np, np->n_size, np->n_vattr.nva_size, 0xf00d00f1);
653
654 /* now remove the silly file */
655 nfs_removeit(nsp);
656
657 /* clear all flags other than these */
b0d623f7 658 nfs_node_lock_force(np);
2d21ac55 659 np->n_flag &= (NMODIFIED);
b0d623f7
A
660 nfs_node_unlock(np);
661
662 if (!busyerror)
663 nfs_node_clear_busy2(nsp->nsr_dnp, np);
2d21ac55
A
664
665 if (unhash && vnode_isinuse(vp, 0)) {
666 /* vnode now inuse after silly remove? */
667 unhash = 0;
668 ubc_setsize(vp, np->n_size);
669 }
670
671 lck_mtx_lock(nfs_node_hash_mutex);
672 if (unhash) {
55e303ae
A
673 /*
674 * remove nfsnode from hash now so we can't accidentally find it
675 * again if another object gets created with the same filehandle
676 * before this vnode gets reclaimed
677 */
2d21ac55
A
678 if (np->n_hflag & NHHASHED) {
679 LIST_REMOVE(np, n_hash);
680 np->n_hflag &= ~NHHASHED;
681 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
1c79356b 682 }
2d21ac55 683 vnode_recycle(vp);
1c79356b 684 }
2d21ac55
A
685 /* unlock the node */
686 np->n_hflag &= ~NHLOCKED;
687 if (np->n_hflag & NHLOCKWANT) {
688 np->n_hflag &= ~NHLOCKWANT;
689 wakeup(np);
690 }
691 lck_mtx_unlock(nfs_node_hash_mutex);
692
693 /* cleanup sillyrename info */
694 if (nsp->nsr_cred != NOCRED)
695 kauth_cred_unref(&nsp->nsr_cred);
696 vnode_rele(NFSTOV(nsp->nsr_dnp));
697 FREE_ZONE(nsp, sizeof(*nsp), M_NFSREQ);
698
699 FSDBG_BOT(264, vp, np, np->n_flag, 0);
1c79356b
A
700 return (0);
701}
702
703/*
704 * Reclaim an nfsnode so that it can be used for other purposes.
705 */
706int
2d21ac55 707nfs_vnop_reclaim(ap)
91447636
A
708 struct vnop_reclaim_args /* {
709 struct vnodeop_desc *a_desc;
710 vnode_t a_vp;
711 vfs_context_t a_context;
1c79356b
A
712 } */ *ap;
713{
91447636 714 vnode_t vp = ap->a_vp;
2d21ac55 715 nfsnode_t np = VTONFS(vp);
b0d623f7
A
716 vfs_context_t ctx = ap->a_context;
717 struct nfs_open_file *nofp, *nextnofp;
718 struct nfs_file_lock *nflp, *nextnflp;
719 struct nfs_lock_owner *nlop, *nextnlop;
b0d623f7 720 struct nfsmount *nmp = np->n_mount ? VFSTONFS(np->n_mount) : NFSTONMP(np);
6d2010ae
A
721 mount_t mp = vnode_mount(vp);
722 int force;
1c79356b 723
2d21ac55 724 FSDBG_TOP(265, vp, np, np->n_flag, 0);
6d2010ae 725 force = (!mp || (mp->mnt_kern_flag & MNTK_FRCUNMOUNT));
2d21ac55 726
b0d623f7
A
727 /* There shouldn't be any open or lock state at this point */
728 lck_mtx_lock(&np->n_openlock);
729
730 if (nmp && (nmp->nm_vers >= NFS_VER4)) {
731 /* need to drop a delegation */
6d2010ae
A
732 if (np->n_dreturn.tqe_next != NFSNOLIST) {
733 /* remove this node from the delegation return list */
734 lck_mtx_lock(&nmp->nm_lock);
735 if (np->n_dreturn.tqe_next != NFSNOLIST) {
736 TAILQ_REMOVE(&nmp->nm_dreturnq, np, n_dreturn);
737 np->n_dreturn.tqe_next = NFSNOLIST;
738 }
739 lck_mtx_unlock(&nmp->nm_lock);
740 }
b0d623f7 741 if (np->n_dlink.tqe_next != NFSNOLIST) {
6d2010ae 742 /* remove this node from the delegation list */
b0d623f7
A
743 lck_mtx_lock(&nmp->nm_lock);
744 if (np->n_dlink.tqe_next != NFSNOLIST) {
6d2010ae 745 TAILQ_REMOVE(&nmp->nm_delegations, np, n_dlink);
b0d623f7
A
746 np->n_dlink.tqe_next = NFSNOLIST;
747 }
748 lck_mtx_unlock(&nmp->nm_lock);
749 }
6d2010ae
A
750 if ((np->n_openflags & N_DELEG_MASK) && !force) {
751 /* try to return the delegation */
b0d623f7
A
752 np->n_openflags &= ~N_DELEG_MASK;
753 nfs4_delegreturn_rpc(nmp, np->n_fhp, np->n_fhsize, &np->n_dstateid,
6d2010ae
A
754 R_RECOVER, vfs_context_thread(ctx), vfs_context_ucred(ctx));
755 }
756 if (np->n_attrdirfh) {
757 FREE(np->n_attrdirfh, M_TEMP);
758 np->n_attrdirfh = NULL;
b0d623f7
A
759 }
760 }
761
762 /* clean up file locks */
763 TAILQ_FOREACH_SAFE(nflp, &np->n_locks, nfl_link, nextnflp) {
6d2010ae
A
764 if (!(nflp->nfl_flags & NFS_FILE_LOCK_DEAD) && !force) {
765 NP(np, "nfs_vnop_reclaim: lock 0x%llx 0x%llx 0x%x (bc %d)",
766 nflp->nfl_start, nflp->nfl_end, nflp->nfl_flags, nflp->nfl_blockcnt);
b0d623f7 767 }
6d2010ae
A
768 if (!(nflp->nfl_flags & (NFS_FILE_LOCK_BLOCKED|NFS_FILE_LOCK_DEAD))) {
769 /* try sending an unlock RPC if it wasn't delegated */
770 if (!(nflp->nfl_flags & NFS_FILE_LOCK_DELEGATED) && !force)
771 nmp->nm_funcs->nf_unlock_rpc(np, nflp->nfl_owner, F_WRLCK, nflp->nfl_start, nflp->nfl_end, R_RECOVER,
772 NULL, nflp->nfl_owner->nlo_open_owner->noo_cred);
b0d623f7
A
773 lck_mtx_lock(&nflp->nfl_owner->nlo_lock);
774 TAILQ_REMOVE(&nflp->nfl_owner->nlo_locks, nflp, nfl_lolink);
775 lck_mtx_unlock(&nflp->nfl_owner->nlo_lock);
776 }
777 TAILQ_REMOVE(&np->n_locks, nflp, nfl_link);
778 nfs_file_lock_destroy(nflp);
779 }
780 /* clean up lock owners */
781 TAILQ_FOREACH_SAFE(nlop, &np->n_lock_owners, nlo_link, nextnlop) {
6d2010ae
A
782 if (!TAILQ_EMPTY(&nlop->nlo_locks) && !force)
783 NP(np, "nfs_vnop_reclaim: lock owner with locks");
b0d623f7
A
784 TAILQ_REMOVE(&np->n_lock_owners, nlop, nlo_link);
785 nfs_lock_owner_destroy(nlop);
786 }
787 /* clean up open state */
6d2010ae
A
788 if (np->n_openrefcnt && !force)
789 NP(np, "nfs_vnop_reclaim: still open: %d", np->n_openrefcnt);
b0d623f7 790 TAILQ_FOREACH_SAFE(nofp, &np->n_opens, nof_link, nextnofp) {
6d2010ae
A
791 if (nofp->nof_flags & NFS_OPEN_FILE_BUSY)
792 NP(np, "nfs_vnop_reclaim: open file busy");
793 if (!(np->n_flag & NREVOKE) && !(nofp->nof_flags & NFS_OPEN_FILE_LOST)) {
794 if (nofp->nof_opencnt && !force)
795 NP(np, "nfs_vnop_reclaim: file still open: %d", nofp->nof_opencnt);
796 if (!force && (nofp->nof_access || nofp->nof_deny ||
797 nofp->nof_mmap_access || nofp->nof_mmap_deny ||
798 nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
799 nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
800 nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw ||
801 nofp->nof_d_r || nofp->nof_d_w || nofp->nof_d_rw ||
802 nofp->nof_d_r_dw || nofp->nof_d_w_dw || nofp->nof_d_rw_dw ||
803 nofp->nof_d_r_drw || nofp->nof_d_w_drw || nofp->nof_d_rw_drw)) {
804 NP(np, "nfs_vnop_reclaim: non-zero access: %d %d %d %d # %u.%u %u.%u %u.%u dw %u.%u %u.%u %u.%u drw %u.%u %u.%u %u.%u",
805 nofp->nof_access, nofp->nof_deny,
806 nofp->nof_mmap_access, nofp->nof_mmap_deny,
807 nofp->nof_r, nofp->nof_d_r,
808 nofp->nof_w, nofp->nof_d_w,
809 nofp->nof_rw, nofp->nof_d_rw,
810 nofp->nof_r_dw, nofp->nof_d_r_dw,
811 nofp->nof_w_dw, nofp->nof_d_w_dw,
812 nofp->nof_rw_dw, nofp->nof_d_rw_dw,
813 nofp->nof_r_drw, nofp->nof_d_r_drw,
814 nofp->nof_w_drw, nofp->nof_d_w_drw,
815 nofp->nof_rw_drw, nofp->nof_d_rw_drw);
816 /* try sending a close RPC if it wasn't delegated */
817 if (nofp->nof_r || nofp->nof_w || nofp->nof_rw ||
818 nofp->nof_r_dw || nofp->nof_w_dw || nofp->nof_rw_dw ||
819 nofp->nof_r_drw || nofp->nof_w_drw || nofp->nof_rw_drw)
820 nfs4_close_rpc(np, nofp, NULL, nofp->nof_owner->noo_cred, R_RECOVER);
821 }
b0d623f7
A
822 }
823 TAILQ_REMOVE(&np->n_opens, nofp, nof_link);
824 nfs_open_file_destroy(nofp);
825 }
826 lck_mtx_unlock(&np->n_openlock);
827
6d2010ae
A
828 if (np->n_monlink.le_next != NFSNOLIST) {
829 /* Wait for any in-progress getattr to complete, */
830 /* then remove this node from the monitored node list. */
831 lck_mtx_lock(&nmp->nm_lock);
832 while (np->n_mflag & NMMONSCANINPROG) {
833 struct timespec ts = { 1, 0 };
834 np->n_mflag |= NMMONSCANWANT;
835 msleep(&np->n_mflag, &nmp->nm_lock, PZERO-1, "nfswaitmonscan", &ts);
836 }
837 if (np->n_monlink.le_next != NFSNOLIST) {
838 LIST_REMOVE(np, n_monlink);
839 np->n_monlink.le_next = NFSNOLIST;
840 }
841 lck_mtx_unlock(&nmp->nm_lock);
b0d623f7 842 }
6d2010ae
A
843
844 lck_mtx_lock(nfs_buf_mutex);
845 if (!force && (!LIST_EMPTY(&np->n_dirtyblkhd) || !LIST_EMPTY(&np->n_cleanblkhd)))
846 NP(np, "nfs_reclaim: dropping %s buffers", (!LIST_EMPTY(&np->n_dirtyblkhd) ? "dirty" : "clean"));
b0d623f7 847 lck_mtx_unlock(nfs_buf_mutex);
b0d623f7
A
848 nfs_vinvalbuf(vp, V_IGNORE_WRITEERR, ap->a_context, 0);
849
2d21ac55
A
850 lck_mtx_lock(nfs_node_hash_mutex);
851
b0d623f7 852 if ((vnode_vtype(vp) != VDIR) && np->n_sillyrename) {
6d2010ae
A
853 if (!force)
854 NP(np, "nfs_reclaim: leaving unlinked file %s", np->n_sillyrename->nsr_name);
b0d623f7
A
855 if (np->n_sillyrename->nsr_cred != NOCRED)
856 kauth_cred_unref(&np->n_sillyrename->nsr_cred);
857 vnode_rele(NFSTOV(np->n_sillyrename->nsr_dnp));
858 FREE_ZONE(np->n_sillyrename, sizeof(*np->n_sillyrename), M_NFSREQ);
859 }
2d21ac55 860
91447636 861 vnode_removefsref(vp);
1c79356b 862
2d21ac55 863 if (np->n_hflag & NHHASHED) {
55e303ae 864 LIST_REMOVE(np, n_hash);
2d21ac55
A
865 np->n_hflag &= ~NHHASHED;
866 FSDBG(266, 0, np, np->n_flag, 0xb1eb1e);
1c79356b 867 }
2d21ac55 868 lck_mtx_unlock(nfs_node_hash_mutex);
1c79356b
A
869
870 /*
b0d623f7
A
871 * Free up any directory cookie structures and large file handle
872 * structures that might be associated with this nfs node.
1c79356b 873 */
b0d623f7
A
874 nfs_node_lock_force(np);
875 if ((vnode_vtype(vp) == VDIR) && np->n_cookiecache)
876 FREE_ZONE(np->n_cookiecache, sizeof(struct nfsdmap), M_NFSDIROFF);
877 if (np->n_fhsize > NFS_SMALLFH)
91447636 878 FREE_ZONE(np->n_fhp, np->n_fhsize, M_NFSBIGFH);
6d2010ae
A
879 if (np->n_vattr.nva_acl)
880 kauth_acl_free(np->n_vattr.nva_acl);
b0d623f7 881 nfs_node_unlock(np);
91447636 882 vnode_clearfsnode(vp);
1c79356b 883
2d21ac55
A
884 if (np->n_parent) {
885 if (!vnode_get(np->n_parent)) {
886 vnode_rele(np->n_parent);
887 vnode_put(np->n_parent);
888 }
889 np->n_parent = NULL;
890 }
891
b0d623f7
A
892 lck_mtx_destroy(&np->n_lock, nfs_node_lck_grp);
893 lck_rw_destroy(&np->n_datalock, nfs_data_lck_grp);
894 lck_mtx_destroy(&np->n_openlock, nfs_open_grp);
2d21ac55
A
895
896 FSDBG_BOT(265, vp, np, np->n_flag, 0xd1ed1e);
91447636 897 FREE_ZONE(np, sizeof(struct nfsnode), M_NFSNODE);
1c79356b
A
898 return (0);
899}
900
2d21ac55
A
901/*
902 * Acquire an NFS node lock
903 */
b0d623f7 904
2d21ac55 905int
b0d623f7 906nfs_node_lock_internal(nfsnode_t np, int force)
2d21ac55 907{
b0d623f7
A
908 FSDBG_TOP(268, np, force, 0, 0);
909 lck_mtx_lock(&np->n_lock);
910 if (!force && !(np->n_hflag && NHHASHED)) {
911 FSDBG_BOT(268, np, 0xdead, 0, 0);
912 lck_mtx_unlock(&np->n_lock);
2d21ac55
A
913 return (ENOENT);
914 }
b0d623f7 915 FSDBG_BOT(268, np, force, 0, 0);
2d21ac55
A
916 return (0);
917}
918
b0d623f7
A
919int
920nfs_node_lock(nfsnode_t np)
921{
922 return nfs_node_lock_internal(np, 0);
923}
924
925void
926nfs_node_lock_force(nfsnode_t np)
927{
928 nfs_node_lock_internal(np, 1);
929}
930
2d21ac55
A
931/*
932 * Release an NFS node lock
933 */
934void
b0d623f7 935nfs_node_unlock(nfsnode_t np)
2d21ac55 936{
b0d623f7
A
937 FSDBG(269, np, current_thread(), 0, 0);
938 lck_mtx_unlock(&np->n_lock);
2d21ac55
A
939}
940
941/*
942 * Acquire 2 NFS node locks
b0d623f7 943 * - locks taken in reverse address order
2d21ac55
A
944 * - both or neither of the locks are taken
945 * - only one lock taken per node (dup nodes are skipped)
946 */
947int
b0d623f7 948nfs_node_lock2(nfsnode_t np1, nfsnode_t np2)
2d21ac55 949{
b0d623f7 950 nfsnode_t first, second;
2d21ac55
A
951 int error;
952
b0d623f7
A
953 first = (np1 > np2) ? np1 : np2;
954 second = (np1 > np2) ? np2 : np1;
955 if ((error = nfs_node_lock(first)))
2d21ac55
A
956 return (error);
957 if (np1 == np2)
958 return (error);
b0d623f7
A
959 if ((error = nfs_node_lock(second)))
960 nfs_node_unlock(first);
2d21ac55
A
961 return (error);
962}
963
2d21ac55 964void
b0d623f7 965nfs_node_unlock2(nfsnode_t np1, nfsnode_t np2)
2d21ac55 966{
b0d623f7 967 nfs_node_unlock(np1);
2d21ac55 968 if (np1 != np2)
b0d623f7 969 nfs_node_unlock(np2);
2d21ac55
A
970}
971
972/*
b0d623f7
A
973 * Manage NFS node busy state.
974 * (Similar to NFS node locks above)
2d21ac55
A
975 */
976int
b0d623f7 977nfs_node_set_busy(nfsnode_t np, thread_t thd)
2d21ac55 978{
b0d623f7
A
979 struct timespec ts = { 2, 0 };
980 int error;
981
982 if ((error = nfs_node_lock(np)))
983 return (error);
984 while (ISSET(np->n_flag, NBUSY)) {
985 SET(np->n_flag, NBUSYWANT);
986 msleep(np, &np->n_lock, PZERO-1, "nfsbusywant", &ts);
987 if ((error = nfs_sigintr(NFSTONMP(np), NULL, thd, 0)))
988 break;
2d21ac55 989 }
b0d623f7
A
990 if (!error)
991 SET(np->n_flag, NBUSY);
992 nfs_node_unlock(np);
993 return (error);
994}
2d21ac55 995
b0d623f7
A
996void
997nfs_node_clear_busy(nfsnode_t np)
998{
999 int wanted;
1000
1001 nfs_node_lock_force(np);
1002 wanted = ISSET(np->n_flag, NBUSYWANT);
1003 CLR(np->n_flag, NBUSY|NBUSYWANT);
1004 nfs_node_unlock(np);
1005 if (wanted)
1006 wakeup(np);
1007}
1008
1009int
1010nfs_node_set_busy2(nfsnode_t np1, nfsnode_t np2, thread_t thd)
1011{
1012 nfsnode_t first, second;
1013 int error;
1014
1015 first = (np1 > np2) ? np1 : np2;
1016 second = (np1 > np2) ? np2 : np1;
1017 if ((error = nfs_node_set_busy(first, thd)))
1018 return (error);
1019 if (np1 == np2)
1020 return (error);
1021 if ((error = nfs_node_set_busy(second, thd)))
1022 nfs_node_clear_busy(first);
1023 return (error);
1024}
1025
1026void
1027nfs_node_clear_busy2(nfsnode_t np1, nfsnode_t np2)
1028{
1029 nfs_node_clear_busy(np1);
1030 if (np1 != np2)
1031 nfs_node_clear_busy(np2);
1032}
1033
1034/* helper function to sort four nodes in reverse address order (no dupes) */
1035static void
1036nfs_node_sort4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4, nfsnode_t *list, int *lcntp)
1037{
1038 nfsnode_t na[2], nb[2];
1039 int a, b, i, lcnt;
1040
1041 /* sort pairs then merge */
1042 na[0] = (np1 > np2) ? np1 : np2;
1043 na[1] = (np1 > np2) ? np2 : np1;
1044 nb[0] = (np3 > np4) ? np3 : np4;
1045 nb[1] = (np3 > np4) ? np4 : np3;
1046 for (a = b = i = lcnt = 0; i < 4; i++) {
1047 if (a >= 2)
1048 list[lcnt] = nb[b++];
1049 else if ((b >= 2) || (na[a] >= nb[b]))
1050 list[lcnt] = na[a++];
1051 else
1052 list[lcnt] = nb[b++];
1053 if ((lcnt <= 0) || (list[lcnt] != list[lcnt-1]))
1054 lcnt++; /* omit dups */
2d21ac55 1055 }
b0d623f7
A
1056 if (list[lcnt-1] == NULL)
1057 lcnt--;
1058 *lcntp = lcnt;
1059}
1060
1061int
1062nfs_node_set_busy4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4, thread_t thd)
1063{
1064 nfsnode_t list[4];
1065 int i, lcnt, error;
1066
1067 nfs_node_sort4(np1, np2, np3, np4, list, &lcnt);
2d21ac55
A
1068
1069 /* Now we can lock using list[0 - lcnt-1] */
b0d623f7
A
1070 for (i = 0; i < lcnt; ++i)
1071 if ((error = nfs_node_set_busy(list[i], thd))) {
1072 /* Drop any locks we acquired. */
1073 while (--i >= 0)
1074 nfs_node_clear_busy(list[i]);
1075 return (error);
1076 }
2d21ac55
A
1077 return (0);
1078}
1079
2d21ac55 1080void
b0d623f7 1081nfs_node_clear_busy4(nfsnode_t np1, nfsnode_t np2, nfsnode_t np3, nfsnode_t np4)
2d21ac55
A
1082{
1083 nfsnode_t list[4];
b0d623f7
A
1084 int lcnt;
1085
1086 nfs_node_sort4(np1, np2, np3, np4, list, &lcnt);
1087 while (--lcnt >= 0)
1088 nfs_node_clear_busy(list[lcnt]);
2d21ac55
A
1089}
1090
1091/*
1092 * Acquire an NFS node data lock
1093 */
1094void
1095nfs_data_lock(nfsnode_t np, int locktype)
1096{
b0d623f7
A
1097 nfs_data_lock_internal(np, locktype, 1);
1098}
1099void
1100nfs_data_lock_noupdate(nfsnode_t np, int locktype)
1101{
1102 nfs_data_lock_internal(np, locktype, 0);
2d21ac55
A
1103}
1104void
b0d623f7 1105nfs_data_lock_internal(nfsnode_t np, int locktype, int updatesize)
2d21ac55
A
1106{
1107 FSDBG_TOP(270, np, locktype, np->n_datalockowner, 0);
b0d623f7 1108 if (locktype == NFS_DATA_LOCK_SHARED) {
2d21ac55
A
1109 if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
1110 nfs_data_update_size(np, 0);
1111 lck_rw_lock_shared(&np->n_datalock);
1112 } else {
1113 lck_rw_lock_exclusive(&np->n_datalock);
1114 np->n_datalockowner = current_thread();
1115 if (updatesize && ISSET(np->n_flag, NUPDATESIZE))
1116 nfs_data_update_size(np, 1);
1117 }
1118 FSDBG_BOT(270, np, locktype, np->n_datalockowner, 0);
1119}
1120
1121/*
1122 * Release an NFS node data lock
1123 */
1124void
1125nfs_data_unlock(nfsnode_t np)
1126{
b0d623f7
A
1127 nfs_data_unlock_internal(np, 1);
1128}
1129void
1130nfs_data_unlock_noupdate(nfsnode_t np)
1131{
1132 nfs_data_unlock_internal(np, 0);
2d21ac55
A
1133}
1134void
b0d623f7 1135nfs_data_unlock_internal(nfsnode_t np, int updatesize)
2d21ac55
A
1136{
1137 int mine = (np->n_datalockowner == current_thread());
1138 FSDBG_TOP(271, np, np->n_datalockowner, current_thread(), 0);
1139 if (updatesize && mine && ISSET(np->n_flag, NUPDATESIZE))
1140 nfs_data_update_size(np, 1);
1141 np->n_datalockowner = NULL;
1142 lck_rw_done(&np->n_datalock);
1143 if (updatesize && !mine && ISSET(np->n_flag, NUPDATESIZE))
1144 nfs_data_update_size(np, 0);
1145 FSDBG_BOT(271, np, np->n_datalockowner, current_thread(), 0);
1146}
1147
1148
1149/*
1150 * update an NFS node's size
1151 */
1152void
1153nfs_data_update_size(nfsnode_t np, int datalocked)
1154{
1155 int error;
1156
1157 FSDBG_TOP(272, np, np->n_flag, np->n_size, np->n_newsize);
1158 if (!datalocked) {
b0d623f7 1159 nfs_data_lock(np, NFS_DATA_LOCK_EXCLUSIVE);
2d21ac55
A
1160 /* grabbing data lock will automatically update size */
1161 nfs_data_unlock(np);
1162 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
1163 return;
1164 }
b0d623f7 1165 error = nfs_node_lock(np);
2d21ac55
A
1166 if (error || !ISSET(np->n_flag, NUPDATESIZE)) {
1167 if (!error)
b0d623f7 1168 nfs_node_unlock(np);
2d21ac55
A
1169 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
1170 return;
1171 }
1172 CLR(np->n_flag, NUPDATESIZE);
1173 np->n_size = np->n_newsize;
1174 /* make sure we invalidate buffers the next chance we get */
1175 SET(np->n_flag, NNEEDINVALIDATE);
b0d623f7 1176 nfs_node_unlock(np);
2d21ac55
A
1177 ubc_setsize(NFSTOV(np), (off_t)np->n_size); /* XXX error? */
1178 FSDBG_BOT(272, np, np->n_flag, np->n_size, np->n_newsize);
1179}
1180
316670eb
A
1181#define DODEBUG 1
1182int
1183nfs_mount_is_dirty(mount_t mp)
1184{
1185 u_long i;
1186 nfsnode_t np;
1187#ifdef DODEBUG
1188 struct timeval now, then, diff;
1189 u_long ncnt = 0;
1190 microuptime(&now);
1191#endif
1192 lck_mtx_lock(nfs_node_hash_mutex);
1193 for (i = 0; i <= nfsnodehash; i++) {
1194 LIST_FOREACH(np, &nfsnodehashtbl[i], n_hash) {
1195#ifdef DODEBUG
1196 ncnt++;
1197#endif
1198 if (np->n_mount == mp && !LIST_EMPTY(&np->n_dirtyblkhd))
1199 goto out;
1200 }
1201 }
1202out:
1203 lck_mtx_unlock(nfs_node_hash_mutex);
1204#ifdef DODEBUG
1205 microuptime(&then);
1206 timersub(&then, &now, &diff);
1207
1208 printf("nfs_mount_is_dirty took %lld mics for %ld slots and %ld nodes return %d\n",
1209 (uint64_t)diff.tv_sec * 1000000LL + diff.tv_usec, i, ncnt, (i <= nfsnodehash));
1210#endif
1211
1212 return (i <= nfsnodehash);
1213}