mirror of
https://github.com/freebsd/freebsd-src
synced 2024-10-15 12:54:27 +00:00
vfs_vnops.c: Use va_bytes >= va_size hint to avoid SEEK_DATA/SEEKHOLE
vn_generic_copy_file_range() tries to maintain holes in file ranges being copied, using SEEK_DATA/SEEK_HOLE where possible, Unfortunately SEEK_DATA/SEEK_HOLE operations can take a long time under certain circumstances. Although it is not currently possible to know if a file has unallocated data regions, the case where va_bytes >= va_size is a strong hint that there are no unallocated data regions. This hint does not work well for file systems doing compression, but since it is only a hint, it is still useful. For the case of va_bytes >= va_size, avoid doing SEEK_DATA/SEEK_HOLE. Reviewed by: kib MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D44509
This commit is contained in:
parent
6a6ec90681
commit
89f1dcb3eb
|
@ -3334,14 +3334,15 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||||
struct vnode *outvp, off_t *outoffp, size_t *lenp, unsigned int flags,
|
struct vnode *outvp, off_t *outoffp, size_t *lenp, unsigned int flags,
|
||||||
struct ucred *incred, struct ucred *outcred, struct thread *fsize_td)
|
struct ucred *incred, struct ucred *outcred, struct thread *fsize_td)
|
||||||
{
|
{
|
||||||
|
struct vattr inva;
|
||||||
struct mount *mp;
|
struct mount *mp;
|
||||||
off_t startoff, endoff, xfer, xfer2;
|
off_t startoff, endoff, xfer, xfer2;
|
||||||
u_long blksize;
|
u_long blksize;
|
||||||
int error, interrupted;
|
int error, interrupted;
|
||||||
bool cantseek, readzeros, eof, lastblock, holetoeof;
|
bool cantseek, readzeros, eof, lastblock, holetoeof, sparse;
|
||||||
ssize_t aresid, r = 0;
|
ssize_t aresid, r = 0;
|
||||||
size_t copylen, len, savlen;
|
size_t copylen, len, savlen;
|
||||||
off_t insize, outsize;
|
off_t outsize;
|
||||||
char *dat;
|
char *dat;
|
||||||
long holein, holeout;
|
long holein, holeout;
|
||||||
struct timespec curts, endts;
|
struct timespec curts, endts;
|
||||||
|
@ -3357,11 +3358,26 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||||
goto out;
|
goto out;
|
||||||
if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &holein) != 0)
|
if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &holein) != 0)
|
||||||
holein = 0;
|
holein = 0;
|
||||||
error = vn_getsize_locked(invp, &insize, incred);
|
error = VOP_GETATTR(invp, &inva, incred);
|
||||||
|
if (error == 0 && inva.va_size > OFF_MAX)
|
||||||
|
error = EFBIG;
|
||||||
VOP_UNLOCK(invp);
|
VOP_UNLOCK(invp);
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use va_bytes >= va_size as a hint that the file does not have
|
||||||
|
* sufficient holes to justify the overhead of doing FIOSEEKHOLE.
|
||||||
|
* This hint does not work well for file systems doing compression
|
||||||
|
* and may fail when allocations for extended attributes increases
|
||||||
|
* the value of va_bytes to >= va_size.
|
||||||
|
*/
|
||||||
|
sparse = true;
|
||||||
|
if (holein != 0 && inva.va_bytes >= inva.va_size) {
|
||||||
|
holein = 0;
|
||||||
|
sparse = false;
|
||||||
|
}
|
||||||
|
|
||||||
mp = NULL;
|
mp = NULL;
|
||||||
error = vn_start_write(outvp, &mp, V_WAIT);
|
error = vn_start_write(outvp, &mp, V_WAIT);
|
||||||
if (error == 0)
|
if (error == 0)
|
||||||
|
@ -3395,9 +3411,9 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||||
error = vn_getsize_locked(outvp, &outsize, outcred);
|
error = vn_getsize_locked(outvp, &outsize, outcred);
|
||||||
if (error == 0 && outsize > *outoffp &&
|
if (error == 0 && outsize > *outoffp &&
|
||||||
*outoffp <= OFF_MAX - len && outsize <= *outoffp + len &&
|
*outoffp <= OFF_MAX - len && outsize <= *outoffp + len &&
|
||||||
*inoffp < insize &&
|
*inoffp < inva.va_size &&
|
||||||
*outoffp <= OFF_MAX - (insize - *inoffp) &&
|
*outoffp <= OFF_MAX - (inva.va_size - *inoffp) &&
|
||||||
outsize <= *outoffp + (insize - *inoffp)) {
|
outsize <= *outoffp + (inva.va_size - *inoffp)) {
|
||||||
#ifdef MAC
|
#ifdef MAC
|
||||||
error = mac_vnode_check_write(curthread->td_ucred,
|
error = mac_vnode_check_write(curthread->td_ucred,
|
||||||
outcred, outvp);
|
outcred, outvp);
|
||||||
|
@ -3415,7 +3431,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
if (holein == 0 && holeout > 0) {
|
if (sparse && holein == 0 && holeout > 0) {
|
||||||
/*
|
/*
|
||||||
* For this special case, the input data will be scanned
|
* For this special case, the input data will be scanned
|
||||||
* for blocks of all 0 bytes. For these blocks, the
|
* for blocks of all 0 bytes. For these blocks, the
|
||||||
|
@ -3486,7 +3502,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||||
error = VOP_IOCTL(invp, FIOSEEKDATA, &startoff, 0,
|
error = VOP_IOCTL(invp, FIOSEEKDATA, &startoff, 0,
|
||||||
incred, curthread);
|
incred, curthread);
|
||||||
if (error == ENXIO) {
|
if (error == ENXIO) {
|
||||||
startoff = endoff = insize;
|
startoff = endoff = inva.va_size;
|
||||||
eof = holetoeof = true;
|
eof = holetoeof = true;
|
||||||
error = 0;
|
error = 0;
|
||||||
}
|
}
|
||||||
|
@ -3549,6 +3565,8 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
||||||
cantseek = false;
|
cantseek = false;
|
||||||
} else {
|
} else {
|
||||||
cantseek = true;
|
cantseek = true;
|
||||||
|
if (!sparse)
|
||||||
|
cantseek = false;
|
||||||
startoff = *inoffp;
|
startoff = *inoffp;
|
||||||
copylen = len;
|
copylen = len;
|
||||||
error = 0;
|
error = 0;
|
||||||
|
|
Loading…
Reference in a new issue