+/*
+ * 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.
+ */
+struct nfs_dir_buf_header {
+ uint16_t ndbh_flags; /* flags (see below) */
+ uint16_t ndbh_count; /* # of entries */
+ off_t ndbh_entry_end; /* end offset of direntry data */
+ uint32_t ndbh_ncgen; /* name cache generation# */
+ uint32_t ndbh_pad; /* reserved */
+};
+/* ndbh_flags */
+#define NDB_FULL 0x0001 /* buffer has been filled */
+#define NDB_EOF 0x0002 /* buffer contains EOF */
+#define NDB_PLUS 0x0004 /* buffer contains RDIRPLUS data */
+
+#define NFS_DIR_BUF_FIRST_DIRENTRY(BP) \
+ ((struct direntry*)((char*)((BP)->nb_data) + sizeof(*ndbhp)))
+#define NFS_DIR_BUF_NVATTR(BP, IDX) \
+ (&((struct nfs_vattr*)((char*)((BP)->nb_data) + (BP)->nb_bufsize))[-((IDX)+1)])
+#define NFS_DIRENTRY_LEN(namlen) \
+ ((sizeof(struct direntry) + (namlen) - (MAXPATHLEN-1) + 7) & ~7)
+#define NFS_DIRENTRY_LEN_16(namlen) \
+ (uint16_t)(NFS_DIRENTRY_LEN(namlen)); assert((namlen) <= UINT16_MAX);
+#define NFS_DIRENT_LEN(namlen) \
+ ((sizeof(struct dirent) - (NAME_MAX+1)) + (((namlen) + 1 + 3) &~ 3))
+#define NFS_DIRENTRY_NEXT(DP) \
+ ((struct direntry*)((char*)(DP) + (DP)->d_reclen))
+#define NFS_DIR_COOKIE_POTENTIALLY_TRUNCATED(C) \
+ ((C) && ((((C) >> 32) == 0) || (((C) & 0x80000000ULL) && (((C) >> 32) == 0xffffffff))))
+#define NFS_DIR_COOKIE_SAME32(C1, C2) \
+ (((C1) & 0xffffffffULL) == ((C2) & 0xffffffffULL))
+
+/*
+ * NFS directory cookie cache
+ *
+ * This structure is used to cache cookie-to-buffer mappings for
+ * cookies recently returned from READDIR. The entries are kept in an
+ * array. The most-recently-used (MRU) list is headed by the entry at
+ * index "mru". The index of the next entry in the list is kept in the
+ * "next" array. (An index value of -1 marks an invalid entry.)
+ */
+#define NFSNUMCOOKIES 14