diff --git a/NEWS b/NEWS
index 7889461f16..7d106fcb9f 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,50 @@
                        User-Visible OpenAFS Changes
 
+OpenAFS 1.8.16pre1
+
+  All platforms
+
+    * A build error has been fixed for platforms with glibc 2.43 or later and C
+      compilers which are enforcing ISO C23 rules. This change fixes a
+      build error on Ubuntu 26.04 development releases (16751).
+
+    * A build error during parallel builds has been fixed (16758).
+
+  Linux client
+
+    * A bug that caused dentries to not properly be revalidated on Linux 6.17
+      has been fixed. The bug manifested as not seeing filename changes made on
+      other clients; that is, files being renamed, created, or deleted (16729).
+
+    * A Linux kernel function for folio migration has been implemented,
+      silencing a kernel log message warning that a fallback implementation
+      was used instead (16709).
+
+    * A potential bug (with no known path to trigger it) has been fixed where
+      a recursive writeback operation left page dirty flags in an inconsistent
+      state (potentially leading to data corruption) or leaked a folio
+      reference (in effect, leaking memory) (16702).
+
+    * A Linux kernel log message is issued if an unexpected recursive call to
+      writeback occurs. While this condition should not cause any problems,
+      the message should be reported. (16705).
+
+    * Support for Linux kernel 6.18 has been added (16701, 16703, 16704).
+
+    * Support for Linux kernel 6.19 has been added (16707, 16708).
+
+    * Support for Linux kernel 7.0 has been added (16738).
+
+  MacOS client
+
+    * Several binaries (bos, cmdebug, fs, xstat_cm_test, xstat_fs_test, and
+      backup) are now distributed as x86_64-only thin binaries, instead of as
+      universal binaries like all other commands. On arm64, these commands will
+      still run automatically via Rosetta2. This avoids an issue where those
+      binaries experienced segfaults in LWP in arm64 code. The original
+      universal binaries for these utilities are distributed in
+      /Library/OpenAFS/Tools/bin.orig/. (16753)
+
 OpenAFS 1.8.15
 
   All platforms
diff --git a/configure-libafs.ac b/configure-libafs.ac
index 0f3881a341..97811093c0 100644
--- a/configure-libafs.ac
+++ b/configure-libafs.ac
@@ -4,7 +4,7 @@ AC_CONFIG_AUX_DIR([build-tools])
 AC_CONFIG_SRCDIR([src/libafs/Makefile.common.in])
 
 AC_CONFIG_HEADERS([src/config/afsconfig.h])
-MACOS_VERSION=1.8.15
+MACOS_VERSION=1.8.16fc1
 
 AC_SUBST([MACOS_VERSION])
 
diff --git a/configure.ac b/configure.ac
index ec7ba2b203..3dbacb219b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -5,7 +5,7 @@ AC_CONFIG_MACRO_DIR([src/cf])
 AC_CONFIG_SRCDIR([src/config/stds.h])
 
 AC_CONFIG_HEADERS([src/config/afsconfig.h])
-MACOS_VERSION=1.8.15
+MACOS_VERSION=1.8.16fc1
 
 AC_SUBST([MACOS_VERSION])
 
diff --git a/src/afs/LINUX/osi_misc.c b/src/afs/LINUX/osi_misc.c
index 27f73cd1f1..89d98e5576 100644
--- a/src/afs/LINUX/osi_misc.c
+++ b/src/afs/LINUX/osi_misc.c
@@ -75,11 +75,17 @@ osi_lookupname_internal(char *aname, int followlink, struct vfsmount **mnt,
     return code;
 }
 
+static void
+afs_putname(char *name)
+{
+    __putname(name);
+}
+
 static char *
 afs_getname(char *aname)
 {
     int len;
-    char *name = kmem_cache_alloc(names_cachep, GFP_KERNEL);
+    char *name = __getname();
 
     if (!name)
 	return ERR_PTR(-ENOMEM);
@@ -94,16 +100,10 @@ afs_getname(char *aname)
     return name;
 
 error:
-    kmem_cache_free(names_cachep, name);
+    afs_putname(name);
     return ERR_PTR(len);
 }
 
-static void
-afs_putname(char *name)
-{
-    kmem_cache_free(names_cachep, name);
-}
-
 int
 osi_lookupname(char *aname, uio_seg_t seg, int followlink,
 	       struct dentry **dpp)
diff --git a/src/afs/LINUX/osi_vfsops.c b/src/afs/LINUX/osi_vfsops.c
index 18506474ee..9d13c937a2 100644
--- a/src/afs/LINUX/osi_vfsops.c
+++ b/src/afs/LINUX/osi_vfsops.c
@@ -21,6 +21,9 @@
 #include "afs/sysincludes.h"
 #include "afsincludes.h"
 #include "afs/afs_stats.h"
+#if defined(HAVE_LINUX_GET_TREE_NODEV)
+# include "linux/fs_context.h"
+#endif
 
 #include "osi_compat.h"
 
@@ -38,56 +41,69 @@ extern struct afs_q VLRU;
 
 extern struct dentry_operations afs_dentry_operations;
 
-/* Forward declarations */
-static int afs_root(struct super_block *afsp);
-static int afs_fill_super(struct super_block *sb, void *data, int silent);
-
-
-/*
- * afs_mount (2.6.37+) and afs_get_sb (2.6.36-) are the entry
- * points from the vfs when mounting afs.  The super block
- * structure is setup in the afs_fill_super callback function.
- */
+struct backing_dev_info *afs_backing_dev_info;
 
-#if defined(STRUCT_FILE_SYSTEM_TYPE_HAS_MOUNT)
-static struct dentry *
-afs_mount(struct file_system_type *fs_type, int flags,
-	  const char *dev_name, void *data)
-{
-    return mount_nodev(fs_type, flags, data, afs_fill_super);
-}
-#elif defined(GET_SB_HAS_STRUCT_VFSMOUNT)
+/* afs_root - stat the root of the file system. AFS global held on entry. */
 static int
