1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
|
/* Copyright (C) CZ.NIC, z.s.p.o. <knot-resolver@labs.nic.cz>
* SPDX-License-Identifier: GPL-3.0-or-later
*/
/** \file
*
* Utils for sharing memory among multiple processes via mmap() on a file.
*/
#pragma once
#include "lib/defines.h"
// All functions here return combination of the following flags or kr_error(...).
enum mmapped_state {
MMAPPED_PENDING = 1, // write lock acquired, (re)initialize and call finish
MMAPPED_EXISTING = 2, // using existing data, check consistency
};
struct mmapped {
void *mem;
size_t size;
int fd;
bool write_lock;
bool persistent;
};
/* Initialize/Use file data as mmapped memory.
*
* If write flock can be acquired and persistency is not requested, the file is resized, zeroed and mmapped,
* header is copied at its beginning and MMAPPED_PENDING is returned;
* you should finish initialization and call mmapped_init_finish to degrade flock to shared.
*
* Otherwise, it either acquires write flock or waits for shared flock,
* calls mmap, verifies that header is byte-wise identical
* and returns MMAPPED_EXISTING, possibly ORed with MMAPPED_PENDING based on the lock type.
*
* On header or size mismatch, either the outcome is the same as in the first case (if write flock was acquired),
* or kr_error(ENOTRECOVERABLE) is returned;
* on a system error, kr_error(errno) is returned.
*
* If size is set to zero, only using existing data is allowed. */
KR_EXPORT
int mmapped_init(struct mmapped *mmapped, const char *mmap_file, size_t size, void *header, size_t header_size, bool persistent);
/* Reinitialize mmapped data (incl. size) as in the first case of mmapped_init.
*
* To be called if existing mmapped file data cannot be used and we still own write flock
* (i.e. MMAPPED_PENDING flag was returned from the last mmapped_ call).
* Possible return values are the same as in mmapped_init.
*
* If MMAPPED_PENDING was not set, kr_error(ENOTRECOVERABLE) is returned. */
int mmapped_init_reset(struct mmapped *mmapped, const char *mmap_file, size_t size, void *header, size_t header_size);
/* Degrade flock to shared after getting MMAPPED_PENDING; void if MMAPPED_PENDING wasn't set.
*
* Returns zero on success and kr_error(errno) on system error. */
KR_EXPORT
int mmapped_init_finish(struct mmapped *mmapped);
/* Free mmapped memory and,
* unless the underlying file is used by other processes or persistence is requested, truncate it to zero size. */
KR_EXPORT
void mmapped_deinit(struct mmapped *mmapped);
/* Detailed description of init return states:
mmapped_init:
PENDING: (size > 0 only)
new memory initialized, header copied there; first process with exclusive lock
finish initialization and call _init_finish
EXISTING | PENDING: (persistent case only)
using existing data with equal header; first process with exclusive lock
possibly perform other checks / modifications and call _init_finish
or call _init_reset
EXISTING:
using existing data with equal header; shared lock
success; no further action required (but _init_finish possible)
kr_error(ENOTRECOVERABLE):
file header is not valid and
exclusive lock cannot be acquired or
reset is not allowed (size == 0)
system error (<0)
mmapped_init_reset:
PENDING:
new memory initialized, header copied; still exclusive lock
kr_error(ENOTRECOVERABLE):
exclusive lock not acquired or
reset is not allowed (size == 0)
system error (<0)
mmapped_init_finish:
0:
lock degraded to shared (or was already)
success; no further action required
system error (<0)
*/
/* Example usage, based on state flags returned by above functions:
persistent case:
mmapped_init
if (>=0 && EXISTING) {
if (!valid) {
mmapped_init_reset // -> continue with init/fail below
} else {
mmapped_init_finish // required only if PENDING
}
}
if (>=0 && !EXISTING && PENDING) { // == PENDING
... // init
mmapped_init_finish
}
if (<0) fail + return
assertion (==0) // if both _finish above were used
// done
non-persistent case:
mmapped_init
if (>=0 && EXISTING) { // == EXISTING
if (!valid) fail + return
mmapped_init_finish // not required
} else if (>=0 && PENDING) { // == PENDING
... // init
mmapped_init_finish
}
if (<0) fail + return
assertion (==0) // no other outcome if both _finish above were used
// done
*/
|