Make vn_generic_copy_file_range() interruptible via a signal.

Without this patch, when vn_generic_copy_file_range() is
doing a large copy, it will remain in the function for a
considerable amount of time, delaying handling of any
outstanding signals until the copy completes.

This patch adds checks for signals that need to be
processed after each successful data copy cycle.
When sig_intr() returns non-zero, vn_generic_copy_file_range()
will return.
The check "if (len < savlen)" ensures that some data
has been copied, so that progress will be made.

Note that, since copy_file_range(2) is allowed to
return fewer bytes copied than requested, it
will never return EINTR/ERESTART when sig_intr()
returns non-zero.

Reviewed by:	kib, asomers
Differential Revision:	https://reviews.freebsd.org/D26620
This commit is contained in:
Rick Macklem 2020-10-09 01:04:28 +00:00
parent a21d9b36a1
commit 19fe23fa2b

View File

@ -3017,7 +3017,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
struct uio io;
off_t startoff, endoff, xfer, xfer2;
u_long blksize;
int error;
int error, interrupted;
bool cantseek, readzeros, eof, lastblock;
ssize_t aresid;
size_t copylen, len, rem, savlen;
@ -3027,6 +3027,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
holein = holeout = 0;
savlen = len = *lenp;
error = 0;
interrupted = 0;
dat = NULL;
error = vn_lock(invp, LK_SHARED);
@ -3116,7 +3117,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
* support holes on the server, but do not support FIOSEEKHOLE.
*/
eof = false;
while (len > 0 && error == 0 && !eof) {
while (len > 0 && error == 0 && !eof && interrupted == 0) {
endoff = 0; /* To shut up compilers. */
cantseek = true;
startoff = *inoffp;
@ -3177,6 +3178,8 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
*inoffp += xfer;
*outoffp += xfer;
len -= xfer;
if (len < savlen)
interrupted = sig_intr();
}
}
copylen = MIN(len, endoff - startoff);
@ -3198,7 +3201,7 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
xfer -= (*inoffp % blksize);
}
/* Loop copying the data block. */
while (copylen > 0 && error == 0 && !eof) {
while (copylen > 0 && error == 0 && !eof && interrupted == 0) {
if (copylen < xfer)
xfer = copylen;
error = vn_lock(invp, LK_SHARED);
@ -3239,6 +3242,8 @@ vn_generic_copy_file_range(struct vnode *invp, off_t *inoffp,
*outoffp += xfer;
copylen -= xfer;
len -= xfer;
if (len < savlen)
interrupted = sig_intr();
}
}
xfer = blksize;