Fix the kernel part of pnfsdscopymr() to handle holes in the file being copied.

If a mirrored DS is being recovered that has a lot of large sparse files,
pnfsdscopymr(8) would use a lot of space on the recovered mirror since it
would write the "holes" in the file being mirrored.
This patch adds code to check for a "hole" and skip doing the write.
The check is done on a "per PNFSDS_COPYSIZ size block", which is currently 64K.
I think that most file server file systems will be using a blocksize at
least this large. If the file server is using a smaller blocksize and
smaller holes need to be preserved, PNFSDS_COPYSIZ could be decreased.
The block of 0s is malloc()d, since pnfsdcopymr(8) should be an infrequent
occurrence.
This commit is contained in:
rmacklem 2018-07-08 18:15:55 +00:00
parent f3d2853f4b
commit da485b59a5
2 changed files with 20 additions and 3 deletions

View File

@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$");
NFSDLOCKMUTEX;
NFSV4ROOTLOCKMUTEX;
struct nfsv4lock nfsd_suspend_lock;
char *nfsrv_zeropnfsdat = NULL;
/*
* Mapping of old NFS Version 2 RPC numbers to generic numbers.
@ -565,6 +566,8 @@ nfsrvd_init(int terminating)
nfsrv_freealllayoutsanddevids();
nfsrv_freeallbackchannel_xprts();
svcpool_close(nfsrvd_pool);
free(nfsrv_zeropnfsdat, M_TEMP);
nfsrv_zeropnfsdat = NULL;
NFSD_LOCK();
} else {
NFSD_UNLOCK();

View File

@ -60,6 +60,7 @@ NFSSTATESPINLOCK;
extern struct nfsdontlisthead nfsrv_dontlisthead;
extern volatile int nfsrv_devidcnt;
extern struct nfslayouthead nfsrv_recalllisthead;
extern char *nfsrv_zeropnfsdat;
SYSCTL_DECL(_vfs_nfsd);
int nfsrv_statehashsize = NFSSTATEHASHSIZE;
@ -8124,9 +8125,15 @@ tryagain2:
if (retacl != 0 && retacl != ENOATTR)
NFSD_DEBUG(1, "nfsrv_copymr: vop_getacl=%d\n", retacl);
dat = malloc(PNFSDS_COPYSIZ, M_TEMP, M_WAITOK);
/* Malloc a block of 0s used to check for holes. */
if (nfsrv_zeropnfsdat == NULL)
nfsrv_zeropnfsdat = malloc(PNFSDS_COPYSIZ, M_TEMP,
M_WAITOK | M_ZERO);
rdpos = wrpos = 0;
mp = NULL;
ret = vn_start_write(tvp, &mp, V_WAIT | PCATCH);
if (ret == 0)
ret = VOP_GETATTR(fvp, &va, cred);
aresid = 0;
while (ret == 0 && aresid == 0) {
ret = vn_rdwr(UIO_READ, fvp, dat, PNFSDS_COPYSIZ,
@ -8135,9 +8142,16 @@ tryagain2:
xfer = PNFSDS_COPYSIZ - aresid;
if (ret == 0 && xfer > 0) {
rdpos += xfer;
ret = vn_rdwr(UIO_WRITE, tvp, dat, xfer,
wrpos, UIO_SYSSPACE, IO_NODELOCKED,
cred, NULL, NULL, p);
/*
* Skip the write for holes, except for the
* last block.
*/
if (xfer < PNFSDS_COPYSIZ || rdpos ==
va.va_size || NFSBCMP(dat,
nfsrv_zeropnfsdat, PNFSDS_COPYSIZ) != 0)
ret = vn_rdwr(UIO_WRITE, tvp, dat, xfer,
wrpos, UIO_SYSSPACE, IO_NODELOCKED,
cred, NULL, NULL, p);
if (ret == 0)
wrpos += xfer;
}