diff --git a/src/pipewire/mem.c b/src/pipewire/mem.c index 1bfb75158..19cad6af1 100644 --- a/src/pipewire/mem.c +++ b/src/pipewire/mem.c @@ -104,6 +104,9 @@ static int pw_memfd_create(const char *name, unsigned int flags) #define pw_mempool_emit_added(p,b) pw_mempool_emit(p, added, 0, b) #define pw_mempool_emit_removed(p,b) pw_mempool_emit(p, removed, 0, b) +#define memblock_emit(b,m,v,...) spa_hook_list_call(&b->listener_list, struct memblock_events, m, v, ##__VA_ARGS__) +#define memblock_emit_invalidated(b) memblock_emit(b, invalidated, 0) + struct mempool { struct pw_mempool this; @@ -119,6 +122,15 @@ struct memblock { struct spa_list link; /* link in mempool */ struct spa_list mappings; /* list of struct mapping */ struct spa_list memmaps; /* list of struct memmap */ + struct memblock *owner; /* owner of fd, if another memblock */ + struct spa_hook owner_listener; /* listen for fd owner memblock events */ + struct spa_hook_list listener_list; +}; + +struct memblock_events { +#define VERSION_MEMBLOCK_EVENTS 0 + uint32_t version; + void (*invalidated) (void *data); }; /* a mapped region of a block */ @@ -311,6 +323,11 @@ static struct mapping * memblock_map(struct memblock *b, return NULL; } + if (b->this.fd == -1) { + pw_log_error("%p: block:%p cannot map memory with stale fd", p, b); + errno = EINVAL; + return NULL; + } ptr = mmap(NULL, size, prot, fl, b->this.fd, offset); if (ptr == MAP_FAILED) { @@ -373,6 +390,12 @@ struct pw_memmap * pw_memblock_map(struct pw_memblock *block, struct pw_map_range range; struct stat sb; + if (b->this.fd == -1) { + pw_log_error("%p: block:%p cannot map memory with stale fd", p, block); + errno = EINVAL; + return NULL; + } + if (fstat(b->this.fd, &sb) != 0) return NULL; @@ -508,6 +531,7 @@ struct pw_memblock * pw_mempool_alloc(struct pw_mempool *pool, enum pw_memblock_ b->this.size = size; spa_list_init(&b->mappings); spa_list_init(&b->memmaps); + spa_hook_list_init(&b->listener_list); #ifdef HAVE_MEMFD_CREATE char name[128]; @@ -593,6 +617,9 @@ static struct memblock * mempool_find_fd(struct pw_mempool *pool, int fd) struct memblock *b; spa_list_for_each(b, &impl->blocks, link) { + if (b->this.fd == -1) + continue; + if (fd == b->this.fd) { pw_log_debug("%p: found %p id:%u fd:%d ref:%d", pool, &b->this, b->this.id, fd, b->this.ref); @@ -609,6 +636,12 @@ struct pw_memblock * pw_mempool_import(struct pw_mempool *pool, struct mempool *impl = SPA_CONTAINER_OF(pool, struct mempool, this); struct memblock *b; + if (fd < 0) { + pw_log_error("%p: cannot import invalid fd", pool); + errno = EINVAL; + return NULL; + } + b = mempool_find_fd(pool, fd); if (b != NULL) { b->this.ref++; @@ -621,6 +654,7 @@ struct pw_memblock * pw_mempool_import(struct pw_mempool *pool, spa_list_init(&b->memmaps); spa_list_init(&b->mappings); + spa_hook_list_init(&b->listener_list); b->this.ref = 1; b->this.pool = pool; @@ -639,15 +673,57 @@ struct pw_memblock * pw_mempool_import(struct pw_mempool *pool, return &b->this; } +static void memblock_invalidated(void *data) +{ + struct memblock *b = data; + + if (!b->owner) + return; + + pw_log_debug("%p: invalidated block:%p id:%u fd:%d ref:%d owner:%p", + b->this.pool, b, b->this.id, b->this.fd, b->this.ref, b->owner); + + spa_hook_remove(&b->owner_listener); + b->owner = NULL; + + b->this.fd = -1; +} + +static const struct memblock_events memblock_events = { + VERSION_MEMBLOCK_EVENTS, + .invalidated = memblock_invalidated, +}; + SPA_EXPORT struct pw_memblock * pw_mempool_import_block(struct pw_mempool *pool, struct pw_memblock *mem) { + struct pw_memblock *block; + struct memblock *b; + pw_log_debug("%p: import block:%p type:%d fd:%d", pool, mem, mem->type, mem->fd); - return pw_mempool_import(pool, + + block = pw_mempool_import(pool, mem->flags | PW_MEMBLOCK_FLAG_DONT_CLOSE, mem->type, mem->fd); + if (!block) + return NULL; + + b = SPA_CONTAINER_OF(block, struct memblock, this); + if (!b->owner) { + struct memblock *bmem = SPA_CONTAINER_OF(mem, struct memblock, this); + + while (bmem->owner) + bmem = bmem->owner; + + if (!(bmem->this.flags & PW_MEMBLOCK_FLAG_DONT_CLOSE)) { + b->owner = bmem; + spa_hook_list_append(&bmem->listener_list, &b->owner_listener, &memblock_events, b); + } + } + + return block; } SPA_EXPORT @@ -751,6 +827,13 @@ void pw_memblock_free(struct pw_memblock *block) if (!SPA_FLAG_IS_SET(block->flags, PW_MEMBLOCK_FLAG_DONT_NOTIFY)) pw_mempool_emit_removed(impl, block); + if (b->owner) { + spa_hook_remove(&b->owner_listener); + b->owner = NULL; + } + + memblock_emit_invalidated(b); + spa_list_consume(mm, &b->memmaps, link) pw_memmap_free(&mm->this); @@ -763,6 +846,9 @@ void pw_memblock_free(struct pw_memblock *block) pw_log_debug("%p: close fd:%d", pool, block->fd); close(block->fd); } + + spa_hook_list_clean(&b->listener_list); + free(b); }