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