|
| 1 | +//===-- vDSO based RNG ----------------------------------------------------===// |
| 2 | +// |
| 3 | +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | +// See https://llvm.org/LICENSE.txt for license information. |
| 5 | +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | +// |
| 7 | +//===----------------------------------------------------------------------===// |
| 8 | + |
| 9 | +#ifndef LIBC_SRC_STDLIB_LINUX_VSDO_RNG_H |
| 10 | +#define LIBC_SRC_STDLIB_LINUX_VSDO_RNG_H |
| 11 | + |
| 12 | +#include "src/__support/CPP/bit.h" |
| 13 | +#include "src/__support/CPP/mutex.h" |
| 14 | +#include "src/__support/OSUtil/linux/vdso.h" |
| 15 | +#include "src/__support/OSUtil/syscall.h" |
| 16 | +#include "src/__support/blockstore.h" |
| 17 | +#include "src/__support/common.h" |
| 18 | +#include "src/__support/macros/config.h" |
| 19 | +#include "src/__support/mpmc_stack.h" |
| 20 | +#include "src/__support/threads/callonce.h" |
| 21 | +#include "src/__support/threads/linux/raw_mutex.h" |
| 22 | +#include "src/sys/auxv/getauxval.h" |
| 23 | + |
| 24 | +namespace LIBC_NAMESPACE_DECL { |
| 25 | +namespace vsdo_rng { |
| 26 | +class GlobalState { |
| 27 | +public: |
| 28 | + struct VGetrandomOpaqueParams { |
| 29 | + unsigned int size_of_opaque_states; |
| 30 | + unsigned int mmap_prot; |
| 31 | + unsigned int mmap_flags; |
| 32 | + unsigned int reserved[13]; |
| 33 | + }; |
| 34 | + |
| 35 | +private: |
| 36 | + struct Config { |
| 37 | + size_t page_size; |
| 38 | + size_t pages_per_alloc; |
| 39 | + size_t states_per_page; |
| 40 | + vdso::VDSOSymType<vdso::VDSOSym::GetRandom> getrandom; |
| 41 | + VGetrandomOpaqueParams params; |
| 42 | + }; |
| 43 | + |
| 44 | + // A lock-free stack of free opaque states. |
| 45 | + MPMCStack<void *> free_list{}; |
| 46 | + // A mutex protecting the allocation of new pages. |
| 47 | + RawMutex allocation_mutex{}; |
| 48 | + // A block store of allocated pages. |
| 49 | + BlockStore<void *, 16> allocations{}; |
| 50 | + |
| 51 | + // Shared global configuration. |
| 52 | + static CallOnceFlag config_flag; |
| 53 | + static Config config; |
| 54 | + |
| 55 | + // We grow the states by the number of CPUs. This function uses |
| 56 | + // SYS_sched_getaffinity to get the number of CPUs. |
| 57 | + LIBC_INLINE static size_t cpu_count(); |
| 58 | + |
| 59 | + // Grow available states. This function can fail if the system is out of |
| 60 | + // memory. |
| 61 | + LIBC_INLINE bool grow(); |
| 62 | + |
| 63 | +public: |
| 64 | + LIBC_INLINE constexpr GlobalState() {} |
| 65 | + LIBC_INLINE static Config &get_config(); |
| 66 | + LIBC_INLINE ~GlobalState() {} |
| 67 | +}; |
| 68 | + |
| 69 | +class LocalState {}; |
| 70 | + |
| 71 | +LIBC_INLINE_VAR GlobalState::Config GlobalState::config{}; |
| 72 | +LIBC_INLINE_VAR CallOnceFlag GlobalState::config_flag = 0; |
| 73 | + |
| 74 | +LIBC_INLINE size_t GlobalState::cpu_count() { |
| 75 | + char cpu_set[128] = {0}; |
| 76 | + int res = LIBC_NAMESPACE::syscall_impl<int>(SYS_sched_getaffinity, 0, |
| 77 | + sizeof(cpu_set), cpu_set); |
| 78 | + if (res <= 0) |
| 79 | + return 1; |
| 80 | + |
| 81 | + size_t count = 0; |
| 82 | + for (size_t i = 0; i < sizeof(cpu_set) / sizeof(unsigned long); ++i) { |
| 83 | + unsigned long *mask_ptr = reinterpret_cast<unsigned long *>(cpu_set); |
| 84 | + count += LIBC_NAMESPACE::cpp::popcount(mask_ptr[i]); |
| 85 | + } |
| 86 | + |
| 87 | + return count > 0 ? count : 1; |
| 88 | +} |
| 89 | + |
| 90 | +LIBC_INLINE GlobalState::Config &GlobalState::get_config() { |
| 91 | + callonce(&config_flag, []() { |
| 92 | + config.getrandom = |
| 93 | + LIBC_NAMESPACE::vdso::TypedSymbol<vdso::VDSOSym::GetRandom>{}; |
| 94 | + if (!config.getrandom) |
| 95 | + return; |
| 96 | + |
| 97 | + // Call with special flag to get the desired configuration. |
| 98 | + int res = config.getrandom( |
| 99 | + /*buf=*/nullptr, /*count=*/0, /*flags=*/0, |
| 100 | + /*opaque_states=*/&config.params, |
| 101 | + /*size_of_opaque_states=*/~0); |
| 102 | + if (res != 0) |
| 103 | + return; |
| 104 | + |
| 105 | + config.page_size = LIBC_NAMESPACE::getauxval(AT_PAGESZ); |
| 106 | + if (!config.page_size) |
| 107 | + return; |
| 108 | + |
| 109 | + size_t count = cpu_count(); |
| 110 | + |
| 111 | + config.states_per_page = |
| 112 | + config.page_size / config.params.size_of_opaque_states; |
| 113 | + |
| 114 | + config.pages_per_alloc = |
| 115 | + count / config.states_per_page + (count % config.states_per_page != 0); |
| 116 | + }); |
| 117 | + return config; |
| 118 | +} |
| 119 | + |
| 120 | +LIBC_INLINE bool GlobalState::grow() { |
| 121 | + // reserve a slot for the new page. |
| 122 | + if (!allocations.push_back(nullptr)) |
| 123 | + return false; |
| 124 | +} |
| 125 | + |
| 126 | +} // namespace vsdo_rng |
| 127 | +} // namespace LIBC_NAMESPACE_DECL |
| 128 | + |
| 129 | +#endif // LIBC_SRC_STDLIB_LINUX_VSDO_RNG_H |
0 commit comments