/* * UDS and VDO keep track of which threads are allowed to allocate memory freely, and which threads * must be careful to not do a memory allocation that does an I/O request. The 'allocating_threads' * thread_registry and its associated methods implement this tracking.
*/ staticstruct thread_registry allocating_threads;
/* * Register the current thread as an allocating thread. * * An optional flag location can be supplied indicating whether, at any given point in time, the * threads associated with that flag should be allocating storage. If the flag is false, a message * will be logged. * * If no flag is supplied, the thread is always allowed to allocate storage without complaint. * * @new_thread: registered_thread structure to use for the current thread * @flag_ptr: Location of the allocation-allowed flag
*/ void vdo_register_allocating_thread(struct registered_thread *new_thread, constbool *flag_ptr)
{ if (flag_ptr == NULL) { staticconstbool allocation_always_allowed = true;
/* Unregister the current thread as an allocating thread. */ void vdo_unregister_allocating_thread(void)
{
vdo_unregister_thread(&allocating_threads);
}
/* * We track how much memory has been allocated and freed. When we unload the module, we log an * error if we have not freed all the memory that we allocated. Nearly all memory allocation and * freeing is done using this module. * * We do not use kernel functions like the kvasprintf() method, which allocate memory indirectly * using kmalloc. * * These data structures and methods are used to track the amount of memory used.
*/
/* * We allocate very few large objects, and allocation/deallocation isn't done in a * performance-critical stage for us, so a linked list should be fine.
*/ struct vmalloc_block_info { void *ptr;
size_t size; struct vmalloc_block_info *next;
};
spin_unlock_irqrestore(&memory_stats.lock, flags); if (block != NULL)
vdo_free(block); else
vdo_log_info("attempting to remove ptr %px not found in vmalloc list", ptr);
}
/* * Determine whether allocating a memory block should use kmalloc or __vmalloc. * * vmalloc can allocate any integral number of pages. * * kmalloc can allocate any number of bytes up to a configured limit, which defaults to 8 megabytes * on some systems. kmalloc is especially good when memory is being both allocated and freed, and * it does this efficiently in a multi CPU environment. * * kmalloc usually rounds the size of the block up to the next power of two, so when the requested * block is bigger than PAGE_SIZE / 2 bytes, kmalloc will never give you less space than the * corresponding vmalloc allocation. Sometimes vmalloc will use less overhead than kmalloc. * * The advantages of kmalloc do not help out UDS or VDO, because we allocate all our memory up * front and do not free and reallocate it. Sometimes we have problems using kmalloc, because the * Linux memory page map can become so fragmented that kmalloc will not give us a 32KB chunk. We * have used vmalloc as a backup to kmalloc in the past, and a follow-up vmalloc of 32KB will work. * But there is no strong case to be made for using kmalloc over vmalloc for these size chunks. * * The kmalloc/vmalloc boundary is set at 4KB, and kmalloc gets the 4KB requests. There is no * strong reason for favoring either kmalloc or vmalloc for 4KB requests, except that tracking * vmalloc statistics uses a linked list implementation. Using a simple test, this choice of * boundary results in 132 vmalloc calls. Using vmalloc for requests of exactly 4KB results in an * additional 6374 vmalloc calls, which is much less efficient for tracking. * * @size: How many bytes to allocate
*/ staticinlinebool use_kmalloc(size_t size)
{ return size <= PAGE_SIZE;
}
/* * Allocate storage based on memory size and alignment, logging an error if the allocation fails. * The memory will be zeroed. * * @size: The size of an object * @align: The required alignment * @what: What is being allocated (for error logging) * @ptr: A pointer to hold the allocated memory * * Return: VDO_SUCCESS or an error code
*/ int vdo_allocate_memory(size_t size, size_t align, constchar *what, void *ptr)
{ /* * The __GFP_RETRY_MAYFAIL flag means the VM implementation will retry memory reclaim * procedures that have previously failed if there is some indication that progress has * been made elsewhere. It can wait for other tasks to attempt high level approaches to * freeing memory such as compaction (which removes fragmentation) and page-out. There is * still a definite limit to the number of retries, but it is a larger limit than with * __GFP_NORETRY. Allocations with this flag may fail, but only when there is genuinely * little unused memory. While these allocations do not directly trigger the OOM killer, * their failure indicates that the system is likely to need to use the OOM killer soon. * The caller must handle failure, but can reasonably do so by failing a higher-level * request, or completing it only in a much less efficient manner.
*/ const gfp_t gfp_flags = GFP_KERNEL | __GFP_ZERO | __GFP_RETRY_MAYFAIL; unsignedint noio_flags; bool allocations_restricted = !allocations_allowed(); unsignedlong start_time; void *p = NULL;
if (allocations_restricted)
noio_flags = memalloc_noio_save();
start_time = jiffies; if (use_kmalloc(size) && (align < PAGE_SIZE)) {
p = kmalloc(size, gfp_flags | __GFP_NOWARN); if (p == NULL) { /* * It is possible for kmalloc to fail to allocate memory because there is * no page available. A short sleep may allow the page reclaimer to * free a page.
*/
fsleep(1000);
p = kmalloc(size, gfp_flags);
}
if (vdo_allocate(1, struct vmalloc_block_info, __func__, &block) == VDO_SUCCESS) { /* * It is possible for __vmalloc to fail to allocate memory because there * are no pages available. A short sleep may allow the page reclaimer * to free enough pages for a small allocation. * * For larger allocations, the page_alloc code is racing against the page * reclaimer. If the page reclaimer can stay ahead of page_alloc, the * __vmalloc will succeed. But if page_alloc overtakes the page reclaimer, * the allocation fails. It is possible that more retries will succeed.
*/ for (;;) {
p = __vmalloc(size, gfp_flags | __GFP_NOWARN); if (p != NULL) break;
if (jiffies_to_msecs(jiffies - start_time) > 1000) { /* Try one more time, logging a failure for this call. */
p = __vmalloc(size, gfp_flags); break;
}
if (allocations_restricted)
memalloc_noio_restore(noio_flags);
if (unlikely(p == NULL)) {
vdo_log_error("Could not allocate %zu bytes for %s in %u msecs",
size, what, jiffies_to_msecs(jiffies - start_time)); return -ENOMEM;
}
*((void **) ptr) = p; return VDO_SUCCESS;
}
/* * Allocate storage based on memory size, failing immediately if the required memory is not * available. The memory will be zeroed. * * @size: The size of an object. * @what: What is being allocated (for error logging) * * Return: pointer to the allocated memory, or NULL if the required space is not available.
*/ void *vdo_allocate_memory_nowait(size_t size, constchar *what __maybe_unused)
{ void *p = kmalloc(size, GFP_NOWAIT | __GFP_ZERO);
/* * Reallocate dynamically allocated memory. There are no alignment guarantees for the reallocated * memory. If the new memory is larger than the old memory, the new space will be zeroed. * * @ptr: The memory to reallocate. * @old_size: The old size of the memory * @size: The new size to allocate * @what: What is being allocated (for error logging) * @new_ptr: A pointer to hold the reallocated pointer * * Return: VDO_SUCCESS or an error code
*/ int vdo_reallocate_memory(void *ptr, size_t old_size, size_t size, constchar *what, void *new_ptr)
{ int result;
void vdo_memory_exit(void)
{
VDO_ASSERT_LOG_ONLY(memory_stats.kmalloc_bytes == 0, "kmalloc memory used (%zd bytes in %zd blocks) is returned to the kernel",
memory_stats.kmalloc_bytes, memory_stats.kmalloc_blocks);
VDO_ASSERT_LOG_ONLY(memory_stats.vmalloc_bytes == 0, "vmalloc memory used (%zd bytes in %zd blocks) is returned to the kernel",
memory_stats.vmalloc_bytes, memory_stats.vmalloc_blocks);
vdo_log_debug("peak usage %zd bytes", memory_stats.peak_bytes);
}
/* * Report stats on any allocated memory that we're tracking. Not all allocation types are * guaranteed to be tracked in bytes (e.g., bios).
*/ void vdo_report_memory_usage(void)
{ unsignedlong flags;
u64 kmalloc_blocks;
u64 kmalloc_bytes;
u64 vmalloc_blocks;
u64 vmalloc_bytes;
u64 peak_usage;
u64 total_bytes;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.