diff options
author | Vladimír Čunát <vladimir.cunat@nic.cz> | 2024-11-04 15:54:48 +0100 |
---|---|---|
committer | Vladimír Čunát <vladimir.cunat@nic.cz> | 2024-11-04 16:08:02 +0100 |
commit | 19c4c0b59666554f8ef072aac0699bde9069184c (patch) | |
tree | 9a4506b0b7a023cf4f46681bf3ebc5f46151562c /lib | |
parent | daemon/mmapped: use static_assert on undefined padding (diff) | |
download | knot-resolver-19c4c0b59666554f8ef072aac0699bde9069184c.tar.xz knot-resolver-19c4c0b59666554f8ef072aac0699bde9069184c.zip |
mmapped_* nit: move from daemon/ to lib/
We'll utilize this for cache, so this will be an easy way for GC
to access the mmapped_* symbols.
In lib/ we usually prefix symbols by kr_ but I don't think it's worth
the hassle in this case, as mmapped_ seems like a good enough prefix.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/meson.build | 2 | ||||
-rw-r--r-- | lib/mmapped.c | 113 | ||||
-rw-r--r-- | lib/mmapped.h | 33 |
3 files changed, 148 insertions, 0 deletions
diff --git a/lib/meson.build b/lib/meson.build index e369a452..94bb80c7 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -25,6 +25,7 @@ libkres_src = files([ 'layer/iterate.c', 'layer/validate.c', 'log.c', + 'mmapped.c', 'proto.c', 'rules/api.c', 'rules/defaults.c', @@ -63,6 +64,7 @@ libkres_headers = files([ 'layer.h', 'layer/iterate.h', 'log.h', + 'mmapped.h', 'module.h', 'proto.h', 'resolve.h', diff --git a/lib/mmapped.c b/lib/mmapped.c new file mode 100644 index 00000000..f37748b7 --- /dev/null +++ b/lib/mmapped.c @@ -0,0 +1,113 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include <fcntl.h> +#include <sys/mman.h> +#include <errno.h> +#include <string.h> + +#include "lib/mmapped.h" +#include "lib/utils.h" + +static inline bool fcntl_flock_whole(int fd, short int type, bool wait) +{ + struct flock fl = { + .l_type = type, // F_WRLCK, F_RDLCK, F_UNLCK + .l_whence = SEEK_SET, + .l_start = 0, + .l_len = 0 }; + return fcntl(fd, (wait ? F_SETLKW : F_SETLK), &fl) != -1; +} + +int mmapped_init(struct mmapped *mmapped, const char *mmap_file, size_t size, void *header, size_t header_size) +{ + int ret = 0; + int fd = mmapped->fd = open(mmap_file, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); + if (fd == -1) { + ret = kr_error(errno); + kr_log_crit(SYSTEM, "Cannot open file %s with shared data: %s\n", + mmap_file, strerror(errno)); + goto fail; + } + + // try to acquire write lock; copy header on success + if (fcntl_flock_whole(fd, F_WRLCK, false)) { + if (ftruncate(fd, 0) == -1 || ftruncate(fd, size) == -1) { // get all zeroed + ret = kr_error(errno); + kr_log_crit(SYSTEM, "Cannot change size of file %s containing shared data: %s\n", + mmap_file, strerror(errno)); + goto fail; + } + mmapped->mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mmapped->mem == MAP_FAILED) goto fail_errno; + + memcpy(mmapped->mem, header, header_size); + + return MMAPPED_WAS_FIRST; + } + + // wait for acquiring shared lock; check header on success + if (!fcntl_flock_whole(fd, F_RDLCK, true)) goto fail_errno; + + struct stat s; + bool succ = (fstat(fd, &s) == 0); + if (!succ) goto fail_errno; + if (s.st_size != size) goto fail_header_mismatch; + + mmapped->mem = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mmapped->mem == MAP_FAILED) goto fail_errno; + if (memcmp(mmapped->mem, header, header_size) != 0) { + munmap(mmapped->mem, size); + goto fail_header_mismatch; + } + + return 0; + + +fail_header_mismatch: + kr_log_crit(SYSTEM, "Another instance of kresd uses file %s with different configuration.", mmap_file); + errno = ENOTRECOVERABLE; + +fail_errno: + ret = kr_error(errno); + +fail: + if (fd >= 0) { + fcntl_flock_whole(fd, F_UNLCK, false); + close(fd); + } + mmapped->mem = NULL; + return ret; +} + +int mmapped_init_continue(struct mmapped *mmapped) +{ + if (!fcntl_flock_whole(mmapped->fd, F_RDLCK, false)) return kr_error(errno); + return 0; +} + +void mmapped_deinit(struct mmapped *mmapped) +{ + if (mmapped->mem == NULL) return; + int fd = mmapped->fd; + + munmap(mmapped->mem, mmapped->size); + mmapped->mem = NULL; + + fcntl_flock_whole(fd, F_UNLCK, false); + + // remove file data unless it is still locked by other processes + if (fcntl_flock_whole(fd, F_WRLCK, false)) { + + /* If the configuration is updated at runtime, manager may remove the file + * and the new processes create it again while old processes are still using the old data. + * Here we keep zero-size file not to accidentally remove the new file instead of the old one. + * Still truncating the file will cause currently starting processes waiting for read lock on the same file to fail, + * but such processes are not expected to exist. */ + ftruncate(fd, 0); + + fcntl_flock_whole(fd, F_UNLCK, false); + } + close(fd); +} diff --git a/lib/mmapped.h b/lib/mmapped.h new file mode 100644 index 00000000..eb542096 --- /dev/null +++ b/lib/mmapped.h @@ -0,0 +1,33 @@ +/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz> + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include "lib/defines.h" + +#define MMAPPED_WAS_FIRST 1 + +struct mmapped { + void *mem; + size_t size; + int fd; +}; + +/* Initialize/Use file data as mmapped memory. + * + * If write flock can be acquired, the file is resized, zeroed and mmapped, + * header is copied at its beginning and MMAPPED_WAS_FIRST is returned; + * you should finish initialization and call mmapped_init_continue to degrade flock to shared. + * Otherwise, it waits for shared flock, calls mmap, verifies that header is byte-wise identical and returns zero. + * On header mismatch, kr_error(ENOTRECOVERABLE) is returned; on a system error, kr_error(errno) is returned. */ +KR_EXPORT +int mmapped_init(struct mmapped *mmapped, const char *mmap_file, size_t size, void *header, size_t header_size); + +/* Degrade flock to shared after getting MMAPPED_WAS_FIRST from mmapped_init. + * + * Returns zero on success and kr_error(errno) on system error. */ +KR_EXPORT +int mmapped_init_continue(struct mmapped *mmapped); + +/* Free mmapped memory and, unless the underlying file is used by other processes, truncate it to zero size. */ +KR_EXPORT +void mmapped_deinit(struct mmapped *mmapped); |