diff --git a/NEWS b/NEWS
index 3742408228..67f8a6a358 100644
--- a/NEWS
+++ b/NEWS
@@ -1,5 +1,49 @@
                        User-Visible OpenAFS Changes
 
+OpenAFS 1.8.17pre1
+
+  All platforms
+
+    * The Rx RPC call statistics code now checks that a call's function
+      number fits within the statistics storage allocated for its RPC
+      package before recording it. Previously, if two RPC packages were
+      tracked under the same statistics slot but declared different
+      numbers of functions, a call could write past the end of that
+      storage and corrupt unrelated memory. The RPC packages shipped in
+      OpenAFS do not currently collide this way; this is a safety check
+      for adding new Rx services in the future (16229).
+
+    * A change from OpenAFS 1.8.10 has been reverted, which previously made
+      "vos convertROtoRW" take the RO volume offline from the client side. The
+      reverted change is obviated by a server side implementation (in change
+      14433) previously introduced in OpenAFS 1.8.8 (16895).
+
+  All UNIX clients
+
+    * The "unlog -cell" command no longer discards tokens for cells other than
+      the one given on the command line. Previously, on UNIX clients, it
+      removed all tokens and then re-registered tokens for the other cells
+      (16896, 16897, 16898, 16910, 16988, 17006).
+
+    * A potential bug (with no known path to trigger it) has been fixed where
+      various code paths did not properly obtain references to unixuser structs
+      when traversing over all unixusers in the system, potentially leading to
+      memory corruption (17007, 17008).
+
+  Linux client
+
+    * Support for Linux 7.2 has been added (16928).
+
+    * The OpenAFS directory name lookup cache (DNLC) is now disabled by default
+      on Linux, since the Linux kernel's own dentry cache already serves this
+      purpose.  This avoids the overhead and possible issues of maintaining a
+      redundant cache.  The DNLC can be re-enabled with the `afs.afs_usednlc`
+      sysctl (16927).
+
+  MacOS client
+
+    * Support for macOS 27 has been added (Golden Gate) (16953, 16954, 16955).
+
 OpenAFS 1.8.16.1
 
   All UNIX clients
diff --git a/configure-libafs.ac b/configure-libafs.ac
index 0581eeed77..5ca0656ffb 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.17d1
+MACOS_VERSION=1.8.17fc1
 
 AC_SUBST([MACOS_VERSION])
 
diff --git a/configure.ac b/configure.ac
index 9c2b9a9393..76b3e35b23 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.17d1
+MACOS_VERSION=1.8.17fc1
 
 AC_SUBST([MACOS_VERSION])
 
diff --git a/src/afs/DARWIN/osi_file.c b/src/afs/DARWIN/osi_file.c
index 4d525fe41f..02df77f048 100644
--- a/src/afs/DARWIN/osi_file.c
+++ b/src/afs/DARWIN/osi_file.c
@@ -187,7 +187,7 @@ osi_UFSOpen(afs_dcache_id_t *ainode)
     }
     afile->vnode = vp;
     afile->offset = 0;
-    afile->proc = (int (*)())0;
+    afile->proc = NULL;
 #ifndef AFS_CACHE_VNODE_PATH
     afile->size = va.va_size;
 #else
diff --git a/src/afs/DARWIN/osi_groups.c b/src/afs/DARWIN/osi_groups.c
index ecb49d6df6..01eb79826a 100644
--- a/src/afs/DARWIN/osi_groups.c
+++ b/src/afs/DARWIN/osi_groups.c
@@ -24,13 +24,9 @@
 /* We should be doing something better anyway */
 #ifdef AFS_DARWIN80_ENV
 int
-setpag(proc, cred, pagvalue, newpag, change_parent)
-     struct proc *proc;
-     struct ucred **cred;
-     afs_uint32 pagvalue;
-     afs_uint32 *newpag;
-     afs_uint32 change_parent;
-{ 
+setpag(struct proc *proc, struct ucred **cred, afs_uint32 pagvalue,
+       afs_uint32 *newpag, afs_uint32 change_parent)
+{
     return EINVAL;
 }
 #else
@@ -43,10 +39,7 @@ static int
 		gid_t * gidset, int change_parent);
 
 int
-Afs_xsetgroups(p, args, retval)
-     struct proc *p;
-     void *args;
-     int *retval;
+Afs_xsetgroups(struct proc *p, void *args, int *retval)
 {
     int code = 0;
     struct vrequest treq;
@@ -86,12 +79,8 @@ Afs_xsetgroups(p, args, retval)
 }
 
 int
-setpag(proc, cred, pagvalue, newpag, change_parent)
-     struct proc *proc;
-     struct ucred **cred;
-     afs_uint32 pagvalue;
-     afs_uint32 *newpag;
-     afs_uint32 change_parent;
+setpag(struct proc *proc, struct ucred **cred, afs_uint32 pagvalue,
+       afs_uint32 *newpag, afs_uint32 change_parent)
 {
     gid_t gidset[NGROUPS];
     int ngroups, code;
@@ -123,7 +112,6 @@ setpag(proc, cred, pagvalue, newpag, change_parent)
     return code;
 }
 
-
 static int
 afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset)
 {
@@ -138,8 +126,6 @@ afs_getgroups(struct ucred *cred, int ngroups, gid_t * gidset)
     return savengrps;
 }
 
-
-
 static int
 afs_setgroups(struct proc *proc, struct ucred **cred, int ngroups,
 	      gid_t * gidset, int change_parent)
diff --git a/src/afs/DARWIN/osi_inode.c b/src/afs/DARWIN/osi_inode.c
index 6ed3763f4b..7d04b7d337 100644
--- a/src/afs/DARWIN/osi_inode.c
+++ b/src/afs/DARWIN/osi_inode.c
@@ -1,7 +1,7 @@
 /*
  * Copyright 2000, International Business Machines Corporation and others.
  * All Rights Reserved.
- * 
+ *
  * This software has been released under the terms of the IBM Public
  * License.  For details, see the LICENSE file in the top-level source
  * directory or online at http://www.openafs.org/dl/license10.html
@@ -28,19 +28,14 @@ extern int afs_CacheFSType;
 
 #ifdef AFS_DARWIN80_ENV
 int
-getinode(fs, dev, inode, vpp, perror)
-    mount_t fs;
-    vnode_t *vpp;
-    dev_t dev;
-    ino_t inode;
-    int *perror;
+getinode(mount_t fs, dev_t dev, ino_t inode, vnode_t *vpp, int *perror)
 {
     struct vnode *vp;
     int code;
     vfs_context_t ctx;
     char volfspath[64];
     size_t len = sizeof(volfspath);
-    
+
     *vpp = 0;
     *perror = 0;
     if (snprintf(volfspath, len, "/.vol/%d/%d", dev, inode) >= len) {
@@ -55,23 +50,18 @@ getinode(fs, dev, inode, vpp, perror)
 	*vpp = vp;
 	return (0);
     }
-}    
-    
+}
+
 int
-igetinode(vfsp, dev, inode, vpp, va, perror)
-    vnode_t *vpp;
-    mount_t vfsp;
-    dev_t dev;
-    ino_t inode;
-    struct vattr *va;
-    int *perror;
+igetinode(mount_t vfsp, dev_t dev, ino_t inode, vnode_t *vpp, struct vattr *va,
+	  int *perror)
 {
     vnode_t pvp, vp;
     extern struct osi_dev cacheDev;
     int code = 0;
-    
+
     *perror = 0;
-    
+
     AFS_STATCNT(igetinode);
     if ((code = getinode(vfsp, dev, inode, &vp, perror)) != 0) {
 	return (code);
@@ -88,11 +78,11 @@ igetinode(vfsp, dev, inode, vpp, va, perror)
     code = vnode_getattr(vp, va, afs_osi_ctxtp);
     if (code) {
 	vnode_close(vp, O_RDWR, afs_osi_ctxtp);
-        return code;
+	return code;
     }
     if (!VATTR_ALL_SUPPORTED(va)) {
 	vnode_close(vp, O_RDWR, afs_osi_ctxtp);
-        return ENOENT;
+	return ENOENT;
     }
     if (va->va_mode == 0) {
 	vnode_close(vp, O_RDWR, afs_osi_ctxtp);
@@ -103,17 +93,13 @@ igetinode(vfsp, dev, inode, vpp, va, perror)
 	vnode_close(vp, O_RDWR, afs_osi_ctxtp);
 	return (ENOENT);
     }
-    
+
     *vpp = vp;
     return (0);
 }
 #else
-getinode(fs, dev, inode, vpp, perror)
-     struct mount *fs;
-     struct vnode **vpp;
-     dev_t dev;
-     ino_t inode;
-     int *perror;
+int
+getinode(struct mount *fs, dev_t dev, ino_t inode, struct vnode **vpp, int *perror)
 {
     struct vnode *vp;
     int code;
@@ -131,7 +117,7 @@ getinode(fs, dev, inode, vpp, perror)
 	if (mp = rootfs)
 	    do {
 		/*
-		 * XXX Also do the test for MFS 
+		 * XXX Also do the test for MFS
 		 */
 		if (!strcmp(mp->mnt_vfc->vfc_name, "ufs")) {
 		    ump = VFSTOUFS(mp);
@@ -165,13 +151,9 @@ getinode(fs, dev, inode, vpp, perror)
     }
 }
 
-igetinode(vfsp, dev, inode, vpp, va, perror)
-     struct vnode **vpp;
-     struct mount *vfsp;
-     dev_t dev;
-     ino_t inode;
-     struct vattr *va;
-     int *perror;
+int
+igetinode(struct mount *vfsp, dev_t dev, ino_t inode, struct vnode **vpp,
+	  struct vattr *va, int *perror)
 {
     struct vnode *pvp, *vp;
     extern struct osi_dev cacheDev;
@@ -207,10 +189,9 @@ igetinode(vfsp, dev, inode, vpp, va, perror)
     return (0);
 }
 