-afs_get_sb(struct file_system_type *fs_type, int flags,
-	   const char *dev_name, void *data, struct vfsmount *mnt)
-{
-    return get_sb_nodev(fs_type, flags, data, afs_fill_super, mnt);
-}
-#else
-static struct super_block *
-afs_get_sb(struct file_system_type *fs_type, int flags,
-	   const char *dev_name, void *data)
+afs_root(struct super_block *afsp)
 {
-    return get_sb_nodev(fs_type, flags, data, afs_fill_super);
-}
-#endif
+    afs_int32 code = 0;
+    struct vcache *tvp = 0;
 
-struct file_system_type afs_fs_type = {
-    .owner = THIS_MODULE,
-    .name = "afs",
-#if defined(STRUCT_FILE_SYSTEM_TYPE_HAS_MOUNT)
-    .mount = afs_mount,
+    AFS_STATCNT(afs_root);
+    if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
+	tvp = afs_globalVp;
+    } else {
+	struct vrequest *treq = NULL;
+	cred_t *credp = crref();
+
+	if (afs_globalVp) {
+	    afs_PutVCache(afs_globalVp);
+	    afs_globalVp = NULL;
+	}
+
+	if (!(code = afs_CreateReq(&treq, credp)) && !(code = afs_CheckInit())) {
+	    tvp = afs_GetVCache(&afs_rootFid, treq);
+	    if (tvp) {
+		struct inode *ip = AFSTOV(tvp);
+		struct vattr *vattr = NULL;
+
+		code = afs_CreateAttr(&vattr);
+		if (!code) {
+		    afs_getattr(tvp, vattr, credp);
+		    afs_fill_inode(ip, vattr);
+
+		    /* setup super_block and mount point inode. */
+		    afs_globalVp = tvp;
+#if defined(HAVE_LINUX_D_MAKE_ROOT)
+		    afsp->s_root = d_make_root(ip);
 #else
-    .get_sb = afs_get_sb,
+		    afsp->s_root = d_alloc_root(ip);
 #endif
-    .kill_sb = kill_anon_super,
-    .fs_flags = FS_BINARY_MOUNTDATA,
-};
+#if !defined(HAVE_LINUX_SET_DEFAULT_D_OP) && !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
+		    afsp->s_root->d_op = &afs_dentry_operations;
+#endif
+		    afs_DestroyAttr(vattr);
+		}
+	    } else
+		code = EIO;
+	}
+	crfree(credp);
+	afs_DestroyReq(treq);
+    }
 
-struct backing_dev_info *afs_backing_dev_info;
+    afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, afs_globalVp,
+	       ICL_TYPE_INT32, code);
+    return code;
+}
 
+#if defined(HAVE_LINUX_GET_TREE_NODEV)
+static int
+afs_fill_super(struct super_block *sb, struct fs_context *fc)
+#else
 static int
 afs_fill_super(struct super_block *sb, void *data, int silent)
+#endif
 {
     int code = 0;
 #if defined(HAVE_LINUX_BDI_INIT)
@@ -117,8 +133,12 @@ afs_fill_super(struct super_block *sb, void *data, int silent)
     sb->s_magic = AFS_VFSMAGIC;
     sb->s_op = &afs_sops;	/* Super block (vfs) ops */
 
-#if defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
+#if defined(HAVE_LINUX_SET_DEFAULT_D_OP)
+    set_default_d_op(sb, &afs_dentry_operations);
+#elif defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
     sb->s_d_op = &afs_dentry_operations;
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38)
+# error "Missing method to set the super_block's dentry operations member"
 #endif
 #if defined(HAVE_LINUX_SUPER_SETUP_BDI)
     code = super_setup_bdi(sb);
@@ -183,60 +203,53 @@ out:
     return code ? -EINVAL : 0;
 }
 
-
-/* afs_root - stat the root of the file system. AFS global held on entry. */
+#if defined(HAVE_LINUX_GET_TREE_NODEV)
 static int
-afs_root(struct super_block *afsp)
+afs_fc_get_tree(struct fs_context *fc)
 {
-    afs_int32 code = 0;
-    struct vcache *tvp = 0;
-
-    AFS_STATCNT(afs_root);
-    if (afs_globalVp && (afs_globalVp->f.states & CStatd)) {
-	tvp = afs_globalVp;
-    } else {
-	struct vrequest *treq = NULL;
-	cred_t *credp = crref();
-
-	if (afs_globalVp) {
-	    afs_PutVCache(afs_globalVp);
-	    afs_globalVp = NULL;
-	}
-
-	if (!(code = afs_CreateReq(&treq, credp)) && !(code = afs_CheckInit())) {
-	    tvp = afs_GetVCache(&afs_rootFid, treq);
-	    if (tvp) {
-		struct inode *ip = AFSTOV(tvp);
-		struct vattr *vattr = NULL;
+    return get_tree_nodev(fc, afs_fill_super);
+}
 
-		code = afs_CreateAttr(&vattr);
-		if (!code) {
-		    afs_getattr(tvp, vattr, credp);
-		    afs_fill_inode(ip, vattr);
+static struct fs_context_operations afs_fs_context_ops = {
+    .get_tree = afs_fc_get_tree,
+};
 
-		    /* setup super_block and mount point inode. */
-		    afs_globalVp = tvp;
-#if defined(HAVE_LINUX_D_MAKE_ROOT)
-		    afsp->s_root = d_make_root(ip);
+static int
+afs_init_fs_context(struct fs_context *fc)
+{
+    fc->ops = &afs_fs_context_ops;
+    return 0;
+}
 #else
-		    afsp->s_root = d_alloc_root(ip);
-#endif
-#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
-		    afsp->s_root->d_op = &afs_dentry_operations;
-#endif
-		    afs_DestroyAttr(vattr);
-		}
-	    } else
-		code = EIO;
-	}
-	crfree(credp);
-	afs_DestroyReq(treq);
-    }
+/*
+ * afs_mount (2.6.37+) and afs_get_sb (2.6.36-) are the entry
+ * points from the vfs when mounting afs.  The super block
+ * structure is setup in the afs_fill_super callback function.
+ */
 
