+ * NFS buf pages struct
+ */
+typedef struct {
+ uint64_t pages[8];
+} nfsbufpgs;
+
+/*
+ * The nfsbuf is the nfs equivalent to a struct buf.
+ */
+struct nfsbuf {
+ LIST_ENTRY(nfsbuf) nb_hash; /* hash chain */
+ LIST_ENTRY(nfsbuf) nb_vnbufs; /* nfsnode's nfsbuf chain */
+ TAILQ_ENTRY(nfsbuf) nb_free; /* free list position if not active. */
+ os_refcnt_t nb_refs; /* outstanding references. */
+ daddr64_t nb_lblkno; /* logical block number. */
+ uint64_t nb_verf; /* V3 write verifier */
+ time_t nb_timestamp; /* buffer timestamp */
+ nfsbufpgs nb_valid; /* valid pages in buf */
+ nfsbufpgs nb_dirty; /* dirty pages in buf */
+ caddr_t nb_data; /* mapped buffer */
+ nfsnode_t nb_np; /* nfsnode buffer belongs to */
+ kauth_cred_t nb_rcred; /* read credentials reference */
+ kauth_cred_t nb_wcred; /* write credentials reference */
+ void * nb_pagelist; /* upl */
+ volatile uint32_t nb_flags; /* NB_* flags. */
+ volatile uint32_t nb_lflags; /* NBL_* flags. */
+ uint32_t nb_bufsize; /* buffer size */
+ int nb_error; /* errno value. */
+ int nb_commitlevel; /* lowest write commit level */
+ off_t nb_validoff; /* offset in buffer of valid region. */
+ off_t nb_validend; /* offset of end of valid region. */
+ off_t nb_dirtyoff; /* offset in buffer of dirty region. */
+ off_t nb_dirtyend; /* offset of end of dirty region. */
+ off_t nb_offio; /* offset in buffer of I/O region. */
+ off_t nb_endio; /* offset of end of I/O region. */
+ uint64_t nb_rpcs; /* Count of RPCs remaining for this buffer. */
+};
+
+#define NFS_MAXBSIZE (8 * 64 * PAGE_SIZE) /* valid/dirty page masks limit buffer size */
+
+#define NFS_A_LOT_OF_NEEDCOMMITS 256 /* max# uncommitted buffers for a node */
+#define NFS_A_LOT_OF_DELAYED_WRITES MAX(nfsbufcnt/8,512) /* max# "delwri" buffers in system */
+
+/*
+ * These flags are kept in b_lflags...
+ * nfs_buf_mutex must be held before examining/updating
+ */
+#define NBL_BUSY 0x00000001 /* I/O in progress. */
+#define NBL_WANTED 0x00000002 /* Process wants this buffer. */
+
+/*
+ * These flags are kept in nb_flags and they're (purposefully)
+ * very similar to the B_* flags for struct buf.
+ * nfs_buf_mutex is not needed to examine/update these.
+ */
+#define NB_STALEWVERF 0x00000001 /* write verifier changed on us */
+#define NB_NEEDCOMMIT 0x00000002 /* buffer needs to be committed */
+#define NB_ASYNC 0x00000004 /* Start I/O, do not wait. */
+#define NB_CACHE 0x00000020 /* buffer data found in the cache */
+#define NB_STABLE 0x00000040 /* write FILESYNC not UNSTABLE */
+#define NB_DELWRI 0x00000080 /* delayed write: dirty range needs to be written */
+#define NB_DONE 0x00000200 /* I/O completed. */
+#define NB_EINTR 0x00000400 /* I/O was interrupted */
+#define NB_ERROR 0x00000800 /* I/O error occurred. */
+#define NB_INVAL 0x00002000 /* Does not contain valid info. */
+#define NB_NCRDAHEAD 0x00004000 /* "nocache readahead" data */
+#define NB_NOCACHE 0x00008000 /* Do not cache block after use. */
+#define NB_WRITE 0x00000000 /* Write buffer (pseudo flag). */
+#define NB_READ 0x00100000 /* Read buffer. */
+#define NB_MULTASYNCRPC 0x00200000 /* multiple async RPCs issued for buffer */
+#define NB_PAGELIST 0x00400000 /* Buffer describes pagelist I/O. */
+#define NB_WRITEINPROG 0x01000000 /* Write in progress. */
+#define NB_META 0x40000000 /* buffer contains meta-data. */
+
+/* Flags for operation type in nfs_buf_get() */
+#define NBLK_READ 0x00000001 /* buffer for read */
+#define NBLK_WRITE 0x00000002 /* buffer for write */
+#define NBLK_META 0x00000004 /* buffer for metadata */
+#define NBLK_OPMASK 0x00000007 /* operation mask */
+/* modifiers for above flags... */
+#define NBLK_NOWAIT 0x40000000 /* don't wait on busy buffer */
+#define NBLK_ONLYVALID 0x80000000 /* only return cached buffer */
+
+/* These flags are used for nfsbuf iterating */
+#define NBI_ITER 0x01 /* iteration in progress */
+#define NBI_ITERWANT 0x02 /* waiting to iterate */
+#define NBI_CLEAN 0x04 /* requesting clean buffers */
+#define NBI_DIRTY 0x08 /* requesting dirty buffers */
+#define NBI_NOWAIT 0x10 /* don't block on NBI_ITER */
+
+/* Flags for nfs_buf_acquire */
+#define NBAC_NOWAIT 0x01 /* Don't wait if buffer is busy */
+#define NBAC_REMOVE 0x02 /* Remove from free list once buffer is acquired */
+
+/* macros for nfsbufpgs */
+#define NBPGS_STRUCT_SIZE sizeof(nfsbufpgs)
+#define NBPGS_ELEMENT_SIZE sizeof(((nfsbufpgs *)0)->pages[0])
+#define NBPGS_ELEMENT_PAGES (8 * NBPGS_ELEMENT_SIZE)
+#define NBPGS_ELEMENTS (NBPGS_STRUCT_SIZE / NBPGS_ELEMENT_SIZE)
+#define NBPGS_ERASE(NFSBP) bzero((NFSBP), NBPGS_STRUCT_SIZE)
+#define NBPGS_COPY(NFSBPDST, NFSBPSRC) memcpy((NFSBPDST), (NFSBPSRC), NBPGS_STRUCT_SIZE)
+#define NBPGS_IS_EQUAL(NFSBP1, NFSBP2) (memcmp((NFSBP1), (NFSBP2), NBPGS_STRUCT_SIZE) == 0)
+#define NBPGS_GET(NFSBP, P) ((NFSBP)->pages[((P)/NBPGS_ELEMENT_PAGES)] & (1LLU << ((P) % NBPGS_ELEMENT_PAGES)))
+#define NBPGS_SET(NFSBP, P) ((NFSBP)->pages[((P)/NBPGS_ELEMENT_PAGES)] |= (1LLU << ((P) % NBPGS_ELEMENT_PAGES)))
+#define NBPGS_UNSET(NFSBP, P) ((NFSBP)->pages[((P)/NBPGS_ELEMENT_PAGES)] &= ~(1LLU << ((P) % NBPGS_ELEMENT_PAGES)))
+
+/* some convenience macros... */
+#define NBOFF(BP) ((off_t)(BP)->nb_lblkno * (off_t)(BP)->nb_bufsize)
+#define NBPGVALID(BP, P) NBPGS_GET(&((BP)->nb_valid), (P))
+#define NBPGDIRTY(BP, P) NBPGS_GET(&((BP)->nb_dirty), (P))
+#define NBPGVALID_SET(BP, P) NBPGS_SET(&((BP)->nb_valid), (P))
+#define NBPGDIRTY_SET(BP, P) NBPGS_SET(&((BP)->nb_dirty), (P))
+
+#define NBUFSTAMPVALID(BP) ((BP)->nb_timestamp != ~0)
+#define NBUFSTAMPINVALIDATE(BP) ((BP)->nb_timestamp = ~0)
+
+#define NFS_BUF_MAP(BP) \
+ do { \
+ if (!(BP)->nb_data && nfs_buf_map(BP)) \
+ panic("nfs_buf_map failed"); \
+ } while (0)
+
+LIST_HEAD(nfsbuflists, nfsbuf);
+TAILQ_HEAD(nfsbuffreehead, nfsbuf);
+
+extern lck_mtx_t nfs_buf_mutex;
+extern int nfsbufcnt, nfsbufmin, nfsbufmax, nfsbufmetacnt, nfsbufmetamax;
+extern int nfsbuffreecnt, nfsbuffreemetacnt, nfsbufdelwricnt, nfsneedbuffer;
+extern int nfs_nbdwrite;
+extern struct nfsbuffreehead nfsbuffree, nfsbufdelwri;
+
+#ifdef NFSBUFDEBUG
+#define NFSBUFCNTCHK() \
+ do { \
+ if ( (nfsbufcnt < 0) || \
+ (nfsbufcnt > nfsbufmax) || \
+ (nfsbufmetacnt < 0) || \
+ (nfsbufmetacnt > nfsbufmetamax) || \
+ (nfsbufmetacnt > nfsbufcnt) || \
+ (nfsbuffreecnt < 0) || \
+ (nfsbuffreecnt > nfsbufmax) || \
+ (nfsbuffreecnt > nfsbufcnt) || \
+ (nfsbuffreemetacnt < 0) || \
+ (nfsbuffreemetacnt > nfsbufmax) || \
+ (nfsbuffreemetacnt > nfsbufcnt) || \
+ (nfsbuffreemetacnt > nfsbufmetamax) || \
+ (nfsbuffreemetacnt > nfsbufmetacnt) || \
+ (nfsbufdelwricnt < 0) || \
+ (nfsbufdelwricnt > nfsbufmax) || \
+ (nfsbufdelwricnt > nfsbufcnt) || \
+ (nfs_nbdwrite < 0) || \
+ (nfs_nbdwrite > nfsbufcnt) || \
+ 0) \
+ panic("nfsbuf count error: max %d meta %d cnt %d meta %d free %d meta %d delwr %d bdw %d\n", \
+ nfsbufmax, nfsbufmetamax, nfsbufcnt, nfsbufmetacnt, nfsbuffreecnt, nfsbuffreemetacnt, \
+ nfsbufdelwricnt, nfs_nbdwrite); \
+ } while (0)
+#else
+#define NFSBUFCNTCHK()
+#endif
+
+/*
+ * NFS directory buffer
+ *
+ * Each buffer for a directory consists of:
+ *
+ * - a small header
+ * - a packed list of direntry structures
+ * (if RDIRPLUS is enabled, a file handle and attrstamp are
+ * packed after the direntry name.)
+ * - free/unused space
+ * - if RDIRPLUS is enabled, an array of attributes
+ * that is indexed backwards from the end of the buffer.