mirror of
https://github.com/freebsd/freebsd-src
synced 2024-07-22 10:48:02 +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 ucred *incred, struct ucred *outcred, struct thread *fsize_td)
|
||||
{
|
||||
struct vattr inva;
|
||||
struct mount *mp;
|
||||
off_t startoff, endoff, xfer, xfer2;
|
||||
u_long blksize;
|
||||
int error, interrupted;
|
||||
bool cantseek, readzeros, eof, lastblock, holetoeof;
|
||||
bool cantseek, readzeros, eof, lastblock, holetoeof, sparse;
|
||||
ssize_t aresid, r = 0;
|
||||
size_t copylen, len, savlen;
|
||||
off_t insize, outsize;
|
||||
off_t outsize;
|
||||
char *dat;
|
||||
long holein, holeout;
|
||||
struct timespec curts, endts;
|
||||
|
@ -3357,11 +3358,26 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
|||
goto out;
|
||||
if (VOP_PATHCONF(invp, _PC_MIN_HOLE_SIZE, &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);
|
||||
if (error != 0)
|
||||
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;
|
||||
error = vn_start_write(outvp, &mp, V_WAIT);
|
||||
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);
|
||||
if (error == 0 && outsize > *outoffp &&
|
||||
*outoffp <= OFF_MAX - len && outsize <= *outoffp + len &&
|
||||
*inoffp < insize &&
|
||||
*outoffp <= OFF_MAX - (insize - *inoffp) &&
|
||||
outsize <= *outoffp + (insize - *inoffp)) {
|
||||
*inoffp < inva.va_size &&
|
||||
*outoffp <= OFF_MAX - (inva.va_size - *inoffp) &&
|
||||
outsize <= *outoffp + (inva.va_size - *inoffp)) {
|
||||
#ifdef MAC
|
||||
error = mac_vnode_check_write(curthread->td_ucred,
|
||||
outcred, outvp);
|
||||
|
@ -3415,7 +3431,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
|||
if (error != 0)
|
||||
goto out;
|
||||
|
||||
if (holein == 0 && holeout > 0) {
|
||||
if (sparse && holein == 0 && holeout > 0) {
|
||||
/*
|
||||
* For this special case, the input data will be scanned
|
||||
* 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,
|
||||
incred, curthread);
|
||||
if (error == ENXIO) {
|
||||
startoff = endoff = insize;
|
||||
startoff = endoff = inva.va_size;
|
||||
eof = holetoeof = true;
|
||||
error = 0;
|
||||
}
|
||||
|
@ -3549,6 +3565,8 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
|
|||
cantseek = false;
|
||||
} else {
|
||||
cantseek = true;
|
||||
if (!sparse)
|
||||
cantseek = false;
|
||||
startoff = *inoffp;
|
||||
copylen = len;
|
||||
error = 0;
|
||||
|
|
Loading…
Reference in a new issue