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