-iforget(vp)
-     struct vnode *vp;
+int
+iforget(struct vnode *vp)
 {
-
     AFS_STATCNT(iforget);
     /* XXX could sleep */
     vn_lock(vp, LK_EXCLUSIVE | LK_RETRY, current_proc());
@@ -226,7 +207,7 @@ iforget(vp)
 #endif
 
 int
-afs_syscall_icreate(long dev, long near_inode, long param1, long param2, 
+afs_syscall_icreate(long dev, long near_inode, long param1, long param2,
 		    long param3, long param4, long *retval)
 {
     return ENOTSUP;
diff --git a/src/afs/DARWIN/osi_vnodeops.c b/src/afs/DARWIN/osi_vnodeops.c
index aef06bcbcb..f7745a3625 100644
--- a/src/afs/DARWIN/osi_vnodeops.c
+++ b/src/afs/DARWIN/osi_vnodeops.c
@@ -218,10 +218,10 @@ struct vnodeopv_desc afs_dead_vnodeop_opv_desc =
 
 #define DROPNAME() FREE(name, M_TEMP)
 
-void 
+void
 darwin_vn_hold(struct vnode *vp)
 {
-    int haveGlock=ISAFS_GLOCK(); 
+    int haveGlock=ISAFS_GLOCK();
     struct vcache *tvc = VTOAFS(vp);
 
 #ifndef AFS_DARWIN80_ENV
@@ -232,18 +232,18 @@ darwin_vn_hold(struct vnode *vp)
     if (tvc->f.states & CDeadVnode)
        osi_Assert(!vnode_isinuse(vp, 1));
 #endif
-    if (haveGlock) AFS_GUNLOCK(); 
+    if (haveGlock) AFS_GUNLOCK();
 
 #ifdef AFS_DARWIN80_ENV
 	if (vnode_get(vp)) {
            /* being terminated. kernel won't give us a ref. Now what? our
               callers don't expect us to fail */
-           if (haveGlock) AFS_GLOCK(); 
+           if (haveGlock) AFS_GLOCK();
            return;
         }
 	if (vnode_ref(vp)) {
 	    vnode_put(vp);
-	    if (haveGlock) AFS_GLOCK(); 
+	    if (haveGlock) AFS_GLOCK();
 	    return;
 	}
 	vnode_put(vp);
@@ -254,30 +254,31 @@ darwin_vn_hold(struct vnode *vp)
        the ubc... so we just call VREF there now anyway. */
 
     if (VREFCOUNT_GT(tvc, 0))
-	VREF(((struct vnode *)(vp))); 
-     else 
+	VREF(((struct vnode *)(vp)));
+     else
 	afs_vget(afs_globalVFS, 0, (vp));
 #endif
 
 #ifndef AFS_DARWIN80_ENV
     if (UBCINFOMISSING(vp) || UBCINFORECLAIMED(vp)) {
-	ubc_info_init(vp); 
+	ubc_info_init(vp);
     }
 #endif
 
-    if (haveGlock) AFS_GLOCK(); 
+    if (haveGlock) AFS_GLOCK();
 #ifndef AFS_DARWIN80_ENV
     tvc->f.states &= ~CUBCinit;
 #endif
 }
+
 int
-afs_vop_lookup(ap)
-     struct VOPPROT(lookup_args)/* {
+afs_vop_lookup(struct VOPPROT(lookup_args) *ap)
+				/* {
 				 * struct vnodeop_desc * a_desc;
 				 * struct vnode *a_dvp;
 				 * struct vnode **a_vpp;
 				 * struct componentname *a_cnp;
-				 * } */ *ap;
+				 * } */
 {
     int error;
     struct vcache *vcp;
@@ -297,9 +298,9 @@ afs_vop_lookup(ap)
 	return ENOENT;
     if (vcp->mvstat != AFS_MVSTAT_MTPT) {
 	error = cache_lookup(ap->a_dvp, ap->a_vpp, ap->a_cnp);
-	if (error == -1) 
+	if (error == -1)
 	    return 0;
-	if (error == ENOENT) 
+	if (error == ENOENT)
 	    return error;
     }
 #endif
@@ -393,13 +394,13 @@ afs_vop_lookup(ap)
 }
 
 int
-afs_vop_create(ap)
-     struct VOPPROT(create_args)	/* {
+afs_vop_create(struct VOPPROT(create_args) *ap)
+				/* {
 				 * struct vnode *a_dvp;
 				 * struct vnode **a_vpp;
 				 * struct componentname *a_cnp;
 				 * struct vattr *a_vap;
-				 * } */ *ap;
+				 * } */
 {
     int error = 0;
     struct vcache *vcp;
@@ -454,13 +455,13 @@ afs_vop_create(ap)
 }
 
 int
-afs_vop_mknod(ap)
-     struct VOPPROT(mknod_args)	/* {
+afs_vop_mknod(struct VOPPROT(mknod_args) *ap)
+				/* {
 				 * struct vnode *a_dvp;
 				 * struct vnode **a_vpp;
 				 * struct componentname *a_cnp;
 				 * struct vattr *a_vap;
-				 * } */ *ap;
+				 * } */
 {
 #ifndef AFS_DARWIN80_ENV
     FREE_ZONE(ap->a_cnp->cn_pnbuf, ap->a_cnp->cn_pnlen, M_NAMEI);
@@ -470,13 +471,13 @@ afs_vop_mknod(ap)
 }
 
 int
-afs_vop_open(ap)
-     struct VOPPROT(open_args)	/* {
+afs_vop_open(struct VOPPROT(open_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * int  a_mode;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     int error;
     struct vnode *vp = ap->a_vp;
@@ -512,13 +513,13 @@ afs_vop_open(ap)
 }
 
 int
-afs_vop_close(ap)
-     struct VOPPROT(close_args)	/* {
+afs_vop_close(struct VOPPROT(close_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * int  a_fflag;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     int code;
     struct vnode *vp = ap->a_vp;
@@ -543,12 +544,12 @@ afs_vop_close(ap)
 extern int afs_fakestat_enable;
 
 int
-afs_vop_access(ap)
-     struct VOPPROT(access_args)        /* {
-                                 * struct vnode *a_vp;
-                                 * int  a_action;
-                                 * vfs_context_t a_context;
-                                 * } */ *ap;
+afs_vop_access(struct VOPPROT(access_args) *ap)
+				/* {
+				 * struct vnode *a_vp;
+				 * int  a_action;
+				 * vfs_context_t a_context;
+				 * } */
 {
     int code;
     struct vrequest treq;
@@ -620,7 +621,7 @@ afs_vop_access(ap)
     if (ap->a_action & KAUTH_VNODE_WRITE_SECURITY)
        bits |= PRSFS_WRITE;
     /* we can't check for KAUTH_VNODE_TAKE_OWNERSHIP, so we always permit it */
-    
+
     code = afs_AccessOK(tvc, bits, &treq, cmb);
     /*
      * Special cased dropbox handling:
@@ -651,13 +652,13 @@ out2:
 }
 #else
 int
-afs_vop_access(ap)
-     struct VOPPROT(access_args)	/* {
+afs_vop_access(struct VOPPROT(access_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * int  a_mode;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     int code;
     AFS_GLOCK();
@@ -668,13 +669,13 @@ afs_vop_access(ap)
 #endif
 
 int
-afs_vop_getattr(ap)
-     struct VOPPROT(getattr_args)	/* {
+afs_vop_getattr(struct VOPPROT(getattr_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct vattr *a_vap;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     int code;
 
@@ -747,13 +748,13 @@ afs_vop_getattr(ap)
 }
 
 int
-afs_vop_setattr(ap)
-     struct VOPPROT(setattr_args)	/* {
+afs_vop_setattr(struct VOPPROT(setattr_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct vattr *a_vap;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     int code, pass = 0;
     struct vcache *avc = VTOAFS(ap->a_vp);
@@ -778,19 +779,19 @@ retry:
 }
 
 int
-afs_vop_read(ap)
-     struct VOPPROT(read_args)	/* {
+afs_vop_read(struct VOPPROT(read_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct uio *a_uio;
 				 * int a_ioflag;
 				 * struct ucred *a_cred;
-				 * } */ *ap;
+				 * } */
 {
     int code;
     struct vnode *vp = ap->a_vp;
     struct vcache *avc = VTOAFS(vp);
 
-    if (vnode_isdir(ap->a_vp)) 
+    if (vnode_isdir(ap->a_vp))
 	return EISDIR;
 #ifdef AFS_DARWIN80_ENV
     ubc_msync_range(ap->a_vp, AFS_UIO_OFFSET(ap->a_uio), AFS_UIO_OFFSET(ap->a_uio) + AFS_UIO_RESID(ap->a_uio), UBC_PUSHDIRTY);
@@ -807,8 +808,8 @@ afs_vop_read(ap)
 }
 
 int
-afs_vop_pagein(ap)
-     struct VOPPROT(pagein_args)	/* {
+afs_vop_pagein(struct VOPPROT(pagein_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * upl_t a_pl;
 				 * vm_offset_t a_pl_offset;
@@ -816,7 +817,7 @@ afs_vop_pagein(ap)
 				 * size_t a_size;
 				 * struct ucred *a_cred;
 				 * int a_flags;
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *vp = ap->a_vp;
     upl_t pl = ap->a_pl;
@@ -925,13 +926,13 @@ afs_vop_pagein(ap)
 }
 
 int
-afs_vop_write(ap)
-     struct VOPPROT(write_args)	/* {
+afs_vop_write(struct VOPPROT(write_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct uio *a_uio;
 				 * int a_ioflag;
 				 * struct ucred *a_cred;
-				 * } */ *ap;
+				 * } */
 {
     int code;
     struct vcache *avc = VTOAFS(ap->a_vp);
@@ -955,8 +956,8 @@ afs_vop_write(ap)
 }
 
 int
-afs_vop_pageout(ap)
-     struct VOPPROT(pageout_args)	/* {
+afs_vop_pageout(struct VOPPROT(pageout_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * upl_t   a_pl,
 				 * vm_offset_t   a_pl_offset,
@@ -964,7 +965,7 @@ afs_vop_pageout(ap)
 				 * size_t        a_size,
 				 * struct ucred *a_cred,
 				 * int           a_flags
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *vp = ap->a_vp;
     upl_t pl = ap->a_pl;
@@ -1058,7 +1059,7 @@ afs_vop_pageout(ap)
 
     /* size will always be a multiple of PAGE_SIZE */
     /* pageout isn't supposed to extend files */
-    if (f_offset + size > tvc->f.m.Length) 
+    if (f_offset + size > tvc->f.m.Length)
         iosize = tvc->f.m.Length - f_offset;
     else
         iosize = size;
@@ -1127,15 +1128,15 @@ afs_vop_pageout(ap)
 }
 
 int
-afs_vop_ioctl(ap)
-     struct VOPPROT(ioctl_args)	/* {
+afs_vop_ioctl(struct VOPPROT(ioctl_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * int  a_command;
 				 * caddr_t  a_data;
 				 * int  a_fflag;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     struct vcache *tvc = VTOAFS(ap->a_vp);
     struct afs_ioctl data;
@@ -1158,14 +1159,14 @@ afs_vop_ioctl(ap)
 
 /* ARGSUSED */
 int
-afs_vop_select(ap)
-     struct VOPPROT(select_args)	/* {
+afs_vop_select(struct VOPPROT(select_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * int  a_which;
 				 * int  a_fflags;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     /*
      * We should really check to see if I/O is possible.
@@ -1180,25 +1181,25 @@ afs_vop_select(ap)
  */
 /* ARGSUSED */
 int
