From 196e18db462e56415965762a6bbd7217aeea792b Mon Sep 17 00:00:00 2001 From: Pantelis Antoniou Date: Fri, 22 Sep 2023 17:58:08 +0300 Subject: [PATCH] linux file backing store probing Signed-off-by: Pantelis Antoniou --- src/blake3/blake3_host_state.c | 122 +++++++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 4 deletions(-) diff --git a/src/blake3/blake3_host_state.c b/src/blake3/blake3_host_state.c index 4f83666e..cf16166f 100644 --- a/src/blake3/blake3_host_state.c +++ b/src/blake3/blake3_host_state.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "blake3.h" #include "blake3_impl.h" @@ -222,6 +223,109 @@ void blake3_hasher_destroy(blake3_hasher *self) free(self); } +#if defined(__linux__) + +static int linux_block_dev_is_rotational(dev_t dev) +{ + char *path = NULL; + int rc, fd; + uint8_t c[2]; + int rotational; + + /* by default it's the filesize */ + rotational = -1; /* not found */ + + /* read the rotational attribute of the root */ + rc = asprintf(&path, "/sys/dev/block/%u:%u/queue/rotational", major(dev), 0); + if (rc != -1) { + fd = open(path, O_RDONLY); + if (fd != -1) { + if (read(fd, c, 2) == 2) + rotational = c[0] == '1' && c[1] == '\n'; + close(fd); + } + free(path); + } + + return rotational; +} + +static ssize_t linux_file_cached_size(int fd, void *mem, size_t filesize) +{ + long pagesize = sysconf(_SC_PAGESIZE); + size_t vec_size, resident_size, i; + unsigned char *vec; + int rc; + + (void)rc; + vec_size = (filesize + pagesize - 1) / pagesize; + vec = malloc(vec_size + 1); + if (!vec) + return -1; + + rc = mincore(mem, filesize, vec); + assert(!rc); + + /* this could be parallelized */ + resident_size = 0; + for (i = 0; i < vec_size; i++) { + if (vec[i] & 1) + resident_size += pagesize; + } + + free(vec); + + if (resident_size > filesize) + resident_size = filesize; + + return (ssize_t)resident_size; +} + +#define BLAKE3_MMAP_MIN_CHUNKSIZE (1U << 20) // minimum chunksize is 1MB + +static size_t blake3_mmap_file_chunksize(int fd, dev_t dev, void *mem, size_t filesize) +{ + size_t chunksize; + ssize_t cached_size; + int rotational; + + /* if the file is small enough don't bother with the rest */ + if (filesize <= BLAKE3_MMAP_MIN_CHUNKSIZE) + return BLAKE3_MMAP_MIN_CHUNKSIZE; + + /* by default it's the filesize */ + chunksize = filesize; + + /* starting, check if the rotational attribute exists in this dev */ + rotational = linux_block_dev_is_rotational(dev); + if (rotational == -1) { + /* attribute not found? check the non-partition */ + rotational = linux_block_dev_is_rotational(makedev(major(dev), 0)); + } + + if (rotational == 1) { + /* OK, it's rotational, but is it in cache? + * to avoid checking for the cached status of the whole file + * we just probe the MIN_CHUNKSIZE + * We will thrash in the case where the file is only cached + * for the first few bytes, but this is generally unusual. + */ + cached_size = linux_file_cached_size(fd, mem, BLAKE3_MMAP_MIN_CHUNKSIZE); + + if (cached_size <= 0) + chunksize = BLAKE3_MMAP_MIN_CHUNKSIZE; + } + + return chunksize; +} +#else +static size_t blake3_mmap_file_chunksize(int fd, dev_t dev, void *mem, size_t filesize) +{ + /* for all others, just use mmap at max */ + return filesize; +} +#endif + /* 256K threshold for using alloca */ #define BLAKE3_ALLOCA_BUFFER_SIZE (256U << 10) @@ -230,11 +334,11 @@ int blake3_hash_file(blake3_hasher *hasher, const char *filename, { blake3_host_state *hs; FILE *fp = NULL; - void *mem = NULL; - void *buf = NULL; + void *mem = NULL, *buf = NULL, *p; int fd = -1, ret = -1; - size_t rdn, filesize, bufsz = 0; + size_t rdn, filesize, bufsz = 0, max_chunk, left, chunk; struct stat sb; + dev_t dev = 0; int rc; if (!hasher || !filename || !output) @@ -278,6 +382,7 @@ int blake3_hash_file(blake3_hasher *hasher, const char *filename, if (mem != MAP_FAILED) { close(fd); fd = -1; + dev = sb.st_dev; } else mem = NULL; } @@ -296,7 +401,16 @@ int blake3_hash_file(blake3_hasher *hasher, const char *filename, /* mmap case, very simple */ if (mem) { - blake3_hasher_update(hasher, mem, filesize); + max_chunk = blake3_mmap_file_chunksize(fd, dev, mem, filesize); + + p = mem; + left = filesize; + while (left > 0) { + chunk = left > max_chunk ? max_chunk : left; + blake3_hasher_update(hasher, p, chunk); + p += chunk; + left -= chunk; + } } else { /* slow path using file reads */