]> git.saurik.com Git - apple/xnu.git/blame - bsd/nfs/nfs_bio.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / bsd / nfs / nfs_bio.c
CommitLineData
1c79356b 1/*
cb323159 2 * Copyright (c) 2000-2019 Apple Inc. All rights reserved.
5d5c5d0d 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 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.
0a7de745 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.
0a7de745 17 *
2d21ac55
A
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.
0a7de745 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_bio.c 8.9 (Berkeley) 3/30/95
65 * FreeBSD-Id: nfs_bio.c,v 1.44 1997/09/10 19:52:25 phk Exp $
66 */
ea3f0419
A
67
68#include <nfs/nfs_conf.h>
69#if CONFIG_NFS_CLIENT
70
1c79356b
A
71#include <sys/param.h>
72#include <sys/systm.h>
73#include <sys/resourcevar.h>
74#include <sys/signalvar.h>
91447636
A
75#include <sys/proc_internal.h>
76#include <sys/kauth.h>
55e303ae 77#include <sys/malloc.h>
1c79356b 78#include <sys/vnode.h>
55e303ae 79#include <sys/dirent.h>
91447636 80#include <sys/mount_internal.h>
1c79356b 81#include <sys/kernel.h>
91447636
A
82#include <sys/ubc_internal.h>
83#include <sys/uio_internal.h>
6d2010ae 84#include <sys/kpi_mbuf.h>
1c79356b
A
85
86#include <sys/vm.h>
87#include <sys/vmparam.h>
88
89#include <sys/time.h>
90#include <kern/clock.h>
91447636
A
91#include <libkern/OSAtomic.h>
92#include <kern/kalloc.h>
2d21ac55 93#include <kern/thread_call.h>
1c79356b
A
94
95#include <nfs/rpcv2.h>
96#include <nfs/nfsproto.h>
97#include <nfs/nfs.h>
2d21ac55 98#include <nfs/nfs_gss.h>
1c79356b 99#include <nfs/nfsmount.h>
1c79356b 100#include <nfs/nfsnode.h>
91447636 101#include <sys/buf_internal.h>
2d21ac55 102#include <libkern/OSAtomic.h>
cb323159 103#include <os/refcnt.h>
1c79356b 104
39037602
A
105#define NFS_BIO_DBG(...) NFS_DBG(NFS_FAC_BIO, 7, ## __VA_ARGS__)
106
0a7de745 107kern_return_t thread_terminate(thread_t); /* XXX */
55e303ae 108
0a7de745 109#define NFSBUFHASH(np, lbn) \
91447636 110 (&nfsbufhashtbl[((long)(np) / sizeof(*(np)) + (int)(lbn)) & nfsbufhash])
0a7de745 111LIST_HEAD(nfsbufhashhead, nfsbuf) * nfsbufhashtbl;
483a1d10 112struct nfsbuffreehead nfsbuffree, nfsbuffreemeta, nfsbufdelwri;
55e303ae 113u_long nfsbufhash;
91447636 114int nfsbufcnt, nfsbufmin, nfsbufmax, nfsbufmetacnt, nfsbufmetamax;
483a1d10 115int nfsbuffreecnt, nfsbuffreemetacnt, nfsbufdelwricnt, nfsneedbuffer;
55e303ae 116int nfs_nbdwrite;
2d21ac55
A
117int nfs_buf_timer_on = 0;
118thread_t nfsbufdelwrithd = NULL;
483a1d10 119
c3c9b80d 120static ZONE_DECLARE(nfsbuf_zone, "NFS bio", sizeof(struct nfsbuf), ZC_NONE);
f427ee49 121
c3c9b80d
A
122static LCK_GRP_DECLARE(nfs_buf_lck_grp, "nfs buf");
123LCK_MTX_DECLARE(nfs_buf_mutex, &nfs_buf_lck_grp);
91447636 124
0a7de745
A
125#define NFSBUF_FREE_PERIOD 30 /* seconds */
126#define NFSBUF_LRU_STALE 120
127#define NFSBUF_META_STALE 240
483a1d10
A
128
129/* number of nfsbufs nfs_buf_freeup() should attempt to free from nfsbuffree list */
0a7de745 130#define LRU_TO_FREEUP 6
483a1d10 131/* number of nfsbufs nfs_buf_freeup() should attempt to free from nfsbuffreemeta list */
0a7de745 132#define META_TO_FREEUP 3
483a1d10 133/* total number of nfsbufs nfs_buf_freeup() should attempt to free */
0a7de745 134#define TOTAL_TO_FREEUP (LRU_TO_FREEUP+META_TO_FREEUP)
2d21ac55 135/* fraction of nfsbufs nfs_buf_freeup() should attempt to free from nfsbuffree list when called from timer */
0a7de745 136#define LRU_FREEUP_FRAC_ON_TIMER 8
2d21ac55 137/* fraction of nfsbufs nfs_buf_freeup() should attempt to free from nfsbuffreemeta list when called from timer */
0a7de745 138#define META_FREEUP_FRAC_ON_TIMER 16
483a1d10 139/* fraction of total nfsbufs that nfsbuffreecnt should exceed before bothering to call nfs_buf_freeup() */
0a7de745 140#define LRU_FREEUP_MIN_FRAC 4
483a1d10 141/* fraction of total nfsbufs that nfsbuffreemetacnt should exceed before bothering to call nfs_buf_freeup() */
0a7de745 142#define META_FREEUP_MIN_FRAC 2
55e303ae 143
f427ee49
A
144#define NFS_ROUND_BLOCK(p, blksize) ((((uint64_t)(p) + blksize - 1) & ~((uint64_t)blksize - 1)) / blksize)
145
483a1d10 146#define NFS_BUF_FREEUP() \
91447636 147 do { \
0a7de745
A
148 /* only call nfs_buf_freeup() if it has work to do: */ \
149 if (((nfsbuffreecnt > nfsbufcnt/LRU_FREEUP_MIN_FRAC) || \
150 (nfsbuffreemetacnt > nfsbufcnt/META_FREEUP_MIN_FRAC)) && \
151 ((nfsbufcnt - TOTAL_TO_FREEUP) > nfsbufmin)) \
152 nfs_buf_freeup(0); \
483a1d10 153 } while (0)
55e303ae 154
f427ee49
A
155void
156nfs_buf_pgs_get_page_mask(nfsbufpgs *nfsbp, off_t page)
157{
158 off_t page_pos = page / NBPGS_ELEMENT_PAGES;
159 off_t max_page = NBPGS_STRUCT_SIZE * 8;
160 NBPGS_ERASE(nfsbp);
161
162 if (page >= max_page) {
163 nfs_buf_pgs_bit_not(nfsbp);
164 return;
165 }
166
167 NBPGS_SET(nfsbp, page);
168 nfsbp->pages[page_pos]--;
169 for (off_t i = page_pos - 1; i >= 0; i--) {
170 nfsbp->pages[i] = ~0;
171 }
172}
173
174void
175nfs_buf_pgs_bit_not(nfsbufpgs *nfsbp)
176{
177 for (uint32_t i = 0; i < NBPGS_ELEMENTS; i++) {
178 nfsbp->pages[i] = ~nfsbp->pages[i];
179 }
180}
181
182void
183nfs_buf_pgs_bit_and(nfsbufpgs *nfsbp_src1, nfsbufpgs *nfsbp_src2, nfsbufpgs *nfsbp_dst)
184{
185 for (uint32_t i = 0; i < NBPGS_ELEMENTS; i++) {
186 nfsbp_dst->pages[i] = nfsbp_src1->pages[i] & nfsbp_src2->pages[i];
187 }
188}
189
190void
191nfs_buf_pgs_set_pages_between(nfsbufpgs *nfsbp, off_t firstpg, off_t lastpg)
192{
193 nfsbufpgs pagemaskfirst, pagemasklast;
194
195 nfs_buf_pgs_get_page_mask(&pagemasklast, lastpg);
196 nfs_buf_pgs_get_page_mask(&pagemaskfirst, firstpg);
197 nfs_buf_pgs_bit_not(&pagemaskfirst);
198 nfs_buf_pgs_bit_and(&pagemaskfirst, &pagemasklast, nfsbp);
199}
200
201int
202nfs_buf_pgs_is_set(nfsbufpgs *nfsbp)
203{
204 for (uint32_t i = 0; i < NBPGS_ELEMENTS; i++) {
205 if (nfsbp->pages[i] != 0) {
206 return 1;
207 }
208 }
209 return 0;
210}
211
55e303ae
A
212/*
213 * Initialize nfsbuf lists
214 */
215void
216nfs_nbinit(void)
217{
91447636 218 nfsbufcnt = nfsbufmetacnt =
0a7de745 219 nfsbuffreecnt = nfsbuffreemetacnt = nfsbufdelwricnt = 0;
91447636 220 nfsbufmin = 128;
2d21ac55 221 /* size nfsbufmax to cover at most half sane_size (w/default buf size) */
f427ee49 222 nfsbufmax = (int)(sane_size >> PAGE_SHIFT) / (2 * (NFS_RWSIZE >> PAGE_SHIFT));
2d21ac55 223 nfsbufmetamax = nfsbufmax / 4;
55e303ae
A
224 nfsneedbuffer = 0;
225 nfs_nbdwrite = 0;
91447636 226
f427ee49 227 nfsbufhashtbl = hashinit(nfsbufmax / 4, M_NFSBIO, &nfsbufhash);
91447636
A
228 TAILQ_INIT(&nfsbuffree);
229 TAILQ_INIT(&nfsbuffreemeta);
230 TAILQ_INIT(&nfsbufdelwri);
55e303ae
A
231}
232
2d21ac55
A
233/*
234 * Check periodically for stale/unused nfs bufs
235 */
236void
237nfs_buf_timer(__unused void *param0, __unused void *param1)
238{
239 nfs_buf_freeup(1);
240
c3c9b80d 241 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
242 if (nfsbufcnt <= nfsbufmin) {
243 nfs_buf_timer_on = 0;
c3c9b80d 244 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
245 return;
246 }
c3c9b80d 247 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
248
249 nfs_interval_timer_start(nfs_buf_timer_call,
0a7de745 250 NFSBUF_FREE_PERIOD * 1000);
2d21ac55
A
251}
252
55e303ae
A
253/*
254 * try to free up some excess, unused nfsbufs
255 */
483a1d10
A
256void
257nfs_buf_freeup(int timer)
55e303ae
A
258{
259 struct nfsbuf *fbp;
483a1d10
A
260 struct timeval now;
261 int count;
91447636
A
262 struct nfsbuffreehead nfsbuffreeup;
263
264 TAILQ_INIT(&nfsbuffreeup);
265
c3c9b80d 266 lck_mtx_lock(&nfs_buf_mutex);
55e303ae 267
483a1d10 268 microuptime(&now);
55e303ae 269
91447636
A
270 FSDBG(320, nfsbufcnt, nfsbuffreecnt, nfsbuffreemetacnt, 0);
271
0a7de745 272 count = timer ? nfsbuffreecnt / LRU_FREEUP_FRAC_ON_TIMER : LRU_TO_FREEUP;
483a1d10 273 while ((nfsbufcnt > nfsbufmin) && (count-- > 0)) {
55e303ae 274 fbp = TAILQ_FIRST(&nfsbuffree);
0a7de745 275 if (!fbp) {
55e303ae 276 break;
0a7de745 277 }
cb323159 278 if (os_ref_get_count(&fbp->nb_refs) > 1) {
91447636 279 break;
0a7de745 280 }
91447636 281 if (NBUFSTAMPVALID(fbp) &&
0a7de745 282 (fbp->nb_timestamp + (2 * NFSBUF_LRU_STALE)) > now.tv_sec) {
483a1d10 283 break;
0a7de745 284 }
483a1d10 285 nfs_buf_remfree(fbp);
2d21ac55
A
286 /* disassociate buffer from any nfsnode */
287 if (fbp->nb_np) {
483a1d10
A
288 if (fbp->nb_vnbufs.le_next != NFSNOLIST) {
289 LIST_REMOVE(fbp, nb_vnbufs);
290 fbp->nb_vnbufs.le_next = NFSNOLIST;
291 }
2d21ac55 292 fbp->nb_np = NULL;
483a1d10
A
293 }
294 LIST_REMOVE(fbp, nb_hash);
91447636 295 TAILQ_INSERT_TAIL(&nfsbuffreeup, fbp, nb_free);
483a1d10
A
296 nfsbufcnt--;
297 }
298
0a7de745 299 count = timer ? nfsbuffreemetacnt / META_FREEUP_FRAC_ON_TIMER : META_TO_FREEUP;
483a1d10
A
300 while ((nfsbufcnt > nfsbufmin) && (count-- > 0)) {
301 fbp = TAILQ_FIRST(&nfsbuffreemeta);
0a7de745 302 if (!fbp) {
483a1d10 303 break;
0a7de745 304 }
cb323159 305 if (os_ref_get_count(&fbp->nb_refs) > 1) {
91447636 306 break;
0a7de745 307 }
91447636 308 if (NBUFSTAMPVALID(fbp) &&
0a7de745 309 (fbp->nb_timestamp + (2 * NFSBUF_META_STALE)) > now.tv_sec) {
483a1d10 310 break;
0a7de745 311 }
55e303ae 312 nfs_buf_remfree(fbp);
2d21ac55
A
313 /* disassociate buffer from any nfsnode */
314 if (fbp->nb_np) {
55e303ae
A
315 if (fbp->nb_vnbufs.le_next != NFSNOLIST) {
316 LIST_REMOVE(fbp, nb_vnbufs);
317 fbp->nb_vnbufs.le_next = NFSNOLIST;
318 }
2d21ac55 319 fbp->nb_np = NULL;
55e303ae
A
320 }
321 LIST_REMOVE(fbp, nb_hash);
91447636
A
322 TAILQ_INSERT_TAIL(&nfsbuffreeup, fbp, nb_free);
323 nfsbufcnt--;
324 nfsbufmetacnt--;
325 }
326
327 FSDBG(320, nfsbufcnt, nfsbuffreecnt, nfsbuffreemetacnt, 0);
2d21ac55 328 NFSBUFCNTCHK();
91447636 329
c3c9b80d 330 lck_mtx_unlock(&nfs_buf_mutex);
91447636
A
331
332 while ((fbp = TAILQ_FIRST(&nfsbuffreeup))) {
333 TAILQ_REMOVE(&nfsbuffreeup, fbp, nb_free);
55e303ae 334 /* nuke any creds */
0a7de745 335 if (IS_VALID_CRED(fbp->nb_rcred)) {
0c530ab8 336 kauth_cred_unref(&fbp->nb_rcred);
0a7de745
A
337 }
338 if (IS_VALID_CRED(fbp->nb_wcred)) {
0c530ab8 339 kauth_cred_unref(&fbp->nb_wcred);
0a7de745 340 }
91447636 341 /* if buf was NB_META, dump buffer */
0a7de745 342 if (ISSET(fbp->nb_flags, NB_META) && fbp->nb_data) {
f427ee49 343 kheap_free(KHEAP_DATA_BUFFERS, fbp->nb_data, fbp->nb_bufsize);
0a7de745 344 }
f427ee49 345 NFS_ZFREE(nfsbuf_zone, fbp);
55e303ae 346 }
55e303ae
A
347}
348
91447636
A
349/*
350 * remove a buffer from the freelist
351 * (must be called with nfs_buf_mutex held)
352 */
55e303ae
A
353void
354nfs_buf_remfree(struct nfsbuf *bp)
355{
0a7de745 356 if (bp->nb_free.tqe_next == NFSNOLIST) {
55e303ae 357 panic("nfsbuf not on free list");
0a7de745 358 }
55e303ae
A
359 if (ISSET(bp->nb_flags, NB_DELWRI)) {
360 nfsbufdelwricnt--;
361 TAILQ_REMOVE(&nfsbufdelwri, bp, nb_free);
91447636 362 } else if (ISSET(bp->nb_flags, NB_META)) {
483a1d10
A
363 nfsbuffreemetacnt--;
364 TAILQ_REMOVE(&nfsbuffreemeta, bp, nb_free);
55e303ae
A
365 } else {
366 nfsbuffreecnt--;
367 TAILQ_REMOVE(&nfsbuffree, bp, nb_free);
368 }
369 bp->nb_free.tqe_next = NFSNOLIST;
2d21ac55 370 NFSBUFCNTCHK();
55e303ae
A
371}
372
373/*
374 * check for existence of nfsbuf in cache
375 */
91447636 376boolean_t
2d21ac55 377nfs_buf_is_incore(nfsnode_t np, daddr64_t blkno)
91447636
A
378{
379 boolean_t rv;
c3c9b80d 380 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 381 if (nfs_buf_incore(np, blkno)) {
91447636 382 rv = TRUE;
0a7de745 383 } else {
91447636 384 rv = FALSE;
0a7de745 385 }
c3c9b80d 386 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 387 return rv;
91447636
A
388}
389
390/*
391 * return incore buffer (must be called with nfs_buf_mutex held)
392 */
55e303ae 393struct nfsbuf *
2d21ac55 394nfs_buf_incore(nfsnode_t np, daddr64_t blkno)
55e303ae
A
395{
396 /* Search hash chain */
2d21ac55 397 struct nfsbuf * bp = NFSBUFHASH(np, blkno)->lh_first;
0a7de745 398 for (; bp != NULL; bp = bp->nb_hash.le_next) {
2d21ac55 399 if ((bp->nb_lblkno == blkno) && (bp->nb_np == np)) {
483a1d10 400 if (!ISSET(bp->nb_flags, NB_INVAL)) {
2d21ac55 401 FSDBG(547, bp, blkno, bp->nb_flags, bp->nb_np);
0a7de745 402 return bp;
483a1d10
A
403 }
404 }
0a7de745
A
405 }
406 return NULL;
55e303ae
A
407}
408
409/*
410 * Check if it's OK to drop a page.
411 *
412 * Called by vnode_pager() on pageout request of non-dirty page.
413 * We need to make sure that it's not part of a delayed write.
414 * If it is, we can't let the VM drop it because we may need it
415 * later when/if we need to write the data (again).
416 */
417int
91447636 418nfs_buf_page_inval(vnode_t vp, off_t offset)
55e303ae 419{
2d21ac55 420 struct nfsmount *nmp = VTONMP(vp);
55e303ae 421 struct nfsbuf *bp;
91447636
A
422 int error = 0;
423
0a7de745
A
424 if (nfs_mount_gone(nmp)) {
425 return ENXIO;
426 }
2d21ac55 427
c3c9b80d 428 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55 429 bp = nfs_buf_incore(VTONFS(vp), (daddr64_t)(offset / nmp->nm_biosize));
0a7de745 430 if (!bp) {
91447636 431 goto out;
0a7de745 432 }
55e303ae 433 FSDBG(325, bp, bp->nb_flags, bp->nb_dirtyoff, bp->nb_dirtyend);
91447636
A
434 if (ISSET(bp->nb_lflags, NBL_BUSY)) {
435 error = EBUSY;
436 goto out;
437 }
55e303ae
A
438 /*
439 * If there's a dirty range in the buffer, check to
440 * see if this page intersects with the dirty range.
441 * If it does, we can't let the pager drop the page.
442 */
443 if (bp->nb_dirtyend > 0) {
f427ee49 444 off_t start = offset - NBOFF(bp);
b0d623f7
A
445 if ((bp->nb_dirtyend > start) &&
446 (bp->nb_dirtyoff < (start + PAGE_SIZE))) {
447 /*
448 * Before returning the bad news, move the
449 * buffer to the start of the delwri list and
450 * give the list a push to try to flush the
451 * buffer out.
452 */
91447636 453 error = EBUSY;
b0d623f7
A
454 nfs_buf_remfree(bp);
455 TAILQ_INSERT_HEAD(&nfsbufdelwri, bp, nb_free);
456 nfsbufdelwricnt++;
457 nfs_buf_delwri_push(1);
458 }
55e303ae 459 }
91447636 460out:
c3c9b80d 461 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 462 return error;
55e303ae
A
463}
464
91447636
A
465/*
466 * set up the UPL for a buffer
467 * (must NOT be called with nfs_buf_mutex held)
468 */
55e303ae
A
469int
470nfs_buf_upl_setup(struct nfsbuf *bp)
471{
472 kern_return_t kret;
473 upl_t upl;
91447636 474 int upl_flags;
55e303ae 475
0a7de745
A
476 if (ISSET(bp->nb_flags, NB_PAGELIST)) {
477 return 0;
478 }
55e303ae 479
91447636 480 upl_flags = UPL_PRECIOUS;
2d21ac55 481 if (!ISSET(bp->nb_flags, NB_READ)) {
91447636
A
482 /*
483 * We're doing a "write", so we intend to modify
484 * the pages we're gathering.
485 */
486 upl_flags |= UPL_WILL_MODIFY;
487 }
5ba3f43e 488 kret = ubc_create_upl_kernel(NFSTOV(bp->nb_np), NBOFF(bp), bp->nb_bufsize,
0a7de745 489 &upl, NULL, upl_flags, VM_KERN_MEMORY_FILE);
55e303ae
A
490 if (kret == KERN_INVALID_ARGUMENT) {
491 /* vm object probably doesn't exist any more */
492 bp->nb_pagelist = NULL;
0a7de745 493 return EINVAL;
55e303ae
A
494 }
495 if (kret != KERN_SUCCESS) {
496 printf("nfs_buf_upl_setup(): failed to get pagelist %d\n", kret);
497 bp->nb_pagelist = NULL;
0a7de745 498 return EIO;
55e303ae
A
499 }
500
2d21ac55 501 FSDBG(538, bp, NBOFF(bp), bp->nb_bufsize, bp->nb_np);
55e303ae 502
55e303ae
A
503 bp->nb_pagelist = upl;
504 SET(bp->nb_flags, NB_PAGELIST);
0a7de745 505 return 0;
55e303ae
A
506}
507
91447636
A
508/*
509 * update buffer's valid/dirty info from UBC
510 * (must NOT be called with nfs_buf_mutex held)
511 */
55e303ae
A
512void
513nfs_buf_upl_check(struct nfsbuf *bp)
514{
515 upl_page_info_t *pl;
516 off_t filesize, fileoffset;
517 int i, npages;
518
0a7de745 519 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
55e303ae 520 return;
0a7de745 521 }
55e303ae
A
522
523 npages = round_page_32(bp->nb_bufsize) / PAGE_SIZE;
2d21ac55 524 filesize = ubc_getsize(NFSTOV(bp->nb_np));
55e303ae 525 fileoffset = NBOFF(bp);
0a7de745 526 if (fileoffset < filesize) {
55e303ae 527 SET(bp->nb_flags, NB_CACHE);
0a7de745 528 } else {
55e303ae 529 CLR(bp->nb_flags, NB_CACHE);
0a7de745 530 }
55e303ae
A
531
532 pl = ubc_upl_pageinfo(bp->nb_pagelist);
f427ee49
A
533 NBPGS_ERASE(&bp->nb_valid);
534 NBPGS_ERASE(&bp->nb_dirty);
55e303ae 535
0a7de745 536 for (i = 0; i < npages; i++, fileoffset += PAGE_SIZE_64) {
55e303ae 537 /* anything beyond the end of the file is not valid or dirty */
0a7de745 538 if (fileoffset >= filesize) {
55e303ae 539 break;
0a7de745 540 }
55e303ae
A
541 if (!upl_valid_page(pl, i)) {
542 CLR(bp->nb_flags, NB_CACHE);
543 continue;
544 }
0a7de745
A
545 NBPGVALID_SET(bp, i);
546 if (upl_dirty_page(pl, i)) {
55e303ae 547 NBPGDIRTY_SET(bp, i);
0a7de745 548 }
55e303ae
A
549 }
550 fileoffset = NBOFF(bp);
551 if (ISSET(bp->nb_flags, NB_CACHE)) {
552 bp->nb_validoff = 0;
553 bp->nb_validend = bp->nb_bufsize;
0a7de745 554 if (fileoffset + bp->nb_validend > filesize) {
55e303ae 555 bp->nb_validend = filesize - fileoffset;
0a7de745 556 }
55e303ae
A
557 } else {
558 bp->nb_validoff = bp->nb_validend = -1;
559 }
560 FSDBG(539, bp, fileoffset, bp->nb_valid, bp->nb_dirty);
561 FSDBG(539, bp->nb_validoff, bp->nb_validend, bp->nb_dirtyoff, bp->nb_dirtyend);
562}
563
91447636
A
564/*
565 * make sure that a buffer is mapped
566 * (must NOT be called with nfs_buf_mutex held)
567 */
2d21ac55 568int
55e303ae
A
569nfs_buf_map(struct nfsbuf *bp)
570{
571 kern_return_t kret;
572
0a7de745
A
573 if (bp->nb_data) {
574 return 0;
575 }
576 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
577 return EINVAL;
578 }
55e303ae 579
b0d623f7 580 kret = ubc_upl_map(bp->nb_pagelist, (vm_offset_t *)&(bp->nb_data));
0a7de745 581 if (kret != KERN_SUCCESS) {
55e303ae 582 panic("nfs_buf_map: ubc_upl_map() failed with (%d)", kret);
0a7de745
A
583 }
584 if (bp->nb_data == 0) {
55e303ae 585 panic("ubc_upl_map mapped 0");
0a7de745 586 }
55e303ae 587 FSDBG(540, bp, bp->nb_flags, NBOFF(bp), bp->nb_data);
0a7de745 588 return 0;
55e303ae
A
589}
590
55e303ae
A
591/*
592 * normalize an nfsbuf's valid range
593 *
594 * the read/write code guarantees that we'll always have a valid
595 * region that is an integral number of pages. If either end
596 * of the valid range isn't page-aligned, it gets corrected
597 * here as we extend the valid range through all of the
598 * contiguous valid pages.
599 */
2d21ac55
A
600void
601nfs_buf_normalize_valid_range(nfsnode_t np, struct nfsbuf *bp)
55e303ae 602{
f427ee49 603 off_t pg, npg;
55e303ae 604 /* pull validoff back to start of contiguous valid page range */
0a7de745
A
605 pg = bp->nb_validoff / PAGE_SIZE;
606 while (pg >= 0 && NBPGVALID(bp, pg)) {
55e303ae 607 pg--;
0a7de745
A
608 }
609 bp->nb_validoff = (pg + 1) * PAGE_SIZE;
55e303ae 610 /* push validend forward to end of contiguous valid page range */
0a7de745
A
611 npg = bp->nb_bufsize / PAGE_SIZE;
612 pg = bp->nb_validend / PAGE_SIZE;
613 while (pg < npg && NBPGVALID(bp, pg)) {
55e303ae 614 pg++;
0a7de745 615 }
55e303ae
A
616 bp->nb_validend = pg * PAGE_SIZE;
617 /* clip to EOF */
0a7de745 618 if (NBOFF(bp) + bp->nb_validend > (off_t)np->n_size) {
55e303ae 619 bp->nb_validend = np->n_size % bp->nb_bufsize;
0a7de745 620 }
55e303ae
A
621}
622
623/*
2d21ac55
A
624 * process some entries on the delayed write queue
625 * (must be called with nfs_buf_mutex held)
55e303ae 626 */
b0d623f7 627void
2d21ac55 628nfs_buf_delwri_service(void)
55e303ae
A
629{
630 struct nfsbuf *bp;
2d21ac55
A
631 nfsnode_t np;
632 int error, i = 0;
55e303ae 633
55e303ae 634 while (i < 8 && (bp = TAILQ_FIRST(&nfsbufdelwri)) != NULL) {
2d21ac55 635 np = bp->nb_np;
55e303ae 636 nfs_buf_remfree(bp);
91447636 637 nfs_buf_refget(bp);
0a7de745
A
638 while ((error = nfs_buf_acquire(bp, 0, 0, 0)) == EAGAIN) {
639 ;
640 }
91447636 641 nfs_buf_refrele(bp);
0a7de745 642 if (error) {
91447636 643 break;
0a7de745 644 }
2d21ac55 645 if (!bp->nb_np) {
91447636
A
646 /* buffer is no longer valid */
647 nfs_buf_drop(bp);
648 continue;
649 }
0a7de745 650 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
8f6c56a5 651 nfs_buf_check_write_verifier(np, bp);
0a7de745 652 }
55e303ae
A
653 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
654 /* put buffer at end of delwri list */
655 TAILQ_INSERT_TAIL(&nfsbufdelwri, bp, nb_free);
656 nfsbufdelwricnt++;
91447636 657 nfs_buf_drop(bp);
c3c9b80d 658 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 659 nfs_flushcommits(np, 1);
55e303ae 660 } else {
91447636 661 SET(bp->nb_flags, NB_ASYNC);
c3c9b80d 662 lck_mtx_unlock(&nfs_buf_mutex);
55e303ae
A
663 nfs_buf_write(bp);
664 }
665 i++;
c3c9b80d 666 lck_mtx_lock(&nfs_buf_mutex);
55e303ae 667 }
2d21ac55
A
668}
669
670/*
671 * thread to service the delayed write queue when asked
672 */
b0d623f7 673void
2d21ac55
A
674nfs_buf_delwri_thread(__unused void *arg, __unused wait_result_t wr)
675{
cb323159 676 struct timespec ts = { .tv_sec = 30, .tv_nsec = 0 };
2d21ac55
A
677 int error = 0;
678
c3c9b80d 679 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
680 while (!error) {
681 nfs_buf_delwri_service();
c3c9b80d 682 error = msleep(&nfsbufdelwrithd, &nfs_buf_mutex, 0, "nfsbufdelwri", &ts);
2d21ac55
A
683 }
684 nfsbufdelwrithd = NULL;
c3c9b80d 685 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
686 thread_terminate(nfsbufdelwrithd);
687}
688
689/*
690 * try to push out some delayed/uncommitted writes
691 * ("locked" indicates whether nfs_buf_mutex is already held)
692 */
b0d623f7 693void
2d21ac55
A
694nfs_buf_delwri_push(int locked)
695{
0a7de745 696 if (TAILQ_EMPTY(&nfsbufdelwri)) {
2d21ac55 697 return;
0a7de745
A
698 }
699 if (!locked) {
c3c9b80d 700 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 701 }
2d21ac55 702 /* wake up the delayed write service thread */
0a7de745 703 if (nfsbufdelwrithd) {
2d21ac55 704 wakeup(&nfsbufdelwrithd);
0a7de745 705 } else if (kernel_thread_start(nfs_buf_delwri_thread, NULL, &nfsbufdelwrithd) == KERN_SUCCESS) {
2d21ac55 706 thread_deallocate(nfsbufdelwrithd);
0a7de745 707 }
2d21ac55 708 /* otherwise, try to do some of the work ourselves */
0a7de745 709 if (!nfsbufdelwrithd) {
2d21ac55 710 nfs_buf_delwri_service();
0a7de745
A
711 }
712 if (!locked) {
c3c9b80d 713 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 714 }
55e303ae
A
715}
716
717/*
91447636
A
718 * Get an nfs buffer.
719 *
720 * Returns errno on error, 0 otherwise.
721 * Any buffer is returned in *bpp.
722 *
723 * If NBLK_ONLYVALID is set, only return buffer if found in cache.
724 * If NBLK_NOWAIT is set, don't wait for the buffer if it's marked BUSY.
725 *
726 * Check for existence of buffer in cache.
727 * Or attempt to reuse a buffer from one of the free lists.
728 * Or allocate a new buffer if we haven't already hit max allocation.
729 * Or wait for a free buffer.
730 *
731 * If available buffer found, prepare it, and return it.
732 *
733 * If the calling process is interrupted by a signal for
734 * an interruptible mount point, return EINTR.
55e303ae 735 */
91447636 736int
55e303ae 737nfs_buf_get(
2d21ac55 738 nfsnode_t np,
91447636 739 daddr64_t blkno,
b0d623f7 740 uint32_t size,
2d21ac55 741 thread_t thd,
91447636
A
742 int flags,
743 struct nfsbuf **bpp)
55e303ae 744{
2d21ac55
A
745 vnode_t vp = NFSTOV(np);
746 struct nfsmount *nmp = VTONMP(vp);
55e303ae 747 struct nfsbuf *bp;
b0d623f7 748 uint32_t bufsize;
55e303ae 749 int slpflag = PCATCH;
91447636
A
750 int operation = (flags & NBLK_OPMASK);
751 int error = 0;
752 struct timespec ts;
55e303ae 753
2d21ac55 754 FSDBG_TOP(541, np, blkno, size, flags);
91447636 755 *bpp = NULL;
55e303ae
A
756
757 bufsize = size;
0a7de745 758 if (bufsize > NFS_MAXBSIZE) {
0c530ab8 759 panic("nfs_buf_get: buffer larger than NFS_MAXBSIZE requested");
0a7de745 760 }
55e303ae 761
fe8ab488 762 if (nfs_mount_gone(nmp)) {
2d21ac55 763 FSDBG_BOT(541, np, blkno, 0, ENXIO);
0a7de745 764 return ENXIO;
0c530ab8 765 }
55e303ae 766
2d21ac55 767 if (!UBCINFOEXISTS(vp)) {
91447636 768 operation = NBLK_META;
b0d623f7 769 } else if (bufsize < (uint32_t)nmp->nm_biosize) {
55e303ae 770 /* reg files should always have biosize blocks */
2d21ac55 771 bufsize = nmp->nm_biosize;
91447636 772 }
55e303ae 773
91447636 774 /* if NBLK_WRITE, check for too many delayed/uncommitted writes */
2d21ac55
A
775 if ((operation == NBLK_WRITE) && (nfs_nbdwrite > NFS_A_LOT_OF_DELAYED_WRITES)) {
776 FSDBG_TOP(542, np, blkno, nfs_nbdwrite, NFS_A_LOT_OF_DELAYED_WRITES);
55e303ae
A
777
778 /* poke the delwri list */
91447636 779 nfs_buf_delwri_push(0);
55e303ae
A
780
781 /* sleep to let other threads run... */
782 tsleep(&nfs_nbdwrite, PCATCH, "nfs_nbdwrite", 1);
2d21ac55 783 FSDBG_BOT(542, np, blkno, nfs_nbdwrite, NFS_A_LOT_OF_DELAYED_WRITES);
55e303ae
A
784 }
785
786loop:
c3c9b80d 787 lck_mtx_lock(&nfs_buf_mutex);
55e303ae 788
6d2010ae
A
789 /* wait for any buffer invalidation/flushing to complete */
790 while (np->n_bflag & NBINVALINPROG) {
791 np->n_bflag |= NBINVALWANT;
792 ts.tv_sec = 2;
793 ts.tv_nsec = 0;
c3c9b80d 794 msleep(&np->n_bflag, &nfs_buf_mutex, slpflag, "nfs_buf_get_invalwait", &ts);
6d2010ae 795 if ((error = nfs_sigintr(VTONMP(vp), NULL, thd, 0))) {
c3c9b80d 796 lck_mtx_unlock(&nfs_buf_mutex);
6d2010ae 797 FSDBG_BOT(541, np, blkno, 0, error);
0a7de745 798 return error;
6d2010ae 799 }
0a7de745 800 if (np->n_bflag & NBINVALINPROG) {
6d2010ae 801 slpflag = 0;
0a7de745 802 }
6d2010ae
A
803 }
804
55e303ae 805 /* check for existence of nfsbuf in cache */
2d21ac55 806 if ((bp = nfs_buf_incore(np, blkno))) {
55e303ae 807 /* if busy, set wanted and wait */
91447636
A
808 if (ISSET(bp->nb_lflags, NBL_BUSY)) {
809 if (flags & NBLK_NOWAIT) {
c3c9b80d 810 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 811 FSDBG_BOT(541, np, blkno, bp, 0xbcbcbcbc);
0a7de745 812 return 0;
91447636 813 }
2d21ac55 814 FSDBG_TOP(543, np, blkno, bp, bp->nb_flags);
91447636
A
815 SET(bp->nb_lflags, NBL_WANTED);
816
817 ts.tv_sec = 2;
818 ts.tv_nsec = 0;
c3c9b80d 819 msleep(bp, &nfs_buf_mutex, slpflag | (PRIBIO + 1) | PDROP,
0a7de745 820 "nfsbufget", (slpflag == PCATCH) ? NULL : &ts);
55e303ae 821 slpflag = 0;
2d21ac55 822 FSDBG_BOT(543, np, blkno, bp, bp->nb_flags);
36401178 823 if ((error = nfs_sigintr(VTONMP(vp), NULL, thd, 0))) {
2d21ac55 824 FSDBG_BOT(541, np, blkno, 0, error);
0a7de745 825 return error;
55e303ae
A
826 }
827 goto loop;
828 }
0a7de745 829 if (bp->nb_bufsize != bufsize) {
55e303ae 830 panic("nfsbuf size mismatch");
0a7de745 831 }
91447636
A
832 SET(bp->nb_lflags, NBL_BUSY);
833 SET(bp->nb_flags, NB_CACHE);
55e303ae
A
834 nfs_buf_remfree(bp);
835 /* additional paranoia: */
0a7de745 836 if (ISSET(bp->nb_flags, NB_PAGELIST)) {
55e303ae 837 panic("pagelist buffer was not busy");
0a7de745 838 }
55e303ae
A
839 goto buffer_setup;
840 }
841
91447636 842 if (flags & NBLK_ONLYVALID) {
c3c9b80d 843 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 844 FSDBG_BOT(541, np, blkno, 0, 0x0000cace);
0a7de745 845 return 0;
91447636
A
846 }
847
55e303ae
A
848 /*
849 * where to get a free buffer:
91447636 850 * - if meta and maxmeta reached, must reuse meta
55e303ae 851 * - alloc new if we haven't reached min bufs
483a1d10
A
852 * - if free lists are NOT empty
853 * - if free list is stale, use it
854 * - else if freemeta list is stale, use it
855 * - else if max bufs allocated, use least-time-to-stale
55e303ae
A
856 * - alloc new if we haven't reached max allowed
857 * - start clearing out delwri list and try again
858 */
859
91447636
A
860 if ((operation == NBLK_META) && (nfsbufmetacnt >= nfsbufmetamax)) {
861 /* if we've hit max meta buffers, must reuse a meta buffer */
862 bp = TAILQ_FIRST(&nfsbuffreemeta);
863 } else if ((nfsbufcnt > nfsbufmin) &&
483a1d10
A
864 (!TAILQ_EMPTY(&nfsbuffree) || !TAILQ_EMPTY(&nfsbuffreemeta))) {
865 /* try to pull an nfsbuf off a free list */
866 struct nfsbuf *lrubp, *metabp;
867 struct timeval now;
868 microuptime(&now);
869
91447636 870 /* if the next LRU or META buffer is invalid or stale, use it */
483a1d10 871 lrubp = TAILQ_FIRST(&nfsbuffree);
91447636 872 if (lrubp && (!NBUFSTAMPVALID(lrubp) ||
0a7de745 873 ((lrubp->nb_timestamp + NFSBUF_LRU_STALE) < now.tv_sec))) {
483a1d10 874 bp = lrubp;
0a7de745 875 }
483a1d10 876 metabp = TAILQ_FIRST(&nfsbuffreemeta);
91447636 877 if (!bp && metabp && (!NBUFSTAMPVALID(metabp) ||
0a7de745 878 ((metabp->nb_timestamp + NFSBUF_META_STALE) < now.tv_sec))) {
483a1d10 879 bp = metabp;
0a7de745 880 }
483a1d10
A
881
882 if (!bp && (nfsbufcnt >= nfsbufmax)) {
883 /* we've already allocated all bufs, so */
884 /* choose the buffer that'll go stale first */
0a7de745 885 if (!metabp) {
483a1d10 886 bp = lrubp;
0a7de745 887 } else if (!lrubp) {
483a1d10 888 bp = metabp;
0a7de745 889 } else {
f427ee49 890 time_t lru_stale_time, meta_stale_time;
483a1d10
A
891 lru_stale_time = lrubp->nb_timestamp + NFSBUF_LRU_STALE;
892 meta_stale_time = metabp->nb_timestamp + NFSBUF_META_STALE;
0a7de745 893 if (lru_stale_time <= meta_stale_time) {
483a1d10 894 bp = lrubp;
0a7de745 895 } else {
483a1d10 896 bp = metabp;
0a7de745 897 }
55e303ae 898 }
55e303ae 899 }
91447636 900 }
483a1d10 901
91447636
A
902 if (bp) {
903 /* we have a buffer to reuse */
2d21ac55 904 FSDBG(544, np, blkno, bp, bp->nb_flags);
91447636 905 nfs_buf_remfree(bp);
0a7de745 906 if (ISSET(bp->nb_flags, NB_DELWRI)) {
91447636 907 panic("nfs_buf_get: delwri");
0a7de745 908 }
91447636 909 SET(bp->nb_lflags, NBL_BUSY);
2d21ac55
A
910 /* disassociate buffer from previous nfsnode */
911 if (bp->nb_np) {
91447636
A
912 if (bp->nb_vnbufs.le_next != NFSNOLIST) {
913 LIST_REMOVE(bp, nb_vnbufs);
914 bp->nb_vnbufs.le_next = NFSNOLIST;
483a1d10 915 }
2d21ac55 916 bp->nb_np = NULL;
91447636
A
917 }
918 LIST_REMOVE(bp, nb_hash);
919 /* nuke any creds we're holding */
0a7de745 920 if (IS_VALID_CRED(bp->nb_rcred)) {
0c530ab8 921 kauth_cred_unref(&bp->nb_rcred);
0a7de745
A
922 }
923 if (IS_VALID_CRED(bp->nb_wcred)) {
0c530ab8 924 kauth_cred_unref(&bp->nb_wcred);
0a7de745 925 }
91447636
A
926 /* if buf will no longer be NB_META, dump old buffer */
927 if (operation == NBLK_META) {
0a7de745 928 if (!ISSET(bp->nb_flags, NB_META)) {
91447636 929 nfsbufmetacnt++;
0a7de745 930 }
91447636
A
931 } else if (ISSET(bp->nb_flags, NB_META)) {
932 if (bp->nb_data) {
f427ee49 933 kheap_free(KHEAP_DATA_BUFFERS, bp->nb_data, bp->nb_bufsize);
483a1d10
A
934 bp->nb_data = NULL;
935 }
91447636 936 nfsbufmetacnt--;
55e303ae 937 }
91447636
A
938 /* re-init buf fields */
939 bp->nb_error = 0;
940 bp->nb_validoff = bp->nb_validend = -1;
941 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
f427ee49
A
942 NBPGS_ERASE(&bp->nb_valid);
943 NBPGS_ERASE(&bp->nb_dirty);
8f6c56a5 944 bp->nb_verf = 0;
91447636
A
945 } else {
946 /* no buffer to reuse */
947 if ((nfsbufcnt < nfsbufmax) &&
948 ((operation != NBLK_META) || (nfsbufmetacnt < nfsbufmetamax))) {
483a1d10 949 /* just alloc a new one */
f427ee49 950 bp = zalloc(nfsbuf_zone);
483a1d10 951 nfsbufcnt++;
2d21ac55
A
952
953 /*
954 * If any excess bufs, make sure the timer
955 * is running to free them up later.
956 */
957 if (nfsbufcnt > nfsbufmin && !nfs_buf_timer_on) {
958 nfs_buf_timer_on = 1;
959 nfs_interval_timer_start(nfs_buf_timer_call,
0a7de745 960 NFSBUF_FREE_PERIOD * 1000);
2d21ac55
A
961 }
962
0a7de745 963 if (operation == NBLK_META) {
91447636 964 nfsbufmetacnt++;
0a7de745 965 }
2d21ac55 966 NFSBUFCNTCHK();
483a1d10
A
967 /* init nfsbuf */
968 bzero(bp, sizeof(*bp));
cb323159
A
969 os_ref_init(&bp->nb_refs, NULL);
970
483a1d10
A
971 bp->nb_free.tqe_next = NFSNOLIST;
972 bp->nb_validoff = bp->nb_validend = -1;
2d21ac55 973 FSDBG(545, np, blkno, bp, 0);
483a1d10
A
974 } else {
975 /* too many bufs... wait for buffers to free up */
2d21ac55 976 FSDBG_TOP(546, np, blkno, nfsbufcnt, nfsbufmax);
55e303ae 977
483a1d10 978 /* poke the delwri list */
91447636 979 nfs_buf_delwri_push(1);
483a1d10
A
980
981 nfsneedbuffer = 1;
c3c9b80d 982 msleep(&nfsneedbuffer, &nfs_buf_mutex, PCATCH | PDROP, "nfsbufget", NULL);
2d21ac55 983 FSDBG_BOT(546, np, blkno, nfsbufcnt, nfsbufmax);
36401178 984 if ((error = nfs_sigintr(VTONMP(vp), NULL, thd, 0))) {
2d21ac55 985 FSDBG_BOT(541, np, blkno, 0, error);
0a7de745 986 return error;
483a1d10
A
987 }
988 goto loop;
55e303ae 989 }
55e303ae
A
990 }
991
b0d623f7
A
992 /* set up nfsbuf */
993 SET(bp->nb_lflags, NBL_BUSY);
91447636 994 bp->nb_flags = 0;
55e303ae
A
995 bp->nb_lblkno = blkno;
996 /* insert buf in hash */
91447636 997 LIST_INSERT_HEAD(NFSBUFHASH(np, blkno), bp, nb_hash);
2d21ac55
A
998 /* associate buffer with new nfsnode */
999 bp->nb_np = np;
55e303ae
A
1000 LIST_INSERT_HEAD(&np->n_cleanblkhd, bp, nb_vnbufs);
1001
1002buffer_setup:
1003
91447636 1004 /* unlock hash */
c3c9b80d 1005 lck_mtx_unlock(&nfs_buf_mutex);
91447636 1006
55e303ae 1007 switch (operation) {
91447636 1008 case NBLK_META:
55e303ae
A
1009 SET(bp->nb_flags, NB_META);
1010 if ((bp->nb_bufsize != bufsize) && bp->nb_data) {
f427ee49 1011 kheap_free(KHEAP_DATA_BUFFERS, bp->nb_data, bp->nb_bufsize);
55e303ae
A
1012 bp->nb_data = NULL;
1013 bp->nb_validoff = bp->nb_validend = -1;
1014 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
f427ee49
A
1015 NBPGS_ERASE(&bp->nb_valid);
1016 NBPGS_ERASE(&bp->nb_dirty);
55e303ae
A
1017 CLR(bp->nb_flags, NB_CACHE);
1018 }
0a7de745 1019 if (!bp->nb_data) {
f427ee49
A
1020 bp->nb_data = kheap_alloc(KHEAP_DATA_BUFFERS,
1021 bufsize, Z_WAITOK);
0a7de745 1022 }
91447636
A
1023 if (!bp->nb_data) {
1024 /* Ack! couldn't allocate the data buffer! */
2d21ac55 1025 /* clean up buffer and return error */
c3c9b80d 1026 lck_mtx_lock(&nfs_buf_mutex);
91447636
A
1027 LIST_REMOVE(bp, nb_vnbufs);
1028 bp->nb_vnbufs.le_next = NFSNOLIST;
2d21ac55 1029 bp->nb_np = NULL;
91447636
A
1030 /* invalidate usage timestamp to allow immediate freeing */
1031 NBUFSTAMPINVALIDATE(bp);
0a7de745 1032 if (bp->nb_free.tqe_next != NFSNOLIST) {
91447636 1033 panic("nfsbuf on freelist");
0a7de745 1034 }
91447636
A
1035 TAILQ_INSERT_HEAD(&nfsbuffree, bp, nb_free);
1036 nfsbuffreecnt++;
c3c9b80d 1037 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 1038 FSDBG_BOT(541, np, blkno, 0xb00, ENOMEM);
0a7de745 1039 return ENOMEM;
91447636 1040 }
55e303ae
A
1041 bp->nb_bufsize = bufsize;
1042 break;
1043
91447636
A
1044 case NBLK_READ:
1045 case NBLK_WRITE:
1046 /*
1047 * Set or clear NB_READ now to let the UPL subsystem know
1048 * if we intend to modify the pages or not.
1049 */
1050 if (operation == NBLK_READ) {
1051 SET(bp->nb_flags, NB_READ);
1052 } else {
1053 CLR(bp->nb_flags, NB_READ);
1054 }
0a7de745 1055 if (bufsize < PAGE_SIZE) {
55e303ae 1056 bufsize = PAGE_SIZE;
0a7de745 1057 }
55e303ae
A
1058 bp->nb_bufsize = bufsize;
1059 bp->nb_validoff = bp->nb_validend = -1;
1060
91447636 1061 if (UBCINFOEXISTS(vp)) {
2d21ac55 1062 /* set up upl */
55e303ae
A
1063 if (nfs_buf_upl_setup(bp)) {
1064 /* unable to create upl */
1065 /* vm object must no longer exist */
2d21ac55 1066 /* clean up buffer and return error */
c3c9b80d 1067 lck_mtx_lock(&nfs_buf_mutex);
55e303ae
A
1068 LIST_REMOVE(bp, nb_vnbufs);
1069 bp->nb_vnbufs.le_next = NFSNOLIST;
2d21ac55 1070 bp->nb_np = NULL;
91447636
A
1071 /* invalidate usage timestamp to allow immediate freeing */
1072 NBUFSTAMPINVALIDATE(bp);
0a7de745 1073 if (bp->nb_free.tqe_next != NFSNOLIST) {
55e303ae 1074 panic("nfsbuf on freelist");
0a7de745 1075 }
55e303ae
A
1076 TAILQ_INSERT_HEAD(&nfsbuffree, bp, nb_free);
1077 nfsbuffreecnt++;
c3c9b80d 1078 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 1079 FSDBG_BOT(541, np, blkno, 0x2bc, EIO);
0a7de745 1080 return EIO;
55e303ae
A
1081 }
1082 nfs_buf_upl_check(bp);
1083 }
1084 break;
1085
1086 default:
1087 panic("nfs_buf_get: %d unknown operation", operation);
1088 }
1089
91447636 1090 *bpp = bp;
55e303ae 1091
2d21ac55 1092 FSDBG_BOT(541, np, blkno, bp, bp->nb_flags);
55e303ae 1093
0a7de745 1094 return 0;
55e303ae
A
1095}
1096
1097void
483a1d10 1098nfs_buf_release(struct nfsbuf *bp, int freeup)
55e303ae 1099{
2d21ac55
A
1100 nfsnode_t np = bp->nb_np;
1101 vnode_t vp;
483a1d10 1102 struct timeval now;
91447636 1103 int wakeup_needbuffer, wakeup_buffer, wakeup_nbdwrite;
55e303ae
A
1104
1105 FSDBG_TOP(548, bp, NBOFF(bp), bp->nb_flags, bp->nb_data);
1106 FSDBG(548, bp->nb_validoff, bp->nb_validend, bp->nb_dirtyoff, bp->nb_dirtyend);
1107 FSDBG(548, bp->nb_valid, 0, bp->nb_dirty, 0);
1108
2d21ac55
A
1109 vp = np ? NFSTOV(np) : NULL;
1110 if (vp && UBCINFOEXISTS(vp) && bp->nb_bufsize) {
b0d623f7 1111 int upl_flags, rv;
55e303ae 1112 upl_t upl;
b0d623f7 1113 uint32_t i;
55e303ae
A
1114
1115 if (!ISSET(bp->nb_flags, NB_PAGELIST) && !ISSET(bp->nb_flags, NB_INVAL)) {
1116 rv = nfs_buf_upl_setup(bp);
0a7de745 1117 if (rv) {
55e303ae 1118 printf("nfs_buf_release: upl create failed %d\n", rv);
0a7de745 1119 } else {
55e303ae 1120 nfs_buf_upl_check(bp);
0a7de745 1121 }
55e303ae
A
1122 }
1123 upl = bp->nb_pagelist;
0a7de745 1124 if (!upl) {
55e303ae 1125 goto pagelist_cleanup_done;
0a7de745 1126 }
55e303ae 1127 if (bp->nb_data) {
0a7de745 1128 if (ubc_upl_unmap(upl) != KERN_SUCCESS) {
55e303ae 1129 panic("ubc_upl_unmap failed");
0a7de745 1130 }
55e303ae
A
1131 bp->nb_data = NULL;
1132 }
2d21ac55
A
1133 /*
1134 * Abort the pages on error or: if this is an invalid or
1135 * non-needcommit nocache buffer AND no pages are dirty.
1136 */
f427ee49 1137 if (ISSET(bp->nb_flags, NB_ERROR) || (!nfs_buf_pgs_is_set(&bp->nb_dirty) && (ISSET(bp->nb_flags, NB_INVAL) ||
2d21ac55 1138 (ISSET(bp->nb_flags, NB_NOCACHE) && !ISSET(bp->nb_flags, (NB_NEEDCOMMIT | NB_DELWRI)))))) {
0a7de745 1139 if (ISSET(bp->nb_flags, (NB_READ | NB_INVAL | NB_NOCACHE))) {
55e303ae 1140 upl_flags = UPL_ABORT_DUMP_PAGES;
0a7de745 1141 } else {
55e303ae 1142 upl_flags = 0;
0a7de745 1143 }
55e303ae
A
1144 ubc_upl_abort(upl, upl_flags);
1145 goto pagelist_cleanup_done;
1146 }
0a7de745
A
1147 for (i = 0; i <= (bp->nb_bufsize - 1) / PAGE_SIZE; i++) {
1148 if (!NBPGVALID(bp, i)) {
55e303ae 1149 ubc_upl_abort_range(upl,
0a7de745
A
1150 i * PAGE_SIZE, PAGE_SIZE,
1151 UPL_ABORT_DUMP_PAGES |
1152 UPL_ABORT_FREE_ON_EMPTY);
1153 } else {
1154 if (NBPGDIRTY(bp, i)) {
55e303ae 1155 upl_flags = UPL_COMMIT_SET_DIRTY;
0a7de745 1156 } else {
55e303ae 1157 upl_flags = UPL_COMMIT_CLEAR_DIRTY;
0a7de745
A
1158 }
1159
1160 if (!ISSET(bp->nb_flags, (NB_NEEDCOMMIT | NB_DELWRI))) {
b0d623f7 1161 upl_flags |= UPL_COMMIT_CLEAR_PRECIOUS;
0a7de745 1162 }
b0d623f7 1163
55e303ae 1164 ubc_upl_commit_range(upl,
0a7de745
A
1165 i * PAGE_SIZE, PAGE_SIZE,
1166 upl_flags |
1167 UPL_COMMIT_INACTIVATE |
1168 UPL_COMMIT_FREE_ON_EMPTY);
55e303ae
A
1169 }
1170 }
1171pagelist_cleanup_done:
b0d623f7 1172 /* invalidate any pages past EOF */
2d21ac55 1173 if (NBOFF(bp) + bp->nb_bufsize > (off_t)(np->n_size)) {
91447636 1174 off_t start, end;
2d21ac55 1175 start = trunc_page_64(np->n_size) + PAGE_SIZE_64;
0c530ab8 1176 end = trunc_page_64(NBOFF(bp) + bp->nb_bufsize);
0a7de745 1177 if (start < NBOFF(bp)) {
b0d623f7 1178 start = NBOFF(bp);
0a7de745 1179 }
91447636 1180 if (end > start) {
0a7de745 1181 if ((rv = ubc_msync(vp, start, end, NULL, UBC_INVALIDATE))) {
6d2010ae 1182 printf("nfs_buf_release(): ubc_msync failed!, error %d\n", rv);
0a7de745 1183 }
91447636 1184 }
55e303ae
A
1185 }
1186 CLR(bp->nb_flags, NB_PAGELIST);
1187 bp->nb_pagelist = NULL;
1188 }
1189
c3c9b80d 1190 lck_mtx_lock(&nfs_buf_mutex);
91447636
A
1191
1192 wakeup_needbuffer = wakeup_buffer = wakeup_nbdwrite = 0;
1193
55e303ae
A
1194 /* Wake up any processes waiting for any buffer to become free. */
1195 if (nfsneedbuffer) {
1196 nfsneedbuffer = 0;
91447636 1197 wakeup_needbuffer = 1;
55e303ae
A
1198 }
1199 /* Wake up any processes waiting for _this_ buffer to become free. */
91447636
A
1200 if (ISSET(bp->nb_lflags, NBL_WANTED)) {
1201 CLR(bp->nb_lflags, NBL_WANTED);
1202 wakeup_buffer = 1;
55e303ae
A
1203 }
1204
0c530ab8
A
1205 /* If it's non-needcommit nocache, or an error, mark it invalid. */
1206 if (ISSET(bp->nb_flags, NB_ERROR) ||
0a7de745 1207 (ISSET(bp->nb_flags, NB_NOCACHE) && !ISSET(bp->nb_flags, (NB_NEEDCOMMIT | NB_DELWRI)))) {
55e303ae 1208 SET(bp->nb_flags, NB_INVAL);
0a7de745 1209 }
55e303ae
A
1210
1211 if ((bp->nb_bufsize <= 0) || ISSET(bp->nb_flags, NB_INVAL)) {
2d21ac55 1212 /* If it's invalid or empty, dissociate it from its nfsnode */
55e303ae
A
1213 if (bp->nb_vnbufs.le_next != NFSNOLIST) {
1214 LIST_REMOVE(bp, nb_vnbufs);
1215 bp->nb_vnbufs.le_next = NFSNOLIST;
1216 }
2d21ac55 1217 bp->nb_np = NULL;
55e303ae
A
1218 /* if this was a delayed write, wakeup anyone */
1219 /* waiting for delayed writes to complete */
1220 if (ISSET(bp->nb_flags, NB_DELWRI)) {
1221 CLR(bp->nb_flags, NB_DELWRI);
2d21ac55
A
1222 nfs_nbdwrite--;
1223 NFSBUFCNTCHK();
91447636 1224 wakeup_nbdwrite = 1;
55e303ae 1225 }
91447636
A
1226 /* invalidate usage timestamp to allow immediate freeing */
1227 NBUFSTAMPINVALIDATE(bp);
55e303ae 1228 /* put buffer at head of free list */
0a7de745 1229 if (bp->nb_free.tqe_next != NFSNOLIST) {
55e303ae 1230 panic("nfsbuf on freelist");
0a7de745 1231 }
483a1d10 1232 SET(bp->nb_flags, NB_INVAL);
91447636
A
1233 if (ISSET(bp->nb_flags, NB_META)) {
1234 TAILQ_INSERT_HEAD(&nfsbuffreemeta, bp, nb_free);
1235 nfsbuffreemetacnt++;
1236 } else {
1237 TAILQ_INSERT_HEAD(&nfsbuffree, bp, nb_free);
1238 nfsbuffreecnt++;
1239 }
55e303ae
A
1240 } else if (ISSET(bp->nb_flags, NB_DELWRI)) {
1241 /* put buffer at end of delwri list */
0a7de745 1242 if (bp->nb_free.tqe_next != NFSNOLIST) {
55e303ae 1243 panic("nfsbuf on freelist");
0a7de745 1244 }
55e303ae
A
1245 TAILQ_INSERT_TAIL(&nfsbufdelwri, bp, nb_free);
1246 nfsbufdelwricnt++;
91447636 1247 freeup = 0;
55e303ae 1248 } else {
483a1d10
A
1249 /* update usage timestamp */
1250 microuptime(&now);
1251 bp->nb_timestamp = now.tv_sec;
55e303ae 1252 /* put buffer at end of free list */
0a7de745 1253 if (bp->nb_free.tqe_next != NFSNOLIST) {
55e303ae 1254 panic("nfsbuf on freelist");
0a7de745 1255 }
483a1d10
A
1256 if (ISSET(bp->nb_flags, NB_META)) {
1257 TAILQ_INSERT_TAIL(&nfsbuffreemeta, bp, nb_free);
1258 nfsbuffreemetacnt++;
1259 } else {
1260 TAILQ_INSERT_TAIL(&nfsbuffree, bp, nb_free);
1261 nfsbuffreecnt++;
1262 }
55e303ae
A
1263 }
1264
2d21ac55 1265 NFSBUFCNTCHK();
55e303ae
A
1266
1267 /* Unlock the buffer. */
2d21ac55 1268 CLR(bp->nb_flags, (NB_ASYNC | NB_STABLE));
91447636 1269 CLR(bp->nb_lflags, NBL_BUSY);
55e303ae
A
1270
1271 FSDBG_BOT(548, bp, NBOFF(bp), bp->nb_flags, bp->nb_data);
91447636 1272
c3c9b80d 1273 lck_mtx_unlock(&nfs_buf_mutex);
91447636 1274
0a7de745 1275 if (wakeup_needbuffer) {
91447636 1276 wakeup(&nfsneedbuffer);
0a7de745
A
1277 }
1278 if (wakeup_buffer) {
91447636 1279 wakeup(bp);
0a7de745
A
1280 }
1281 if (wakeup_nbdwrite) {
91447636 1282 wakeup(&nfs_nbdwrite);
0a7de745
A
1283 }
1284 if (freeup) {
91447636 1285 NFS_BUF_FREEUP();
0a7de745 1286 }
55e303ae
A
1287}
1288
1289/*
1290 * Wait for operations on the buffer to complete.
1291 * When they do, extract and return the I/O's error value.
1292 */
1293int
1294nfs_buf_iowait(struct nfsbuf *bp)
1295{
1296 FSDBG_TOP(549, bp, NBOFF(bp), bp->nb_flags, bp->nb_error);
1297
c3c9b80d 1298 lck_mtx_lock(&nfs_buf_mutex);
91447636 1299
0a7de745 1300 while (!ISSET(bp->nb_flags, NB_DONE)) {
c3c9b80d 1301 msleep(bp, &nfs_buf_mutex, PRIBIO + 1, "nfs_buf_iowait", NULL);
0a7de745 1302 }
91447636 1303
c3c9b80d 1304 lck_mtx_unlock(&nfs_buf_mutex);
55e303ae
A
1305
1306 FSDBG_BOT(549, bp, NBOFF(bp), bp->nb_flags, bp->nb_error);
1307
1308 /* check for interruption of I/O, then errors. */
1309 if (ISSET(bp->nb_flags, NB_EINTR)) {
1310 CLR(bp->nb_flags, NB_EINTR);
0a7de745
A
1311 return EINTR;
1312 } else if (ISSET(bp->nb_flags, NB_ERROR)) {
1313 return bp->nb_error ? bp->nb_error : EIO;
1314 }
1315 return 0;
55e303ae
A
1316}
1317
1318/*
1319 * Mark I/O complete on a buffer.
1320 */
1321void
1322nfs_buf_iodone(struct nfsbuf *bp)
1323{
55e303ae
A
1324 FSDBG_TOP(550, bp, NBOFF(bp), bp->nb_flags, bp->nb_error);
1325
0a7de745 1326 if (ISSET(bp->nb_flags, NB_DONE)) {
55e303ae 1327 panic("nfs_buf_iodone already");
0a7de745 1328 }
55e303ae
A
1329
1330 if (!ISSET(bp->nb_flags, NB_READ)) {
1331 CLR(bp->nb_flags, NB_WRITEINPROG);
91447636
A
1332 /*
1333 * vnode_writedone() takes care of waking up
1334 * any throttled write operations
1335 */
2d21ac55 1336 vnode_writedone(NFSTOV(bp->nb_np));
b0d623f7
A
1337 nfs_node_lock_force(bp->nb_np);
1338 bp->nb_np->n_numoutput--;
1339 nfs_node_unlock(bp->nb_np);
55e303ae 1340 }
0a7de745
A
1341 if (ISSET(bp->nb_flags, NB_ASYNC)) { /* if async, release it */
1342 SET(bp->nb_flags, NB_DONE); /* note that it's done */
483a1d10 1343 nfs_buf_release(bp, 1);
0a7de745 1344 } else { /* or just wakeup the buffer */
c3c9b80d 1345 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 1346 SET(bp->nb_flags, NB_DONE); /* note that it's done */
91447636 1347 CLR(bp->nb_lflags, NBL_WANTED);
c3c9b80d 1348 lck_mtx_unlock(&nfs_buf_mutex);
55e303ae
A
1349 wakeup(bp);
1350 }
1351
1352 FSDBG_BOT(550, bp, NBOFF(bp), bp->nb_flags, bp->nb_error);
1353}
1354
1355void
2d21ac55 1356nfs_buf_write_delayed(struct nfsbuf *bp)
55e303ae 1357{
2d21ac55 1358 nfsnode_t np = bp->nb_np;
55e303ae
A
1359
1360 FSDBG_TOP(551, bp, NBOFF(bp), bp->nb_flags, 0);
1361 FSDBG(551, bp, bp->nb_dirtyoff, bp->nb_dirtyend, bp->nb_dirty);
1362
1363 /*
1364 * If the block hasn't been seen before:
1365 * (1) Mark it as having been seen,
2d21ac55 1366 * (2) Make sure it's on its node's correct block list,
55e303ae
A
1367 */
1368 if (!ISSET(bp->nb_flags, NB_DELWRI)) {
1369 SET(bp->nb_flags, NB_DELWRI);
55e303ae 1370 /* move to dirty list */
c3c9b80d 1371 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
1372 nfs_nbdwrite++;
1373 NFSBUFCNTCHK();
0a7de745 1374 if (bp->nb_vnbufs.le_next != NFSNOLIST) {
55e303ae 1375 LIST_REMOVE(bp, nb_vnbufs);
0a7de745 1376 }
2d21ac55 1377 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
c3c9b80d 1378 lck_mtx_unlock(&nfs_buf_mutex);
55e303ae
A
1379 }
1380
1381 /*
1382 * If the vnode has "too many" write operations in progress
1383 * wait for them to finish the IO
1384 */
2d21ac55
A
1385 vnode_waitforwrites(NFSTOV(np), VNODE_ASYNC_THROTTLE, 0, 0, "nfs_buf_write_delayed");
1386
1387 /* the file is in a modified state, so make sure the flag's set */
b0d623f7 1388 nfs_node_lock_force(np);
2d21ac55 1389 np->n_flag |= NMODIFIED;
b0d623f7 1390 nfs_node_unlock(np);
55e303ae
A
1391
1392 /*
2d21ac55
A
1393 * If we have too many delayed write buffers,
1394 * just fall back to doing the async write.
55e303ae 1395 */
0a7de745 1396 if (nfs_nbdwrite < 0) {
55e303ae 1397 panic("nfs_buf_write_delayed: Negative nfs_nbdwrite");
0a7de745 1398 }
2d21ac55 1399 if (nfs_nbdwrite > NFS_A_LOT_OF_DELAYED_WRITES) {
55e303ae
A
1400 /* issue async write */
1401 SET(bp->nb_flags, NB_ASYNC);
1402 nfs_buf_write(bp);
1403 FSDBG_BOT(551, bp, NBOFF(bp), bp->nb_flags, bp->nb_error);
1404 return;
1405 }
2d21ac55 1406
55e303ae
A
1407 /* Otherwise, the "write" is done, so mark and release the buffer. */
1408 SET(bp->nb_flags, NB_DONE);
483a1d10 1409 nfs_buf_release(bp, 1);
55e303ae
A
1410 FSDBG_BOT(551, bp, NBOFF(bp), bp->nb_flags, 0);
1411 return;
1412}
1413
8f6c56a5
A
1414/*
1415 * Check that a "needcommit" buffer can still be committed.
1416 * If the write verifier has changed, we need to clear the
1417 * the needcommit flag.
1418 */
1419void
2d21ac55 1420nfs_buf_check_write_verifier(nfsnode_t np, struct nfsbuf *bp)
8f6c56a5
A
1421{
1422 struct nfsmount *nmp;
1423
0a7de745 1424 if (!ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
8f6c56a5 1425 return;
0a7de745 1426 }
8f6c56a5 1427
2d21ac55 1428 nmp = NFSTONMP(np);
0a7de745 1429 if (nfs_mount_gone(nmp)) {
2d21ac55 1430 return;
0a7de745
A
1431 }
1432 if (!ISSET(bp->nb_flags, NB_STALEWVERF) && (bp->nb_verf == nmp->nm_verf)) {
8f6c56a5 1433 return;
0a7de745 1434 }
8f6c56a5 1435
2d21ac55
A
1436 /* write verifier changed, clear commit/wverf flags */
1437 CLR(bp->nb_flags, (NB_NEEDCOMMIT | NB_STALEWVERF));
1438 bp->nb_verf = 0;
b0d623f7 1439 nfs_node_lock_force(np);
8f6c56a5
A
1440 np->n_needcommitcnt--;
1441 CHECK_NEEDCOMMITCNT(np);
b0d623f7 1442 nfs_node_unlock(np);
8f6c56a5
A
1443}
1444
91447636
A
1445/*
1446 * add a reference to a buffer so it doesn't disappear while being used
1447 * (must be called with nfs_buf_mutex held)
1448 */
1449void
1450nfs_buf_refget(struct nfsbuf *bp)
1451{
cb323159 1452 os_ref_retain_locked(&bp->nb_refs);
91447636
A
1453}
1454/*
1455 * release a reference on a buffer
1456 * (must be called with nfs_buf_mutex held)
1457 */
1458void
1459nfs_buf_refrele(struct nfsbuf *bp)
1460{
cb323159 1461 (void) os_ref_release_locked(&bp->nb_refs);
91447636
A
1462}
1463
1464/*
1465 * mark a particular buffer as BUSY
1466 * (must be called with nfs_buf_mutex held)
1467 */
1468errno_t
1469nfs_buf_acquire(struct nfsbuf *bp, int flags, int slpflag, int slptimeo)
1470{
1471 errno_t error;
1472 struct timespec ts;
1473
1474 if (ISSET(bp->nb_lflags, NBL_BUSY)) {
0a7de745 1475 /*
b0d623f7 1476 * since the lck_mtx_lock may block, the buffer
91447636
A
1477 * may become BUSY, so we need to recheck for
1478 * a NOWAIT request
1479 */
0a7de745
A
1480 if (flags & NBAC_NOWAIT) {
1481 return EBUSY;
1482 }
1483 SET(bp->nb_lflags, NBL_WANTED);
91447636 1484
0a7de745 1485 ts.tv_sec = (slptimeo / 100);
2d21ac55
A
1486 /* the hz value is 100; which leads to 10ms */
1487 ts.tv_nsec = (slptimeo % 100) * 10 * NSEC_PER_USEC * 1000;
91447636 1488
c3c9b80d 1489 error = msleep(bp, &nfs_buf_mutex, slpflag | (PRIBIO + 1),
0a7de745
A
1490 "nfs_buf_acquire", &ts);
1491 if (error) {
1492 return error;
1493 }
1494 return EAGAIN;
1495 }
1496 if (flags & NBAC_REMOVE) {
1497 nfs_buf_remfree(bp);
91447636 1498 }
91447636
A
1499 SET(bp->nb_lflags, NBL_BUSY);
1500
0a7de745 1501 return 0;
91447636
A
1502}
1503
1504/*
1505 * simply drop the BUSY status of a buffer
1506 * (must be called with nfs_buf_mutex held)
1507 */
1508void
1509nfs_buf_drop(struct nfsbuf *bp)
1510{
1511 int need_wakeup = 0;
1512
0a7de745 1513 if (!ISSET(bp->nb_lflags, NBL_BUSY)) {
91447636 1514 panic("nfs_buf_drop: buffer not busy!");
0a7de745 1515 }
91447636 1516 if (ISSET(bp->nb_lflags, NBL_WANTED)) {
0a7de745 1517 /* delay the actual wakeup until after we clear NBL_BUSY */
91447636
A
1518 need_wakeup = 1;
1519 }
1520 /* Unlock the buffer. */
1521 CLR(bp->nb_lflags, (NBL_BUSY | NBL_WANTED));
1522
0a7de745
A
1523 if (need_wakeup) {
1524 wakeup(bp);
1525 }
91447636
A
1526}
1527
1528/*
1529 * prepare for iterating over an nfsnode's buffer list
1530 * this lock protects the queue manipulation
1531 * (must be called with nfs_buf_mutex held)
1532 */
1533int
2d21ac55 1534nfs_buf_iterprepare(nfsnode_t np, struct nfsbuflists *iterheadp, int flags)
91447636
A
1535{
1536 struct nfsbuflists *listheadp;
1537
0a7de745 1538 if (flags & NBI_DIRTY) {
91447636 1539 listheadp = &np->n_dirtyblkhd;
0a7de745 1540 } else {
91447636 1541 listheadp = &np->n_cleanblkhd;
0a7de745 1542 }
91447636
A
1543
1544 if ((flags & NBI_NOWAIT) && (np->n_bufiterflags & NBI_ITER)) {
0a7de745
A
1545 LIST_INIT(iterheadp);
1546 return EWOULDBLOCK;
91447636
A
1547 }
1548
0a7de745
A
1549 while (np->n_bufiterflags & NBI_ITER) {
1550 np->n_bufiterflags |= NBI_ITERWANT;
c3c9b80d 1551 msleep(&np->n_bufiterflags, &nfs_buf_mutex, 0, "nfs_buf_iterprepare", NULL);
91447636
A
1552 }
1553 if (LIST_EMPTY(listheadp)) {
0a7de745
A
1554 LIST_INIT(iterheadp);
1555 return EINVAL;
91447636
A
1556 }
1557 np->n_bufiterflags |= NBI_ITER;
1558
1559 iterheadp->lh_first = listheadp->lh_first;
0a7de745 1560 listheadp->lh_first->nb_vnbufs.le_prev = &iterheadp->lh_first;
91447636
A
1561 LIST_INIT(listheadp);
1562
0a7de745 1563 return 0;
91447636
A
1564}
1565
1566/*
2d21ac55 1567 * clean up after iterating over an nfsnode's buffer list
91447636
A
1568 * this lock protects the queue manipulation
1569 * (must be called with nfs_buf_mutex held)
1570 */
1571void
2d21ac55 1572nfs_buf_itercomplete(nfsnode_t np, struct nfsbuflists *iterheadp, int flags)
91447636
A
1573{
1574 struct nfsbuflists * listheadp;
1575 struct nfsbuf *bp;
1576
0a7de745 1577 if (flags & NBI_DIRTY) {
91447636 1578 listheadp = &np->n_dirtyblkhd;
0a7de745 1579 } else {
91447636 1580 listheadp = &np->n_cleanblkhd;
0a7de745 1581 }
91447636
A
1582
1583 while (!LIST_EMPTY(iterheadp)) {
1584 bp = LIST_FIRST(iterheadp);
1585 LIST_REMOVE(bp, nb_vnbufs);
1586 LIST_INSERT_HEAD(listheadp, bp, nb_vnbufs);
1587 }
1588
1589 np->n_bufiterflags &= ~NBI_ITER;
1590 if (np->n_bufiterflags & NBI_ITERWANT) {
1591 np->n_bufiterflags &= ~NBI_ITERWANT;
1592 wakeup(&np->n_bufiterflags);
1593 }
1594}
1595
1c79356b
A
1596
1597/*
2d21ac55 1598 * Read an NFS buffer for a file.
1c79356b
A
1599 */
1600int
2d21ac55 1601nfs_buf_read(struct nfsbuf *bp)
1c79356b 1602{
2d21ac55
A
1603 int error = 0;
1604 nfsnode_t np;
1605 thread_t thd;
1606 kauth_cred_t cred;
55e303ae 1607
2d21ac55
A
1608 np = bp->nb_np;
1609 cred = bp->nb_rcred;
0a7de745 1610 if (IS_VALID_CRED(cred)) {
2d21ac55 1611 kauth_cred_ref(cred);
0a7de745 1612 }
2d21ac55 1613 thd = ISSET(bp->nb_flags, NB_ASYNC) ? NULL : current_thread();
1c79356b 1614
2d21ac55 1615 /* sanity checks */
0a7de745 1616 if (!ISSET(bp->nb_flags, NB_READ)) {
2d21ac55 1617 panic("nfs_buf_read: !NB_READ");
0a7de745
A
1618 }
1619 if (ISSET(bp->nb_flags, NB_DONE)) {
2d21ac55 1620 CLR(bp->nb_flags, NB_DONE);
0a7de745 1621 }
91447636 1622
2d21ac55 1623 NFS_BUF_MAP(bp);
0c530ab8 1624
316670eb 1625 OSAddAtomic64(1, &nfsstats.read_bios);
2d21ac55
A
1626
1627 error = nfs_buf_read_rpc(bp, thd, cred);
1c79356b 1628 /*
2d21ac55
A
1629 * For async I/O, the callbacks will finish up the
1630 * read. Otherwise, the read has already been finished.
1c79356b 1631 */
2d21ac55 1632
0a7de745 1633 if (IS_VALID_CRED(cred)) {
2d21ac55 1634 kauth_cred_unref(&cred);
0a7de745
A
1635 }
1636 return error;
2d21ac55
A
1637}
1638
1639/*
1640 * finish the reading of a buffer
1641 */
1642void
1643nfs_buf_read_finish(struct nfsbuf *bp)
1644{
1645 nfsnode_t np = bp->nb_np;
1646 struct nfsmount *nmp;
1647
1648 if (!ISSET(bp->nb_flags, NB_ERROR)) {
1649 /* update valid range */
1650 bp->nb_validoff = 0;
1651 bp->nb_validend = bp->nb_endio;
f427ee49 1652 if (bp->nb_endio < bp->nb_bufsize) {
2d21ac55
A
1653 /*
1654 * The read may be short because we have unflushed writes
1655 * that are extending the file size and the reads hit the
1656 * (old) EOF on the server. So, just make sure nb_validend
1657 * correctly tracks EOF.
1658 * Note that the missing data should have already been zeroed
1659 * in nfs_buf_read_rpc_finish().
1660 */
1661 off_t boff = NBOFF(bp);
0a7de745 1662 if ((off_t)np->n_size >= (boff + bp->nb_bufsize)) {
2d21ac55 1663 bp->nb_validend = bp->nb_bufsize;
0a7de745 1664 } else if ((off_t)np->n_size >= boff) {
2d21ac55 1665 bp->nb_validend = np->n_size - boff;
0a7de745 1666 } else {
2d21ac55 1667 bp->nb_validend = 0;
0a7de745 1668 }
91447636 1669 }
2d21ac55 1670 if ((nmp = NFSTONMP(np)) && (nmp->nm_vers == NFS_VER2) &&
0a7de745 1671 ((NBOFF(bp) + bp->nb_validend) > 0x100000000LL)) {
2d21ac55 1672 bp->nb_validend = 0x100000000LL - NBOFF(bp);
0a7de745 1673 }
f427ee49 1674 nfs_buf_pgs_get_page_mask(&bp->nb_valid, round_page_64(bp->nb_validend) / PAGE_SIZE);
2d21ac55
A
1675 if (bp->nb_validend & PAGE_MASK) {
1676 /* zero-fill remainder of last page */
6d2010ae 1677 bzero(bp->nb_data + bp->nb_validend, PAGE_SIZE - (bp->nb_validend & PAGE_MASK));
91447636 1678 }
2d21ac55
A
1679 }
1680 nfs_buf_iodone(bp);
1681}
1682
1683/*
1684 * initiate the NFS READ RPC(s) for a buffer
1685 */
1686int
1687nfs_buf_read_rpc(struct nfsbuf *bp, thread_t thd, kauth_cred_t cred)
1688{
1689 struct nfsmount *nmp;
1690 nfsnode_t np = bp->nb_np;
1691 int error = 0, nfsvers, async;
f427ee49
A
1692 int offset;
1693 uint64_t length, nrpcs;
1694 uint32_t nmrsize;
1695 size_t len;
2d21ac55
A
1696 off_t boff;
1697 struct nfsreq *req;
1698 struct nfsreq_cbinfo cb;
1699
1700 nmp = NFSTONMP(np);
fe8ab488 1701 if (nfs_mount_gone(nmp)) {
2d21ac55
A
1702 bp->nb_error = error = ENXIO;
1703 SET(bp->nb_flags, NB_ERROR);
1704 nfs_buf_iodone(bp);
0a7de745 1705 return error;
2d21ac55
A
1706 }
1707 nfsvers = nmp->nm_vers;
1708 nmrsize = nmp->nm_rsize;
1709
1710 boff = NBOFF(bp);
1711 offset = 0;
1712 length = bp->nb_bufsize;
1713
1714 if (nfsvers == NFS_VER2) {
1715 if (boff > 0xffffffffLL) {
1716 bp->nb_error = error = EFBIG;
1717 SET(bp->nb_flags, NB_ERROR);
1718 nfs_buf_iodone(bp);
0a7de745 1719 return error;
91447636 1720 }
0a7de745 1721 if ((boff + length - 1) > 0xffffffffLL) {
2d21ac55 1722 length = 0x100000000LL - boff;
0a7de745 1723 }
91447636
A
1724 }
1725
2d21ac55
A
1726 /* Note: Can only do async I/O if nfsiods are configured. */
1727 async = (bp->nb_flags & NB_ASYNC);
1728 cb.rcb_func = async ? nfs_buf_read_rpc_finish : NULL;
1729 cb.rcb_bp = bp;
1730
1731 bp->nb_offio = bp->nb_endio = 0;
1732 bp->nb_rpcs = nrpcs = (length + nmrsize - 1) / nmrsize;
1733 if (async && (nrpcs > 1)) {
1734 SET(bp->nb_flags, NB_MULTASYNCRPC);
1735 } else {
1736 CLR(bp->nb_flags, NB_MULTASYNCRPC);
1c79356b 1737 }
1c79356b 1738
2d21ac55
A
1739 while (length > 0) {
1740 if (ISSET(bp->nb_flags, NB_ERROR)) {
1741 error = bp->nb_error;
91447636 1742 break;
2d21ac55 1743 }
f427ee49
A
1744 len = (length > nmrsize) ? nmrsize : (uint32_t)length;
1745 cb.rcb_args.offset = offset;
1746 cb.rcb_args.length = len;
cb323159 1747#if CONFIG_NFS4
0a7de745 1748 if (nmp->nm_vers >= NFS_VER4) {
f427ee49 1749 cb.rcb_args.stategenid = nmp->nm_stategenid;
0a7de745 1750 }
cb323159 1751#endif
2d21ac55
A
1752 req = NULL;
1753 error = nmp->nm_funcs->nf_read_rpc_async(np, boff + offset, len, thd, cred, &cb, &req);
0a7de745 1754 if (error) {
1c79356b 1755 break;
0a7de745 1756 }
2d21ac55
A
1757 offset += len;
1758 length -= len;
0a7de745 1759 if (async) {
2d21ac55 1760 continue;
0a7de745 1761 }
2d21ac55
A
1762 nfs_buf_read_rpc_finish(req);
1763 if (ISSET(bp->nb_flags, NB_ERROR)) {
1764 error = bp->nb_error;
1765 break;
1766 }
1767 }
55e303ae 1768
2d21ac55 1769 if (length > 0) {
55e303ae 1770 /*
2d21ac55
A
1771 * Something bad happened while trying to send the RPC(s).
1772 * Wait for any outstanding requests to complete.
55e303ae 1773 */
2d21ac55
A
1774 bp->nb_error = error;
1775 SET(bp->nb_flags, NB_ERROR);
1776 if (ISSET(bp->nb_flags, NB_MULTASYNCRPC)) {
1777 nrpcs = (length + nmrsize - 1) / nmrsize;
c3c9b80d 1778 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
1779 bp->nb_rpcs -= nrpcs;
1780 if (bp->nb_rpcs == 0) {
1781 /* No RPCs left, so the buffer's done */
c3c9b80d 1782 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
1783 nfs_buf_iodone(bp);
1784 } else {
1785 /* wait for the last RPC to mark it done */
0a7de745 1786 while (bp->nb_rpcs > 0) {
c3c9b80d 1787 msleep(&bp->nb_rpcs, &nfs_buf_mutex, 0,
0a7de745
A
1788 "nfs_buf_read_rpc_cancel", NULL);
1789 }
c3c9b80d 1790 lck_mtx_unlock(&nfs_buf_mutex);
55e303ae 1791 }
2d21ac55
A
1792 } else {
1793 nfs_buf_iodone(bp);
55e303ae 1794 }
2d21ac55 1795 }
55e303ae 1796
0a7de745 1797 return error;
2d21ac55 1798}
1c79356b 1799
2d21ac55
A
1800/*
1801 * finish up an NFS READ RPC on a buffer
1802 */
1803void
1804nfs_buf_read_rpc_finish(struct nfsreq *req)
1805{
1806 struct nfsmount *nmp;
f427ee49 1807 size_t rlen, length;
2d21ac55
A
1808 struct nfsreq_cbinfo cb;
1809 struct nfsbuf *bp;
f427ee49
A
1810 int error = 0, nfsvers, eof = 0, multasyncrpc, finished;
1811 off_t offset;
2d21ac55
A
1812 void *wakeme = NULL;
1813 struct nfsreq *rreq = NULL;
1814 nfsnode_t np;
1815 thread_t thd;
1816 kauth_cred_t cred;
b0d623f7 1817 uio_t auio;
0a7de745 1818 char uio_buf[UIO_SIZEOF(1)];
2d21ac55
A
1819
1820finish:
1821 np = req->r_np;
1822 thd = req->r_thread;
1823 cred = req->r_cred;
0a7de745 1824 if (IS_VALID_CRED(cred)) {
2d21ac55 1825 kauth_cred_ref(cred);
0a7de745 1826 }
2d21ac55
A
1827 cb = req->r_callback;
1828 bp = cb.rcb_bp;
0a7de745 1829 if (cb.rcb_func) { /* take an extra reference on the nfsreq in case we want to resend it later due to grace error */
6d2010ae 1830 nfs_request_ref(req, 0);
0a7de745 1831 }
2d21ac55
A
1832
1833 nmp = NFSTONMP(np);
fe8ab488 1834 if (nfs_mount_gone(nmp)) {
2d21ac55
A
1835 SET(bp->nb_flags, NB_ERROR);
1836 bp->nb_error = error = ENXIO;
1837 }
1838 if (error || ISSET(bp->nb_flags, NB_ERROR)) {
1839 /* just drop it */
1840 nfs_request_async_cancel(req);
1841 goto out;
1842 }
1843
1844 nfsvers = nmp->nm_vers;
f427ee49
A
1845 offset = cb.rcb_args.offset;
1846 rlen = length = cb.rcb_args.length;
2d21ac55 1847
b0d623f7 1848 auio = uio_createwithbuffer(1, NBOFF(bp) + offset, UIO_SYSSPACE,
0a7de745 1849 UIO_READ, &uio_buf, sizeof(uio_buf));
b0d623f7 1850 uio_addiov(auio, CAST_USER_ADDR_T(bp->nb_data + offset), length);
2d21ac55
A
1851
1852 /* finish the RPC */
b0d623f7 1853 error = nmp->nm_funcs->nf_read_rpc_async_finish(np, req, auio, &rlen, &eof);
2d21ac55
A
1854 if ((error == EINPROGRESS) && cb.rcb_func) {
1855 /* async request restarted */
0a7de745 1856 if (cb.rcb_func) {
6d2010ae 1857 nfs_request_rele(req);
0a7de745
A
1858 }
1859 if (IS_VALID_CRED(cred)) {
2d21ac55 1860 kauth_cred_unref(&cred);
0a7de745 1861 }
2d21ac55
A
1862 return;
1863 }
cb323159 1864#if CONFIG_NFS4
b0d623f7
A
1865 if ((nmp->nm_vers >= NFS_VER4) && nfs_mount_state_error_should_restart(error) && !ISSET(bp->nb_flags, NB_ERROR)) {
1866 lck_mtx_lock(&nmp->nm_lock);
f427ee49 1867 if ((error != NFSERR_OLD_STATEID) && (error != NFSERR_GRACE) && (cb.rcb_args.stategenid == nmp->nm_stategenid)) {
6d2010ae 1868 NP(np, "nfs_buf_read_rpc_finish: error %d @ 0x%llx, 0x%x 0x%x, initiating recovery",
f427ee49 1869 error, NBOFF(bp) + offset, cb.rcb_args.stategenid, nmp->nm_stategenid);
6d2010ae 1870 nfs_need_recover(nmp, error);
b0d623f7
A
1871 }
1872 lck_mtx_unlock(&nmp->nm_lock);
6d2010ae
A
1873 if (np->n_flag & NREVOKE) {
1874 error = EIO;
1875 } else {
1876 if (error == NFSERR_GRACE) {
1877 if (cb.rcb_func) {
1878 /*
1879 * For an async I/O request, handle a grace delay just like
1880 * jukebox errors. Set the resend time and queue it up.
1881 */
1882 struct timeval now;
1883 if (req->r_nmrep.nmc_mhead) {
1884 mbuf_freem(req->r_nmrep.nmc_mhead);
1885 req->r_nmrep.nmc_mhead = NULL;
1886 }
1887 req->r_error = 0;
1888 microuptime(&now);
1889 lck_mtx_lock(&req->r_mtx);
1890 req->r_resendtime = now.tv_sec + 2;
1891 req->r_xid = 0; // get a new XID
1892 req->r_flags |= R_RESTART;
1893 req->r_start = 0;
1894 nfs_asyncio_resend(req);
1895 lck_mtx_unlock(&req->r_mtx);
0a7de745 1896 if (IS_VALID_CRED(cred)) {
6d2010ae 1897 kauth_cred_unref(&cred);
0a7de745 1898 }
6d2010ae
A
1899 /* Note: nfsreq reference taken will be dropped later when finished */
1900 return;
1901 }
1902 /* otherwise, just pause a couple seconds and retry */
0a7de745 1903 tsleep(&nmp->nm_state, (PZERO - 1), "nfsgrace", 2 * hz);
6d2010ae
A
1904 }
1905 if (!(error = nfs_mount_state_wait_for_recovery(nmp))) {
1906 rlen = 0;
1907 goto readagain;
1908 }
b0d623f7
A
1909 }
1910 }
cb323159 1911#endif
2d21ac55
A
1912 if (error) {
1913 SET(bp->nb_flags, NB_ERROR);
1914 bp->nb_error = error;
1915 goto out;
1916 }
1917
0a7de745 1918 if ((rlen > 0) && (bp->nb_endio < (offset + (int)rlen))) {
2d21ac55 1919 bp->nb_endio = offset + rlen;
0a7de745 1920 }
2d21ac55
A
1921
1922 if ((nfsvers == NFS_VER2) || eof || (rlen == 0)) {
1923 /* zero out the remaining data (up to EOF) */
1924 off_t rpcrem, eofrem, rem;
1925 rpcrem = (length - rlen);
1926 eofrem = np->n_size - (NBOFF(bp) + offset + rlen);
1927 rem = (rpcrem < eofrem) ? rpcrem : eofrem;
0a7de745 1928 if (rem > 0) {
f427ee49 1929 NFS_BZERO(bp->nb_data + offset + rlen, rem);
0a7de745 1930 }
f427ee49 1931 } else if ((rlen < length) && !ISSET(bp->nb_flags, NB_ERROR)) {
2d21ac55
A
1932 /*
1933 * short read
1934 *
1935 * We haven't hit EOF and we didn't get all the data
1936 * requested, so we need to issue another read for the rest.
1937 * (Don't bother if the buffer already hit an error.)
1938 */
cb323159 1939#if CONFIG_NFS4
b0d623f7 1940readagain:
cb323159 1941#endif
2d21ac55
A
1942 offset += rlen;
1943 length -= rlen;
f427ee49
A
1944 cb.rcb_args.offset = offset;
1945 cb.rcb_args.length = length;
cb323159 1946#if CONFIG_NFS4
0a7de745 1947 if (nmp->nm_vers >= NFS_VER4) {
f427ee49 1948 cb.rcb_args.stategenid = nmp->nm_stategenid;
0a7de745 1949 }
cb323159 1950#endif
b0d623f7 1951 error = nmp->nm_funcs->nf_read_rpc_async(np, NBOFF(bp) + offset, length, thd, cred, &cb, &rreq);
2d21ac55 1952 if (!error) {
0a7de745 1953 if (IS_VALID_CRED(cred)) {
2d21ac55 1954 kauth_cred_unref(&cred);
0a7de745 1955 }
2d21ac55
A
1956 if (!cb.rcb_func) {
1957 /* if !async we'll need to wait for this RPC to finish */
1958 req = rreq;
b0d623f7 1959 rreq = NULL;
2d21ac55
A
1960 goto finish;
1961 }
6d2010ae 1962 nfs_request_rele(req);
2d21ac55
A
1963 /*
1964 * We're done here.
1965 * Outstanding RPC count is unchanged.
1966 * Callback will be called when RPC is done.
1967 */
1968 return;
1969 }
1970 SET(bp->nb_flags, NB_ERROR);
1971 bp->nb_error = error;
1972 }
1973
1974out:
0a7de745 1975 if (cb.rcb_func) {
6d2010ae 1976 nfs_request_rele(req);
0a7de745
A
1977 }
1978 if (IS_VALID_CRED(cred)) {
2d21ac55 1979 kauth_cred_unref(&cred);
0a7de745 1980 }
2d21ac55
A
1981
1982 /*
1983 * Decrement outstanding RPC count on buffer
1984 * and call nfs_buf_read_finish on last RPC.
1985 *
1986 * (Note: when there are multiple async RPCs issued for a
1987 * buffer we need nfs_buffer_mutex to avoid problems when
1988 * aborting a partially-initiated set of RPCs)
1989 */
1990
1991 multasyncrpc = ISSET(bp->nb_flags, NB_MULTASYNCRPC);
0a7de745 1992 if (multasyncrpc) {
c3c9b80d 1993 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 1994 }
2d21ac55
A
1995
1996 bp->nb_rpcs--;
1997 finished = (bp->nb_rpcs == 0);
1998
0a7de745 1999 if (multasyncrpc) {
c3c9b80d 2000 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 2001 }
2d21ac55
A
2002
2003 if (finished) {
0a7de745 2004 if (multasyncrpc) {
2d21ac55 2005 wakeme = &bp->nb_rpcs;
0a7de745 2006 }
2d21ac55 2007 nfs_buf_read_finish(bp);
0a7de745 2008 if (wakeme) {
2d21ac55 2009 wakeup(wakeme);
0a7de745 2010 }
2d21ac55
A
2011 }
2012}
2013
2014/*
2015 * Do buffer readahead.
2016 * Initiate async I/O to read buffers not in cache.
2017 */
b0d623f7 2018int
2d21ac55
A
2019nfs_buf_readahead(nfsnode_t np, int ioflag, daddr64_t *rabnp, daddr64_t lastrabn, thread_t thd, kauth_cred_t cred)
2020{
2021 struct nfsmount *nmp = NFSTONMP(np);
2022 struct nfsbuf *bp;
6d2010ae
A
2023 int error = 0;
2024 uint32_t nra;
2d21ac55 2025
0a7de745
A
2026 if (nfs_mount_gone(nmp)) {
2027 return ENXIO;
2028 }
2029 if (nmp->nm_readahead <= 0) {
2030 return 0;
2031 }
2032 if (*rabnp > lastrabn) {
2033 return 0;
2034 }
2d21ac55
A
2035
2036 for (nra = 0; (nra < nmp->nm_readahead) && (*rabnp <= lastrabn); nra++, *rabnp = *rabnp + 1) {
2037 /* check if block exists and is valid. */
b0d623f7
A
2038 if ((*rabnp * nmp->nm_biosize) >= (off_t)np->n_size) {
2039 /* stop reading ahead if we're beyond EOF */
2040 *rabnp = lastrabn;
2041 break;
2042 }
0a7de745
A
2043 error = nfs_buf_get(np, *rabnp, nmp->nm_biosize, thd, NBLK_READ | NBLK_NOWAIT, &bp);
2044 if (error) {
2d21ac55 2045 break;
0a7de745 2046 }
b0d623f7
A
2047 nfs_node_lock_force(np);
2048 np->n_lastrahead = *rabnp;
2049 nfs_node_unlock(np);
0a7de745 2050 if (!bp) {
2d21ac55 2051 continue;
0a7de745 2052 }
2d21ac55 2053 if ((ioflag & IO_NOCACHE) && ISSET(bp->nb_flags, NB_CACHE) &&
f427ee49 2054 !nfs_buf_pgs_is_set(&bp->nb_dirty) && !ISSET(bp->nb_flags, (NB_DELWRI | NB_NCRDAHEAD))) {
2d21ac55 2055 CLR(bp->nb_flags, NB_CACHE);
f427ee49 2056 NBPGS_ERASE(&bp->nb_valid);
2d21ac55
A
2057 bp->nb_validoff = bp->nb_validend = -1;
2058 }
f427ee49 2059 if ((bp->nb_dirtyend <= 0) && !nfs_buf_pgs_is_set(&bp->nb_dirty) &&
0a7de745
A
2060 !ISSET(bp->nb_flags, (NB_CACHE | NB_DELWRI))) {
2061 SET(bp->nb_flags, (NB_READ | NB_ASYNC));
2062 if (ioflag & IO_NOCACHE) {
2d21ac55 2063 SET(bp->nb_flags, NB_NCRDAHEAD);
0a7de745 2064 }
2d21ac55
A
2065 if (!IS_VALID_CRED(bp->nb_rcred) && IS_VALID_CRED(cred)) {
2066 kauth_cred_ref(cred);
2067 bp->nb_rcred = cred;
2068 }
0a7de745 2069 if ((error = nfs_buf_read(bp))) {
2d21ac55 2070 break;
0a7de745 2071 }
2d21ac55
A
2072 continue;
2073 }
2074 nfs_buf_release(bp, 1);
2075 }
0a7de745 2076 return error;
2d21ac55
A
2077}
2078
2079/*
b0d623f7 2080 * NFS buffer I/O for reading files.
2d21ac55
A
2081 */
2082int
b0d623f7 2083nfs_bioread(nfsnode_t np, uio_t uio, int ioflag, vfs_context_t ctx)
2d21ac55
A
2084{
2085 vnode_t vp = NFSTOV(np);
2086 struct nfsbuf *bp = NULL;
2d21ac55 2087 struct nfsmount *nmp = VTONMP(vp);
b0d623f7 2088 daddr64_t lbn, rabn = 0, lastrabn, maxrabn = -1;
f427ee49
A
2089 off_t diff, on = 0, n = 0;
2090 int error = 0, n32;
b0d623f7 2091 int nfsvers, biosize, modified, readaheads = 0;
2d21ac55
A
2092 thread_t thd;
2093 kauth_cred_t cred;
b0d623f7 2094 int64_t io_resid;
2d21ac55 2095
b0d623f7 2096 FSDBG_TOP(514, np, uio_offset(uio), uio_resid(uio), ioflag);
2d21ac55
A
2097
2098 nfsvers = nmp->nm_vers;
2099 biosize = nmp->nm_biosize;
2100 thd = vfs_context_thread(ctx);
2101 cred = vfs_context_ucred(ctx);
2102
b0d623f7
A
2103 if (vnode_vtype(vp) != VREG) {
2104 printf("nfs_bioread: type %x unexpected\n", vnode_vtype(vp));
2d21ac55 2105 FSDBG_BOT(514, np, 0xd1e0016, 0, EINVAL);
0a7de745 2106 return EINVAL;
2d21ac55
A
2107 }
2108
2109 /*
b0d623f7 2110 * For NFS, cache consistency can only be maintained approximately.
2d21ac55
A
2111 * Although RFC1094 does not specify the criteria, the following is
2112 * believed to be compatible with the reference port.
0a7de745 2113 *
b0d623f7
A
2114 * If the file has changed since the last read RPC or you have
2115 * written to the file, you may have lost data cache consistency
2116 * with the server. So, check for a change, and flush all of the
2117 * file's data out of the cache.
2d21ac55 2118 * NB: This implies that cache data can be read when up to
b0d623f7
A
2119 * NFS_MAXATTRTIMO seconds out of date. If you find that you
2120 * need current attributes, nfs_getattr() can be forced to fetch
2121 * new attributes (via NATTRINVALIDATE() or NGA_UNCACHED).
2d21ac55
A
2122 */
2123
0a7de745 2124 if (ISSET(np->n_flag, NUPDATESIZE)) {
2d21ac55 2125 nfs_data_update_size(np, 0);
0a7de745 2126 }
2d21ac55 2127
b0d623f7 2128 if ((error = nfs_node_lock(np))) {
2d21ac55 2129 FSDBG_BOT(514, np, 0xd1e0222, 0, error);
0a7de745 2130 return error;
2d21ac55
A
2131 }
2132
2133 if (np->n_flag & NNEEDINVALIDATE) {
2134 np->n_flag &= ~NNEEDINVALIDATE;
b0d623f7 2135 nfs_node_unlock(np);
0a7de745
A
2136 error = nfs_vinvalbuf(vp, V_SAVE | V_IGNORE_WRITEERR, ctx, 1);
2137 if (!error) {
b0d623f7 2138 error = nfs_node_lock(np);
0a7de745 2139 }
b0d623f7 2140 if (error) {
2d21ac55 2141 FSDBG_BOT(514, np, 0xd1e0322, 0, error);
0a7de745 2142 return error;
2d21ac55
A
2143 }
2144 }
2145
b0d623f7
A
2146 modified = (np->n_flag & NMODIFIED);
2147 nfs_node_unlock(np);
2148 /* nfs_getattr() will check changed and purge caches */
6d2010ae 2149 error = nfs_getattr(np, NULL, ctx, modified ? NGA_UNCACHED : NGA_CACHED);
b0d623f7
A
2150 if (error) {
2151 FSDBG_BOT(514, np, 0xd1e0004, 0, error);
0a7de745 2152 return error;
2d21ac55
A
2153 }
2154
b0d623f7
A
2155 if (uio_resid(uio) == 0) {
2156 FSDBG_BOT(514, np, 0xd1e0001, 0, 0);
0a7de745 2157 return 0;
b0d623f7
A
2158 }
2159 if (uio_offset(uio) < 0) {
2160 FSDBG_BOT(514, np, 0xd1e0002, 0, EINVAL);
0a7de745 2161 return EINVAL;
b0d623f7 2162 }
2d21ac55 2163
b0d623f7
A
2164 /*
2165 * set up readahead - which may be limited by:
2166 * + current request length (for IO_NOCACHE)
2167 * + readahead setting
2168 * + file size
2169 */
2170 if (nmp->nm_readahead > 0) {
2171 off_t end = uio_offset(uio) + uio_resid(uio);
0a7de745 2172 if (end > (off_t)np->n_size) {
b0d623f7 2173 end = np->n_size;
0a7de745 2174 }
b0d623f7
A
2175 rabn = uio_offset(uio) / biosize;
2176 maxrabn = (end - 1) / biosize;
2177 nfs_node_lock_force(np);
2178 if (!(ioflag & IO_NOCACHE) &&
0a7de745 2179 (!rabn || (rabn == np->n_lastread) || (rabn == (np->n_lastread + 1)))) {
b0d623f7 2180 maxrabn += nmp->nm_readahead;
0a7de745
A
2181 if ((maxrabn * biosize) >= (off_t)np->n_size) {
2182 maxrabn = ((off_t)np->n_size - 1) / biosize;
2183 }
b0d623f7 2184 }
0a7de745 2185 if (maxrabn < np->n_lastrahead) {
b0d623f7 2186 np->n_lastrahead = -1;
0a7de745
A
2187 }
2188 if (rabn < np->n_lastrahead) {
b0d623f7 2189 rabn = np->n_lastrahead + 1;
0a7de745 2190 }
b0d623f7
A
2191 nfs_node_unlock(np);
2192 } else {
2193 rabn = maxrabn = 0;
2d21ac55
A
2194 }
2195
2196 do {
b0d623f7
A
2197 nfs_data_lock(np, NFS_DATA_LOCK_SHARED);
2198 lbn = uio_offset(uio) / biosize;
2d21ac55
A
2199
2200 /*
2201 * Copy directly from any cached pages without grabbing the bufs.
b0d623f7
A
2202 * (If we are NOCACHE and we've issued readahead requests, we need
2203 * to grab the NB_NCRDAHEAD bufs to drop them.)
2d21ac55 2204 */
b0d623f7
A
2205 if ((!(ioflag & IO_NOCACHE) || !readaheads) &&
2206 ((uio->uio_segflg == UIO_USERSPACE32 ||
0a7de745
A
2207 uio->uio_segflg == UIO_USERSPACE64 ||
2208 uio->uio_segflg == UIO_USERSPACE))) {
b0d623f7
A
2209 io_resid = uio_resid(uio);
2210 diff = np->n_size - uio_offset(uio);
0a7de745 2211 if (diff < io_resid) {
2d21ac55 2212 io_resid = diff;
0a7de745 2213 }
2d21ac55 2214 if (io_resid > 0) {
f427ee49 2215 int count = (io_resid > INT_MAX) ? INT_MAX : (int)io_resid;
b0d623f7 2216 error = cluster_copy_ubc_data(vp, uio, &count, 0);
91447636 2217 if (error) {
2d21ac55 2218 nfs_data_unlock(np);
b0d623f7 2219 FSDBG_BOT(514, np, uio_offset(uio), 0xcacefeed, error);
0a7de745 2220 return error;
91447636 2221 }
2d21ac55
A
2222 }
2223 /* count any biocache reads that we just copied directly */
0a7de745 2224 if (lbn != (uio_offset(uio) / biosize)) {
f427ee49 2225 OSAddAtomic64(NFS_ROUND_BLOCK(uio_offset(uio), biosize) - lbn, &nfsstats.biocache_reads);
b0d623f7 2226 FSDBG(514, np, 0xcacefeed, uio_offset(uio), error);
2d21ac55
A
2227 }
2228 }
2229
b0d623f7
A
2230 lbn = uio_offset(uio) / biosize;
2231 on = uio_offset(uio) % biosize;
2232 nfs_node_lock_force(np);
2233 np->n_lastread = (uio_offset(uio) - 1) / biosize;
2234 nfs_node_unlock(np);
2d21ac55 2235
6d2010ae
A
2236 if ((uio_resid(uio) <= 0) || (uio_offset(uio) >= (off_t)np->n_size)) {
2237 nfs_data_unlock(np);
2238 FSDBG_BOT(514, np, uio_offset(uio), uio_resid(uio), 0xaaaaaaaa);
0a7de745 2239 return 0;
6d2010ae
A
2240 }
2241
2d21ac55 2242 /* adjust readahead block number, if necessary */
0a7de745 2243 if (rabn < lbn) {
2d21ac55 2244 rabn = lbn;
0a7de745 2245 }
2d21ac55
A
2246 lastrabn = MIN(maxrabn, lbn + nmp->nm_readahead);
2247 if (rabn <= lastrabn) { /* start readaheads */
2248 error = nfs_buf_readahead(np, ioflag, &rabn, lastrabn, thd, cred);
2249 if (error) {
2250 nfs_data_unlock(np);
2251 FSDBG_BOT(514, np, 0xd1e000b, 1, error);
0a7de745 2252 return error;
55e303ae 2253 }
b0d623f7 2254 readaheads = 1;
f427ee49
A
2255 OSAddAtomic64(rabn - lbn, &nfsstats.biocache_reads);
2256 } else {
2257 OSAddAtomic64(1, &nfsstats.biocache_reads);
1c79356b
A
2258 }
2259
2260 /*
2261 * If the block is in the cache and has the required data
2262 * in a valid region, just copy it out.
2263 * Otherwise, get the block and write back/read in,
2264 * as required.
2265 */
2266again:
b0d623f7
A
2267 io_resid = uio_resid(uio);
2268 n = (io_resid > (biosize - on)) ? (biosize - on) : io_resid;
2269 diff = np->n_size - uio_offset(uio);
0a7de745 2270 if (diff < n) {
1c79356b 2271 n = diff;
0a7de745 2272 }
55e303ae 2273
2d21ac55 2274 error = nfs_buf_get(np, lbn, biosize, thd, NBLK_READ, &bp);
91447636 2275 if (error) {
2d21ac55
A
2276 nfs_data_unlock(np);
2277 FSDBG_BOT(514, np, 0xd1e000c, 0, error);
0a7de745 2278 return error;
2d21ac55
A
2279 }
2280
2281 if ((ioflag & IO_NOCACHE) && ISSET(bp->nb_flags, NB_CACHE)) {
2282 /*
2283 * IO_NOCACHE found a cached buffer.
2284 * Flush the buffer if it's dirty.
2285 * Invalidate the data if it wasn't just read
2286 * in as part of a "nocache readahead".
2287 */
f427ee49 2288 if (nfs_buf_pgs_is_set(&bp->nb_dirty) || (bp->nb_dirtyend > 0)) {
2d21ac55
A
2289 /* so write the buffer out and try again */
2290 SET(bp->nb_flags, NB_NOCACHE);
2291 goto flushbuffer;
2292 }
b0d623f7 2293 if (ISSET(bp->nb_flags, NB_NCRDAHEAD)) {
2d21ac55 2294 CLR(bp->nb_flags, NB_NCRDAHEAD);
b0d623f7 2295 SET(bp->nb_flags, NB_NOCACHE);
2d21ac55 2296 }
55e303ae
A
2297 }
2298
2299 /* if any pages are valid... */
f427ee49 2300 if (nfs_buf_pgs_is_set(&bp->nb_valid)) {
55e303ae 2301 /* ...check for any invalid pages in the read range */
f427ee49 2302 off_t pg, firstpg, lastpg, dirtypg;
55e303ae 2303 dirtypg = firstpg = lastpg = -1;
0a7de745
A
2304 pg = on / PAGE_SIZE;
2305 while (pg <= (on + n - 1) / PAGE_SIZE) {
2306 if (!NBPGVALID(bp, pg)) {
2307 if (firstpg < 0) {
55e303ae 2308 firstpg = pg;
0a7de745 2309 }
55e303ae 2310 lastpg = pg;
0a7de745 2311 } else if (firstpg >= 0 && dirtypg < 0 && NBPGDIRTY(bp, pg)) {
55e303ae 2312 dirtypg = pg;
0a7de745 2313 }
55e303ae
A
2314 pg++;
2315 }
2316
2317 /* if there are no invalid pages, we're all set */
2318 if (firstpg < 0) {
2319 if (bp->nb_validoff < 0) {
2320 /* valid range isn't set up, so */
2321 /* set it to what we know is valid */
f427ee49
A
2322 bp->nb_validoff = trunc_page_64(on);
2323 bp->nb_validend = round_page_64(on + n);
55e303ae
A
2324 nfs_buf_normalize_valid_range(np, bp);
2325 }
2326 goto buffer_ready;
2327 }
2328
2329 /* there are invalid pages in the read range */
2d21ac55 2330 if (((dirtypg > firstpg) && (dirtypg < lastpg)) ||
0a7de745 2331 (((firstpg * PAGE_SIZE) < bp->nb_dirtyend) && (((lastpg + 1) * PAGE_SIZE) > bp->nb_dirtyoff))) {
2d21ac55 2332 /* there are also dirty page(s) (or range) in the read range, */
55e303ae 2333 /* so write the buffer out and try again */
2d21ac55 2334flushbuffer:
55e303ae
A
2335 CLR(bp->nb_flags, (NB_DONE | NB_ERROR | NB_INVAL));
2336 SET(bp->nb_flags, NB_ASYNC);
2d21ac55 2337 if (!IS_VALID_CRED(bp->nb_wcred)) {
91447636
A
2338 kauth_cred_ref(cred);
2339 bp->nb_wcred = cred;
2340 }
55e303ae
A
2341 error = nfs_buf_write(bp);
2342 if (error) {
2d21ac55
A
2343 nfs_data_unlock(np);
2344 FSDBG_BOT(514, np, 0xd1e000d, 0, error);
0a7de745 2345 return error;
55e303ae 2346 }
1c79356b
A
2347 goto again;
2348 }
f427ee49 2349 if (!nfs_buf_pgs_is_set(&bp->nb_dirty) && bp->nb_dirtyend <= 0 &&
0a7de745 2350 (lastpg - firstpg + 1) > (biosize / PAGE_SIZE) / 2) {
55e303ae
A
2351 /* we need to read in more than half the buffer and the */
2352 /* buffer's not dirty, so just fetch the whole buffer */
f427ee49 2353 NBPGS_ERASE(&bp->nb_valid);
55e303ae
A
2354 } else {
2355 /* read the page range in */
91447636 2356 uio_t auio;
0a7de745
A
2357 char uio_buf[UIO_SIZEOF(1)];
2358
55e303ae 2359 NFS_BUF_MAP(bp);
2d21ac55 2360 auio = uio_createwithbuffer(1, (NBOFF(bp) + firstpg * PAGE_SIZE_64),
0a7de745 2361 UIO_SYSSPACE, UIO_READ, &uio_buf[0], sizeof(uio_buf));
91447636
A
2362 if (!auio) {
2363 error = ENOMEM;
2364 } else {
f427ee49 2365 NFS_UIO_ADDIOV(auio, CAST_USER_ADDR_T(bp->nb_data + (firstpg * PAGE_SIZE)),
0a7de745 2366 ((lastpg - firstpg + 1) * PAGE_SIZE));
2d21ac55 2367 error = nfs_read_rpc(np, auio, ctx);
91447636 2368 }
55e303ae 2369 if (error) {
0a7de745 2370 if (ioflag & IO_NOCACHE) {
91447636 2371 SET(bp->nb_flags, NB_NOCACHE);
0a7de745 2372 }
483a1d10 2373 nfs_buf_release(bp, 1);
2d21ac55
A
2374 nfs_data_unlock(np);
2375 FSDBG_BOT(514, np, 0xd1e000e, 0, error);
0a7de745 2376 return error;
55e303ae
A
2377 }
2378 /* Make sure that the valid range is set to cover this read. */
f427ee49
A
2379 bp->nb_validoff = trunc_page_64(on);
2380 bp->nb_validend = round_page_64(on + n);
55e303ae 2381 nfs_buf_normalize_valid_range(np, bp);
91447636 2382 if (uio_resid(auio) > 0) {
55e303ae
A
2383 /* if short read, must have hit EOF, */
2384 /* so zero the rest of the range */
91447636 2385 bzero(CAST_DOWN(caddr_t, uio_curriovbase(auio)), uio_resid(auio));
55e303ae
A
2386 }
2387 /* mark the pages (successfully read) as valid */
0a7de745
A
2388 for (pg = firstpg; pg <= lastpg; pg++) {
2389 NBPGVALID_SET(bp, pg);
2390 }
55e303ae 2391 }
1c79356b 2392 }
55e303ae 2393 /* if no pages are valid, read the whole block */
f427ee49 2394 if (!nfs_buf_pgs_is_set(&bp->nb_valid)) {
2d21ac55
A
2395 if (!IS_VALID_CRED(bp->nb_rcred) && IS_VALID_CRED(cred)) {
2396 kauth_cred_ref(cred);
2397 bp->nb_rcred = cred;
2398 }
55e303ae
A
2399 SET(bp->nb_flags, NB_READ);
2400 CLR(bp->nb_flags, (NB_DONE | NB_ERROR | NB_INVAL));
2d21ac55 2401 error = nfs_buf_read(bp);
0a7de745 2402 if (ioflag & IO_NOCACHE) {
b0d623f7 2403 SET(bp->nb_flags, NB_NOCACHE);
0a7de745 2404 }
55e303ae 2405 if (error) {
2d21ac55 2406 nfs_data_unlock(np);
483a1d10 2407 nfs_buf_release(bp, 1);
2d21ac55 2408 FSDBG_BOT(514, np, 0xd1e000f, 0, error);
0a7de745 2409 return error;
55e303ae
A
2410 }
2411 }
2412buffer_ready:
55e303ae
A
2413 /* validate read range against valid range and clip */
2414 if (bp->nb_validend > 0) {
2415 diff = (on >= bp->nb_validend) ? 0 : (bp->nb_validend - on);
0a7de745 2416 if (diff < n) {
55e303ae 2417 n = diff;
0a7de745 2418 }
55e303ae 2419 }
55e303ae 2420 if (n > 0) {
b0d623f7 2421 NFS_BUF_MAP(bp);
f427ee49
A
2422 n32 = n > INT_MAX ? INT_MAX : (int)n;
2423 error = uiomove(bp->nb_data + on, n32, uio);
2424 if (!error && n > n32) {
2425 error = uiomove(bp->nb_data + on + n32, (int)(n - n32), uio);
2426 }
55e303ae 2427 }
2d21ac55 2428
cb323159 2429
2d21ac55
A
2430 nfs_buf_release(bp, 1);
2431 nfs_data_unlock(np);
b0d623f7
A
2432 nfs_node_lock_force(np);
2433 np->n_lastread = (uio_offset(uio) - 1) / biosize;
2434 nfs_node_unlock(np);
2435 } while (error == 0 && uio_resid(uio) > 0 && n > 0);
2436 FSDBG_BOT(514, np, uio_offset(uio), uio_resid(uio), error);
0a7de745 2437 return error;
1c79356b
A
2438}
2439
2d21ac55
A
2440/*
2441 * limit the number of outstanding async I/O writes
2442 */
b0d623f7 2443int
2d21ac55
A
2444nfs_async_write_start(struct nfsmount *nmp)
2445{
6d2010ae 2446 int error = 0, slpflag = NMFLAG(nmp, INTR) ? PCATCH : 0;
cb323159 2447 struct timespec ts = { .tv_sec = 1, .tv_nsec = 0 };
2d21ac55 2448
0a7de745
A
2449 if (nfs_max_async_writes <= 0) {
2450 return 0;
2451 }
2d21ac55 2452 lck_mtx_lock(&nmp->nm_lock);
36401178 2453 while ((nfs_max_async_writes > 0) && (nmp->nm_asyncwrites >= nfs_max_async_writes)) {
0a7de745 2454 if ((error = nfs_sigintr(nmp, NULL, current_thread(), 1))) {
2d21ac55 2455 break;
0a7de745
A
2456 }
2457 msleep(&nmp->nm_asyncwrites, &nmp->nm_lock, slpflag | (PZERO - 1), "nfsasyncwrites", &ts);
36401178 2458 slpflag = 0;
2d21ac55 2459 }
0a7de745 2460 if (!error) {
2d21ac55 2461 nmp->nm_asyncwrites++;
0a7de745 2462 }
2d21ac55 2463 lck_mtx_unlock(&nmp->nm_lock);
0a7de745 2464 return error;
2d21ac55 2465}
b0d623f7 2466void
2d21ac55
A
2467nfs_async_write_done(struct nfsmount *nmp)
2468{
0a7de745 2469 if (nmp->nm_asyncwrites <= 0) {
2d21ac55 2470 return;
0a7de745 2471 }
2d21ac55 2472 lck_mtx_lock(&nmp->nm_lock);
0a7de745 2473 if (nmp->nm_asyncwrites-- >= nfs_max_async_writes) {
2d21ac55 2474 wakeup(&nmp->nm_asyncwrites);
0a7de745 2475 }
2d21ac55
A
2476 lck_mtx_unlock(&nmp->nm_lock);
2477}
fa4905b1 2478
1c79356b 2479/*
2d21ac55
A
2480 * write (or commit) the given NFS buffer
2481 *
2482 * Commit the buffer if we can.
2483 * Write out any dirty range.
2484 * If any dirty pages remain, write them out.
2485 * Mark buffer done.
2486 *
2487 * For async requests, all the work beyond sending the initial
2488 * write RPC is handled in the RPC callback(s).
1c79356b
A
2489 */
2490int
2d21ac55 2491nfs_buf_write(struct nfsbuf *bp)
1c79356b 2492{
2d21ac55
A
2493 int error = 0, oldflags, async;
2494 nfsnode_t np;
2495 thread_t thd;
91447636 2496 kauth_cred_t cred;
2d21ac55 2497 proc_t p = current_proc();
f427ee49
A
2498 int iomode;
2499 off_t doff, dend, firstpg, lastpg;
91447636 2500
2d21ac55 2501 FSDBG_TOP(553, bp, NBOFF(bp), bp->nb_flags, 0);
91447636 2502
0a7de745 2503 if (!ISSET(bp->nb_lflags, NBL_BUSY)) {
2d21ac55 2504 panic("nfs_buf_write: buffer is not busy???");
0a7de745 2505 }
91447636 2506
2d21ac55
A
2507 np = bp->nb_np;
2508 async = ISSET(bp->nb_flags, NB_ASYNC);
2509 oldflags = bp->nb_flags;
91447636 2510
0a7de745 2511 CLR(bp->nb_flags, (NB_READ | NB_DONE | NB_ERROR | NB_DELWRI));
2d21ac55 2512 if (ISSET(oldflags, NB_DELWRI)) {
c3c9b80d 2513 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
2514 nfs_nbdwrite--;
2515 NFSBUFCNTCHK();
c3c9b80d 2516 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 2517 wakeup(&nfs_nbdwrite);
91447636 2518 }
2d21ac55
A
2519
2520 /* move to clean list */
0a7de745 2521 if (ISSET(oldflags, (NB_ASYNC | NB_DELWRI))) {
c3c9b80d 2522 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 2523 if (bp->nb_vnbufs.le_next != NFSNOLIST) {
2d21ac55 2524 LIST_REMOVE(bp, nb_vnbufs);
0a7de745 2525 }
2d21ac55 2526 LIST_INSERT_HEAD(&np->n_cleanblkhd, bp, nb_vnbufs);
c3c9b80d 2527 lck_mtx_unlock(&nfs_buf_mutex);
1c79356b 2528 }
b0d623f7
A
2529 nfs_node_lock_force(np);
2530 np->n_numoutput++;
2531 nfs_node_unlock(np);
2d21ac55 2532 vnode_startwrite(NFSTOV(np));
0c530ab8 2533
0a7de745 2534 if (p && p->p_stats) {
b0d623f7 2535 OSIncrementAtomicLong(&p->p_stats->p_ru.ru_oublock);
0a7de745 2536 }
0c530ab8 2537
2d21ac55 2538 cred = bp->nb_wcred;
0a7de745 2539 if (!IS_VALID_CRED(cred) && ISSET(bp->nb_flags, NB_READ)) {
2d21ac55 2540 cred = bp->nb_rcred; /* shouldn't really happen, but... */
0a7de745
A
2541 }
2542 if (IS_VALID_CRED(cred)) {
2d21ac55 2543 kauth_cred_ref(cred);
0a7de745 2544 }
2d21ac55
A
2545 thd = async ? NULL : current_thread();
2546
2547 /* We need to make sure the pages are locked before doing I/O. */
fe8ab488
A
2548 if (!ISSET(bp->nb_flags, NB_META)) {
2549 if (UBCINFOEXISTS(NFSTOV(np))) {
2550 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
2551 error = nfs_buf_upl_setup(bp);
2552 if (error) {
2553 printf("nfs_buf_write: upl create failed %d\n", error);
2554 SET(bp->nb_flags, NB_ERROR);
2555 bp->nb_error = error = EIO;
2556 nfs_buf_iodone(bp);
2557 goto out;
2558 }
2559 nfs_buf_upl_check(bp);
55e303ae 2560 }
fe8ab488
A
2561 } else {
2562 /* We should never be in nfs_buf_write() with no UBCINFO. */
2563 printf("nfs_buf_write: ubcinfo already gone\n");
2564 SET(bp->nb_flags, NB_ERROR);
2565 bp->nb_error = error = EIO;
2566 nfs_buf_iodone(bp);
2567 goto out;
1c79356b
A
2568 }
2569 }
55e303ae 2570
2d21ac55 2571 /* If NB_NEEDCOMMIT is set, a commit RPC may do the trick. */
0a7de745 2572 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
2d21ac55 2573 nfs_buf_check_write_verifier(np, bp);
0a7de745 2574 }
2d21ac55
A
2575 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
2576 struct nfsmount *nmp = NFSTONMP(np);
fe8ab488 2577 if (nfs_mount_gone(nmp)) {
2d21ac55
A
2578 SET(bp->nb_flags, NB_ERROR);
2579 bp->nb_error = error = EIO;
2580 nfs_buf_iodone(bp);
2581 goto out;
2582 }
2583 SET(bp->nb_flags, NB_WRITEINPROG);
2584 error = nmp->nm_funcs->nf_commit_rpc(np, NBOFF(bp) + bp->nb_dirtyoff,
0a7de745 2585 bp->nb_dirtyend - bp->nb_dirtyoff, bp->nb_wcred, bp->nb_verf);
2d21ac55
A
2586 CLR(bp->nb_flags, NB_WRITEINPROG);
2587 if (error) {
2588 if (error != NFSERR_STALEWRITEVERF) {
2589 SET(bp->nb_flags, NB_ERROR);
2590 bp->nb_error = error;
55e303ae 2591 }
2d21ac55
A
2592 nfs_buf_iodone(bp);
2593 goto out;
2594 }
2595 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
2596 CLR(bp->nb_flags, NB_NEEDCOMMIT);
b0d623f7 2597 nfs_node_lock_force(np);
2d21ac55
A
2598 np->n_needcommitcnt--;
2599 CHECK_NEEDCOMMITCNT(np);
b0d623f7 2600 nfs_node_unlock(np);
2d21ac55
A
2601 }
2602 if (!error && (bp->nb_dirtyend > 0)) {
2603 /* sanity check the dirty range */
2604 if (NBOFF(bp) + bp->nb_dirtyend > (off_t) np->n_size) {
2605 bp->nb_dirtyend = np->n_size - NBOFF(bp);
0a7de745 2606 if (bp->nb_dirtyoff >= bp->nb_dirtyend) {
2d21ac55 2607 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
0a7de745 2608 }
55e303ae 2609 }
91447636 2610 }
2d21ac55
A
2611 if (!error && (bp->nb_dirtyend > 0)) {
2612 /* there's a dirty range that needs to be written out */
f427ee49 2613 nfsbufpgs pagemask, pagemaskand;
2d21ac55
A
2614 NFS_BUF_MAP(bp);
2615
2616 doff = bp->nb_dirtyoff;
2617 dend = bp->nb_dirtyend;
2618
2619 /* if doff page is dirty, move doff to start of page */
0a7de745 2620 if (NBPGDIRTY(bp, doff / PAGE_SIZE)) {
2d21ac55 2621 doff -= doff & PAGE_MASK;
0a7de745 2622 }
2d21ac55 2623 /* try to expand write range to include preceding dirty pages */
0a7de745
A
2624 if (!(doff & PAGE_MASK)) {
2625 while ((doff > 0) && NBPGDIRTY(bp, (doff - 1) / PAGE_SIZE)) {
2d21ac55 2626 doff -= PAGE_SIZE;
0a7de745
A
2627 }
2628 }
2d21ac55 2629 /* if dend page is dirty, move dend to start of next page */
0a7de745 2630 if ((dend & PAGE_MASK) && NBPGDIRTY(bp, dend / PAGE_SIZE)) {
f427ee49 2631 dend = round_page_64(dend);
0a7de745 2632 }
2d21ac55 2633 /* try to expand write range to include trailing dirty pages */
0a7de745
A
2634 if (!(dend & PAGE_MASK)) {
2635 while ((dend < (int)bp->nb_bufsize) && NBPGDIRTY(bp, dend / PAGE_SIZE)) {
2d21ac55 2636 dend += PAGE_SIZE;
0a7de745
A
2637 }
2638 }
2d21ac55 2639 /* make sure to keep dend clipped to EOF */
0a7de745 2640 if ((NBOFF(bp) + dend) > (off_t) np->n_size) {
2d21ac55 2641 dend = np->n_size - NBOFF(bp);
0a7de745 2642 }
2d21ac55 2643 /* calculate range of complete pages being written */
f427ee49
A
2644 if (dend > doff) {
2645 firstpg = doff / PAGE_SIZE;
2646 lastpg = (dend - 1) / PAGE_SIZE;
2647 /* calculate mask for that page range */
2648 nfs_buf_pgs_set_pages_between(&pagemask, firstpg, lastpg + 1);
2649 } else {
2650 NBPGS_ERASE(&pagemask);
2651 }
91447636 2652
fa4905b1 2653 /*
2d21ac55
A
2654 * compare page mask to nb_dirty; if there are other dirty pages
2655 * then write FILESYNC; otherwise, write UNSTABLE if async and
2656 * not needcommit/stable; otherwise write FILESYNC
fa4905b1 2657 */
f427ee49
A
2658 nfs_buf_pgs_bit_not(&pagemask);
2659 nfs_buf_pgs_bit_and(&bp->nb_dirty, &pagemask, &pagemaskand);
2660 if (nfs_buf_pgs_is_set(&pagemaskand)) {
2d21ac55 2661 iomode = NFS_WRITE_FILESYNC;
0a7de745 2662 } else if ((bp->nb_flags & (NB_ASYNC | NB_NEEDCOMMIT | NB_STABLE)) == NB_ASYNC) {
2d21ac55 2663 iomode = NFS_WRITE_UNSTABLE;
0a7de745 2664 } else {
2d21ac55 2665 iomode = NFS_WRITE_FILESYNC;
0a7de745 2666 }
55e303ae 2667
2d21ac55
A
2668 /* write the whole contiguous dirty range */
2669 bp->nb_offio = doff;
2670 bp->nb_endio = dend;
55e303ae 2671
316670eb 2672 OSAddAtomic64(1, &nfsstats.write_bios);
55e303ae 2673
2d21ac55
A
2674 SET(bp->nb_flags, NB_WRITEINPROG);
2675 error = nfs_buf_write_rpc(bp, iomode, thd, cred);
55e303ae 2676 /*
2d21ac55
A
2677 * For async I/O, the callbacks will finish up the
2678 * write and push out any dirty pages. Otherwise,
2679 * the write has already been finished and any dirty
2680 * pages pushed out.
55e303ae 2681 */
2d21ac55 2682 } else {
f427ee49 2683 if (!error && nfs_buf_pgs_is_set(&bp->nb_dirty)) { /* write out any dirty pages */
2d21ac55 2684 error = nfs_buf_write_dirty_pages(bp, thd, cred);
0a7de745 2685 }
2d21ac55
A
2686 nfs_buf_iodone(bp);
2687 }
2688 /* note: bp is still valid only for !async case */
2689out:
2690 if (!async) {
2691 error = nfs_buf_iowait(bp);
2692 /* move to clean list */
2693 if (oldflags & NB_DELWRI) {
c3c9b80d 2694 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 2695 if (bp->nb_vnbufs.le_next != NFSNOLIST) {
2d21ac55 2696 LIST_REMOVE(bp, nb_vnbufs);
0a7de745 2697 }
2d21ac55 2698 LIST_INSERT_HEAD(&np->n_cleanblkhd, bp, nb_vnbufs);
c3c9b80d 2699 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
2700 }
2701 FSDBG_BOT(553, bp, NBOFF(bp), bp->nb_flags, error);
2702 nfs_buf_release(bp, 1);
2703 /* check if we need to invalidate (and we can) */
2704 if ((np->n_flag & NNEEDINVALIDATE) &&
0a7de745 2705 !(np->n_bflag & (NBINVALINPROG | NBFLUSHINPROG))) {
2d21ac55 2706 int invalidate = 0;
b0d623f7 2707 nfs_node_lock_force(np);
2d21ac55
A
2708 if (np->n_flag & NNEEDINVALIDATE) {
2709 invalidate = 1;
2710 np->n_flag &= ~NNEEDINVALIDATE;
55e303ae 2711 }
b0d623f7 2712 nfs_node_unlock(np);
2d21ac55
A
2713 if (invalidate) {
2714 /*
2715 * There was a write error and we need to
2716 * invalidate attrs and flush buffers in
2717 * order to sync up with the server.
2718 * (if this write was extending the file,
2719 * we may no longer know the correct size)
2720 *
2721 * But we couldn't call vinvalbuf while holding
2722 * the buffer busy. So we call vinvalbuf() after
2723 * releasing the buffer.
2724 */
0a7de745 2725 nfs_vinvalbuf2(NFSTOV(np), V_SAVE | V_IGNORE_WRITEERR, thd, cred, 1);
55e303ae 2726 }
55e303ae 2727 }
2d21ac55
A
2728 }
2729
0a7de745 2730 if (IS_VALID_CRED(cred)) {
2d21ac55 2731 kauth_cred_unref(&cred);
0a7de745
A
2732 }
2733 return error;
2d21ac55 2734}
55e303ae 2735
2d21ac55
A
2736/*
2737 * finish the writing of a buffer
2738 */
2739void
2740nfs_buf_write_finish(struct nfsbuf *bp, thread_t thd, kauth_cred_t cred)
2741{
2742 nfsnode_t np = bp->nb_np;
2743 int error = (bp->nb_flags & NB_ERROR) ? bp->nb_error : 0;
f427ee49 2744 off_t firstpg, lastpg;
2d21ac55
A
2745
2746 if ((error == EINTR) || (error == ERESTART)) {
2747 CLR(bp->nb_flags, NB_ERROR);
2748 SET(bp->nb_flags, NB_EINTR);
2749 }
2750
2751 if (!error) {
f427ee49 2752 nfsbufpgs pagemask;
2d21ac55 2753 /* calculate range of complete pages being written */
f427ee49
A
2754 if (bp->nb_endio > bp->nb_offio) {
2755 firstpg = bp->nb_offio / PAGE_SIZE;
2756 lastpg = (bp->nb_endio - 1) / PAGE_SIZE;
2757 /* calculate mask for that page range written */
2758 nfs_buf_pgs_set_pages_between(&pagemask, firstpg, lastpg + 1);
2759 } else {
2760 NBPGS_ERASE(&pagemask);
2761 }
2d21ac55 2762 /* clear dirty bits for pages we've written */
f427ee49
A
2763 nfs_buf_pgs_bit_not(&pagemask);
2764 nfs_buf_pgs_bit_and(&bp->nb_dirty, &pagemask, &bp->nb_dirty);
2d21ac55
A
2765 }
2766
2767 /* manage needcommit state */
2768 if (!error && (bp->nb_commitlevel == NFS_WRITE_UNSTABLE)) {
2769 if (!ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
b0d623f7 2770 nfs_node_lock_force(np);
2d21ac55 2771 np->n_needcommitcnt++;
b0d623f7 2772 nfs_node_unlock(np);
2d21ac55
A
2773 SET(bp->nb_flags, NB_NEEDCOMMIT);
2774 }
2775 /* make sure nb_dirtyoff/nb_dirtyend reflect actual range written */
2776 bp->nb_dirtyoff = bp->nb_offio;
2777 bp->nb_dirtyend = bp->nb_endio;
2778 } else if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
b0d623f7 2779 nfs_node_lock_force(np);
2d21ac55
A
2780 np->n_needcommitcnt--;
2781 CHECK_NEEDCOMMITCNT(np);
b0d623f7 2782 nfs_node_unlock(np);
2d21ac55
A
2783 CLR(bp->nb_flags, NB_NEEDCOMMIT);
2784 }
2785
2786 CLR(bp->nb_flags, NB_WRITEINPROG);
2787
2788 /*
2789 * For an unstable write, the buffer is still treated as dirty until
2790 * a commit (or stable (re)write) is performed. Buffers needing only
2791 * a commit are marked with the NB_DELWRI and NB_NEEDCOMMIT flags.
2792 *
2793 * If the write was interrupted we set NB_EINTR. Don't set NB_ERROR
2794 * because that would cause the buffer to be dropped. The buffer is
2795 * still valid and simply needs to be written again.
2796 */
2797 if ((error == EINTR) || (error == ERESTART) || (!error && (bp->nb_flags & NB_NEEDCOMMIT))) {
2798 CLR(bp->nb_flags, NB_INVAL);
2799 if (!ISSET(bp->nb_flags, NB_DELWRI)) {
2800 SET(bp->nb_flags, NB_DELWRI);
c3c9b80d 2801 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
2802 nfs_nbdwrite++;
2803 NFSBUFCNTCHK();
c3c9b80d 2804 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 2805 }
fa4905b1 2806 /*
2d21ac55
A
2807 * Since for the NB_ASYNC case, we've reassigned the buffer to the
2808 * clean list, we have to reassign it back to the dirty one. Ugh.
fa4905b1 2809 */
2d21ac55
A
2810 if (ISSET(bp->nb_flags, NB_ASYNC)) {
2811 /* move to dirty list */
c3c9b80d 2812 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 2813 if (bp->nb_vnbufs.le_next != NFSNOLIST) {
2d21ac55 2814 LIST_REMOVE(bp, nb_vnbufs);
0a7de745 2815 }
2d21ac55 2816 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
c3c9b80d 2817 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
2818 }
2819 } else {
2820 /* either there's an error or we don't need to commit */
2821 if (error) {
2822 /*
2823 * There was a write error and we need to invalidate
2824 * attrs and flush buffers in order to sync up with the
2825 * server. (if this write was extending the file, we
2826 * may no longer know the correct size)
2827 *
2828 * But we can't call vinvalbuf while holding this
2829 * buffer busy. Set a flag to do it after releasing
2830 * the buffer.
2831 */
b0d623f7 2832 nfs_node_lock_force(np);
2d21ac55
A
2833 np->n_error = error;
2834 np->n_flag |= (NWRITEERR | NNEEDINVALIDATE);
2835 NATTRINVALIDATE(np);
b0d623f7 2836 nfs_node_unlock(np);
2d21ac55
A
2837 }
2838 /* clear the dirty range */
2839 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
2840 }
55e303ae 2841
f427ee49 2842 if (!error && nfs_buf_pgs_is_set(&bp->nb_dirty)) {
2d21ac55 2843 nfs_buf_write_dirty_pages(bp, thd, cred);
0a7de745 2844 }
2d21ac55
A
2845 nfs_buf_iodone(bp);
2846}
fa4905b1 2847
2d21ac55
A
2848/*
2849 * write out any pages marked dirty in a buffer
2850 *
2851 * We do use unstable writes and follow up with a commit.
2852 * If we catch the write verifier changing we'll restart
2853 * do the writes filesync.
2854 */
2855int
2856nfs_buf_write_dirty_pages(struct nfsbuf *bp, thread_t thd, kauth_cred_t cred)
2857{
2858 nfsnode_t np = bp->nb_np;
2859 struct nfsmount *nmp = NFSTONMP(np);
2860 int error = 0, commit, iomode, iomode2, len, pg, count, npages, off;
f427ee49 2861 nfsbufpgs dirty;
2d21ac55 2862 uint64_t wverf;
b0d623f7 2863 uio_t auio;
0a7de745 2864 char uio_buf[UIO_SIZEOF(1)];
55e303ae 2865
f427ee49 2866 if (!nfs_buf_pgs_is_set(&bp->nb_dirty)) {
0a7de745
A
2867 return 0;
2868 }
2d21ac55
A
2869
2870 /* there are pages marked dirty that need to be written out */
316670eb 2871 OSAddAtomic64(1, &nfsstats.write_bios);
2d21ac55
A
2872 NFS_BUF_MAP(bp);
2873 SET(bp->nb_flags, NB_WRITEINPROG);
2874 npages = bp->nb_bufsize / PAGE_SIZE;
2875 iomode = NFS_WRITE_UNSTABLE;
2876
b0d623f7 2877 auio = uio_createwithbuffer(1, 0, UIO_SYSSPACE, UIO_WRITE,
0a7de745 2878 &uio_buf, sizeof(uio_buf));
2d21ac55
A
2879
2880again:
f427ee49 2881 NBPGS_COPY(&dirty, &bp->nb_dirty);
2d21ac55
A
2882 wverf = bp->nb_verf;
2883 commit = NFS_WRITE_FILESYNC;
2884 for (pg = 0; pg < npages; pg++) {
0a7de745 2885 if (!NBPGDIRTY(bp, pg)) {
2d21ac55 2886 continue;
0a7de745 2887 }
2d21ac55 2888 count = 1;
0a7de745 2889 while (((pg + count) < npages) && NBPGDIRTY(bp, pg + count)) {
2d21ac55 2890 count++;
0a7de745 2891 }
2d21ac55
A
2892 /* write count pages starting with page pg */
2893 off = pg * PAGE_SIZE;
2894 len = count * PAGE_SIZE;
2895 /* clip writes to EOF */
0a7de745 2896 if (NBOFF(bp) + off + len > (off_t) np->n_size) {
2d21ac55 2897 len -= (NBOFF(bp) + off + len) - np->n_size;
0a7de745 2898 }
2d21ac55
A
2899 if (len > 0) {
2900 iomode2 = iomode;
b0d623f7
A
2901 uio_reset(auio, NBOFF(bp) + off, UIO_SYSSPACE, UIO_WRITE);
2902 uio_addiov(auio, CAST_USER_ADDR_T(bp->nb_data + off), len);
2903 error = nfs_write_rpc2(np, auio, thd, cred, &iomode2, &bp->nb_verf);
0a7de745 2904 if (error) {
2d21ac55 2905 break;
0a7de745
A
2906 }
2907 if (iomode2 < commit) { /* Retain the lowest commitment level returned. */
2d21ac55 2908 commit = iomode2;
0a7de745 2909 }
2d21ac55
A
2910 if ((commit != NFS_WRITE_FILESYNC) && (wverf != bp->nb_verf)) {
2911 /* verifier changed, redo all the writes filesync */
2912 iomode = NFS_WRITE_FILESYNC;
2913 goto again;
fa4905b1
A
2914 }
2915 }
2d21ac55
A
2916 /* clear dirty bits */
2917 while (count--) {
f427ee49 2918 NBPGS_UNSET(&dirty, pg);
0a7de745 2919 if (count) { /* leave pg on last page */
2d21ac55 2920 pg++;
0a7de745 2921 }
2d21ac55
A
2922 }
2923 }
2924 CLR(bp->nb_flags, NB_WRITEINPROG);
2925
2926 if (!error && (commit != NFS_WRITE_FILESYNC)) {
6d2010ae 2927 error = nmp->nm_funcs->nf_commit_rpc(np, NBOFF(bp), bp->nb_bufsize, cred, wverf);
2d21ac55
A
2928 if (error == NFSERR_STALEWRITEVERF) {
2929 /* verifier changed, so we need to restart all the writes */
2930 iomode = NFS_WRITE_FILESYNC;
2931 goto again;
2932 }
2933 }
2934 if (!error) {
f427ee49 2935 NBPGS_COPY(&bp->nb_dirty, &dirty);
2d21ac55
A
2936 } else {
2937 SET(bp->nb_flags, NB_ERROR);
2938 bp->nb_error = error;
2939 }
0a7de745 2940 return error;
2d21ac55
A
2941}
2942
2943/*
2944 * initiate the NFS WRITE RPC(s) for a buffer
2945 */
2946int
2947nfs_buf_write_rpc(struct nfsbuf *bp, int iomode, thread_t thd, kauth_cred_t cred)
2948{
2949 struct nfsmount *nmp;
2950 nfsnode_t np = bp->nb_np;
2951 int error = 0, nfsvers, async;
f427ee49
A
2952 int64_t nrpcs;
2953 size_t len;
2954 uint32_t nmwsize;
2d21ac55
A
2955 struct nfsreq *req;
2956 struct nfsreq_cbinfo cb;
b0d623f7 2957 uio_t auio;
0a7de745 2958 char uio_buf[UIO_SIZEOF(1)];
f427ee49 2959 off_t offset, length;
2d21ac55
A
2960
2961 nmp = NFSTONMP(np);
fe8ab488 2962 if (nfs_mount_gone(nmp)) {
2d21ac55
A
2963 bp->nb_error = error = ENXIO;
2964 SET(bp->nb_flags, NB_ERROR);
2965 nfs_buf_iodone(bp);
0a7de745 2966 return error;
2d21ac55
A
2967 }
2968 nfsvers = nmp->nm_vers;
2969 nmwsize = nmp->nm_wsize;
2970
2971 offset = bp->nb_offio;
2972 length = bp->nb_endio - bp->nb_offio;
2973
2974 /* Note: Can only do async I/O if nfsiods are configured. */
2975 async = (bp->nb_flags & NB_ASYNC) && (NFSIOD_MAX > 0);
2976 bp->nb_commitlevel = NFS_WRITE_FILESYNC;
2977 cb.rcb_func = async ? nfs_buf_write_rpc_finish : NULL;
2978 cb.rcb_bp = bp;
2979
2980 if ((nfsvers == NFS_VER2) && ((NBOFF(bp) + bp->nb_endio) > 0xffffffffLL)) {
2981 bp->nb_error = error = EFBIG;
2982 SET(bp->nb_flags, NB_ERROR);
2983 nfs_buf_iodone(bp);
0a7de745 2984 return error;
2d21ac55
A
2985 }
2986
2a1bd2d3
A
2987 if (length == 0) {
2988 /* We should never get here */
2989#if DEVELOPMENT
2990 printf("nfs_buf_write_rpc: Got request with zero length. np %p, bp %p, offset %lld\n", np, bp, offset);
2991#else
2992 printf("nfs_buf_write_rpc: Got request with zero length.\n");
2993#endif /* DEVELOPMENT */
2994 nfs_buf_iodone(bp);
2995 return 0;
2996 }
2997
b0d623f7 2998 auio = uio_createwithbuffer(1, NBOFF(bp) + offset, UIO_SYSSPACE,
0a7de745 2999 UIO_WRITE, &uio_buf, sizeof(uio_buf));
f427ee49 3000 NFS_UIO_ADDIOV(auio, CAST_USER_ADDR_T(bp->nb_data + offset), length);
2d21ac55
A
3001
3002 bp->nb_rpcs = nrpcs = (length + nmwsize - 1) / nmwsize;
3003 if (async && (nrpcs > 1)) {
3004 SET(bp->nb_flags, NB_MULTASYNCRPC);
3005 } else {
3006 CLR(bp->nb_flags, NB_MULTASYNCRPC);
3007 }
3008
3009 while (length > 0) {
3010 if (ISSET(bp->nb_flags, NB_ERROR)) {
3011 error = bp->nb_error;
3012 break;
3013 }
f427ee49
A
3014 len = (length > nmwsize) ? nmwsize : (uint32_t)length;
3015 cb.rcb_args.offset = offset;
3016 cb.rcb_args.length = len;
cb323159 3017#if CONFIG_NFS4
0a7de745 3018 if (nmp->nm_vers >= NFS_VER4) {
f427ee49 3019 cb.rcb_args.stategenid = nmp->nm_stategenid;
0a7de745 3020 }
cb323159 3021#endif
0a7de745 3022 if (async && ((error = nfs_async_write_start(nmp)))) {
2d21ac55 3023 break;
0a7de745 3024 }
2d21ac55 3025 req = NULL;
b0d623f7 3026 error = nmp->nm_funcs->nf_write_rpc_async(np, auio, len, thd, cred,
0a7de745 3027 iomode, &cb, &req);
2d21ac55 3028 if (error) {
0a7de745 3029 if (async) {
2d21ac55 3030 nfs_async_write_done(nmp);
0a7de745 3031 }
2d21ac55
A
3032 break;
3033 }
3034 offset += len;
3035 length -= len;
0a7de745 3036 if (async) {
2d21ac55 3037 continue;
0a7de745 3038 }
2d21ac55
A
3039 nfs_buf_write_rpc_finish(req);
3040 }
3041
3042 if (length > 0) {
fa4905b1 3043 /*
2d21ac55
A
3044 * Something bad happened while trying to send the RPCs.
3045 * Wait for any outstanding requests to complete.
fa4905b1 3046 */
2d21ac55
A
3047 bp->nb_error = error;
3048 SET(bp->nb_flags, NB_ERROR);
3049 if (ISSET(bp->nb_flags, NB_MULTASYNCRPC)) {
3050 nrpcs = (length + nmwsize - 1) / nmwsize;
c3c9b80d 3051 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
3052 bp->nb_rpcs -= nrpcs;
3053 if (bp->nb_rpcs == 0) {
3054 /* No RPCs left, so the buffer's done */
c3c9b80d 3055 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
3056 nfs_buf_write_finish(bp, thd, cred);
3057 } else {
3058 /* wait for the last RPC to mark it done */
0a7de745 3059 while (bp->nb_rpcs > 0) {
c3c9b80d 3060 msleep(&bp->nb_rpcs, &nfs_buf_mutex, 0,
0a7de745
A
3061 "nfs_buf_write_rpc_cancel", NULL);
3062 }
c3c9b80d 3063 lck_mtx_unlock(&nfs_buf_mutex);
fa4905b1 3064 }
2d21ac55
A
3065 } else {
3066 nfs_buf_write_finish(bp, thd, cred);
3067 }
6d2010ae 3068 /* It may have just been an interrupt... that's OK */
0a7de745 3069 if (!ISSET(bp->nb_flags, NB_ERROR)) {
6d2010ae 3070 error = 0;
0a7de745 3071 }
2d21ac55 3072 }
55e303ae 3073
0a7de745 3074 return error;
2d21ac55
A
3075}
3076
3077/*
3078 * finish up an NFS WRITE RPC on a buffer
3079 */
3080void
3081nfs_buf_write_rpc_finish(struct nfsreq *req)
3082{
f427ee49 3083 int error = 0, nfsvers, multasyncrpc, finished;
2d21ac55
A
3084 int committed = NFS_WRITE_FILESYNC;
3085 uint64_t wverf = 0;
f427ee49
A
3086 off_t offset;
3087 size_t rlen, length;
2d21ac55
A
3088 void *wakeme = NULL;
3089 struct nfsreq_cbinfo cb;
3090 struct nfsreq *wreq = NULL;
3091 struct nfsbuf *bp;
3092 struct nfsmount *nmp;
3093 nfsnode_t np;
3094 thread_t thd;
3095 kauth_cred_t cred;
b0d623f7 3096 uio_t auio;
0a7de745 3097 char uio_buf[UIO_SIZEOF(1)];
2d21ac55
A
3098
3099finish:
3100 np = req->r_np;
3101 thd = req->r_thread;
3102 cred = req->r_cred;
0a7de745 3103 if (IS_VALID_CRED(cred)) {
2d21ac55 3104 kauth_cred_ref(cred);
0a7de745 3105 }
2d21ac55
A
3106 cb = req->r_callback;
3107 bp = cb.rcb_bp;
0a7de745 3108 if (cb.rcb_func) { /* take an extra reference on the nfsreq in case we want to resend it later due to grace error */
6d2010ae 3109 nfs_request_ref(req, 0);
0a7de745 3110 }
2d21ac55
A
3111
3112 nmp = NFSTONMP(np);
fe8ab488 3113 if (nfs_mount_gone(nmp)) {
2d21ac55
A
3114 SET(bp->nb_flags, NB_ERROR);
3115 bp->nb_error = error = ENXIO;
3116 }
3117 if (error || ISSET(bp->nb_flags, NB_ERROR)) {
3118 /* just drop it */
3119 nfs_request_async_cancel(req);
3120 goto out;
3121 }
3122 nfsvers = nmp->nm_vers;
3123
f427ee49
A
3124 offset = cb.rcb_args.offset;
3125 rlen = length = cb.rcb_args.length;
2d21ac55
A
3126
3127 /* finish the RPC */
3128 error = nmp->nm_funcs->nf_write_rpc_async_finish(np, req, &committed, &rlen, &wverf);
3129 if ((error == EINPROGRESS) && cb.rcb_func) {
3130 /* async request restarted */
0a7de745 3131 if (cb.rcb_func) {
6d2010ae 3132 nfs_request_rele(req);
0a7de745
A
3133 }
3134 if (IS_VALID_CRED(cred)) {
2d21ac55 3135 kauth_cred_unref(&cred);
0a7de745 3136 }
2d21ac55
A
3137 return;
3138 }
cb323159 3139#if CONFIG_NFS4
b0d623f7
A
3140 if ((nmp->nm_vers >= NFS_VER4) && nfs_mount_state_error_should_restart(error) && !ISSET(bp->nb_flags, NB_ERROR)) {
3141 lck_mtx_lock(&nmp->nm_lock);
f427ee49 3142 if ((error != NFSERR_OLD_STATEID) && (error != NFSERR_GRACE) && (cb.rcb_args.stategenid == nmp->nm_stategenid)) {
6d2010ae 3143 NP(np, "nfs_buf_write_rpc_finish: error %d @ 0x%llx, 0x%x 0x%x, initiating recovery",
f427ee49 3144 error, NBOFF(bp) + offset, cb.rcb_args.stategenid, nmp->nm_stategenid);
6d2010ae 3145 nfs_need_recover(nmp, error);
b0d623f7
A
3146 }
3147 lck_mtx_unlock(&nmp->nm_lock);
6d2010ae
A
3148 if (np->n_flag & NREVOKE) {
3149 error = EIO;
3150 } else {
3151 if (error == NFSERR_GRACE) {
3152 if (cb.rcb_func) {
3153 /*
3154 * For an async I/O request, handle a grace delay just like
3155 * jukebox errors. Set the resend time and queue it up.
3156 */
3157 struct timeval now;
3158 if (req->r_nmrep.nmc_mhead) {
3159 mbuf_freem(req->r_nmrep.nmc_mhead);
3160 req->r_nmrep.nmc_mhead = NULL;
3161 }
3162 req->r_error = 0;
3163 microuptime(&now);
3164 lck_mtx_lock(&req->r_mtx);
3165 req->r_resendtime = now.tv_sec + 2;
3166 req->r_xid = 0; // get a new XID
3167 req->r_flags |= R_RESTART;
3168 req->r_start = 0;
3169 nfs_asyncio_resend(req);
3170 lck_mtx_unlock(&req->r_mtx);
0a7de745 3171 if (IS_VALID_CRED(cred)) {
6d2010ae 3172 kauth_cred_unref(&cred);
0a7de745 3173 }
6d2010ae
A
3174 /* Note: nfsreq reference taken will be dropped later when finished */
3175 return;
3176 }
3177 /* otherwise, just pause a couple seconds and retry */
0a7de745 3178 tsleep(&nmp->nm_state, (PZERO - 1), "nfsgrace", 2 * hz);
6d2010ae
A
3179 }
3180 if (!(error = nfs_mount_state_wait_for_recovery(nmp))) {
3181 rlen = 0;
3182 goto writeagain;
3183 }
b0d623f7
A
3184 }
3185 }
cb323159 3186#endif
2d21ac55
A
3187 if (error) {
3188 SET(bp->nb_flags, NB_ERROR);
3189 bp->nb_error = error;
3190 }
0a7de745 3191 if (error || (nfsvers == NFS_VER2)) {
2d21ac55 3192 goto out;
0a7de745 3193 }
2d21ac55
A
3194 if (rlen <= 0) {
3195 SET(bp->nb_flags, NB_ERROR);
3196 bp->nb_error = error = EIO;
3197 goto out;
3198 }
3199
3200 /* save lowest commit level returned */
0a7de745 3201 if (committed < bp->nb_commitlevel) {
2d21ac55 3202 bp->nb_commitlevel = committed;
0a7de745 3203 }
2d21ac55
A
3204
3205 /* check the write verifier */
3206 if (!bp->nb_verf) {
3207 bp->nb_verf = wverf;
3208 } else if (bp->nb_verf != wverf) {
3209 /* verifier changed, so buffer will need to be rewritten */
3210 bp->nb_flags |= NB_STALEWVERF;
3211 bp->nb_commitlevel = NFS_WRITE_UNSTABLE;
3212 bp->nb_verf = wverf;
3213 }
3214
2a1bd2d3 3215 if (!ISSET(bp->nb_flags, NB_STALEWVERF) && rlen > 0 && (bp->nb_offio < (offset + (int)rlen))) {
f427ee49
A
3216 bp->nb_offio = offset + rlen;
3217 }
3218
2d21ac55
A
3219 /*
3220 * check for a short write
3221 *
3222 * If the server didn't write all the data, then we
3223 * need to issue another write for the rest of it.
3224 * (Don't bother if the buffer hit an error or stale wverf.)
3225 */
f427ee49 3226 if ((rlen < length) && !(bp->nb_flags & (NB_STALEWVERF | NB_ERROR))) {
cb323159 3227#if CONFIG_NFS4
b0d623f7 3228writeagain:
cb323159 3229#endif
2d21ac55
A
3230 offset += rlen;
3231 length -= rlen;
3232
b0d623f7 3233 auio = uio_createwithbuffer(1, NBOFF(bp) + offset, UIO_SYSSPACE,
0a7de745 3234 UIO_WRITE, &uio_buf, sizeof(uio_buf));
b0d623f7 3235 uio_addiov(auio, CAST_USER_ADDR_T(bp->nb_data + offset), length);
55e303ae 3236
f427ee49
A
3237 cb.rcb_args.offset = offset;
3238 cb.rcb_args.length = length;
cb323159 3239#if CONFIG_NFS4
0a7de745 3240 if (nmp->nm_vers >= NFS_VER4) {
f427ee49 3241 cb.rcb_args.stategenid = nmp->nm_stategenid;
0a7de745 3242 }
cb323159 3243#endif
b0d623f7
A
3244 // XXX iomode should really match the original request
3245 error = nmp->nm_funcs->nf_write_rpc_async(np, auio, length, thd, cred,
0a7de745 3246 NFS_WRITE_FILESYNC, &cb, &wreq);
2d21ac55 3247 if (!error) {
0a7de745 3248 if (IS_VALID_CRED(cred)) {
2d21ac55 3249 kauth_cred_unref(&cred);
0a7de745 3250 }
2d21ac55
A
3251 if (!cb.rcb_func) {
3252 /* if !async we'll need to wait for this RPC to finish */
3253 req = wreq;
b0d623f7 3254 wreq = NULL;
2d21ac55 3255 goto finish;
fa4905b1 3256 }
6d2010ae 3257 nfs_request_rele(req);
2d21ac55
A
3258 /*
3259 * We're done here.
3260 * Outstanding RPC count is unchanged.
3261 * Callback will be called when RPC is done.
3262 */
3263 return;
fa4905b1 3264 }
2d21ac55
A
3265 SET(bp->nb_flags, NB_ERROR);
3266 bp->nb_error = error;
3267 }
55e303ae 3268
2d21ac55 3269out:
6d2010ae 3270 if (cb.rcb_func) {
2d21ac55 3271 nfs_async_write_done(nmp);
6d2010ae
A
3272 nfs_request_rele(req);
3273 }
2d21ac55
A
3274 /*
3275 * Decrement outstanding RPC count on buffer
3276 * and call nfs_buf_write_finish on last RPC.
3277 *
3278 * (Note: when there are multiple async RPCs issued for a
3279 * buffer we need nfs_buffer_mutex to avoid problems when
3280 * aborting a partially-initiated set of RPCs)
3281 */
3282 multasyncrpc = ISSET(bp->nb_flags, NB_MULTASYNCRPC);
0a7de745 3283 if (multasyncrpc) {
c3c9b80d 3284 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 3285 }
2d21ac55
A
3286
3287 bp->nb_rpcs--;
3288 finished = (bp->nb_rpcs == 0);
55e303ae 3289
0a7de745 3290 if (multasyncrpc) {
c3c9b80d 3291 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 3292 }
2d21ac55
A
3293
3294 if (finished) {
0a7de745 3295 if (multasyncrpc) {
2d21ac55 3296 wakeme = &bp->nb_rpcs;
0a7de745 3297 }
2d21ac55 3298 nfs_buf_write_finish(bp, thd, cred);
0a7de745 3299 if (wakeme) {
2d21ac55 3300 wakeup(wakeme);
0a7de745 3301 }
2d21ac55
A
3302 }
3303
0a7de745 3304 if (IS_VALID_CRED(cred)) {
2d21ac55 3305 kauth_cred_unref(&cred);
0a7de745 3306 }
f427ee49
A
3307
3308 if (cb.rcb_func && np->n_needcommitcnt >= NFS_A_LOT_OF_NEEDCOMMITS) {
3309 nfs_flushcommits(np, 1);
3310 }
2d21ac55
A
3311}
3312
3313/*
0a7de745 3314 * Send commit(s) for the given node's "needcommit" buffers
2d21ac55
A
3315 */
3316int
3317nfs_flushcommits(nfsnode_t np, int nowait)
3318{
3319 struct nfsmount *nmp;
b0d623f7 3320 struct nfsbuf *bp, *prevlbp, *lbp;
2d21ac55 3321 struct nfsbuflists blist, commitlist;
f427ee49 3322 int error = 0, retv, wcred_set, flags;
2d21ac55 3323 u_quad_t off, endoff, toff;
f427ee49 3324 uint64_t wverf, count;
2d21ac55 3325 kauth_cred_t wcred = NULL;
f427ee49 3326 nfsbufpgs dirty;
2d21ac55
A
3327
3328 FSDBG_TOP(557, np, 0, 0, 0);
3329
3330 /*
3331 * A nb_flags == (NB_DELWRI | NB_NEEDCOMMIT) block has been written to the
3332 * server, but nas not been committed to stable storage on the server
3333 * yet. The byte range is worked out for as many nfsbufs as we can handle
3334 * and the commit rpc is done.
3335 */
3336 if (!LIST_EMPTY(&np->n_dirtyblkhd)) {
b0d623f7 3337 error = nfs_node_lock(np);
0a7de745 3338 if (error) {
2d21ac55 3339 goto done;
0a7de745 3340 }
1c79356b 3341 np->n_flag |= NMODIFIED;
b0d623f7 3342 nfs_node_unlock(np);
2d21ac55 3343 }
1c79356b 3344
2d21ac55
A
3345 off = (u_quad_t)-1;
3346 endoff = 0;
3347 wcred_set = 0;
3348 LIST_INIT(&commitlist);
3349
3350 nmp = NFSTONMP(np);
fe8ab488 3351 if (nfs_mount_gone(nmp)) {
2d21ac55
A
3352 error = ENXIO;
3353 goto done;
3354 }
3355 if (nmp->nm_vers == NFS_VER2) {
3356 error = EINVAL;
3357 goto done;
3358 }
3359
3360 flags = NBI_DIRTY;
0a7de745 3361 if (nowait) {
2d21ac55 3362 flags |= NBI_NOWAIT;
0a7de745 3363 }
c3c9b80d 3364 lck_mtx_lock(&nfs_buf_mutex);
6d2010ae 3365 wverf = nmp->nm_verf;
2d21ac55
A
3366 if (!nfs_buf_iterprepare(np, &blist, flags)) {
3367 while ((bp = LIST_FIRST(&blist))) {
3368 LIST_REMOVE(bp, nb_vnbufs);
3369 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
3370 error = nfs_buf_acquire(bp, NBAC_NOWAIT, 0, 0);
0a7de745 3371 if (error) {
2d21ac55 3372 continue;
0a7de745
A
3373 }
3374 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
2d21ac55 3375 nfs_buf_check_write_verifier(np, bp);
0a7de745 3376 }
6d2010ae
A
3377 if (((bp->nb_flags & (NB_DELWRI | NB_NEEDCOMMIT)) != (NB_DELWRI | NB_NEEDCOMMIT)) ||
3378 (bp->nb_verf != wverf)) {
2d21ac55
A
3379 nfs_buf_drop(bp);
3380 continue;
3381 }
3382 nfs_buf_remfree(bp);
b0d623f7
A
3383
3384 /* buffer UPLs will be grabbed *in order* below */
2d21ac55
A
3385
3386 FSDBG(557, bp, bp->nb_flags, bp->nb_valid, bp->nb_dirty);
3387 FSDBG(557, bp->nb_validoff, bp->nb_validend,
0a7de745 3388 bp->nb_dirtyoff, bp->nb_dirtyend);
55e303ae 3389
2d21ac55
A
3390 /*
3391 * Work out if all buffers are using the same cred
3392 * so we can deal with them all with one commit.
3393 *
3394 * Note: creds in bp's must be obtained by kauth_cred_ref
3395 * on the same original cred in order for them to be equal.
3396 */
3397 if (wcred_set == 0) {
3398 wcred = bp->nb_wcred;
0a7de745 3399 if (!IS_VALID_CRED(wcred)) {
2d21ac55 3400 panic("nfs: needcommit w/out wcred");
0a7de745 3401 }
2d21ac55
A
3402 wcred_set = 1;
3403 } else if ((wcred_set == 1) && wcred != bp->nb_wcred) {
3404 wcred_set = -1;
3405 }
3406 SET(bp->nb_flags, NB_WRITEINPROG);
3407
3408 /*
b0d623f7
A
3409 * Add this buffer to the list of buffers we are committing.
3410 * Buffers are inserted into the list in ascending order so that
3411 * we can take the UPLs in order after the list is complete.
2d21ac55 3412 */
b0d623f7
A
3413 prevlbp = NULL;
3414 LIST_FOREACH(lbp, &commitlist, nb_vnbufs) {
0a7de745 3415 if (bp->nb_lblkno < lbp->nb_lblkno) {
b0d623f7 3416 break;
0a7de745 3417 }
b0d623f7
A
3418 prevlbp = lbp;
3419 }
2d21ac55 3420 LIST_REMOVE(bp, nb_vnbufs);
0a7de745 3421 if (prevlbp) {
b0d623f7 3422 LIST_INSERT_AFTER(prevlbp, bp, nb_vnbufs);
0a7de745 3423 } else {
b0d623f7 3424 LIST_INSERT_HEAD(&commitlist, bp, nb_vnbufs);
0a7de745 3425 }
b0d623f7
A
3426
3427 /* update commit range start, end */
2d21ac55 3428 toff = NBOFF(bp) + bp->nb_dirtyoff;
0a7de745 3429 if (toff < off) {
2d21ac55 3430 off = toff;
0a7de745 3431 }
2d21ac55 3432 toff += (u_quad_t)(bp->nb_dirtyend - bp->nb_dirtyoff);
0a7de745 3433 if (toff > endoff) {
2d21ac55 3434 endoff = toff;
0a7de745 3435 }
2d21ac55
A
3436 }
3437 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
3438 }
c3c9b80d 3439 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
3440
3441 if (LIST_EMPTY(&commitlist)) {
3442 error = ENOBUFS;
3443 goto done;
3444 }
3445
b0d623f7
A
3446 /*
3447 * We need a UPL to prevent others from accessing the buffers during
3448 * our commit RPC(s).
3449 *
3450 * We used to also check for dirty pages here; if there were any we'd
3451 * abort the commit and force the entire buffer to be written again.
3452 * Instead of doing that, we just go ahead and commit the dirty range,
3453 * and then leave the buffer around with dirty pages that will be
3454 * written out later.
3455 */
3456 LIST_FOREACH(bp, &commitlist, nb_vnbufs) {
3457 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
3458 retv = nfs_buf_upl_setup(bp);
3459 if (retv) {
3460 /* Unable to create the UPL, the VM object probably no longer exists. */
3461 printf("nfs_flushcommits: upl create failed %d\n", retv);
f427ee49
A
3462 NBPGS_ERASE(&bp->nb_valid);
3463 NBPGS_ERASE(&bp->nb_dirty);
b0d623f7
A
3464 }
3465 }
3466 nfs_buf_upl_check(bp);
3467 }
3468
2d21ac55
A
3469 /*
3470 * Commit data on the server, as required.
3471 * If all bufs are using the same wcred, then use that with
3472 * one call for all of them, otherwise commit each one
3473 * separately.
3474 */
3475 if (wcred_set == 1) {
3476 /*
3477 * Note, it's possible the commit range could be >2^32-1.
3478 * If it is, we'll send one commit that covers the whole file.
3479 */
0a7de745 3480 if ((endoff - off) > 0xffffffff) {
2d21ac55 3481 count = 0;
0a7de745 3482 } else {
2d21ac55 3483 count = (endoff - off);
0a7de745 3484 }
6d2010ae 3485 retv = nmp->nm_funcs->nf_commit_rpc(np, off, count, wcred, wverf);
2d21ac55
A
3486 } else {
3487 retv = 0;
3488 LIST_FOREACH(bp, &commitlist, nb_vnbufs) {
3489 toff = NBOFF(bp) + bp->nb_dirtyoff;
3490 count = bp->nb_dirtyend - bp->nb_dirtyoff;
6d2010ae 3491 retv = nmp->nm_funcs->nf_commit_rpc(np, toff, count, bp->nb_wcred, wverf);
0a7de745 3492 if (retv) {
2d21ac55 3493 break;
0a7de745 3494 }
55e303ae 3495 }
2d21ac55
A
3496 }
3497
3498 /*
3499 * Now, either mark the blocks I/O done or mark the
3500 * blocks dirty, depending on whether the commit
3501 * succeeded.
3502 */
3503 while ((bp = LIST_FIRST(&commitlist))) {
3504 LIST_REMOVE(bp, nb_vnbufs);
3505 FSDBG(557, bp, retv, bp->nb_flags, bp->nb_dirty);
b0d623f7 3506 nfs_node_lock_force(np);
2d21ac55
A
3507 CLR(bp->nb_flags, (NB_NEEDCOMMIT | NB_WRITEINPROG));
3508 np->n_needcommitcnt--;
3509 CHECK_NEEDCOMMITCNT(np);
b0d623f7 3510 nfs_node_unlock(np);
2d21ac55
A
3511
3512 if (retv) {
3513 /* move back to dirty list */
c3c9b80d 3514 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55 3515 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
c3c9b80d 3516 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
3517 nfs_buf_release(bp, 1);
3518 continue;
1c79356b 3519 }
2d21ac55 3520
b0d623f7
A
3521 nfs_node_lock_force(np);
3522 np->n_numoutput++;
3523 nfs_node_unlock(np);
2d21ac55
A
3524 vnode_startwrite(NFSTOV(np));
3525 if (ISSET(bp->nb_flags, NB_DELWRI)) {
c3c9b80d 3526 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
3527 nfs_nbdwrite--;
3528 NFSBUFCNTCHK();
c3c9b80d 3529 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 3530 wakeup(&nfs_nbdwrite);
1c79356b 3531 }
0a7de745 3532 CLR(bp->nb_flags, (NB_READ | NB_DONE | NB_ERROR | NB_DELWRI));
2d21ac55
A
3533 /* if block still has dirty pages, we don't want it to */
3534 /* be released in nfs_buf_iodone(). So, don't set NB_ASYNC. */
f427ee49
A
3535 NBPGS_COPY(&dirty, &bp->nb_dirty);
3536 if (!nfs_buf_pgs_is_set(&dirty)) {
2d21ac55 3537 SET(bp->nb_flags, NB_ASYNC);
0a7de745 3538 } else {
2d21ac55 3539 CLR(bp->nb_flags, NB_ASYNC);
0a7de745 3540 }
1c79356b 3541
2d21ac55 3542 /* move to clean list */
c3c9b80d 3543 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55 3544 LIST_INSERT_HEAD(&np->n_cleanblkhd, bp, nb_vnbufs);
c3c9b80d 3545 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
3546
3547 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
3548
3549 nfs_buf_iodone(bp);
f427ee49 3550 if (nfs_buf_pgs_is_set(&dirty)) {
2d21ac55
A
3551 /* throw it back in as a delayed write buffer */
3552 CLR(bp->nb_flags, NB_DONE);
3553 nfs_buf_write_delayed(bp);
55e303ae 3554 }
2d21ac55 3555 }
1c79356b 3556
2d21ac55
A
3557done:
3558 FSDBG_BOT(557, np, 0, 0, error);
0a7de745 3559 return error;
2d21ac55
A
3560}
3561
3562/*
3563 * Flush all the blocks associated with a vnode.
0a7de745 3564 * Walk through the buffer pool and push any dirty pages
2d21ac55
A
3565 * associated with the vnode.
3566 */
3567int
3568nfs_flush(nfsnode_t np, int waitfor, thread_t thd, int ignore_writeerr)
3569{
3570 struct nfsbuf *bp;
3571 struct nfsbuflists blist;
3572 struct nfsmount *nmp = NFSTONMP(np);
3573 int error = 0, error2, slptimeo = 0, slpflag = 0;
3574 int nfsvers, flags, passone = 1;
3575
3576 FSDBG_TOP(517, np, waitfor, ignore_writeerr, 0);
3577
fe8ab488 3578 if (nfs_mount_gone(nmp)) {
2d21ac55
A
3579 error = ENXIO;
3580 goto out;
3581 }
3582 nfsvers = nmp->nm_vers;
0a7de745 3583 if (NMFLAG(nmp, INTR)) {
2d21ac55 3584 slpflag = PCATCH;
0a7de745 3585 }
2d21ac55
A
3586
3587 if (!LIST_EMPTY(&np->n_dirtyblkhd)) {
b0d623f7 3588 nfs_node_lock_force(np);
2d21ac55 3589 np->n_flag |= NMODIFIED;
b0d623f7 3590 nfs_node_unlock(np);
2d21ac55
A
3591 }
3592
c3c9b80d 3593 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
3594 while (np->n_bflag & NBFLUSHINPROG) {
3595 np->n_bflag |= NBFLUSHWANT;
c3c9b80d 3596 error = msleep(&np->n_bflag, &nfs_buf_mutex, slpflag, "nfs_flush", NULL);
6d2010ae
A
3597 if ((error && (error != EWOULDBLOCK)) ||
3598 ((error = nfs_sigintr(NFSTONMP(np), NULL, thd, 0)))) {
c3c9b80d 3599 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
3600 goto out;
3601 }
3602 }
3603 np->n_bflag |= NBFLUSHINPROG;
3604
3605 /*
3606 * On the first pass, start async/unstable writes on all
3607 * delayed write buffers. Then wait for all writes to complete
3608 * and call nfs_flushcommits() to commit any uncommitted buffers.
3609 * On all subsequent passes, start STABLE writes on any remaining
3610 * dirty buffers. Then wait for all writes to complete.
3611 */
3612again:
3613 FSDBG(518, LIST_FIRST(&np->n_dirtyblkhd), np->n_flag, 0, 0);
3614 if (!NFSTONMP(np)) {
c3c9b80d 3615 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
3616 error = ENXIO;
3617 goto done;
3618 }
3619
3620 /* Start/do any write(s) that are required. */
3621 if (!nfs_buf_iterprepare(np, &blist, NBI_DIRTY)) {
3622 while ((bp = LIST_FIRST(&blist))) {
3623 LIST_REMOVE(bp, nb_vnbufs);
3624 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
b0d623f7 3625 flags = (passone || !(waitfor == MNT_WAIT || waitfor == MNT_DWAIT)) ? NBAC_NOWAIT : 0;
0a7de745 3626 if (flags != NBAC_NOWAIT) {
2d21ac55 3627 nfs_buf_refget(bp);
0a7de745 3628 }
2d21ac55
A
3629 while ((error = nfs_buf_acquire(bp, flags, slpflag, slptimeo))) {
3630 FSDBG(524, bp, flags, bp->nb_lflags, bp->nb_flags);
0a7de745 3631 if (error == EBUSY) {
2d21ac55 3632 break;
0a7de745 3633 }
2d21ac55
A
3634 if (error) {
3635 error2 = nfs_sigintr(NFSTONMP(np), NULL, thd, 0);
3636 if (error2) {
0a7de745 3637 if (flags != NBAC_NOWAIT) {
2d21ac55 3638 nfs_buf_refrele(bp);
0a7de745 3639 }
2d21ac55 3640 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
c3c9b80d 3641 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
3642 error = error2;
3643 goto done;
3644 }
3645 if (slpflag == PCATCH) {
3646 slpflag = 0;
3647 slptimeo = 2 * hz;
3648 }
3649 }
3650 }
0a7de745 3651 if (flags != NBAC_NOWAIT) {
2d21ac55 3652 nfs_buf_refrele(bp);
0a7de745
A
3653 }
3654 if (error == EBUSY) {
2d21ac55 3655 continue;
0a7de745 3656 }
2d21ac55
A
3657 if (!bp->nb_np) {
3658 /* buffer is no longer valid */
3659 nfs_buf_drop(bp);
3660 continue;
3661 }
0a7de745 3662 if (ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
2d21ac55 3663 nfs_buf_check_write_verifier(np, bp);
0a7de745 3664 }
2d21ac55
A
3665 if (!ISSET(bp->nb_flags, NB_DELWRI)) {
3666 /* buffer is no longer dirty */
3667 nfs_buf_drop(bp);
3668 continue;
3669 }
3670 FSDBG(525, bp, passone, bp->nb_lflags, bp->nb_flags);
b0d623f7 3671 if ((passone || !(waitfor == MNT_WAIT || waitfor == MNT_DWAIT)) &&
2d21ac55
A
3672 ISSET(bp->nb_flags, NB_NEEDCOMMIT)) {
3673 nfs_buf_drop(bp);
3674 continue;
3675 }
3676 nfs_buf_remfree(bp);
c3c9b80d 3677 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 3678 if (ISSET(bp->nb_flags, NB_ERROR)) {
b0d623f7 3679 nfs_node_lock_force(np);
2d21ac55
A
3680 np->n_error = bp->nb_error ? bp->nb_error : EIO;
3681 np->n_flag |= NWRITEERR;
b0d623f7 3682 nfs_node_unlock(np);
2d21ac55 3683 nfs_buf_release(bp, 1);
c3c9b80d 3684 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
3685 continue;
3686 }
3687 SET(bp->nb_flags, NB_ASYNC);
3688 if (!passone) {
3689 /* NB_STABLE forces this to be written FILESYNC */
3690 SET(bp->nb_flags, NB_STABLE);
3691 }
3692 nfs_buf_write(bp);
c3c9b80d 3693 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
3694 }
3695 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
3696 }
c3c9b80d 3697 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 3698
b0d623f7 3699 if (waitfor == MNT_WAIT || waitfor == MNT_DWAIT) {
0a7de745
A
3700 while ((error = vnode_waitforwrites(NFSTOV(np), 0, slpflag, slptimeo, "nfsflush"))) {
3701 error2 = nfs_sigintr(NFSTONMP(np), NULL, thd, 0);
2d21ac55 3702 if (error2) {
0a7de745 3703 error = error2;
2d21ac55
A
3704 goto done;
3705 }
3706 if (slpflag == PCATCH) {
3707 slpflag = 0;
3708 slptimeo = 2 * hz;
55e303ae 3709 }
2d21ac55
A
3710 }
3711 }
55e303ae 3712
2d21ac55
A
3713 if (nfsvers != NFS_VER2) {
3714 /* loop while it looks like there are still buffers to be */
3715 /* commited and nfs_flushcommits() seems to be handling them. */
0a7de745
A
3716 while (np->n_needcommitcnt) {
3717 if (nfs_flushcommits(np, 0)) {
2d21ac55 3718 break;
0a7de745
A
3719 }
3720 }
2d21ac55 3721 }
55e303ae 3722
2d21ac55
A
3723 if (passone) {
3724 passone = 0;
3725 if (!LIST_EMPTY(&np->n_dirtyblkhd)) {
b0d623f7 3726 nfs_node_lock_force(np);
2d21ac55 3727 np->n_flag |= NMODIFIED;
b0d623f7 3728 nfs_node_unlock(np);
2d21ac55 3729 }
c3c9b80d 3730 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
3731 goto again;
3732 }
55e303ae 3733
b0d623f7 3734 if (waitfor == MNT_WAIT || waitfor == MNT_DWAIT) {
2d21ac55 3735 if (!LIST_EMPTY(&np->n_dirtyblkhd)) {
b0d623f7 3736 nfs_node_lock_force(np);
2d21ac55 3737 np->n_flag |= NMODIFIED;
b0d623f7 3738 nfs_node_unlock(np);
2d21ac55 3739 }
c3c9b80d 3740 lck_mtx_lock(&nfs_buf_mutex);
0a7de745 3741 if (!LIST_EMPTY(&np->n_dirtyblkhd)) {
2d21ac55 3742 goto again;
0a7de745 3743 }
c3c9b80d 3744 lck_mtx_unlock(&nfs_buf_mutex);
b0d623f7
A
3745 nfs_node_lock_force(np);
3746 /*
3747 * OK, it looks like there are no dirty blocks. If we have no
3748 * writes in flight and no one in the write code, we can clear
3749 * the modified flag. In order to make sure we see the latest
3750 * attributes and size, we also invalidate the attributes and
3751 * advance the attribute cache XID to guarantee that attributes
3752 * newer than our clearing of NMODIFIED will get loaded next.
3753 * (If we don't do this, it's possible for the flush's final
3754 * write/commit (xid1) to be executed in parallel with a subsequent
3755 * getattr request (xid2). The getattr could return attributes
3756 * from *before* the write/commit completed but the stale attributes
3757 * would be preferred because of the xid ordering.)
3758 */
3759 if (!np->n_wrbusy && !np->n_numoutput) {
2d21ac55 3760 np->n_flag &= ~NMODIFIED;
b0d623f7
A
3761 NATTRINVALIDATE(np);
3762 nfs_get_xid(&np->n_xid);
3763 }
2d21ac55 3764 } else {
b0d623f7 3765 nfs_node_lock_force(np);
0c530ab8
A
3766 }
3767
2d21ac55
A
3768 FSDBG(526, np->n_flag, np->n_error, 0, 0);
3769 if (!ignore_writeerr && (np->n_flag & NWRITEERR)) {
3770 error = np->n_error;
3771 np->n_flag &= ~NWRITEERR;
3772 }
b0d623f7 3773 nfs_node_unlock(np);
2d21ac55 3774done:
c3c9b80d 3775 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55 3776 flags = np->n_bflag;
0a7de745 3777 np->n_bflag &= ~(NBFLUSHINPROG | NBFLUSHWANT);
c3c9b80d 3778 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 3779 if (flags & NBFLUSHWANT) {
2d21ac55 3780 wakeup(&np->n_bflag);
0a7de745 3781 }
2d21ac55
A
3782out:
3783 FSDBG_BOT(517, np, error, ignore_writeerr, 0);
0a7de745 3784 return error;
1c79356b
A
3785}
3786
1c79356b 3787/*
55e303ae
A
3788 * Flush out and invalidate all buffers associated with a vnode.
3789 * Called with the underlying object locked.
1c79356b 3790 */
b0d623f7 3791int
91447636 3792nfs_vinvalbuf_internal(
2d21ac55 3793 nfsnode_t np,
91447636 3794 int flags,
2d21ac55 3795 thread_t thd,
91447636 3796 kauth_cred_t cred,
91447636
A
3797 int slpflag,
3798 int slptimeo)
1c79356b 3799{
55e303ae 3800 struct nfsbuf *bp;
91447636
A
3801 struct nfsbuflists blist;
3802 int list, error = 0;
9bccf70c 3803
55e303ae 3804 if (flags & V_SAVE) {
0a7de745
A
3805 if ((error = nfs_flush(np, MNT_WAIT, thd, (flags & V_IGNORE_WRITEERR)))) {
3806 return error;
3807 }
9bccf70c
A
3808 }
3809
c3c9b80d 3810 lck_mtx_lock(&nfs_buf_mutex);
55e303ae 3811 for (;;) {
91447636
A
3812 list = NBI_CLEAN;
3813 if (nfs_buf_iterprepare(np, &blist, list)) {
3814 list = NBI_DIRTY;
0a7de745 3815 if (nfs_buf_iterprepare(np, &blist, list)) {
91447636 3816 break;
0a7de745 3817 }
91447636
A
3818 }
3819 while ((bp = LIST_FIRST(&blist))) {
3820 LIST_REMOVE(bp, nb_vnbufs);
0a7de745 3821 if (list == NBI_CLEAN) {
91447636 3822 LIST_INSERT_HEAD(&np->n_cleanblkhd, bp, nb_vnbufs);
0a7de745 3823 } else {
91447636 3824 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
0a7de745 3825 }
91447636
A
3826 nfs_buf_refget(bp);
3827 while ((error = nfs_buf_acquire(bp, NBAC_REMOVE, slpflag, slptimeo))) {
2d21ac55 3828 FSDBG(556, np, bp, NBOFF(bp), bp->nb_flags);
91447636 3829 if (error != EAGAIN) {
2d21ac55 3830 FSDBG(554, np, bp, -1, error);
91447636
A
3831 nfs_buf_refrele(bp);
3832 nfs_buf_itercomplete(np, &blist, list);
c3c9b80d 3833 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 3834 return error;
55e303ae 3835 }
55e303ae 3836 }
91447636 3837 nfs_buf_refrele(bp);
2d21ac55 3838 FSDBG(554, np, bp, NBOFF(bp), bp->nb_flags);
c3c9b80d 3839 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55 3840 if ((flags & V_SAVE) && UBCINFOEXISTS(NFSTOV(np)) && bp->nb_np &&
91447636 3841 (NBOFF(bp) < (off_t)np->n_size)) {
2d21ac55 3842 /* extra paranoia: make sure we're not */
55e303ae 3843 /* somehow leaving any dirty data around */
f427ee49 3844 nfsbufpgs pagemask;
55e303ae 3845 int mustwrite = 0;
f427ee49
A
3846 off_t end = (NBOFF(bp) + bp->nb_bufsize > (off_t)np->n_size) ?
3847 (np->n_size - NBOFF(bp)) : bp->nb_bufsize;
55e303ae
A
3848 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
3849 error = nfs_buf_upl_setup(bp);
3850 if (error == EINVAL) {
3851 /* vm object must no longer exist */
3852 /* hopefully we don't need to do */
3853 /* anything for this buffer */
0a7de745 3854 } else if (error) {
91447636 3855 printf("nfs_vinvalbuf: upl setup failed %d\n", error);
0a7de745 3856 }
f427ee49
A
3857 NBPGS_ERASE(&bp->nb_valid);
3858 NBPGS_ERASE(&bp->nb_dirty);
55e303ae
A
3859 }
3860 nfs_buf_upl_check(bp);
3861 /* check for any dirty data before the EOF */
2d21ac55 3862 if ((bp->nb_dirtyend > 0) && (bp->nb_dirtyoff < end)) {
55e303ae 3863 /* clip dirty range to EOF */
2d21ac55 3864 if (bp->nb_dirtyend > end) {
55e303ae 3865 bp->nb_dirtyend = end;
0a7de745 3866 if (bp->nb_dirtyoff >= bp->nb_dirtyend) {
2d21ac55 3867 bp->nb_dirtyoff = bp->nb_dirtyend = 0;
0a7de745 3868 }
2d21ac55 3869 }
0a7de745 3870 if ((bp->nb_dirtyend > 0) && (bp->nb_dirtyoff < end)) {
2d21ac55 3871 mustwrite++;
0a7de745 3872 }
55e303ae 3873 }
f427ee49
A
3874 nfs_buf_pgs_get_page_mask(&pagemask, round_page_64(end) / PAGE_SIZE);
3875 nfs_buf_pgs_bit_and(&bp->nb_dirty, &pagemask, &bp->nb_dirty);
3876 if (nfs_buf_pgs_is_set(&bp->nb_dirty)) {
2d21ac55 3877 mustwrite++;
0a7de745 3878 }
91447636 3879 /* also make sure we'll have a credential to do the write */
0c530ab8 3880 if (mustwrite && !IS_VALID_CRED(bp->nb_wcred) && !IS_VALID_CRED(cred)) {
91447636
A
3881 printf("nfs_vinvalbuf: found dirty buffer with no write creds\n");
3882 mustwrite = 0;
3883 }
55e303ae 3884 if (mustwrite) {
2d21ac55 3885 FSDBG(554, np, bp, 0xd00dee, bp->nb_flags);
0a7de745 3886 if (!ISSET(bp->nb_flags, NB_PAGELIST)) {
55e303ae 3887 panic("nfs_vinvalbuf: dirty buffer without upl");
0a7de745 3888 }
55e303ae
A
3889 /* gotta write out dirty data before invalidating */
3890 /* (NB_STABLE indicates that data writes should be FILESYNC) */
3891 /* (NB_NOCACHE indicates buffer should be discarded) */
3892 CLR(bp->nb_flags, (NB_DONE | NB_ERROR | NB_INVAL | NB_ASYNC));
3893 SET(bp->nb_flags, NB_STABLE | NB_NOCACHE);
0c530ab8 3894 if (!IS_VALID_CRED(bp->nb_wcred)) {
91447636
A
3895 kauth_cred_ref(cred);
3896 bp->nb_wcred = cred;
3897 }
55e303ae
A
3898 error = nfs_buf_write(bp);
3899 // Note: bp has been released
3900 if (error) {
3901 FSDBG(554, bp, 0xd00dee, 0xbad, error);
b0d623f7 3902 nfs_node_lock_force(np);
6d2010ae
A
3903 if ((error != EINTR) && (error != ERESTART)) {
3904 np->n_error = error;
3905 np->n_flag |= NWRITEERR;
3906 }
91447636
A
3907 /*
3908 * There was a write error and we need to
3909 * invalidate attrs to sync with server.
3910 * (if this write was extending the file,
3911 * we may no longer know the correct size)
3912 */
3913 NATTRINVALIDATE(np);
b0d623f7 3914 nfs_node_unlock(np);
6d2010ae 3915 if ((error == EINTR) || (error == ERESTART)) {
b0d623f7
A
3916 /*
3917 * Abort on EINTR. If we don't, we could
3918 * be stuck in this loop forever because
3919 * the buffer will continue to stay dirty.
3920 */
c3c9b80d 3921 lck_mtx_lock(&nfs_buf_mutex);
b0d623f7 3922 nfs_buf_itercomplete(np, &blist, list);
c3c9b80d 3923 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 3924 return error;
b0d623f7 3925 }
55e303ae
A
3926 error = 0;
3927 }
c3c9b80d 3928 lck_mtx_lock(&nfs_buf_mutex);
91447636 3929 continue;
55e303ae
A
3930 }
3931 }
3932 SET(bp->nb_flags, NB_INVAL);
91447636 3933 // hold off on FREEUPs until we're done here
483a1d10 3934 nfs_buf_release(bp, 0);
c3c9b80d 3935 lck_mtx_lock(&nfs_buf_mutex);
55e303ae 3936 }
91447636 3937 nfs_buf_itercomplete(np, &blist, list);
55e303ae 3938 }
0a7de745 3939 if (!LIST_EMPTY(&(np)->n_dirtyblkhd) || !LIST_EMPTY(&(np)->n_cleanblkhd)) {
2d21ac55 3940 panic("nfs_vinvalbuf: flush/inval failed");
0a7de745 3941 }
c3c9b80d 3942 lck_mtx_unlock(&nfs_buf_mutex);
b0d623f7 3943 nfs_node_lock_force(np);
0a7de745 3944 if (!(flags & V_SAVE)) {
2d21ac55 3945 np->n_flag &= ~NMODIFIED;
0a7de745
A
3946 }
3947 if (vnode_vtype(NFSTOV(np)) == VREG) {
b0d623f7 3948 np->n_lastrahead = -1;
0a7de745 3949 }
b0d623f7 3950 nfs_node_unlock(np);
483a1d10 3951 NFS_BUF_FREEUP();
0a7de745 3952 return 0;
1c79356b
A
3953}
3954
55e303ae 3955
1c79356b
A
3956/*
3957 * Flush and invalidate all dirty buffers. If another process is already
3958 * doing the flush, just wait for completion.
3959 */
3960int
2d21ac55
A
3961nfs_vinvalbuf(vnode_t vp, int flags, vfs_context_t ctx, int intrflg)
3962{
3963 return nfs_vinvalbuf2(vp, flags, vfs_context_thread(ctx), vfs_context_ucred(ctx), intrflg);
3964}
3965
3966int
3967nfs_vinvalbuf2(vnode_t vp, int flags, thread_t thd, kauth_cred_t cred, int intrflg)
1c79356b 3968{
2d21ac55
A
3969 nfsnode_t np = VTONFS(vp);
3970 struct nfsmount *nmp = VTONMP(vp);
6d2010ae 3971 int error, slpflag, slptimeo, nflags, retry = 0;
fe8ab488 3972 int ubcflags = UBC_PUSHALL | UBC_SYNC | UBC_INVALIDATE;
cb323159 3973 struct timespec ts = { .tv_sec = 2, .tv_nsec = 0 };
91447636 3974 off_t size;
1c79356b 3975
2d21ac55 3976 FSDBG_TOP(554, np, flags, intrflg, 0);
55e303ae 3977
0a7de745 3978 if (nmp && !NMFLAG(nmp, INTR)) {
1c79356b 3979 intrflg = 0;
0a7de745 3980 }
1c79356b
A
3981 if (intrflg) {
3982 slpflag = PCATCH;
3983 slptimeo = 2 * hz;
3984 } else {
3985 slpflag = 0;
3986 slptimeo = 0;
3987 }
1c79356b 3988
2d21ac55 3989 /* First wait for any other process doing a flush to complete. */
c3c9b80d 3990 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55
A
3991 while (np->n_bflag & NBINVALINPROG) {
3992 np->n_bflag |= NBINVALWANT;
c3c9b80d 3993 msleep(&np->n_bflag, &nfs_buf_mutex, slpflag, "nfs_vinvalbuf", &ts);
b0d623f7 3994 if ((error = nfs_sigintr(VTONMP(vp), NULL, thd, 0))) {
c3c9b80d 3995 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 3996 return error;
1c79356b 3997 }
0a7de745 3998 if (np->n_bflag & NBINVALINPROG) {
6d2010ae 3999 slpflag = 0;
0a7de745 4000 }
1c79356b 4001 }
2d21ac55 4002 np->n_bflag |= NBINVALINPROG;
c3c9b80d 4003 lck_mtx_unlock(&nfs_buf_mutex);
2d21ac55
A
4004
4005 /* Now, flush as required. */
6d2010ae 4006again:
c3c9b80d
A
4007 /* If the mount is gone no sense to try and write anything. and hang trying to do IO. */
4008 if (nfs_mount_gone(nmp)) {
4009 flags &= ~V_SAVE;
4010 }
4011
2d21ac55
A
4012 error = nfs_vinvalbuf_internal(np, flags, thd, cred, slpflag, 0);
4013 while (error) {
4014 FSDBG(554, np, 0, 0, error);
0a7de745 4015 if ((error = nfs_sigintr(VTONMP(vp), NULL, thd, 0))) {
2d21ac55 4016 goto done;
0a7de745 4017 }
2d21ac55 4018 error = nfs_vinvalbuf_internal(np, flags, thd, cred, 0, slptimeo);
1c79356b 4019 }
2d21ac55 4020
c3c9b80d
A
4021 /* If the mount is gone no sense to try and write anything. and hang trying to do IO. */
4022 if (nfs_mount_gone(nmp)) {
4023 ubcflags &= ~UBC_PUSHALL;
4024 }
4025
2d21ac55 4026 /* get the pages out of vm also */
0a7de745 4027 if (UBCINFOEXISTS(vp) && (size = ubc_getsize(vp))) {
fe8ab488 4028 if ((error = ubc_msync(vp, 0, size, NULL, ubcflags))) {
0a7de745 4029 if (error == EINVAL) {
6d2010ae 4030 panic("nfs_vinvalbuf(): ubc_msync failed!, error %d", error);
0a7de745 4031 }
fe8ab488 4032 if (retry++ < 10) { /* retry invalidating a few times */
0a7de745 4033 if (retry > 1 || error == ENXIO) {
fe8ab488 4034 ubcflags &= ~UBC_PUSHALL;
0a7de745 4035 }
6d2010ae 4036 goto again;
fe8ab488 4037 }
6d2010ae 4038 /* give up */
fe8ab488 4039 printf("nfs_vinvalbuf(): ubc_msync failed!, error %d\n", error);
6d2010ae 4040 }
0a7de745 4041 }
2d21ac55 4042done:
c3c9b80d 4043 lck_mtx_lock(&nfs_buf_mutex);
2d21ac55 4044 nflags = np->n_bflag;
0a7de745 4045 np->n_bflag &= ~(NBINVALINPROG | NBINVALWANT);
c3c9b80d 4046 lck_mtx_unlock(&nfs_buf_mutex);
0a7de745 4047 if (nflags & NBINVALWANT) {
2d21ac55 4048 wakeup(&np->n_bflag);
0a7de745 4049 }
91447636 4050
2d21ac55 4051 FSDBG_BOT(554, np, flags, intrflg, error);
0a7de745 4052 return error;
1c79356b
A
4053}
4054
6d2010ae
A
4055/*
4056 * Wait for any busy buffers to complete.
4057 */
4058void
4059nfs_wait_bufs(nfsnode_t np)
4060{
4061 struct nfsbuf *bp;
4062 struct nfsbuflists blist;
4063 int error = 0;
4064
c3c9b80d 4065 lck_mtx_lock(&nfs_buf_mutex);
6d2010ae
A
4066 if (!nfs_buf_iterprepare(np, &blist, NBI_CLEAN)) {
4067 while ((bp = LIST_FIRST(&blist))) {
4068 LIST_REMOVE(bp, nb_vnbufs);
4069 LIST_INSERT_HEAD(&np->n_cleanblkhd, bp, nb_vnbufs);
4070 nfs_buf_refget(bp);
4071 while ((error = nfs_buf_acquire(bp, 0, 0, 0))) {
4072 if (error != EAGAIN) {
4073 nfs_buf_refrele(bp);
4074 nfs_buf_itercomplete(np, &blist, NBI_CLEAN);
c3c9b80d 4075 lck_mtx_unlock(&nfs_buf_mutex);
6d2010ae
A
4076 return;
4077 }
4078 }
4079 nfs_buf_refrele(bp);
4080 nfs_buf_drop(bp);
4081 }
4082 nfs_buf_itercomplete(np, &blist, NBI_CLEAN);
4083 }
4084 if (!nfs_buf_iterprepare(np, &blist, NBI_DIRTY)) {
4085 while ((bp = LIST_FIRST(&blist))) {
4086 LIST_REMOVE(bp, nb_vnbufs);
4087 LIST_INSERT_HEAD(&np->n_dirtyblkhd, bp, nb_vnbufs);
4088 nfs_buf_refget(bp);
4089 while ((error = nfs_buf_acquire(bp, 0, 0, 0))) {
4090 if (error != EAGAIN) {
4091 nfs_buf_refrele(bp);
4092 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
c3c9b80d 4093 lck_mtx_unlock(&nfs_buf_mutex);
6d2010ae
A
4094 return;
4095 }
4096 }
4097 nfs_buf_refrele(bp);
4098 nfs_buf_drop(bp);
4099 }
4100 nfs_buf_itercomplete(np, &blist, NBI_DIRTY);
4101 }
c3c9b80d 4102 lck_mtx_unlock(&nfs_buf_mutex);
6d2010ae
A
4103}
4104
4105
1c79356b 4106/*
2d21ac55
A
4107 * Add an async I/O request to the mount's async I/O queue and make
4108 * sure that an nfsiod will service it.
1c79356b 4109 */
2d21ac55
A
4110void
4111nfs_asyncio_finish(struct nfsreq *req)
1c79356b
A
4112{
4113 struct nfsmount *nmp;
2d21ac55
A
4114 struct nfsiod *niod;
4115 int started = 0;
1c79356b 4116
2d21ac55 4117 FSDBG_TOP(552, nmp, 0, 0, 0);
1c79356b 4118again:
fe8ab488
A
4119 nmp = req->r_nmp;
4120
0a7de745 4121 if (nmp == NULL) {
2d21ac55 4122 return;
0a7de745 4123 }
fe8ab488 4124
c3c9b80d 4125 lck_mtx_lock(&nfsiod_mutex);
2d21ac55
A
4126 niod = nmp->nm_niod;
4127
4128 /* grab an nfsiod if we don't have one already */
4129 if (!niod) {
4130 niod = TAILQ_FIRST(&nfsiodfree);
4131 if (niod) {
4132 TAILQ_REMOVE(&nfsiodfree, niod, niod_link);
4133 TAILQ_INSERT_TAIL(&nfsiodwork, niod, niod_link);
4134 niod->niod_nmp = nmp;
4135 } else if (((nfsiod_thread_count < NFSIOD_MAX) || (nfsiod_thread_count <= 0)) && (started < 4)) {
1c79356b 4136 /*
2d21ac55
A
4137 * Try starting a new thread.
4138 * We may try a couple times if other callers
4139 * get the new threads before we do.
1c79356b 4140 */
c3c9b80d 4141 lck_mtx_unlock(&nfsiod_mutex);
2d21ac55 4142 started++;
0a7de745 4143 if (!nfsiod_start()) {
2d21ac55 4144 goto again;
0a7de745 4145 }
c3c9b80d 4146 lck_mtx_lock(&nfsiod_mutex);
1c79356b 4147 }
91447636 4148 }
55e303ae 4149
39037602
A
4150 /*
4151 * If we got here while being on the resendq we need to get off. This
4152 * happens when the timer fires and errors out requests from nfs_sigintr
4153 * or we receive a reply (UDP case) while being on the resend queue so
4154 * we're just finishing up and are not going to be resent.
4155 */
4156 lck_mtx_lock(&req->r_mtx);
4157 if (req->r_flags & R_RESENDQ) {
4158 lck_mtx_lock(&nmp->nm_lock);
f427ee49 4159 if ((req->r_flags & R_RESENDQ) && req->r_rchain.tqe_next != NFSREQNOLIST) {
39037602
A
4160 NFS_BIO_DBG("Proccessing async request on resendq. Removing");
4161 TAILQ_REMOVE(&nmp->nm_resendq, req, r_rchain);
f427ee49 4162 req->r_flags &= ~R_RESENDQ;
39037602
A
4163 req->r_rchain.tqe_next = NFSREQNOLIST;
4164 assert(req->r_refs > 1);
4165 /* Remove resendq reference */
4166 req->r_refs--;
4167 }
4168 lck_mtx_unlock(&nmp->nm_lock);
39037602
A
4169 }
4170 lck_mtx_unlock(&req->r_mtx);
4171
0a7de745 4172 if (req->r_achain.tqe_next == NFSREQNOLIST) {
2d21ac55 4173 TAILQ_INSERT_TAIL(&nmp->nm_iodq, req, r_achain);
0a7de745 4174 }
2d21ac55
A
4175
4176 /* If this mount doesn't already have an nfsiod working on it... */
4177 if (!nmp->nm_niod) {
4178 if (niod) { /* give it the nfsiod we just grabbed */
4179 nmp->nm_niod = niod;
c3c9b80d 4180 lck_mtx_unlock(&nfsiod_mutex);
2d21ac55
A
4181 wakeup(niod);
4182 } else if (nfsiod_thread_count > 0) {
fe8ab488 4183 /* just queue it up on nfsiod mounts queue if needed */
0a7de745 4184 if (nmp->nm_iodlink.tqe_next == NFSNOLIST) {
fe8ab488 4185 TAILQ_INSERT_TAIL(&nfsiodmounts, nmp, nm_iodlink);
0a7de745 4186 }
c3c9b80d 4187 lck_mtx_unlock(&nfsiod_mutex);
2d21ac55
A
4188 } else {
4189 printf("nfs_asyncio(): no nfsiods? %d %d (%d)\n", nfsiod_thread_count, NFSIOD_MAX, started);
c3c9b80d 4190 lck_mtx_unlock(&nfsiod_mutex);
2d21ac55
A
4191 /* we have no other option but to be persistent */
4192 started = 0;
4193 goto again;
1c79356b 4194 }
2d21ac55 4195 } else {
c3c9b80d 4196 lck_mtx_unlock(&nfsiod_mutex);
1c79356b
A
4197 }
4198
2d21ac55
A
4199 FSDBG_BOT(552, nmp, 0, 0, 0);
4200}
1c79356b 4201
2d21ac55
A
4202/*
4203 * queue up async I/O request for resend
f427ee49 4204 * Must be called with req->r_mtx locked.
2d21ac55
A
4205 */
4206void
4207nfs_asyncio_resend(struct nfsreq *req)
4208{
4209 struct nfsmount *nmp = req->r_nmp;
1c79356b 4210
0a7de745 4211 if (nfs_mount_gone(nmp)) {
2d21ac55 4212 return;
0a7de745 4213 }
3e170ce0 4214
cb323159 4215#if CONFIG_NFS_GSS
2d21ac55 4216 nfs_gss_clnt_rpcdone(req);
cb323159 4217#endif
2d21ac55 4218 lck_mtx_lock(&nmp->nm_lock);
b0d623f7 4219 if (!(req->r_flags & R_RESENDQ)) {
2d21ac55
A
4220 TAILQ_INSERT_TAIL(&nmp->nm_resendq, req, r_rchain);
4221 req->r_flags |= R_RESENDQ;
3e170ce0
A
4222 /*
4223 * We take a reference on this request so that it can't be
4224 * destroyed while a resend is queued or in progress.
4225 */
4226 nfs_request_ref(req, 1);
1c79356b 4227 }
2d21ac55
A
4228 nfs_mount_sock_thread_wake(nmp);
4229 lck_mtx_unlock(&nmp->nm_lock);
1c79356b
A
4230}
4231
4232/*
b0d623f7
A
4233 * Read directory data into a buffer.
4234 *
4235 * Buffer will be filled (unless EOF is hit).
4236 * Buffers after this one may also be completely/partially filled.
1c79356b
A
4237 */
4238int
2d21ac55 4239nfs_buf_readdir(struct nfsbuf *bp, vfs_context_t ctx)
1c79356b 4240{
b0d623f7
A
4241 nfsnode_t np = bp->nb_np;
4242 struct nfsmount *nmp = NFSTONMP(np);
4243 int error = 0;
1c79356b 4244
0a7de745
A
4245 if (nfs_mount_gone(nmp)) {
4246 return ENXIO;
4247 }
55e303ae 4248
0a7de745 4249 if (nmp->nm_vers < NFS_VER4) {
b0d623f7 4250 error = nfs3_readdir_rpc(np, bp, ctx);
cb323159
A
4251 }
4252#if CONFIG_NFS4
4253 else {
b0d623f7 4254 error = nfs4_readdir_rpc(np, bp, ctx);
0a7de745 4255 }
cb323159 4256#endif
b0d623f7 4257 if (error && (error != NFSERR_DIRBUFDROPPED)) {
55e303ae
A
4258 SET(bp->nb_flags, NB_ERROR);
4259 bp->nb_error = error;
1c79356b 4260 }
0a7de745 4261 return error;
1c79356b 4262}
ea3f0419
A
4263
4264#endif /* CONFIG_NFS_CLIENT */