-afs_vop_mmap(ap)
-     struct VOPPROT(mmap_args)	/* {
+afs_vop_mmap(struct VOPPROT(mmap_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * int  a_fflags;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     return (EINVAL);
 }
 
 int
-afs_vop_fsync(ap)
-     struct VOPPROT(fsync_args)	/* {
+afs_vop_fsync(struct VOPPROT(fsync_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct ucred *a_cred;
 				 * int a_waitfor;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     int wait = ap->a_waitfor == MNT_WAIT;
     int error;
@@ -1221,13 +1222,13 @@ afs_vop_fsync(ap)
 
 #ifndef AFS_DARWIN80_ENV
 int
-afs_vop_seek(ap)
-     struct VOPPROT(seek_args)	/* {
+afs_vop_seek(struct VOPPROT(seek_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * off_t  a_oldoff;
 				 * off_t  a_newoff;
 				 * struct ucred *a_cred;
-				 * } */ *ap;
+				 * } */
 {
     if (ap->a_newoff > ULONG_MAX)	/* AFS doesn't support 64-bit offsets */
 	return EINVAL;
@@ -1236,12 +1237,12 @@ afs_vop_seek(ap)
 #endif
 
 int
-afs_vop_remove(ap)
-     struct VOPPROT(remove_args)	/* {
+afs_vop_remove(struct VOPPROT(remove_args) *ap)
+				/* {
 				 * struct vnode *a_dvp;
 				 * struct vnode *a_vp;
 				 * struct componentname *a_cnp;
-				 * } */ *ap;
+				 * } */
 {
     int error = 0;
     struct vnode *vp = ap->a_vp;
@@ -1265,7 +1266,7 @@ afs_vop_remove(ap)
     if (!error) {
 #ifdef AFS_DARWIN80_ENV
 	struct vcache *tvc = VTOAFS(vp);
-	
+
 	if (!(tvc->f.states & CUnlinked)) {
             ubc_setsize(vp, (off_t)0);
             vnode_recycle(vp);
@@ -1281,7 +1282,7 @@ afs_vop_remove(ap)
 	/* should check for PRSFS_INSERT and not PRSFS_DELETE, but the
 	   goal here is to deal with Finder's unhappiness with resource
 	   forks that have no resources in a dropbox setting */
-	if (name[0] == '.' && name[1] == '_' && error == EACCES) 
+	if (name[0] == '.' && name[1] == '_' && error == EACCES)
 	    error = 0;
     }
 
@@ -1301,12 +1302,12 @@ afs_vop_remove(ap)
 }
 
 int
-afs_vop_link(ap)
-     struct VOPPROT(link_args)	/* {
+afs_vop_link(struct VOPPROT(link_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct vnode *a_tdvp;
 				 * struct componentname *a_cnp;
-				 * } */ *ap;
+				 * } */
 {
     int error = 0;
     struct vnode *dvp = ap->a_tdvp;
@@ -1345,15 +1346,15 @@ afs_vop_link(ap)
 }
 
 int
-afs_vop_rename(ap)
-     struct VOPPROT(rename_args)	/* {
+afs_vop_rename(struct VOPPROT(rename_args) *ap)
+				/* {
 				 * struct vnode *a_fdvp;
 				 * struct vnode *a_fvp;
 				 * struct componentname *a_fcnp;
 				 * struct vnode *a_tdvp;
 				 * struct vnode *a_tvp;
 				 * struct componentname *a_tcnp;
-				 * } */ *ap;
+				 * } */
 {
     int error = 0;
     struct componentname *fcnp = ap->a_fcnp;
@@ -1364,7 +1365,7 @@ afs_vop_rename(ap)
     struct vnode *tdvp = ap->a_tdvp;
     struct vnode *fvp = ap->a_fvp;
     struct vnode *fdvp = ap->a_fdvp;
-    struct proc *p; 
+    struct proc *p;
 
     p = cn_proc(fcnp);
 
@@ -1420,7 +1421,7 @@ afs_vop_rename(ap)
 	    panic("afs_rename: lost from startdir");
 	fcnp->cn_nameiop = DELETE;
 
-        VREF(fdvp); 
+        VREF(fdvp);
         error=relookup(fdvp, &fvp, fcnp);
         if (error == 0)
 	    vrele(fdvp);
@@ -1428,7 +1429,7 @@ afs_vop_rename(ap)
 	    return (ENOENT);
         }
         error=VOP_REMOVE(fdvp, fvp, fcnp);
-        
+
         if (fdvp == fvp)
             vrele(fdvp);
         else
@@ -1453,7 +1454,7 @@ afs_vop_rename(ap)
     error =
 	afs_rename(VTOAFS(fdvp), fname, VTOAFS(tdvp), tname, cn_cred(tcnp));
 
-#if !defined(AFS_DARWIN80_ENV) 
+#if !defined(AFS_DARWIN80_ENV)
     AFS_GUNLOCK();
     VOP_UNLOCK(fvp, 0, p);
     if (error)
@@ -1553,13 +1554,13 @@ afs_vop_rename(ap)
 }
 
 int
-afs_vop_mkdir(ap)
-     struct VOPPROT(mkdir_args)	/* {
+afs_vop_mkdir(struct VOPPROT(mkdir_args) *ap)
+				/* {
 				 * struct vnode *a_dvp;
 				 * struct vnode **a_vpp;
 				 * struct componentname *a_cnp;
 				 * struct vattr *a_vap;
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *dvp = ap->a_dvp;
     struct vattr *vap = ap->a_vap;
@@ -1604,12 +1605,12 @@ afs_vop_mkdir(ap)
 }
 
 int
-afs_vop_rmdir(ap)
-     struct VOPPROT(rmdir_args)	/* {
+afs_vop_rmdir(struct VOPPROT(rmdir_args) *ap)
+				/* {
 				 * struct vnode *a_dvp;
 				 * struct vnode *a_vp;
 				 * struct componentname *a_cnp;
-				 * } */ *ap;
+				 * } */
 {
     int error = 0;
     struct vnode *vp = ap->a_vp;
@@ -1640,14 +1641,14 @@ afs_vop_rmdir(ap)
 }
 
 int
-afs_vop_symlink(ap)
-     struct VOPPROT(symlink_args)	/* {
+afs_vop_symlink(struct VOPPROT(symlink_args) *ap)
+				/* {
 				 * struct vnode *a_dvp;
 				 * struct vnode **a_vpp;
 				 * struct componentname *a_cnp;
 				 * struct vattr *a_vap;
 				 * char *a_target;
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *dvp = ap->a_dvp;
     struct vcache *pvc = NULL;
@@ -1673,15 +1674,15 @@ afs_vop_symlink(ap)
 }
 
 int
-afs_vop_readdir(ap)
-     struct VOPPROT(readdir_args)	/* {
+afs_vop_readdir(struct VOPPROT(readdir_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct uio *a_uio;
 				 * struct ucred *a_cred;
 				 * int *a_eofflag;
 				 * u_long *a_cookies;
 				 * int ncookies;
-				 * } */ *ap;
+				 * } */
 {
     int error;
     off_t off;
@@ -1731,12 +1732,12 @@ afs_vop_readdir(ap)
 }
 
 int
-afs_vop_readlink(ap)
-     struct VOPPROT(readlink_args)	/* {
+afs_vop_readlink(struct VOPPROT(readlink_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct uio *a_uio;
 				 * struct ucred *a_cred;
-				 * } */ *ap;
+				 * } */
 {
     int error;
 /*    printf("readlink %x\n", ap->a_vp);*/
@@ -1749,11 +1750,11 @@ afs_vop_readlink(ap)
 extern int prtactive;
 
 int
-afs_vop_inactive(ap)
-     struct VOPPROT(inactive_args)	/* {
+afs_vop_inactive(struct VOPPROT(inactive_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *vp = ap->a_vp;
     struct vcache *tvc = VTOAFS(vp);
@@ -1782,10 +1783,10 @@ afs_vop_inactive(ap)
 }
 
 int
-afs_vop_reclaim(ap)
-     struct VOPPROT(reclaim_args)	/* {
+afs_vop_reclaim(struct VOPPROT(reclaim_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
-				 * } */ *ap;
+				 * } */
 {
     int error = 0;
     int sl, writelocked;
@@ -1826,7 +1827,7 @@ afs_vop_reclaim(ap)
 	   }
 	   if (!error && vnode_fsnode(vp))
 	       panic("afs_reclaim: vnode not cleaned");
-	   if (!error && (tvc->v != NULL)) 
+	   if (!error && (tvc->v != NULL))
 	       panic("afs_reclaim: vcache not cleaned");
 	   ReleaseWriteLock(&afs_xvcache);
        }
@@ -1839,12 +1840,12 @@ afs_vop_reclaim(ap)
  * Return POSIX pathconf information applicable to ufs filesystems.
  */
 int
-afs_vop_pathconf(ap)
-     struct VOPPROT(pathconf_args)	/* {
+afs_vop_pathconf(struct VOPPROT(pathconf_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * int a_name;
 				 * int *a_retval;
-				 * } */ *ap;
+				 * } */
 {
     AFS_STATCNT(afs_cntl);
     switch (ap->a_name) {
@@ -1885,14 +1886,14 @@ afs_vop_pathconf(ap)
  * Advisory record locking support (fcntl() POSIX style)
  */
 int
-afs_vop_advlock(ap)
-     struct VOPPROT(advlock_args)	/* {
+afs_vop_advlock(struct VOPPROT(advlock_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * caddr_t  a_id;
 				 * int  a_op;
 				 * struct flock *a_fl;
 				 * int  a_flags;
-				 * } */ *ap;
+				 * } */
 {
     int error;
     struct ucred *tcr;
@@ -1934,24 +1935,24 @@ afs_vop_advlock(ap)
 }
 
 int
-afs_vop_blktooff(ap)
-     struct VOPPROT(blktooff_args)	/* {
+afs_vop_blktooff(struct VOPPROT(blktooff_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * daddr_t a_lblkno;
-				 * off_t *a_offset;    
-				 * } */ *ap;
+				 * off_t *a_offset;
+				 * } */
 {
     *ap->a_offset = (off_t) (ap->a_lblkno * DEV_BSIZE);
     return 0;
 }
 
 int
-afs_vop_offtoblk(ap)
-     struct VOPPROT(offtoblk_args)	/* {
+afs_vop_offtoblk(struct VOPPROT(offtoblk_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
-				 * off_t a_offset;    
+				 * off_t a_offset;
 				 * daddr_t *a_lblkno;
-				 * } */ *ap;
+				 * } */
 {
     *ap->a_lblkno = (daddr_t) (ap->a_offset / DEV_BSIZE);
 
@@ -1960,10 +1961,10 @@ afs_vop_offtoblk(ap)
 
 #ifndef AFS_DARWIN80_ENV
 int
-afs_vop_lock(ap)
-     struct VOPPROT(lock_args)	/* {
+afs_vop_lock(struct VOPPROT(lock_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *vp = ap->a_vp;
     struct vcache *avc = VTOAFS(vp);
@@ -1975,10 +1976,10 @@ afs_vop_lock(ap)
 }
 
 int
-afs_vop_unlock(ap)
-     struct VOPPROT(unlock_args)	/* {
+afs_vop_unlock(struct VOPPROT(unlock_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *vp = ap->a_vp;
     struct vcache *avc = VTOAFS(vp);
@@ -1990,42 +1991,42 @@ afs_vop_unlock(ap)
 }
 
 int
-afs_vop_truncate(ap)
-     struct VOPPROT(truncate_args)	/* {
+afs_vop_truncate(struct VOPPROT(truncate_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * off_t a_length;
 				 * int a_flags;
 				 * struct ucred *a_cred;
 				 * struct proc *a_p;
-				 * } */ *ap;
+				 * } */
 {
     /* printf("stray afs_vop_truncate\n"); */
     return ENOTSUP;
 }
 
 int
-afs_vop_update(ap)
-     struct VOPPROT(update_args)	/* {
+afs_vop_update(struct VOPPROT(update_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * struct timeval *a_access;
 				 * struct timeval *a_modify;
 				 * int a_waitfor;
-				 * } */ *ap;
+				 * } */
 {
     /* printf("stray afs_vop_update\n"); */
     return ENOTSUP;
 }
 
 int
