fallocate: dig holes only in data extents
Based on patch from Vaclav Dolezal <vdolezal@redhat.com>, this implementation is less invasive. The patch adds a new while() for pread() call (so diff is mostly code indention). The pread() is called for a real data only (addressed by 'off' and 'end') and we use SEEK_{DATA,HOLE} before the pread() to skip already existing holes. The variables 'file_off' and 'file_end' addresses area in the file as specified on fallocate command line. Test: $ truncate -s 10G testfile $ dd if=/dev/zero of=testfile count=10 bs=1M conv=notrunc old version: $ time /usr/bin/fallocate --dig-holes --verbose testfile testfile: 10 GiB (10737418240 bytes) converted to sparse holes. real 0m3.013s user 0m0.700s sys 0m2.304s new version: $ time ./fallocate --dig-holes --verbose testfile testfile: 10 MiB (10485760 bytes) converted to sparse holes. real 0m0.026s user 0m0.002s sys 0m0.004s The old version scans all file. The change has minimal overhead for files without holes. Addresses: https://github.com/karelzak/util-linux/issues/421 Co-Author: Vaclav Dolezal <vdolezal@redhat.com> Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
f4b03edb73
commit
173ef88237
|
@ -147,25 +147,6 @@ static void xposix_fallocate(int fd, off_t offset, off_t length)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static int skip_hole(int fd, off_t *off)
|
|
||||||
{
|
|
||||||
off_t newoff;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
newoff = lseek(fd, *off, SEEK_DATA);
|
|
||||||
|
|
||||||
/* ENXIO means that there is no more data -- probably sparse hole at
|
|
||||||
* the end of the file */
|
|
||||||
if (newoff < 0 && errno == ENXIO)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
if (newoff > *off) {
|
|
||||||
*off = newoff;
|
|
||||||
return 0; /* success */
|
|
||||||
}
|
|
||||||
return -1; /* no hole */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The real buffer size has to be bufsize + sizeof(uintptr_t) */
|
/* The real buffer size has to be bufsize + sizeof(uintptr_t) */
|
||||||
static int is_nul(void *buf, size_t bufsize)
|
static int is_nul(void *buf, size_t bufsize)
|
||||||
{
|
{
|
||||||
|
@ -191,16 +172,16 @@ static int is_nul(void *buf, size_t bufsize)
|
||||||
return cbuf + bufsize < cp;
|
return cbuf + bufsize < cp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dig_holes(int fd, off_t off, off_t len)
|
static void dig_holes(int fd, off_t file_off, off_t len)
|
||||||
{
|
{
|
||||||
off_t end = len ? off + len : 0;
|
off_t file_end = len ? file_off + len : 0;
|
||||||
off_t hole_start = 0, hole_sz = 0;
|
off_t hole_start = 0, hole_sz = 0;
|
||||||
uintmax_t ct = 0;
|
uintmax_t ct = 0;
|
||||||
size_t bufsz;
|
size_t bufsz;
|
||||||
char *buf;
|
char *buf;
|
||||||
struct stat st;
|
struct stat st;
|
||||||
#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
|
#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
|
||||||
off_t cache_start = off;
|
off_t cache_start = file_off;
|
||||||
/*
|
/*
|
||||||
* We don't want to call POSIX_FADV_DONTNEED to discard cached
|
* We don't want to call POSIX_FADV_DONTNEED to discard cached
|
||||||
* data in PAGE_SIZE steps. IMHO it's overkill (too many syscalls).
|
* data in PAGE_SIZE steps. IMHO it's overkill (too many syscalls).
|
||||||
|
@ -217,60 +198,70 @@ static void dig_holes(int fd, off_t off, off_t len)
|
||||||
|
|
||||||
bufsz = st.st_blksize;
|
bufsz = st.st_blksize;
|
||||||
|
|
||||||
if (lseek(fd, off, SEEK_SET) < 0)
|
if (lseek(fd, file_off, SEEK_SET) < 0)
|
||||||
err(EXIT_FAILURE, _("seek on %s failed"), filename);
|
err(EXIT_FAILURE, _("seek on %s failed"), filename);
|
||||||
|
|
||||||
/* buffer + extra space for is_nul() sentinel */
|
/* buffer + extra space for is_nul() sentinel */
|
||||||
buf = xmalloc(bufsz + sizeof(uintptr_t));
|
buf = xmalloc(bufsz + sizeof(uintptr_t));
|
||||||
#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
|
while (file_end == 0 || file_off < file_end) {
|
||||||
posix_fadvise(fd, off, 0, POSIX_FADV_SEQUENTIAL);
|
/*
|
||||||
#endif
|
* Detect data area (skip exiting holes)
|
||||||
|
*/
|
||||||
|
off_t end, off;
|
||||||
|
|
||||||
while (end == 0 || off < end) {
|
off = lseek(fd, file_off, SEEK_DATA);
|
||||||
ssize_t rsz;
|
if ((off == -1 && errno == ENXIO) ||
|
||||||
|
(file_end && off >= file_end))
|
||||||
rsz = pread(fd, buf, bufsz, off);
|
|
||||||
if (rsz < 0 && errno)
|
|
||||||
err(EXIT_FAILURE, _("%s: read failed"), filename);
|
|
||||||
if (end && rsz > 0 && off > end - rsz)
|
|
||||||
rsz = end - off;
|
|
||||||
if (rsz <= 0)
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (is_nul(buf, rsz)) {
|
end = lseek(fd, off, SEEK_HOLE);
|
||||||
if (!hole_sz) { /* new hole detected */
|
if (file_end && end > file_end)
|
||||||
int rc = skip_hole(fd, &off);
|
end = file_end;
|
||||||
if (rc == 0)
|
|
||||||
continue; /* hole skipped */
|
#if defined(POSIX_FADV_SEQUENTIAL) && defined(HAVE_POSIX_FADVISE)
|
||||||
else if (rc == 1)
|
posix_fadvise(fd, off, end, POSIX_FADV_SEQUENTIAL);
|
||||||
break; /* end of file */
|
#endif
|
||||||
hole_start = off;
|
/*
|
||||||
|
* Dig holes in the area
|
||||||
|
*/
|
||||||
|
while (off < end) {
|
||||||
|
ssize_t rsz = pread(fd, buf, bufsz, off);
|
||||||
|
if (rsz < 0 && errno)
|
||||||
|
err(EXIT_FAILURE, _("%s: read failed"), filename);
|
||||||
|
if (end && rsz > 0 && off > end - rsz)
|
||||||
|
rsz = end - off;
|
||||||
|
if (rsz <= 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (is_nul(buf, rsz)) {
|
||||||
|
if (!hole_sz) /* new hole detected */
|
||||||
|
hole_start = off;
|
||||||
|
hole_sz += rsz;
|
||||||
|
} else if (hole_sz) {
|
||||||
|
xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
|
||||||
|
hole_start, hole_sz);
|
||||||
|
ct += hole_sz;
|
||||||
|
hole_sz = hole_start = 0;
|
||||||
}
|
}
|
||||||
hole_sz += rsz;
|
|
||||||
} else if (hole_sz) {
|
|
||||||
xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
|
|
||||||
hole_start, hole_sz);
|
|
||||||
ct += hole_sz;
|
|
||||||
hole_sz = hole_start = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE)
|
#if defined(POSIX_FADV_DONTNEED) && defined(HAVE_POSIX_FADVISE)
|
||||||
/* discard cached data */
|
/* discard cached data */
|
||||||
if (off - cache_start > (off_t) cachesz) {
|
if (off - cache_start > (off_t) cachesz) {
|
||||||
size_t clen = off - cache_start;
|
size_t clen = off - cache_start;
|
||||||
|
|
||||||
clen = (clen / cachesz) * cachesz;
|
clen = (clen / cachesz) * cachesz;
|
||||||
posix_fadvise(fd, cache_start, clen, POSIX_FADV_DONTNEED);
|
posix_fadvise(fd, cache_start, clen, POSIX_FADV_DONTNEED);
|
||||||
cache_start = cache_start + clen;
|
cache_start = cache_start + clen;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
off += rsz;
|
off += rsz;
|
||||||
}
|
}
|
||||||
|
if (hole_sz) {
|
||||||
if (hole_sz) {
|
xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
|
||||||
xfallocate(fd, FALLOC_FL_PUNCH_HOLE|FALLOC_FL_KEEP_SIZE,
|
hole_start, hole_sz);
|
||||||
hole_start, hole_sz);
|
ct += hole_sz;
|
||||||
ct += hole_sz;
|
}
|
||||||
|
file_off = off;
|
||||||
}
|
}
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
|
|
Loading…
Reference in New Issue