-    afs_Trace2(afs_iclSetp, CM_TRACE_VFSROOT, ICL_TYPE_POINTER, afs_globalVp,
-	       ICL_TYPE_INT32, code);
-    return code;
+# if defined(STRUCT_FILE_SYSTEM_TYPE_HAS_MOUNT)
+static struct dentry *
+afs_mount(struct file_system_type *fs_type, int flags,
+	  const char *dev_name, void *data)
+{
+    return mount_nodev(fs_type, flags, data, afs_fill_super);
+}
+# elif defined(GET_SB_HAS_STRUCT_VFSMOUNT)
+static int
+afs_get_sb(struct file_system_type *fs_type, int flags,
+	   const char *dev_name, void *data, struct vfsmount *mnt)
+{
+    return get_sb_nodev(fs_type, flags, data, afs_fill_super, mnt);
 }
+# else
+static struct super_block *
+afs_get_sb(struct file_system_type *fs_type, int flags,
+	   const char *dev_name, void *data)
+{
+    return get_sb_nodev(fs_type, flags, data, afs_fill_super);
+}
+# endif /* STRUCT_FILE_SYSTEM_TYPE_HAS_MOUNT */
+#endif /* HAVE_LINUX_GET_TREE_NODEV */
 
 /* super_operations */
 
@@ -402,6 +415,20 @@ afs_statfs(struct super_block *sbp, struct kstatfs *statp)
     return 0;
 }
 
+struct file_system_type afs_fs_type = {
+    .owner = THIS_MODULE,
+    .name = "afs",
+#if defined(HAVE_LINUX_GET_TREE_NODEV)
+    .init_fs_context = afs_init_fs_context,
+#elif defined(STRUCT_FILE_SYSTEM_TYPE_HAS_MOUNT)
+    .mount = afs_mount,
+#else
+    .get_sb = afs_get_sb,
+#endif
+    .kill_sb = kill_anon_super,
+    .fs_flags = FS_BINARY_MOUNTDATA,
+};
+
 struct super_operations afs_sops = {
 #if defined(STRUCT_SUPER_OPERATIONS_HAS_ALLOC_INODE)
   .alloc_inode =	afs_alloc_inode,
diff --git a/src/afs/LINUX/osi_vnodeops.c b/src/afs/LINUX/osi_vnodeops.c
index b525126c20..020fbec7cb 100644
--- a/src/afs/LINUX/osi_vnodeops.c
+++ b/src/afs/LINUX/osi_vnodeops.c
@@ -34,9 +34,14 @@
 #include <linux/writeback.h>
 #if defined(HAVE_LINUX_FOLIO_ADD_LRU) || defined(HAVE_LINUX_LRU_CACHE_ADD_FILE)
 # include <linux/swap.h>
-#else
-# include <linux/pagevec.h>
 #endif
+#include <linux/pagevec.h>
+
+#if defined(LINUX_WRITE_CACHE_PAGES_USES_FOLIOS) || defined(LINUX_NEED_CUSTOM_WRITE_CACHE_PAGES)
+# define LINUX_WRITEPAGES_USES_FOLIOS
+# include <linux/migrate.h>
+#endif
+
 #include <linux/aio.h>
 #include "afs/lock.h"
 #include "afs/afs_bypasscache.h"
@@ -1844,7 +1849,7 @@ afs_linux_create(struct inode *dip, struct dentry *dp, int mode)
 	afs_getattr(vcp, vattr, credp);
 	afs_fill_inode(ip, vattr);
 	insert_inode_hash(ip);
-#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
+#if !defined(HAVE_LINUX_SET_DEFAULT_D_OP) && !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
 	dp->d_op = &afs_dentry_operations;
 #endif
 	dp->d_time = parent_vcache_dv(dip, credp);
@@ -1925,7 +1930,7 @@ afs_linux_lookup(struct inode *dip, struct dentry *dp)
 
 	afs_DestroyAttr(vattr);
     }
-#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
+#if !defined(HAVE_LINUX_SET_DEFAULT_D_OP) && !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
     dp->d_op = &afs_dentry_operations;
 #endif
     dp->d_time = parent_vcache_dv(dip, credp);
@@ -2171,7 +2176,7 @@ afs_linux_mkdir(struct inode *dip, struct dentry *dp, int mode)
 	afs_getattr(tvcp, vattr, credp);
 	afs_fill_inode(ip, vattr);
 
-#if !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
+#if !defined(HAVE_LINUX_SET_DEFAULT_D_OP) && !defined(STRUCT_SUPER_BLOCK_HAS_S_D_OP)
 	dp->d_op = &afs_dentry_operations;
 #endif
 	dp->d_time = parent_vcache_dv(dip, credp);
@@ -2402,15 +2407,31 @@ mapping_read_page(struct address_space *mapping, struct page *page)
 #endif
 }
 
