153a5a1b3Sopenharmony_ci/*** 253a5a1b3Sopenharmony_ci This file is part of PulseAudio. 353a5a1b3Sopenharmony_ci 453a5a1b3Sopenharmony_ci Copyright 2004-2006 Lennart Poettering 553a5a1b3Sopenharmony_ci Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB 653a5a1b3Sopenharmony_ci 753a5a1b3Sopenharmony_ci PulseAudio is free software; you can redistribute it and/or modify 853a5a1b3Sopenharmony_ci it under the terms of the GNU Lesser General Public License as 953a5a1b3Sopenharmony_ci published by the Free Software Foundation; either version 2.1 of the 1053a5a1b3Sopenharmony_ci License, or (at your option) any later version. 1153a5a1b3Sopenharmony_ci 1253a5a1b3Sopenharmony_ci PulseAudio is distributed in the hope that it will be useful, but 1353a5a1b3Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 1453a5a1b3Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 1553a5a1b3Sopenharmony_ci Lesser General Public License for more details 1653a5a1b3Sopenharmony_ci 1753a5a1b3Sopenharmony_ci You should have received a copy of the GNU Lesser General Public 1853a5a1b3Sopenharmony_ci License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 1953a5a1b3Sopenharmony_ci***/ 2053a5a1b3Sopenharmony_ci 2153a5a1b3Sopenharmony_ci#ifdef HAVE_CONFIG_H 2253a5a1b3Sopenharmony_ci#include <config.h> 2353a5a1b3Sopenharmony_ci#endif 2453a5a1b3Sopenharmony_ci 2553a5a1b3Sopenharmony_ci#ifndef LOG_TAG 2653a5a1b3Sopenharmony_ci#define LOG_TAG "Memblock" 2753a5a1b3Sopenharmony_ci#endif 2853a5a1b3Sopenharmony_ci 2953a5a1b3Sopenharmony_ci#include <stdio.h> 3053a5a1b3Sopenharmony_ci#include <stdlib.h> 3153a5a1b3Sopenharmony_ci#include <string.h> 3253a5a1b3Sopenharmony_ci#include <unistd.h> 3353a5a1b3Sopenharmony_ci#include <signal.h> 3453a5a1b3Sopenharmony_ci#include <errno.h> 3553a5a1b3Sopenharmony_ci 3653a5a1b3Sopenharmony_ci#ifdef HAVE_VALGRIND_MEMCHECK_H 3753a5a1b3Sopenharmony_ci#include <valgrind/memcheck.h> 3853a5a1b3Sopenharmony_ci#endif 3953a5a1b3Sopenharmony_ci 4053a5a1b3Sopenharmony_ci#include <pulse/xmalloc.h> 4153a5a1b3Sopenharmony_ci#include <pulse/def.h> 4253a5a1b3Sopenharmony_ci 4353a5a1b3Sopenharmony_ci#include <pulsecore/log.h> 4453a5a1b3Sopenharmony_ci#include <pulsecore/hashmap.h> 4553a5a1b3Sopenharmony_ci#include <pulsecore/semaphore.h> 4653a5a1b3Sopenharmony_ci#include <pulsecore/mutex.h> 4753a5a1b3Sopenharmony_ci#include <pulsecore/macro.h> 4853a5a1b3Sopenharmony_ci#include <pulsecore/llist.h> 4953a5a1b3Sopenharmony_ci#include <pulsecore/core-util.h> 5053a5a1b3Sopenharmony_ci 5153a5a1b3Sopenharmony_ci#include "memblock.h" 5253a5a1b3Sopenharmony_ci 5353a5a1b3Sopenharmony_ci/* We can allocate 64*1024*1024 bytes at maximum. That's 64MB. Please 5453a5a1b3Sopenharmony_ci * note that the footprint is usually much smaller, since the data is 5553a5a1b3Sopenharmony_ci * stored in SHM and our OS does not commit the memory before we use 5653a5a1b3Sopenharmony_ci * it for the first time. */ 5753a5a1b3Sopenharmony_ci#define PA_MEMPOOL_SLOTS_MAX 1024 5853a5a1b3Sopenharmony_ci#define PA_MEMPOOL_SLOT_SIZE (64*1024) 5953a5a1b3Sopenharmony_ci 6053a5a1b3Sopenharmony_ci#define PA_MEMEXPORT_SLOTS_MAX 128 6153a5a1b3Sopenharmony_ci 6253a5a1b3Sopenharmony_ci#define PA_MEMIMPORT_SLOTS_MAX 160 6353a5a1b3Sopenharmony_ci#define PA_MEMIMPORT_SEGMENTS_MAX 16 6453a5a1b3Sopenharmony_ci/* 6553a5a1b3Sopenharmony_ci * If true, this segment's lifetime will not be limited by the 6653a5a1b3Sopenharmony_ci * number of active blocks (seg->n_blocks) using its shared memory. 6753a5a1b3Sopenharmony_ci * Rather, it will exist for the full lifetime of the memimport it 6853a5a1b3Sopenharmony_ci * is attached to. 6953a5a1b3Sopenharmony_ci * 7053a5a1b3Sopenharmony_ci * This is done to support memfd blocks transport. 7153a5a1b3Sopenharmony_ci * 7253a5a1b3Sopenharmony_ci * To transfer memfd-backed blocks without passing their fd every 7353a5a1b3Sopenharmony_ci * time, thus minimizing overhead and avoiding fd leaks, a command 7453a5a1b3Sopenharmony_ci * is sent with the memfd fd as ancil data very early on. 7553a5a1b3Sopenharmony_ci * 7653a5a1b3Sopenharmony_ci * This command has an ID that identifies the memfd region. Further 7753a5a1b3Sopenharmony_ci * block references are then exclusively done using this ID. On the 7853a5a1b3Sopenharmony_ci * receiving end, such logic is enabled by the memimport's segment 7953a5a1b3Sopenharmony_ci * hash and 'permanent' segments below. 8053a5a1b3Sopenharmony_ci */ 8153a5a1b3Sopenharmony_cistatic bool segment_is_permanent(pa_memimport_segment *seg) { 8253a5a1b3Sopenharmony_ci pa_assert(seg); 8353a5a1b3Sopenharmony_ci return seg->memory.type == PA_MEM_TYPE_SHARED_MEMFD; 8453a5a1b3Sopenharmony_ci} 8553a5a1b3Sopenharmony_ci 8653a5a1b3Sopenharmony_ci/* A collection of multiple segments */ 8753a5a1b3Sopenharmony_cistruct pa_memimport { 8853a5a1b3Sopenharmony_ci pa_mutex *mutex; 8953a5a1b3Sopenharmony_ci 9053a5a1b3Sopenharmony_ci pa_mempool *pool; 9153a5a1b3Sopenharmony_ci pa_hashmap *segments; 9253a5a1b3Sopenharmony_ci pa_hashmap *blocks; 9353a5a1b3Sopenharmony_ci 9453a5a1b3Sopenharmony_ci /* Called whenever an imported memory block is no longer 9553a5a1b3Sopenharmony_ci * needed. */ 9653a5a1b3Sopenharmony_ci pa_memimport_release_cb_t release_cb; 9753a5a1b3Sopenharmony_ci void *userdata; 9853a5a1b3Sopenharmony_ci 9953a5a1b3Sopenharmony_ci PA_LLIST_FIELDS(pa_memimport); 10053a5a1b3Sopenharmony_ci}; 10153a5a1b3Sopenharmony_ci 10253a5a1b3Sopenharmony_cistruct memexport_slot { 10353a5a1b3Sopenharmony_ci PA_LLIST_FIELDS(struct memexport_slot); 10453a5a1b3Sopenharmony_ci pa_memblock *block; 10553a5a1b3Sopenharmony_ci}; 10653a5a1b3Sopenharmony_ci 10753a5a1b3Sopenharmony_cistruct pa_memexport { 10853a5a1b3Sopenharmony_ci pa_mutex *mutex; 10953a5a1b3Sopenharmony_ci pa_mempool *pool; 11053a5a1b3Sopenharmony_ci 11153a5a1b3Sopenharmony_ci struct memexport_slot slots[PA_MEMEXPORT_SLOTS_MAX]; 11253a5a1b3Sopenharmony_ci 11353a5a1b3Sopenharmony_ci PA_LLIST_HEAD(struct memexport_slot, free_slots); 11453a5a1b3Sopenharmony_ci PA_LLIST_HEAD(struct memexport_slot, used_slots); 11553a5a1b3Sopenharmony_ci unsigned n_init; 11653a5a1b3Sopenharmony_ci unsigned baseidx; 11753a5a1b3Sopenharmony_ci 11853a5a1b3Sopenharmony_ci /* Called whenever a client from which we imported a memory block 11953a5a1b3Sopenharmony_ci which we in turn exported to another client dies and we need to 12053a5a1b3Sopenharmony_ci revoke the memory block accordingly */ 12153a5a1b3Sopenharmony_ci pa_memexport_revoke_cb_t revoke_cb; 12253a5a1b3Sopenharmony_ci void *userdata; 12353a5a1b3Sopenharmony_ci 12453a5a1b3Sopenharmony_ci PA_LLIST_FIELDS(pa_memexport); 12553a5a1b3Sopenharmony_ci}; 12653a5a1b3Sopenharmony_ci 12753a5a1b3Sopenharmony_cistruct pa_mempool { 12853a5a1b3Sopenharmony_ci /* Reference count the mempool 12953a5a1b3Sopenharmony_ci * 13053a5a1b3Sopenharmony_ci * Any block allocation from the pool itself, or even just imported from 13153a5a1b3Sopenharmony_ci * another process through SHM and attached to it (PA_MEMBLOCK_IMPORTED), 13253a5a1b3Sopenharmony_ci * shall increase the refcount. 13353a5a1b3Sopenharmony_ci * 13453a5a1b3Sopenharmony_ci * This is done for per-client mempools: global references to blocks in 13553a5a1b3Sopenharmony_ci * the pool, or just to attached ones, can still be lingering around when 13653a5a1b3Sopenharmony_ci * the client connection dies and all per-client objects are to be freed. 13753a5a1b3Sopenharmony_ci * That is, current PulseAudio design does not guarantee that the client 13853a5a1b3Sopenharmony_ci * mempool blocks are referenced only by client-specific objects. 13953a5a1b3Sopenharmony_ci * 14053a5a1b3Sopenharmony_ci * For further details, please check: 14153a5a1b3Sopenharmony_ci * https://lists.freedesktop.org/archives/pulseaudio-discuss/2016-February/025587.html 14253a5a1b3Sopenharmony_ci */ 14353a5a1b3Sopenharmony_ci PA_REFCNT_DECLARE; 14453a5a1b3Sopenharmony_ci 14553a5a1b3Sopenharmony_ci pa_semaphore *semaphore; 14653a5a1b3Sopenharmony_ci pa_mutex *mutex; 14753a5a1b3Sopenharmony_ci 14853a5a1b3Sopenharmony_ci pa_shm memory; 14953a5a1b3Sopenharmony_ci 15053a5a1b3Sopenharmony_ci bool global; 15153a5a1b3Sopenharmony_ci 15253a5a1b3Sopenharmony_ci size_t block_size; 15353a5a1b3Sopenharmony_ci unsigned n_blocks; 15453a5a1b3Sopenharmony_ci bool is_remote_writable; 15553a5a1b3Sopenharmony_ci 15653a5a1b3Sopenharmony_ci pa_atomic_t n_init; 15753a5a1b3Sopenharmony_ci 15853a5a1b3Sopenharmony_ci PA_LLIST_HEAD(pa_memimport, imports); 15953a5a1b3Sopenharmony_ci PA_LLIST_HEAD(pa_memexport, exports); 16053a5a1b3Sopenharmony_ci 16153a5a1b3Sopenharmony_ci /* A list of free slots that may be reused */ 16253a5a1b3Sopenharmony_ci pa_flist *free_slots; 16353a5a1b3Sopenharmony_ci 16453a5a1b3Sopenharmony_ci pa_mempool_stat stat; 16553a5a1b3Sopenharmony_ci}; 16653a5a1b3Sopenharmony_ci 16753a5a1b3Sopenharmony_cistatic void segment_detach(pa_memimport_segment *seg); 16853a5a1b3Sopenharmony_ci 16953a5a1b3Sopenharmony_ciPA_STATIC_FLIST_DECLARE(unused_memblocks, 0, pa_xfree); 17053a5a1b3Sopenharmony_ci 17153a5a1b3Sopenharmony_ci/* No lock necessary */ 17253a5a1b3Sopenharmony_cistatic void stat_add(pa_memblock*b) { 17353a5a1b3Sopenharmony_ci pa_assert(b); 17453a5a1b3Sopenharmony_ci pa_assert(b->pool); 17553a5a1b3Sopenharmony_ci 17653a5a1b3Sopenharmony_ci pa_atomic_inc(&b->pool->stat.n_allocated); 17753a5a1b3Sopenharmony_ci pa_atomic_add(&b->pool->stat.allocated_size, (int) b->length); 17853a5a1b3Sopenharmony_ci 17953a5a1b3Sopenharmony_ci pa_atomic_inc(&b->pool->stat.n_accumulated); 18053a5a1b3Sopenharmony_ci pa_atomic_add(&b->pool->stat.accumulated_size, (int) b->length); 18153a5a1b3Sopenharmony_ci 18253a5a1b3Sopenharmony_ci if (b->type == PA_MEMBLOCK_IMPORTED) { 18353a5a1b3Sopenharmony_ci pa_atomic_inc(&b->pool->stat.n_imported); 18453a5a1b3Sopenharmony_ci pa_atomic_add(&b->pool->stat.imported_size, (int) b->length); 18553a5a1b3Sopenharmony_ci } 18653a5a1b3Sopenharmony_ci 18753a5a1b3Sopenharmony_ci pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]); 18853a5a1b3Sopenharmony_ci pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]); 18953a5a1b3Sopenharmony_ci} 19053a5a1b3Sopenharmony_ci 19153a5a1b3Sopenharmony_ci/* No lock necessary */ 19253a5a1b3Sopenharmony_cistatic void stat_remove(pa_memblock *b) { 19353a5a1b3Sopenharmony_ci pa_assert(b); 19453a5a1b3Sopenharmony_ci pa_assert(b->pool); 19553a5a1b3Sopenharmony_ci 19653a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&b->pool->stat.n_allocated) > 0); 19753a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&b->pool->stat.allocated_size) >= (int) b->length); 19853a5a1b3Sopenharmony_ci 19953a5a1b3Sopenharmony_ci pa_atomic_dec(&b->pool->stat.n_allocated); 20053a5a1b3Sopenharmony_ci pa_atomic_sub(&b->pool->stat.allocated_size, (int) b->length); 20153a5a1b3Sopenharmony_ci 20253a5a1b3Sopenharmony_ci if (b->type == PA_MEMBLOCK_IMPORTED) { 20353a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0); 20453a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length); 20553a5a1b3Sopenharmony_ci 20653a5a1b3Sopenharmony_ci pa_atomic_dec(&b->pool->stat.n_imported); 20753a5a1b3Sopenharmony_ci pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length); 20853a5a1b3Sopenharmony_ci } 20953a5a1b3Sopenharmony_ci 21053a5a1b3Sopenharmony_ci pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]); 21153a5a1b3Sopenharmony_ci} 21253a5a1b3Sopenharmony_ci 21353a5a1b3Sopenharmony_cistatic pa_memblock *memblock_new_appended(pa_mempool *p, size_t length); 21453a5a1b3Sopenharmony_ci 21553a5a1b3Sopenharmony_ci/* No lock necessary */ 21653a5a1b3Sopenharmony_cipa_memblock *pa_memblock_new(pa_mempool *p, size_t length) { 21753a5a1b3Sopenharmony_ci pa_memblock *b; 21853a5a1b3Sopenharmony_ci 21953a5a1b3Sopenharmony_ci pa_assert(p); 22053a5a1b3Sopenharmony_ci pa_assert(length); 22153a5a1b3Sopenharmony_ci 22253a5a1b3Sopenharmony_ci if (!(b = pa_memblock_new_pool(p, length))) 22353a5a1b3Sopenharmony_ci b = memblock_new_appended(p, length); 22453a5a1b3Sopenharmony_ci 22553a5a1b3Sopenharmony_ci return b; 22653a5a1b3Sopenharmony_ci} 22753a5a1b3Sopenharmony_ci 22853a5a1b3Sopenharmony_ci/* No lock necessary */ 22953a5a1b3Sopenharmony_cistatic pa_memblock *memblock_new_appended(pa_mempool *p, size_t length) { 23053a5a1b3Sopenharmony_ci pa_memblock *b; 23153a5a1b3Sopenharmony_ci 23253a5a1b3Sopenharmony_ci pa_assert(p); 23353a5a1b3Sopenharmony_ci pa_assert(length); 23453a5a1b3Sopenharmony_ci 23553a5a1b3Sopenharmony_ci /* If -1 is passed as length we choose the size for the caller. */ 23653a5a1b3Sopenharmony_ci 23753a5a1b3Sopenharmony_ci if (length == (size_t) -1) 23853a5a1b3Sopenharmony_ci length = pa_mempool_block_size_max(p); 23953a5a1b3Sopenharmony_ci 24053a5a1b3Sopenharmony_ci b = pa_xmalloc(PA_ALIGN(sizeof(pa_memblock)) + length); 24153a5a1b3Sopenharmony_ci PA_REFCNT_INIT(b); 24253a5a1b3Sopenharmony_ci b->pool = p; 24353a5a1b3Sopenharmony_ci pa_mempool_ref(b->pool); 24453a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_APPENDED; 24553a5a1b3Sopenharmony_ci b->read_only = b->is_silence = false; 24653a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); 24753a5a1b3Sopenharmony_ci b->length = length; 24853a5a1b3Sopenharmony_ci pa_atomic_store(&b->n_acquired, 0); 24953a5a1b3Sopenharmony_ci pa_atomic_store(&b->please_signal, 0); 25053a5a1b3Sopenharmony_ci 25153a5a1b3Sopenharmony_ci stat_add(b); 25253a5a1b3Sopenharmony_ci return b; 25353a5a1b3Sopenharmony_ci} 25453a5a1b3Sopenharmony_ci 25553a5a1b3Sopenharmony_ci/* No lock necessary */ 25653a5a1b3Sopenharmony_cistatic struct mempool_slot* mempool_allocate_slot(pa_mempool *p) { 25753a5a1b3Sopenharmony_ci struct mempool_slot *slot; 25853a5a1b3Sopenharmony_ci pa_assert(p); 25953a5a1b3Sopenharmony_ci 26053a5a1b3Sopenharmony_ci if (!(slot = pa_flist_pop(p->free_slots))) { 26153a5a1b3Sopenharmony_ci int idx; 26253a5a1b3Sopenharmony_ci 26353a5a1b3Sopenharmony_ci /* The free list was empty, we have to allocate a new entry */ 26453a5a1b3Sopenharmony_ci 26553a5a1b3Sopenharmony_ci if ((unsigned) (idx = pa_atomic_inc(&p->n_init)) >= p->n_blocks) 26653a5a1b3Sopenharmony_ci pa_atomic_dec(&p->n_init); 26753a5a1b3Sopenharmony_ci else 26853a5a1b3Sopenharmony_ci slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) idx)); 26953a5a1b3Sopenharmony_ci 27053a5a1b3Sopenharmony_ci if (!slot) { 27153a5a1b3Sopenharmony_ci if (pa_log_ratelimit(PA_LOG_DEBUG)) 27253a5a1b3Sopenharmony_ci pa_log_debug("Pool full"); 27353a5a1b3Sopenharmony_ci pa_atomic_inc(&p->stat.n_pool_full); 27453a5a1b3Sopenharmony_ci return NULL; 27553a5a1b3Sopenharmony_ci } 27653a5a1b3Sopenharmony_ci } 27753a5a1b3Sopenharmony_ci 27853a5a1b3Sopenharmony_ci/* #ifdef HAVE_VALGRIND_MEMCHECK_H */ 27953a5a1b3Sopenharmony_ci/* if (PA_UNLIKELY(pa_in_valgrind())) { */ 28053a5a1b3Sopenharmony_ci/* VALGRIND_MALLOCLIKE_BLOCK(slot, p->block_size, 0, 0); */ 28153a5a1b3Sopenharmony_ci/* } */ 28253a5a1b3Sopenharmony_ci/* #endif */ 28353a5a1b3Sopenharmony_ci 28453a5a1b3Sopenharmony_ci return slot; 28553a5a1b3Sopenharmony_ci} 28653a5a1b3Sopenharmony_ci 28753a5a1b3Sopenharmony_ci/* No lock necessary, totally redundant anyway */ 28853a5a1b3Sopenharmony_cistatic inline void* mempool_slot_data(struct mempool_slot *slot) { 28953a5a1b3Sopenharmony_ci return slot; 29053a5a1b3Sopenharmony_ci} 29153a5a1b3Sopenharmony_ci 29253a5a1b3Sopenharmony_ci/* No lock necessary */ 29353a5a1b3Sopenharmony_cistatic unsigned mempool_slot_idx(pa_mempool *p, void *ptr) { 29453a5a1b3Sopenharmony_ci pa_assert(p); 29553a5a1b3Sopenharmony_ci 29653a5a1b3Sopenharmony_ci pa_assert((uint8_t*) ptr >= (uint8_t*) p->memory.ptr); 29753a5a1b3Sopenharmony_ci pa_assert((uint8_t*) ptr < (uint8_t*) p->memory.ptr + p->memory.size); 29853a5a1b3Sopenharmony_ci 29953a5a1b3Sopenharmony_ci return (unsigned) ((size_t) ((uint8_t*) ptr - (uint8_t*) p->memory.ptr) / p->block_size); 30053a5a1b3Sopenharmony_ci} 30153a5a1b3Sopenharmony_ci 30253a5a1b3Sopenharmony_ci/* No lock necessary */ 30353a5a1b3Sopenharmony_cistatic struct mempool_slot* mempool_slot_by_ptr(pa_mempool *p, void *ptr) { 30453a5a1b3Sopenharmony_ci unsigned idx; 30553a5a1b3Sopenharmony_ci 30653a5a1b3Sopenharmony_ci if ((idx = mempool_slot_idx(p, ptr)) == (unsigned) -1) 30753a5a1b3Sopenharmony_ci return NULL; 30853a5a1b3Sopenharmony_ci 30953a5a1b3Sopenharmony_ci return (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (idx * p->block_size)); 31053a5a1b3Sopenharmony_ci} 31153a5a1b3Sopenharmony_ci 31253a5a1b3Sopenharmony_ci/* No lock necessary */ 31353a5a1b3Sopenharmony_cibool pa_mempool_is_remote_writable(pa_mempool *p) { 31453a5a1b3Sopenharmony_ci pa_assert(p); 31553a5a1b3Sopenharmony_ci return p->is_remote_writable; 31653a5a1b3Sopenharmony_ci} 31753a5a1b3Sopenharmony_ci 31853a5a1b3Sopenharmony_ci/* No lock necessary */ 31953a5a1b3Sopenharmony_civoid pa_mempool_set_is_remote_writable(pa_mempool *p, bool writable) { 32053a5a1b3Sopenharmony_ci pa_assert(p); 32153a5a1b3Sopenharmony_ci pa_assert(!writable || pa_mempool_is_shared(p)); 32253a5a1b3Sopenharmony_ci p->is_remote_writable = writable; 32353a5a1b3Sopenharmony_ci} 32453a5a1b3Sopenharmony_ci 32553a5a1b3Sopenharmony_ci/* No lock necessary */ 32653a5a1b3Sopenharmony_cipa_memblock *pa_memblock_new_pool(pa_mempool *p, size_t length) { 32753a5a1b3Sopenharmony_ci pa_memblock *b = NULL; 32853a5a1b3Sopenharmony_ci struct mempool_slot *slot; 32953a5a1b3Sopenharmony_ci static int mempool_disable = 0; 33053a5a1b3Sopenharmony_ci 33153a5a1b3Sopenharmony_ci pa_assert(p); 33253a5a1b3Sopenharmony_ci pa_assert(length); 33353a5a1b3Sopenharmony_ci 33453a5a1b3Sopenharmony_ci if (mempool_disable == 0) 33553a5a1b3Sopenharmony_ci mempool_disable = getenv("PULSE_MEMPOOL_DISABLE") ? 1 : -1; 33653a5a1b3Sopenharmony_ci 33753a5a1b3Sopenharmony_ci if (mempool_disable > 0) 33853a5a1b3Sopenharmony_ci return NULL; 33953a5a1b3Sopenharmony_ci 34053a5a1b3Sopenharmony_ci /* If -1 is passed as length we choose the size for the caller: we 34153a5a1b3Sopenharmony_ci * take the largest size that fits in one of our slots. */ 34253a5a1b3Sopenharmony_ci 34353a5a1b3Sopenharmony_ci if (length == (size_t) -1) 34453a5a1b3Sopenharmony_ci length = pa_mempool_block_size_max(p); 34553a5a1b3Sopenharmony_ci 34653a5a1b3Sopenharmony_ci if (p->block_size >= PA_ALIGN(sizeof(pa_memblock)) + length) { 34753a5a1b3Sopenharmony_ci 34853a5a1b3Sopenharmony_ci if (!(slot = mempool_allocate_slot(p))) 34953a5a1b3Sopenharmony_ci return NULL; 35053a5a1b3Sopenharmony_ci 35153a5a1b3Sopenharmony_ci b = mempool_slot_data(slot); 35253a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_POOL; 35353a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, (uint8_t*) b + PA_ALIGN(sizeof(pa_memblock))); 35453a5a1b3Sopenharmony_ci 35553a5a1b3Sopenharmony_ci } else if (p->block_size >= length) { 35653a5a1b3Sopenharmony_ci 35753a5a1b3Sopenharmony_ci if (!(slot = mempool_allocate_slot(p))) 35853a5a1b3Sopenharmony_ci return NULL; 35953a5a1b3Sopenharmony_ci 36053a5a1b3Sopenharmony_ci if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) 36153a5a1b3Sopenharmony_ci b = pa_xnew(pa_memblock, 1); 36253a5a1b3Sopenharmony_ci 36353a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_POOL_EXTERNAL; 36453a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, mempool_slot_data(slot)); 36553a5a1b3Sopenharmony_ci 36653a5a1b3Sopenharmony_ci } else { 36753a5a1b3Sopenharmony_ci pa_log_debug("Memory block too large for pool: %lu > %lu", 36853a5a1b3Sopenharmony_ci (unsigned long) length, (unsigned long) p->block_size); 36953a5a1b3Sopenharmony_ci pa_atomic_inc(&p->stat.n_too_large_for_pool); 37053a5a1b3Sopenharmony_ci return NULL; 37153a5a1b3Sopenharmony_ci } 37253a5a1b3Sopenharmony_ci 37353a5a1b3Sopenharmony_ci PA_REFCNT_INIT(b); 37453a5a1b3Sopenharmony_ci b->pool = p; 37553a5a1b3Sopenharmony_ci pa_mempool_ref(b->pool); 37653a5a1b3Sopenharmony_ci b->read_only = b->is_silence = false; 37753a5a1b3Sopenharmony_ci b->length = length; 37853a5a1b3Sopenharmony_ci pa_atomic_store(&b->n_acquired, 0); 37953a5a1b3Sopenharmony_ci pa_atomic_store(&b->please_signal, 0); 38053a5a1b3Sopenharmony_ci 38153a5a1b3Sopenharmony_ci stat_add(b); 38253a5a1b3Sopenharmony_ci return b; 38353a5a1b3Sopenharmony_ci} 38453a5a1b3Sopenharmony_ci 38553a5a1b3Sopenharmony_ci/* No lock necessary */ 38653a5a1b3Sopenharmony_cipa_memblock *pa_memblock_new_fixed(pa_mempool *p, void *d, size_t length, bool read_only) { 38753a5a1b3Sopenharmony_ci pa_memblock *b; 38853a5a1b3Sopenharmony_ci 38953a5a1b3Sopenharmony_ci pa_assert(p); 39053a5a1b3Sopenharmony_ci pa_assert(d); 39153a5a1b3Sopenharmony_ci pa_assert(length != (size_t) -1); 39253a5a1b3Sopenharmony_ci pa_assert(length); 39353a5a1b3Sopenharmony_ci 39453a5a1b3Sopenharmony_ci if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) 39553a5a1b3Sopenharmony_ci b = pa_xnew(pa_memblock, 1); 39653a5a1b3Sopenharmony_ci 39753a5a1b3Sopenharmony_ci PA_REFCNT_INIT(b); 39853a5a1b3Sopenharmony_ci b->pool = p; 39953a5a1b3Sopenharmony_ci pa_mempool_ref(b->pool); 40053a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_FIXED; 40153a5a1b3Sopenharmony_ci b->read_only = read_only; 40253a5a1b3Sopenharmony_ci b->is_silence = false; 40353a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, d); 40453a5a1b3Sopenharmony_ci b->length = length; 40553a5a1b3Sopenharmony_ci pa_atomic_store(&b->n_acquired, 0); 40653a5a1b3Sopenharmony_ci pa_atomic_store(&b->please_signal, 0); 40753a5a1b3Sopenharmony_ci 40853a5a1b3Sopenharmony_ci stat_add(b); 40953a5a1b3Sopenharmony_ci return b; 41053a5a1b3Sopenharmony_ci} 41153a5a1b3Sopenharmony_ci 41253a5a1b3Sopenharmony_ci/* No lock necessary */ 41353a5a1b3Sopenharmony_cipa_memblock *pa_memblock_new_user( 41453a5a1b3Sopenharmony_ci pa_mempool *p, 41553a5a1b3Sopenharmony_ci void *d, 41653a5a1b3Sopenharmony_ci size_t length, 41753a5a1b3Sopenharmony_ci pa_free_cb_t free_cb, 41853a5a1b3Sopenharmony_ci void *free_cb_data, 41953a5a1b3Sopenharmony_ci bool read_only) { 42053a5a1b3Sopenharmony_ci pa_memblock *b; 42153a5a1b3Sopenharmony_ci 42253a5a1b3Sopenharmony_ci pa_assert(p); 42353a5a1b3Sopenharmony_ci pa_assert(d); 42453a5a1b3Sopenharmony_ci pa_assert(length); 42553a5a1b3Sopenharmony_ci pa_assert(length != (size_t) -1); 42653a5a1b3Sopenharmony_ci pa_assert(free_cb); 42753a5a1b3Sopenharmony_ci 42853a5a1b3Sopenharmony_ci if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) 42953a5a1b3Sopenharmony_ci b = pa_xnew(pa_memblock, 1); 43053a5a1b3Sopenharmony_ci 43153a5a1b3Sopenharmony_ci PA_REFCNT_INIT(b); 43253a5a1b3Sopenharmony_ci b->pool = p; 43353a5a1b3Sopenharmony_ci pa_mempool_ref(b->pool); 43453a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_USER; 43553a5a1b3Sopenharmony_ci b->read_only = read_only; 43653a5a1b3Sopenharmony_ci b->is_silence = false; 43753a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, d); 43853a5a1b3Sopenharmony_ci b->length = length; 43953a5a1b3Sopenharmony_ci pa_atomic_store(&b->n_acquired, 0); 44053a5a1b3Sopenharmony_ci pa_atomic_store(&b->please_signal, 0); 44153a5a1b3Sopenharmony_ci 44253a5a1b3Sopenharmony_ci b->per_type.user.free_cb = free_cb; 44353a5a1b3Sopenharmony_ci b->per_type.user.free_cb_data = free_cb_data; 44453a5a1b3Sopenharmony_ci 44553a5a1b3Sopenharmony_ci stat_add(b); 44653a5a1b3Sopenharmony_ci return b; 44753a5a1b3Sopenharmony_ci} 44853a5a1b3Sopenharmony_ci 44953a5a1b3Sopenharmony_ci/* No lock necessary */ 45053a5a1b3Sopenharmony_cibool pa_memblock_is_ours(pa_memblock *b) { 45153a5a1b3Sopenharmony_ci pa_assert(b); 45253a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 45353a5a1b3Sopenharmony_ci 45453a5a1b3Sopenharmony_ci return b->type != PA_MEMBLOCK_IMPORTED; 45553a5a1b3Sopenharmony_ci} 45653a5a1b3Sopenharmony_ci 45753a5a1b3Sopenharmony_ci/* No lock necessary */ 45853a5a1b3Sopenharmony_cibool pa_memblock_is_read_only(pa_memblock *b) { 45953a5a1b3Sopenharmony_ci pa_assert(b); 46053a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 46153a5a1b3Sopenharmony_ci 46253a5a1b3Sopenharmony_ci return b->read_only || PA_REFCNT_VALUE(b) > 1; 46353a5a1b3Sopenharmony_ci} 46453a5a1b3Sopenharmony_ci 46553a5a1b3Sopenharmony_ci/* No lock necessary */ 46653a5a1b3Sopenharmony_cibool pa_memblock_is_silence(pa_memblock *b) { 46753a5a1b3Sopenharmony_ci pa_assert(b); 46853a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 46953a5a1b3Sopenharmony_ci 47053a5a1b3Sopenharmony_ci return b->is_silence; 47153a5a1b3Sopenharmony_ci} 47253a5a1b3Sopenharmony_ci 47353a5a1b3Sopenharmony_ci/* No lock necessary */ 47453a5a1b3Sopenharmony_civoid pa_memblock_set_is_silence(pa_memblock *b, bool v) { 47553a5a1b3Sopenharmony_ci pa_assert(b); 47653a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 47753a5a1b3Sopenharmony_ci 47853a5a1b3Sopenharmony_ci b->is_silence = v; 47953a5a1b3Sopenharmony_ci} 48053a5a1b3Sopenharmony_ci 48153a5a1b3Sopenharmony_ci/* No lock necessary */ 48253a5a1b3Sopenharmony_cibool pa_memblock_ref_is_one(pa_memblock *b) { 48353a5a1b3Sopenharmony_ci int r; 48453a5a1b3Sopenharmony_ci pa_assert(b); 48553a5a1b3Sopenharmony_ci 48653a5a1b3Sopenharmony_ci pa_assert_se((r = PA_REFCNT_VALUE(b)) > 0); 48753a5a1b3Sopenharmony_ci 48853a5a1b3Sopenharmony_ci return r == 1; 48953a5a1b3Sopenharmony_ci} 49053a5a1b3Sopenharmony_ci 49153a5a1b3Sopenharmony_ci/* No lock necessary */ 49253a5a1b3Sopenharmony_civoid* pa_memblock_acquire(pa_memblock *b) { 49353a5a1b3Sopenharmony_ci pa_assert(b); 49453a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 49553a5a1b3Sopenharmony_ci 49653a5a1b3Sopenharmony_ci pa_atomic_inc(&b->n_acquired); 49753a5a1b3Sopenharmony_ci 49853a5a1b3Sopenharmony_ci return pa_atomic_ptr_load(&b->data); 49953a5a1b3Sopenharmony_ci} 50053a5a1b3Sopenharmony_ci 50153a5a1b3Sopenharmony_ci/* No lock necessary */ 50253a5a1b3Sopenharmony_civoid *pa_memblock_acquire_chunk(const pa_memchunk *c) { 50353a5a1b3Sopenharmony_ci pa_assert(c); 50453a5a1b3Sopenharmony_ci 50553a5a1b3Sopenharmony_ci return (uint8_t *) pa_memblock_acquire(c->memblock) + c->index; 50653a5a1b3Sopenharmony_ci} 50753a5a1b3Sopenharmony_ci 50853a5a1b3Sopenharmony_ci/* No lock necessary, in corner cases locks by its own */ 50953a5a1b3Sopenharmony_civoid pa_memblock_release(pa_memblock *b) { 51053a5a1b3Sopenharmony_ci int r; 51153a5a1b3Sopenharmony_ci pa_assert(b); 51253a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 51353a5a1b3Sopenharmony_ci 51453a5a1b3Sopenharmony_ci r = pa_atomic_dec(&b->n_acquired); 51553a5a1b3Sopenharmony_ci pa_assert(r >= 1); 51653a5a1b3Sopenharmony_ci 51753a5a1b3Sopenharmony_ci /* Signal a waiting thread that this memblock is no longer used */ 51853a5a1b3Sopenharmony_ci if (r == 1 && pa_atomic_load(&b->please_signal)) 51953a5a1b3Sopenharmony_ci pa_semaphore_post(b->pool->semaphore); 52053a5a1b3Sopenharmony_ci} 52153a5a1b3Sopenharmony_ci 52253a5a1b3Sopenharmony_cisize_t pa_memblock_get_length(pa_memblock *b) { 52353a5a1b3Sopenharmony_ci pa_assert(b); 52453a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 52553a5a1b3Sopenharmony_ci 52653a5a1b3Sopenharmony_ci return b->length; 52753a5a1b3Sopenharmony_ci} 52853a5a1b3Sopenharmony_ci 52953a5a1b3Sopenharmony_ci/* Note! Always unref the returned pool after use */ 53053a5a1b3Sopenharmony_cipa_mempool* pa_memblock_get_pool(pa_memblock *b) { 53153a5a1b3Sopenharmony_ci pa_assert(b); 53253a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 53353a5a1b3Sopenharmony_ci pa_assert(b->pool); 53453a5a1b3Sopenharmony_ci 53553a5a1b3Sopenharmony_ci pa_mempool_ref(b->pool); 53653a5a1b3Sopenharmony_ci return b->pool; 53753a5a1b3Sopenharmony_ci} 53853a5a1b3Sopenharmony_ci 53953a5a1b3Sopenharmony_ci/* No lock necessary */ 54053a5a1b3Sopenharmony_cipa_memblock* pa_memblock_ref(pa_memblock*b) { 54153a5a1b3Sopenharmony_ci pa_assert(b); 54253a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 54353a5a1b3Sopenharmony_ci 54453a5a1b3Sopenharmony_ci PA_REFCNT_INC(b); 54553a5a1b3Sopenharmony_ci return b; 54653a5a1b3Sopenharmony_ci} 54753a5a1b3Sopenharmony_ci 54853a5a1b3Sopenharmony_cistatic void memblock_free(pa_memblock *b) { 54953a5a1b3Sopenharmony_ci pa_mempool *pool; 55053a5a1b3Sopenharmony_ci 55153a5a1b3Sopenharmony_ci pa_assert(b); 55253a5a1b3Sopenharmony_ci pa_assert(b->pool); 55353a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&b->n_acquired) == 0); 55453a5a1b3Sopenharmony_ci 55553a5a1b3Sopenharmony_ci pool = b->pool; 55653a5a1b3Sopenharmony_ci stat_remove(b); 55753a5a1b3Sopenharmony_ci 55853a5a1b3Sopenharmony_ci switch (b->type) { 55953a5a1b3Sopenharmony_ci case PA_MEMBLOCK_USER : 56053a5a1b3Sopenharmony_ci pa_assert(b->per_type.user.free_cb); 56153a5a1b3Sopenharmony_ci b->per_type.user.free_cb(b->per_type.user.free_cb_data); 56253a5a1b3Sopenharmony_ci 56353a5a1b3Sopenharmony_ci /* Fall through */ 56453a5a1b3Sopenharmony_ci 56553a5a1b3Sopenharmony_ci case PA_MEMBLOCK_FIXED: 56653a5a1b3Sopenharmony_ci if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0) 56753a5a1b3Sopenharmony_ci pa_xfree(b); 56853a5a1b3Sopenharmony_ci 56953a5a1b3Sopenharmony_ci break; 57053a5a1b3Sopenharmony_ci 57153a5a1b3Sopenharmony_ci case PA_MEMBLOCK_APPENDED: 57253a5a1b3Sopenharmony_ci 57353a5a1b3Sopenharmony_ci /* We could attach it to unused_memblocks, but that would 57453a5a1b3Sopenharmony_ci * probably waste some considerable amount of memory */ 57553a5a1b3Sopenharmony_ci pa_xfree(b); 57653a5a1b3Sopenharmony_ci break; 57753a5a1b3Sopenharmony_ci 57853a5a1b3Sopenharmony_ci case PA_MEMBLOCK_IMPORTED: { 57953a5a1b3Sopenharmony_ci pa_memimport_segment *segment; 58053a5a1b3Sopenharmony_ci pa_memimport *import; 58153a5a1b3Sopenharmony_ci 58253a5a1b3Sopenharmony_ci /* FIXME! This should be implemented lock-free */ 58353a5a1b3Sopenharmony_ci 58453a5a1b3Sopenharmony_ci pa_assert_se(segment = b->per_type.imported.segment); 58553a5a1b3Sopenharmony_ci pa_assert_se(import = segment->import); 58653a5a1b3Sopenharmony_ci 58753a5a1b3Sopenharmony_ci pa_mutex_lock(import->mutex); 58853a5a1b3Sopenharmony_ci 58953a5a1b3Sopenharmony_ci pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id))); 59053a5a1b3Sopenharmony_ci 59153a5a1b3Sopenharmony_ci pa_assert(segment->n_blocks >= 1); 59253a5a1b3Sopenharmony_ci if (-- segment->n_blocks <= 0) 59353a5a1b3Sopenharmony_ci segment_detach(segment); 59453a5a1b3Sopenharmony_ci 59553a5a1b3Sopenharmony_ci pa_mutex_unlock(import->mutex); 59653a5a1b3Sopenharmony_ci 59753a5a1b3Sopenharmony_ci import->release_cb(import, b->per_type.imported.id, import->userdata); 59853a5a1b3Sopenharmony_ci 59953a5a1b3Sopenharmony_ci if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0) 60053a5a1b3Sopenharmony_ci pa_xfree(b); 60153a5a1b3Sopenharmony_ci 60253a5a1b3Sopenharmony_ci break; 60353a5a1b3Sopenharmony_ci } 60453a5a1b3Sopenharmony_ci 60553a5a1b3Sopenharmony_ci case PA_MEMBLOCK_POOL_EXTERNAL: 60653a5a1b3Sopenharmony_ci case PA_MEMBLOCK_POOL: { 60753a5a1b3Sopenharmony_ci struct mempool_slot *slot; 60853a5a1b3Sopenharmony_ci bool call_free; 60953a5a1b3Sopenharmony_ci 61053a5a1b3Sopenharmony_ci pa_assert_se(slot = mempool_slot_by_ptr(b->pool, pa_atomic_ptr_load(&b->data))); 61153a5a1b3Sopenharmony_ci 61253a5a1b3Sopenharmony_ci call_free = b->type == PA_MEMBLOCK_POOL_EXTERNAL; 61353a5a1b3Sopenharmony_ci 61453a5a1b3Sopenharmony_ci/* #ifdef HAVE_VALGRIND_MEMCHECK_H */ 61553a5a1b3Sopenharmony_ci/* if (PA_UNLIKELY(pa_in_valgrind())) { */ 61653a5a1b3Sopenharmony_ci/* VALGRIND_FREELIKE_BLOCK(slot, b->pool->block_size); */ 61753a5a1b3Sopenharmony_ci/* } */ 61853a5a1b3Sopenharmony_ci/* #endif */ 61953a5a1b3Sopenharmony_ci 62053a5a1b3Sopenharmony_ci /* The free list dimensions should easily allow all slots 62153a5a1b3Sopenharmony_ci * to fit in, hence try harder if pushing this slot into 62253a5a1b3Sopenharmony_ci * the free list fails */ 62353a5a1b3Sopenharmony_ci while (pa_flist_push(b->pool->free_slots, slot) < 0) 62453a5a1b3Sopenharmony_ci ; 62553a5a1b3Sopenharmony_ci 62653a5a1b3Sopenharmony_ci if (call_free) 62753a5a1b3Sopenharmony_ci if (pa_flist_push(PA_STATIC_FLIST_GET(unused_memblocks), b) < 0) 62853a5a1b3Sopenharmony_ci pa_xfree(b); 62953a5a1b3Sopenharmony_ci 63053a5a1b3Sopenharmony_ci break; 63153a5a1b3Sopenharmony_ci } 63253a5a1b3Sopenharmony_ci 63353a5a1b3Sopenharmony_ci case PA_MEMBLOCK_TYPE_MAX: 63453a5a1b3Sopenharmony_ci default: 63553a5a1b3Sopenharmony_ci pa_assert_not_reached(); 63653a5a1b3Sopenharmony_ci } 63753a5a1b3Sopenharmony_ci 63853a5a1b3Sopenharmony_ci pa_mempool_unref(pool); 63953a5a1b3Sopenharmony_ci} 64053a5a1b3Sopenharmony_ci 64153a5a1b3Sopenharmony_ci/* No lock necessary */ 64253a5a1b3Sopenharmony_civoid pa_memblock_unref(pa_memblock*b) { 64353a5a1b3Sopenharmony_ci pa_assert(b); 64453a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 64553a5a1b3Sopenharmony_ci 64653a5a1b3Sopenharmony_ci if (PA_REFCNT_DEC(b) > 0) 64753a5a1b3Sopenharmony_ci return; 64853a5a1b3Sopenharmony_ci 64953a5a1b3Sopenharmony_ci memblock_free(b); 65053a5a1b3Sopenharmony_ci} 65153a5a1b3Sopenharmony_ci 65253a5a1b3Sopenharmony_ci/* Self locked */ 65353a5a1b3Sopenharmony_cistatic void memblock_wait(pa_memblock *b) { 65453a5a1b3Sopenharmony_ci pa_assert(b); 65553a5a1b3Sopenharmony_ci 65653a5a1b3Sopenharmony_ci if (pa_atomic_load(&b->n_acquired) > 0) { 65753a5a1b3Sopenharmony_ci /* We need to wait until all threads gave up access to the 65853a5a1b3Sopenharmony_ci * memory block before we can go on. Unfortunately this means 65953a5a1b3Sopenharmony_ci * that we have to lock and wait here. Sniff! */ 66053a5a1b3Sopenharmony_ci 66153a5a1b3Sopenharmony_ci pa_atomic_inc(&b->please_signal); 66253a5a1b3Sopenharmony_ci 66353a5a1b3Sopenharmony_ci while (pa_atomic_load(&b->n_acquired) > 0) 66453a5a1b3Sopenharmony_ci pa_semaphore_wait(b->pool->semaphore); 66553a5a1b3Sopenharmony_ci 66653a5a1b3Sopenharmony_ci pa_atomic_dec(&b->please_signal); 66753a5a1b3Sopenharmony_ci } 66853a5a1b3Sopenharmony_ci} 66953a5a1b3Sopenharmony_ci 67053a5a1b3Sopenharmony_ci/* No lock necessary. This function is not multiple caller safe! */ 67153a5a1b3Sopenharmony_cistatic void memblock_make_local(pa_memblock *b) { 67253a5a1b3Sopenharmony_ci pa_assert(b); 67353a5a1b3Sopenharmony_ci 67453a5a1b3Sopenharmony_ci pa_atomic_dec(&b->pool->stat.n_allocated_by_type[b->type]); 67553a5a1b3Sopenharmony_ci 67653a5a1b3Sopenharmony_ci if (b->length <= b->pool->block_size) { 67753a5a1b3Sopenharmony_ci struct mempool_slot *slot; 67853a5a1b3Sopenharmony_ci 67953a5a1b3Sopenharmony_ci if ((slot = mempool_allocate_slot(b->pool))) { 68053a5a1b3Sopenharmony_ci void *new_data; 68153a5a1b3Sopenharmony_ci /* We can move it into a local pool, perfect! */ 68253a5a1b3Sopenharmony_ci 68353a5a1b3Sopenharmony_ci new_data = mempool_slot_data(slot); 68453a5a1b3Sopenharmony_ci memcpy(new_data, pa_atomic_ptr_load(&b->data), b->length); 68553a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, new_data); 68653a5a1b3Sopenharmony_ci 68753a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_POOL_EXTERNAL; 68853a5a1b3Sopenharmony_ci b->read_only = false; 68953a5a1b3Sopenharmony_ci 69053a5a1b3Sopenharmony_ci goto finish; 69153a5a1b3Sopenharmony_ci } 69253a5a1b3Sopenharmony_ci } 69353a5a1b3Sopenharmony_ci 69453a5a1b3Sopenharmony_ci /* Humm, not enough space in the pool, so lets allocate the memory with malloc() */ 69553a5a1b3Sopenharmony_ci b->per_type.user.free_cb = pa_xfree; 69653a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, pa_xmemdup(pa_atomic_ptr_load(&b->data), b->length)); 69753a5a1b3Sopenharmony_ci b->per_type.user.free_cb_data = pa_atomic_ptr_load(&b->data); 69853a5a1b3Sopenharmony_ci 69953a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_USER; 70053a5a1b3Sopenharmony_ci b->read_only = false; 70153a5a1b3Sopenharmony_ci 70253a5a1b3Sopenharmony_cifinish: 70353a5a1b3Sopenharmony_ci pa_atomic_inc(&b->pool->stat.n_allocated_by_type[b->type]); 70453a5a1b3Sopenharmony_ci pa_atomic_inc(&b->pool->stat.n_accumulated_by_type[b->type]); 70553a5a1b3Sopenharmony_ci memblock_wait(b); 70653a5a1b3Sopenharmony_ci} 70753a5a1b3Sopenharmony_ci 70853a5a1b3Sopenharmony_ci/* No lock necessary. This function is not multiple caller safe */ 70953a5a1b3Sopenharmony_civoid pa_memblock_unref_fixed(pa_memblock *b) { 71053a5a1b3Sopenharmony_ci pa_assert(b); 71153a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 71253a5a1b3Sopenharmony_ci pa_assert(b->type == PA_MEMBLOCK_FIXED); 71353a5a1b3Sopenharmony_ci 71453a5a1b3Sopenharmony_ci if (PA_REFCNT_VALUE(b) > 1) 71553a5a1b3Sopenharmony_ci memblock_make_local(b); 71653a5a1b3Sopenharmony_ci 71753a5a1b3Sopenharmony_ci pa_memblock_unref(b); 71853a5a1b3Sopenharmony_ci} 71953a5a1b3Sopenharmony_ci 72053a5a1b3Sopenharmony_ci/* No lock necessary. */ 72153a5a1b3Sopenharmony_cipa_memblock *pa_memblock_will_need(pa_memblock *b) { 72253a5a1b3Sopenharmony_ci void *p; 72353a5a1b3Sopenharmony_ci 72453a5a1b3Sopenharmony_ci pa_assert(b); 72553a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(b) > 0); 72653a5a1b3Sopenharmony_ci 72753a5a1b3Sopenharmony_ci p = pa_memblock_acquire(b); 72853a5a1b3Sopenharmony_ci pa_will_need(p, b->length); 72953a5a1b3Sopenharmony_ci pa_memblock_release(b); 73053a5a1b3Sopenharmony_ci 73153a5a1b3Sopenharmony_ci return b; 73253a5a1b3Sopenharmony_ci} 73353a5a1b3Sopenharmony_ci 73453a5a1b3Sopenharmony_ci/* Self-locked. This function is not multiple-caller safe */ 73553a5a1b3Sopenharmony_cistatic void memblock_replace_import(pa_memblock *b) { 73653a5a1b3Sopenharmony_ci pa_memimport_segment *segment; 73753a5a1b3Sopenharmony_ci pa_memimport *import; 73853a5a1b3Sopenharmony_ci 73953a5a1b3Sopenharmony_ci pa_assert(b); 74053a5a1b3Sopenharmony_ci pa_assert(b->type == PA_MEMBLOCK_IMPORTED); 74153a5a1b3Sopenharmony_ci 74253a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&b->pool->stat.n_imported) > 0); 74353a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&b->pool->stat.imported_size) >= (int) b->length); 74453a5a1b3Sopenharmony_ci pa_atomic_dec(&b->pool->stat.n_imported); 74553a5a1b3Sopenharmony_ci pa_atomic_sub(&b->pool->stat.imported_size, (int) b->length); 74653a5a1b3Sopenharmony_ci 74753a5a1b3Sopenharmony_ci pa_assert_se(segment = b->per_type.imported.segment); 74853a5a1b3Sopenharmony_ci pa_assert_se(import = segment->import); 74953a5a1b3Sopenharmony_ci 75053a5a1b3Sopenharmony_ci pa_mutex_lock(import->mutex); 75153a5a1b3Sopenharmony_ci 75253a5a1b3Sopenharmony_ci pa_assert_se(pa_hashmap_remove(import->blocks, PA_UINT32_TO_PTR(b->per_type.imported.id))); 75353a5a1b3Sopenharmony_ci 75453a5a1b3Sopenharmony_ci memblock_make_local(b); 75553a5a1b3Sopenharmony_ci 75653a5a1b3Sopenharmony_ci pa_assert(segment->n_blocks >= 1); 75753a5a1b3Sopenharmony_ci if (-- segment->n_blocks <= 0) 75853a5a1b3Sopenharmony_ci segment_detach(segment); 75953a5a1b3Sopenharmony_ci 76053a5a1b3Sopenharmony_ci pa_mutex_unlock(import->mutex); 76153a5a1b3Sopenharmony_ci} 76253a5a1b3Sopenharmony_ci 76353a5a1b3Sopenharmony_ci/*@per_client: This is a security measure. By default this should 76453a5a1b3Sopenharmony_ci * be set to true where the created mempool is never shared with more 76553a5a1b3Sopenharmony_ci * than one client in the system. Set this to false if a global 76653a5a1b3Sopenharmony_ci * mempool, shared with all existing and future clients, is required. 76753a5a1b3Sopenharmony_ci * 76853a5a1b3Sopenharmony_ci * NOTE-1: Do not create any further global mempools! They allow data 76953a5a1b3Sopenharmony_ci * leaks between clients and thus conflict with the xdg-app containers 77053a5a1b3Sopenharmony_ci * model. They also complicate the handling of memfd-based pools. 77153a5a1b3Sopenharmony_ci * 77253a5a1b3Sopenharmony_ci * NOTE-2: Almost all mempools are now created on a per client basis. 77353a5a1b3Sopenharmony_ci * The only exception is the pa_core's mempool which is still shared 77453a5a1b3Sopenharmony_ci * between all clients of the system. 77553a5a1b3Sopenharmony_ci * 77653a5a1b3Sopenharmony_ci * Beside security issues, special marking for global mempools is 77753a5a1b3Sopenharmony_ci * required for memfd communication. To avoid fd leaks, memfd pools 77853a5a1b3Sopenharmony_ci * are registered with the connection pstream to create an ID<->memfd 77953a5a1b3Sopenharmony_ci * mapping on both PA endpoints. Such memory regions are then always 78053a5a1b3Sopenharmony_ci * referenced by their IDs and never by their fds and thus their fds 78153a5a1b3Sopenharmony_ci * can be quickly closed later. 78253a5a1b3Sopenharmony_ci * 78353a5a1b3Sopenharmony_ci * Unfortunately this scheme cannot work with global pools since the 78453a5a1b3Sopenharmony_ci * ID registration mechanism needs to happen for each newly connected 78553a5a1b3Sopenharmony_ci * client, and thus the need for a more special handling. That is, 78653a5a1b3Sopenharmony_ci * for the pool's fd to be always open :-( 78753a5a1b3Sopenharmony_ci * 78853a5a1b3Sopenharmony_ci * TODO-1: Transform the global core mempool to a per-client one 78953a5a1b3Sopenharmony_ci * TODO-2: Remove global mempools support */ 79053a5a1b3Sopenharmony_cipa_mempool *pa_mempool_new(pa_mem_type_t type, size_t size, bool per_client) { 79153a5a1b3Sopenharmony_ci pa_log_debug("pa_mempool_new:type %d, size %zu, per_client %d,", type, size, per_client); 79253a5a1b3Sopenharmony_ci pa_mempool *p; 79353a5a1b3Sopenharmony_ci char t1[PA_BYTES_SNPRINT_MAX], t2[PA_BYTES_SNPRINT_MAX]; 79453a5a1b3Sopenharmony_ci const size_t page_size = pa_page_size(); 79553a5a1b3Sopenharmony_ci 79653a5a1b3Sopenharmony_ci p = pa_xnew0(pa_mempool, 1); 79753a5a1b3Sopenharmony_ci PA_REFCNT_INIT(p); 79853a5a1b3Sopenharmony_ci 79953a5a1b3Sopenharmony_ci p->block_size = PA_PAGE_ALIGN(PA_MEMPOOL_SLOT_SIZE); 80053a5a1b3Sopenharmony_ci if (p->block_size < page_size) 80153a5a1b3Sopenharmony_ci p->block_size = page_size; 80253a5a1b3Sopenharmony_ci 80353a5a1b3Sopenharmony_ci if (size <= 0) 80453a5a1b3Sopenharmony_ci p->n_blocks = PA_MEMPOOL_SLOTS_MAX; 80553a5a1b3Sopenharmony_ci else { 80653a5a1b3Sopenharmony_ci p->n_blocks = (unsigned) (size / p->block_size); 80753a5a1b3Sopenharmony_ci 80853a5a1b3Sopenharmony_ci if (p->n_blocks < 2) 80953a5a1b3Sopenharmony_ci p->n_blocks = 2; 81053a5a1b3Sopenharmony_ci } 81153a5a1b3Sopenharmony_ci 81253a5a1b3Sopenharmony_ci if (pa_shm_create_rw(&p->memory, type, p->n_blocks * p->block_size, 0700) < 0) { 81353a5a1b3Sopenharmony_ci pa_xfree(p); 81453a5a1b3Sopenharmony_ci return NULL; 81553a5a1b3Sopenharmony_ci } 81653a5a1b3Sopenharmony_ci 81753a5a1b3Sopenharmony_ci pa_log_debug("Using %s memory pool with %u slots of size %s each, total size is" 81853a5a1b3Sopenharmony_ci "%s, maximum usable slot size is %lu", 81953a5a1b3Sopenharmony_ci pa_mem_type_to_string(type), 82053a5a1b3Sopenharmony_ci p->n_blocks, 82153a5a1b3Sopenharmony_ci pa_bytes_snprint(t1, sizeof(t1), (unsigned) p->block_size), 82253a5a1b3Sopenharmony_ci pa_bytes_snprint(t2, sizeof(t2), (unsigned) (p->n_blocks * p->block_size)), 82353a5a1b3Sopenharmony_ci (unsigned long) pa_mempool_block_size_max(p)); 82453a5a1b3Sopenharmony_ci 82553a5a1b3Sopenharmony_ci p->global = !per_client; 82653a5a1b3Sopenharmony_ci 82753a5a1b3Sopenharmony_ci pa_atomic_store(&p->n_init, 0); 82853a5a1b3Sopenharmony_ci 82953a5a1b3Sopenharmony_ci PA_LLIST_HEAD_INIT(pa_memimport, p->imports); 83053a5a1b3Sopenharmony_ci PA_LLIST_HEAD_INIT(pa_memexport, p->exports); 83153a5a1b3Sopenharmony_ci 83253a5a1b3Sopenharmony_ci p->mutex = pa_mutex_new(true, true); 83353a5a1b3Sopenharmony_ci p->semaphore = pa_semaphore_new(0); 83453a5a1b3Sopenharmony_ci 83553a5a1b3Sopenharmony_ci p->free_slots = pa_flist_new(p->n_blocks); 83653a5a1b3Sopenharmony_ci 83753a5a1b3Sopenharmony_ci return p; 83853a5a1b3Sopenharmony_ci} 83953a5a1b3Sopenharmony_ci 84053a5a1b3Sopenharmony_cistatic void mempool_free(pa_mempool *p) { 84153a5a1b3Sopenharmony_ci pa_assert(p); 84253a5a1b3Sopenharmony_ci 84353a5a1b3Sopenharmony_ci pa_mutex_lock(p->mutex); 84453a5a1b3Sopenharmony_ci 84553a5a1b3Sopenharmony_ci while (p->imports) 84653a5a1b3Sopenharmony_ci pa_memimport_free(p->imports); 84753a5a1b3Sopenharmony_ci 84853a5a1b3Sopenharmony_ci while (p->exports) 84953a5a1b3Sopenharmony_ci pa_memexport_free(p->exports); 85053a5a1b3Sopenharmony_ci 85153a5a1b3Sopenharmony_ci pa_mutex_unlock(p->mutex); 85253a5a1b3Sopenharmony_ci 85353a5a1b3Sopenharmony_ci pa_flist_free(p->free_slots, NULL); 85453a5a1b3Sopenharmony_ci 85553a5a1b3Sopenharmony_ci if (pa_atomic_load(&p->stat.n_allocated) > 0) { 85653a5a1b3Sopenharmony_ci 85753a5a1b3Sopenharmony_ci /* Ouch, somebody is retaining a memory block reference! */ 85853a5a1b3Sopenharmony_ci 85953a5a1b3Sopenharmony_ci#ifdef DEBUG_REF 86053a5a1b3Sopenharmony_ci unsigned i; 86153a5a1b3Sopenharmony_ci pa_flist *list; 86253a5a1b3Sopenharmony_ci 86353a5a1b3Sopenharmony_ci /* Let's try to find at least one of those leaked memory blocks */ 86453a5a1b3Sopenharmony_ci 86553a5a1b3Sopenharmony_ci list = pa_flist_new(p->n_blocks); 86653a5a1b3Sopenharmony_ci 86753a5a1b3Sopenharmony_ci for (i = 0; i < (unsigned) pa_atomic_load(&p->n_init); i++) { 86853a5a1b3Sopenharmony_ci struct mempool_slot *slot; 86953a5a1b3Sopenharmony_ci pa_memblock *b, *k; 87053a5a1b3Sopenharmony_ci 87153a5a1b3Sopenharmony_ci slot = (struct mempool_slot*) ((uint8_t*) p->memory.ptr + (p->block_size * (size_t) i)); 87253a5a1b3Sopenharmony_ci b = mempool_slot_data(slot); 87353a5a1b3Sopenharmony_ci 87453a5a1b3Sopenharmony_ci while ((k = pa_flist_pop(p->free_slots))) { 87553a5a1b3Sopenharmony_ci while (pa_flist_push(list, k) < 0) 87653a5a1b3Sopenharmony_ci ; 87753a5a1b3Sopenharmony_ci 87853a5a1b3Sopenharmony_ci if (b == k) 87953a5a1b3Sopenharmony_ci break; 88053a5a1b3Sopenharmony_ci } 88153a5a1b3Sopenharmony_ci 88253a5a1b3Sopenharmony_ci if (!k) 88353a5a1b3Sopenharmony_ci pa_log_error("REF: Leaked memory block %p", b); 88453a5a1b3Sopenharmony_ci 88553a5a1b3Sopenharmony_ci while ((k = pa_flist_pop(list))) 88653a5a1b3Sopenharmony_ci while (pa_flist_push(p->free_slots, k) < 0) 88753a5a1b3Sopenharmony_ci ; 88853a5a1b3Sopenharmony_ci } 88953a5a1b3Sopenharmony_ci 89053a5a1b3Sopenharmony_ci pa_flist_free(list, NULL); 89153a5a1b3Sopenharmony_ci 89253a5a1b3Sopenharmony_ci#endif 89353a5a1b3Sopenharmony_ci 89453a5a1b3Sopenharmony_ci pa_log_error("Memory pool destroyed but not all memory blocks freed! %u remain.", 89553a5a1b3Sopenharmony_ci pa_atomic_load(&p->stat.n_allocated)); 89653a5a1b3Sopenharmony_ci 89753a5a1b3Sopenharmony_ci/* PA_DEBUG_TRAP; */ 89853a5a1b3Sopenharmony_ci } 89953a5a1b3Sopenharmony_ci 90053a5a1b3Sopenharmony_ci pa_shm_free(&p->memory); 90153a5a1b3Sopenharmony_ci 90253a5a1b3Sopenharmony_ci pa_mutex_free(p->mutex); 90353a5a1b3Sopenharmony_ci pa_semaphore_free(p->semaphore); 90453a5a1b3Sopenharmony_ci 90553a5a1b3Sopenharmony_ci pa_xfree(p); 90653a5a1b3Sopenharmony_ci} 90753a5a1b3Sopenharmony_ci 90853a5a1b3Sopenharmony_ci/* No lock necessary */ 90953a5a1b3Sopenharmony_ciconst pa_mempool_stat* pa_mempool_get_stat(pa_mempool *p) { 91053a5a1b3Sopenharmony_ci pa_assert(p); 91153a5a1b3Sopenharmony_ci 91253a5a1b3Sopenharmony_ci return &p->stat; 91353a5a1b3Sopenharmony_ci} 91453a5a1b3Sopenharmony_ci 91553a5a1b3Sopenharmony_ci/* No lock necessary */ 91653a5a1b3Sopenharmony_cisize_t pa_mempool_block_size_max(pa_mempool *p) { 91753a5a1b3Sopenharmony_ci pa_assert(p); 91853a5a1b3Sopenharmony_ci 91953a5a1b3Sopenharmony_ci return p->block_size - PA_ALIGN(sizeof(pa_memblock)); 92053a5a1b3Sopenharmony_ci} 92153a5a1b3Sopenharmony_ci 92253a5a1b3Sopenharmony_ci/* No lock necessary */ 92353a5a1b3Sopenharmony_civoid pa_mempool_vacuum(pa_mempool *p) { 92453a5a1b3Sopenharmony_ci struct mempool_slot *slot; 92553a5a1b3Sopenharmony_ci pa_flist *list; 92653a5a1b3Sopenharmony_ci 92753a5a1b3Sopenharmony_ci pa_assert(p); 92853a5a1b3Sopenharmony_ci 92953a5a1b3Sopenharmony_ci list = pa_flist_new(p->n_blocks); 93053a5a1b3Sopenharmony_ci 93153a5a1b3Sopenharmony_ci while ((slot = pa_flist_pop(p->free_slots))) 93253a5a1b3Sopenharmony_ci while (pa_flist_push(list, slot) < 0) 93353a5a1b3Sopenharmony_ci ; 93453a5a1b3Sopenharmony_ci 93553a5a1b3Sopenharmony_ci while ((slot = pa_flist_pop(list))) { 93653a5a1b3Sopenharmony_ci pa_shm_punch(&p->memory, (size_t) ((uint8_t*) slot - (uint8_t*) p->memory.ptr), p->block_size); 93753a5a1b3Sopenharmony_ci 93853a5a1b3Sopenharmony_ci while (pa_flist_push(p->free_slots, slot)) 93953a5a1b3Sopenharmony_ci ; 94053a5a1b3Sopenharmony_ci } 94153a5a1b3Sopenharmony_ci 94253a5a1b3Sopenharmony_ci pa_flist_free(list, NULL); 94353a5a1b3Sopenharmony_ci} 94453a5a1b3Sopenharmony_ci 94553a5a1b3Sopenharmony_ci/* No lock necessary */ 94653a5a1b3Sopenharmony_cibool pa_mempool_is_shared(pa_mempool *p) { 94753a5a1b3Sopenharmony_ci pa_assert(p); 94853a5a1b3Sopenharmony_ci 94953a5a1b3Sopenharmony_ci return pa_mem_type_is_shared(p->memory.type); 95053a5a1b3Sopenharmony_ci} 95153a5a1b3Sopenharmony_ci 95253a5a1b3Sopenharmony_ci/* No lock necessary */ 95353a5a1b3Sopenharmony_cibool pa_mempool_is_memfd_backed(const pa_mempool *p) { 95453a5a1b3Sopenharmony_ci pa_assert(p); 95553a5a1b3Sopenharmony_ci 95653a5a1b3Sopenharmony_ci return (p->memory.type == PA_MEM_TYPE_SHARED_MEMFD); 95753a5a1b3Sopenharmony_ci} 95853a5a1b3Sopenharmony_ci 95953a5a1b3Sopenharmony_ci/* No lock necessary */ 96053a5a1b3Sopenharmony_ciint pa_mempool_get_shm_id(pa_mempool *p, uint32_t *id) { 96153a5a1b3Sopenharmony_ci pa_assert(p); 96253a5a1b3Sopenharmony_ci 96353a5a1b3Sopenharmony_ci if (!pa_mempool_is_shared(p)) 96453a5a1b3Sopenharmony_ci return -1; 96553a5a1b3Sopenharmony_ci 96653a5a1b3Sopenharmony_ci *id = p->memory.id; 96753a5a1b3Sopenharmony_ci 96853a5a1b3Sopenharmony_ci return 0; 96953a5a1b3Sopenharmony_ci} 97053a5a1b3Sopenharmony_ci 97153a5a1b3Sopenharmony_cipa_mempool* pa_mempool_ref(pa_mempool *p) { 97253a5a1b3Sopenharmony_ci pa_assert(p); 97353a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(p) > 0); 97453a5a1b3Sopenharmony_ci 97553a5a1b3Sopenharmony_ci PA_REFCNT_INC(p); 97653a5a1b3Sopenharmony_ci return p; 97753a5a1b3Sopenharmony_ci} 97853a5a1b3Sopenharmony_ci 97953a5a1b3Sopenharmony_civoid pa_mempool_unref(pa_mempool *p) { 98053a5a1b3Sopenharmony_ci pa_assert(p); 98153a5a1b3Sopenharmony_ci pa_assert(PA_REFCNT_VALUE(p) > 0); 98253a5a1b3Sopenharmony_ci 98353a5a1b3Sopenharmony_ci if (PA_REFCNT_DEC(p) <= 0) 98453a5a1b3Sopenharmony_ci mempool_free(p); 98553a5a1b3Sopenharmony_ci} 98653a5a1b3Sopenharmony_ci 98753a5a1b3Sopenharmony_ci/* No lock necessary 98853a5a1b3Sopenharmony_ci * Check pa_mempool_new() for per-client vs. global mempools */ 98953a5a1b3Sopenharmony_cibool pa_mempool_is_global(pa_mempool *p) { 99053a5a1b3Sopenharmony_ci pa_assert(p); 99153a5a1b3Sopenharmony_ci 99253a5a1b3Sopenharmony_ci return p->global; 99353a5a1b3Sopenharmony_ci} 99453a5a1b3Sopenharmony_ci 99553a5a1b3Sopenharmony_ci/* No lock necessary 99653a5a1b3Sopenharmony_ci * Check pa_mempool_new() for per-client vs. global mempools */ 99753a5a1b3Sopenharmony_cibool pa_mempool_is_per_client(pa_mempool *p) { 99853a5a1b3Sopenharmony_ci return !pa_mempool_is_global(p); 99953a5a1b3Sopenharmony_ci} 100053a5a1b3Sopenharmony_ci 100153a5a1b3Sopenharmony_ci/* Self-locked 100253a5a1b3Sopenharmony_ci * 100353a5a1b3Sopenharmony_ci * This is only for per-client mempools! 100453a5a1b3Sopenharmony_ci * 100553a5a1b3Sopenharmony_ci * After this method's return, the caller owns the file descriptor 100653a5a1b3Sopenharmony_ci * and is responsible for closing it in the appropriate time. This 100753a5a1b3Sopenharmony_ci * should only be called once during during a mempool's lifetime. 100853a5a1b3Sopenharmony_ci * 100953a5a1b3Sopenharmony_ci * Check pa_shm->fd and pa_mempool_new() for further context. */ 101053a5a1b3Sopenharmony_ciint pa_mempool_take_memfd_fd(pa_mempool *p) { 101153a5a1b3Sopenharmony_ci int memfd_fd; 101253a5a1b3Sopenharmony_ci 101353a5a1b3Sopenharmony_ci pa_assert(p); 101453a5a1b3Sopenharmony_ci pa_assert(pa_mempool_is_shared(p)); 101553a5a1b3Sopenharmony_ci pa_assert(pa_mempool_is_memfd_backed(p)); 101653a5a1b3Sopenharmony_ci pa_assert(pa_mempool_is_per_client(p)); 101753a5a1b3Sopenharmony_ci 101853a5a1b3Sopenharmony_ci pa_mutex_lock(p->mutex); 101953a5a1b3Sopenharmony_ci 102053a5a1b3Sopenharmony_ci memfd_fd = p->memory.fd; 102153a5a1b3Sopenharmony_ci p->memory.fd = -1; 102253a5a1b3Sopenharmony_ci 102353a5a1b3Sopenharmony_ci pa_mutex_unlock(p->mutex); 102453a5a1b3Sopenharmony_ci 102553a5a1b3Sopenharmony_ci pa_assert(memfd_fd != -1); 102653a5a1b3Sopenharmony_ci return memfd_fd; 102753a5a1b3Sopenharmony_ci} 102853a5a1b3Sopenharmony_ci 102953a5a1b3Sopenharmony_ci/* No lock necessary 103053a5a1b3Sopenharmony_ci * 103153a5a1b3Sopenharmony_ci * This is only for global mempools! 103253a5a1b3Sopenharmony_ci * 103353a5a1b3Sopenharmony_ci * Global mempools have their memfd descriptor always open. DO NOT 103453a5a1b3Sopenharmony_ci * close the returned descriptor by your own. 103553a5a1b3Sopenharmony_ci * 103653a5a1b3Sopenharmony_ci * Check pa_mempool_new() for further context. */ 103753a5a1b3Sopenharmony_ciint pa_mempool_get_memfd_fd(pa_mempool *p) { 103853a5a1b3Sopenharmony_ci int memfd_fd; 103953a5a1b3Sopenharmony_ci 104053a5a1b3Sopenharmony_ci pa_assert(p); 104153a5a1b3Sopenharmony_ci pa_assert(pa_mempool_is_shared(p)); 104253a5a1b3Sopenharmony_ci pa_assert(pa_mempool_is_memfd_backed(p)); 104353a5a1b3Sopenharmony_ci pa_assert(pa_mempool_is_global(p)); 104453a5a1b3Sopenharmony_ci 104553a5a1b3Sopenharmony_ci memfd_fd = p->memory.fd; 104653a5a1b3Sopenharmony_ci pa_assert(memfd_fd != -1); 104753a5a1b3Sopenharmony_ci 104853a5a1b3Sopenharmony_ci return memfd_fd; 104953a5a1b3Sopenharmony_ci} 105053a5a1b3Sopenharmony_ci 105153a5a1b3Sopenharmony_ci/* For receiving blocks from other nodes */ 105253a5a1b3Sopenharmony_cipa_memimport* pa_memimport_new(pa_mempool *p, pa_memimport_release_cb_t cb, void *userdata) { 105353a5a1b3Sopenharmony_ci pa_memimport *i; 105453a5a1b3Sopenharmony_ci 105553a5a1b3Sopenharmony_ci pa_assert(p); 105653a5a1b3Sopenharmony_ci pa_assert(cb); 105753a5a1b3Sopenharmony_ci 105853a5a1b3Sopenharmony_ci i = pa_xnew(pa_memimport, 1); 105953a5a1b3Sopenharmony_ci i->mutex = pa_mutex_new(true, true); 106053a5a1b3Sopenharmony_ci i->pool = p; 106153a5a1b3Sopenharmony_ci pa_mempool_ref(i->pool); 106253a5a1b3Sopenharmony_ci i->segments = pa_hashmap_new(NULL, NULL); 106353a5a1b3Sopenharmony_ci i->blocks = pa_hashmap_new(NULL, NULL); 106453a5a1b3Sopenharmony_ci i->release_cb = cb; 106553a5a1b3Sopenharmony_ci i->userdata = userdata; 106653a5a1b3Sopenharmony_ci 106753a5a1b3Sopenharmony_ci pa_mutex_lock(p->mutex); 106853a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(pa_memimport, p->imports, i); 106953a5a1b3Sopenharmony_ci pa_mutex_unlock(p->mutex); 107053a5a1b3Sopenharmony_ci 107153a5a1b3Sopenharmony_ci return i; 107253a5a1b3Sopenharmony_ci} 107353a5a1b3Sopenharmony_ci 107453a5a1b3Sopenharmony_cistatic void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i); 107553a5a1b3Sopenharmony_ci 107653a5a1b3Sopenharmony_ci/* Should be called locked 107753a5a1b3Sopenharmony_ci * Caller owns passed @memfd_fd and must close it down when appropriate. */ 107853a5a1b3Sopenharmony_cistatic pa_memimport_segment* segment_attach(pa_memimport *i, pa_mem_type_t type, uint32_t shm_id, 107953a5a1b3Sopenharmony_ci int memfd_fd, bool writable) { 108053a5a1b3Sopenharmony_ci pa_memimport_segment* seg; 108153a5a1b3Sopenharmony_ci pa_assert(pa_mem_type_is_shared(type)); 108253a5a1b3Sopenharmony_ci 108353a5a1b3Sopenharmony_ci if (pa_hashmap_size(i->segments) >= PA_MEMIMPORT_SEGMENTS_MAX) 108453a5a1b3Sopenharmony_ci return NULL; 108553a5a1b3Sopenharmony_ci 108653a5a1b3Sopenharmony_ci seg = pa_xnew0(pa_memimport_segment, 1); 108753a5a1b3Sopenharmony_ci 108853a5a1b3Sopenharmony_ci if (pa_shm_attach(&seg->memory, type, shm_id, memfd_fd, writable) < 0) { 108953a5a1b3Sopenharmony_ci pa_xfree(seg); 109053a5a1b3Sopenharmony_ci return NULL; 109153a5a1b3Sopenharmony_ci } 109253a5a1b3Sopenharmony_ci 109353a5a1b3Sopenharmony_ci seg->writable = writable; 109453a5a1b3Sopenharmony_ci seg->import = i; 109553a5a1b3Sopenharmony_ci seg->trap = pa_memtrap_add(seg->memory.ptr, seg->memory.size); 109653a5a1b3Sopenharmony_ci 109753a5a1b3Sopenharmony_ci pa_hashmap_put(i->segments, PA_UINT32_TO_PTR(seg->memory.id), seg); 109853a5a1b3Sopenharmony_ci return seg; 109953a5a1b3Sopenharmony_ci} 110053a5a1b3Sopenharmony_ci 110153a5a1b3Sopenharmony_ci/* Should be called locked */ 110253a5a1b3Sopenharmony_cistatic void segment_detach(pa_memimport_segment *seg) { 110353a5a1b3Sopenharmony_ci pa_assert(seg); 110453a5a1b3Sopenharmony_ci pa_assert(seg->n_blocks == (segment_is_permanent(seg) ? 1u : 0u)); 110553a5a1b3Sopenharmony_ci 110653a5a1b3Sopenharmony_ci pa_hashmap_remove(seg->import->segments, PA_UINT32_TO_PTR(seg->memory.id)); 110753a5a1b3Sopenharmony_ci pa_shm_free(&seg->memory); 110853a5a1b3Sopenharmony_ci 110953a5a1b3Sopenharmony_ci if (seg->trap) 111053a5a1b3Sopenharmony_ci pa_memtrap_remove(seg->trap); 111153a5a1b3Sopenharmony_ci 111253a5a1b3Sopenharmony_ci pa_xfree(seg); 111353a5a1b3Sopenharmony_ci} 111453a5a1b3Sopenharmony_ci 111553a5a1b3Sopenharmony_ci/* Self-locked. Not multiple-caller safe */ 111653a5a1b3Sopenharmony_civoid pa_memimport_free(pa_memimport *i) { 111753a5a1b3Sopenharmony_ci pa_memexport *e; 111853a5a1b3Sopenharmony_ci pa_memblock *b; 111953a5a1b3Sopenharmony_ci pa_memimport_segment *seg; 112053a5a1b3Sopenharmony_ci void *state = NULL; 112153a5a1b3Sopenharmony_ci 112253a5a1b3Sopenharmony_ci pa_assert(i); 112353a5a1b3Sopenharmony_ci 112453a5a1b3Sopenharmony_ci pa_mutex_lock(i->mutex); 112553a5a1b3Sopenharmony_ci 112653a5a1b3Sopenharmony_ci while ((b = pa_hashmap_first(i->blocks))) 112753a5a1b3Sopenharmony_ci memblock_replace_import(b); 112853a5a1b3Sopenharmony_ci 112953a5a1b3Sopenharmony_ci /* Permanent segments exist for the lifetime of the memimport. Now 113053a5a1b3Sopenharmony_ci * that we're freeing the memimport itself, clear them all up. 113153a5a1b3Sopenharmony_ci * 113253a5a1b3Sopenharmony_ci * Careful! segment_detach() internally removes itself from the 113353a5a1b3Sopenharmony_ci * memimport's hash; the same hash we're now using for iteration. */ 113453a5a1b3Sopenharmony_ci PA_HASHMAP_FOREACH(seg, i->segments, state) { 113553a5a1b3Sopenharmony_ci if (segment_is_permanent(seg)) 113653a5a1b3Sopenharmony_ci segment_detach(seg); 113753a5a1b3Sopenharmony_ci } 113853a5a1b3Sopenharmony_ci pa_assert(pa_hashmap_size(i->segments) == 0); 113953a5a1b3Sopenharmony_ci 114053a5a1b3Sopenharmony_ci pa_mutex_unlock(i->mutex); 114153a5a1b3Sopenharmony_ci 114253a5a1b3Sopenharmony_ci pa_mutex_lock(i->pool->mutex); 114353a5a1b3Sopenharmony_ci 114453a5a1b3Sopenharmony_ci /* If we've exported this block further we need to revoke that export */ 114553a5a1b3Sopenharmony_ci for (e = i->pool->exports; e; e = e->next) 114653a5a1b3Sopenharmony_ci memexport_revoke_blocks(e, i); 114753a5a1b3Sopenharmony_ci 114853a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(pa_memimport, i->pool->imports, i); 114953a5a1b3Sopenharmony_ci 115053a5a1b3Sopenharmony_ci pa_mutex_unlock(i->pool->mutex); 115153a5a1b3Sopenharmony_ci 115253a5a1b3Sopenharmony_ci pa_mempool_unref(i->pool); 115353a5a1b3Sopenharmony_ci pa_hashmap_free(i->blocks); 115453a5a1b3Sopenharmony_ci pa_hashmap_free(i->segments); 115553a5a1b3Sopenharmony_ci 115653a5a1b3Sopenharmony_ci pa_mutex_free(i->mutex); 115753a5a1b3Sopenharmony_ci 115853a5a1b3Sopenharmony_ci pa_xfree(i); 115953a5a1b3Sopenharmony_ci} 116053a5a1b3Sopenharmony_ci 116153a5a1b3Sopenharmony_ci/* Create a new memimport's memfd segment entry, with passed SHM ID 116253a5a1b3Sopenharmony_ci * as key and the newly-created segment (with its mmap()-ed memfd 116353a5a1b3Sopenharmony_ci * memory region) as its value. 116453a5a1b3Sopenharmony_ci * 116553a5a1b3Sopenharmony_ci * Note! check comments at 'pa_shm->fd', 'segment_is_permanent()', 116653a5a1b3Sopenharmony_ci * and 'pa_pstream_register_memfd_mempool()' for further details. 116753a5a1b3Sopenharmony_ci * 116853a5a1b3Sopenharmony_ci * Caller owns passed @memfd_fd and must close it down when appropriate. */ 116953a5a1b3Sopenharmony_ciint pa_memimport_attach_memfd(pa_memimport *i, uint32_t shm_id, int memfd_fd, bool writable) { 117053a5a1b3Sopenharmony_ci pa_memimport_segment *seg; 117153a5a1b3Sopenharmony_ci int ret = -1; 117253a5a1b3Sopenharmony_ci 117353a5a1b3Sopenharmony_ci pa_assert(i); 117453a5a1b3Sopenharmony_ci pa_assert(memfd_fd != -1); 117553a5a1b3Sopenharmony_ci 117653a5a1b3Sopenharmony_ci pa_mutex_lock(i->mutex); 117753a5a1b3Sopenharmony_ci 117853a5a1b3Sopenharmony_ci if (!(seg = segment_attach(i, PA_MEM_TYPE_SHARED_MEMFD, shm_id, memfd_fd, writable))) 117953a5a1b3Sopenharmony_ci goto finish; 118053a5a1b3Sopenharmony_ci 118153a5a1b3Sopenharmony_ci /* n_blocks acts as a segment reference count. To avoid the segment 118253a5a1b3Sopenharmony_ci * being deleted when receiving silent memchunks, etc., mark our 118353a5a1b3Sopenharmony_ci * permanent presence by incrementing that refcount. */ 118453a5a1b3Sopenharmony_ci seg->n_blocks++; 118553a5a1b3Sopenharmony_ci 118653a5a1b3Sopenharmony_ci pa_assert(segment_is_permanent(seg)); 118753a5a1b3Sopenharmony_ci ret = 0; 118853a5a1b3Sopenharmony_ci 118953a5a1b3Sopenharmony_cifinish: 119053a5a1b3Sopenharmony_ci pa_mutex_unlock(i->mutex); 119153a5a1b3Sopenharmony_ci return ret; 119253a5a1b3Sopenharmony_ci} 119353a5a1b3Sopenharmony_ci 119453a5a1b3Sopenharmony_ci/* Self-locked */ 119553a5a1b3Sopenharmony_cipa_memblock* pa_memimport_get(pa_memimport *i, pa_mem_type_t type, uint32_t block_id, uint32_t shm_id, 119653a5a1b3Sopenharmony_ci size_t offset, size_t size, bool writable) { 119753a5a1b3Sopenharmony_ci pa_memblock *b = NULL; 119853a5a1b3Sopenharmony_ci pa_memimport_segment *seg; 119953a5a1b3Sopenharmony_ci 120053a5a1b3Sopenharmony_ci pa_assert(i); 120153a5a1b3Sopenharmony_ci pa_assert(pa_mem_type_is_shared(type)); 120253a5a1b3Sopenharmony_ci 120353a5a1b3Sopenharmony_ci pa_mutex_lock(i->mutex); 120453a5a1b3Sopenharmony_ci 120553a5a1b3Sopenharmony_ci if ((b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(block_id)))) { 120653a5a1b3Sopenharmony_ci pa_memblock_ref(b); 120753a5a1b3Sopenharmony_ci goto finish; 120853a5a1b3Sopenharmony_ci } 120953a5a1b3Sopenharmony_ci 121053a5a1b3Sopenharmony_ci if (pa_hashmap_size(i->blocks) >= PA_MEMIMPORT_SLOTS_MAX) 121153a5a1b3Sopenharmony_ci goto finish; 121253a5a1b3Sopenharmony_ci 121353a5a1b3Sopenharmony_ci if (!(seg = pa_hashmap_get(i->segments, PA_UINT32_TO_PTR(shm_id)))) { 121453a5a1b3Sopenharmony_ci if (type == PA_MEM_TYPE_SHARED_MEMFD) { 121553a5a1b3Sopenharmony_ci pa_log_error("Bailing out! No cached memimport segment for memfd ID %u", shm_id); 121653a5a1b3Sopenharmony_ci pa_log_error("Did the other PA endpoint forget registering its memfd pool?"); 121753a5a1b3Sopenharmony_ci goto finish; 121853a5a1b3Sopenharmony_ci } 121953a5a1b3Sopenharmony_ci 122053a5a1b3Sopenharmony_ci pa_assert(type == PA_MEM_TYPE_SHARED_POSIX); 122153a5a1b3Sopenharmony_ci if (!(seg = segment_attach(i, type, shm_id, -1, writable))) 122253a5a1b3Sopenharmony_ci goto finish; 122353a5a1b3Sopenharmony_ci } 122453a5a1b3Sopenharmony_ci 122553a5a1b3Sopenharmony_ci if (writable && !seg->writable) { 122653a5a1b3Sopenharmony_ci pa_log_error("Cannot import cached segment in write mode - previously mapped as read-only"); 122753a5a1b3Sopenharmony_ci goto finish; 122853a5a1b3Sopenharmony_ci } 122953a5a1b3Sopenharmony_ci 123053a5a1b3Sopenharmony_ci if (offset+size > seg->memory.size) 123153a5a1b3Sopenharmony_ci goto finish; 123253a5a1b3Sopenharmony_ci 123353a5a1b3Sopenharmony_ci if (!(b = pa_flist_pop(PA_STATIC_FLIST_GET(unused_memblocks)))) 123453a5a1b3Sopenharmony_ci b = pa_xnew(pa_memblock, 1); 123553a5a1b3Sopenharmony_ci 123653a5a1b3Sopenharmony_ci PA_REFCNT_INIT(b); 123753a5a1b3Sopenharmony_ci b->pool = i->pool; 123853a5a1b3Sopenharmony_ci pa_mempool_ref(b->pool); 123953a5a1b3Sopenharmony_ci b->type = PA_MEMBLOCK_IMPORTED; 124053a5a1b3Sopenharmony_ci b->read_only = !writable; 124153a5a1b3Sopenharmony_ci b->is_silence = false; 124253a5a1b3Sopenharmony_ci pa_atomic_ptr_store(&b->data, (uint8_t*) seg->memory.ptr + offset); 124353a5a1b3Sopenharmony_ci b->length = size; 124453a5a1b3Sopenharmony_ci pa_atomic_store(&b->n_acquired, 0); 124553a5a1b3Sopenharmony_ci pa_atomic_store(&b->please_signal, 0); 124653a5a1b3Sopenharmony_ci b->per_type.imported.id = block_id; 124753a5a1b3Sopenharmony_ci b->per_type.imported.segment = seg; 124853a5a1b3Sopenharmony_ci 124953a5a1b3Sopenharmony_ci pa_hashmap_put(i->blocks, PA_UINT32_TO_PTR(block_id), b); 125053a5a1b3Sopenharmony_ci 125153a5a1b3Sopenharmony_ci seg->n_blocks++; 125253a5a1b3Sopenharmony_ci 125353a5a1b3Sopenharmony_ci stat_add(b); 125453a5a1b3Sopenharmony_ci 125553a5a1b3Sopenharmony_cifinish: 125653a5a1b3Sopenharmony_ci pa_mutex_unlock(i->mutex); 125753a5a1b3Sopenharmony_ci 125853a5a1b3Sopenharmony_ci return b; 125953a5a1b3Sopenharmony_ci} 126053a5a1b3Sopenharmony_ci 126153a5a1b3Sopenharmony_ciint pa_memimport_process_revoke(pa_memimport *i, uint32_t id) { 126253a5a1b3Sopenharmony_ci pa_memblock *b; 126353a5a1b3Sopenharmony_ci int ret = 0; 126453a5a1b3Sopenharmony_ci pa_assert(i); 126553a5a1b3Sopenharmony_ci 126653a5a1b3Sopenharmony_ci pa_mutex_lock(i->mutex); 126753a5a1b3Sopenharmony_ci 126853a5a1b3Sopenharmony_ci if (!(b = pa_hashmap_get(i->blocks, PA_UINT32_TO_PTR(id)))) { 126953a5a1b3Sopenharmony_ci ret = -1; 127053a5a1b3Sopenharmony_ci goto finish; 127153a5a1b3Sopenharmony_ci } 127253a5a1b3Sopenharmony_ci 127353a5a1b3Sopenharmony_ci memblock_replace_import(b); 127453a5a1b3Sopenharmony_ci 127553a5a1b3Sopenharmony_cifinish: 127653a5a1b3Sopenharmony_ci pa_mutex_unlock(i->mutex); 127753a5a1b3Sopenharmony_ci 127853a5a1b3Sopenharmony_ci return ret; 127953a5a1b3Sopenharmony_ci} 128053a5a1b3Sopenharmony_ci 128153a5a1b3Sopenharmony_ci/* For sending blocks to other nodes */ 128253a5a1b3Sopenharmony_cipa_memexport* pa_memexport_new(pa_mempool *p, pa_memexport_revoke_cb_t cb, void *userdata) { 128353a5a1b3Sopenharmony_ci pa_memexport *e; 128453a5a1b3Sopenharmony_ci 128553a5a1b3Sopenharmony_ci static pa_atomic_t export_baseidx = PA_ATOMIC_INIT(0); 128653a5a1b3Sopenharmony_ci 128753a5a1b3Sopenharmony_ci pa_assert(p); 128853a5a1b3Sopenharmony_ci pa_assert(cb); 128953a5a1b3Sopenharmony_ci 129053a5a1b3Sopenharmony_ci if (!pa_mempool_is_shared(p)) 129153a5a1b3Sopenharmony_ci return NULL; 129253a5a1b3Sopenharmony_ci 129353a5a1b3Sopenharmony_ci e = pa_xnew(pa_memexport, 1); 129453a5a1b3Sopenharmony_ci e->mutex = pa_mutex_new(true, true); 129553a5a1b3Sopenharmony_ci e->pool = p; 129653a5a1b3Sopenharmony_ci pa_mempool_ref(e->pool); 129753a5a1b3Sopenharmony_ci PA_LLIST_HEAD_INIT(struct memexport_slot, e->free_slots); 129853a5a1b3Sopenharmony_ci PA_LLIST_HEAD_INIT(struct memexport_slot, e->used_slots); 129953a5a1b3Sopenharmony_ci e->n_init = 0; 130053a5a1b3Sopenharmony_ci e->revoke_cb = cb; 130153a5a1b3Sopenharmony_ci e->userdata = userdata; 130253a5a1b3Sopenharmony_ci 130353a5a1b3Sopenharmony_ci pa_mutex_lock(p->mutex); 130453a5a1b3Sopenharmony_ci 130553a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(pa_memexport, p->exports, e); 130653a5a1b3Sopenharmony_ci e->baseidx = (uint32_t) pa_atomic_add(&export_baseidx, PA_MEMEXPORT_SLOTS_MAX); 130753a5a1b3Sopenharmony_ci 130853a5a1b3Sopenharmony_ci pa_mutex_unlock(p->mutex); 130953a5a1b3Sopenharmony_ci return e; 131053a5a1b3Sopenharmony_ci} 131153a5a1b3Sopenharmony_ci 131253a5a1b3Sopenharmony_civoid pa_memexport_free(pa_memexport *e) { 131353a5a1b3Sopenharmony_ci pa_assert(e); 131453a5a1b3Sopenharmony_ci 131553a5a1b3Sopenharmony_ci pa_mutex_lock(e->mutex); 131653a5a1b3Sopenharmony_ci while (e->used_slots) 131753a5a1b3Sopenharmony_ci pa_memexport_process_release(e, (uint32_t) (e->used_slots - e->slots + e->baseidx)); 131853a5a1b3Sopenharmony_ci pa_mutex_unlock(e->mutex); 131953a5a1b3Sopenharmony_ci 132053a5a1b3Sopenharmony_ci pa_mutex_lock(e->pool->mutex); 132153a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(pa_memexport, e->pool->exports, e); 132253a5a1b3Sopenharmony_ci pa_mutex_unlock(e->pool->mutex); 132353a5a1b3Sopenharmony_ci 132453a5a1b3Sopenharmony_ci pa_mempool_unref(e->pool); 132553a5a1b3Sopenharmony_ci pa_mutex_free(e->mutex); 132653a5a1b3Sopenharmony_ci pa_xfree(e); 132753a5a1b3Sopenharmony_ci} 132853a5a1b3Sopenharmony_ci 132953a5a1b3Sopenharmony_ci/* Self-locked */ 133053a5a1b3Sopenharmony_ciint pa_memexport_process_release(pa_memexport *e, uint32_t id) { 133153a5a1b3Sopenharmony_ci pa_memblock *b; 133253a5a1b3Sopenharmony_ci 133353a5a1b3Sopenharmony_ci pa_assert(e); 133453a5a1b3Sopenharmony_ci 133553a5a1b3Sopenharmony_ci pa_mutex_lock(e->mutex); 133653a5a1b3Sopenharmony_ci 133753a5a1b3Sopenharmony_ci if (id < e->baseidx) 133853a5a1b3Sopenharmony_ci goto fail; 133953a5a1b3Sopenharmony_ci id -= e->baseidx; 134053a5a1b3Sopenharmony_ci 134153a5a1b3Sopenharmony_ci if (id >= e->n_init) 134253a5a1b3Sopenharmony_ci goto fail; 134353a5a1b3Sopenharmony_ci 134453a5a1b3Sopenharmony_ci if (!e->slots[id].block) 134553a5a1b3Sopenharmony_ci goto fail; 134653a5a1b3Sopenharmony_ci 134753a5a1b3Sopenharmony_ci b = e->slots[id].block; 134853a5a1b3Sopenharmony_ci e->slots[id].block = NULL; 134953a5a1b3Sopenharmony_ci 135053a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(struct memexport_slot, e->used_slots, &e->slots[id]); 135153a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(struct memexport_slot, e->free_slots, &e->slots[id]); 135253a5a1b3Sopenharmony_ci 135353a5a1b3Sopenharmony_ci pa_mutex_unlock(e->mutex); 135453a5a1b3Sopenharmony_ci 135553a5a1b3Sopenharmony_ci/* pa_log("Processing release for %u", id); */ 135653a5a1b3Sopenharmony_ci 135753a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&e->pool->stat.n_exported) > 0); 135853a5a1b3Sopenharmony_ci pa_assert(pa_atomic_load(&e->pool->stat.exported_size) >= (int) b->length); 135953a5a1b3Sopenharmony_ci 136053a5a1b3Sopenharmony_ci pa_atomic_dec(&e->pool->stat.n_exported); 136153a5a1b3Sopenharmony_ci pa_atomic_sub(&e->pool->stat.exported_size, (int) b->length); 136253a5a1b3Sopenharmony_ci 136353a5a1b3Sopenharmony_ci pa_memblock_unref(b); 136453a5a1b3Sopenharmony_ci 136553a5a1b3Sopenharmony_ci return 0; 136653a5a1b3Sopenharmony_ci 136753a5a1b3Sopenharmony_cifail: 136853a5a1b3Sopenharmony_ci pa_mutex_unlock(e->mutex); 136953a5a1b3Sopenharmony_ci 137053a5a1b3Sopenharmony_ci return -1; 137153a5a1b3Sopenharmony_ci} 137253a5a1b3Sopenharmony_ci 137353a5a1b3Sopenharmony_ci/* Self-locked */ 137453a5a1b3Sopenharmony_cistatic void memexport_revoke_blocks(pa_memexport *e, pa_memimport *i) { 137553a5a1b3Sopenharmony_ci struct memexport_slot *slot, *next; 137653a5a1b3Sopenharmony_ci pa_assert(e); 137753a5a1b3Sopenharmony_ci pa_assert(i); 137853a5a1b3Sopenharmony_ci 137953a5a1b3Sopenharmony_ci pa_mutex_lock(e->mutex); 138053a5a1b3Sopenharmony_ci 138153a5a1b3Sopenharmony_ci for (slot = e->used_slots; slot; slot = next) { 138253a5a1b3Sopenharmony_ci uint32_t idx; 138353a5a1b3Sopenharmony_ci next = slot->next; 138453a5a1b3Sopenharmony_ci 138553a5a1b3Sopenharmony_ci if (slot->block->type != PA_MEMBLOCK_IMPORTED || 138653a5a1b3Sopenharmony_ci slot->block->per_type.imported.segment->import != i) 138753a5a1b3Sopenharmony_ci continue; 138853a5a1b3Sopenharmony_ci 138953a5a1b3Sopenharmony_ci idx = (uint32_t) (slot - e->slots + e->baseidx); 139053a5a1b3Sopenharmony_ci e->revoke_cb(e, idx, e->userdata); 139153a5a1b3Sopenharmony_ci pa_memexport_process_release(e, idx); 139253a5a1b3Sopenharmony_ci } 139353a5a1b3Sopenharmony_ci 139453a5a1b3Sopenharmony_ci pa_mutex_unlock(e->mutex); 139553a5a1b3Sopenharmony_ci} 139653a5a1b3Sopenharmony_ci 139753a5a1b3Sopenharmony_ci/* No lock necessary */ 139853a5a1b3Sopenharmony_cistatic pa_memblock *memblock_shared_copy(pa_mempool *p, pa_memblock *b) { 139953a5a1b3Sopenharmony_ci pa_memblock *n; 140053a5a1b3Sopenharmony_ci 140153a5a1b3Sopenharmony_ci pa_assert(p); 140253a5a1b3Sopenharmony_ci pa_assert(b); 140353a5a1b3Sopenharmony_ci 140453a5a1b3Sopenharmony_ci if (b->type == PA_MEMBLOCK_IMPORTED || 140553a5a1b3Sopenharmony_ci b->type == PA_MEMBLOCK_POOL || 140653a5a1b3Sopenharmony_ci b->type == PA_MEMBLOCK_POOL_EXTERNAL) { 140753a5a1b3Sopenharmony_ci pa_assert(b->pool == p); 140853a5a1b3Sopenharmony_ci return pa_memblock_ref(b); 140953a5a1b3Sopenharmony_ci } 141053a5a1b3Sopenharmony_ci 141153a5a1b3Sopenharmony_ci if (!(n = pa_memblock_new_pool(p, b->length))) 141253a5a1b3Sopenharmony_ci return NULL; 141353a5a1b3Sopenharmony_ci 141453a5a1b3Sopenharmony_ci memcpy(pa_atomic_ptr_load(&n->data), pa_atomic_ptr_load(&b->data), b->length); 141553a5a1b3Sopenharmony_ci return n; 141653a5a1b3Sopenharmony_ci} 141753a5a1b3Sopenharmony_ci 141853a5a1b3Sopenharmony_ci/* Self-locked */ 141953a5a1b3Sopenharmony_ciint pa_memexport_put(pa_memexport *e, pa_memblock *b, pa_mem_type_t *type, uint32_t *block_id, 142053a5a1b3Sopenharmony_ci uint32_t *shm_id, size_t *offset, size_t * size) { 142153a5a1b3Sopenharmony_ci pa_shm *memory; 142253a5a1b3Sopenharmony_ci struct memexport_slot *slot; 142353a5a1b3Sopenharmony_ci void *data; 142453a5a1b3Sopenharmony_ci 142553a5a1b3Sopenharmony_ci pa_assert(e); 142653a5a1b3Sopenharmony_ci pa_assert(b); 142753a5a1b3Sopenharmony_ci pa_assert(type); 142853a5a1b3Sopenharmony_ci pa_assert(block_id); 142953a5a1b3Sopenharmony_ci pa_assert(shm_id); 143053a5a1b3Sopenharmony_ci pa_assert(offset); 143153a5a1b3Sopenharmony_ci pa_assert(size); 143253a5a1b3Sopenharmony_ci pa_assert(b->pool == e->pool); 143353a5a1b3Sopenharmony_ci 143453a5a1b3Sopenharmony_ci if (!(b = memblock_shared_copy(e->pool, b))) 143553a5a1b3Sopenharmony_ci return -1; 143653a5a1b3Sopenharmony_ci 143753a5a1b3Sopenharmony_ci pa_mutex_lock(e->mutex); 143853a5a1b3Sopenharmony_ci 143953a5a1b3Sopenharmony_ci if (e->free_slots) { 144053a5a1b3Sopenharmony_ci slot = e->free_slots; 144153a5a1b3Sopenharmony_ci PA_LLIST_REMOVE(struct memexport_slot, e->free_slots, slot); 144253a5a1b3Sopenharmony_ci } else if (e->n_init < PA_MEMEXPORT_SLOTS_MAX) 144353a5a1b3Sopenharmony_ci slot = &e->slots[e->n_init++]; 144453a5a1b3Sopenharmony_ci else { 144553a5a1b3Sopenharmony_ci pa_mutex_unlock(e->mutex); 144653a5a1b3Sopenharmony_ci pa_memblock_unref(b); 144753a5a1b3Sopenharmony_ci return -1; 144853a5a1b3Sopenharmony_ci } 144953a5a1b3Sopenharmony_ci 145053a5a1b3Sopenharmony_ci PA_LLIST_PREPEND(struct memexport_slot, e->used_slots, slot); 145153a5a1b3Sopenharmony_ci slot->block = b; 145253a5a1b3Sopenharmony_ci *block_id = (uint32_t) (slot - e->slots + e->baseidx); 145353a5a1b3Sopenharmony_ci 145453a5a1b3Sopenharmony_ci pa_mutex_unlock(e->mutex); 145553a5a1b3Sopenharmony_ci/* pa_log("Got block id %u", *block_id); */ 145653a5a1b3Sopenharmony_ci 145753a5a1b3Sopenharmony_ci data = pa_memblock_acquire(b); 145853a5a1b3Sopenharmony_ci 145953a5a1b3Sopenharmony_ci if (b->type == PA_MEMBLOCK_IMPORTED) { 146053a5a1b3Sopenharmony_ci pa_assert(b->per_type.imported.segment); 146153a5a1b3Sopenharmony_ci memory = &b->per_type.imported.segment->memory; 146253a5a1b3Sopenharmony_ci } else { 146353a5a1b3Sopenharmony_ci pa_assert(b->type == PA_MEMBLOCK_POOL || b->type == PA_MEMBLOCK_POOL_EXTERNAL); 146453a5a1b3Sopenharmony_ci pa_assert(b->pool); 146553a5a1b3Sopenharmony_ci pa_assert(pa_mempool_is_shared(b->pool)); 146653a5a1b3Sopenharmony_ci memory = &b->pool->memory; 146753a5a1b3Sopenharmony_ci } 146853a5a1b3Sopenharmony_ci 146953a5a1b3Sopenharmony_ci pa_assert(data >= memory->ptr); 147053a5a1b3Sopenharmony_ci pa_assert((uint8_t*) data + b->length <= (uint8_t*) memory->ptr + memory->size); 147153a5a1b3Sopenharmony_ci 147253a5a1b3Sopenharmony_ci *type = memory->type; 147353a5a1b3Sopenharmony_ci *shm_id = memory->id; 147453a5a1b3Sopenharmony_ci *offset = (size_t) ((uint8_t*) data - (uint8_t*) memory->ptr); 147553a5a1b3Sopenharmony_ci *size = b->length; 147653a5a1b3Sopenharmony_ci 147753a5a1b3Sopenharmony_ci pa_memblock_release(b); 147853a5a1b3Sopenharmony_ci 147953a5a1b3Sopenharmony_ci pa_atomic_inc(&e->pool->stat.n_exported); 148053a5a1b3Sopenharmony_ci pa_atomic_add(&e->pool->stat.exported_size, (int) b->length); 148153a5a1b3Sopenharmony_ci 148253a5a1b3Sopenharmony_ci return 0; 148353a5a1b3Sopenharmony_ci} 1484