/* GStreamer shared memory allocator * * Copyright (C) 2012 Intel Corporation * Copyright (C) 2012 Sreerenj Balachandran * Copyright (C) 2023 Netflix Inc. * Author: Xavier Claessens * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "gstshmallocator.h" #ifdef HAVE_MMAP #include #include #include #include #endif struct _GstShmAllocator { GstFdAllocator parent; }; #define GST_CAT_DEFAULT gst_shm_debug GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT); G_DEFINE_TYPE_WITH_CODE (GstShmAllocator, gst_shm_allocator, GST_TYPE_FD_ALLOCATOR, GST_DEBUG_CATEGORY_INIT (gst_shm_debug, "shmallocator", 0, "Shared memory allocator"); ); static GstMemory * gst_shm_allocator_alloc (GstAllocator * allocator, gsize size, GstAllocationParams * params) { #if defined(HAVE_MEMFD_CREATE) || defined(HAVE_SHM_OPEN) GstShmAllocator *self = GST_SHM_ALLOCATOR (allocator); int fd; GstMemory *mem; GstMapInfo info; /* See _sysmem_new_block() for details */ gsize maxsize = size + params->prefix + params->padding; gsize align = params->align; align |= gst_memory_alignment; maxsize += align; #ifdef HAVE_MEMFD_CREATE fd = memfd_create ("gst-shm", MFD_CLOEXEC | MFD_ALLOW_SEALING); if (fd < 0) { GST_ERROR_OBJECT (self, "memfd_create() failed: %s", strerror (errno)); return NULL; } #else { char filename[1024]; static int init = 0; int flags = O_RDWR | O_CREAT | O_EXCL; int perms = S_IRUSR | S_IWUSR | S_IRGRP; snprintf (filename, 1024, "/gst-shm.%d.%d", getpid (), init++); fd = shm_open (filename, flags, perms); if (fd < 0) { GST_ERROR_OBJECT (self, "shm_open() failed: %s", strerror (errno)); return NULL; } shm_unlink (filename); } #endif if (ftruncate (fd, maxsize) < 0) { GST_ERROR_OBJECT (self, "ftruncate failed: %s", strerror (errno)); close (fd); return NULL; } mem = gst_fd_allocator_alloc (allocator, fd, size, GST_FD_MEMORY_FLAG_KEEP_MAPPED); if (G_UNLIKELY (!mem)) { GST_ERROR_OBJECT (self, "GstFdMemory allocation failed"); close (fd); return NULL; } /* We use GST_FD_MEMORY_FLAG_KEEP_MAPPED, so make sure the first map is RW. */ if (!gst_memory_map (mem, &info, GST_MAP_READWRITE)) { GST_ERROR_OBJECT (self, "GstFdMemory map failed"); gst_memory_unref (mem); return NULL; } /* See _sysmem_new_block() for details */ guint8 *data = info.data; gsize aoffset; if ((aoffset = ((guintptr) data & align))) { aoffset = (align + 1) - aoffset; data += aoffset; maxsize -= aoffset; } if (params->prefix && (params->flags & GST_MEMORY_FLAG_ZERO_PREFIXED)) memset (data, 0, params->prefix); gsize padding = maxsize - (params->prefix + size); if (padding && (params->flags & GST_MEMORY_FLAG_ZERO_PADDED)) memset (data + params->prefix + size, 0, padding); mem->align = align; mem->maxsize = maxsize; mem->offset = params->prefix + aoffset; gst_memory_unmap (mem, &info); #ifdef HAVE_MEMFD_CREATE /* Now that it's kept mapped RW, seal it for any future mapping. This ensures * that other processes receiving this memfd won't be able to modify it. */ fcntl (fd, F_ADD_SEALS, F_SEAL_SHRINK | F_SEAL_FUTURE_WRITE); #endif return mem; #else return NULL; #endif } static void gst_shm_allocator_class_init (GstShmAllocatorClass * klass) { GstAllocatorClass *alloc_class = (GstAllocatorClass *) klass; alloc_class->alloc = GST_DEBUG_FUNCPTR (gst_shm_allocator_alloc); } static void gst_shm_allocator_init (GstShmAllocator * self) { GstAllocator *alloc = GST_ALLOCATOR_CAST (self); alloc->mem_type = GST_ALLOCATOR_SHM; GST_OBJECT_FLAG_SET (self, GST_ALLOCATOR_FLAG_NO_COPY); } void gst_shm_allocator_init_once (void) { static gsize _init = 0; if (g_once_init_enter (&_init)) { GstAllocator *alloc; alloc = (GstAllocator *) g_object_new (GST_TYPE_SHM_ALLOCATOR, NULL); gst_object_ref_sink (alloc); gst_allocator_register (GST_ALLOCATOR_SHM, alloc); g_once_init_leave (&_init, 1); } } GstAllocator * gst_shm_allocator_get (void) { return gst_allocator_find (GST_ALLOCATOR_SHM); }