nfscl: Add vfs.nfs.maxalloclen to limit Allocate/Deallocate RPC RTT

Unlike Copy, the NFSv4.2 Allocate and Deallocate operations do not
allow a reply with partial completion.  As such, the only way to
limit the time the operation takes to provide a reasonable RPC RTT
is to limit the size of the allocation/deallocation in the NFSv4.2
client.

This patch adds a sysctl called vfs.nfs.maxalloclen to set
the limit on the size of the Allocate operation.
There is no way to know how long a server will take to do an
allocate operation, but 64Mbytes results in a reasonable
RPC RTT for the slow hardware I test on, so that is what
the default value for vfs.nfs.maxalloclen is set to.

For an 8Gbyte allocation, the elapsed time for doing it in 64Mbyte
chunks was the same as the elapsed time taken for a single large
allocation operation for a FreeBSD server with a UFS file system.

MFC after:	2 weeks
This commit is contained in:
Rick Macklem 2021-09-15 17:29:45 -07:00
parent e51aabf8cb
commit 9ebe4b8c67

View File

@ -301,6 +301,10 @@ int newnfs_directio_allow_mmap = 1;
SYSCTL_INT(_vfs_nfs, OID_AUTO, nfs_directio_allow_mmap, CTLFLAG_RW,
&newnfs_directio_allow_mmap, 0, "Enable mmaped IO on file with O_DIRECT opens");
static uint64_t nfs_maxalloclen = 64 * 1024 * 1024;
SYSCTL_U64(_vfs_nfs, OID_AUTO, maxalloclen, CTLFLAG_RW,
&nfs_maxalloclen, 0, "NFS max allocate/deallocate length");
#define NFSACCESS_ALL (NFSACCESS_READ | NFSACCESS_MODIFY \
| NFSACCESS_EXTEND | NFSACCESS_EXECUTE \
| NFSACCESS_DELETE | NFSACCESS_LOOKUP)
@ -3639,6 +3643,7 @@ nfs_allocate(struct vop_allocate_args *ap)
struct thread *td = curthread;
struct nfsvattr nfsva;
struct nfsmount *nmp;
off_t alen;
int attrflag, error, ret;
attrflag = 0;
@ -3652,12 +3657,16 @@ nfs_allocate(struct vop_allocate_args *ap)
* file's allocation on the server.
*/
error = ncl_flush(vp, MNT_WAIT, td, 1, 0);
if (error == 0)
error = nfsrpc_allocate(vp, *ap->a_offset, *ap->a_len,
&nfsva, &attrflag, td->td_ucred, td, NULL);
if (error == 0) {
*ap->a_offset += *ap->a_len;
*ap->a_len = 0;
alen = *ap->a_len;
if ((uint64_t)alen > nfs_maxalloclen)
alen = nfs_maxalloclen;
error = nfsrpc_allocate(vp, *ap->a_offset, alen,
&nfsva, &attrflag, td->td_ucred, td, NULL);
}
if (error == 0) {
*ap->a_offset += alen;
*ap->a_len -= alen;
} else if (error == NFSERR_NOTSUPP) {
mtx_lock(&nmp->nm_mtx);
nmp->nm_privflag |= NFSMNTP_NOALLOCATE;