Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F148442661
D21155.id60448.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
4 KB
Referenced Files
None
Subscribers
None
D21155.id60448.diff
View Options
Index: kern/vfs_vnops.c
===================================================================
--- kern/vfs_vnops.c
+++ kern/vfs_vnops.c
@@ -2713,18 +2713,87 @@
}
/*
+ * Look for a hole in the output file and, if found, adjust *outoffp
+ * and *xferp to skip past the hole.
+ * *xferp is the entire hole length to be written and xfer2 is how many bytes
+ * to be written as 0's upon return.
+ */
+static off_t
+vn_skip_hole(struct vnode *outvp, off_t xfer2, off_t *outoffp, off_t *xferp,
+ off_t *dataoffp, off_t *holeoffp, struct ucred *cred)
+{
+ int error;
+ off_t delta;
+
+ if (*holeoffp == 0 || *holeoffp <= *outoffp) {
+ *dataoffp = *outoffp;
+ error = VOP_IOCTL(outvp, FIOSEEKDATA, dataoffp, 0, cred,
+ curthread);
+ if (error == 0) {
+ *holeoffp = *dataoffp;
+ error = VOP_IOCTL(outvp, FIOSEEKHOLE, holeoffp, 0, cred,
+ curthread);
+ }
+ if (error != 0 || *holeoffp == *dataoffp) {
+ /*
+ * Since the vnode is not locked, I think it is
+ * possible for another thread to do a truncate() plus
+ * write() to create a hole at *dataoffp. For that
+ * case, *holeoffp == *dataoffp and the hole find
+ * has failed.
+ */
+ *holeoffp = -1; /* Disable use of vn_skip_hole(). */
+ return (xfer2);
+ }
+ KASSERT(*dataoffp >= *outoffp,
+ ("vn_skip_hole: dataoff=%jd < outoff=%jd",
+ (intmax_t)*dataoffp, (intmax_t)*outoffp));
+ KASSERT(*holeoffp > *dataoffp,
+ ("vn_skip_hole: holeoff=%jd <= dataoff=%jd",
+ (intmax_t)*holeoffp, (intmax_t)*dataoffp));
+ }
+
+ /*
+ * If there is a hole before the data starts, advance *outoffp and
+ * *xferp past the hole.
+ */
+ if (*dataoffp > *outoffp) {
+ delta = *dataoffp - *outoffp;
+ if (delta >= *xferp) {
+ /* Entire *xferp is a hole. */
+ *outoffp += *xferp;
+ *xferp = 0;
+ return (0);
+ }
+ *xferp -= delta;
+ *outoffp += delta;
+ xfer2 = MIN(xfer2, *xferp);
+ }
+
+ /*
+ * If a hole starts before the end of this xfer2, reduce this xfer2 so
+ * that the write ends at the start of the hole.
+ */
+ if (*holeoffp < *outoffp + xfer2)
+ xfer2 = *holeoffp - *outoffp;
+ return (xfer2);
+}
+
+/*
* Write an xfer sized chunk to outvp in blksize blocks from dat.
* dat is a maximum of blksize in length and can be written repeatedly in
* the chunk.
* If growfile == true, just grow the file via vn_truncate_locked() instead
* of doing actual writes.
+ * If checkhole == true, a hole is being punched, so skip over any hole
+ * already in the output file.
*/
static int
vn_write_outvp(struct vnode *outvp, char *dat, off_t outoff, off_t xfer,
- u_long blksize, bool growfile, struct ucred *cred)
+ u_long blksize, bool growfile, bool checkhole, struct ucred *cred)
{
struct mount *mp;
- off_t xfer2;
+ off_t dataoff, holeoff, xfer2;
int error, lckf;
/*
@@ -2733,7 +2802,24 @@
* done for each iteration, since the xfer argument can be very
* large if there is a large hole to punch in the output file.
*/
+ error = 0;
+ holeoff = 0;
do {
+ xfer2 = MIN(xfer, blksize);
+ if (checkhole) {
+ /*
+ * Punching a hole. Skip writing if there is
+ * already a hole in the output file.
+ */
+ xfer2 = vn_skip_hole(outvp, xfer2, &outoff, &xfer,
+ &dataoff, &holeoff, cred);
+ if (xfer == 0)
+ break;
+ if (holeoff < 0)
+ checkhole = false;
+ KASSERT(xfer2 > 0, ("vn_write_outvp: xfer2=%jd",
+ (intmax_t)xfer2));
+ }
bwillwrite();
mp = NULL;
error = vn_start_write(outvp, &mp, V_WAIT);
@@ -2749,7 +2835,6 @@
error = vn_truncate_locked(outvp, outoff + xfer,
false, cred);
else {
- xfer2 = MIN(xfer, blksize);
error = vn_rdwr(UIO_WRITE, outvp, dat, xfer2,
outoff, UIO_SYSSPACE, IO_NODELOCKED,
curthread->td_ucred, cred, NULL, curthread);
@@ -2907,7 +2992,7 @@
memset(dat, 0, MIN(xfer2, blksize));
error = vn_write_outvp(outvp, dat,
*outoffp, xfer2, blksize, false,
- outcred);
+ holeout > 0, outcred);
}
if (error == 0 && *outoffp + xfer >
@@ -2915,7 +3000,7 @@
/* Grow last block. */
error = vn_write_outvp(outvp, dat,
*outoffp, xfer, blksize, true,
- outcred);
+ false, outcred);
if (error == 0) {
*inoffp += xfer;
*outoffp += xfer;
@@ -2971,7 +3056,8 @@
error = vn_write_outvp(outvp, dat,
*outoffp, xfer, blksize,
readzeros && xfer == len &&
- *outoffp >= va.va_size, outcred);
+ *outoffp >= va.va_size, false,
+ outcred);
if (error == 0) {
*inoffp += xfer;
startoff += xfer;
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Wed, Mar 18, 10:15 PM (14 h, 39 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
29884997
Default Alt Text
D21155.id60448.diff (4 KB)
Attached To
Mode
D21155: fix copy_file_range(2) so that it doesn't allocate unneeded data blocks of all 0s in the output file
Attached
Detach File
Event Timeline
Log In to Comment