Modify vfs_register() to use a hash calculation

on vfc_name to set vfc_typenum, so that vfc_typenum doesn't
change when file systems are loaded in different orders. This
keeps NFS file handles from changing, for file systems that
use vfc_typenum in their fsid. This change is controlled via
a loader.conf variable called vfs.typenumhash, since vfc_typenum
will change once when this is enabled. It defaults to 1 for
9.0, but will default to 0 when MFC'd to stable/8.

Tested by:	hrs
Reviewed by:	jhb, pjd (earlier version)
Approved by:	re (kib)
MFC after:	1 month
This commit is contained in:
Rick Macklem 2011-09-13 21:01:26 +00:00
parent 43fb93b0da
commit 4d30adc494
2 changed files with 56 additions and 1 deletions

View File

@ -22,6 +22,18 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 9.x IS SLOW:
machines to maximize performance. (To disable malloc debugging, run
ln -s aj /etc/malloc.conf.)
20110913:
This commit modifies vfs_register() so that it uses a hash
calculation to set vfc_typenum, which is enabled by default.
The first time a system is booted after this change, the
vfc_typenum values will change for all file systems. The
main effect of this is a change to the NFS server file handles
for file systems that use vfc_typenum in their fsid, such as ZFS.
It will, however, prevent vfc_typenum from changing when file
systems are loaded in a different order for subsequent reboots.
To disable this, you can set vfs.typenumhash=0 in /boot/loader.conf
until you are ready to remount all NFS clients after a reboot.
20110828:
Bump the shared library version numbers for libraries that
do not use symbol versioning, have changed the ABI compared

View File

@ -39,6 +39,7 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/fnv_hash.h>
#include <sys/kernel.h>
#include <sys/linker.h>
#include <sys/mount.h>
@ -64,6 +65,18 @@ int maxvfsconf = VFS_GENERIC + 1;
*/
struct vfsconfhead vfsconf = TAILQ_HEAD_INITIALIZER(vfsconf);
/*
* Loader.conf variable vfs.typenumhash enables setting vfc_typenum using a hash
* calculation on vfc_name, so that it doesn't change when file systems are
* loaded in a different order. This will avoid the NFS server file handles from
* changing for file systems that use vfc_typenum in their fsid.
*/
static int vfs_typenumhash = 1;
TUNABLE_INT("vfs.typenumhash", &vfs_typenumhash);
SYSCTL_INT(_vfs, OID_AUTO, typenumhash, CTLFLAG_RDTUN, &vfs_typenumhash, 0,
"Set vfc_typenum using a hash calculation on vfc_name, so that it does not"
"change when file systems are loaded in a different order.");
/*
* A Zen vnode attribute structure.
*
@ -138,6 +151,9 @@ vfs_register(struct vfsconf *vfc)
struct sysctl_oid *oidp;
struct vfsops *vfsops;
static int once;
struct vfsconf *tvfc;
uint32_t hashval;
int secondpass;
if (!once) {
vattr_null(&va_null);
@ -152,7 +168,34 @@ vfs_register(struct vfsconf *vfc)
if (vfs_byname(vfc->vfc_name) != NULL)
return EEXIST;
vfc->vfc_typenum = maxvfsconf++;
if (vfs_typenumhash != 0) {
/*
* Calculate a hash on vfc_name to use for vfc_typenum. Unless
* all of 1<->255 are assigned, it is limited to 8bits since
* that is what ZFS uses from vfc_typenum and is also the
* preferred range for vfs_getnewfsid().
*/
hashval = fnv_32_str(vfc->vfc_name, FNV1_32_INIT);
hashval &= 0xff;
secondpass = 0;
do {
/* Look for and fix any collision. */
TAILQ_FOREACH(tvfc, &vfsconf, vfc_list) {
if (hashval == tvfc->vfc_typenum) {
if (hashval == 255 && secondpass == 0) {
hashval = 1;
secondpass = 1;
} else
hashval++;
break;
}
}
} while (tvfc != NULL);
vfc->vfc_typenum = hashval;
if (vfc->vfc_typenum >= maxvfsconf)
maxvfsconf = vfc->vfc_typenum + 1;
} else
vfc->vfc_typenum = maxvfsconf++;
TAILQ_INSERT_TAIL(&vfsconf, vfc, vfc_list);
/*