-afs_vop_bmap(ap)
-     struct VOPPROT(bmap_args)	/* {
+afs_vop_bmap(struct VOPPROT(bmap_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
 				 * daddr_t  a_bn;
 				 * struct vnode **a_vpp;
 				 * daddr_t *a_bnp;
 				 * int *a_runp;
 				 * int *a_runb;
-				 * } */ *ap;
+				 * } */
 {
     int error;
     if (ap->a_bnp) {
@@ -2041,10 +2042,10 @@ afs_vop_bmap(ap)
 }
 
 int
-afs_vop_strategy(ap)
-     struct VOPPROT(strategy_args)	/* {
+afs_vop_strategy(struct VOPPROT(strategy_args) *ap)
+				/* {
 				 * struct buf *a_bp;
-				 * } */ *ap;
+				 * } */
 {
     int error;
     AFS_GLOCK();
@@ -2054,10 +2055,10 @@ afs_vop_strategy(ap)
 }
 
 int
-afs_vop_print(ap)
-     struct VOPPROT(print_args)	/* {
+afs_vop_print(struct VOPPROT(print_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
-				 * } */ *ap;
+				 * } */
 {
     struct vnode *vp = ap->a_vp;
     struct vcache *vc = VTOAFS(ap->a_vp);
@@ -2084,25 +2085,25 @@ afs_vop_print(ap)
 }
 
 int
-afs_vop_islocked(ap)
-     struct VOPPROT(islocked_args)	/* {
+afs_vop_islocked(struct VOPPROT(islocked_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
-				 * } */ *ap;
+				 * } */
 {
     struct vcache *vc = VTOAFS(ap->a_vp);
     return lockstatus(&vc->rwlock);
 }
 
 int
-afs_vop_cmap(ap)
-     struct VOPPROT(cmap_args)	/* {
+afs_vop_cmap(struct VOPPROT(cmap_args) *ap)
+				/* {
 				 * struct vnode *a_vp;
-				 * off_t a_foffset;    
+				 * off_t a_foffset;
 				 * size_t a_size;
 				 * daddr_t *a_bpn;
 				 * size_t *a_run;
 				 * void *a_poff;
-				 * } */ *ap;
+				 * } */
 {
     *ap->a_bpn = (daddr_t) (ap->a_foffset / DEV_BSIZE);
     *ap->a_run = opr_max(ap->a_size, AFS_CHUNKSIZE(ap->a_foffset));
@@ -2144,7 +2145,7 @@ afs_darwin_getnewvnode(struct vcache *avc)
 #endif
 }
 #ifdef AFS_DARWIN80_ENV
