#define MAX_COMMIT_COUNT (1024 * 1024)
-#define NUM_HEURISTIC 1017
+#define NUM_HEURISTIC 1031
#define NHUSE_INIT 64
#define NHUSE_INC 16
#define NHUSE_MAX 2048
static struct nfsheur {
struct vnode *nh_vp; /* vp to match (unreferenced pointer) */
- off_t nh_nextr; /* next offset for sequential detection */
+ off_t nh_nextoff; /* next offset for sequential detection */
int nh_use; /* use count for selection */
int nh_seqcount; /* heuristic */
} nfsheur[NUM_HEURISTIC];
struct nfsrv_descript *);
/*
+ * Heuristic to detect sequential operation.
+ */
+static struct nfsheur *
+nfsrv_sequential_heuristic(struct uio *uio, struct vnode *vp, int writeop)
+{
+ struct nfsheur *nh;
+ int hi, try;
+
+ /* Locate best candidate */
+ try = 32;
+ hi = ((int)(vm_offset_t) vp / sizeof(struct vnode)) % NUM_HEURISTIC;
+ nh = &nfsheur[hi];
+
+ while (try--) {
+ if (nfsheur[hi].nh_vp == vp) {
+ nh = &nfsheur[hi];
+ break;
+ }
+ if (nfsheur[hi].nh_use > 0)
+ --nfsheur[hi].nh_use;
+ hi = (hi + 1) % NUM_HEURISTIC;
+ if (nfsheur[hi].nh_use < nh->nh_use)
+ nh = &nfsheur[hi];
+ }
+
+ /* Initialize hint if this is a new file */
+ if (nh->nh_vp != vp) {
+ nh->nh_vp = vp;
+ nh->nh_nextoff = uio->uio_offset;
+ nh->nh_use = NHUSE_INIT;
+ if (uio->uio_offset == 0)
+ nh->nh_seqcount = 4;
+ else
+ nh->nh_seqcount = 1;
+ }
+
+ /*
+ * Calculate heuristic
+ *
+ * See vfs_vnops.c:sequential_heuristic().
+ */
+ if ((uio->uio_offset == 0 && nh->nh_seqcount > 0) ||
+ uio->uio_offset == nh->nh_nextoff) {
+ nh->nh_seqcount += howmany(uio->uio_resid, 16384);
+ if (nh->nh_seqcount > IO_SEQMAX)
+ nh->nh_seqcount = IO_SEQMAX;
+ } else if (nh->nh_seqcount > 1) {
+ nh->nh_seqcount = 1;
+ } else {
+ nh->nh_seqcount = 0;
+ }
+ nh->nh_use += NHUSE_INC;
+ if (nh->nh_use > NHUSE_MAX)
+ nh->nh_use = NHUSE_MAX;
+ return (nh);
+}
+
+/*
* nfs v3 access service
*/
int
else
cnt = reqlen;
- /*
- * Calculate seqcount for heuristic
- */
-
- {
- int hi;
- int try = 32;
-
- /*
- * Locate best candidate
- */
-
- hi = ((int)(vm_offset_t)vp / sizeof(struct vnode)) % NUM_HEURISTIC;
- nh = &nfsheur[hi];
-
- while (try--) {
- if (nfsheur[hi].nh_vp == vp) {
- nh = &nfsheur[hi];
- break;
- }
- if (nfsheur[hi].nh_use > 0)
- --nfsheur[hi].nh_use;
- hi = (hi + 1) % NUM_HEURISTIC;
- if (nfsheur[hi].nh_use < nh->nh_use)
- nh = &nfsheur[hi];
- }
-
- if (nh->nh_vp != vp) {
- nh->nh_vp = vp;
- nh->nh_nextr = off;
- nh->nh_use = NHUSE_INIT;
- if (off == 0)
- nh->nh_seqcount = 4;
- else
- nh->nh_seqcount = 1;
- }
-
- /*
- * Calculate heuristic
- */
-
- if ((off == 0 && nh->nh_seqcount > 0) || off == nh->nh_nextr) {
- if (++nh->nh_seqcount > IO_SEQMAX)
- nh->nh_seqcount = IO_SEQMAX;
- } else if (nh->nh_seqcount > 1) {
- nh->nh_seqcount = 1;
- } else {
- nh->nh_seqcount = 0;
- }
- nh->nh_use += NHUSE_INC;
- if (nh->nh_use > NHUSE_MAX)
- nh->nh_use = NHUSE_MAX;
- ioflag |= nh->nh_seqcount << IO_SEQSHIFT;
- }
-
NEGKEEPOUT(nfsm_reply(&info, nfsd, slp,
NFSX_POSTOPORFATTR(info.v3) +
3 * NFSX_UNSIGNED + nfsm_rndup(cnt),
uiop->uio_resid = len;
uiop->uio_rw = UIO_READ;
uiop->uio_segflg = UIO_SYSSPACE;
+ nh = nfsrv_sequential_heuristic(uiop, vp, 0);
+ ioflag |= nh->nh_seqcount << IO_SEQSHIFT;
error = VOP_READ(vp, uiop, IO_NODELOCKED | ioflag, cred);
- off = uiop->uio_offset;
- nh->nh_nextr = off;
+ if (error == 0) {
+ off = uiop->uio_offset;
+ nh->nh_nextoff = off;
+ }
kfree((caddr_t)iv2, M_TEMP);
if (error || (getret = VOP_GETATTR(vp, vap))) {
if (!error)
nfsm_adj(info.mb, len - tlen, tlen - cnt);
if (info.v3) {
*tl++ = txdr_unsigned(cnt);
- if (len < reqlen)
+ if (cnt < reqlen)
*tl++ = nfs_true;
else
*tl++ = nfs_false;
int stable = NFSV3WRITE_FILESYNC;
struct vnode *vp = NULL;
struct mount *mp = NULL;
+ struct nfsheur *nh;
nfsfh_t nfh;
fhandle_t *fhp;
struct uio io, *uiop = &io;
uiop->uio_segflg = UIO_SYSSPACE;
uiop->uio_td = NULL;
uiop->uio_offset = off;
+ nh = nfsrv_sequential_heuristic(uiop, vp, 1);
+ ioflags |= nh->nh_seqcount << IO_SEQSHIFT;
error = VOP_WRITE(vp, uiop, ioflags, cred);
+ if (error == 0)
+ nh->nh_nextoff = uiop->uio_offset;
nfsstats.srvvop_writes++;
kfree((caddr_t)iv, M_TEMP);
}
}
for_ret = VOP_GETATTR(vp, &bfor);
- if (cnt > MAX_COMMIT_COUNT) {
+ /*
+ * RFC 1813 3.3.21: If count is 0, a flush from offset to the end of
+ * file is done. At this time VOP_FSYNC does not accept offset and
+ * byte count parameters, so call VOP_FSYNC the whole file for now.
+ */
+ if (cnt == 0 || cnt > MAX_COMMIT_COUNT) {
/*
* Give up and do the whole thing
*/
#ifndef NFS_NOSERVER
static long numnfsrvcache;
-static long desirednfsrvcache = NFSRVCACHESIZ;
+static long desirednfsrvcache;
#define NFSRCHASH(xid) \
(&nfsrvhashtbl[((xid) + ((xid) >> 24)) & nfsrvhash])
};
/*
+ * Size the NFS server's duplicate request cache at 1/2 the nmbclusters,
+ * within a (64, 2048) range. This is to prevent all mbuf clusters being
+ * tied up in the NFS dupreq cache for small values of nmbclusters.
+ */
+static void
+nfsrvcache_size_change(void)
+{
+ desirednfsrvcache = nmbclusters / 2;
+ desirednfsrvcache = MIN(desirednfsrvcache, NFSRVCACHE_MAX_SIZE);
+ desirednfsrvcache = MAX(desirednfsrvcache, NFSRVCACHE_MIN_SIZE);
+}
+
+/*
* Initialize the server request cache list
*/
void
nfsrv_initcache(void)
{
+ nfsrvcache_size_change();
nfsrvhashtbl = hashinit(desirednfsrvcache, M_NFSD, &nfsrvhash);
TAILQ_INIT(&nfsrvlruhead);
}