+#if defined(HAVE_LINUX_FILEMAP_ALLOC_FOLIO_MEMPOLICY)
+# define HAVE_AFS_FILEMAP_ALLOC_FOLIO
+static struct folio *
+afs_filemap_alloc_folio(gfp_t gfp, unsigned int order)
+{
+    return filemap_alloc_folio(gfp, order, NULL);
+}
+#elif defined(HAVE_LINUX_FILEMAP_ALLOC_FOLIO)
+# define HAVE_AFS_FILEMAP_ALLOC_FOLIO
+static struct folio *
+afs_filemap_alloc_folio(gfp_t gfp, unsigned int order)
+{
+    return filemap_alloc_folio(gfp, order);
+}
+#endif /* HAVE_LINUX_FILEMAP_ALLOC_FOLIO_MEMPOLICY */
+
 /*
  * small compat wrapper for filemap_alloc_folio/page_cache_alloc
  */
 static struct page *
 afs_page_cache_alloc(struct address_space *cachemapping)
 {
-#if defined(HAVE_LINUX_FILEMAP_ALLOC_FOLIO)
+#if defined(HAVE_AFS_FILEMAP_ALLOC_FOLIO)
     struct folio *folio;
-    folio = filemap_alloc_folio(mapping_gfp_mask(cachemapping), 0);
+    folio = afs_filemap_alloc_folio(mapping_gfp_mask(cachemapping), 0);
     if (folio == NULL) {
 	return NULL;
     }
@@ -3336,7 +3357,14 @@ afs_linux_prepare_writeback(struct vcache *avc) {
     spin_lock(&avc->pagewriter_lock);
     list_for_each_entry(pw, &avc->pagewriters, link) {
 	if (pw->writer == pid) {
+	    static int logged;
 	    spin_unlock(&avc->pagewriter_lock);
+	    if (!logged) {
+		logged = 1;
+		afs_warn("afs: Avoiding recursive writeback. This should not cause "
+			 "problems, but is unexpected; please report this as a bug.\n");
+		dump_stack();
+	    }
 	    return AOP_WRITEPAGE_ACTIVATE;
 	}
     }
@@ -3528,7 +3556,11 @@ afs_linux_end_writeback(struct vcache *vcp, cred_t **acredp, int written_size, u
 
 #if defined(LINUX_WRITEPAGES_USES_FOLIOS)
 /*
- * Callback function for write_cache_pages
+ * Write out the given folio (for a ->writepages() request) by calling
+ * afs_linux_begin_writeback(), afs_linux_page_writeback(), and
+ * afs_linux_end_writeback().
+ *
+ * Used as a callback for write_cache_pages().
  */
 static int
 afs_linux_writefolio_cb(struct folio *folio, struct writeback_control *wbc, void *priv)
@@ -3569,9 +3601,12 @@ afs_linux_writefolio_cb(struct folio *folio, struct writeback_control *wbc, void
     }
     code = afs_linux_begin_writeback(vcp, &credp);
     if (code == AOP_WRITEPAGE_ACTIVATE) {
-	/* WRITEPAGE_ACTIVATE is the only return value that permits us
-	 * to return with the folio still locked */
-	return code;
+	/*
+	 * afs_linux_begin_writeback() detected a recursive writeback, so bail
+	 * out. We didn't actually write out the folio, so mark it as dirty
+	 * again.
+	 */
+	goto redirty;
     }
 
     folio_start_writeback(folio);
@@ -3612,13 +3647,247 @@ afs_linux_writefolio_cb(struct folio *folio, struct writeback_control *wbc, void
     }
 
     return code;
+
+ redirty:
+    folio_mark_dirty(folio);
+    folio_unlock(folio);
+    folio_put(folio);
+    return 0;
+}
+
+# if defined(LINUX_NEED_CUSTOM_WRITE_CACHE_PAGES)
+/*
+ * Write out a single folio, as part of a ->writepages() request.
+ */
+static int
+afs_linux_write_pages_folio(struct address_space *mapping,
+			    struct writeback_control *wbc, struct folio *folio)
+{
+    int code = 0;
+
+    folio_lock(folio);
+
+    /*
+     * Save how far we've gotten in writing pages. Save the index for the
+     * _next_ page index, so if we encounter an error writing out the page,
+     * we'll try the next page on the next attempt, and won't keep trying to
+     * write the failed page over and over again.
+     */
+    wbc->index = folio_next_index(folio);
+
+    if (folio->mapping != mapping || !folio_test_dirty(folio)) {
+	/* Not within our mapping, or not dirty. */
+	goto unlock_skip;
+    }
+
+    if (wbc->sync_mode == WB_SYNC_ALL) {
+	/*
+	 * For WB_SYNC_ALL, if the folio is being processed within another
+	 * writeback, wait for it to finish.
+	 */
+	while (folio_test_writeback(folio)) {
+	    folio_wait_bit(folio, PG_writeback);
+	}
+
+    } else if (folio_test_writeback(folio)) {
+	/*
+	 * For WB_SYNC_NONE, if the folio is being processed within another
+	 * writeback, just skip it.
+	 */
+	goto unlock_skip;
+    }
+
+    osi_Assert(!folio_test_writeback(folio));
+
+    if (!folio_clear_dirty_for_io(folio)) {
+	/* Folio isn't dirty. */
+	goto unlock_skip;
+    }
+
+    /* Decrement the number of pages to write within this writeback request */
+    wbc->nr_to_write -= folio_nr_pages(folio);
+
+    /* afs_linux_writefolio_cb() unlocks the folio on success or error. */
+    code = afs_linux_writefolio_cb(folio, wbc, NULL);
+
+    /* Make sure the mapping gets marked with any errors */
+    mapping_set_error(mapping, code);
+
+    return code;
+
+ unlock_skip:
+    folio_unlock(folio);
+    return code;
+}
+
+/*
+ * Writeout all of the pages in the given mapping, from page index 'start' to
+ * 'end', as part of a ->writepages() request.
+ *
+ * On return, *a_eof is set if we processed the entire range, and didn't bail
+ * out early (we hit "eof" of the given range or the mapping).
+ */
+static int
+afs_linux_write_pages_range(struct address_space *mapping,
+			    struct writeback_control *wbc, pgoff_t start,
+			    pgoff_t end, int *a_eof)
+{
+    int code;
+    int saved_err = 0;
+    struct folio *folio;
+    xa_mark_t tag;
+
+    if (wbc->sync_mode == WB_SYNC_ALL || wbc->tagged_writepages) {
+	/*
+	 * Tag any dirty folios with PAGECACHE_TAG_TOWRITE. We do this instead
+	 * of looking for dirty pages directly to avoid some livelock
+	 * situations, where a process is constantly dirtying pages faster than
+	 * we write them out. See the documentation for
+	 * tag_pages_for_writeback() for details.
+	 */
+	tag_pages_for_writeback(mapping, start, end);
+	tag = PAGECACHE_TAG_TOWRITE;
+
+    } else {
+	/*
+	 * For WB_SYNC_NONE by default, we don't care about those livelock
+	 * scenarios, since we are just clearing out pages in the background.
+	 * So just look for dirty pages directly.
+	 */
+	tag = PAGECACHE_TAG_DIRTY;
+    }
+
+    /*
+     * Obtain the next batch of folios tagged with PAGECACHE_TAG_TOWRITE (or
+     * _DIRTY). filemap_get_folios_tag() will adjust 'start' to move forward as
+     * we go, and will return 0 when there are no more pages to write (when
+     * 'start' is past 'end', or we hit EOF).
+     */
+    while (filemap_get_folios_tag(mapping, &start, end, tag,
+				  &wbc->fbatch) != 0) {
+	/* Go through each folio in the fbatch. */
+	while ((folio = folio_batch_next(&wbc->fbatch)) != NULL) {
+	    code = afs_linux_write_pages_folio(mapping, wbc, folio);
+	    if (wbc->sync_mode == WB_SYNC_ALL) {
+		/*
+		 * For WB_SYNC_ALL, don't stop if we saw an error. We must
+		 * process all pages, even if we encounter an error. But save
+		 * the error we got, and return it later.
+		 */
+		if (saved_err == 0) {
+		    saved_err = code;
+		}
+	    } else {
+		/*
+		 * For WB_SYNC_NONE, we can stop if we saw an error, or if
+		 * we've written wbc->nr_to_write pages.
+		 */
+		if (code != 0 || wbc->nr_to_write <= 0) {
+		    goto done;
+		}
+	    }
+	}
+	folio_batch_release(&wbc->fbatch);
+    }
+
+    *a_eof = 1;
+    code = 0;
+
+ done:
+    /*
+     * Make sure we release our fbatch. We may release it twice, which is fine;
+     * folio_batch_release() is idempotent.
+     */
+    folio_batch_release(&wbc->fbatch);
+    if (saved_err != 0) {
+	code = saved_err;
+    }
+    return code;
 }
 
+/*
+ * Write out pages in the given address space and range. This is used both for
+ * synchronous writes and background best-effort writes.
+ *
+ * If wbc->mode == WB_SYNC_ALL:
+ * - This is for an fsync(), umount(), etc.
+ * - We must write out all pages in the given range (wbc->range_start to
+ *   wbc->range_end).
+ * - If a page is busy being written out as part of another ->writepages()
+ *   request, we must wait synchronously for it to finish.
+ * - If we encounter an error, we must still try to writeout all pages in the
+ *   range (but we must return the error).
+ *
+ * If wbc->mode == WB_SYNC_NONE:
+ * - This is a background request to cleanup pages.
+ * - We can stop writing pages after writing out about wbc->nr_to_write pages,
+ *   or if we encounter an error.
+ * - If a page is busy being written out as part of another ->writepages()
+ *   request, just skip it and don't wait.
+ *
+ * If wbc->range_cyclic (only set for WB_SYNC_NONE):
+ * - Ignore wbc->range_start and wbc->range_end, and instead start writing from
+ *   mapping->writeback_index to EOF.
+ * - We may also then writeout the remainder of the mapping starting from 0, but
+ *   this isn't required (and we currently never do this, for simplicity).
+ * - Update mapping->writeback_index before returning to save how far we got,
+ *   so we'll start from there the next time afs_linux_write_pages() is called
+ *   for this mapping.
+ */
+static int
+afs_linux_write_pages(struct address_space *mapping,
+		      struct writeback_control *wbc)
+{
+    pgoff_t start = wbc->range_start >> PAGE_SHIFT;
+    pgoff_t end = wbc->range_end >> PAGE_SHIFT;
+    int code;
+    int eof = 0;
+
+    if (wbc->range_cyclic) {
+	/*
+	 * wbc->range_cyclic is set by Linux's background writeback when
+	 * cleaning dirty folios (WB_SYNC_NONE). The background writeback works
+	 * in chunks with the wbc->nr_to_write indicating the number of pages
+	 * to be handled. In order to ensure that the scan for dirty pages
+	 * isn't always starting at the beginning of the mapping, start where
+	 * the previous scanning left off (mapping->writeback_index).
+	 */
+	start = mapping->writeback_index;
+	end = -1; /* EOF */
+    }
+
+    /*
+     * These wbc fields are private to us; we can do whatever we want with
+     * them.
+     * - wbc->fbatch is used to just store the fbatch we're working on
+     * - wbc->index stores the next page offset to process, which is used to
+     *   save where we were, if we stop before processing all pages.
+     */
+    folio_batch_init(&wbc->fbatch);
+    wbc->index = start;
+
+    code = afs_linux_write_pages_range(mapping, wbc, start, end, &eof);
+
+    if (wbc->range_cyclic) {
+	/*
+	 * If we hit EOF, start from index 0 next time. Otherwise, start from
+	 * where we left off (wbc->index) next time.
+	 */
+	if (eof) {
+	    wbc->index = 0;
+	}
+	mapping->writeback_index = wbc->index;
+    }
+
+    return code;
+}
+# else
 static int
 afs_linux_write_pages(struct address_space *mapping, struct writeback_control *wbc)
 {
     return write_cache_pages(mapping, wbc, afs_linux_writefolio_cb, NULL);
 }
+# endif /* LINUX_NEED_CUSTOM_WRITE_CACHE_PAGES */
 
 #else /* LINUX_WRITEPAGES_USES_FOLIOS */
 static int
@@ -4011,6 +4280,7 @@ static struct address_space_operations afs_file_aops = {
 #endif
 #if defined(LINUX_WRITEPAGES_USES_FOLIOS)
   .writepages =		afs_linux_write_pages,
+  .migrate_folio =      migrate_folio,
 #else
   .writepage =		afs_linux_writepage,
 #endif
diff --git a/src/auth/Makefile.in b/src/auth/Makefile.in
index 21ce8eecb5..e3b8479413 100644
--- a/src/auth/Makefile.in
+++ b/src/auth/Makefile.in
@@ -99,7 +99,7 @@ ktc.krb.lo: ktc.c ${INCLS} ${TOP_INCDIR}/afs/vice.h
 libauth.a: $(LT_objs)
 	$(LT_LDLIB_lwp) $(LT_objs)
 
-libauth.krb.a: $(KRB_objs)
+libauth.krb.a: libauth.a $(KRB_objs)
 	$(LT_LDLIB_lwp) $(KRB_objs)
 
 liboafs_auth.la: liboafs_auth.la.sym $(LT_objs) $(LT_deps)
diff --git a/src/bozo/bos.c b/src/bozo/bos.c
index 62f2a60303..292854d8ad 100644
--- a/src/bozo/bos.c
+++ b/src/bozo/bos.c
@@ -192,7 +192,7 @@ SetAuth(struct cmd_syndesc *as, void *arock)
 static int
 ComputeDestDir(char *aname, char *adir, char *aresult, afs_int32 alen)
 {
-    char *tp;
+    const char *tp;
 
     strcpy(aresult, adir);
     tp = strrchr(aname, '/');
diff --git a/src/cf/linux-kernel-assorted.m4 b/src/cf/linux-kernel-assorted.m4
index 85af7179c3..3655ea1784 100644
--- a/src/cf/linux-kernel-assorted.m4
+++ b/src/cf/linux-kernel-assorted.m4
@@ -61,7 +61,8 @@ LINUX_KERNEL_READ_OFFSET_IS_LAST
 LINUX_KEYRING_SEARCH_TAKES_RECURSE
 LINUX_GENERIC_FILLATTR_TAKES_REQUEST_MASK
 LINUX_FILE_LOCK_CORE
-LINUX_WRITEPAGES_USES_FOLIOS
+LINUX_WRITE_CACHE_PAGES_USES_FOLIOS
+LINUX_NEED_CUSTOM_WRITE_CACHE_PAGES
 
 dnl If take_dentry_name_snapshot isn't present
 dnl don't bother checking if name_snapshot uses qstr
diff --git a/src/cf/linux-kernel-func.m4 b/src/cf/linux-kernel-func.m4
index 0784262d70..b5b6c3c296 100644
--- a/src/cf/linux-kernel-func.m4
+++ b/src/cf/linux-kernel-func.m4
@@ -379,6 +379,45 @@ AC_CHECK_LINUX_FUNC([write_begin_end_kiocb],
 		    [[aops->write_begin(kiocb, mapping, 0, 0, &foliop, fsdata);
 		      aops->write_end(kiocb, mapping, 0, 0, 0, foliop, fsdata);]])
 
+dnl Linux 6.17 added a helper function set_default_d_op() to set the default
+dnl dentry_operations for a super_block.  The super_block member s_d_op is now
+dnl a private member of the super_block.
+AC_CHECK_LINUX_FUNC([set_default_d_op],
+		    [#include <linux/kernel.h>
+		     #include <linux/dcache.h>],
+		    [[static struct super_block sb;
+		      static struct dentry_operations d_op;
+		      set_default_d_op(&sb, &d_op);]])
+
+dnl Linux 6.18 removed mount_nodev().  The replacement is get_tree_nodev()
+dnl (introduced with Linux 5.2), which involves using the
+dnl struct file_system_type.init_fs_context() API.
+AC_CHECK_LINUX_FUNC([get_tree_nodev],
+		    [[#include <linux/fs_context.h>
+		      static int fill_super(struct super_block *sb, struct fs_context *fc) { return 0;}]],
+		    [[static int code;
+		      code = get_tree_nodev(NULL, fill_super);]])
+
+dnl Linux 6.19 introduced struct sockaddr_unsized and updated the
+dnl signature for socket->ops->bind() to use sockaddr_unsized instead of sockaddr.
+dnl When calling, it is sufficient to simply cast a struct sockaddr_in to
+dnl sockaddr_unsized.
+AC_CHECK_LINUX_FUNC([socket_bind_sockaddr_unsized],
+		    [[#include <linux/net.h>
+		      #include <linux/socket.h>]],
+		    [[static struct socket *sockp;
+		      static struct sockaddr_unsized socka;
+		      static int code;
+		      code = sockp->ops->bind(sockp, &socka, sizeof(socka));]])
+
+dnl Linux 6.19 updated filemap_alloc_folio() to require a 3rd parameter that
+dnl for NUMA mempolicy support.  Existing callers can pass a NULL for this parameter.
+AC_CHECK_LINUX_FUNC([filemap_alloc_folio_mempolicy],
+		    [#include <linux/kernel.h>
+		     #include <linux/pagemap.h>],
+		    [[static struct folio *folio;
+		      folio = filemap_alloc_folio(0, 0, NULL);]])
+
 dnl Consequences - things which get set as a result of the
 dnl                above tests
 AS_IF([test "x$ac_cv_linux_func_d_alloc_anon" = "xno"],
diff --git a/src/cf/linux-kernel-struct.m4 b/src/cf/linux-kernel-struct.m4
index 24841e9f32..46c23a25fb 100644
--- a/src/cf/linux-kernel-struct.m4
+++ b/src/cf/linux-kernel-struct.m4
@@ -44,6 +44,10 @@ AC_CHECK_LINUX_STRUCT([nameidata], [path], [namei.h])
 AC_CHECK_LINUX_STRUCT([proc_dir_entry], [owner], [proc_fs.h])
 AC_CHECK_LINUX_STRUCT([proc_ops], [proc_compat_ioctl], [proc_fs.h])
 AC_CHECK_LINUX_STRUCT([super_block], [s_bdi], [fs.h])
+dnl Linux 2.6.38 added the s_d_op field to hold the default dentry ops.
+dnl Linux 6.17 renamed s_d_op to a private member, requiring users to instead
+dnl call set_default_d_op() to initialize the super_block with the default
+dnl dentry ops.
 AC_CHECK_LINUX_STRUCT([super_block], [s_d_op], [fs.h])
 AC_CHECK_LINUX_STRUCT([super_operations], [alloc_inode],
                       [fs.h])
diff --git a/src/cf/linux-test4.m4 b/src/cf/linux-test4.m4
index 9139f24358..c978bb0f2e 100644
--- a/src/cf/linux-test4.m4
+++ b/src/cf/linux-test4.m4
@@ -923,9 +923,9 @@ dnl of Linux's page to folio transistion. Convert from providing aop->writepage
 dnl to providing aop->writepages and use Linux's write_cache_pages with a callback.
 dnl Test to see whether write_cache_pages uses folios to determine if writepages
 dnl should be implemented.
-AC_DEFUN([LINUX_WRITEPAGES_USES_FOLIOS], [
-  AC_CHECK_LINUX_BUILD([whether aop.writepages can use folios],
-                       [ac_cv_linux_writepages_uses_folios],
+AC_DEFUN([LINUX_WRITE_CACHE_PAGES_USES_FOLIOS], [
+  AC_CHECK_LINUX_BUILD([whether write_cache_pages uses folios],
+                       [ac_cv_linux_write_cache_pages_uses_folios],
                        [[#include <linux/writeback.h>
                          #include <linux/mm_types.h>
                          static int writepages_cb(struct folio *folio,
@@ -933,7 +933,33 @@ AC_DEFUN([LINUX_WRITEPAGES_USES_FOLIOS], [
                                                   void *priv) { return 0; }]],
                        [[static int code;
                          code = write_cache_pages(NULL, NULL, writepages_cb, NULL);]],
-                       [[LINUX_WRITEPAGES_USES_FOLIOS]],
+                       [[LINUX_WRITE_CACHE_PAGES_USES_FOLIOS]],
                        [[define if aop.writepages can use folios]],
                        [[-Werror]])
 ])
+
+dnl Linux 6.18 removed write_cache_pages() with no usable replacement, so we
+dnl need to write our own replacement. Check if we need to make our own
+dnl write_cache_pages() by checking:
+dnl - If write_cache_pages() doesn't exist (by defining our own func with that
+dnl   name)
+dnl - If address_space_operations.writepages() uses folios in general (by
+dnl   checking if filemap_get_folios_tag() (Linux 6.2) and folio_batch_next()
+dnl   (Linux 6.8) both exist)
+AC_DEFUN([LINUX_NEED_CUSTOM_WRITE_CACHE_PAGES], [
+  AC_CHECK_LINUX_BUILD([whether we need our own write_cache_pages],
+		       [ac_cv_linux_need_custom_write_cache_pages],
+		       [[#include <linux/pagemap.h>
+			 #include <linux/pagevec.h>
+			 #include <linux/version.h>
+			 static void write_cache_pages(void *x) {return;}]],
+		       [[struct folio_batch fbatch;
+			 struct address_space mapping;
+			 static struct folio *testfolio;
+			 write_cache_pages(testfolio);
+			 filemap_get_folios_tag(&mapping, NULL, 0, 0, &fbatch);
+			 testfolio = folio_batch_next(&fbatch);]],
+		       [[LINUX_NEED_CUSTOM_WRITE_CACHE_PAGES]],
+		       [define if we need to create our own write_cache_pages()],
+		       [-Werror])
+])
diff --git a/src/comerr/compile_et.c b/src/comerr/compile_et.c
index e5b780c9d3..865211dae3 100644
--- a/src/comerr/compile_et.c
+++ b/src/comerr/compile_et.c
@@ -140,6 +140,7 @@ dup_err(char const *type, char const *one, char const *two)
 int
 main(int argc, char **argv)
 {
+    const char *last_path_separator;
     char *p, *ename;
     char *et_file;
     char const *const *cpp;
@@ -167,9 +168,10 @@ main(int argc, char **argv)
     debug = 0;
     filename = 0;
     whoami = argv[0];
-    p = strrchr(whoami, '/');
-    if (p)
-	whoami = p + 1;
+    last_path_separator = strrchr(whoami, '/');
+    if (last_path_separator != NULL) {
+	whoami = last_path_separator + 1;
+    }
     while (--argc) {
 	char *arg = *++argv;
 	if (arg[0] != '-') {
diff --git a/src/config/NTMakefile.amd64_w2k b/src/config/NTMakefile.amd64_w2k
index 6ad1c41c48..a7656c0f49 100644
--- a/src/config/NTMakefile.amd64_w2k
+++ b/src/config/NTMakefile.amd64_w2k
@@ -88,7 +88,7 @@ AFSPRODUCT_VER_MAJOR=1
 AFSPRODUCT_VER_MINOR=8
 !ENDIF
 !IF !DEFINED(AFSPRODUCT_VER_PATCH)
-AFSPRODUCT_VER_PATCH=1501
+AFSPRODUCT_VER_PATCH=1600
 !ENDIF
 !IF !DEFINED(AFSPRODUCT_VER_BUILD)
 AFSPRODUCT_VER_BUILD=0
diff --git a/src/config/NTMakefile.i386_nt40 b/src/config/NTMakefile.i386_nt40
index a1cb6b6db1..f0dbd29b08 100644
--- a/src/config/NTMakefile.i386_nt40
+++ b/src/config/NTMakefile.i386_nt40
@@ -88,7 +88,7 @@ AFSPRODUCT_VER_MAJOR=1
 AFSPRODUCT_VER_MINOR=8
 !ENDIF
 !IF !DEFINED(AFSPRODUCT_VER_PATCH)
-AFSPRODUCT_VER_PATCH=1501
+AFSPRODUCT_VER_PATCH=1600
 !ENDIF
 !IF !DEFINED(AFSPRODUCT_VER_BUILD)
 AFSPRODUCT_VER_BUILD=0
diff --git a/src/config/NTMakefile.i386_w2k b/src/config/NTMakefile.i386_w2k
index c82a10b792..8de8d986f6 100644
--- a/src/config/NTMakefile.i386_w2k
+++ b/src/config/NTMakefile.i386_w2k
@@ -92,7 +92,7 @@ AFSPRODUCT_VER_MAJOR=1
 AFSPRODUCT_VER_MINOR=8
 !ENDIF
 !IF !DEFINED(AFSPRODUCT_VER_PATCH)
-AFSPRODUCT_VER_PATCH=1501
+AFSPRODUCT_VER_PATCH=1600
 !ENDIF
 !IF !DEFINED(AFSPRODUCT_VER_BUILD)
 AFSPRODUCT_VER_BUILD=0
diff --git a/src/kauth/Makefile.in b/src/kauth/Makefile.in
index 7181f99f2e..20b83eaa7e 100644
--- a/src/kauth/Makefile.in
+++ b/src/kauth/Makefile.in
@@ -180,7 +180,7 @@ Kkauth.h: kauth.rg
 libkauth.a: $(LWP_objs)
 	$(LT_LDLIB_lwp) $(LWP_objs)
 
-libkauth.krb.a: $(KRB_objs)
+libkauth.krb.a: libkauth.a $(KRB_objs)
 	$(LT_LDLIB_lwp) $(KRB_objs)
 
 kas.o: kas.c ${INCLS} AFS_component_version_number.o
diff --git a/src/packaging/MacOS/pkgbuild.sh.in b/src/packaging/MacOS/pkgbuild.sh.in
index 84b74cc3b0..95f66794ef 100644
--- a/src/packaging/MacOS/pkgbuild.sh.in
+++ b/src/packaging/MacOS/pkgbuild.sh.in
@@ -355,6 +355,36 @@ if [ x"$PASS1" = x1 ]; then
     chmod -R og-w "$PKGROOT"/private
     chmod  og-rx "$PKGROOT"/private/var/db/openafs/cache
 
+    # Workaround to avoid LWP segfault/buserr on Apple Silicon:
+    # If we have built universal binaries (both arm64 and x86_64) for selected
+    # LWP programs, use 'lipo' to convert these to x86_64-only.  These will
+    # then run under Rosetta2 on Apple Silicon.
+    for lwp_bin in "$PKGROOT/Library/OpenAFS/Tools/bin/bos" \
+		   "$PKGROOT/Library/OpenAFS/Tools/bin/cmdebug" \
+		   "$PKGROOT/Library/OpenAFS/Tools/bin/fs" \
+		   "$PKGROOT/Library/OpenAFS/Tools/bin/xstat_cm_test" \
+		   "$PKGROOT/Library/OpenAFS/Tools/bin/xstat_fs_test" \
+		   "$PKGROOT/Library/OpenAFS/Tools/etc/backup" \
+		   "$PKGROOT/Library/OpenAFS/Tools/root.server/usr/afs/bin/bos" \
+		   "$PKGROOT/Library/OpenAFS/Tools/root.server/usr/afs/bin/fs"
+    do
+	# Example 'lipo $bin -archs' output:
+	# $ lipo fs -archs
+	# x86_64 arm64
+	if lipo "$lwp_bin" -archs | grep -w x86_64 | grep -w -q arm64 ; then
+	    save_dir="$PKGROOT/Library/OpenAFS/Tools/bin.orig"
+	    mkdir -p "$save_dir"
+
+	    # Remove the arm64 arch in, e.g., 'fs'. Also preserve the original
+	    # universal binary in $save_dir. Some binaries are found at more
+	    # than one path; for those, we'll just keep one original copy
+	    # around (the others will get clobbered when we copy over them).
+	    cp -f "$lwp_bin" "$save_dir"/
+	    lipo "$lwp_bin" -thin x86_64 -output "$lwp_bin".tmp
+	    mv -f "$lwp_bin".tmp "$lwp_bin"
+	fi
+    done
+
     if [ x"$APP_KEY" != x ] ; then
 	# To be notarized by Apple, all files must be signed.
 	find "$PKGROOT" -type f -exec codesign --verbose --force \
diff --git a/src/pam/Makefile.in b/src/pam/Makefile.in
index f4f066618f..0cfd401cec 100644
--- a/src/pam/Makefile.in
+++ b/src/pam/Makefile.in
@@ -70,7 +70,7 @@ pam_afs.la: $(LT_objs) $(LT_deps) \
 		afs_setcred.lo afs_auth.lo afs_util.lo ktc.lo \
 		$(LT_objs) $(LT_deps) $(LT_libs)
 
-pam_afs.krb.la: $(LT_objs) $(LT_deps) \
+pam_afs.krb.la: pam_afs.la $(LT_objs) $(LT_deps) \
 		afs_setcred_krb.lo afs_auth_krb.lo afs_util_krb.lo \
 		ktc_krb.lo pam_afs.map
 	$(LIBTOOL) --quiet --mode=link --tag=CC $(MT_CC) $(XLDFLAGS) -rpath $(libdir) \
diff --git a/src/rx/LINUX/rx_knet.c b/src/rx/LINUX/rx_knet.c
index 0ca67a56de..32cba6c6ff 100644
--- a/src/rx/LINUX/rx_knet.c
+++ b/src/rx/LINUX/rx_knet.c
@@ -36,6 +36,12 @@
 #endif
 #include "osi_compat.h"
 
+#if defined(HAVE_LINUX_SOCKET_BIND_SOCKADDR_UNSIZED)
+# define BIND_SOCKADDR(sa) ((struct sockaddr_unsized *)(sa))
+#else
+# define BIND_SOCKADDR(sa) ((struct sockaddr *)(sa))
+#endif
+
 /* rxk_NewSocket
  * open and bind RX socket
  */
@@ -67,8 +73,7 @@ rxk_NewSocketHost(afs_uint32 ahost, short aport)
     myaddr.sin_family = AF_INET;
     myaddr.sin_addr.s_addr = ahost;
     myaddr.sin_port = aport;
-    code =
-	sockp->ops->bind(sockp, (struct sockaddr *)&myaddr, sizeof(myaddr));
+    code = sockp->ops->bind(sockp, BIND_SOCKADDR(&myaddr), sizeof(myaddr));
 
     if (code < 0) {
 	printk("sock_release(rx_socket) FIXME\n");
diff --git a/src/tests/warnerr.c b/src/tests/warnerr.c
index 84385fdcf0..5667fb1b22 100644
--- a/src/tests/warnerr.c
+++ b/src/tests/warnerr.c
@@ -54,12 +54,12 @@ void
 setprogname(const char *argv0)
 {
 #ifndef HAVE___PROGNAME
-    char *p;
+    const char *p;
     if (argv0 == NULL)
 	return;
     p = strrchr(argv0, '/');
     if (p == NULL)
-      p = (char *)argv0;
+      p = argv0;
     else
 	p++;
     __progname = p;