-/* if this fails, then tvc has been unrefed and may have been freed. 
+/* if this fails, then tvc has been unrefed and may have been freed.
    Don't touch! */
 int
 afs_darwin_finalizevnode(struct vcache *avc, struct vnode *dvp,
diff --git a/src/afs/IRIX/osi_idbg.c b/src/afs/IRIX/osi_idbg.c
index 760c6ca604..4530edb062 100644
--- a/src/afs/IRIX/osi_idbg.c
+++ b/src/afs/IRIX/osi_idbg.c
@@ -154,9 +154,13 @@ idbg_afsuser(void *x)
     AFS_GLOCK();
 
     if (x == (void *)-1L) {
+	ObtainReadLock(&afs_xuser);
 	for (i = 0; i < NUSERS; i++)
-	    for (tu = afs_users[i]; tu; tu = tu->next)
+	    for (afs_UserScan(afs_users[i], tu)) {
 		idbg_pruser(tu);
+	    }
+	ReleaseReadLock(&afs_xuser);
+
     } else
 	idbg_pruser((struct unixuser *)x);
 
diff --git a/src/afs/LINUX/osi_machdep.h b/src/afs/LINUX/osi_machdep.h
index 263c8633c1..435852859a 100644
--- a/src/afs/LINUX/osi_machdep.h
+++ b/src/afs/LINUX/osi_machdep.h
@@ -498,7 +498,9 @@ do { \
 #  define __NR_ia32_unlink 10
 # endif
 #endif
-
-#define osi_procname(procname, size) strncpy(procname, current->comm, size)
-
+#if defined(HAVE_LINUX_STRSCPY)
+# define osi_procname(procname, size) strscpy(procname, current->comm, size)
+#else
+# define osi_procname(procname, size) strncpy(procname, current->comm, size)
+#endif
 #endif /* OSI_MACHDEP_H_ */
diff --git a/src/afs/LINUX/osi_proc.c b/src/afs/LINUX/osi_proc.c
index ad7c695b91..40f566c708 100644
--- a/src/afs/LINUX/osi_proc.c
+++ b/src/afs/LINUX/osi_proc.c
@@ -155,7 +155,7 @@ uu_start(struct seq_file *m, loff_t *pos)
     ret = NULL;
 
     for (i = 0; i < NUSERS; i++) {
-	for (tu = afs_users[i]; tu; tu = tu->next) {
+	for (afs_UserScan(afs_users[i], tu)) {
 	    if (++n == *pos) {
 		ret = tu;
 		goto done;
@@ -174,23 +174,49 @@ uu_next(struct seq_file *m, void *p, loff_t *pos)
     struct unixuser *tu = p;
     afs_int32 i = 0;
 
+    AFS_GLOCK();
+
     (*pos)++;
-    if (!p) return NULL;
+    if (tu == NULL) {
+	goto done;
+    }
 
     if (p != (void *)1) {
-	if (tu->next) return tu->next;
+	tu->refCount--;
+	if (tu->next != NULL) {
+	    tu = tu->next;
+	    goto done;
+	}
 	i = UHash(tu->uid) + 1;
     }
 
-    for (; i < NUSERS; i++)
-	if (afs_users[i]) return afs_users[i];
-    return NULL;
+    for (; i < NUSERS; i++) {
+	tu = afs_users[i];
+	if (tu != NULL) {
+	    goto done;
+	}
+    }
+
+    tu = NULL;
+
+ done:
+    if (tu != NULL) {
+	tu->refCount++;
+    }
+
+    AFS_GUNLOCK();
+
+    return tu;
 }
 
 static void
 uu_stop(struct seq_file *m, void *p)
 {
     AFS_GLOCK();
+    if (p != NULL && p != (void *)1) {
+	struct unixuser *tu = p;
+	tu->refCount--;
+    }
     ReleaseReadLock(&afs_xuser);
     AFS_GUNLOCK();
 }
diff --git a/src/afs/LINUX/osi_sysctl.c b/src/afs/LINUX/osi_sysctl.c
index c1116fc79e..35d8ea142c 100644
--- a/src/afs/LINUX/osi_sysctl.c
+++ b/src/afs/LINUX/osi_sysctl.c
@@ -83,6 +83,8 @@ static struct ctl_table afs_sysctl_table[] = {
     AFS_SYSCTL_INT( 13, 0644, afs_cacheBlocks),
     AFS_SYSCTL_INT2(14, 0644, "md5inum", afs_md5inum),
 
+    AFS_SYSCTL_INT( 15, 0644, afs_usednlc),
+
     AFS_SYSCTL_SENTINEL
 };
 # if !defined(HAVE_LINUX_REGISTER_SYSCTL)
diff --git a/src/afs/afs.h b/src/afs/afs.h
index 569dde1773..51511cba82 100644
--- a/src/afs/afs.h
+++ b/src/afs/afs.h
@@ -414,6 +414,41 @@ struct unixuser {
     afs_rwlock_t lock;
 };
 
+/*
+ * A macro to iterate over unixusers.
+ *
+ * Use like this:
+ *
+ * {
+ *     struct unixuser *tu;
+ *
+ *     ObtainReadLock(&afs_xuser);
+ *     for (afs_UserScan(afs_users[i], tu)) {
+ *         if (tu->uid == xyz) {
+ *             break;
+ *         }
+ *     }
+ *     ReleaseReadLock(&afs_xuser);
+ *
+ *     if (tu != NULL) {
+ *         do_something_with(tu);
+ *         afs_PutUser(tu, 0);
+ *     }
+ * }
+ *
+ * On each iteration of the loop body, 'tu' will have a ref held (which is
+ * released on the next iteration). So if you 'break' or 'return' early, make
+ * sure to release the ref on 'tu', such as with afs_PutUser().
+ *
+ * @pre AFS_GLOCK must be held
+ * @pre afs_xuser must be at least read-locked
+ */
+#define afs_UserScan(start, cur) \
+    (cur) = (start); \
+    ((cur) != NULL) ? ((cur)->refCount++, 1) : 0; \
+    (cur)->refCount--, (cur) = (cur)->next
+
+
 #define CVEC_LEN 3 /* per-user connection pool */
 
 struct sa_conn_vector;
diff --git a/src/afs/afs_osidnlc.c b/src/afs/afs_osidnlc.c
index 7c6a65c090..eedad35891 100644
--- a/src/afs/afs_osidnlc.c
+++ b/src/afs/afs_osidnlc.c
@@ -54,7 +54,11 @@ typedef enum { osi_dnlc_enterT, InsertEntryT, osi_dnlc_lookupT,
     osi_dnlc_purgevpT, osi_dnlc_purgeT
 } traceevt;
 
+#ifdef AFS_LINUX_ENV
+int afs_usednlc = 0;
+#else
 int afs_usednlc = 1;
+#endif
 
 struct dt {
     traceevt event;
diff --git a/src/afs/afs_pag_cred.c b/src/afs/afs_pag_cred.c
index e77cf8ae36..04fab18d09 100644
--- a/src/afs/afs_pag_cred.c
+++ b/src/afs/afs_pag_cred.c
@@ -105,7 +105,7 @@ afspag_PUnlog(char *ain, afs_int32 ainSize, afs_ucred_t **acred)
     uid = (pag == NOPAG) ? afs_cr_uid(*acred) : pag;
     i = UHash(uid);
     ObtainWriteLock(&afs_xuser, 823);
-    for (tu = afs_users[i]; tu; tu = tu->next) {
+    for (afs_UserScan(afs_users[i], tu)) {
 	if (tu->uid == uid) {
 	    tu->refCount++;
 	    ReleaseWriteLock(&afs_xuser);
@@ -239,7 +239,8 @@ SPAGCB_GetCreds(struct rx_call *a_call, afs_int32 a_uid,
 
     /* count them first */
     bucket = UHash(a_uid);
-    for (count = 0, tu = afs_users[bucket]; tu; tu = tu->next) {
+    count = 0;
+    for (afs_UserScan(afs_users[bucket], tu)) {
 	if (tu->uid == a_uid) count++;
     }
 
@@ -255,11 +256,11 @@ SPAGCB_GetCreds(struct rx_call *a_call, afs_int32 a_uid,
     a_creds->CredInfos_len = count;
     memset(a_creds->CredInfos_val, 0, count * sizeof(CredInfo));
 
-    for (i = 0, tu = afs_users[bucket]; tu; tu = tu->next, i++) {
+    i = 0;
+    for (afs_UserScan(afs_users[bucket], tu), i++) {
 	if (tu->uid == a_uid && tu->cellinfo &&
 	    (tu->states & UHasTokens) && !(tu->states & UTokensBad)) {
 
-	    tu->refCount++;
 	    ReleaseWriteLock(&afs_xuser);
 
 	    afs_LockUser(tu, READ_LOCK, 0);
@@ -298,6 +299,13 @@ SPAGCB_GetCreds(struct rx_call *a_call, afs_int32 a_uid,
 	    if (tu->states & UPrimary)
 		tci->states |= UPrimary;
 
+	    /*
+	     * Obtain an extra ref for afs_PutUser() to release. Note that
+	     * afs_UserScan() obtains its own ref that it releases for the next
+	     * loop iteration; this is in addition to afs_UserScan()'s ref.
+	     */
+	    tu->refCount++;
+
 	    afs_PutUser(tu, READ_LOCK);
 	    ObtainWriteLock(&afs_xuser, 372);
 	}
diff --git a/src/afs/afs_pioctl.c b/src/afs/afs_pioctl.c
index 857da8aae1..c88783cf27 100644
--- a/src/afs/afs_pioctl.c
+++ b/src/afs/afs_pioctl.c
@@ -316,6 +316,7 @@ DECL_PIOCTL(PGetPAG);
 DECL_PIOCTL(PSetCachingThreshold);
 #endif
 DECL_PIOCTL(PGetLiteralFID);
+DECL_PIOCTL(PUnlogCell);
 
 /*
  * A macro that says whether we're going to need HandleClientContext().
@@ -439,10 +440,11 @@ static pioctlFunction OpioctlSw[]  = {
     PBogus,			/* 0 */
     PNFSNukeCreds,		/* 1 -- nuke all creds for NFS client */
 #if defined(AFS_CACHE_BYPASS) && defined(AFS_LINUX_ENV)
-    PSetCachingThreshold        /* 2 -- get/set cache-bypass size threshold */
+    PSetCachingThreshold,	/* 2 -- get/set cache-bypass size threshold */
 #else
-    PNoop                       /* 2 -- get/set cache-bypass size threshold */
+    PNoop,			/* 2 -- get/set cache-bypass size threshold */
 #endif
+    PUnlogCell,			/* 3 -- Unlog using cell name */
 };
 
 #define PSetClientContext 99	/*  Special pioctl to setup caller's creds  */
@@ -1776,9 +1778,8 @@ DECL_PIOCTL(PGetUserCell)
     /* return the cell name of the primary cell for this user */
     i = UHash(areq->uid);
     ObtainWriteLock(&afs_xuser, 224);
-    for (tu = afs_users[i]; tu; tu = tu->next) {
+    for (afs_UserScan(afs_users[i], tu)) {
 	if (tu->uid == areq->uid && (tu->states & UPrimary)) {
-	    tu->refCount++;
 	    ReleaseWriteLock(&afs_xuser);
 	    afs_LockUser(tu, READ_LOCK, 0);
 	    break;
@@ -2367,15 +2368,12 @@ getNthCell(afs_int32 uid, afs_int32 iterator) {
 
     i = UHash(uid);
     ObtainReadLock(&afs_xuser);
-    for (tu = afs_users[i]; tu; tu = tu->next) {
+    for (afs_UserScan(afs_users[i], tu)) {
 	if (tu->uid == uid && (tu->states & UHasTokens)) {
 	    if (iterator-- == 0)
 	    break;	/* are we done yet? */
 	}
     }
-    if (tu) {
-	tu->refCount++;
-    }
     ReleaseReadLock(&afs_xuser);
     if (tu) {
 	afs_LockUser(tu, READ_LOCK, 0);
@@ -2529,7 +2527,7 @@ DECL_PIOCTL(PUnlog)
 
     i = UHash(areq->uid);
     ObtainWriteLock(&afs_xuser, 227);
-    for (tu = afs_users[i]; tu; tu = tu->next) {
+    for (afs_UserScan(afs_users[i], tu)) {
 	if (tu->uid == areq->uid) {
 	    tu->refCount++;
 	    ReleaseWriteLock(&afs_xuser);
@@ -2562,6 +2560,62 @@ DECL_PIOCTL(PUnlog)
     return 0;
 }
 
+/*!
+ * VIOC_UNLOG_CELL (3) - Invalidate tokens by cellname
+ *
+ * \ingroup pioctl
+ *
+ * \param[in] ain	name of the cell
+ * \param[out] aout	not in use
+ *
+ * \retval EIO
+ *	Error if the afs daemon hasn't been started yet
+ * \retval EDOM
+ *	The user has no tokens for the specified cell
+ * \retval EINVAL
+ *	Error if some of the standard args aren't set
+ * \retval ESRCH
+ *	Error if the cell for which the Token is being unset can't be found
+ */
+DECL_PIOCTL(PUnlogCell)
+{
+    afs_int32 cellNum;
+    struct unixuser *tu;
+    char *cellName = NULL;
+    int code;
+
+    AFS_STATCNT(PUnlogCell);
+    if (!afs_resourceinit_flag) {	/* afs daemons haven't started yet */
+	return EIO;		/* Inappropriate ioctl for device */
+    }
+
+    if (afs_pd_getStringPtr(ain, &cellName) != 0) {
+	return EINVAL;
+    }
+
+    code = _settok_tokenCell(cellName, &cellNum, NULL);
+    if (code != 0) {
+	return code;
+    }
+
+    tu = afs_FindUser(areq->uid, cellNum, WRITE_LOCK);
+    if (tu == NULL) {
+	return EDOM;
+    }
+
+    tu->states &= ~UHasTokens;
+    afs_FreeTokens(&tu->tokens);
+    afs_NotifyUser(tu, UTokensDropped);
+    afs_ResetUserConns(tu);
+#ifdef UKERNEL
+    /* set expire time to 0, causes afs_GCUserData to remove this entry */
+    tu->tokenTime = 0;
+#endif /* UKERNEL */
+    afs_PutUser(tu, WRITE_LOCK);
+
+    return 0;
+}
+
 /*!
  * VIOC_AFS_MARINER_HOST (32) - Get/set mariner (cache manager monitor) host
  *
@@ -5652,7 +5706,7 @@ DECL_PIOCTL(PNFSNukeCreds)
 
     ObtainWriteLock(&afs_xuser, 227);
     for (i = 0; i < NUSERS; i++) {
-	for (tu = afs_users[i]; tu; tu = tu->next) {
+	for (afs_UserScan(afs_users[i], tu)) {
 	    if (tu->exporter && EXP_CHECKHOST(tu->exporter, addr)) {
 		tu->refCount++;
 		ReleaseWriteLock(&afs_xuser);
diff --git a/src/afs/afs_prototypes.h b/src/afs/afs_prototypes.h
index fb236e92a5..cce5cc1170 100644
--- a/src/afs/afs_prototypes.h
+++ b/src/afs/afs_prototypes.h
@@ -863,6 +863,8 @@ extern int osi_dnlc_purgevol(struct VenusFid *fidp);
 extern int osi_dnlc_init(void);
 extern int osi_dnlc_shutdown(void);
 
+extern int afs_usednlc;
+
 /* afs_pag_cred.c */
 extern void afspag_SetPrimaryCell(char *acell);
 
diff --git a/src/afs/afs_stats.h b/src/afs/afs_stats.h
index 1a05a78d89..d6de896d55 100644
--- a/src/afs/afs_stats.h
+++ b/src/afs/afs_stats.h
@@ -660,7 +660,8 @@ struct afs_MeanStats {
     AFS_CS(PFlushAllVolumeData)	/* afs_pioctl.c */ \
     AFS_CS(afs_InitVolSlot)     /* afs_volume.c */ \
     AFS_CS(afs_SetupVolSlot)    /* afs_volume.c */ \
-    AFS_CS(PGetLiteralFID)	/* afs_pioctl.c */
+    AFS_CS(PGetLiteralFID)	/* afs_pioctl.c */ \
+    AFS_CS(PUnlogCell)		/* afs_pioctl.c */
 
 struct afs_CMCallStats {
 #define AFS_CS(call) afs_int32 C_ ## call;
diff --git a/src/auth/auth.p.h b/src/auth/auth.p.h
index 9423dd0984..b35bd00717 100644
--- a/src/auth/auth.p.h
+++ b/src/auth/auth.p.h
@@ -37,6 +37,7 @@ int ktc_ListTokensEx(int, int *, char **cellName);
 int ktc_ListTokens(int, int *, struct ktc_principal *);
 int ktc_ForgetToken(struct ktc_principal *);
 int ktc_ForgetAllTokens(void);
+int ktc_ForgetTokensByCell(const char *cell);
 afs_uint32 ktc_curpag(void);
 
 #ifdef AFS_KERBEROS_ENV
diff --git a/src/auth/ktc.c b/src/auth/ktc.c
index 004ac1f7e9..914e205277 100644
--- a/src/auth/ktc.c
+++ b/src/auth/ktc.c
@@ -989,6 +989,88 @@ ktc_ListTokens(int aprevIndex,
     return 0;
 }
 
+/*!
+ * Invalidates tokens for a specified cell name.
+ *
+ * This function first invalidates any matching entries in the local token
+ * cache, then issues the VIOC_UNLOG_CELL pioctl with cell name to unlog tokens
+ * for that cell.
+ *
+ * \param[in] cell
+ *	Name of the cell for which tokens will be invalidated.
+ *
+ * \retval 0
+ *	Successful removal of tokens.
+ * \retval KTC_PIOCTLFAIL
+ *	The pioctl call failed (specific error in 'errno').
+ */
+static int
+ForgetTokensByCell(const char *cell)
+{
+    struct ViceIoctl iob;
+    afs_int32 code;
+    int i;
+
+    for (i = 0; i < MAXLOCALTOKENS; i++) {
+	if (local_tokens[i].valid &&
+	    strcmp(local_tokens[i].server.cell, cell) == 0) {
+	    local_tokens[i].valid = 0;
+	}
+    }
+
+    iob.in = (caddr_t)cell;
+    iob.in_size = strlen(cell) + 1;
+    iob.out = 0;
+    iob.out_size = 0;
+#ifndef NO_AFS_CLIENT
+    code = PIOCTL(0, VIOC_UNLOG_CELL, &iob, 0);
+    if (code != 0) {
+	return KTC_PIOCTLFAIL;
+    }
+#endif /* NO_AFS_CLIENT */
+    return 0;
+}
+
+/*!
+ * Invalidate tokens associated with a specified cell name
+ *
+ * This function removes the tokens associated with the provided cell
+ * name. Unlike ktc_ForgetAllTokens(), it does not destroy the corresponding
+ * tickets in the Kerberos ticket file (KRBTKFILE) for the AFS_KERBEROS_ENV
+ * environment.
+ *
+ * \param[in] cell
+ *	Name of the cell for which tokens will be invalidated.
+ *
+ * \retval 0
+ *	Successful removal of tokens.
+ * \retval KTC_NOPIOCTL
+ *	The pioctl request is not understood by the kernel.
+ * \retval KTC_PIOCTLFAIL
+ *	Operation failed.
+ */
+int
+ktc_ForgetTokensByCell(const char *cell)
+{
+    int ocode;
+
+    LOCK_GLOBAL_MUTEX;
+
+    ocode = ForgetTokensByCell(cell);
+    if (ocode != 0) {
+	if (ocode == KTC_PIOCTLFAIL) {
+	    ocode = errno;
+	}
+	UNLOCK_GLOBAL_MUTEX;
+	if (ocode == EINVAL) {
+	    return KTC_NOPIOCTL;
+	}
+	return KTC_PIOCTLFAIL;
+    }
+    UNLOCK_GLOBAL_MUTEX;
+    return 0;
+}
+
 static int
 ForgetAll(void)
 {
diff --git a/src/auth/ktc_nt.c b/src/auth/ktc_nt.c
index 36575be80c..00a3b4c283 100644
--- a/src/auth/ktc_nt.c
+++ b/src/auth/ktc_nt.c
@@ -824,17 +824,13 @@ ktc_ListTokens(int cellNum, int *cellNumP, struct ktc_principal *server)
 }
 
 int
-ktc_ForgetToken(struct ktc_principal *server)
+ktc_ForgetTokensByCell(const char *cell)
 {
     struct ViceIoctl iob;
     char tbuffer[TBUFFERSIZE];
-    char *tp;
     int code;
     HANDLE ktcMutex = NULL;
 
-    if (strcmp(server->name, "afs")) {
-	return ForgetOneLocalToken(server);
-    }
     ktcMutex = CreateMutex(NULL, TRUE, AFSGlobalKTCMutexName);
     if (ktcMutex == NULL)
 	return KTC_TOKEN_MUTEX_FAIL;
@@ -845,15 +841,9 @@ ktc_ForgetToken(struct ktc_principal *server)
 	}
     }
 
-    tp = tbuffer;
-
-    /* cell name */
-    strcpy(tp, server->cell);
-    tp += strlen(tp) + 1;
-
     /* do pioctl */
-    iob.in = tbuffer;
-    iob.in_size = (long)(tp - tbuffer);
+    iob.in = cell;
+    iob.in_size = strlen(cell) + 1;
     iob.out = tbuffer;
     iob.out_size = sizeof(tbuffer);
 
@@ -877,6 +867,16 @@ ktc_ForgetToken(struct ktc_principal *server)
     return 0;
 }
 
+int
+ktc_ForgetToken(struct ktc_principal *server)
+{
+    if (strcmp(server->name, "afs")) {
+	return ForgetOneLocalToken(server);
+    }
+
+    return ktc_ForgetTokensByCell(server->cell);
+}
+
 int
 ktc_ForgetAllTokens()
 {
diff --git a/src/auth/liboafs_auth.la.sym b/src/auth/liboafs_auth.la.sym
index 44b6b67738..5d04ad0783 100644
--- a/src/auth/liboafs_auth.la.sym
+++ b/src/auth/liboafs_auth.la.sym
@@ -54,6 +54,7 @@ afsconf_typedKey_values
 initialize_ACFG_error_table
 initialize_KTC_error_table
 ktc_ForgetAllTokens
+ktc_ForgetTokensByCell
 ktc_GetToken
 ktc_GetTokenEx
 ktc_OldPioctl
diff --git a/src/cf/linux-kernel-func.m4 b/src/cf/linux-kernel-func.m4
index 20f659da4e..4000e5ce0f 100644
--- a/src/cf/linux-kernel-func.m4
+++ b/src/cf/linux-kernel-func.m4
@@ -173,6 +173,14 @@ AC_CHECK_LINUX_FUNC([lru_cache_add_file],
                     [#include <linux/swap.h>],
                     [lru_cache_add_file(NULL);])
 
+dnl Linux 4.3 introduced strscpy()
+AC_CHECK_LINUX_FUNC([strscpy],
+		    [[#include <linux/string.h>
+		      char buff1[32];
+		      char buff2[32];]],
+		    [[static size_t code;
+		      code = strscpy(buff1, buff2, sizeof(buff1));]])
+
 dnl Linux 4.6 introduced in_compat_syscall as replacement for is_compat_task
 dnl for certain platforms.
 AC_CHECK_LINUX_FUNC([in_compat_syscall],
diff --git a/src/cf/osconf.m4 b/src/cf/osconf.m4
index ff6028adde..f43fcbdceb 100644
--- a/src/cf/osconf.m4
+++ b/src/cf/osconf.m4
@@ -309,7 +309,7 @@ case $AFS_SYSNAME in
 		XLIBS="${LIB_AFSDB} -framework CoreFoundation"
 		;;
 
-	*_darwin_190 | *_darwin_200 | *_darwin_210 | *_darwin_220 | *_darwin_230 | *_darwin_240 | *_darwin_250)
+	*_darwin_190 | *_darwin_200 | *_darwin_210 | *_darwin_220 | *_darwin_230 | *_darwin_240 | *_darwin_250 | *_darwin_270)
 		AFSD_LDFLAGS="-F/System/Library/PrivateFrameworks -framework DiskArbitration -framework SystemConfiguration -framework IOKit -framework CoreFoundation"
 		MT_CFLAGS="-D_REENTRANT"
 		MT_LIBS='${XLIBS}'
diff --git a/src/cf/sysname.m4 b/src/cf/sysname.m4
index d852801047..181766e80c 100644
--- a/src/cf/sysname.m4
+++ b/src/cf/sysname.m4
@@ -240,6 +240,12 @@ else
 		aarch64-apple-darwin25.*)
 			AFS_SYSNAME="arm_darwin_250"
 			;;
+		arm-apple-darwin27.*)
+			AFS_SYSNAME="arm_darwin_270"
+			;;
+		aarch64-apple-darwin27.*)
+			AFS_SYSNAME="arm_darwin_270"
+			;;
                 sparc-sun-solaris2.8)
                         AFS_SYSNAME="sun4x_58"
                         ;;
@@ -449,6 +455,9 @@ case $AFS_SYSNAME in
         *_darwin_250)
                 AFS_PARAM=param.darwin_250.h
                 ;;
+        *_darwin_270)
+                AFS_PARAM=param.darwin_270.h
+                ;;
         *)
                 AFS_PARAM=param.${AFS_SYSNAME}.h
                 ;;
diff --git a/src/config/NTMakefile.amd64_w2k b/src/config/NTMakefile.amd64_w2k
index 2d57cf86e6..b42863c2f7 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=1602
+AFSPRODUCT_VER_PATCH=1700
 !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 5102e9121c..ed70c40e45 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=1602
+AFSPRODUCT_VER_PATCH=1700
 !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 1bfad54cc2..57507374ec 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=1602
+AFSPRODUCT_VER_PATCH=1700
 !ENDIF
 !IF !DEFINED(AFSPRODUCT_VER_BUILD)
 AFSPRODUCT_VER_BUILD=0
diff --git a/src/config/afs_sysnames.h b/src/config/afs_sysnames.h
index 98863594c4..3c894492dc 100644
--- a/src/config/afs_sysnames.h
+++ b/src/config/afs_sysnames.h
@@ -107,6 +107,7 @@
 #define SYS_NAME_ID_arm_darwin_240       554
 #define SYS_NAME_ID_amd64_darwin_250     555
 #define SYS_NAME_ID_arm_darwin_250       556
+#define SYS_NAME_ID_arm_darwin_270       557
 
 #define SYS_NAME_ID_next_mach20		 601
 #define SYS_NAME_ID_next_mach30		 602
diff --git a/src/config/param.darwin_270.h b/src/config/param.darwin_270.h
new file mode 100644
index 0000000000..d324b02505
--- /dev/null
+++ b/src/config/param.darwin_270.h
@@ -0,0 +1,174 @@
+#ifndef AFS_PARAM_H
+# define AFS_PARAM_H
+
+# ifndef UKERNEL
+/* This section for kernel libafs compiles only */
+
+#  define AFS_ENV                     1
+#  define AFS_64BIT_ENV               1	/* Defines afs_int32 as int, not long. */
+#  define AFS_64BIT_CLIENT            1
+#  define AFS_64BIT_IOPS_ENV          1
+#  define AFS_64BIT_SIZEOF            1 /* seriously? */
+
+#  include <afs/afs_sysnames.h>
+
+#  define AFS_DARWIN_ENV
+#  define AFS_DARWIN70_ENV
+#  define AFS_DARWIN80_ENV
+#  define AFS_DARWIN90_ENV
+#  define AFS_DARWIN100_ENV
+#  define AFS_DARWIN110_ENV
+#  define AFS_DARWIN120_ENV
+#  define AFS_DARWIN130_ENV
+#  define AFS_DARWIN140_ENV
+#  define AFS_DARWIN150_ENV
+#  define AFS_DARWIN160_ENV
+#  define AFS_DARWIN170_ENV
+#  define AFS_DARWIN180_ENV
+#  define AFS_DARWIN190_ENV
+#  define AFS_DARWIN200_ENV
+#  define AFS_DARWIN210_ENV
+#  define AFS_DARWIN220_ENV
+#  define AFS_DARWIN230_ENV
+#  define AFS_DARWIN240_ENV
+#  define AFS_DARWIN250_ENV
+#  define AFS_DARWIN270_ENV
+#  undef  AFS_NONFSTRANS
+#  define AFS_NONFSTRANS
+#  define AFS_SYSCALL                 230
+#  define AFS_NAMEI_ENV               1
+#  define DARWIN_REFBASE              3
+#  define AFS_WARNUSER_MARINER_ENV    1
+#  define AFS_CACHE_VNODE_PATH
+#  define AFS_NEW_BKG                 1
+#  define NEED_IOCTL32
+
+#  define AFS_HAVE_FFS                1	/* Use system's ffs. */
+
+#  define AFS_GCPAGS                  0
+#  define RXK_UPCALL_ENV              1
+#  define RXK_TIMEDSLEEP_ENV          1
+#  define AFS_USERSPACE_IP_ADDR       1
+#  define AFS_SOCKPROXY_ENV           1
+
+#  ifdef KERNEL
+#   undef MACRO_BEGIN
+#   undef MACRO_END
+
+#   include <kern/macro_help.h>
+
+#   define AFS_GLOBAL_SUNLOCK         1
+#   define AFS_VFS34                  1	/* What is VFS34??? */
+
+#   define afsio_iov                  uio_iov
+#   define afsio_iovcnt               uio_iovcnt
+#   define afsio_offset               uio_offset
+#   define afsio_seg                  uio_segflg
+#   define afsio_resid                uio_resid
+
+#   define AFS_UIOSYS                 UIO_SYSSPACE
+#   define AFS_UIOUSER                UIO_USERSPACE
+#   define AFS_CLBYTES                CLBYTES
+#   define AFS_KALLOC(x)              _MALLOC(x, M_TEMP, M_WAITOK)
+#   define AFS_KFREE(x,y)             _FREE(x,M_TEMP)
+
+#   define v_count                    v_usecount
+#   define v_vfsp                     v_mount
+#   define vfs_bsize                  mnt_stat.f_bsize
+#   define vfs_fsid                   mnt_stat.f_fsid
+#   define va_nodeid                  va_fileid
+#   define vfs_vnodecovered           mnt_vnodecovered
+#   define direct                     dirent
+
+#   define BIND_8_COMPAT
+#  endif /* KERNEL */
+
+# else /* !defined(UKERNEL) */
+
+/* This section for user space compiles only */
+
+#  define AFS_ENV                     1
+#  define AFS_64BIT_ENV               1	/* Defines afs_int32 as int, not long. */
+#  define AFS_64BIT_CLIENT            1
+
+#  include <afs/afs_sysnames.h>
+
+#  define AFS_USERSPACE_ENV
+#  define AFS_USR_DARWIN_ENV
+#  define AFS_USR_DARWIN70_ENV
+#  define AFS_USR_DARWIN80_ENV
+#  define AFS_USR_DARWIN90_ENV
+#  define AFS_USR_DARWIN100_ENV
+#  define AFS_USR_DARWIN110_ENV
+#  define AFS_USR_DARWIN120_ENV
+#  define AFS_USR_DARWIN130_ENV
+#  define AFS_USR_DARWIN140_ENV
+#  define AFS_USR_DARWIN150_ENV
+#  define AFS_USR_DARWIN160_ENV
+#  define AFS_USR_DARWIN170_ENV
+#  define AFS_USR_DARWIN180_ENV
+#  define AFS_USR_DARWIN190_ENV
+#  define AFS_USR_DARWIN200_ENV
+#  define AFS_USR_DARWIN210_ENV
+#  define AFS_USR_DARWIN220_ENV
+#  define AFS_USR_DARWIN230_ENV
+#  define AFS_USR_DARWIN240_ENV
+#  define AFS_USR_DARWIN250_ENV
+#  define AFS_USR_DARWIN270_ENV
+
+#  undef  AFS_NONFSTRANS
+#  define AFS_NONFSTRANS
+
+#  define AFS_SYSCALL                 230
+#  define DARWIN_REFBASE              0
+#  define AFS_WARNUSER_MARINER_ENV    1
+
+#  define AFS_HAVE_FFS                1	/* Use system's ffs. */
+
+#  define AFS_UIOSYS                  UIO_SYSSPACE
+#  define AFS_UIOUSER                 UIO_USERSPACE
+
+#  define AFS_GCPAGS                  0	/* if nonzero, garbage collect PAGs */
+#  define RXK_LISTENER_ENV            1
+
+#  define AFS_VFS34                   1	/* What is VFS34??? */
+
+#  define afsio_iov                   uio_iov
+#  define afsio_iovcnt                uio_iovcnt
+#  define afsio_offset                uio_offset
+#  define afsio_seg                   uio_segflg
+#  define afsio_resid                 uio_resid
+
+#  define VATTR_NULL                  usr_vattr_null
+
+#  define AFS_DIRENT
+#  ifndef CMSERVERPREF
+#   define CMSERVERPREF
+#  endif
+
+#  define BIND_8_COMPAT
+# endif /* !defined(UKERNEL) */
+
+/* Machine / Operating system information */
+# if defined(__arm64__)
+
+#  define AFS_ARM_ENV                 1
+#  define AFS_ARM64_DARWIN_ENV        1
+
+#  define sys_arm_darwin_200          1
+#  define sys_arm_darwin_210          1
+#  define sys_arm_darwin_220          1
+#  define sys_arm_darwin_230          1
+#  define sys_arm_darwin_240          1
+#  define sys_arm_darwin_250          1
+#  define sys_arm_darwin_270          1
+
+#  define SYS_NAME                    "arm_darwin_270"
+#  define SYS_NAME_ID                 SYS_NAME_ID_arm_darwin_270
+#  define AFSLITTLE_ENDIAN            1
+
+# else
+#  error Unsupported architecture
+# endif /* __arm64__ */
+
+#endif /* AFS_PARAM_H */
diff --git a/src/config/venus.h b/src/config/venus.h
index 81f0b87c61..cd68109b05 100644
--- a/src/config/venus.h
+++ b/src/config/venus.h
@@ -107,9 +107,4 @@ struct cm_initparams {
 
 #endif /* !defined(UKERNEL) */
 
-#if defined(AFS_CACHE_BYPASS)
-/* Uncoordinated 'O' pioctls */
-#define VIOC_SETBYPASS_THRESH	_OVICEIOCTL(2) /* cache-bypass size thresh */
-#endif
-
 #endif /* AFS_VENUS_H */
diff --git a/src/config/vioc.h b/src/config/vioc.h
index c9155d2e9b..df876760c1 100644
--- a/src/config/vioc.h
+++ b/src/config/vioc.h
@@ -25,12 +25,58 @@
 #include <afs/vice.h>
 #endif
 
-/* IOCTLS to Venus.  Apply these to open file decriptors. */
+
+/*
+ * IOCTLS to Venus.  Apply these to open file descriptors.
+ *
+ * Transarc/IBM and Legacy codes.
+ *
+ * This namespace is managed by IBM.
+ *
+ * ** Reserved codes
+ *
+ * Code 0 is reserved and must not be used.
+ *
+ * Code V.239 is reserved for future code space expansion
+ * by the registrar and must not be used for any other purpose.
+ *
+ * Codes V.240 through V.247 (VIOC_SITE1 through VIOC_SITE8)
+ * are reserved for use by extensions local to a specific site.
+ * They should never be used in any distributed software.
+ *
+ * Codes V.248 through V.255 (VIOC_PRIVATE1 through VIOC_PRIVATE8)
+ * are reserved for private implementation-specific extensions.
+ * They should be used only by software which is absolutely certain
+ * which cache manager implementation is in use.
+ */
 #define	VIOCCLOSEWAIT		_VICEIOCTL(1)	/* Force close to wait for store */
 #define	VIOCABORT		_VICEIOCTL(2)	/* Abort close on this fd */
 #define	VIOCIGETCELL		_VICEIOCTL(3)	/* ioctl to get cell name */
+/* End of IBM managed ioctl namespace */
 
-/* PIOCTLS to Venus.  Apply these to path names with pioctl. */
+/*
+ * PIOCTLS to Venus.  Apply these to path names with pioctl.
+ *
+ * Transarc/IBM and Legacy codes.
+ *
+ * This namespace is managed by IBM.
+ *
+ * ** Reserved codes
+ *
+ * Code 0 is reserved and must not be used.
+ *
+ * Code V.239 is reserved for future code space expansion
+ * by the registrar and must not be used for any other purpose.
+ *
+ * Codes V.240 through V.247 (VIOC_SITE1 through VIOC_SITE8)
+ * are reserved for use by extensions local to a specific site.
+ * They should never be used in any distributed software.
+ *
+ * Codes V.248 through V.255 (VIOC_PRIVATE1 through VIOC_PRIVATE8)
+ * are reserved for private implementation-specific extensions.
+ * They should be used only by software which is absolutely certain
+ * which cache manager implementation is in use.
+ */
 #define	VIOCSETAL		_VICEIOCTL(1)	/* Set access control list */
 #define	VIOCGETAL		_VICEIOCTL(2)	/* Get access control list */
 #define	VIOCSETTOK		_VICEIOCTL(3)	/* Set authentication tokens */
@@ -91,10 +137,19 @@
 #define VIOC_FS_CMD             _VICEIOCTL(67)  /* fs extensions for osd etc. */
 #define VIOC_RESIDENCY_CMD      VIOC_FS_CMD
 
-#define VIOC_STATISTICS         _VICEIOCTL(68)	/* arla: fetch statistics */
 #define VIOC_GETVCXSTATUS2      _VICEIOCTL(69)  /* vcache statistics */
+/* End of IBM managed pioctl namespace */
 
-/* Coordinated 'C' pioctl's */
+/*
+ * Coordinated 'C' pioctl's managed by https://registrar.central.org
+ *
+ * Codes for ioctl() and pioctl() functions in the 'C' group will be
+ * assigned on request for new common, implementation-independent functions
+ * supported by any cache manager implementation wishing to provide these
+ * interfaces.
+ *
+ * Submit requests to registrar@grand.central.org.
+ */
 #define VIOC_NEWALIAS		_CVICEIOCTL(1)	/* create new cell alias */
 #define VIOC_GETALIAS		_CVICEIOCTL(2)	/* get alias info */
 #define VIOC_CBADDR		_CVICEIOCTL(3)	/* push callback addr */
@@ -106,8 +161,20 @@
 #define VIOC_GETPAG             _CVICEIOCTL(13) /* get pag value */
 #define VIOC_FLUSHALL           _CVICEIOCTL(14) /* flush all volume data */
 #define VIOC_GETLITERALFID      _CVICEIOCTL(15) /* get fid from parent directory */
+/* End of Coordinated 'C' pioctl namespace.*/
 
-/* OpenAFS-specific 'O' pioctl's */
+/*
+ * OpenAFS-specific 'O' pioctl's
+ *
+ * The OpenAFS-specific codes are managed by the maintainers of OpenAFS.
+ *
+ * Please send a notification of allocation to registrar@grand.central.org
+ * to permit the https://registrar.central.org published registry to be
+ * updated.
+ */
 #define VIOC_NFS_NUKE_CREDS	_OVICEIOCTL(1)	/* nuke creds for all PAG's */
+#define VIOC_SETBYPASS_THRESH	_OVICEIOCTL(2)	/* cache-bypass size thresh */
+#define VIOC_UNLOG_CELL		_OVICEIOCTL(3)	/* Invalidate token by cell */
+/* End of OpenAFS 'O' managed pioctl namespace */
 
 #endif /* AFS_VIOC_H */
diff --git a/src/libafs/afs.arm_darwin_270.plist.in b/src/libafs/afs.arm_darwin_270.plist.in
new file mode 100644
index 0000000000..a1385718c5
--- /dev/null
+++ b/src/libafs/afs.arm_darwin_270.plist.in
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist SYSTEM "file://localhost/System/Library/DTDs/PropertyList.dtd">
+<plist version="0.9">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>English</string>
+	<key>CFBundleExecutable</key>
+	<string>afs</string>
+	<key>CFBundleIdentifier</key>
+	<string>org.openafs.filesystems.afs</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>8.0</string>
+	<key>CFBundleName</key>
+	<string>afs</string>
+	<key>CFBundlePackageType</key>
+	<string>KEXT</string>
+	<key>CFBundleShortVersionString</key>
+	<string>@MACOS_VERSION@</string>
+	<key>CFBundleSignature</key>
+	<string>????</string>
+	<key>CFBundleVersion</key>
+	<string>@MACOS_VERSION@</string>
+	<key>OSBundleLibraries</key>
+	<dict>
+		<key>com.apple.kpi.bsd</key>
+		<string>8.0.0</string>
+		<key>com.apple.kpi.mach</key>
+		<string>8.0.0</string>
+		<key>com.apple.kpi.libkern</key>
+		<string>8.0</string>
+	</dict>
+</dict>
+</plist>
diff --git a/src/libafsauthent/afsauthent.def b/src/libafsauthent/afsauthent.def
index 10c71cbfc7..32baf28de1 100644
--- a/src/libafsauthent/afsauthent.def
+++ b/src/libafsauthent/afsauthent.def
@@ -210,3 +210,4 @@ EXPORTS
         xdr_namelist					@209
         xdr_prlist					@210
         afsconf_CountKeys				@211
+	ktc_ForgetTokensByCell				@215
diff --git a/src/libafsauthent/afsauthent.exp b/src/libafsauthent/afsauthent.exp
index fc617cef88..c788d36206 100644
--- a/src/libafsauthent/afsauthent.exp
+++ b/src/libafsauthent/afsauthent.exp
@@ -44,6 +44,7 @@ ka_UserAuthenticateGeneral
 ka_VerifyUserToken
 ktc_ForgetAllTokens
 ktc_ForgetToken
+ktc_ForgetTokensByCell
 ktc_GetToken
 ktc_ListTokens
 ktc_OldPioctl
diff --git a/src/libafsauthent/libafsauthent.la.sym b/src/libafsauthent/libafsauthent.la.sym
index 83aa04ee81..68ca6d60dc 100644
--- a/src/libafsauthent/libafsauthent.la.sym
+++ b/src/libafsauthent/libafsauthent.la.sym
@@ -88,6 +88,7 @@ ka_UserAuthenticateGeneral
 ka_VerifyUserToken
 ktc_ForgetAllTokens
 ktc_ForgetToken
+ktc_ForgetTokensByCell
 ktc_GetToken
 ktc_GetTokenEx
 ktc_ListTokens
diff --git a/src/log/unlog.c b/src/log/unlog.c
index bc4428e483..995c8765f2 100644
--- a/src/log/unlog.c
+++ b/src/log/unlog.c
@@ -38,21 +38,21 @@
 #include <afs/cellconfig.h>
 #include <afs/afsutil.h>
 #include <afs/cmd.h>
+#include <afs/token.h>
+#include <afs/ktc.h>
 
 #undef VIRTUE
 #undef VICE
 
 
 struct tokenInfo {
-    struct ktc_token token;
-    struct ktc_principal service;
-    struct ktc_principal client;
+    struct ktc_setTokenData *tokenSet;
     int deleted;
 };
 
 static int unlog_ForgetCertainTokens(char **, int);
 static int unlog_NormalizeCellNames(char **, int);
-static int unlog_CheckUnlogList(char **, int, struct ktc_principal *);
+static int unlog_CheckUnlogList(char **, int, char *);
 static int unlog_VerifyUnlog(char **, int, struct tokenInfo *, int);
 
 static int
@@ -115,35 +115,51 @@ main(int argc, char *argv[])
     exit(code != 0);
 }				/*Main routine */
 
-
 /*
- * Problem: only the KTC gives you the ability to selectively destroy
- *          a specified token.
- *
- * Solution: Build a list of tokens, delete the bad ones (the ones to
- *           remove from the permissions list,) destroy all tokens, and
- *           then re-register the good ones.  Ugly, but it works.
+ * This function first attempts to delete the tokens for the cells specified
+ * in the list. If the PUnlogCell pioctl is not supported, it falls back to the
+ * older logic, which builds a list of tokens, deletes the bad ones, destroys
+ * all tokens, and then re-registers the good ones.
  */
-
 static int
 unlog_ForgetCertainTokens(char **list, int listSize)
 {
+    int cell_i;
     int index, index2;
     int count;
-    afs_int32 code;
-    struct ktc_principal serviceName;
+    afs_int32 code = 0;
+    char *cellName = NULL;
     struct tokenInfo *tokenInfoP;
 
     /* normalize all the names in the list */
     unlog_NormalizeCellNames(list, listSize);
 
+    for (cell_i = 0; cell_i < listSize; cell_i++) {
+	afs_int32 ret = ktc_ForgetTokensByCell(list[cell_i]);
+	if (ret == KTC_NOPIOCTL && cell_i == 0) {
+	    fprintf(stderr, "unlog: Kernel module does not support unlog by "
+		    "cell. Using legacy unlog method.\n");
+	    /* Fallback to older logic */
+	    goto fallback;
+	}
+	if (ret != 0) {
+	    fprintf(stderr, "unlog: could not discard tokens for cell %s, "
+		"code %d\n", list[cell_i], ret);
+	    code = 1;
+	}
+    }
+    return code;
+
+ fallback:
     /* figure out how many tokens exist */
     count = 0;
     do {
-	code = ktc_ListTokens(count, &count, &serviceName);
+	code = ktc_ListTokensEx(count, &count, &cellName);
+	free(cellName);
+	cellName = NULL;
     } while (!code);
 
-    tokenInfoP = malloc((sizeof(struct tokenInfo) * count));
+    tokenInfoP = calloc(count, sizeof(tokenInfoP[0]));
     if (!tokenInfoP) {
 	perror("unlog_ForgetCertainTokens -- osi_Alloc failed");
 	exit(1);
@@ -151,20 +167,19 @@ unlog_ForgetCertainTokens(char **list, int listSize)
 
     for (code = index = index2 = 0; (!code) && (index < count); index++) {
 	code =
-	    ktc_ListTokens(index2, &index2, &(tokenInfoP + index)->service);
+	    ktc_ListTokensEx(index2, &index2, &cellName);
 
 	if (!code) {
 	    code =
-		ktc_GetToken(&(tokenInfoP + index)->service,
-			     &(tokenInfoP + index)->token,
-			     sizeof(struct ktc_token),
-			     &(tokenInfoP + index)->client);
+		ktc_GetTokenEx(cellName, &tokenInfoP[index].tokenSet);
 
 	    if (!code)
-		(tokenInfoP + index)->deleted =
+		tokenInfoP[index].deleted =
 		    unlog_CheckUnlogList(list, listSize,
-					 &(tokenInfoP + index)->client);
+					 tokenInfoP[index].tokenSet->cell);
 	}
+	free(cellName);
+	cellName = NULL;
     }
 
     unlog_VerifyUnlog(list, listSize, tokenInfoP, count);
@@ -176,11 +191,9 @@ unlog_ForgetCertainTokens(char **list, int listSize)
     }
 
     for (code = index = 0; index < count; index++) {
-	if (!((tokenInfoP + index)->deleted)) {
+	if (!tokenInfoP[index].deleted) {
 	    code =
-		ktc_SetToken(&(tokenInfoP + index)->service,
-			     &(tokenInfoP + index)->token,
-			     &(tokenInfoP + index)->client, 0);
+		ktc_SetTokenEx(tokenInfoP[index].tokenSet);
 	    if (code) {
 		fprintf(stderr, "Couldn't re-register token, code = %d\n",
 			code);
@@ -194,10 +207,10 @@ unlog_ForgetCertainTokens(char **list, int listSize)
  * 0 if not in list, 1 if in list
  */
 int
-unlog_CheckUnlogList(char **list, int count, struct ktc_principal *principal)
+unlog_CheckUnlogList(char **list, int count, char *cell)
 {
     do {
-	if (strcmp(*list, principal->cell) == 0)
+	if (strcmp(*list, cell) == 0)
 	    return 1;
 	list++;
 	--count;
@@ -270,7 +283,7 @@ unlog_VerifyUnlog(char **cellList, int cellListSize, struct tokenInfo *tokenList
 	for (found = index2 = 0; !found && index2 < tokenListSize; index2++)
 	    found =
 		strcmp(cellList[index],
-		       (tokenList + index2)->client.cell) == 0;
+		       tokenList[index2].tokenSet->cell) == 0;
 
 	if (!found)
 	    fprintf(stderr, "unlog: Warning - no tokens held for cell %s\n",
diff --git a/src/packaging/MacOS/ReadMe.rtf.27 b/src/packaging/MacOS/ReadMe.rtf.27
new file mode 100644
index 0000000000..c9d1b6bb29
--- /dev/null
+++ b/src/packaging/MacOS/ReadMe.rtf.27
@@ -0,0 +1,7 @@
+{\rtf1\ansi\ansicpg1252\cocoartf1038\cocoasubrtf320
+{\fonttbl\f0\fswiss\fcharset0 Helvetica;}
+{\colortbl;\red255\green255\blue255;}
+\margl1440\margr1440\vieww9000\viewh9000\viewkind0
+\pard\tx1440\tx2880\tx4320\tx5760\tx7200\ql\qnatural
+
+\f0\fs24 \cf0 This release of OpenAFS is targeted at MacOS 27.X (Golden Gate) and is not recommended for use with any other version.}
diff --git a/src/packaging/MacOS/pkgbuild.sh.in b/src/packaging/MacOS/pkgbuild.sh.in
index 95f66794ef..7d382d96d8 100644
--- a/src/packaging/MacOS/pkgbuild.sh.in
+++ b/src/packaging/MacOS/pkgbuild.sh.in
@@ -177,6 +177,10 @@ elif [ x"$majorvers" = x25 ]; then
     RELNAME="Tahoe"
     THISREL=26
     OSVER=26
+elif [ x"$majorvers" = x27 ]; then
+    RELNAME="Golden Gate"
+    THISREL=27
+    OSVER=27
 
 
 elif [ x"$majorvers" = x ] ; then
diff --git a/src/rx/rx.c b/src/rx/rx.c
index 5048d02cb4..6836854f07 100644
--- a/src/rx/rx.c
+++ b/src/rx/rx.c
@@ -8642,6 +8642,30 @@ rxi_AddRpcStat(struct opr_queue *stats, afs_uint32 rxInterface,
 	goto fail;
     }
 
+    if (totalFunc != rpc_stat->stats[0].func_total) {
+	/*
+	 * The struct we found matches the rxInterface and isServer of the
+	 * caller, but the total number of functions doesn't match. That must
+	 * be a mistake; the interface that the caller is talking about doesn't
+	 * seem to match the interface in our struct. Don't count the stats for
+	 * this; the caller is almost certainly talking about a different
+	 * function, and so including it in the stats will make the stats
+	 * wrong/meaningless.
+	 */
+	rc = -1;
+	goto fail;
+    }
+
+    if (currentFunc >= rpc_stat->stats[0].func_total) {
+	/*
+	 * rpc_stat->stats doesn't have enough space allocated to store stats
+	 * for this function. This should never happen, but if it somehow does,
+	 * make sure we don't corrupt memory.
+	 */
+	rc = -1;
+	goto fail;
+    }
+
     /*
      * Increment the stats for this function
      */
diff --git a/src/volser/vsprocs.c b/src/volser/vsprocs.c
index 9c36a46d3c..91dc47faf9 100644
--- a/src/volser/vsprocs.c
+++ b/src/volser/vsprocs.c
@@ -1340,14 +1340,6 @@ UV_ConvertRO(afs_uint32 server, afs_uint32 partition, afs_uint32 volid,
 	}
     }
 
-    /* volume must be inaccessible during this process */
-    code = UV_SetVolume(server, partition, volid, ITOffline, VTOutOfService, 0);
-    if (code != 0) {
-	fprintf(STDERR, "Taking RO volume %u offline failed with code %d.\n",
-		volid, code);
-	goto error_exit;
-    }
-
     aconn = UV_Bind(server, AFSCONF_VOLUMEPORT);
     code = AFSVolConvertROtoRWvolume(aconn, partition, volid);
     if (code) {
@@ -1357,18 +1349,6 @@ UV_ConvertRO(afs_uint32 server, afs_uint32 partition, afs_uint32 volid,
 	PrintError("convertROtoRW ", code);
 	goto error_exit;
     }
-
-    /*
-     * Since the inService flag is copied from the RO volume, the new RW copy is
-     * offline. Change the status of this RW volume to online.
-     */
-    code = UV_SetVolume(server, partition, entry->volumeId[RWVOL], ITOffline,
-			0 /* online */, 0);
-    if (code != 0) {
-	fprintf(STDERR, "Warning: Attempt to set RW volume %u as online failed "
-			"with code %d.\n", entry->volumeId[RWVOL], code);
-    }
-
     /* Update the VLDB to match what we did on disk as much as possible.  */
     /* If the converted RO was in the VLDB, make it look like the new RW. */
     if (roserver) {
