Message ID | 1471621724-8485-9-git-send-email-christophe.milard@linaro.org |
---|---|
State | Superseded |
Headers | show |
Coverity flagged a number of issues against this module (as well as _fdserver.c). These should be addressed now rather than later: On Fri, Aug 19, 2016 at 10:48 AM, Christophe Milard < christophe.milard@linaro.org> wrote: > A new ODP internal memory allocator, called ishm (for internal shmem) > is introduced here. > This memory allocator enables the following: > - works for odpthreads being linux processes, regardless for fork time. > - guarantees the uniqueness of the virtual space mapping address over > all ODP threads (even processes and regardless of fork time), > when the required _ODP_ISHM_SINGLE_VA flag is used. > > Signed-off-by: Christophe Milard <christophe.milard@linaro.org> > --- > platform/linux-generic/Makefile.am | 4 + > platform/linux-generic/_ishm.c | 1336 > ++++++++++++++++++++ > platform/linux-generic/_ishmphy.c | 168 +++ > platform/linux-generic/arch/arm/odp/api/cpu_arch.h | 25 +- > platform/linux-generic/arch/arm/odp_cpu_arch.c | 49 +- > .../linux-generic/arch/arm/odp_sysinfo_parse.c | 28 +- > platform/linux-generic/arch/powerpc/odp_cpu_arch.c | 49 +- > .../linux-generic/include/_fdserver_internal.h | 1 + > platform/linux-generic/include/_ishm_internal.h | 45 + > platform/linux-generic/include/_ishmphy_internal.h | 25 + > platform/linux-generic/include/ishmphy_internal.h | 24 + > .../linux-generic/include/odp_config_internal.h | 10 + > platform/linux-generic/include/odp_internal.h | 6 + > platform/linux-generic/odp_init.c | 26 + > 14 files changed, 1792 insertions(+), 4 deletions(-) > create mode 100644 platform/linux-generic/_ishm.c > create mode 100644 platform/linux-generic/_ishmphy.c > mode change 120000 => 100644 platform/linux-generic/arch/ > arm/odp/api/cpu_arch.h > mode change 120000 => 100644 platform/linux-generic/arch/ > arm/odp_cpu_arch.c > mode change 120000 => 100644 platform/linux-generic/arch/ > arm/odp_sysinfo_parse.c > mode change 120000 => 100644 platform/linux-generic/arch/ > powerpc/odp_cpu_arch.c > create mode 100644 platform/linux-generic/include/_ishm_internal.h > create mode 100644 platform/linux-generic/include/_ishmphy_internal.h > create mode 100644 platform/linux-generic/include/ishmphy_internal.h > > diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/ > Makefile.am > index cb7c8e9..ac2b2da 100644 > --- a/platform/linux-generic/Makefile.am > +++ b/platform/linux-generic/Makefile.am > @@ -113,6 +113,8 @@ odpdrvplatinclude_HEADERS = \ > > noinst_HEADERS = \ > ${srcdir}/include/_fdserver_internal.h \ > + ${srcdir}/include/_ishm_internal.h \ > + ${srcdir}/include/_ishmphy_internal.h \ > ${srcdir}/include/odp_align_internal.h \ > ${srcdir}/include/odp_atomic_internal.h \ > ${srcdir}/include/odp_buffer_inlines.h \ > @@ -156,6 +158,8 @@ noinst_HEADERS = \ > > __LIB__libodp_linux_la_SOURCES = \ > _fdserver.c \ > + _ishm.c \ > + _ishmphy.c \ > odp_atomic.c \ > odp_barrier.c \ > odp_buffer.c \ > diff --git a/platform/linux-generic/_ishm.c b/platform/linux-generic/_ > ishm.c > new file mode 100644 > index 0000000..a92d5cc > --- /dev/null > +++ b/platform/linux-generic/_ishm.c > @@ -0,0 +1,1336 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +/* This file handles the internal shared memory: internal shared memory > + * is memory which is sharable by all ODP threads regardless of how the > + * ODP thread is implemented (pthread or process) and regardless of fork() > + * time. > + * Moreover, when reserved with the _ODP_ISHM_SINGLE_VA flag, > + * internal shared memory is guaranteed to always be located at the same > virtual > + * address, i.e. pointers to internal shared memory are fully shareable > + * between odp threads (regardless of thread type or fork time) in that > case. > + * Internal shared memory is mainly meant to be used internaly within ODP > + * (hence its name), but may also be allocated by odp applications and > drivers, > + * in the future (through these interfaces). > + * To guarrentee this full pointer shareability (when reserved with the > + * _ODP_ISHM_SINGLE_VA flag) internal shared memory is handled as follows: > + * At global_init time, a huge virtual address space reservation is > performed. > + * Note that this is just reserving virtual space, not physical memory. > + * Because all ODP threads (pthreads or processes) are descendants of the > ODP > + * instantiation process, this VA space is inherited by all ODP threads. > + * When internal shmem reservation actually occurs, and > + * when reserved with the _ODP_ISHM_SINGLE_VA flag, physical memory is > + * allocated, and mapped (MAP_FIXED) to some part in the huge preallocated > + * address space area: > + * because this virtual address space is common to all ODP threads, we > + * know this mapping will succeed, and not clash with anything else. > + * Hence, an ODP threads which perform a lookup for the same ishm block > + * can map it at the same VA address. > + * When internal shared memory is released, the physical memory is > released > + * and the corresponding virtual space returned to its "pool" of > preallocated > + * virtual space (assuming it was allocated from there). > + * Note, though, that, if 2 linux processes share the same ishm block, > + * the virtual space is marked as released as soon as one of the processes > + * releases the ishm block, but the physical memory space is actually > released > + * by the kernel once all processes have done a ishm operation (i,e. a > sync). > + * This is due to the fact that linux does not contain any syscall to > unmap > + * memory from a different process. > + * > + * This file contains functions to handle the VA area (handling > fragmentation > + * and defragmentation resulting from different allocs/release) and also > + * define the functions to allocate, release and lookup internal shared > + * memory: > + * _odp_ishm_reserve(), _odp_ishm_free*() and _odp_ishm_lookup*()... > + */ > +#include <odp_posix_extensions.h> > +#include <odp_config_internal.h> > +#include <odp_internal.h> > +#include <odp/api/spinlock.h> > +#include <odp/api/align.h> > +#include <odp/api/system_info.h> > +#include <odp/api/debug.h> > +#include <odp_shm_internal.h> > +#include <odp_debug_internal.h> > +#include <odp_align_internal.h> > +#include <_fdserver_internal.h> > +#include <_ishm_internal.h> > +#include <_ishmphy_internal.h> > +#include <stdlib.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <string.h> > +#include <errno.h> > +#include <sys/mman.h> > +#include <sys/stat.h> > +#include <sys/syscall.h> > +#include <fcntl.h> > +#include <sys/types.h> > +#include <inttypes.h> > +#include <sys/wait.h> > + > +/* > + * Maximum number of internal shared memory blocks. > + * > + * This the the number of separate ISHM areas that can be reserved > concurrently > + * (Note that freeing such blocks may take time, or possibly never happen > + * if some of the block ownwers never procsync() after free). This number > + * should take that into account) > + */ > +#define ISHM_MAX_NB_BLOCKS 128 > + > +/* > + * Maximum internal shared memory block name length in chars > + * probably taking the same number as SHM name size make sense at this > stage > + */ > +#define ISHM_NAME_MAXLEN 32 > + > +/* > + * Linux underlying file name: <directory>/odp-<odp_pid>-ishm-<name> > + * The <name> part may be replaced by a sequence number if no specific > + * name is given at reserve time > + * <directory> is either /tmp or the hugepagefs mount point for default > size. > + * (searched at init time) > + */ > +#define ISHM_FILENAME_MAXLEN (ISHM_NAME_MAXLEN + 64) > +#define ISHM_FILENAME_FORMAT "%s/odp-%d-ishm-%s" > +#define ISHM_FILENAME_NORMAL_PAGE_DIR "/tmp" > + > +/* > + * At worse case the virtual space gets so fragmented that there is > + * a unallocated fragment between each allocated fragment: > + * In that case, the number of fragments to take care of is twice the > + * number of ISHM blocks + 1. > + */ > +#define ISHM_NB_FRAGMNTS (ISHM_MAX_NB_BLOCKS * 2 + 1) > + > +/* > + * A block describes a piece of reserved memory: Any successful > ishm_reserve() > + * will allocate a block. A ishm_reserve() with the _ODP_ISHM_SINGLE_VA > flag set > + * will allocated both a block and a fragment. > + * Blocks contain only global data common to all processes. > + */ > +typedef struct ishm_fragment ishm_fragment_t; /*forward decl. see below. > */ > +typedef struct ishm_block { > + char name[ISHM_NAME_MAXLEN]; /* name for the ishm block (if > any) */ > + char filename[ISHM_FILENAME_MAXLEN]; /* name of the .../odp-* > file */ > + int main_odpthread; /* The thread which did the initial > reserve*/ > + uint32_t user_flags; /* any flags the user want to remember. > */ > + uint32_t flags; /* block creation flags. > */ > + uint64_t user_len; /* length, as requested at reserve time. > */ > + void *start; /* only valid if _ODP_ISHM_SINGLE_VA is > set*/ > + uint64_t len; /* length. multiple of page size. 0 if > free*/ > + ishm_fragment_t *fragment; /* used when _ODP_ISHM_SINGLE_VA is > used */ > + int huge; /* true if this segment is mapped using huge > pages */ > + uint64_t seq; /* sequence number, incremented on alloc and free > */ > + uint64_t refcnt;/* number of linux processes mapping this block > */ > +} ishm_block_t; > + > +/* > + * Table of blocks describing allocated internal shared memory > + * This table is visible to every ODP thread (linux process or pthreads). > + * (it is allocated shared at odp init time and is therefore inherited by > all) > + * Table index is used as handle, so it cannot move!. Entry is regarded as > + * free when len==0 > + */ > +typedef struct { > + odp_spinlock_t lock; > + uint64_t dev_seq; /* used when creating device names */ > + ishm_block_t block[ISHM_MAX_NB_BLOCKS]; > +} ishm_table_t; > +static ishm_table_t *ishm_tbl; > + > +/* > + * Process local table containing the list of (believed) allocated blocks > seen > + * from the current process. There is one such table per linux process. > linux > + * threads within a process shares this table. > + * The contents within this table may become obsolete when other processes > + * reserve/free ishm blocks. This is what the procsync() function > + * catches by comparing the block sequence number with the one in this > table. > + * This table is filled at ishm_reserve and ishm_lookup time. > + * Entries are removed at ishm_free or procsync time. > + * Note that flags and len are present in this table and seems to be > redundant > + * with those present in the ishm block table: but this is not fully true: > + * When ishm_sync() detects obsolete mappings and tries to remove them, > + * the entry in the ishm block table is then obsolete, and the values > which are > + * found in this table must be used to perform the ummap. > + * (and the values in the block tables are needed at lookup time...) > + */ > +typedef struct { > + int thrd_refcnt; /* number of pthreads in this process, really */ > + struct { > + int block_index; /* entry in the ishm_tbl */ > + uint32_t flags; /* flags used at creation time */ > + uint64_t seq; > + void *start; /* start of block (VA) > */ > + uint64_t len; /* length of block. multiple of page size > */ > + int fd; /* file descriptor used for this block > */ > + } entry[ISHM_MAX_NB_BLOCKS]; > + int nb_entries; > +} ishm_proctable_t; > +static ishm_proctable_t *ishm_proctable; > + > +/* > + * A fragment describes a piece of the shared virtual address space, > + * and is allocated only when allocation is done with the > _ODP_ISHM_SINGLE_VA > + * flag: > + * A fragment is said to be used when it actually does represent some > + * portion of the virtual address space, and is said to be unused when > + * it does not (so at start, one single fragment is used -describing the > + * whole address space as unallocated-, and all others are unused). > + * Fragments get used as address space fragmentation increases. > + * A fragment is allocated if the piece of address space it > + * describes is actually used by a shared memory block. > + * Allocated fragments get their block_index set >=0. > + */ > +typedef struct ishm_fragment { > + struct ishm_fragment *prev; /* not used when the fragment is > unused */ > + struct ishm_fragment *next; > + void *start; /* start of segment (VA) */ > + uintptr_t len; /* length of segment. multiple of page > size */ > + int block_index; /* -1 for unallocated fragments */ > +} ishm_fragment_t; > + > +/* > + * Table of fragments describing the common virtual address space: > + * This table is visible to every ODP thread (linux process or pthreads). > + * (it is allocated at odp init time and is therefore inherited by all) > + */ > +typedef struct { > + ishm_fragment_t fragment[ISHM_NB_FRAGMNTS]; > + ishm_fragment_t *used_fragmnts; /* ordered by increasing start > addr */ > + ishm_fragment_t *unused_fragmnts; > +} ishm_ftable_t; > +static ishm_ftable_t *ishm_ftbl; > + > +#ifndef MAP_ANONYMOUS > +#define MAP_ANONYMOUS MAP_ANON > +#endif > + > +/* prototypes: */ > +static void procsync(void); > + > +/* > + * Take a piece of the preallocated virtual space to fit "size" bytes. > + * (best fit). Size must be rounded up to an integer number of pages size. > + * Possibly split the fragment to keep track of remaining space. > + * Returns the allocated fragment (best_fragmnt) and the corresponding > address. > + * External caller must ensure mutex before the call! > + */ > +static void *alloc_fragment(uintptr_t size, int block_index, intptr_t > align, > + ishm_fragment_t **best_fragmnt) > +{ > + ishm_fragment_t *fragmnt; > + *best_fragmnt = NULL; > + ishm_fragment_t *rem_fragmnt; > + uintptr_t border;/* possible start of new fragment (next > alignement) */ > + intptr_t left; /* room remaining after, if the segment is > allocated */ > + uintptr_t remainder = ODP_CONFIG_ISHM_VA_PREALLOC_SZ; > + > + /* > + * search for the best bit, i.e. search for the unallocated > fragment > + * would give less remainder if the new fragment was allocated > within > + * it: > + */ > + for (fragmnt = ishm_ftbl->used_fragmnts; > + fragmnt; fragmnt = fragmnt->next) { > + /* skip allocated segment: */ > + if (fragmnt->block_index >= 0) > + continue; > + /* skip too short segment: */ > + border = ((uintptr_t)fragmnt->start + align - 1) & > (-align); > + left = > + ((uintptr_t)fragmnt->start + fragmnt->len) - (border + > size); > + if (left < 0) > + continue; > + /* remember best fit: */ > + if ((uintptr_t)left < remainder) { > + remainder = left; /* best, so far */ > + *best_fragmnt = fragmnt; > + } > + } > + > + if (!(*best_fragmnt)) { > + ODP_ERR("unable to get virtual address for shmem > block!\n."); > + return NULL; > + } > + > + (*best_fragmnt)->block_index = block_index; > + border = ((uintptr_t)(*best_fragmnt)->start + align - 1) & > (-align); > + > + /* > + * if there is room between previous fragment and new one, (due to > + * alignement requirement) then fragment (split) the space between > + * the end of the previous fragment and the beginning of the new > one: > + */ > + if (border - (uintptr_t)(*best_fragmnt)->start > 0) { > + /* frangment space, i.e. take a new fragment descriptor... > */ > + rem_fragmnt = ishm_ftbl->unused_fragmnts; > + if (!rem_fragmnt) { > + ODP_ERR("unable to get shmem fragment > descriptor!\n."); > + return NULL; > + } > + ishm_ftbl->unused_fragmnts = rem_fragmnt->next; > + > + /* and link it between best_fragmnt->prev and best_fragmnt > */ > + if ((*best_fragmnt)->prev) > + (*best_fragmnt)->prev->next = rem_fragmnt; > + else > + ishm_ftbl->used_fragmnts = rem_fragmnt; > + rem_fragmnt->prev = (*best_fragmnt)->prev; > + (*best_fragmnt)->prev = rem_fragmnt; > + rem_fragmnt->next = (*best_fragmnt); > + > + /* update length: rem_fragmnt getting space before border > */ > + rem_fragmnt->block_index = -1; > + rem_fragmnt->start = (*best_fragmnt)->start; > + rem_fragmnt->len = border - (uintptr_t)(*best_fragmnt)-> > start; > + (*best_fragmnt)->start = > + (void *)((uintptr_t)rem_fragmnt->start + > rem_fragmnt->len); > + (*best_fragmnt)->len -= rem_fragmnt->len; > + } > + > + /* if this was a perfect fit, i.e. no free space follows, we are > done */ > + if (remainder == 0) > + return (*best_fragmnt)->start; > + > + /* otherwise, frangment space, i.e. take a new fragment > descriptor... */ > + rem_fragmnt = ishm_ftbl->unused_fragmnts; > + if (!rem_fragmnt) { > + ODP_ERR("unable to get shmem fragment descriptor!\n."); > + return (*best_fragmnt)->start; > + } > + ishm_ftbl->unused_fragmnts = rem_fragmnt->next; > + > + /* ... double link it... */ > + rem_fragmnt->next = (*best_fragmnt)->next; > + rem_fragmnt->prev = (*best_fragmnt); > + if ((*best_fragmnt)->next) > + (*best_fragmnt)->next->prev = rem_fragmnt; > + (*best_fragmnt)->next = rem_fragmnt; > + > + /* ... and keep track of the remainder */ > + (*best_fragmnt)->len = size; > + rem_fragmnt->len = remainder; > + rem_fragmnt->start = (void *)((char *)(*best_fragmnt)->start + > size); > + rem_fragmnt->block_index = -1; > + > + return (*best_fragmnt)->start; > +} > + > +/* > + * Free a portion of virtual space. > + * Possibly defragment, if the freed fragment is adjacent to another > + * free virtual fragment. > + * External caller must ensure mutex before the call! > + */ > +static void free_fragment(ishm_fragment_t *fragmnt) > +{ > + ishm_fragment_t *prev_f; > + ishm_fragment_t *next_f; > + > + /* sanity check */ > + if (!fragmnt) > + return; > + > + prev_f = fragmnt->prev; > + next_f = fragmnt->next; > + > + /* free the fragment */ > + fragmnt->block_index = -1; > + > + /* check if the previous fragment is also free: if so, defragment > */ > + if (prev_f && (prev_f->block_index < 0)) { > + fragmnt->start = prev_f->start; > + fragmnt->len += prev_f->len; > + if (prev_f->prev) { > + prev_f->prev->next = fragmnt; > + } else { > + if (ishm_ftbl->used_fragmnts == prev_f) > + ishm_ftbl->used_fragmnts = fragmnt; > + else > + ODP_ERR("corrupted fragment list!.\n"); > + } > + fragmnt->prev = prev_f->prev; > + > + /* put removed fragment in free list */ > + prev_f->prev = NULL; > + prev_f->next = ishm_ftbl->unused_fragmnts; > + ishm_ftbl->unused_fragmnts = prev_f; > + } > + > + /* check if the next fragment is also free: if so, defragment */ > + if (next_f && (next_f->block_index < 0)) { > + fragmnt->len += next_f->len; > + if (next_f->next) > + next_f->next->prev = fragmnt; > + fragmnt->next = next_f->next; > + > + /* put removed fragment in free list */ > + next_f->prev = NULL; > + next_f->next = ishm_ftbl->unused_fragmnts; > + ishm_ftbl->unused_fragmnts = next_f; > + } > +} > + > +/* > + * Create file with size len. returns -1 on error > + * Creates a file to /tmp/odp-<pid>-<sequence_or_name> (for normal pages) > + * or /mnt/huge/odp-<pid>-<sequence_or_name> (for huge pages) > + * Return the new file descriptor, or -1 on error. > + */ > +static int create_file(int block_index, int huge, uint64_t len) > +{ > + char *name; > + int fd; > + ishm_block_t *new_block; /* entry in the main block > table */ > + char seq_string[ISHM_FILENAME_MAXLEN]; /* used to construct > filename*/ > + char filename[ISHM_FILENAME_MAXLEN];/* filename in /tmp/ or > /mnt/huge */ > + int oflag = O_RDWR | O_CREAT | O_TRUNC; /* flags for open > */ > + > + new_block = &ishm_tbl->block[block_index]; > + name = new_block->name; > + > + /* create the filename: */ > + snprintf(seq_string, ISHM_FILENAME_MAXLEN, "%08" PRIu64, > + ishm_tbl->dev_seq++); > + > + /* huge dir must be known to create files there!: */ > + if (huge && !odp_global_data.hugepage_info.default_huge_page_dir) > + return -1; > + > + if (huge) > + snprintf(filename, ISHM_FILENAME_MAXLEN, > + ISHM_FILENAME_FORMAT, > + odp_global_data.hugepage_info. > default_huge_page_dir, > + odp_global_data.main_pid, > + (name && name[0]) ? name : seq_string); > + else > + snprintf(filename, ISHM_FILENAME_MAXLEN, > + ISHM_FILENAME_FORMAT, > + ISHM_FILENAME_NORMAL_PAGE_DIR, > + odp_global_data.main_pid, > + (name && name[0]) ? name : seq_string); > + > + fd = open(filename, oflag, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); > + if (fd < 0) { > + ODP_ERR("open failed for %s: %s.\n", > + filename, strerror(errno)); > + return -1; > + } > + > + if (ftruncate(fd, len) == -1) { > + ODP_ERR("ftruncate failed: fd=%d, err=%s.\n", > + fd, strerror(errno)); > + close(fd); > + return -1; > + } > + > + strncpy(new_block->filename, filename, ISHM_FILENAME_MAXLEN - 1); > + > + return fd; > +} > + > +/* > + * performs the mapping, possibly allocating a fragment of the > pre-reserved > + * VA space if the _ODP_ISHM_SINGLE_VA flag was given. > + * Sets fd, and returns the mapping address. > + * This funstion will also set the _ODP_ISHM_SINGLE_VA flag if the > alignment > + * requires it > + * Mutex must be assured by the caller. > + */ > +static void *do_map(int block_index, uint64_t len, uint32_t align, > + uint32_t flags, int huge, int *fd) > +{ > + ishm_block_t *new_block; /* entry in the main block > table */ > + void *addr = NULL; > + void *mapped_addr; > + ishm_fragment_t *fragment = NULL; > + > + new_block = &ishm_tbl->block[block_index]; > + > + /* > + * Creates a file to /tmp/odp-<pid>-<sequence> (for normal pages) > + * or /mnt/huge/odp-<pid>-<sequence> (for huge pages) > + * unless a fd was already given > + */ > + if (*fd < 0) { > + *fd = create_file(block_index, huge, len); > + if (*fd < 0) > + return NULL; > + } else { > + new_block->filename[0] = 0; > + } > + > + /* allocate an address range in the prebooked VA area if needed */ > + if (flags & _ODP_ISHM_SINGLE_VA) { > + addr = alloc_fragment(len, block_index, align, &fragment); > + if (!addr) { > + ODP_ERR("alloc_fragment failed.\n"); > + if (new_block->filename[0]) { > + close(*fd); > + *fd = -1; > + unlink(new_block->filename); > + } > + return NULL; > + } > + ishm_tbl->block[block_index].fragment = fragment; > + } > + > + /* try to mmap: */ > + mapped_addr = _odp_ishmphy_map(*fd, addr, len, flags); > + if (mapped_addr == NULL) { > + if (flags & _ODP_ISHM_SINGLE_VA) > + free_fragment(fragment); > + if (new_block->filename[0]) { > + close(*fd); > + *fd = -1; > + unlink(new_block->filename); > + } > + return NULL; > + } > + > + new_block->huge = huge; > + > + return mapped_addr; > +} > + > +/* > + * Performs an extra mapping (for a process trying to see an existing > block > + * i.e. performing a lookup). > + * Mutex must be assured by the caller. > + */ > +static void *do_remap(int block_index, int fd) > +{ > + void *mapped_addr; > + ishm_fragment_t *fragment; > + uint64_t len; > + uint32_t flags; > + > + len = ishm_tbl->block[block_index].len; > + flags = ishm_tbl->block[block_index].flags; > + > + if (flags & _ODP_ISHM_SINGLE_VA) { > + fragment = ishm_tbl->block[block_index].fragment; > + if (!fragment) { > + ODP_ERR("invalid fragment failure.\n"); > + return NULL; > + } > + > + /* try to mmap: */ > + mapped_addr = _odp_ishmphy_map(fd, fragment->start, len, > flags); > + if (mapped_addr == NULL) > + return NULL; > + return mapped_addr; > + } > + > + /* try to mmap: */ > + mapped_addr = _odp_ishmphy_map(fd, NULL, len, flags); > + if (mapped_addr == NULL) > + return NULL; > + > + return mapped_addr; > +} > + > +/* > + * Performs unmapping, possibly freeing a prereserved VA space fragment, > + * if the _ODP_ISHM_SINGLE_VA flag was set at alloc time > + * Mutex must be assured by the caller. > + */ > +static int do_unmap(void *start, uint64_t size, uint32_t flags, > + int block_index) > +{ > + int ret; > + > + if (start) > + ret = _odp_ishmphy_unmap(start, size, flags); > + else > + ret = 0; > + > + if ((block_index >= 0) && (flags & _ODP_ISHM_SINGLE_VA)) { > + /* mark reserved address space as free */ > + free_fragment(ishm_tbl->block[block_index].fragment); > + } > + > + return ret; > +} > + > +/* > + * Search for a given used and allocated block name. > + * (search is performed in the global ishm table) > + * Returns the index of the found block (if any) or -1 if none. > + * Mutex must be assured by the caller. > + */ > +static int find_block_by_name(const char *name) > +{ > + int i; > + > + if (name == NULL || name[0] == 0) > + return -1; > + > + for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) { > + if ((ishm_tbl->block[i].len) && > + (strcmp(name, ishm_tbl->block[i].name) == 0)) > + return i; > + } > + > + return -1; > +} > + > +/* > + * Search for a block by address (only works when flag _ODP_ISHM_SINGLE_VA > + * was set at reserve() time, or if the block is already known by this > + * process). > + * Search is performed in the process table and in the global ishm table. > + * The provided address does not have to be at start: any address > + * within the fragment is OK. > + * Returns the index to the found block (if any) or -1 if none. > + * Mutex must be assured by the caller. > + */ > +static int find_block_by_address(void *addr) > +{ > + int block_index; > + int i; > + ishm_fragment_t *fragmnt; > + > + /* > + * first check if there is already a process known block for this > + * address > + */ > + for (i = 0; i < ishm_proctable->nb_entries; i++) { > + block_index = ishm_proctable->entry[i].block_index; > + if ((addr > ishm_proctable->entry[i].start) && > + ((char *)addr < ((char *)ishm_proctable->entry[i].start > + > + ishm_tbl->block[block_index].len))) > + return block_index; > + } > + > + /* > + * then check if there is a existing single VA block known by some > other > + * process and containing the given address > + */ > + for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) { > + if ((!ishm_tbl->block[i].len) || > + (!(ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA))) > + continue; > + fragmnt = ishm_tbl->block[i].fragment; > + if (!fragmnt) { > + ODP_ERR("find_fragment: invalid NULL fragment\n"); > + return -1; > + } > + if ((addr >= fragmnt->start) && > + ((char *)addr < ((char *)fragmnt->start + > fragmnt->len))) > + return i; > + } > + > + /* address does not belong to any accessible block: */ > + return -1; > +} > + > +/* > + * Search a given ishm block in the process local table. Return its index > + * in the process table or -1 if not found (meaning that the ishm table > + * block index was not referenced in the process local table, i.e. the > + * block is known by some other process, but not by the current process). > + * Caller must assure mutex. > + */ > +static int procfind_block(int block_index) > +{ > + int i; > + > + for (i = 0; i < ishm_proctable->nb_entries; i++) { > + if (ishm_proctable->entry[i].block_index == block_index) > + return i; > + } > + return -1; > +} > + > +/* > + * Release the physical memory mapping for blocks which have been freed > + * by other processes. Caller must ensure mutex. > + * Mutex must be assured by the caller. > + */ > +static void procsync(void) > +{ > + int i = 0; > + int last; > + ishm_block_t *block; > + > + last = ishm_proctable->nb_entries; > + while (i < last) { > + /* if the procecess sequence number doesn't match the main > + * table seq number, this entry is obsolete > + */ > + block = &ishm_tbl->block[ishm_proctable->entry[i].block_ > index]; > + if (ishm_proctable->entry[i].seq != block->seq) { > + /* obsolete entry: free memory and remove proc > entry */ > + close(ishm_proctable->entry[i].fd); > + _odp_ishmphy_unmap(ishm_proctable->entry[i].start, > + ishm_proctable->entry[i].len, > + ishm_proctable->entry[i]. > flags); > + ishm_proctable->entry[i] = > + ishm_proctable->entry[--last]; > + } else { > + i++; > + } > + } > + ishm_proctable->nb_entries = last; > +} > + > +/* > + * Allocate and map internal shared memory, or other objects: > + * If a name is given, check that this name is not already in use. > + * If ok, allocate a new shared memory block and map the > + * provided fd in it (if fd >=0 was given). > + * If no fd is provided, a shared memory file desc named > + * /tmp/odp-<pid>-ishm-<name_or_sequence> is created and mapped. > + * (the name is different for huge page file as they must be on > hugepagefs) > + * The function returns the index of the newly created block in the > + * main block table (>=0) or -1 on error. > + */ > +int _odp_ishm_reserve(const char *name, uint64_t size, int fd, > + uint32_t align, uint32_t flags, uint32_t user_flags) > +{ > + int new_index; /* index in the main block > table*/ > + ishm_block_t *new_block; /* entry in the main block > table*/ > + uint64_t page_sz; /* normal page size. usually > 4K*/ > + uint64_t alloc_size; /* includes extra for > alignement*/ > + uint64_t page_hp_size; /* huge page size */ > + uint64_t alloc_hp_size; /* includes extra for > alignement*/ > + uint32_t hp_align; > + uint64_t len; /* mapped length */ > + void *addr = NULL; /* mapping address */ > + int new_proc_entry; > + > + page_sz = odp_sys_page_size(); > + > + odp_spinlock_lock(&ishm_tbl->lock); > + > + /* update this process view... */ > + procsync(); > + > + /* roundup to page size */ > + alloc_size = (size + (page_sz - 1)) & (-page_sz); > + > + page_hp_size = odp_sys_huge_page_size(); > + /* roundup to page size */ > + alloc_hp_size = (size + (page_hp_size - 1)) & (-page_hp_size); > + > + /* check if name already exists */ > + if (name && (find_block_by_name(name) >= 0)) { > + /* Found a block with the same name */ > + odp_spinlock_unlock(&ishm_tbl->lock); > + ODP_ERR("name \"%s\" already used.\n", name); > + return -1; > + } > + > + /* grab a new entry: */ > + for (new_index = 0; new_index < ISHM_MAX_NB_BLOCKS; new_index++) { > + if (ishm_tbl->block[new_index].len == 0) { > + /* Found free block */ > + break; > + } > + } > + > + /* check if we have reached the maximum number of allocation: */ > + if (new_index >= ISHM_MAX_NB_BLOCKS) { > + odp_spinlock_unlock(&ishm_tbl->lock); > + ODP_ERR("ISHM_MAX_NB_BLOCKS limit reached!\n"); > + return -1; > + } > + > + new_block = &ishm_tbl->block[new_index]; > + > + /* save block name (if any given): */ > + if (name) > + strncpy(new_block->name, name, ISHM_NAME_MAXLEN - 1); > + else > + new_block->name[0] = 0; > + > + /* Try first huge pages when possible and needed: */ > + if (page_hp_size && (alloc_size > page_sz)) { > + /* at least, alignment in VA should match page size, but > user > + * can request more: If the user requirement exceeds the > page > + * size then we have to make sure the block will be mapped > at > + * the same address every where, otherwise alignment may be > + * be wrong for some process */ > + hp_align = align; > + if (hp_align < odp_sys_huge_page_size()) > + hp_align = odp_sys_huge_page_size(); > + else > + flags |= _ODP_ISHM_SINGLE_VA; > + len = alloc_hp_size; > + addr = do_map(new_index, len, hp_align, flags, 1, &fd); > + > + if (addr == NULL) > + ODP_DBG("No huge pages, fall back to normal pages, > " > + "check: /proc/sys/vm/nr_hugepages.\n"); > + else > + new_block->huge = 1; > + } > + > + /* try normal pages if huge pages failed */ > + if (addr == NULL) { > + /* at least, alignment in VA should match page size, but > user > + * can request more: If the user requirement exceeds the > page > + * size then we have to make sure the block will be mapped > at > + * the same address every where, otherwise alignment may be > + * be wrong for some process */ > + if (align < odp_sys_page_size()) > + align = odp_sys_page_size(); > + else > + flags |= _ODP_ISHM_SINGLE_VA; > + > + len = alloc_size; > + addr = do_map(new_index, len, align, flags, 0, &fd); > + new_block->huge = 0; > + } > + > + /* if neither huge pages or normal pages works, we cannot proceed: > */ > + if ((addr == NULL) || (len == 0)) { > + if ((new_block->filename[0]) && (fd >= 0)) > + close(fd); > + odp_spinlock_unlock(&ishm_tbl->lock); > + ODP_ERR("_ishm_reserve failed.\n"); > CID 168963 (#1 of 1): Resource leak (RESOURCE_LEAK)17. leaked_handle: Returning without closing handle fd leaks it. > + return -1; > + } > + > + /* remember block data and increment block seq number to mark > change */ > + new_block->len = len; > + new_block->user_len = size; > + new_block->flags = flags; > + new_block->user_flags = user_flags; > + new_block->seq++; > + new_block->refcnt = 1; > + new_block->main_odpthread = odp_thread_id(); > + new_block->start = addr; /* only for SINGLE_VA*/ > + > + /* the allocation succeeded: update the process local view */ > + new_proc_entry = ishm_proctable->nb_entries++; > + ishm_proctable->entry[new_proc_entry].block_index = new_index; > + ishm_proctable->entry[new_proc_entry].flags = flags; > + ishm_proctable->entry[new_proc_entry].seq = new_block->seq; > + ishm_proctable->entry[new_proc_entry].start = addr; > + ishm_proctable->entry[new_proc_entry].len = len; > + ishm_proctable->entry[new_proc_entry].fd = fd; > + > + /* register the file descriptor to the file descriptor server. */ > + _odp_fdserver_register_fd(FD_SRV_CTX_ISHM, new_index, fd); > + > + odp_spinlock_unlock(&ishm_tbl->lock); > + return new_index; > +} > + > +/* > + * Free and unmap internal shared memory: > + * The file descriptor is closed and the .../odp-* file deleted, > + * unless fd was externally provided at reserve() time. > + * return 0 if OK, and -1 on error. > + * Mutex must be assured by the caller. > + */ > +static int block_free(int block_index) > +{ > + int proc_index; > + ishm_block_t *block; /* entry in the main block table*/ > + int last; > + > + if ((block_index < 0) || > + (block_index >= ISHM_MAX_NB_BLOCKS) || > + (ishm_tbl->block[block_index].len == 0)) { > + ODP_ERR("Request to free an invalid block\n"); > + return -1; > + } > + > + block = &ishm_tbl->block[block_index]; > + > + proc_index = procfind_block(block_index); > + if (proc_index >= 0) { > + /* close the fd, unless if it was externaly provided */ > CID 168952 (#1 of 1): Array compared against 0 (NO_EFFECT)array_null: Comparing an array to null is not useful: block->filename, since the test will always evaluate as true. Was block->filename formerly declared as a pointer? + if ((block->filename[0] != 0) || > + (odp_thread_id() != block->main_odpthread)) > + close(ishm_proctable->entry[proc_index].fd); > + > + /* remove the mapping and possible fragment */ > + do_unmap(ishm_proctable->entry[proc_index].start, > + block->len, > + ishm_proctable->entry[proc_index].flags, > + block_index); > + > + /* remove entry from process local table: */ > + last = ishm_proctable->nb_entries - 1; > + ishm_proctable->entry[proc_index] = > + ishm_proctable->entry[last]; > + ishm_proctable->nb_entries = last; > + } else { > + /* just possibly free the fragment as no mapping exist > here: */ > + do_unmap(NULL, 0, block->flags, block_index); > + } > + > + /* remove the .../odp-* file, unless fd was external: */ > + if (block->filename[0] != 0) > + unlink(block->filename); > + > + /* deregister the file descriptor from the file descriptor server. > */ > + _odp_fdserver_deregister_fd(FD_SRV_CTX_ISHM, block_index); > + > + /* mark the block as free in the main block table: */ > + block->len = 0; > + > + /* mark the change so other processes see this entry as obsolete: > */ > + block->seq++; > + > + return 0; > +} > + > +/* > + * Free and unmap internal shared memory, intentified by its block number: > + * return -1 on error. 0 if OK. > + */ > +int _odp_ishm_free_by_index(int block_index) > +{ > + int ret; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + ret = block_free(block_index); > + odp_spinlock_unlock(&ishm_tbl->lock); > + return ret; > +} > + > +/* > + * free and unmap internal shared memory, intentified by its block name: > + * return -1 on error. 0 if OK. > + */ > +int _odp_ishm_free_by_name(const char *name) > +{ > + int block_index; > + int ret; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + /* search the block in main ishm table */ > + block_index = find_block_by_name(name); > + if (block_index < 0) { > + ODP_ERR("Request to free an non existing block..." > + " (double free?)\n"); > + odp_spinlock_unlock(&ishm_tbl->lock); > + return -1; > + } > + > + ret = block_free(block_index); > + odp_spinlock_unlock(&ishm_tbl->lock); > + return ret; > +} > + > +/* > + * Free and unmap internal shared memory identified by address: > + * return -1 on error. 0 if OK. > + */ > +int _odp_ishm_free_by_address(void *addr) > +{ > + int block_index; > + int ret; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + /* search the block in main ishm table */ > + block_index = find_block_by_address(addr); > + if (block_index < 0) { > + ODP_ERR("Request to free an non existing block..." > + " (double free?)\n"); > + odp_spinlock_unlock(&ishm_tbl->lock); > + return -1; > + } > + > + ret = block_free(block_index); > + > + odp_spinlock_unlock(&ishm_tbl->lock); > + return ret; > +} > + > +/* > + * Lookup for an ishm shared memory, identified by its block index > + * in the main ishm block table. > + * Map this ishm area in the process VA (if not already present). > + * Returns the block user address or NULL on error. > + * Mutex must be assured by the caller. > + */ > +static void *block_lookup(int block_index) > +{ > + int proc_index; > + int fd = -1; > + ishm_block_t *block; > + void *mapped_addr; > + int new_entry; > + > + if ((block_index < 0) || > + (block_index >= ISHM_MAX_NB_BLOCKS) || > + (ishm_tbl->block[block_index].len == 0)) { > + ODP_ERR("Request to lookup an invalid block\n"); > + return NULL; > + } > + > + /* search it in process table: if there, this process knows it > already*/ > + proc_index = procfind_block(block_index); > + if (proc_index >= 0) > + return ishm_proctable->entry[proc_index].start; > + > + /* this ishm is not known by this process, yet: we create the > mapping.*/ > + fd = _odp_fdserver_lookup_fd(FD_SRV_CTX_ISHM, block_index); > + if (fd < 0) { > + ODP_ERR("Could not find ishm file descriptor (BUG!)\n"); > + return NULL; > + } > + > + /* perform the mapping */ > + block = &ishm_tbl->block[block_index]; > + > + mapped_addr = do_remap(block_index, fd); > + if (mapped_addr == NULL) { > + ODP_ERR(" lookup: Could not map existing shared > memory!\n"); > + return NULL; > + } > + > + /* the mapping succeeded: update the process local view */ > + new_entry = ishm_proctable->nb_entries++; > + ishm_proctable->entry[new_entry].block_index = block_index; > + ishm_proctable->entry[new_entry].flags = block->flags; > + ishm_proctable->entry[new_entry].seq = block->seq; > + ishm_proctable->entry[new_entry].start = mapped_addr; > + ishm_proctable->entry[new_entry].len = block->len; > + ishm_proctable->entry[new_entry].fd = fd; > + block->refcnt++; > + > + return mapped_addr; > +} > + > +/* > + * Lookup for an ishm shared memory, identified by its block_index. > + * Maps this ishmem area in the process VA (if not already present). > + * Returns the block user address, or NULL if the index > + * does not match any known ishm blocks. > + */ > +void *_odp_ishm_lookup_by_index(int block_index) > +{ > + void *ret; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + ret = block_lookup(block_index); > + odp_spinlock_unlock(&ishm_tbl->lock); > + return ret; > +} > + > +/* > + * Lookup for an ishm shared memory, identified by its block name. > + * Map this ishm area in the process VA (if not already present). > + * Return the block index, or -1 if the index > + * does not match any known ishm blocks. > + */ > +int _odp_ishm_lookup_by_name(const char *name) > +{ > + int block_index; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + /* search the block in main ishm table: return -1 if not found: */ > + block_index = find_block_by_name(name); > + if ((block_index < 0) || (!block_lookup(block_index))) { > + odp_spinlock_unlock(&ishm_tbl->lock); > + return -1; > + } > + > + odp_spinlock_unlock(&ishm_tbl->lock); > + return block_index; > +} > + > +/* > + * Lookup for an ishm shared memory block, identified by its VA address. > + * This works only if the block has already been looked-up (mapped) by the > + * current process or it it was created with the _ODP_ISHM_SINGLE_VA flag. > + * Map this ishm area in the process VA (if not already present). > + * Return the block index, or -1 if the address > + * does not match any known ishm blocks. > + */ > +int _odp_ishm_lookup_by_address(void *addr) > +{ > + int block_index; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + /* search the block in main ishm table: return -1 if not found: */ > + block_index = find_block_by_address(addr); > + if ((block_index < 0) || (!block_lookup(block_index))) { > + odp_spinlock_unlock(&ishm_tbl->lock); > + return -1; > + } > + > + odp_spinlock_unlock(&ishm_tbl->lock); > + return block_index; > +} > + > +/* > + * Returns the VA address of a given block (which has to be known in the > current > + * process). Returns NULL if the block is unknown. > + */ > +void *_odp_ishm_address(int block_index) > +{ > + int proc_index; > + void *addr; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + if ((block_index < 0) || > + (block_index >= ISHM_MAX_NB_BLOCKS) || > + (ishm_tbl->block[block_index].len == 0)) { > + ODP_ERR("Request for address on an invalid block\n"); > + odp_spinlock_unlock(&ishm_tbl->lock); > + return NULL; > + } > + > + proc_index = procfind_block(block_index); > + if (proc_index < 0) { > + odp_spinlock_unlock(&ishm_tbl->lock); > + return NULL; > + } > + > + addr = ishm_proctable->entry[proc_index].start; > + odp_spinlock_unlock(&ishm_tbl->lock); > + return addr; > +} > + > +int _odp_ishm_info(int block_index, _odp_ishm_info_t *info) > +{ > + int proc_index; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + if ((block_index < 0) || > + (block_index >= ISHM_MAX_NB_BLOCKS) || > + (ishm_tbl->block[block_index].len == 0)) { > + odp_spinlock_unlock(&ishm_tbl->lock); > + ODP_ERR("Request for info on an invalid block\n"); > + return -1; > + } > + > + /* search it in process table: if not there, need to map*/ > + proc_index = procfind_block(block_index); > + if (proc_index < 0) { > + odp_spinlock_unlock(&ishm_tbl->lock); > + return -1; > + } > + > + info->name = ishm_tbl->block[block_index].name; > + info->addr = ishm_proctable->entry[proc_index].start; > + info->size = ishm_tbl->block[block_index].user_len; > + info->page_size = ishm_tbl->block[block_index].huge ? > + odp_sys_huge_page_size() : odp_sys_page_size(); > + info->flags = ishm_tbl->block[block_index].flags; > + info->user_flags = ishm_tbl->block[block_index].user_flags; > + > + odp_spinlock_unlock(&ishm_tbl->lock); > + return 0; > +} > + > +int _odp_ishm_init_global(void) > +{ > + void *addr; > + void *spce_addr; > + int i; > + > + if (!odp_global_data.hugepage_info.default_huge_page_dir) > + ODP_DBG("NOTE: No support for huge pages\n"); > + else > + ODP_DBG("Huge pages mount point is: %s\n", > + odp_global_data.hugepage_info. > default_huge_page_dir); > + > + /* allocate space for the internal shared mem block table: */ > + addr = mmap(NULL, sizeof(ishm_table_t), > + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, > -1, 0); > + if (addr == MAP_FAILED) { > + ODP_ERR("unable to mmap the main block table\n."); > + goto init_glob_err1; > + } > + ishm_tbl = addr; > + memset(ishm_tbl, 0, sizeof(ishm_table_t)); > + ishm_tbl->dev_seq = 0; > + odp_spinlock_init(&ishm_tbl->lock); > + > + /* allocate space for the internal shared mem fragment table: */ > + addr = mmap(NULL, sizeof(ishm_ftable_t), > + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, > -1, 0); > + if (addr == MAP_FAILED) { > + ODP_ERR("unable to mmap the main fragment table\n."); > + goto init_glob_err2; > + } > + ishm_ftbl = addr; > + memset(ishm_ftbl, 0, sizeof(ishm_ftable_t)); > + > + /* > + *reserve the address space for _ODP_ISHM_SINGLE_VA reserved > blocks, > + * only address space! > + */ > + spce_addr = _odp_ishmphy_book_va(ODP_CONFIG_ISHM_VA_PREALLOC_SZ, > + odp_sys_huge_page_size()); > + if (!spce_addr) { > + ODP_ERR("unable to reserve virtual space\n."); > + goto init_glob_err3; > + } > + > + /* use the first fragment descriptor to describe to whole VA > space: */ > + ishm_ftbl->fragment[0].block_index = -1; > + ishm_ftbl->fragment[0].start = spce_addr; > + ishm_ftbl->fragment[0].len = ODP_CONFIG_ISHM_VA_PREALLOC_SZ; > + ishm_ftbl->fragment[0].prev = NULL; > + ishm_ftbl->fragment[0].next = NULL; > + ishm_ftbl->used_fragmnts = &ishm_ftbl->fragment[0]; > + > + /* and put all other fragment descriptors in the unused list: */ > + for (i = 1; i < ISHM_NB_FRAGMNTS - 1; i++) { > + ishm_ftbl->fragment[i].prev = NULL; > + ishm_ftbl->fragment[i].next = &ishm_ftbl->fragment[i + > 1]; > + } > + ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].prev = NULL; > + ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].next = NULL; > + ishm_ftbl->unused_fragmnts = &ishm_ftbl->fragment[1]; > + > + return 0; > + > +init_glob_err3: > + if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) > + ODP_ERR("unable to munmap main fragment table\n."); > +init_glob_err2: > + if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) > + ODP_ERR("unable to munmap main block table\n."); > +init_glob_err1: > + return -1; > +} > + > +int _odp_ishm_init_local(void) > +{ > + int i; > + int block_index; > + > + /* > + * the ishm_process table is local to each linux process > + * Check that no other linux threads (of same or ancestor > processes) > + * have already created the table, and create it if needed. > + * We protect this with the general ishm lock to avoid > + * init race condition of different running threads. > + */ > + odp_spinlock_lock(&ishm_tbl->lock); > + if (!ishm_proctable) { > + ishm_proctable = malloc(sizeof(ishm_proctable_t)); > + if (!ishm_proctable) { > + odp_spinlock_unlock(&ishm_tbl->lock); > + return -1; > + } > + memset(ishm_proctable, 0, sizeof(ishm_proctable_t)); > + } > + if (syscall(SYS_gettid) != getpid()) > + ishm_proctable->thrd_refcnt++; /* new linux thread */ > + else > + ishm_proctable->thrd_refcnt = 1;/* new linux process */ > + > + /* > + * if this ODP thread is actually a new linux process, (as opposed > + * to a pthread), i.e, we just forked, then all shmem blocks > + * of the parent process are mapped into this child by inheritance. > + * (The process local table is inherited as well). We hence have to > + * increase the process refcount for each of the inherited > mappings: > + */ > + if (syscall(SYS_gettid) == getpid()) { > + for (i = 0; i < ishm_proctable->nb_entries; i++) { > + block_index = ishm_proctable->entry[i]. > block_index; > + ishm_tbl->block[block_index].refcnt++; > + } > + } > + > + odp_spinlock_unlock(&ishm_tbl->lock); > + return 0; > +} > + > +int _odp_ishm_term_global(void) > +{ > + int ret = 0; > + > + /* free the fragment table */ > + if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) { > + ret = -1; > + ODP_ERR("unable to munmap fragment table\n."); > + } > + /* free the block table */ > + if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) { > + ret = -1; > + ODP_ERR("unable to munmap main table\n."); > + } > + > + /* free the reserved VA space */ > + if (_odp_ishmphy_unbook_va()) > + ret = -1; > + > + return ret; > +} > + > +int _odp_ishm_term_local(void) > +{ > + int i; > + int proc_table_refcnt = 0; > + int block_index; > + ishm_block_t *block; > + > + odp_spinlock_lock(&ishm_tbl->lock); > + procsync(); > + > + /* > + * The ishm_process table is local to each linux process > + * Check that no other linux threads (of this linux process) > + * still needs the table, and free it if so. > + * We protect this with the general ishm lock to avoid > + * term race condition of different running threads. > + */ > + proc_table_refcnt = --ishm_proctable->thrd_refcnt; > + if (!proc_table_refcnt) { > + /* > + * this is the last thread of this process... > + * All mappings for this process are about to be lost... > + * Go through the table of visible blocks for this process, > + * decreasing the refcnt of each visible blocks, and > issuing > + * warning for those no longer referenced by any process. > + * Note that non-referenced blocks are nor freeed: this is > + * deliberate as this would imply that the sementic of the > + * freeing function would differ depending on whether we > run > + * with odp_thread as processes or pthreads. With this > approach, > + * the user should always free the blocks manually, which > is > + * more consistent > + */ > + for (i = 0; i < ishm_proctable->nb_entries; i++) { > + block_index = ishm_proctable->entry[i]. > block_index; > + block = &ishm_tbl->block[block_index]; > + if ((--block->refcnt) <= 0) { > + block->refcnt = 0; > + ODP_DBG("Warning: block %d: name:%s " > + "no longer referenced\n", > + i, > + ishm_tbl->block[i].name[0] ? > + ishm_tbl->block[i].name : "<no > name>"); > + } > + } > + > + free(ishm_proctable); > + ishm_proctable = NULL; > + } > + > + odp_spinlock_unlock(&ishm_tbl->lock); > + return 0; > +} > diff --git a/platform/linux-generic/_ishmphy.c b/platform/linux-generic/_ > ishmphy.c > new file mode 100644 > index 0000000..cf2d531 > --- /dev/null > +++ b/platform/linux-generic/_ishmphy.c > @@ -0,0 +1,168 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +/* > + * This file handles the lower end of the ishm memory allocator: > + * It performs the physical mappings. > + */ > +#include <odp_posix_extensions.h> > +#include <odp_config_internal.h> > +#include <odp_internal.h> > +#include <odp/api/align.h> > +#include <odp/api/system_info.h> > +#include <odp/api/debug.h> > +#include <odp_debug_internal.h> > +#include <odp_align_internal.h> > +#include <_ishm_internal.h> > +#include <_ishmphy_internal.h> > + > +#include <stdlib.h> > +#include <stdio.h> > +#include <unistd.h> > +#include <string.h> > +#include <errno.h> > +#include <sys/mman.h> > +#include <sys/stat.h> > +#include <fcntl.h> > +#include <sys/types.h> > +#include <sys/wait.h> > +#include <_ishmphy_internal.h> > + > +static void *common_va_address; > +static uint64_t common_va_len; > + > +#ifndef MAP_ANONYMOUS > +#define MAP_ANONYMOUS MAP_ANON > +#endif > + > +/* Book some virtual address space > + * This function is called at odp_init_global() time to pre-book some > + * virtual address space inherited by all odpthreads (i.e. descendant > + * processes and threads) and later used to guarantee the unicity the > + * the mapping VA address when memory is reserver with the > _ODP_ISHM_SINGLE_VA > + * flag. > + * returns the address of the mapping or NULL on error. > + */ > +void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align) > +{ > + void *addr; > + > + addr = mmap(NULL, len + align, PROT_NONE, > + MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); > + if (addr == MAP_FAILED) { > + ODP_ERR("_ishmphy_book_va failure\n"); > + return NULL; > + } > + > + madvise(addr, len, MADV_DONTNEED); > + > + common_va_address = addr; > + common_va_len = len; > + > + /* return the nearest aligned address: */ > + return (void *)(((uintptr_t)addr + align - 1) & (-align)); > +} > + > +/* Un-book some virtual address space > + * This function is called at odp_term_global() time to unbook > + * the virtual address space booked by _ishmphy_book_va() > + */ > +int _odp_ishmphy_unbook_va(void) > +{ > + int ret; > + > + ret = munmap(common_va_address, common_va_len); > + if (ret) > + ODP_ERR("_unishmphy_book_va failure\n"); > + return ret; > +} > + > +/* > + * do a mapping: > + * Performs a mapping of the provided file descriptor to the process VA > + * space. If the _ODP_ISHM_SINGLE_VA flag is set, 'start' is assumed to be > + * the VA address where the mapping is to be done. > + * If the flag is not set, a new VA address is taken. > + * returns the address of the mapping or NULL on error. > + */ > +void *_odp_ishmphy_map(int fd, void *start, uint64_t size, > + int flags) > +{ > + void *mapped_addr; > + int mmap_flags = 0; > + > + if (flags & _ODP_ISHM_SINGLE_VA) { > + if (!start) { > + ODP_ERR("failure: missing address\n"); > + return NULL; > + } > + /* maps over fragment of reserved VA: */ > + mapped_addr = mmap(start, size, PROT_READ | PROT_WRITE, > + MAP_SHARED | MAP_FIXED | mmap_flags, > fd, 0); > + } else { > + /* just do a new mapping in the VA space: */ > + mapped_addr = mmap(NULL, size, PROT_READ | PROT_WRITE, > + MAP_SHARED | mmap_flags, fd, 0); > + } > + > + if (mapped_addr == MAP_FAILED) { > + ODP_ERR("mmap failed:%s\n", strerror(errno)); > + return NULL; > + } > + > + /* if locking is requested, lock it...*/ > + if (flags & _ODP_ISHM_LOCK) { > + if (mlock(mapped_addr, size)) { > + if (munmap(mapped_addr, size)) > + ODP_ERR("munmap failed:%s\n", > strerror(errno)); > + ODP_ERR("mlock failed:%s\n", strerror(errno)); > + return NULL; > + } > + } > + return mapped_addr; > +} > + > +/* free a mapping: > + * If the _ODP_ISHM_SINGLE_VA flag was given at creation time the virtual > + * address range must be returned to the preoallocated "pool". this is > + * done by mapping non accessibly memory there (hence blocking the VA but > + * releasing the physical memory). > + * If the _ODP_ISHM_SINGLE_VA flag was not given, both physical memory and > + * virtual address space are realeased by calling the normal munmap. > + * return 0 on success or -1 on error. > + */ > +int _odp_ishmphy_unmap(void *start, uint64_t len, int flags) > +{ > + void *addr; > + int ret; > + int mmap_flgs; > + > + mmap_flgs = MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS | MAP_NORESERVE; > + > + /* if locking was requested, unlock...*/ > + if (flags & _ODP_ISHM_LOCK) > + munlock(start, len); > + > + if (flags & _ODP_ISHM_SINGLE_VA) { > + /* map unnaccessible memory overwrites previous mapping > + * and free the physical memory, but guarantees to block > + * the VA range from other mappings > + */ > + addr = mmap(start, len, PROT_NONE, mmap_flgs, -1, 0); > + if (addr == MAP_FAILED) { > + ODP_ERR("_ishmphy_free failure for > ISHM_SINGLE_VA\n"); > + return -1; > + } > + madvise(start, len, MADV_DONTNEED); > + return 0; > + } > + > + /* just release the mapping */ > + ret = munmap(start, len); > + if (ret) > + ODP_ERR("_ishmphy_free failure: %s\n", strerror(errno)); > + return ret; > +} > diff --git a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h > b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h > deleted file mode 120000 > index e86e132..0000000 > --- a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h > +++ /dev/null > @@ -1 +0,0 @@ > -../../../default/odp/api/cpu_arch.h > \ No newline at end of file > diff --git a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h > b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h > new file mode 100644 > index 0000000..22b1da2 > --- /dev/null > +++ b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h > @@ -0,0 +1,24 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#ifndef ODP_PLAT_CPU_ARCH_H_ > +#define ODP_PLAT_CPU_ARCH_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#define _ODP_CACHE_LINE_SIZE 64 > + > +static inline void odp_cpu_pause(void) > +{ > +} > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c > b/platform/linux-generic/arch/arm/odp_cpu_arch.c > deleted file mode 120000 > index deebc47..0000000 > --- a/platform/linux-generic/arch/arm/odp_cpu_arch.c > +++ /dev/null > @@ -1 +0,0 @@ > -../default/odp_cpu_arch.c > \ No newline at end of file > diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c > b/platform/linux-generic/arch/arm/odp_cpu_arch.c > new file mode 100644 > index 0000000..2ac223e > --- /dev/null > +++ b/platform/linux-generic/arch/arm/odp_cpu_arch.c > @@ -0,0 +1,48 @@ > +/* Copyright (c) 2015, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#include <odp_posix_extensions.h> > + > +#include <stdlib.h> > +#include <time.h> > + > +#include <odp/api/cpu.h> > +#include <odp/api/hints.h> > +#include <odp/api/system_info.h> > +#include <odp_debug_internal.h> > + > +#define GIGA 1000000000 > + > +uint64_t odp_cpu_cycles(void) > +{ > + struct timespec time; > + uint64_t sec, ns, hz, cycles; > + int ret; > + > + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time); > + > + if (ret != 0) > + ODP_ABORT("clock_gettime failed\n"); > + > + hz = odp_cpu_hz_max(); > + sec = (uint64_t)time.tv_sec; > + ns = (uint64_t)time.tv_nsec; > + > + cycles = sec * hz; > + cycles += (ns * hz) / GIGA; > + > + return cycles; > +} > + > +uint64_t odp_cpu_cycles_max(void) > +{ > + return UINT64_MAX; > +} > + > +uint64_t odp_cpu_cycles_resolution(void) > +{ > + return 1; > +} > diff --git a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c > b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c > deleted file mode 120000 > index 39962b8..0000000 > --- a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c > +++ /dev/null > @@ -1 +0,0 @@ > -../default/odp_sysinfo_parse.c > \ No newline at end of file > diff --git a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c > b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c > new file mode 100644 > index 0000000..53e2aae > --- /dev/null > +++ b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c > @@ -0,0 +1,27 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#include <odp_internal.h> > +#include <odp_debug_internal.h> > +#include <string.h> > + > +int cpuinfo_parser(FILE *file ODP_UNUSED, system_info_t *sysinfo) > +{ > + int i; > + > + ODP_DBG("Warning: use dummy values for freq and model string\n"); > + for (i = 0; i < MAX_CPU_NUMBER; i++) { > + sysinfo->cpu_hz_max[i] = 1400000000; > + strcpy(sysinfo->model_str[i], "UNKNOWN"); > + } > + > + return 0; > +} > + > +uint64_t odp_cpu_hz_current(int id ODP_UNUSED) > +{ > + return 0; > +} > diff --git a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c > b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c > deleted file mode 120000 > index deebc47..0000000 > --- a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c > +++ /dev/null > @@ -1 +0,0 @@ > -../default/odp_cpu_arch.c > \ No newline at end of file > diff --git a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c > b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c > new file mode 100644 > index 0000000..2ac223e > --- /dev/null > +++ b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c > @@ -0,0 +1,48 @@ > +/* Copyright (c) 2015, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#include <odp_posix_extensions.h> > + > +#include <stdlib.h> > +#include <time.h> > + > +#include <odp/api/cpu.h> > +#include <odp/api/hints.h> > +#include <odp/api/system_info.h> > +#include <odp_debug_internal.h> > + > +#define GIGA 1000000000 > + > +uint64_t odp_cpu_cycles(void) > +{ > + struct timespec time; > + uint64_t sec, ns, hz, cycles; > + int ret; > + > + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time); > + > + if (ret != 0) > + ODP_ABORT("clock_gettime failed\n"); > + > + hz = odp_cpu_hz_max(); > + sec = (uint64_t)time.tv_sec; > + ns = (uint64_t)time.tv_nsec; > + > + cycles = sec * hz; > + cycles += (ns * hz) / GIGA; > + > + return cycles; > +} > + > +uint64_t odp_cpu_cycles_max(void) > +{ > + return UINT64_MAX; > +} > + > +uint64_t odp_cpu_cycles_resolution(void) > +{ > + return 1; > +} > diff --git a/platform/linux-generic/include/_fdserver_internal.h > b/platform/linux-generic/include/_fdserver_internal.h > index 480ac02..22b2802 100644 > --- a/platform/linux-generic/include/_fdserver_internal.h > +++ b/platform/linux-generic/include/_fdserver_internal.h > @@ -23,6 +23,7 @@ extern "C" { > */ > typedef enum fd_server_context { > FD_SRV_CTX_NA, /* Not Applicable */ > + FD_SRV_CTX_ISHM, > FD_SRV_CTX_END, /* upper enum limit */ > } fd_server_context_e; > > diff --git a/platform/linux-generic/include/_ishm_internal.h > b/platform/linux-generic/include/_ishm_internal.h > new file mode 100644 > index 0000000..7d27477 > --- /dev/null > +++ b/platform/linux-generic/include/_ishm_internal.h > @@ -0,0 +1,45 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#ifndef ODP_ISHM_INTERNAL_H_ > +#define ODP_ISHM_INTERNAL_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +/* flags available at ishm_reserve: */ > +#define _ODP_ISHM_SINGLE_VA 1 > +#define _ODP_ISHM_LOCK 2 > + > +/** > + * Shared memory block info > + */ > +typedef struct _odp_ishm_info_t { > + const char *name; /**< Block name */ > + void *addr; /**< Block address */ > + uint64_t size; /**< Block size in bytes */ > + uint64_t page_size; /**< Memory page size */ > + uint32_t flags; /**< _ODP_ISHM_* flags */ > + uint32_t user_flags;/**< user specific flags */ > +} _odp_ishm_info_t; > + > +int _odp_ishm_reserve(const char *name, uint64_t size, int fd, uint32_t > align, > + uint32_t flags, uint32_t user_flags); > +int _odp_ishm_free_by_index(int block_index); > +int _odp_ishm_free_by_name(const char *name); > +int _odp_ishm_free_by_address(void *addr); > +void *_odp_ishm_lookup_by_index(int block_index); > +int _odp_ishm_lookup_by_name(const char *name); > +int _odp_ishm_lookup_by_address(void *addr); > +void *_odp_ishm_address(int block_index); > +int _odp_ishm_info(int block_index, _odp_ishm_info_t *info); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/include/_ishmphy_internal.h > b/platform/linux-generic/include/_ishmphy_internal.h > new file mode 100644 > index 0000000..4fe560f > --- /dev/null > +++ b/platform/linux-generic/include/_ishmphy_internal.h > @@ -0,0 +1,25 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#ifndef _ISHMPHY_INTERNAL_H > +#define _ISHMPHY_INTERNAL_H > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +#include <stdint.h> > + > +void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align); > +int _odp_ishmphy_unbook_va(void); > +void *_odp_ishmphy_map(int fd, void *start, uint64_t size, int flags); > +int _odp_ishmphy_unmap(void *start, uint64_t len, int flags); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/include/ishmphy_internal.h > b/platform/linux-generic/include/ishmphy_internal.h > new file mode 100644 > index 0000000..0bc4207 > --- /dev/null > +++ b/platform/linux-generic/include/ishmphy_internal.h > @@ -0,0 +1,24 @@ > +/* Copyright (c) 2016, Linaro Limited > + * All rights reserved. > + * > + * SPDX-License-Identifier: BSD-3-Clause > + */ > + > +#ifndef _ISHMPHY_INTERNAL_H_ > +#define _ISHMPHY_INTERNAL_H_ > + > +#ifdef __cplusplus > +extern "C" { > +#endif > + > +void *_ishmphy_book_va(uint64_t len); > +int _ishmphy_unbook_va(void); > +void *_ishmphy_map(int fd, void *start, uint64_t size, > + int flags, int mmap_flags); > +int _ishmphy_unmap(void *start, uint64_t len, int flags); > + > +#ifdef __cplusplus > +} > +#endif > + > +#endif > diff --git a/platform/linux-generic/include/odp_config_internal.h > b/platform/linux-generic/include/odp_config_internal.h > index 989ea08..3eabc7b 100644 > --- a/platform/linux-generic/include/odp_config_internal.h > +++ b/platform/linux-generic/include/odp_config_internal.h > @@ -110,6 +110,16 @@ extern "C" { > */ > #define ODP_CONFIG_SHM_BLOCKS (ODP_CONFIG_POOLS + 48) > > +/* > + * Size of the virtual address space pre-reserver for ISHM > + * > + * This is just virtual space preallocation size, not memory allocation. > + * This address space is used by ISHM to map things at a common address in > + * all ODP threads (when the _ODP_ISHM_SINGLE_VA flag is used). > + * In bytes. > + */ > +#define ODP_CONFIG_ISHM_VA_PREALLOC_SZ (536870912L) > + > #ifdef __cplusplus > } > #endif > diff --git a/platform/linux-generic/include/odp_internal.h > b/platform/linux-generic/include/odp_internal.h > index 2b21777..6ea8cf0 100644 > --- a/platform/linux-generic/include/odp_internal.h > +++ b/platform/linux-generic/include/odp_internal.h > @@ -60,6 +60,7 @@ enum init_stage { > TIME_INIT, > SYSINFO_INIT, > FDSERVER_INIT, > + ISHM_INIT, > SHM_INIT, > THREAD_INIT, > POOL_INIT, > @@ -128,6 +129,11 @@ int _odp_int_name_tbl_term_global(void); > int _odp_fdserver_init_global(void); > int _odp_fdserver_term_global(void); > > +int _odp_ishm_init_global(void); > +int _odp_ishm_init_local(void); > +int _odp_ishm_term_global(void); > +int _odp_ishm_term_local(void); > + > int cpuinfo_parser(FILE *file, system_info_t *sysinfo); > uint64_t odp_cpu_hz_current(int id); > > diff --git a/platform/linux-generic/odp_init.c > b/platform/linux-generic/odp_init.c > index ec84fd0..d33a3ae 100644 > --- a/platform/linux-generic/odp_init.c > +++ b/platform/linux-generic/odp_init.c > @@ -57,6 +57,12 @@ int odp_init_global(odp_instance_t *instance, > } > stage = FDSERVER_INIT; > > + if (_odp_ishm_init_global()) { > + ODP_ERR("ODP ishm init failed.\n"); > + goto init_failed; > + } > + stage = ISHM_INIT; > + > if (odp_shm_init_global()) { > ODP_ERR("ODP shm init failed.\n"); > goto init_failed; > @@ -224,6 +230,13 @@ int _odp_term_global(enum init_stage stage) > } > /* Fall through */ > > + case ISHM_INIT: > + if (_odp_ishm_term_global()) { > + ODP_ERR("ODP ishm term failed.\n"); > + rc = -1; > + } > + /* Fall through */ > + > case FDSERVER_INIT: > if (_odp_fdserver_term_global()) { > ODP_ERR("ODP fdserver term failed.\n"); > @@ -268,6 +281,12 @@ int odp_init_local(odp_instance_t instance, > odp_thread_type_t thr_type) > goto init_fail; > } > > + if (_odp_ishm_init_local()) { > + ODP_ERR("ODP ishm local init failed.\n"); > + goto init_fail; > + } > + stage = ISHM_INIT; > + > if (odp_shm_init_local()) { > ODP_ERR("ODP shm local init failed.\n"); > goto init_fail; > @@ -343,6 +362,13 @@ int _odp_term_local(enum init_stage stage) > } > /* Fall through */ > > + case ISHM_INIT: > + if (_odp_ishm_term_local()) { > + ODP_ERR("ODP ishm local term failed.\n"); > + rc = -1; > + } > + /* Fall through */ > + > default: > break; > } > -- > 2.7.4 > >
Hi Bill : The first is false positive, if I remember right The second has been addressed since coverity was run but I did not rerun as it obvioulsy had an impact on the "main repo stat" Christophe. On 19 August 2016 at 17:44, Bill Fischofer <bill.fischofer@linaro.org> wrote: > Coverity flagged a number of issues against this module (as well as > _fdserver.c). These should be addressed now rather than later: > > On Fri, Aug 19, 2016 at 10:48 AM, Christophe Milard < > christophe.milard@linaro.org> wrote: > >> A new ODP internal memory allocator, called ishm (for internal shmem) >> is introduced here. >> This memory allocator enables the following: >> - works for odpthreads being linux processes, regardless for fork time. >> - guarantees the uniqueness of the virtual space mapping address over >> all ODP threads (even processes and regardless of fork time), >> when the required _ODP_ISHM_SINGLE_VA flag is used. >> >> Signed-off-by: Christophe Milard <christophe.milard@linaro.org> >> --- >> platform/linux-generic/Makefile.am | 4 + >> platform/linux-generic/_ishm.c | 1336 >> ++++++++++++++++++++ >> platform/linux-generic/_ishmphy.c | 168 +++ >> platform/linux-generic/arch/arm/odp/api/cpu_arch.h | 25 +- >> platform/linux-generic/arch/arm/odp_cpu_arch.c | 49 +- >> .../linux-generic/arch/arm/odp_sysinfo_parse.c | 28 +- >> platform/linux-generic/arch/powerpc/odp_cpu_arch.c | 49 +- >> .../linux-generic/include/_fdserver_internal.h | 1 + >> platform/linux-generic/include/_ishm_internal.h | 45 + >> platform/linux-generic/include/_ishmphy_internal.h | 25 + >> platform/linux-generic/include/ishmphy_internal.h | 24 + >> .../linux-generic/include/odp_config_internal.h | 10 + >> platform/linux-generic/include/odp_internal.h | 6 + >> platform/linux-generic/odp_init.c | 26 + >> 14 files changed, 1792 insertions(+), 4 deletions(-) >> create mode 100644 platform/linux-generic/_ishm.c >> create mode 100644 platform/linux-generic/_ishmphy.c >> mode change 120000 => 100644 platform/linux-generic/arch/ar >> m/odp/api/cpu_arch.h >> mode change 120000 => 100644 platform/linux-generic/arch/ar >> m/odp_cpu_arch.c >> mode change 120000 => 100644 platform/linux-generic/arch/ar >> m/odp_sysinfo_parse.c >> mode change 120000 => 100644 platform/linux-generic/arch/po >> werpc/odp_cpu_arch.c >> create mode 100644 platform/linux-generic/include/_ishm_internal.h >> create mode 100644 platform/linux-generic/include/_ishmphy_internal.h >> create mode 100644 platform/linux-generic/include/ishmphy_internal.h >> >> diff --git a/platform/linux-generic/Makefile.am >> b/platform/linux-generic/Makefile.am >> index cb7c8e9..ac2b2da 100644 >> --- a/platform/linux-generic/Makefile.am >> +++ b/platform/linux-generic/Makefile.am >> @@ -113,6 +113,8 @@ odpdrvplatinclude_HEADERS = \ >> >> noinst_HEADERS = \ >> ${srcdir}/include/_fdserver_internal.h \ >> + ${srcdir}/include/_ishm_internal.h \ >> + ${srcdir}/include/_ishmphy_internal.h \ >> ${srcdir}/include/odp_align_internal.h \ >> ${srcdir}/include/odp_atomic_internal.h \ >> ${srcdir}/include/odp_buffer_inlines.h \ >> @@ -156,6 +158,8 @@ noinst_HEADERS = \ >> >> __LIB__libodp_linux_la_SOURCES = \ >> _fdserver.c \ >> + _ishm.c \ >> + _ishmphy.c \ >> odp_atomic.c \ >> odp_barrier.c \ >> odp_buffer.c \ >> diff --git a/platform/linux-generic/_ishm.c >> b/platform/linux-generic/_ishm.c >> new file mode 100644 >> index 0000000..a92d5cc >> --- /dev/null >> +++ b/platform/linux-generic/_ishm.c >> @@ -0,0 +1,1336 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +/* This file handles the internal shared memory: internal shared memory >> + * is memory which is sharable by all ODP threads regardless of how the >> + * ODP thread is implemented (pthread or process) and regardless of >> fork() >> + * time. >> + * Moreover, when reserved with the _ODP_ISHM_SINGLE_VA flag, >> + * internal shared memory is guaranteed to always be located at the same >> virtual >> + * address, i.e. pointers to internal shared memory are fully shareable >> + * between odp threads (regardless of thread type or fork time) in that >> case. >> + * Internal shared memory is mainly meant to be used internaly within ODP >> + * (hence its name), but may also be allocated by odp applications and >> drivers, >> + * in the future (through these interfaces). >> + * To guarrentee this full pointer shareability (when reserved with the >> + * _ODP_ISHM_SINGLE_VA flag) internal shared memory is handled as >> follows: >> + * At global_init time, a huge virtual address space reservation is >> performed. >> + * Note that this is just reserving virtual space, not physical memory. >> + * Because all ODP threads (pthreads or processes) are descendants of >> the ODP >> + * instantiation process, this VA space is inherited by all ODP threads. >> + * When internal shmem reservation actually occurs, and >> + * when reserved with the _ODP_ISHM_SINGLE_VA flag, physical memory is >> + * allocated, and mapped (MAP_FIXED) to some part in the huge >> preallocated >> + * address space area: >> + * because this virtual address space is common to all ODP threads, we >> + * know this mapping will succeed, and not clash with anything else. >> + * Hence, an ODP threads which perform a lookup for the same ishm block >> + * can map it at the same VA address. >> + * When internal shared memory is released, the physical memory is >> released >> + * and the corresponding virtual space returned to its "pool" of >> preallocated >> + * virtual space (assuming it was allocated from there). >> + * Note, though, that, if 2 linux processes share the same ishm block, >> + * the virtual space is marked as released as soon as one of the >> processes >> + * releases the ishm block, but the physical memory space is actually >> released >> + * by the kernel once all processes have done a ishm operation (i,e. a >> sync). >> + * This is due to the fact that linux does not contain any syscall to >> unmap >> + * memory from a different process. >> + * >> + * This file contains functions to handle the VA area (handling >> fragmentation >> + * and defragmentation resulting from different allocs/release) and also >> + * define the functions to allocate, release and lookup internal shared >> + * memory: >> + * _odp_ishm_reserve(), _odp_ishm_free*() and _odp_ishm_lookup*()... >> + */ >> +#include <odp_posix_extensions.h> >> +#include <odp_config_internal.h> >> +#include <odp_internal.h> >> +#include <odp/api/spinlock.h> >> +#include <odp/api/align.h> >> +#include <odp/api/system_info.h> >> +#include <odp/api/debug.h> >> +#include <odp_shm_internal.h> >> +#include <odp_debug_internal.h> >> +#include <odp_align_internal.h> >> +#include <_fdserver_internal.h> >> +#include <_ishm_internal.h> >> +#include <_ishmphy_internal.h> >> +#include <stdlib.h> >> +#include <stdio.h> >> +#include <unistd.h> >> +#include <string.h> >> +#include <errno.h> >> +#include <sys/mman.h> >> +#include <sys/stat.h> >> +#include <sys/syscall.h> >> +#include <fcntl.h> >> +#include <sys/types.h> >> +#include <inttypes.h> >> +#include <sys/wait.h> >> + >> +/* >> + * Maximum number of internal shared memory blocks. >> + * >> + * This the the number of separate ISHM areas that can be reserved >> concurrently >> + * (Note that freeing such blocks may take time, or possibly never happen >> + * if some of the block ownwers never procsync() after free). This number >> + * should take that into account) >> + */ >> +#define ISHM_MAX_NB_BLOCKS 128 >> + >> +/* >> + * Maximum internal shared memory block name length in chars >> + * probably taking the same number as SHM name size make sense at this >> stage >> + */ >> +#define ISHM_NAME_MAXLEN 32 >> + >> +/* >> + * Linux underlying file name: <directory>/odp-<odp_pid>-ishm-<name> >> + * The <name> part may be replaced by a sequence number if no specific >> + * name is given at reserve time >> + * <directory> is either /tmp or the hugepagefs mount point for default >> size. >> + * (searched at init time) >> + */ >> +#define ISHM_FILENAME_MAXLEN (ISHM_NAME_MAXLEN + 64) >> +#define ISHM_FILENAME_FORMAT "%s/odp-%d-ishm-%s" >> +#define ISHM_FILENAME_NORMAL_PAGE_DIR "/tmp" >> + >> +/* >> + * At worse case the virtual space gets so fragmented that there is >> + * a unallocated fragment between each allocated fragment: >> + * In that case, the number of fragments to take care of is twice the >> + * number of ISHM blocks + 1. >> + */ >> +#define ISHM_NB_FRAGMNTS (ISHM_MAX_NB_BLOCKS * 2 + 1) >> + >> +/* >> + * A block describes a piece of reserved memory: Any successful >> ishm_reserve() >> + * will allocate a block. A ishm_reserve() with the _ODP_ISHM_SINGLE_VA >> flag set >> + * will allocated both a block and a fragment. >> + * Blocks contain only global data common to all processes. >> + */ >> +typedef struct ishm_fragment ishm_fragment_t; /*forward decl. see below. >> */ >> +typedef struct ishm_block { >> + char name[ISHM_NAME_MAXLEN]; /* name for the ishm block (if >> any) */ >> + char filename[ISHM_FILENAME_MAXLEN]; /* name of the .../odp-* >> file */ >> + int main_odpthread; /* The thread which did the initial >> reserve*/ >> + uint32_t user_flags; /* any flags the user want to remember. >> */ >> + uint32_t flags; /* block creation flags. >> */ >> + uint64_t user_len; /* length, as requested at reserve >> time. */ >> + void *start; /* only valid if _ODP_ISHM_SINGLE_VA is >> set*/ >> + uint64_t len; /* length. multiple of page size. 0 if >> free*/ >> + ishm_fragment_t *fragment; /* used when _ODP_ISHM_SINGLE_VA is >> used */ >> + int huge; /* true if this segment is mapped using huge >> pages */ >> + uint64_t seq; /* sequence number, incremented on alloc and >> free */ >> + uint64_t refcnt;/* number of linux processes mapping this block >> */ >> +} ishm_block_t; >> + >> +/* >> + * Table of blocks describing allocated internal shared memory >> + * This table is visible to every ODP thread (linux process or pthreads). >> + * (it is allocated shared at odp init time and is therefore inherited >> by all) >> + * Table index is used as handle, so it cannot move!. Entry is regarded >> as >> + * free when len==0 >> + */ >> +typedef struct { >> + odp_spinlock_t lock; >> + uint64_t dev_seq; /* used when creating device names */ >> + ishm_block_t block[ISHM_MAX_NB_BLOCKS]; >> +} ishm_table_t; >> +static ishm_table_t *ishm_tbl; >> + >> +/* >> + * Process local table containing the list of (believed) allocated >> blocks seen >> + * from the current process. There is one such table per linux process. >> linux >> + * threads within a process shares this table. >> + * The contents within this table may become obsolete when other >> processes >> + * reserve/free ishm blocks. This is what the procsync() function >> + * catches by comparing the block sequence number with the one in this >> table. >> + * This table is filled at ishm_reserve and ishm_lookup time. >> + * Entries are removed at ishm_free or procsync time. >> + * Note that flags and len are present in this table and seems to be >> redundant >> + * with those present in the ishm block table: but this is not fully >> true: >> + * When ishm_sync() detects obsolete mappings and tries to remove them, >> + * the entry in the ishm block table is then obsolete, and the values >> which are >> + * found in this table must be used to perform the ummap. >> + * (and the values in the block tables are needed at lookup time...) >> + */ >> +typedef struct { >> + int thrd_refcnt; /* number of pthreads in this process, really */ >> + struct { >> + int block_index; /* entry in the ishm_tbl */ >> + uint32_t flags; /* flags used at creation time */ >> + uint64_t seq; >> + void *start; /* start of block (VA) >> */ >> + uint64_t len; /* length of block. multiple of page size >> */ >> + int fd; /* file descriptor used for this block >> */ >> + } entry[ISHM_MAX_NB_BLOCKS]; >> + int nb_entries; >> +} ishm_proctable_t; >> +static ishm_proctable_t *ishm_proctable; >> + >> +/* >> + * A fragment describes a piece of the shared virtual address space, >> + * and is allocated only when allocation is done with the >> _ODP_ISHM_SINGLE_VA >> + * flag: >> + * A fragment is said to be used when it actually does represent some >> + * portion of the virtual address space, and is said to be unused when >> + * it does not (so at start, one single fragment is used -describing the >> + * whole address space as unallocated-, and all others are unused). >> + * Fragments get used as address space fragmentation increases. >> + * A fragment is allocated if the piece of address space it >> + * describes is actually used by a shared memory block. >> + * Allocated fragments get their block_index set >=0. >> + */ >> +typedef struct ishm_fragment { >> + struct ishm_fragment *prev; /* not used when the fragment is >> unused */ >> + struct ishm_fragment *next; >> + void *start; /* start of segment (VA) */ >> + uintptr_t len; /* length of segment. multiple of page >> size */ >> + int block_index; /* -1 for unallocated fragments */ >> +} ishm_fragment_t; >> + >> +/* >> + * Table of fragments describing the common virtual address space: >> + * This table is visible to every ODP thread (linux process or pthreads). >> + * (it is allocated at odp init time and is therefore inherited by all) >> + */ >> +typedef struct { >> + ishm_fragment_t fragment[ISHM_NB_FRAGMNTS]; >> + ishm_fragment_t *used_fragmnts; /* ordered by increasing start >> addr */ >> + ishm_fragment_t *unused_fragmnts; >> +} ishm_ftable_t; >> +static ishm_ftable_t *ishm_ftbl; >> + >> +#ifndef MAP_ANONYMOUS >> +#define MAP_ANONYMOUS MAP_ANON >> +#endif >> + >> +/* prototypes: */ >> +static void procsync(void); >> + >> +/* >> + * Take a piece of the preallocated virtual space to fit "size" bytes. >> + * (best fit). Size must be rounded up to an integer number of pages >> size. >> + * Possibly split the fragment to keep track of remaining space. >> + * Returns the allocated fragment (best_fragmnt) and the corresponding >> address. >> + * External caller must ensure mutex before the call! >> + */ >> +static void *alloc_fragment(uintptr_t size, int block_index, intptr_t >> align, >> + ishm_fragment_t **best_fragmnt) >> +{ >> + ishm_fragment_t *fragmnt; >> + *best_fragmnt = NULL; >> + ishm_fragment_t *rem_fragmnt; >> + uintptr_t border;/* possible start of new fragment (next >> alignement) */ >> + intptr_t left; /* room remaining after, if the segment is >> allocated */ >> + uintptr_t remainder = ODP_CONFIG_ISHM_VA_PREALLOC_SZ; >> + >> + /* >> + * search for the best bit, i.e. search for the unallocated >> fragment >> + * would give less remainder if the new fragment was allocated >> within >> + * it: >> + */ >> + for (fragmnt = ishm_ftbl->used_fragmnts; >> + fragmnt; fragmnt = fragmnt->next) { >> + /* skip allocated segment: */ >> + if (fragmnt->block_index >= 0) >> + continue; >> + /* skip too short segment: */ >> + border = ((uintptr_t)fragmnt->start + align - 1) & >> (-align); >> + left = >> + ((uintptr_t)fragmnt->start + fragmnt->len) - (border + >> size); >> + if (left < 0) >> + continue; >> + /* remember best fit: */ >> + if ((uintptr_t)left < remainder) { >> + remainder = left; /* best, so far */ >> + *best_fragmnt = fragmnt; >> + } >> + } >> + >> + if (!(*best_fragmnt)) { >> + ODP_ERR("unable to get virtual address for shmem >> block!\n."); >> + return NULL; >> + } >> + >> + (*best_fragmnt)->block_index = block_index; >> + border = ((uintptr_t)(*best_fragmnt)->start + align - 1) & >> (-align); >> + >> + /* >> + * if there is room between previous fragment and new one, (due to >> + * alignement requirement) then fragment (split) the space between >> + * the end of the previous fragment and the beginning of the new >> one: >> + */ >> + if (border - (uintptr_t)(*best_fragmnt)->start > 0) { >> + /* frangment space, i.e. take a new fragment >> descriptor... */ >> + rem_fragmnt = ishm_ftbl->unused_fragmnts; >> + if (!rem_fragmnt) { >> + ODP_ERR("unable to get shmem fragment >> descriptor!\n."); >> + return NULL; >> + } >> + ishm_ftbl->unused_fragmnts = rem_fragmnt->next; >> + >> + /* and link it between best_fragmnt->prev and >> best_fragmnt */ >> + if ((*best_fragmnt)->prev) >> + (*best_fragmnt)->prev->next = rem_fragmnt; >> + else >> + ishm_ftbl->used_fragmnts = rem_fragmnt; >> + rem_fragmnt->prev = (*best_fragmnt)->prev; >> + (*best_fragmnt)->prev = rem_fragmnt; >> + rem_fragmnt->next = (*best_fragmnt); >> + >> + /* update length: rem_fragmnt getting space before border >> */ >> + rem_fragmnt->block_index = -1; >> + rem_fragmnt->start = (*best_fragmnt)->start; >> + rem_fragmnt->len = border - (uintptr_t)(*best_fragmnt)->st >> art; >> + (*best_fragmnt)->start = >> + (void *)((uintptr_t)rem_fragmnt->start + >> rem_fragmnt->len); >> + (*best_fragmnt)->len -= rem_fragmnt->len; >> + } >> + >> + /* if this was a perfect fit, i.e. no free space follows, we are >> done */ >> + if (remainder == 0) >> + return (*best_fragmnt)->start; >> + >> + /* otherwise, frangment space, i.e. take a new fragment >> descriptor... */ >> + rem_fragmnt = ishm_ftbl->unused_fragmnts; >> + if (!rem_fragmnt) { >> + ODP_ERR("unable to get shmem fragment descriptor!\n."); >> + return (*best_fragmnt)->start; >> + } >> + ishm_ftbl->unused_fragmnts = rem_fragmnt->next; >> + >> + /* ... double link it... */ >> + rem_fragmnt->next = (*best_fragmnt)->next; >> + rem_fragmnt->prev = (*best_fragmnt); >> + if ((*best_fragmnt)->next) >> + (*best_fragmnt)->next->prev = rem_fragmnt; >> + (*best_fragmnt)->next = rem_fragmnt; >> + >> + /* ... and keep track of the remainder */ >> + (*best_fragmnt)->len = size; >> + rem_fragmnt->len = remainder; >> + rem_fragmnt->start = (void *)((char *)(*best_fragmnt)->start + >> size); >> + rem_fragmnt->block_index = -1; >> + >> + return (*best_fragmnt)->start; >> +} >> + >> +/* >> + * Free a portion of virtual space. >> + * Possibly defragment, if the freed fragment is adjacent to another >> + * free virtual fragment. >> + * External caller must ensure mutex before the call! >> + */ >> +static void free_fragment(ishm_fragment_t *fragmnt) >> +{ >> + ishm_fragment_t *prev_f; >> + ishm_fragment_t *next_f; >> + >> + /* sanity check */ >> + if (!fragmnt) >> + return; >> + >> + prev_f = fragmnt->prev; >> + next_f = fragmnt->next; >> + >> + /* free the fragment */ >> + fragmnt->block_index = -1; >> + >> + /* check if the previous fragment is also free: if so, defragment >> */ >> + if (prev_f && (prev_f->block_index < 0)) { >> + fragmnt->start = prev_f->start; >> + fragmnt->len += prev_f->len; >> + if (prev_f->prev) { >> + prev_f->prev->next = fragmnt; >> + } else { >> + if (ishm_ftbl->used_fragmnts == prev_f) >> + ishm_ftbl->used_fragmnts = fragmnt; >> + else >> + ODP_ERR("corrupted fragment list!.\n"); >> + } >> + fragmnt->prev = prev_f->prev; >> + >> + /* put removed fragment in free list */ >> + prev_f->prev = NULL; >> + prev_f->next = ishm_ftbl->unused_fragmnts; >> + ishm_ftbl->unused_fragmnts = prev_f; >> + } >> + >> + /* check if the next fragment is also free: if so, defragment */ >> + if (next_f && (next_f->block_index < 0)) { >> + fragmnt->len += next_f->len; >> + if (next_f->next) >> + next_f->next->prev = fragmnt; >> + fragmnt->next = next_f->next; >> + >> + /* put removed fragment in free list */ >> + next_f->prev = NULL; >> + next_f->next = ishm_ftbl->unused_fragmnts; >> + ishm_ftbl->unused_fragmnts = next_f; >> + } >> +} >> + >> +/* >> + * Create file with size len. returns -1 on error >> + * Creates a file to /tmp/odp-<pid>-<sequence_or_name> (for normal >> pages) >> + * or /mnt/huge/odp-<pid>-<sequence_or_name> (for huge pages) >> + * Return the new file descriptor, or -1 on error. >> + */ >> +static int create_file(int block_index, int huge, uint64_t len) >> +{ >> + char *name; >> + int fd; >> + ishm_block_t *new_block; /* entry in the main block >> table */ >> + char seq_string[ISHM_FILENAME_MAXLEN]; /* used to construct >> filename*/ >> + char filename[ISHM_FILENAME_MAXLEN];/* filename in /tmp/ or >> /mnt/huge */ >> + int oflag = O_RDWR | O_CREAT | O_TRUNC; /* flags for open >> */ >> + >> + new_block = &ishm_tbl->block[block_index]; >> + name = new_block->name; >> + >> + /* create the filename: */ >> + snprintf(seq_string, ISHM_FILENAME_MAXLEN, "%08" PRIu64, >> + ishm_tbl->dev_seq++); >> + >> + /* huge dir must be known to create files there!: */ >> + if (huge && !odp_global_data.hugepage_info.default_huge_page_dir) >> + return -1; >> + >> + if (huge) >> + snprintf(filename, ISHM_FILENAME_MAXLEN, >> + ISHM_FILENAME_FORMAT, >> + odp_global_data.hugepage_info. >> default_huge_page_dir, >> + odp_global_data.main_pid, >> + (name && name[0]) ? name : seq_string); >> + else >> + snprintf(filename, ISHM_FILENAME_MAXLEN, >> + ISHM_FILENAME_FORMAT, >> + ISHM_FILENAME_NORMAL_PAGE_DIR, >> + odp_global_data.main_pid, >> + (name && name[0]) ? name : seq_string); >> + >> + fd = open(filename, oflag, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); >> + if (fd < 0) { >> + ODP_ERR("open failed for %s: %s.\n", >> + filename, strerror(errno)); >> + return -1; >> + } >> + >> + if (ftruncate(fd, len) == -1) { >> + ODP_ERR("ftruncate failed: fd=%d, err=%s.\n", >> + fd, strerror(errno)); >> + close(fd); >> + return -1; >> + } >> + >> + strncpy(new_block->filename, filename, ISHM_FILENAME_MAXLEN - 1); >> + >> + return fd; >> +} >> + >> +/* >> + * performs the mapping, possibly allocating a fragment of the >> pre-reserved >> + * VA space if the _ODP_ISHM_SINGLE_VA flag was given. >> + * Sets fd, and returns the mapping address. >> + * This funstion will also set the _ODP_ISHM_SINGLE_VA flag if the >> alignment >> + * requires it >> + * Mutex must be assured by the caller. >> + */ >> +static void *do_map(int block_index, uint64_t len, uint32_t align, >> + uint32_t flags, int huge, int *fd) >> +{ >> + ishm_block_t *new_block; /* entry in the main block >> table */ >> + void *addr = NULL; >> + void *mapped_addr; >> + ishm_fragment_t *fragment = NULL; >> + >> + new_block = &ishm_tbl->block[block_index]; >> + >> + /* >> + * Creates a file to /tmp/odp-<pid>-<sequence> (for normal pages) >> + * or /mnt/huge/odp-<pid>-<sequence> (for huge pages) >> + * unless a fd was already given >> + */ >> + if (*fd < 0) { >> + *fd = create_file(block_index, huge, len); >> + if (*fd < 0) >> + return NULL; >> + } else { >> + new_block->filename[0] = 0; >> + } >> + >> + /* allocate an address range in the prebooked VA area if needed */ >> + if (flags & _ODP_ISHM_SINGLE_VA) { >> + addr = alloc_fragment(len, block_index, align, &fragment); >> + if (!addr) { >> + ODP_ERR("alloc_fragment failed.\n"); >> + if (new_block->filename[0]) { >> + close(*fd); >> + *fd = -1; >> + unlink(new_block->filename); >> + } >> + return NULL; >> + } >> + ishm_tbl->block[block_index].fragment = fragment; >> + } >> + >> + /* try to mmap: */ >> + mapped_addr = _odp_ishmphy_map(*fd, addr, len, flags); >> + if (mapped_addr == NULL) { >> + if (flags & _ODP_ISHM_SINGLE_VA) >> + free_fragment(fragment); >> + if (new_block->filename[0]) { >> + close(*fd); >> + *fd = -1; >> + unlink(new_block->filename); >> + } >> + return NULL; >> + } >> + >> + new_block->huge = huge; >> + >> + return mapped_addr; >> +} >> + >> +/* >> + * Performs an extra mapping (for a process trying to see an existing >> block >> + * i.e. performing a lookup). >> + * Mutex must be assured by the caller. >> + */ >> +static void *do_remap(int block_index, int fd) >> +{ >> + void *mapped_addr; >> + ishm_fragment_t *fragment; >> + uint64_t len; >> + uint32_t flags; >> + >> + len = ishm_tbl->block[block_index].len; >> + flags = ishm_tbl->block[block_index].flags; >> + >> + if (flags & _ODP_ISHM_SINGLE_VA) { >> + fragment = ishm_tbl->block[block_index].fragment; >> + if (!fragment) { >> + ODP_ERR("invalid fragment failure.\n"); >> + return NULL; >> + } >> + >> + /* try to mmap: */ >> + mapped_addr = _odp_ishmphy_map(fd, fragment->start, len, >> flags); >> + if (mapped_addr == NULL) >> + return NULL; >> + return mapped_addr; >> + } >> + >> + /* try to mmap: */ >> + mapped_addr = _odp_ishmphy_map(fd, NULL, len, flags); >> + if (mapped_addr == NULL) >> + return NULL; >> + >> + return mapped_addr; >> +} >> + >> +/* >> + * Performs unmapping, possibly freeing a prereserved VA space fragment, >> + * if the _ODP_ISHM_SINGLE_VA flag was set at alloc time >> + * Mutex must be assured by the caller. >> + */ >> +static int do_unmap(void *start, uint64_t size, uint32_t flags, >> + int block_index) >> +{ >> + int ret; >> + >> + if (start) >> + ret = _odp_ishmphy_unmap(start, size, flags); >> + else >> + ret = 0; >> + >> + if ((block_index >= 0) && (flags & _ODP_ISHM_SINGLE_VA)) { >> + /* mark reserved address space as free */ >> + free_fragment(ishm_tbl->block[block_index].fragment); >> + } >> + >> + return ret; >> +} >> + >> +/* >> + * Search for a given used and allocated block name. >> + * (search is performed in the global ishm table) >> + * Returns the index of the found block (if any) or -1 if none. >> + * Mutex must be assured by the caller. >> + */ >> +static int find_block_by_name(const char *name) >> +{ >> + int i; >> + >> + if (name == NULL || name[0] == 0) >> + return -1; >> + >> + for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) { >> + if ((ishm_tbl->block[i].len) && >> + (strcmp(name, ishm_tbl->block[i].name) == 0)) >> + return i; >> + } >> + >> + return -1; >> +} >> + >> +/* >> + * Search for a block by address (only works when flag >> _ODP_ISHM_SINGLE_VA >> + * was set at reserve() time, or if the block is already known by this >> + * process). >> + * Search is performed in the process table and in the global ishm table. >> + * The provided address does not have to be at start: any address >> + * within the fragment is OK. >> + * Returns the index to the found block (if any) or -1 if none. >> + * Mutex must be assured by the caller. >> + */ >> +static int find_block_by_address(void *addr) >> +{ >> + int block_index; >> + int i; >> + ishm_fragment_t *fragmnt; >> + >> + /* >> + * first check if there is already a process known block for this >> + * address >> + */ >> + for (i = 0; i < ishm_proctable->nb_entries; i++) { >> + block_index = ishm_proctable->entry[i].block_index; >> + if ((addr > ishm_proctable->entry[i].start) && >> + ((char *)addr < ((char *)ishm_proctable->entry[i].start >> + >> + ishm_tbl->block[block_index].len))) >> + return block_index; >> + } >> + >> + /* >> + * then check if there is a existing single VA block known by >> some other >> + * process and containing the given address >> + */ >> + for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) { >> + if ((!ishm_tbl->block[i].len) || >> + (!(ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA))) >> + continue; >> + fragmnt = ishm_tbl->block[i].fragment; >> + if (!fragmnt) { >> + ODP_ERR("find_fragment: invalid NULL fragment\n"); >> + return -1; >> + } >> + if ((addr >= fragmnt->start) && >> + ((char *)addr < ((char *)fragmnt->start + >> fragmnt->len))) >> + return i; >> + } >> + >> + /* address does not belong to any accessible block: */ >> + return -1; >> +} >> + >> +/* >> + * Search a given ishm block in the process local table. Return its index >> + * in the process table or -1 if not found (meaning that the ishm table >> + * block index was not referenced in the process local table, i.e. the >> + * block is known by some other process, but not by the current process). >> + * Caller must assure mutex. >> + */ >> +static int procfind_block(int block_index) >> +{ >> + int i; >> + >> + for (i = 0; i < ishm_proctable->nb_entries; i++) { >> + if (ishm_proctable->entry[i].block_index == block_index) >> + return i; >> + } >> + return -1; >> +} >> + >> +/* >> + * Release the physical memory mapping for blocks which have been freed >> + * by other processes. Caller must ensure mutex. >> + * Mutex must be assured by the caller. >> + */ >> +static void procsync(void) >> +{ >> + int i = 0; >> + int last; >> + ishm_block_t *block; >> + >> + last = ishm_proctable->nb_entries; >> + while (i < last) { >> + /* if the procecess sequence number doesn't match the main >> + * table seq number, this entry is obsolete >> + */ >> + block = &ishm_tbl->block[ishm_proctabl >> e->entry[i].block_index]; >> + if (ishm_proctable->entry[i].seq != block->seq) { >> + /* obsolete entry: free memory and remove proc >> entry */ >> + close(ishm_proctable->entry[i].fd); >> + _odp_ishmphy_unmap(ishm_proct >> able->entry[i].start, >> + ishm_proctable->entry[i].len, >> + ishm_proctable->entry[i].flags >> ); >> + ishm_proctable->entry[i] = >> + ishm_proctable->entry[--last]; >> + } else { >> + i++; >> + } >> + } >> + ishm_proctable->nb_entries = last; >> +} >> + >> +/* >> + * Allocate and map internal shared memory, or other objects: >> + * If a name is given, check that this name is not already in use. >> + * If ok, allocate a new shared memory block and map the >> + * provided fd in it (if fd >=0 was given). >> + * If no fd is provided, a shared memory file desc named >> + * /tmp/odp-<pid>-ishm-<name_or_sequence> is created and mapped. >> + * (the name is different for huge page file as they must be on >> hugepagefs) >> + * The function returns the index of the newly created block in the >> + * main block table (>=0) or -1 on error. >> + */ >> +int _odp_ishm_reserve(const char *name, uint64_t size, int fd, >> + uint32_t align, uint32_t flags, uint32_t user_flags) >> +{ >> + int new_index; /* index in the main block >> table*/ >> + ishm_block_t *new_block; /* entry in the main block >> table*/ >> + uint64_t page_sz; /* normal page size. >> usually 4K*/ >> + uint64_t alloc_size; /* includes extra for >> alignement*/ >> + uint64_t page_hp_size; /* huge page size */ >> + uint64_t alloc_hp_size; /* includes extra for >> alignement*/ >> + uint32_t hp_align; >> + uint64_t len; /* mapped length */ >> + void *addr = NULL; /* mapping address */ >> + int new_proc_entry; >> + >> + page_sz = odp_sys_page_size(); >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + >> + /* update this process view... */ >> + procsync(); >> + >> + /* roundup to page size */ >> + alloc_size = (size + (page_sz - 1)) & (-page_sz); >> + >> + page_hp_size = odp_sys_huge_page_size(); >> + /* roundup to page size */ >> + alloc_hp_size = (size + (page_hp_size - 1)) & (-page_hp_size); >> + >> + /* check if name already exists */ >> + if (name && (find_block_by_name(name) >= 0)) { >> + /* Found a block with the same name */ >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + ODP_ERR("name \"%s\" already used.\n", name); >> + return -1; >> + } >> + >> + /* grab a new entry: */ >> + for (new_index = 0; new_index < ISHM_MAX_NB_BLOCKS; new_index++) { >> + if (ishm_tbl->block[new_index].len == 0) { >> + /* Found free block */ >> + break; >> + } >> + } >> + >> + /* check if we have reached the maximum number of allocation: */ >> + if (new_index >= ISHM_MAX_NB_BLOCKS) { >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + ODP_ERR("ISHM_MAX_NB_BLOCKS limit reached!\n"); >> + return -1; >> + } >> + >> + new_block = &ishm_tbl->block[new_index]; >> + >> + /* save block name (if any given): */ >> + if (name) >> + strncpy(new_block->name, name, ISHM_NAME_MAXLEN - 1); >> + else >> + new_block->name[0] = 0; >> + >> + /* Try first huge pages when possible and needed: */ >> + if (page_hp_size && (alloc_size > page_sz)) { >> + /* at least, alignment in VA should match page size, but >> user >> + * can request more: If the user requirement exceeds the >> page >> + * size then we have to make sure the block will be >> mapped at >> + * the same address every where, otherwise alignment may >> be >> + * be wrong for some process */ >> + hp_align = align; >> + if (hp_align < odp_sys_huge_page_size()) >> + hp_align = odp_sys_huge_page_size(); >> + else >> + flags |= _ODP_ISHM_SINGLE_VA; >> + len = alloc_hp_size; >> + addr = do_map(new_index, len, hp_align, flags, 1, &fd); >> + >> + if (addr == NULL) >> + ODP_DBG("No huge pages, fall back to normal >> pages, " >> + "check: /proc/sys/vm/nr_hugepages.\n"); >> + else >> + new_block->huge = 1; >> + } >> + >> + /* try normal pages if huge pages failed */ >> + if (addr == NULL) { >> + /* at least, alignment in VA should match page size, but >> user >> + * can request more: If the user requirement exceeds the >> page >> + * size then we have to make sure the block will be >> mapped at >> + * the same address every where, otherwise alignment may >> be >> + * be wrong for some process */ >> + if (align < odp_sys_page_size()) >> + align = odp_sys_page_size(); >> + else >> + flags |= _ODP_ISHM_SINGLE_VA; >> + >> + len = alloc_size; >> + addr = do_map(new_index, len, align, flags, 0, &fd); >> + new_block->huge = 0; >> + } >> + >> + /* if neither huge pages or normal pages works, we cannot >> proceed: */ >> + if ((addr == NULL) || (len == 0)) { >> + if ((new_block->filename[0]) && (fd >= 0)) >> + close(fd); >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + ODP_ERR("_ishm_reserve failed.\n"); >> > > > CID 168963 (#1 of 1): Resource leak (RESOURCE_LEAK)17. leaked_handle: Returning > without closing handle fd leaks it. > > >> + return -1; >> + } >> + >> + /* remember block data and increment block seq number to mark >> change */ >> + new_block->len = len; >> + new_block->user_len = size; >> + new_block->flags = flags; >> + new_block->user_flags = user_flags; >> + new_block->seq++; >> + new_block->refcnt = 1; >> + new_block->main_odpthread = odp_thread_id(); >> + new_block->start = addr; /* only for SINGLE_VA*/ >> + >> + /* the allocation succeeded: update the process local view */ >> + new_proc_entry = ishm_proctable->nb_entries++; >> + ishm_proctable->entry[new_proc_entry].block_index = new_index; >> + ishm_proctable->entry[new_proc_entry].flags = flags; >> + ishm_proctable->entry[new_proc_entry].seq = new_block->seq; >> + ishm_proctable->entry[new_proc_entry].start = addr; >> + ishm_proctable->entry[new_proc_entry].len = len; >> + ishm_proctable->entry[new_proc_entry].fd = fd; >> + >> + /* register the file descriptor to the file descriptor server. */ >> + _odp_fdserver_register_fd(FD_SRV_CTX_ISHM, new_index, fd); >> + >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return new_index; >> +} >> + >> +/* >> + * Free and unmap internal shared memory: >> + * The file descriptor is closed and the .../odp-* file deleted, >> + * unless fd was externally provided at reserve() time. >> + * return 0 if OK, and -1 on error. >> + * Mutex must be assured by the caller. >> + */ >> +static int block_free(int block_index) >> +{ >> + int proc_index; >> + ishm_block_t *block; /* entry in the main block table*/ >> + int last; >> + >> + if ((block_index < 0) || >> + (block_index >= ISHM_MAX_NB_BLOCKS) || >> + (ishm_tbl->block[block_index].len == 0)) { >> + ODP_ERR("Request to free an invalid block\n"); >> + return -1; >> + } >> + >> + block = &ishm_tbl->block[block_index]; >> + >> + proc_index = procfind_block(block_index); >> + if (proc_index >= 0) { >> + /* close the fd, unless if it was externaly provided */ >> > > > > CID 168952 (#1 of 1): Array compared against 0 (NO_EFFECT)array_null: > Comparing an array to null is not useful: block->filename, since the test > will always evaluate as true. > > Was block->filename formerly declared as a pointer? > > + if ((block->filename[0] != 0) || >> + (odp_thread_id() != block->main_odpthread)) >> + close(ishm_proctable->entry[proc_index].fd); >> + >> + /* remove the mapping and possible fragment */ >> + do_unmap(ishm_proctable->entry[proc_index].start, >> + block->len, >> + ishm_proctable->entry[proc_index].flags, >> + block_index); >> + >> + /* remove entry from process local table: */ >> + last = ishm_proctable->nb_entries - 1; >> + ishm_proctable->entry[proc_index] = >> + ishm_proctable->entry[last]; >> + ishm_proctable->nb_entries = last; >> + } else { >> + /* just possibly free the fragment as no mapping exist >> here: */ >> + do_unmap(NULL, 0, block->flags, block_index); >> + } >> + >> + /* remove the .../odp-* file, unless fd was external: */ >> + if (block->filename[0] != 0) >> + unlink(block->filename); >> + >> + /* deregister the file descriptor from the file descriptor >> server. */ >> + _odp_fdserver_deregister_fd(FD_SRV_CTX_ISHM, block_index); >> + >> + /* mark the block as free in the main block table: */ >> + block->len = 0; >> + >> + /* mark the change so other processes see this entry as obsolete: >> */ >> + block->seq++; >> + >> + return 0; >> +} >> + >> +/* >> + * Free and unmap internal shared memory, intentified by its block >> number: >> + * return -1 on error. 0 if OK. >> + */ >> +int _odp_ishm_free_by_index(int block_index) >> +{ >> + int ret; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + ret = block_free(block_index); >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return ret; >> +} >> + >> +/* >> + * free and unmap internal shared memory, intentified by its block name: >> + * return -1 on error. 0 if OK. >> + */ >> +int _odp_ishm_free_by_name(const char *name) >> +{ >> + int block_index; >> + int ret; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + /* search the block in main ishm table */ >> + block_index = find_block_by_name(name); >> + if (block_index < 0) { >> + ODP_ERR("Request to free an non existing block..." >> + " (double free?)\n"); >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return -1; >> + } >> + >> + ret = block_free(block_index); >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return ret; >> +} >> + >> +/* >> + * Free and unmap internal shared memory identified by address: >> + * return -1 on error. 0 if OK. >> + */ >> +int _odp_ishm_free_by_address(void *addr) >> +{ >> + int block_index; >> + int ret; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + /* search the block in main ishm table */ >> + block_index = find_block_by_address(addr); >> + if (block_index < 0) { >> + ODP_ERR("Request to free an non existing block..." >> + " (double free?)\n"); >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return -1; >> + } >> + >> + ret = block_free(block_index); >> + >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return ret; >> +} >> + >> +/* >> + * Lookup for an ishm shared memory, identified by its block index >> + * in the main ishm block table. >> + * Map this ishm area in the process VA (if not already present). >> + * Returns the block user address or NULL on error. >> + * Mutex must be assured by the caller. >> + */ >> +static void *block_lookup(int block_index) >> +{ >> + int proc_index; >> + int fd = -1; >> + ishm_block_t *block; >> + void *mapped_addr; >> + int new_entry; >> + >> + if ((block_index < 0) || >> + (block_index >= ISHM_MAX_NB_BLOCKS) || >> + (ishm_tbl->block[block_index].len == 0)) { >> + ODP_ERR("Request to lookup an invalid block\n"); >> + return NULL; >> + } >> + >> + /* search it in process table: if there, this process knows it >> already*/ >> + proc_index = procfind_block(block_index); >> + if (proc_index >= 0) >> + return ishm_proctable->entry[proc_index].start; >> + >> + /* this ishm is not known by this process, yet: we create the >> mapping.*/ >> + fd = _odp_fdserver_lookup_fd(FD_SRV_CTX_ISHM, block_index); >> + if (fd < 0) { >> + ODP_ERR("Could not find ishm file descriptor (BUG!)\n"); >> + return NULL; >> + } >> + >> + /* perform the mapping */ >> + block = &ishm_tbl->block[block_index]; >> + >> + mapped_addr = do_remap(block_index, fd); >> + if (mapped_addr == NULL) { >> + ODP_ERR(" lookup: Could not map existing shared >> memory!\n"); >> + return NULL; >> + } >> + >> + /* the mapping succeeded: update the process local view */ >> + new_entry = ishm_proctable->nb_entries++; >> + ishm_proctable->entry[new_entry].block_index = block_index; >> + ishm_proctable->entry[new_entry].flags = block->flags; >> + ishm_proctable->entry[new_entry].seq = block->seq; >> + ishm_proctable->entry[new_entry].start = mapped_addr; >> + ishm_proctable->entry[new_entry].len = block->len; >> + ishm_proctable->entry[new_entry].fd = fd; >> + block->refcnt++; >> + >> + return mapped_addr; >> +} >> + >> +/* >> + * Lookup for an ishm shared memory, identified by its block_index. >> + * Maps this ishmem area in the process VA (if not already present). >> + * Returns the block user address, or NULL if the index >> + * does not match any known ishm blocks. >> + */ >> +void *_odp_ishm_lookup_by_index(int block_index) >> +{ >> + void *ret; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + ret = block_lookup(block_index); >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return ret; >> +} >> + >> +/* >> + * Lookup for an ishm shared memory, identified by its block name. >> + * Map this ishm area in the process VA (if not already present). >> + * Return the block index, or -1 if the index >> + * does not match any known ishm blocks. >> + */ >> +int _odp_ishm_lookup_by_name(const char *name) >> +{ >> + int block_index; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + /* search the block in main ishm table: return -1 if not found: */ >> + block_index = find_block_by_name(name); >> + if ((block_index < 0) || (!block_lookup(block_index))) { >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return -1; >> + } >> + >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return block_index; >> +} >> + >> +/* >> + * Lookup for an ishm shared memory block, identified by its VA address. >> + * This works only if the block has already been looked-up (mapped) by >> the >> + * current process or it it was created with the _ODP_ISHM_SINGLE_VA >> flag. >> + * Map this ishm area in the process VA (if not already present). >> + * Return the block index, or -1 if the address >> + * does not match any known ishm blocks. >> + */ >> +int _odp_ishm_lookup_by_address(void *addr) >> +{ >> + int block_index; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + /* search the block in main ishm table: return -1 if not found: */ >> + block_index = find_block_by_address(addr); >> + if ((block_index < 0) || (!block_lookup(block_index))) { >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return -1; >> + } >> + >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return block_index; >> +} >> + >> +/* >> + * Returns the VA address of a given block (which has to be known in the >> current >> + * process). Returns NULL if the block is unknown. >> + */ >> +void *_odp_ishm_address(int block_index) >> +{ >> + int proc_index; >> + void *addr; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + if ((block_index < 0) || >> + (block_index >= ISHM_MAX_NB_BLOCKS) || >> + (ishm_tbl->block[block_index].len == 0)) { >> + ODP_ERR("Request for address on an invalid block\n"); >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return NULL; >> + } >> + >> + proc_index = procfind_block(block_index); >> + if (proc_index < 0) { >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return NULL; >> + } >> + >> + addr = ishm_proctable->entry[proc_index].start; >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return addr; >> +} >> + >> +int _odp_ishm_info(int block_index, _odp_ishm_info_t *info) >> +{ >> + int proc_index; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + if ((block_index < 0) || >> + (block_index >= ISHM_MAX_NB_BLOCKS) || >> + (ishm_tbl->block[block_index].len == 0)) { >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + ODP_ERR("Request for info on an invalid block\n"); >> + return -1; >> + } >> + >> + /* search it in process table: if not there, need to map*/ >> + proc_index = procfind_block(block_index); >> + if (proc_index < 0) { >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return -1; >> + } >> + >> + info->name = ishm_tbl->block[block_index].name; >> + info->addr = ishm_proctable->entry[proc_index].start; >> + info->size = ishm_tbl->block[block_index].user_len; >> + info->page_size = ishm_tbl->block[block_index].huge ? >> + odp_sys_huge_page_size() : odp_sys_page_size(); >> + info->flags = ishm_tbl->block[block_index].flags; >> + info->user_flags = ishm_tbl->block[block_index].user_flags; >> + >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return 0; >> +} >> + >> +int _odp_ishm_init_global(void) >> +{ >> + void *addr; >> + void *spce_addr; >> + int i; >> + >> + if (!odp_global_data.hugepage_info.default_huge_page_dir) >> + ODP_DBG("NOTE: No support for huge pages\n"); >> + else >> + ODP_DBG("Huge pages mount point is: %s\n", >> + odp_global_data.hugepage_ >> info.default_huge_page_dir); >> + >> + /* allocate space for the internal shared mem block table: */ >> + addr = mmap(NULL, sizeof(ishm_table_t), >> + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, >> -1, 0); >> + if (addr == MAP_FAILED) { >> + ODP_ERR("unable to mmap the main block table\n."); >> + goto init_glob_err1; >> + } >> + ishm_tbl = addr; >> + memset(ishm_tbl, 0, sizeof(ishm_table_t)); >> + ishm_tbl->dev_seq = 0; >> + odp_spinlock_init(&ishm_tbl->lock); >> + >> + /* allocate space for the internal shared mem fragment table: */ >> + addr = mmap(NULL, sizeof(ishm_ftable_t), >> + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, >> -1, 0); >> + if (addr == MAP_FAILED) { >> + ODP_ERR("unable to mmap the main fragment table\n."); >> + goto init_glob_err2; >> + } >> + ishm_ftbl = addr; >> + memset(ishm_ftbl, 0, sizeof(ishm_ftable_t)); >> + >> + /* >> + *reserve the address space for _ODP_ISHM_SINGLE_VA reserved >> blocks, >> + * only address space! >> + */ >> + spce_addr = _odp_ishmphy_book_va(ODP_CONFIG_ISHM_VA_PREALLOC_SZ, >> + odp_sys_huge_page_size()); >> + if (!spce_addr) { >> + ODP_ERR("unable to reserve virtual space\n."); >> + goto init_glob_err3; >> + } >> + >> + /* use the first fragment descriptor to describe to whole VA >> space: */ >> + ishm_ftbl->fragment[0].block_index = -1; >> + ishm_ftbl->fragment[0].start = spce_addr; >> + ishm_ftbl->fragment[0].len = ODP_CONFIG_ISHM_VA_PREALLOC_SZ; >> + ishm_ftbl->fragment[0].prev = NULL; >> + ishm_ftbl->fragment[0].next = NULL; >> + ishm_ftbl->used_fragmnts = &ishm_ftbl->fragment[0]; >> + >> + /* and put all other fragment descriptors in the unused list: */ >> + for (i = 1; i < ISHM_NB_FRAGMNTS - 1; i++) { >> + ishm_ftbl->fragment[i].prev = NULL; >> + ishm_ftbl->fragment[i].next = &ishm_ftbl->fragment[i + >> 1]; >> + } >> + ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].prev = NULL; >> + ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].next = NULL; >> + ishm_ftbl->unused_fragmnts = &ishm_ftbl->fragment[1]; >> + >> + return 0; >> + >> +init_glob_err3: >> + if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) >> + ODP_ERR("unable to munmap main fragment table\n."); >> +init_glob_err2: >> + if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) >> + ODP_ERR("unable to munmap main block table\n."); >> +init_glob_err1: >> + return -1; >> +} >> + >> +int _odp_ishm_init_local(void) >> +{ >> + int i; >> + int block_index; >> + >> + /* >> + * the ishm_process table is local to each linux process >> + * Check that no other linux threads (of same or ancestor >> processes) >> + * have already created the table, and create it if needed. >> + * We protect this with the general ishm lock to avoid >> + * init race condition of different running threads. >> + */ >> + odp_spinlock_lock(&ishm_tbl->lock); >> + if (!ishm_proctable) { >> + ishm_proctable = malloc(sizeof(ishm_proctable_t)); >> + if (!ishm_proctable) { >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return -1; >> + } >> + memset(ishm_proctable, 0, sizeof(ishm_proctable_t)); >> + } >> + if (syscall(SYS_gettid) != getpid()) >> + ishm_proctable->thrd_refcnt++; /* new linux thread */ >> + else >> + ishm_proctable->thrd_refcnt = 1;/* new linux process */ >> + >> + /* >> + * if this ODP thread is actually a new linux process, (as opposed >> + * to a pthread), i.e, we just forked, then all shmem blocks >> + * of the parent process are mapped into this child by >> inheritance. >> + * (The process local table is inherited as well). We hence have >> to >> + * increase the process refcount for each of the inherited >> mappings: >> + */ >> + if (syscall(SYS_gettid) == getpid()) { >> + for (i = 0; i < ishm_proctable->nb_entries; i++) { >> + block_index = ishm_proctable->entry[i].block >> _index; >> + ishm_tbl->block[block_index].refcnt++; >> + } >> + } >> + >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return 0; >> +} >> + >> +int _odp_ishm_term_global(void) >> +{ >> + int ret = 0; >> + >> + /* free the fragment table */ >> + if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) { >> + ret = -1; >> + ODP_ERR("unable to munmap fragment table\n."); >> + } >> + /* free the block table */ >> + if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) { >> + ret = -1; >> + ODP_ERR("unable to munmap main table\n."); >> + } >> + >> + /* free the reserved VA space */ >> + if (_odp_ishmphy_unbook_va()) >> + ret = -1; >> + >> + return ret; >> +} >> + >> +int _odp_ishm_term_local(void) >> +{ >> + int i; >> + int proc_table_refcnt = 0; >> + int block_index; >> + ishm_block_t *block; >> + >> + odp_spinlock_lock(&ishm_tbl->lock); >> + procsync(); >> + >> + /* >> + * The ishm_process table is local to each linux process >> + * Check that no other linux threads (of this linux process) >> + * still needs the table, and free it if so. >> + * We protect this with the general ishm lock to avoid >> + * term race condition of different running threads. >> + */ >> + proc_table_refcnt = --ishm_proctable->thrd_refcnt; >> + if (!proc_table_refcnt) { >> + /* >> + * this is the last thread of this process... >> + * All mappings for this process are about to be lost... >> + * Go through the table of visible blocks for this >> process, >> + * decreasing the refcnt of each visible blocks, and >> issuing >> + * warning for those no longer referenced by any process. >> + * Note that non-referenced blocks are nor freeed: this is >> + * deliberate as this would imply that the sementic of the >> + * freeing function would differ depending on whether we >> run >> + * with odp_thread as processes or pthreads. With this >> approach, >> + * the user should always free the blocks manually, which >> is >> + * more consistent >> + */ >> + for (i = 0; i < ishm_proctable->nb_entries; i++) { >> + block_index = ishm_proctable->entry[i].block >> _index; >> + block = &ishm_tbl->block[block_index]; >> + if ((--block->refcnt) <= 0) { >> + block->refcnt = 0; >> + ODP_DBG("Warning: block %d: name:%s " >> + "no longer referenced\n", >> + i, >> + ishm_tbl->block[i].name[0] ? >> + ishm_tbl->block[i].name : "<no >> name>"); >> + } >> + } >> + >> + free(ishm_proctable); >> + ishm_proctable = NULL; >> + } >> + >> + odp_spinlock_unlock(&ishm_tbl->lock); >> + return 0; >> +} >> diff --git a/platform/linux-generic/_ishmphy.c >> b/platform/linux-generic/_ishmphy.c >> new file mode 100644 >> index 0000000..cf2d531 >> --- /dev/null >> +++ b/platform/linux-generic/_ishmphy.c >> @@ -0,0 +1,168 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +/* >> + * This file handles the lower end of the ishm memory allocator: >> + * It performs the physical mappings. >> + */ >> +#include <odp_posix_extensions.h> >> +#include <odp_config_internal.h> >> +#include <odp_internal.h> >> +#include <odp/api/align.h> >> +#include <odp/api/system_info.h> >> +#include <odp/api/debug.h> >> +#include <odp_debug_internal.h> >> +#include <odp_align_internal.h> >> +#include <_ishm_internal.h> >> +#include <_ishmphy_internal.h> >> + >> +#include <stdlib.h> >> +#include <stdio.h> >> +#include <unistd.h> >> +#include <string.h> >> +#include <errno.h> >> +#include <sys/mman.h> >> +#include <sys/stat.h> >> +#include <fcntl.h> >> +#include <sys/types.h> >> +#include <sys/wait.h> >> +#include <_ishmphy_internal.h> >> + >> +static void *common_va_address; >> +static uint64_t common_va_len; >> + >> +#ifndef MAP_ANONYMOUS >> +#define MAP_ANONYMOUS MAP_ANON >> +#endif >> + >> +/* Book some virtual address space >> + * This function is called at odp_init_global() time to pre-book some >> + * virtual address space inherited by all odpthreads (i.e. descendant >> + * processes and threads) and later used to guarantee the unicity the >> + * the mapping VA address when memory is reserver with the >> _ODP_ISHM_SINGLE_VA >> + * flag. >> + * returns the address of the mapping or NULL on error. >> + */ >> +void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align) >> +{ >> + void *addr; >> + >> + addr = mmap(NULL, len + align, PROT_NONE, >> + MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); >> + if (addr == MAP_FAILED) { >> + ODP_ERR("_ishmphy_book_va failure\n"); >> + return NULL; >> + } >> + >> + madvise(addr, len, MADV_DONTNEED); >> + >> + common_va_address = addr; >> + common_va_len = len; >> + >> + /* return the nearest aligned address: */ >> + return (void *)(((uintptr_t)addr + align - 1) & (-align)); >> +} >> + >> +/* Un-book some virtual address space >> + * This function is called at odp_term_global() time to unbook >> + * the virtual address space booked by _ishmphy_book_va() >> + */ >> +int _odp_ishmphy_unbook_va(void) >> +{ >> + int ret; >> + >> + ret = munmap(common_va_address, common_va_len); >> + if (ret) >> + ODP_ERR("_unishmphy_book_va failure\n"); >> + return ret; >> +} >> + >> +/* >> + * do a mapping: >> + * Performs a mapping of the provided file descriptor to the process VA >> + * space. If the _ODP_ISHM_SINGLE_VA flag is set, 'start' is assumed to >> be >> + * the VA address where the mapping is to be done. >> + * If the flag is not set, a new VA address is taken. >> + * returns the address of the mapping or NULL on error. >> + */ >> +void *_odp_ishmphy_map(int fd, void *start, uint64_t size, >> + int flags) >> +{ >> + void *mapped_addr; >> + int mmap_flags = 0; >> + >> + if (flags & _ODP_ISHM_SINGLE_VA) { >> + if (!start) { >> + ODP_ERR("failure: missing address\n"); >> + return NULL; >> + } >> + /* maps over fragment of reserved VA: */ >> + mapped_addr = mmap(start, size, PROT_READ | PROT_WRITE, >> + MAP_SHARED | MAP_FIXED | mmap_flags, >> fd, 0); >> + } else { >> + /* just do a new mapping in the VA space: */ >> + mapped_addr = mmap(NULL, size, PROT_READ | PROT_WRITE, >> + MAP_SHARED | mmap_flags, fd, 0); >> + } >> + >> + if (mapped_addr == MAP_FAILED) { >> + ODP_ERR("mmap failed:%s\n", strerror(errno)); >> + return NULL; >> + } >> + >> + /* if locking is requested, lock it...*/ >> + if (flags & _ODP_ISHM_LOCK) { >> + if (mlock(mapped_addr, size)) { >> + if (munmap(mapped_addr, size)) >> + ODP_ERR("munmap failed:%s\n", >> strerror(errno)); >> + ODP_ERR("mlock failed:%s\n", strerror(errno)); >> + return NULL; >> + } >> + } >> + return mapped_addr; >> +} >> + >> +/* free a mapping: >> + * If the _ODP_ISHM_SINGLE_VA flag was given at creation time the virtual >> + * address range must be returned to the preoallocated "pool". this is >> + * done by mapping non accessibly memory there (hence blocking the VA but >> + * releasing the physical memory). >> + * If the _ODP_ISHM_SINGLE_VA flag was not given, both physical memory >> and >> + * virtual address space are realeased by calling the normal munmap. >> + * return 0 on success or -1 on error. >> + */ >> +int _odp_ishmphy_unmap(void *start, uint64_t len, int flags) >> +{ >> + void *addr; >> + int ret; >> + int mmap_flgs; >> + >> + mmap_flgs = MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS | >> MAP_NORESERVE; >> + >> + /* if locking was requested, unlock...*/ >> + if (flags & _ODP_ISHM_LOCK) >> + munlock(start, len); >> + >> + if (flags & _ODP_ISHM_SINGLE_VA) { >> + /* map unnaccessible memory overwrites previous mapping >> + * and free the physical memory, but guarantees to block >> + * the VA range from other mappings >> + */ >> + addr = mmap(start, len, PROT_NONE, mmap_flgs, -1, 0); >> + if (addr == MAP_FAILED) { >> + ODP_ERR("_ishmphy_free failure for >> ISHM_SINGLE_VA\n"); >> + return -1; >> + } >> + madvise(start, len, MADV_DONTNEED); >> + return 0; >> + } >> + >> + /* just release the mapping */ >> + ret = munmap(start, len); >> + if (ret) >> + ODP_ERR("_ishmphy_free failure: %s\n", strerror(errno)); >> + return ret; >> +} >> diff --git a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h >> b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h >> deleted file mode 120000 >> index e86e132..0000000 >> --- a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h >> +++ /dev/null >> @@ -1 +0,0 @@ >> -../../../default/odp/api/cpu_arch.h >> \ No newline at end of file >> diff --git a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h >> b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h >> new file mode 100644 >> index 0000000..22b1da2 >> --- /dev/null >> +++ b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h >> @@ -0,0 +1,24 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#ifndef ODP_PLAT_CPU_ARCH_H_ >> +#define ODP_PLAT_CPU_ARCH_H_ >> + >> +#ifdef __cplusplus >> +extern "C" { >> +#endif >> + >> +#define _ODP_CACHE_LINE_SIZE 64 >> + >> +static inline void odp_cpu_pause(void) >> +{ >> +} >> + >> +#ifdef __cplusplus >> +} >> +#endif >> + >> +#endif >> diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c >> b/platform/linux-generic/arch/arm/odp_cpu_arch.c >> deleted file mode 120000 >> index deebc47..0000000 >> --- a/platform/linux-generic/arch/arm/odp_cpu_arch.c >> +++ /dev/null >> @@ -1 +0,0 @@ >> -../default/odp_cpu_arch.c >> \ No newline at end of file >> diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c >> b/platform/linux-generic/arch/arm/odp_cpu_arch.c >> new file mode 100644 >> index 0000000..2ac223e >> --- /dev/null >> +++ b/platform/linux-generic/arch/arm/odp_cpu_arch.c >> @@ -0,0 +1,48 @@ >> +/* Copyright (c) 2015, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#include <odp_posix_extensions.h> >> + >> +#include <stdlib.h> >> +#include <time.h> >> + >> +#include <odp/api/cpu.h> >> +#include <odp/api/hints.h> >> +#include <odp/api/system_info.h> >> +#include <odp_debug_internal.h> >> + >> +#define GIGA 1000000000 >> + >> +uint64_t odp_cpu_cycles(void) >> +{ >> + struct timespec time; >> + uint64_t sec, ns, hz, cycles; >> + int ret; >> + >> + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time); >> + >> + if (ret != 0) >> + ODP_ABORT("clock_gettime failed\n"); >> + >> + hz = odp_cpu_hz_max(); >> + sec = (uint64_t)time.tv_sec; >> + ns = (uint64_t)time.tv_nsec; >> + >> + cycles = sec * hz; >> + cycles += (ns * hz) / GIGA; >> + >> + return cycles; >> +} >> + >> +uint64_t odp_cpu_cycles_max(void) >> +{ >> + return UINT64_MAX; >> +} >> + >> +uint64_t odp_cpu_cycles_resolution(void) >> +{ >> + return 1; >> +} >> diff --git a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c >> b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c >> deleted file mode 120000 >> index 39962b8..0000000 >> --- a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c >> +++ /dev/null >> @@ -1 +0,0 @@ >> -../default/odp_sysinfo_parse.c >> \ No newline at end of file >> diff --git a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c >> b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c >> new file mode 100644 >> index 0000000..53e2aae >> --- /dev/null >> +++ b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c >> @@ -0,0 +1,27 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#include <odp_internal.h> >> +#include <odp_debug_internal.h> >> +#include <string.h> >> + >> +int cpuinfo_parser(FILE *file ODP_UNUSED, system_info_t *sysinfo) >> +{ >> + int i; >> + >> + ODP_DBG("Warning: use dummy values for freq and model string\n"); >> + for (i = 0; i < MAX_CPU_NUMBER; i++) { >> + sysinfo->cpu_hz_max[i] = 1400000000; >> + strcpy(sysinfo->model_str[i], "UNKNOWN"); >> + } >> + >> + return 0; >> +} >> + >> +uint64_t odp_cpu_hz_current(int id ODP_UNUSED) >> +{ >> + return 0; >> +} >> diff --git a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c >> b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c >> deleted file mode 120000 >> index deebc47..0000000 >> --- a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c >> +++ /dev/null >> @@ -1 +0,0 @@ >> -../default/odp_cpu_arch.c >> \ No newline at end of file >> diff --git a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c >> b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c >> new file mode 100644 >> index 0000000..2ac223e >> --- /dev/null >> +++ b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c >> @@ -0,0 +1,48 @@ >> +/* Copyright (c) 2015, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#include <odp_posix_extensions.h> >> + >> +#include <stdlib.h> >> +#include <time.h> >> + >> +#include <odp/api/cpu.h> >> +#include <odp/api/hints.h> >> +#include <odp/api/system_info.h> >> +#include <odp_debug_internal.h> >> + >> +#define GIGA 1000000000 >> + >> +uint64_t odp_cpu_cycles(void) >> +{ >> + struct timespec time; >> + uint64_t sec, ns, hz, cycles; >> + int ret; >> + >> + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time); >> + >> + if (ret != 0) >> + ODP_ABORT("clock_gettime failed\n"); >> + >> + hz = odp_cpu_hz_max(); >> + sec = (uint64_t)time.tv_sec; >> + ns = (uint64_t)time.tv_nsec; >> + >> + cycles = sec * hz; >> + cycles += (ns * hz) / GIGA; >> + >> + return cycles; >> +} >> + >> +uint64_t odp_cpu_cycles_max(void) >> +{ >> + return UINT64_MAX; >> +} >> + >> +uint64_t odp_cpu_cycles_resolution(void) >> +{ >> + return 1; >> +} >> diff --git a/platform/linux-generic/include/_fdserver_internal.h >> b/platform/linux-generic/include/_fdserver_internal.h >> index 480ac02..22b2802 100644 >> --- a/platform/linux-generic/include/_fdserver_internal.h >> +++ b/platform/linux-generic/include/_fdserver_internal.h >> @@ -23,6 +23,7 @@ extern "C" { >> */ >> typedef enum fd_server_context { >> FD_SRV_CTX_NA, /* Not Applicable */ >> + FD_SRV_CTX_ISHM, >> FD_SRV_CTX_END, /* upper enum limit */ >> } fd_server_context_e; >> >> diff --git a/platform/linux-generic/include/_ishm_internal.h >> b/platform/linux-generic/include/_ishm_internal.h >> new file mode 100644 >> index 0000000..7d27477 >> --- /dev/null >> +++ b/platform/linux-generic/include/_ishm_internal.h >> @@ -0,0 +1,45 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#ifndef ODP_ISHM_INTERNAL_H_ >> +#define ODP_ISHM_INTERNAL_H_ >> + >> +#ifdef __cplusplus >> +extern "C" { >> +#endif >> + >> +/* flags available at ishm_reserve: */ >> +#define _ODP_ISHM_SINGLE_VA 1 >> +#define _ODP_ISHM_LOCK 2 >> + >> +/** >> + * Shared memory block info >> + */ >> +typedef struct _odp_ishm_info_t { >> + const char *name; /**< Block name */ >> + void *addr; /**< Block address */ >> + uint64_t size; /**< Block size in bytes */ >> + uint64_t page_size; /**< Memory page size */ >> + uint32_t flags; /**< _ODP_ISHM_* flags */ >> + uint32_t user_flags;/**< user specific flags */ >> +} _odp_ishm_info_t; >> + >> +int _odp_ishm_reserve(const char *name, uint64_t size, int fd, >> uint32_t align, >> + uint32_t flags, uint32_t user_flags); >> +int _odp_ishm_free_by_index(int block_index); >> +int _odp_ishm_free_by_name(const char *name); >> +int _odp_ishm_free_by_address(void *addr); >> +void *_odp_ishm_lookup_by_index(int block_index); >> +int _odp_ishm_lookup_by_name(const char *name); >> +int _odp_ishm_lookup_by_address(void *addr); >> +void *_odp_ishm_address(int block_index); >> +int _odp_ishm_info(int block_index, _odp_ishm_info_t *info); >> + >> +#ifdef __cplusplus >> +} >> +#endif >> + >> +#endif >> diff --git a/platform/linux-generic/include/_ishmphy_internal.h >> b/platform/linux-generic/include/_ishmphy_internal.h >> new file mode 100644 >> index 0000000..4fe560f >> --- /dev/null >> +++ b/platform/linux-generic/include/_ishmphy_internal.h >> @@ -0,0 +1,25 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#ifndef _ISHMPHY_INTERNAL_H >> +#define _ISHMPHY_INTERNAL_H >> + >> +#ifdef __cplusplus >> +extern "C" { >> +#endif >> + >> +#include <stdint.h> >> + >> +void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align); >> +int _odp_ishmphy_unbook_va(void); >> +void *_odp_ishmphy_map(int fd, void *start, uint64_t size, int flags); >> +int _odp_ishmphy_unmap(void *start, uint64_t len, int flags); >> + >> +#ifdef __cplusplus >> +} >> +#endif >> + >> +#endif >> diff --git a/platform/linux-generic/include/ishmphy_internal.h >> b/platform/linux-generic/include/ishmphy_internal.h >> new file mode 100644 >> index 0000000..0bc4207 >> --- /dev/null >> +++ b/platform/linux-generic/include/ishmphy_internal.h >> @@ -0,0 +1,24 @@ >> +/* Copyright (c) 2016, Linaro Limited >> + * All rights reserved. >> + * >> + * SPDX-License-Identifier: BSD-3-Clause >> + */ >> + >> +#ifndef _ISHMPHY_INTERNAL_H_ >> +#define _ISHMPHY_INTERNAL_H_ >> + >> +#ifdef __cplusplus >> +extern "C" { >> +#endif >> + >> +void *_ishmphy_book_va(uint64_t len); >> +int _ishmphy_unbook_va(void); >> +void *_ishmphy_map(int fd, void *start, uint64_t size, >> + int flags, int mmap_flags); >> +int _ishmphy_unmap(void *start, uint64_t len, int flags); >> + >> +#ifdef __cplusplus >> +} >> +#endif >> + >> +#endif >> diff --git a/platform/linux-generic/include/odp_config_internal.h >> b/platform/linux-generic/include/odp_config_internal.h >> index 989ea08..3eabc7b 100644 >> --- a/platform/linux-generic/include/odp_config_internal.h >> +++ b/platform/linux-generic/include/odp_config_internal.h >> @@ -110,6 +110,16 @@ extern "C" { >> */ >> #define ODP_CONFIG_SHM_BLOCKS (ODP_CONFIG_POOLS + 48) >> >> +/* >> + * Size of the virtual address space pre-reserver for ISHM >> + * >> + * This is just virtual space preallocation size, not memory allocation. >> + * This address space is used by ISHM to map things at a common address >> in >> + * all ODP threads (when the _ODP_ISHM_SINGLE_VA flag is used). >> + * In bytes. >> + */ >> +#define ODP_CONFIG_ISHM_VA_PREALLOC_SZ (536870912L) >> + >> #ifdef __cplusplus >> } >> #endif >> diff --git a/platform/linux-generic/include/odp_internal.h >> b/platform/linux-generic/include/odp_internal.h >> index 2b21777..6ea8cf0 100644 >> --- a/platform/linux-generic/include/odp_internal.h >> +++ b/platform/linux-generic/include/odp_internal.h >> @@ -60,6 +60,7 @@ enum init_stage { >> TIME_INIT, >> SYSINFO_INIT, >> FDSERVER_INIT, >> + ISHM_INIT, >> SHM_INIT, >> THREAD_INIT, >> POOL_INIT, >> @@ -128,6 +129,11 @@ int _odp_int_name_tbl_term_global(void); >> int _odp_fdserver_init_global(void); >> int _odp_fdserver_term_global(void); >> >> +int _odp_ishm_init_global(void); >> +int _odp_ishm_init_local(void); >> +int _odp_ishm_term_global(void); >> +int _odp_ishm_term_local(void); >> + >> int cpuinfo_parser(FILE *file, system_info_t *sysinfo); >> uint64_t odp_cpu_hz_current(int id); >> >> diff --git a/platform/linux-generic/odp_init.c >> b/platform/linux-generic/odp_init.c >> index ec84fd0..d33a3ae 100644 >> --- a/platform/linux-generic/odp_init.c >> +++ b/platform/linux-generic/odp_init.c >> @@ -57,6 +57,12 @@ int odp_init_global(odp_instance_t *instance, >> } >> stage = FDSERVER_INIT; >> >> + if (_odp_ishm_init_global()) { >> + ODP_ERR("ODP ishm init failed.\n"); >> + goto init_failed; >> + } >> + stage = ISHM_INIT; >> + >> if (odp_shm_init_global()) { >> ODP_ERR("ODP shm init failed.\n"); >> goto init_failed; >> @@ -224,6 +230,13 @@ int _odp_term_global(enum init_stage stage) >> } >> /* Fall through */ >> >> + case ISHM_INIT: >> + if (_odp_ishm_term_global()) { >> + ODP_ERR("ODP ishm term failed.\n"); >> + rc = -1; >> + } >> + /* Fall through */ >> + >> case FDSERVER_INIT: >> if (_odp_fdserver_term_global()) { >> ODP_ERR("ODP fdserver term failed.\n"); >> @@ -268,6 +281,12 @@ int odp_init_local(odp_instance_t instance, >> odp_thread_type_t thr_type) >> goto init_fail; >> } >> >> + if (_odp_ishm_init_local()) { >> + ODP_ERR("ODP ishm local init failed.\n"); >> + goto init_fail; >> + } >> + stage = ISHM_INIT; >> + >> if (odp_shm_init_local()) { >> ODP_ERR("ODP shm local init failed.\n"); >> goto init_fail; >> @@ -343,6 +362,13 @@ int _odp_term_local(enum init_stage stage) >> } >> /* Fall through */ >> >> + case ISHM_INIT: >> + if (_odp_ishm_term_local()) { >> + ODP_ERR("ODP ishm local term failed.\n"); >> + rc = -1; >> + } >> + /* Fall through */ >> + >> default: >> break; >> } >> -- >> 2.7.4 >> >> >
diff --git a/platform/linux-generic/Makefile.am b/platform/linux-generic/Makefile.am index cb7c8e9..ac2b2da 100644 --- a/platform/linux-generic/Makefile.am +++ b/platform/linux-generic/Makefile.am @@ -113,6 +113,8 @@ odpdrvplatinclude_HEADERS = \ noinst_HEADERS = \ ${srcdir}/include/_fdserver_internal.h \ + ${srcdir}/include/_ishm_internal.h \ + ${srcdir}/include/_ishmphy_internal.h \ ${srcdir}/include/odp_align_internal.h \ ${srcdir}/include/odp_atomic_internal.h \ ${srcdir}/include/odp_buffer_inlines.h \ @@ -156,6 +158,8 @@ noinst_HEADERS = \ __LIB__libodp_linux_la_SOURCES = \ _fdserver.c \ + _ishm.c \ + _ishmphy.c \ odp_atomic.c \ odp_barrier.c \ odp_buffer.c \ diff --git a/platform/linux-generic/_ishm.c b/platform/linux-generic/_ishm.c new file mode 100644 index 0000000..a92d5cc --- /dev/null +++ b/platform/linux-generic/_ishm.c @@ -0,0 +1,1336 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* This file handles the internal shared memory: internal shared memory + * is memory which is sharable by all ODP threads regardless of how the + * ODP thread is implemented (pthread or process) and regardless of fork() + * time. + * Moreover, when reserved with the _ODP_ISHM_SINGLE_VA flag, + * internal shared memory is guaranteed to always be located at the same virtual + * address, i.e. pointers to internal shared memory are fully shareable + * between odp threads (regardless of thread type or fork time) in that case. + * Internal shared memory is mainly meant to be used internaly within ODP + * (hence its name), but may also be allocated by odp applications and drivers, + * in the future (through these interfaces). + * To guarrentee this full pointer shareability (when reserved with the + * _ODP_ISHM_SINGLE_VA flag) internal shared memory is handled as follows: + * At global_init time, a huge virtual address space reservation is performed. + * Note that this is just reserving virtual space, not physical memory. + * Because all ODP threads (pthreads or processes) are descendants of the ODP + * instantiation process, this VA space is inherited by all ODP threads. + * When internal shmem reservation actually occurs, and + * when reserved with the _ODP_ISHM_SINGLE_VA flag, physical memory is + * allocated, and mapped (MAP_FIXED) to some part in the huge preallocated + * address space area: + * because this virtual address space is common to all ODP threads, we + * know this mapping will succeed, and not clash with anything else. + * Hence, an ODP threads which perform a lookup for the same ishm block + * can map it at the same VA address. + * When internal shared memory is released, the physical memory is released + * and the corresponding virtual space returned to its "pool" of preallocated + * virtual space (assuming it was allocated from there). + * Note, though, that, if 2 linux processes share the same ishm block, + * the virtual space is marked as released as soon as one of the processes + * releases the ishm block, but the physical memory space is actually released + * by the kernel once all processes have done a ishm operation (i,e. a sync). + * This is due to the fact that linux does not contain any syscall to unmap + * memory from a different process. + * + * This file contains functions to handle the VA area (handling fragmentation + * and defragmentation resulting from different allocs/release) and also + * define the functions to allocate, release and lookup internal shared + * memory: + * _odp_ishm_reserve(), _odp_ishm_free*() and _odp_ishm_lookup*()... + */ +#include <odp_posix_extensions.h> +#include <odp_config_internal.h> +#include <odp_internal.h> +#include <odp/api/spinlock.h> +#include <odp/api/align.h> +#include <odp/api/system_info.h> +#include <odp/api/debug.h> +#include <odp_shm_internal.h> +#include <odp_debug_internal.h> +#include <odp_align_internal.h> +#include <_fdserver_internal.h> +#include <_ishm_internal.h> +#include <_ishmphy_internal.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/syscall.h> +#include <fcntl.h> +#include <sys/types.h> +#include <inttypes.h> +#include <sys/wait.h> + +/* + * Maximum number of internal shared memory blocks. + * + * This the the number of separate ISHM areas that can be reserved concurrently + * (Note that freeing such blocks may take time, or possibly never happen + * if some of the block ownwers never procsync() after free). This number + * should take that into account) + */ +#define ISHM_MAX_NB_BLOCKS 128 + +/* + * Maximum internal shared memory block name length in chars + * probably taking the same number as SHM name size make sense at this stage + */ +#define ISHM_NAME_MAXLEN 32 + +/* + * Linux underlying file name: <directory>/odp-<odp_pid>-ishm-<name> + * The <name> part may be replaced by a sequence number if no specific + * name is given at reserve time + * <directory> is either /tmp or the hugepagefs mount point for default size. + * (searched at init time) + */ +#define ISHM_FILENAME_MAXLEN (ISHM_NAME_MAXLEN + 64) +#define ISHM_FILENAME_FORMAT "%s/odp-%d-ishm-%s" +#define ISHM_FILENAME_NORMAL_PAGE_DIR "/tmp" + +/* + * At worse case the virtual space gets so fragmented that there is + * a unallocated fragment between each allocated fragment: + * In that case, the number of fragments to take care of is twice the + * number of ISHM blocks + 1. + */ +#define ISHM_NB_FRAGMNTS (ISHM_MAX_NB_BLOCKS * 2 + 1) + +/* + * A block describes a piece of reserved memory: Any successful ishm_reserve() + * will allocate a block. A ishm_reserve() with the _ODP_ISHM_SINGLE_VA flag set + * will allocated both a block and a fragment. + * Blocks contain only global data common to all processes. + */ +typedef struct ishm_fragment ishm_fragment_t; /*forward decl. see below. */ +typedef struct ishm_block { + char name[ISHM_NAME_MAXLEN]; /* name for the ishm block (if any) */ + char filename[ISHM_FILENAME_MAXLEN]; /* name of the .../odp-* file */ + int main_odpthread; /* The thread which did the initial reserve*/ + uint32_t user_flags; /* any flags the user want to remember. */ + uint32_t flags; /* block creation flags. */ + uint64_t user_len; /* length, as requested at reserve time. */ + void *start; /* only valid if _ODP_ISHM_SINGLE_VA is set*/ + uint64_t len; /* length. multiple of page size. 0 if free*/ + ishm_fragment_t *fragment; /* used when _ODP_ISHM_SINGLE_VA is used */ + int huge; /* true if this segment is mapped using huge pages */ + uint64_t seq; /* sequence number, incremented on alloc and free */ + uint64_t refcnt;/* number of linux processes mapping this block */ +} ishm_block_t; + +/* + * Table of blocks describing allocated internal shared memory + * This table is visible to every ODP thread (linux process or pthreads). + * (it is allocated shared at odp init time and is therefore inherited by all) + * Table index is used as handle, so it cannot move!. Entry is regarded as + * free when len==0 + */ +typedef struct { + odp_spinlock_t lock; + uint64_t dev_seq; /* used when creating device names */ + ishm_block_t block[ISHM_MAX_NB_BLOCKS]; +} ishm_table_t; +static ishm_table_t *ishm_tbl; + +/* + * Process local table containing the list of (believed) allocated blocks seen + * from the current process. There is one such table per linux process. linux + * threads within a process shares this table. + * The contents within this table may become obsolete when other processes + * reserve/free ishm blocks. This is what the procsync() function + * catches by comparing the block sequence number with the one in this table. + * This table is filled at ishm_reserve and ishm_lookup time. + * Entries are removed at ishm_free or procsync time. + * Note that flags and len are present in this table and seems to be redundant + * with those present in the ishm block table: but this is not fully true: + * When ishm_sync() detects obsolete mappings and tries to remove them, + * the entry in the ishm block table is then obsolete, and the values which are + * found in this table must be used to perform the ummap. + * (and the values in the block tables are needed at lookup time...) + */ +typedef struct { + int thrd_refcnt; /* number of pthreads in this process, really */ + struct { + int block_index; /* entry in the ishm_tbl */ + uint32_t flags; /* flags used at creation time */ + uint64_t seq; + void *start; /* start of block (VA) */ + uint64_t len; /* length of block. multiple of page size */ + int fd; /* file descriptor used for this block */ + } entry[ISHM_MAX_NB_BLOCKS]; + int nb_entries; +} ishm_proctable_t; +static ishm_proctable_t *ishm_proctable; + +/* + * A fragment describes a piece of the shared virtual address space, + * and is allocated only when allocation is done with the _ODP_ISHM_SINGLE_VA + * flag: + * A fragment is said to be used when it actually does represent some + * portion of the virtual address space, and is said to be unused when + * it does not (so at start, one single fragment is used -describing the + * whole address space as unallocated-, and all others are unused). + * Fragments get used as address space fragmentation increases. + * A fragment is allocated if the piece of address space it + * describes is actually used by a shared memory block. + * Allocated fragments get their block_index set >=0. + */ +typedef struct ishm_fragment { + struct ishm_fragment *prev; /* not used when the fragment is unused */ + struct ishm_fragment *next; + void *start; /* start of segment (VA) */ + uintptr_t len; /* length of segment. multiple of page size */ + int block_index; /* -1 for unallocated fragments */ +} ishm_fragment_t; + +/* + * Table of fragments describing the common virtual address space: + * This table is visible to every ODP thread (linux process or pthreads). + * (it is allocated at odp init time and is therefore inherited by all) + */ +typedef struct { + ishm_fragment_t fragment[ISHM_NB_FRAGMNTS]; + ishm_fragment_t *used_fragmnts; /* ordered by increasing start addr */ + ishm_fragment_t *unused_fragmnts; +} ishm_ftable_t; +static ishm_ftable_t *ishm_ftbl; + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +/* prototypes: */ +static void procsync(void); + +/* + * Take a piece of the preallocated virtual space to fit "size" bytes. + * (best fit). Size must be rounded up to an integer number of pages size. + * Possibly split the fragment to keep track of remaining space. + * Returns the allocated fragment (best_fragmnt) and the corresponding address. + * External caller must ensure mutex before the call! + */ +static void *alloc_fragment(uintptr_t size, int block_index, intptr_t align, + ishm_fragment_t **best_fragmnt) +{ + ishm_fragment_t *fragmnt; + *best_fragmnt = NULL; + ishm_fragment_t *rem_fragmnt; + uintptr_t border;/* possible start of new fragment (next alignement) */ + intptr_t left; /* room remaining after, if the segment is allocated */ + uintptr_t remainder = ODP_CONFIG_ISHM_VA_PREALLOC_SZ; + + /* + * search for the best bit, i.e. search for the unallocated fragment + * would give less remainder if the new fragment was allocated within + * it: + */ + for (fragmnt = ishm_ftbl->used_fragmnts; + fragmnt; fragmnt = fragmnt->next) { + /* skip allocated segment: */ + if (fragmnt->block_index >= 0) + continue; + /* skip too short segment: */ + border = ((uintptr_t)fragmnt->start + align - 1) & (-align); + left = + ((uintptr_t)fragmnt->start + fragmnt->len) - (border + size); + if (left < 0) + continue; + /* remember best fit: */ + if ((uintptr_t)left < remainder) { + remainder = left; /* best, so far */ + *best_fragmnt = fragmnt; + } + } + + if (!(*best_fragmnt)) { + ODP_ERR("unable to get virtual address for shmem block!\n."); + return NULL; + } + + (*best_fragmnt)->block_index = block_index; + border = ((uintptr_t)(*best_fragmnt)->start + align - 1) & (-align); + + /* + * if there is room between previous fragment and new one, (due to + * alignement requirement) then fragment (split) the space between + * the end of the previous fragment and the beginning of the new one: + */ + if (border - (uintptr_t)(*best_fragmnt)->start > 0) { + /* frangment space, i.e. take a new fragment descriptor... */ + rem_fragmnt = ishm_ftbl->unused_fragmnts; + if (!rem_fragmnt) { + ODP_ERR("unable to get shmem fragment descriptor!\n."); + return NULL; + } + ishm_ftbl->unused_fragmnts = rem_fragmnt->next; + + /* and link it between best_fragmnt->prev and best_fragmnt */ + if ((*best_fragmnt)->prev) + (*best_fragmnt)->prev->next = rem_fragmnt; + else + ishm_ftbl->used_fragmnts = rem_fragmnt; + rem_fragmnt->prev = (*best_fragmnt)->prev; + (*best_fragmnt)->prev = rem_fragmnt; + rem_fragmnt->next = (*best_fragmnt); + + /* update length: rem_fragmnt getting space before border */ + rem_fragmnt->block_index = -1; + rem_fragmnt->start = (*best_fragmnt)->start; + rem_fragmnt->len = border - (uintptr_t)(*best_fragmnt)->start; + (*best_fragmnt)->start = + (void *)((uintptr_t)rem_fragmnt->start + rem_fragmnt->len); + (*best_fragmnt)->len -= rem_fragmnt->len; + } + + /* if this was a perfect fit, i.e. no free space follows, we are done */ + if (remainder == 0) + return (*best_fragmnt)->start; + + /* otherwise, frangment space, i.e. take a new fragment descriptor... */ + rem_fragmnt = ishm_ftbl->unused_fragmnts; + if (!rem_fragmnt) { + ODP_ERR("unable to get shmem fragment descriptor!\n."); + return (*best_fragmnt)->start; + } + ishm_ftbl->unused_fragmnts = rem_fragmnt->next; + + /* ... double link it... */ + rem_fragmnt->next = (*best_fragmnt)->next; + rem_fragmnt->prev = (*best_fragmnt); + if ((*best_fragmnt)->next) + (*best_fragmnt)->next->prev = rem_fragmnt; + (*best_fragmnt)->next = rem_fragmnt; + + /* ... and keep track of the remainder */ + (*best_fragmnt)->len = size; + rem_fragmnt->len = remainder; + rem_fragmnt->start = (void *)((char *)(*best_fragmnt)->start + size); + rem_fragmnt->block_index = -1; + + return (*best_fragmnt)->start; +} + +/* + * Free a portion of virtual space. + * Possibly defragment, if the freed fragment is adjacent to another + * free virtual fragment. + * External caller must ensure mutex before the call! + */ +static void free_fragment(ishm_fragment_t *fragmnt) +{ + ishm_fragment_t *prev_f; + ishm_fragment_t *next_f; + + /* sanity check */ + if (!fragmnt) + return; + + prev_f = fragmnt->prev; + next_f = fragmnt->next; + + /* free the fragment */ + fragmnt->block_index = -1; + + /* check if the previous fragment is also free: if so, defragment */ + if (prev_f && (prev_f->block_index < 0)) { + fragmnt->start = prev_f->start; + fragmnt->len += prev_f->len; + if (prev_f->prev) { + prev_f->prev->next = fragmnt; + } else { + if (ishm_ftbl->used_fragmnts == prev_f) + ishm_ftbl->used_fragmnts = fragmnt; + else + ODP_ERR("corrupted fragment list!.\n"); + } + fragmnt->prev = prev_f->prev; + + /* put removed fragment in free list */ + prev_f->prev = NULL; + prev_f->next = ishm_ftbl->unused_fragmnts; + ishm_ftbl->unused_fragmnts = prev_f; + } + + /* check if the next fragment is also free: if so, defragment */ + if (next_f && (next_f->block_index < 0)) { + fragmnt->len += next_f->len; + if (next_f->next) + next_f->next->prev = fragmnt; + fragmnt->next = next_f->next; + + /* put removed fragment in free list */ + next_f->prev = NULL; + next_f->next = ishm_ftbl->unused_fragmnts; + ishm_ftbl->unused_fragmnts = next_f; + } +} + +/* + * Create file with size len. returns -1 on error + * Creates a file to /tmp/odp-<pid>-<sequence_or_name> (for normal pages) + * or /mnt/huge/odp-<pid>-<sequence_or_name> (for huge pages) + * Return the new file descriptor, or -1 on error. + */ +static int create_file(int block_index, int huge, uint64_t len) +{ + char *name; + int fd; + ishm_block_t *new_block; /* entry in the main block table */ + char seq_string[ISHM_FILENAME_MAXLEN]; /* used to construct filename*/ + char filename[ISHM_FILENAME_MAXLEN];/* filename in /tmp/ or /mnt/huge */ + int oflag = O_RDWR | O_CREAT | O_TRUNC; /* flags for open */ + + new_block = &ishm_tbl->block[block_index]; + name = new_block->name; + + /* create the filename: */ + snprintf(seq_string, ISHM_FILENAME_MAXLEN, "%08" PRIu64, + ishm_tbl->dev_seq++); + + /* huge dir must be known to create files there!: */ + if (huge && !odp_global_data.hugepage_info.default_huge_page_dir) + return -1; + + if (huge) + snprintf(filename, ISHM_FILENAME_MAXLEN, + ISHM_FILENAME_FORMAT, + odp_global_data.hugepage_info.default_huge_page_dir, + odp_global_data.main_pid, + (name && name[0]) ? name : seq_string); + else + snprintf(filename, ISHM_FILENAME_MAXLEN, + ISHM_FILENAME_FORMAT, + ISHM_FILENAME_NORMAL_PAGE_DIR, + odp_global_data.main_pid, + (name && name[0]) ? name : seq_string); + + fd = open(filename, oflag, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (fd < 0) { + ODP_ERR("open failed for %s: %s.\n", + filename, strerror(errno)); + return -1; + } + + if (ftruncate(fd, len) == -1) { + ODP_ERR("ftruncate failed: fd=%d, err=%s.\n", + fd, strerror(errno)); + close(fd); + return -1; + } + + strncpy(new_block->filename, filename, ISHM_FILENAME_MAXLEN - 1); + + return fd; +} + +/* + * performs the mapping, possibly allocating a fragment of the pre-reserved + * VA space if the _ODP_ISHM_SINGLE_VA flag was given. + * Sets fd, and returns the mapping address. + * This funstion will also set the _ODP_ISHM_SINGLE_VA flag if the alignment + * requires it + * Mutex must be assured by the caller. + */ +static void *do_map(int block_index, uint64_t len, uint32_t align, + uint32_t flags, int huge, int *fd) +{ + ishm_block_t *new_block; /* entry in the main block table */ + void *addr = NULL; + void *mapped_addr; + ishm_fragment_t *fragment = NULL; + + new_block = &ishm_tbl->block[block_index]; + + /* + * Creates a file to /tmp/odp-<pid>-<sequence> (for normal pages) + * or /mnt/huge/odp-<pid>-<sequence> (for huge pages) + * unless a fd was already given + */ + if (*fd < 0) { + *fd = create_file(block_index, huge, len); + if (*fd < 0) + return NULL; + } else { + new_block->filename[0] = 0; + } + + /* allocate an address range in the prebooked VA area if needed */ + if (flags & _ODP_ISHM_SINGLE_VA) { + addr = alloc_fragment(len, block_index, align, &fragment); + if (!addr) { + ODP_ERR("alloc_fragment failed.\n"); + if (new_block->filename[0]) { + close(*fd); + *fd = -1; + unlink(new_block->filename); + } + return NULL; + } + ishm_tbl->block[block_index].fragment = fragment; + } + + /* try to mmap: */ + mapped_addr = _odp_ishmphy_map(*fd, addr, len, flags); + if (mapped_addr == NULL) { + if (flags & _ODP_ISHM_SINGLE_VA) + free_fragment(fragment); + if (new_block->filename[0]) { + close(*fd); + *fd = -1; + unlink(new_block->filename); + } + return NULL; + } + + new_block->huge = huge; + + return mapped_addr; +} + +/* + * Performs an extra mapping (for a process trying to see an existing block + * i.e. performing a lookup). + * Mutex must be assured by the caller. + */ +static void *do_remap(int block_index, int fd) +{ + void *mapped_addr; + ishm_fragment_t *fragment; + uint64_t len; + uint32_t flags; + + len = ishm_tbl->block[block_index].len; + flags = ishm_tbl->block[block_index].flags; + + if (flags & _ODP_ISHM_SINGLE_VA) { + fragment = ishm_tbl->block[block_index].fragment; + if (!fragment) { + ODP_ERR("invalid fragment failure.\n"); + return NULL; + } + + /* try to mmap: */ + mapped_addr = _odp_ishmphy_map(fd, fragment->start, len, flags); + if (mapped_addr == NULL) + return NULL; + return mapped_addr; + } + + /* try to mmap: */ + mapped_addr = _odp_ishmphy_map(fd, NULL, len, flags); + if (mapped_addr == NULL) + return NULL; + + return mapped_addr; +} + +/* + * Performs unmapping, possibly freeing a prereserved VA space fragment, + * if the _ODP_ISHM_SINGLE_VA flag was set at alloc time + * Mutex must be assured by the caller. + */ +static int do_unmap(void *start, uint64_t size, uint32_t flags, + int block_index) +{ + int ret; + + if (start) + ret = _odp_ishmphy_unmap(start, size, flags); + else + ret = 0; + + if ((block_index >= 0) && (flags & _ODP_ISHM_SINGLE_VA)) { + /* mark reserved address space as free */ + free_fragment(ishm_tbl->block[block_index].fragment); + } + + return ret; +} + +/* + * Search for a given used and allocated block name. + * (search is performed in the global ishm table) + * Returns the index of the found block (if any) or -1 if none. + * Mutex must be assured by the caller. + */ +static int find_block_by_name(const char *name) +{ + int i; + + if (name == NULL || name[0] == 0) + return -1; + + for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) { + if ((ishm_tbl->block[i].len) && + (strcmp(name, ishm_tbl->block[i].name) == 0)) + return i; + } + + return -1; +} + +/* + * Search for a block by address (only works when flag _ODP_ISHM_SINGLE_VA + * was set at reserve() time, or if the block is already known by this + * process). + * Search is performed in the process table and in the global ishm table. + * The provided address does not have to be at start: any address + * within the fragment is OK. + * Returns the index to the found block (if any) or -1 if none. + * Mutex must be assured by the caller. + */ +static int find_block_by_address(void *addr) +{ + int block_index; + int i; + ishm_fragment_t *fragmnt; + + /* + * first check if there is already a process known block for this + * address + */ + for (i = 0; i < ishm_proctable->nb_entries; i++) { + block_index = ishm_proctable->entry[i].block_index; + if ((addr > ishm_proctable->entry[i].start) && + ((char *)addr < ((char *)ishm_proctable->entry[i].start + + ishm_tbl->block[block_index].len))) + return block_index; + } + + /* + * then check if there is a existing single VA block known by some other + * process and containing the given address + */ + for (i = 0; i < ISHM_MAX_NB_BLOCKS; i++) { + if ((!ishm_tbl->block[i].len) || + (!(ishm_tbl->block[i].flags & _ODP_ISHM_SINGLE_VA))) + continue; + fragmnt = ishm_tbl->block[i].fragment; + if (!fragmnt) { + ODP_ERR("find_fragment: invalid NULL fragment\n"); + return -1; + } + if ((addr >= fragmnt->start) && + ((char *)addr < ((char *)fragmnt->start + fragmnt->len))) + return i; + } + + /* address does not belong to any accessible block: */ + return -1; +} + +/* + * Search a given ishm block in the process local table. Return its index + * in the process table or -1 if not found (meaning that the ishm table + * block index was not referenced in the process local table, i.e. the + * block is known by some other process, but not by the current process). + * Caller must assure mutex. + */ +static int procfind_block(int block_index) +{ + int i; + + for (i = 0; i < ishm_proctable->nb_entries; i++) { + if (ishm_proctable->entry[i].block_index == block_index) + return i; + } + return -1; +} + +/* + * Release the physical memory mapping for blocks which have been freed + * by other processes. Caller must ensure mutex. + * Mutex must be assured by the caller. + */ +static void procsync(void) +{ + int i = 0; + int last; + ishm_block_t *block; + + last = ishm_proctable->nb_entries; + while (i < last) { + /* if the procecess sequence number doesn't match the main + * table seq number, this entry is obsolete + */ + block = &ishm_tbl->block[ishm_proctable->entry[i].block_index]; + if (ishm_proctable->entry[i].seq != block->seq) { + /* obsolete entry: free memory and remove proc entry */ + close(ishm_proctable->entry[i].fd); + _odp_ishmphy_unmap(ishm_proctable->entry[i].start, + ishm_proctable->entry[i].len, + ishm_proctable->entry[i].flags); + ishm_proctable->entry[i] = + ishm_proctable->entry[--last]; + } else { + i++; + } + } + ishm_proctable->nb_entries = last; +} + +/* + * Allocate and map internal shared memory, or other objects: + * If a name is given, check that this name is not already in use. + * If ok, allocate a new shared memory block and map the + * provided fd in it (if fd >=0 was given). + * If no fd is provided, a shared memory file desc named + * /tmp/odp-<pid>-ishm-<name_or_sequence> is created and mapped. + * (the name is different for huge page file as they must be on hugepagefs) + * The function returns the index of the newly created block in the + * main block table (>=0) or -1 on error. + */ +int _odp_ishm_reserve(const char *name, uint64_t size, int fd, + uint32_t align, uint32_t flags, uint32_t user_flags) +{ + int new_index; /* index in the main block table*/ + ishm_block_t *new_block; /* entry in the main block table*/ + uint64_t page_sz; /* normal page size. usually 4K*/ + uint64_t alloc_size; /* includes extra for alignement*/ + uint64_t page_hp_size; /* huge page size */ + uint64_t alloc_hp_size; /* includes extra for alignement*/ + uint32_t hp_align; + uint64_t len; /* mapped length */ + void *addr = NULL; /* mapping address */ + int new_proc_entry; + + page_sz = odp_sys_page_size(); + + odp_spinlock_lock(&ishm_tbl->lock); + + /* update this process view... */ + procsync(); + + /* roundup to page size */ + alloc_size = (size + (page_sz - 1)) & (-page_sz); + + page_hp_size = odp_sys_huge_page_size(); + /* roundup to page size */ + alloc_hp_size = (size + (page_hp_size - 1)) & (-page_hp_size); + + /* check if name already exists */ + if (name && (find_block_by_name(name) >= 0)) { + /* Found a block with the same name */ + odp_spinlock_unlock(&ishm_tbl->lock); + ODP_ERR("name \"%s\" already used.\n", name); + return -1; + } + + /* grab a new entry: */ + for (new_index = 0; new_index < ISHM_MAX_NB_BLOCKS; new_index++) { + if (ishm_tbl->block[new_index].len == 0) { + /* Found free block */ + break; + } + } + + /* check if we have reached the maximum number of allocation: */ + if (new_index >= ISHM_MAX_NB_BLOCKS) { + odp_spinlock_unlock(&ishm_tbl->lock); + ODP_ERR("ISHM_MAX_NB_BLOCKS limit reached!\n"); + return -1; + } + + new_block = &ishm_tbl->block[new_index]; + + /* save block name (if any given): */ + if (name) + strncpy(new_block->name, name, ISHM_NAME_MAXLEN - 1); + else + new_block->name[0] = 0; + + /* Try first huge pages when possible and needed: */ + if (page_hp_size && (alloc_size > page_sz)) { + /* at least, alignment in VA should match page size, but user + * can request more: If the user requirement exceeds the page + * size then we have to make sure the block will be mapped at + * the same address every where, otherwise alignment may be + * be wrong for some process */ + hp_align = align; + if (hp_align < odp_sys_huge_page_size()) + hp_align = odp_sys_huge_page_size(); + else + flags |= _ODP_ISHM_SINGLE_VA; + len = alloc_hp_size; + addr = do_map(new_index, len, hp_align, flags, 1, &fd); + + if (addr == NULL) + ODP_DBG("No huge pages, fall back to normal pages, " + "check: /proc/sys/vm/nr_hugepages.\n"); + else + new_block->huge = 1; + } + + /* try normal pages if huge pages failed */ + if (addr == NULL) { + /* at least, alignment in VA should match page size, but user + * can request more: If the user requirement exceeds the page + * size then we have to make sure the block will be mapped at + * the same address every where, otherwise alignment may be + * be wrong for some process */ + if (align < odp_sys_page_size()) + align = odp_sys_page_size(); + else + flags |= _ODP_ISHM_SINGLE_VA; + + len = alloc_size; + addr = do_map(new_index, len, align, flags, 0, &fd); + new_block->huge = 0; + } + + /* if neither huge pages or normal pages works, we cannot proceed: */ + if ((addr == NULL) || (len == 0)) { + if ((new_block->filename[0]) && (fd >= 0)) + close(fd); + odp_spinlock_unlock(&ishm_tbl->lock); + ODP_ERR("_ishm_reserve failed.\n"); + return -1; + } + + /* remember block data and increment block seq number to mark change */ + new_block->len = len; + new_block->user_len = size; + new_block->flags = flags; + new_block->user_flags = user_flags; + new_block->seq++; + new_block->refcnt = 1; + new_block->main_odpthread = odp_thread_id(); + new_block->start = addr; /* only for SINGLE_VA*/ + + /* the allocation succeeded: update the process local view */ + new_proc_entry = ishm_proctable->nb_entries++; + ishm_proctable->entry[new_proc_entry].block_index = new_index; + ishm_proctable->entry[new_proc_entry].flags = flags; + ishm_proctable->entry[new_proc_entry].seq = new_block->seq; + ishm_proctable->entry[new_proc_entry].start = addr; + ishm_proctable->entry[new_proc_entry].len = len; + ishm_proctable->entry[new_proc_entry].fd = fd; + + /* register the file descriptor to the file descriptor server. */ + _odp_fdserver_register_fd(FD_SRV_CTX_ISHM, new_index, fd); + + odp_spinlock_unlock(&ishm_tbl->lock); + return new_index; +} + +/* + * Free and unmap internal shared memory: + * The file descriptor is closed and the .../odp-* file deleted, + * unless fd was externally provided at reserve() time. + * return 0 if OK, and -1 on error. + * Mutex must be assured by the caller. + */ +static int block_free(int block_index) +{ + int proc_index; + ishm_block_t *block; /* entry in the main block table*/ + int last; + + if ((block_index < 0) || + (block_index >= ISHM_MAX_NB_BLOCKS) || + (ishm_tbl->block[block_index].len == 0)) { + ODP_ERR("Request to free an invalid block\n"); + return -1; + } + + block = &ishm_tbl->block[block_index]; + + proc_index = procfind_block(block_index); + if (proc_index >= 0) { + /* close the fd, unless if it was externaly provided */ + if ((block->filename[0] != 0) || + (odp_thread_id() != block->main_odpthread)) + close(ishm_proctable->entry[proc_index].fd); + + /* remove the mapping and possible fragment */ + do_unmap(ishm_proctable->entry[proc_index].start, + block->len, + ishm_proctable->entry[proc_index].flags, + block_index); + + /* remove entry from process local table: */ + last = ishm_proctable->nb_entries - 1; + ishm_proctable->entry[proc_index] = + ishm_proctable->entry[last]; + ishm_proctable->nb_entries = last; + } else { + /* just possibly free the fragment as no mapping exist here: */ + do_unmap(NULL, 0, block->flags, block_index); + } + + /* remove the .../odp-* file, unless fd was external: */ + if (block->filename[0] != 0) + unlink(block->filename); + + /* deregister the file descriptor from the file descriptor server. */ + _odp_fdserver_deregister_fd(FD_SRV_CTX_ISHM, block_index); + + /* mark the block as free in the main block table: */ + block->len = 0; + + /* mark the change so other processes see this entry as obsolete: */ + block->seq++; + + return 0; +} + +/* + * Free and unmap internal shared memory, intentified by its block number: + * return -1 on error. 0 if OK. + */ +int _odp_ishm_free_by_index(int block_index) +{ + int ret; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + ret = block_free(block_index); + odp_spinlock_unlock(&ishm_tbl->lock); + return ret; +} + +/* + * free and unmap internal shared memory, intentified by its block name: + * return -1 on error. 0 if OK. + */ +int _odp_ishm_free_by_name(const char *name) +{ + int block_index; + int ret; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + /* search the block in main ishm table */ + block_index = find_block_by_name(name); + if (block_index < 0) { + ODP_ERR("Request to free an non existing block..." + " (double free?)\n"); + odp_spinlock_unlock(&ishm_tbl->lock); + return -1; + } + + ret = block_free(block_index); + odp_spinlock_unlock(&ishm_tbl->lock); + return ret; +} + +/* + * Free and unmap internal shared memory identified by address: + * return -1 on error. 0 if OK. + */ +int _odp_ishm_free_by_address(void *addr) +{ + int block_index; + int ret; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + /* search the block in main ishm table */ + block_index = find_block_by_address(addr); + if (block_index < 0) { + ODP_ERR("Request to free an non existing block..." + " (double free?)\n"); + odp_spinlock_unlock(&ishm_tbl->lock); + return -1; + } + + ret = block_free(block_index); + + odp_spinlock_unlock(&ishm_tbl->lock); + return ret; +} + +/* + * Lookup for an ishm shared memory, identified by its block index + * in the main ishm block table. + * Map this ishm area in the process VA (if not already present). + * Returns the block user address or NULL on error. + * Mutex must be assured by the caller. + */ +static void *block_lookup(int block_index) +{ + int proc_index; + int fd = -1; + ishm_block_t *block; + void *mapped_addr; + int new_entry; + + if ((block_index < 0) || + (block_index >= ISHM_MAX_NB_BLOCKS) || + (ishm_tbl->block[block_index].len == 0)) { + ODP_ERR("Request to lookup an invalid block\n"); + return NULL; + } + + /* search it in process table: if there, this process knows it already*/ + proc_index = procfind_block(block_index); + if (proc_index >= 0) + return ishm_proctable->entry[proc_index].start; + + /* this ishm is not known by this process, yet: we create the mapping.*/ + fd = _odp_fdserver_lookup_fd(FD_SRV_CTX_ISHM, block_index); + if (fd < 0) { + ODP_ERR("Could not find ishm file descriptor (BUG!)\n"); + return NULL; + } + + /* perform the mapping */ + block = &ishm_tbl->block[block_index]; + + mapped_addr = do_remap(block_index, fd); + if (mapped_addr == NULL) { + ODP_ERR(" lookup: Could not map existing shared memory!\n"); + return NULL; + } + + /* the mapping succeeded: update the process local view */ + new_entry = ishm_proctable->nb_entries++; + ishm_proctable->entry[new_entry].block_index = block_index; + ishm_proctable->entry[new_entry].flags = block->flags; + ishm_proctable->entry[new_entry].seq = block->seq; + ishm_proctable->entry[new_entry].start = mapped_addr; + ishm_proctable->entry[new_entry].len = block->len; + ishm_proctable->entry[new_entry].fd = fd; + block->refcnt++; + + return mapped_addr; +} + +/* + * Lookup for an ishm shared memory, identified by its block_index. + * Maps this ishmem area in the process VA (if not already present). + * Returns the block user address, or NULL if the index + * does not match any known ishm blocks. + */ +void *_odp_ishm_lookup_by_index(int block_index) +{ + void *ret; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + ret = block_lookup(block_index); + odp_spinlock_unlock(&ishm_tbl->lock); + return ret; +} + +/* + * Lookup for an ishm shared memory, identified by its block name. + * Map this ishm area in the process VA (if not already present). + * Return the block index, or -1 if the index + * does not match any known ishm blocks. + */ +int _odp_ishm_lookup_by_name(const char *name) +{ + int block_index; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + /* search the block in main ishm table: return -1 if not found: */ + block_index = find_block_by_name(name); + if ((block_index < 0) || (!block_lookup(block_index))) { + odp_spinlock_unlock(&ishm_tbl->lock); + return -1; + } + + odp_spinlock_unlock(&ishm_tbl->lock); + return block_index; +} + +/* + * Lookup for an ishm shared memory block, identified by its VA address. + * This works only if the block has already been looked-up (mapped) by the + * current process or it it was created with the _ODP_ISHM_SINGLE_VA flag. + * Map this ishm area in the process VA (if not already present). + * Return the block index, or -1 if the address + * does not match any known ishm blocks. + */ +int _odp_ishm_lookup_by_address(void *addr) +{ + int block_index; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + /* search the block in main ishm table: return -1 if not found: */ + block_index = find_block_by_address(addr); + if ((block_index < 0) || (!block_lookup(block_index))) { + odp_spinlock_unlock(&ishm_tbl->lock); + return -1; + } + + odp_spinlock_unlock(&ishm_tbl->lock); + return block_index; +} + +/* + * Returns the VA address of a given block (which has to be known in the current + * process). Returns NULL if the block is unknown. + */ +void *_odp_ishm_address(int block_index) +{ + int proc_index; + void *addr; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + if ((block_index < 0) || + (block_index >= ISHM_MAX_NB_BLOCKS) || + (ishm_tbl->block[block_index].len == 0)) { + ODP_ERR("Request for address on an invalid block\n"); + odp_spinlock_unlock(&ishm_tbl->lock); + return NULL; + } + + proc_index = procfind_block(block_index); + if (proc_index < 0) { + odp_spinlock_unlock(&ishm_tbl->lock); + return NULL; + } + + addr = ishm_proctable->entry[proc_index].start; + odp_spinlock_unlock(&ishm_tbl->lock); + return addr; +} + +int _odp_ishm_info(int block_index, _odp_ishm_info_t *info) +{ + int proc_index; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + if ((block_index < 0) || + (block_index >= ISHM_MAX_NB_BLOCKS) || + (ishm_tbl->block[block_index].len == 0)) { + odp_spinlock_unlock(&ishm_tbl->lock); + ODP_ERR("Request for info on an invalid block\n"); + return -1; + } + + /* search it in process table: if not there, need to map*/ + proc_index = procfind_block(block_index); + if (proc_index < 0) { + odp_spinlock_unlock(&ishm_tbl->lock); + return -1; + } + + info->name = ishm_tbl->block[block_index].name; + info->addr = ishm_proctable->entry[proc_index].start; + info->size = ishm_tbl->block[block_index].user_len; + info->page_size = ishm_tbl->block[block_index].huge ? + odp_sys_huge_page_size() : odp_sys_page_size(); + info->flags = ishm_tbl->block[block_index].flags; + info->user_flags = ishm_tbl->block[block_index].user_flags; + + odp_spinlock_unlock(&ishm_tbl->lock); + return 0; +} + +int _odp_ishm_init_global(void) +{ + void *addr; + void *spce_addr; + int i; + + if (!odp_global_data.hugepage_info.default_huge_page_dir) + ODP_DBG("NOTE: No support for huge pages\n"); + else + ODP_DBG("Huge pages mount point is: %s\n", + odp_global_data.hugepage_info.default_huge_page_dir); + + /* allocate space for the internal shared mem block table: */ + addr = mmap(NULL, sizeof(ishm_table_t), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) { + ODP_ERR("unable to mmap the main block table\n."); + goto init_glob_err1; + } + ishm_tbl = addr; + memset(ishm_tbl, 0, sizeof(ishm_table_t)); + ishm_tbl->dev_seq = 0; + odp_spinlock_init(&ishm_tbl->lock); + + /* allocate space for the internal shared mem fragment table: */ + addr = mmap(NULL, sizeof(ishm_ftable_t), + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (addr == MAP_FAILED) { + ODP_ERR("unable to mmap the main fragment table\n."); + goto init_glob_err2; + } + ishm_ftbl = addr; + memset(ishm_ftbl, 0, sizeof(ishm_ftable_t)); + + /* + *reserve the address space for _ODP_ISHM_SINGLE_VA reserved blocks, + * only address space! + */ + spce_addr = _odp_ishmphy_book_va(ODP_CONFIG_ISHM_VA_PREALLOC_SZ, + odp_sys_huge_page_size()); + if (!spce_addr) { + ODP_ERR("unable to reserve virtual space\n."); + goto init_glob_err3; + } + + /* use the first fragment descriptor to describe to whole VA space: */ + ishm_ftbl->fragment[0].block_index = -1; + ishm_ftbl->fragment[0].start = spce_addr; + ishm_ftbl->fragment[0].len = ODP_CONFIG_ISHM_VA_PREALLOC_SZ; + ishm_ftbl->fragment[0].prev = NULL; + ishm_ftbl->fragment[0].next = NULL; + ishm_ftbl->used_fragmnts = &ishm_ftbl->fragment[0]; + + /* and put all other fragment descriptors in the unused list: */ + for (i = 1; i < ISHM_NB_FRAGMNTS - 1; i++) { + ishm_ftbl->fragment[i].prev = NULL; + ishm_ftbl->fragment[i].next = &ishm_ftbl->fragment[i + 1]; + } + ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].prev = NULL; + ishm_ftbl->fragment[ISHM_NB_FRAGMNTS - 1].next = NULL; + ishm_ftbl->unused_fragmnts = &ishm_ftbl->fragment[1]; + + return 0; + +init_glob_err3: + if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) + ODP_ERR("unable to munmap main fragment table\n."); +init_glob_err2: + if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) + ODP_ERR("unable to munmap main block table\n."); +init_glob_err1: + return -1; +} + +int _odp_ishm_init_local(void) +{ + int i; + int block_index; + + /* + * the ishm_process table is local to each linux process + * Check that no other linux threads (of same or ancestor processes) + * have already created the table, and create it if needed. + * We protect this with the general ishm lock to avoid + * init race condition of different running threads. + */ + odp_spinlock_lock(&ishm_tbl->lock); + if (!ishm_proctable) { + ishm_proctable = malloc(sizeof(ishm_proctable_t)); + if (!ishm_proctable) { + odp_spinlock_unlock(&ishm_tbl->lock); + return -1; + } + memset(ishm_proctable, 0, sizeof(ishm_proctable_t)); + } + if (syscall(SYS_gettid) != getpid()) + ishm_proctable->thrd_refcnt++; /* new linux thread */ + else + ishm_proctable->thrd_refcnt = 1;/* new linux process */ + + /* + * if this ODP thread is actually a new linux process, (as opposed + * to a pthread), i.e, we just forked, then all shmem blocks + * of the parent process are mapped into this child by inheritance. + * (The process local table is inherited as well). We hence have to + * increase the process refcount for each of the inherited mappings: + */ + if (syscall(SYS_gettid) == getpid()) { + for (i = 0; i < ishm_proctable->nb_entries; i++) { + block_index = ishm_proctable->entry[i].block_index; + ishm_tbl->block[block_index].refcnt++; + } + } + + odp_spinlock_unlock(&ishm_tbl->lock); + return 0; +} + +int _odp_ishm_term_global(void) +{ + int ret = 0; + + /* free the fragment table */ + if (munmap(ishm_ftbl, sizeof(ishm_ftable_t)) < 0) { + ret = -1; + ODP_ERR("unable to munmap fragment table\n."); + } + /* free the block table */ + if (munmap(ishm_tbl, sizeof(ishm_table_t)) < 0) { + ret = -1; + ODP_ERR("unable to munmap main table\n."); + } + + /* free the reserved VA space */ + if (_odp_ishmphy_unbook_va()) + ret = -1; + + return ret; +} + +int _odp_ishm_term_local(void) +{ + int i; + int proc_table_refcnt = 0; + int block_index; + ishm_block_t *block; + + odp_spinlock_lock(&ishm_tbl->lock); + procsync(); + + /* + * The ishm_process table is local to each linux process + * Check that no other linux threads (of this linux process) + * still needs the table, and free it if so. + * We protect this with the general ishm lock to avoid + * term race condition of different running threads. + */ + proc_table_refcnt = --ishm_proctable->thrd_refcnt; + if (!proc_table_refcnt) { + /* + * this is the last thread of this process... + * All mappings for this process are about to be lost... + * Go through the table of visible blocks for this process, + * decreasing the refcnt of each visible blocks, and issuing + * warning for those no longer referenced by any process. + * Note that non-referenced blocks are nor freeed: this is + * deliberate as this would imply that the sementic of the + * freeing function would differ depending on whether we run + * with odp_thread as processes or pthreads. With this approach, + * the user should always free the blocks manually, which is + * more consistent + */ + for (i = 0; i < ishm_proctable->nb_entries; i++) { + block_index = ishm_proctable->entry[i].block_index; + block = &ishm_tbl->block[block_index]; + if ((--block->refcnt) <= 0) { + block->refcnt = 0; + ODP_DBG("Warning: block %d: name:%s " + "no longer referenced\n", + i, + ishm_tbl->block[i].name[0] ? + ishm_tbl->block[i].name : "<no name>"); + } + } + + free(ishm_proctable); + ishm_proctable = NULL; + } + + odp_spinlock_unlock(&ishm_tbl->lock); + return 0; +} diff --git a/platform/linux-generic/_ishmphy.c b/platform/linux-generic/_ishmphy.c new file mode 100644 index 0000000..cf2d531 --- /dev/null +++ b/platform/linux-generic/_ishmphy.c @@ -0,0 +1,168 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +/* + * This file handles the lower end of the ishm memory allocator: + * It performs the physical mappings. + */ +#include <odp_posix_extensions.h> +#include <odp_config_internal.h> +#include <odp_internal.h> +#include <odp/api/align.h> +#include <odp/api/system_info.h> +#include <odp/api/debug.h> +#include <odp_debug_internal.h> +#include <odp_align_internal.h> +#include <_ishm_internal.h> +#include <_ishmphy_internal.h> + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <_ishmphy_internal.h> + +static void *common_va_address; +static uint64_t common_va_len; + +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + +/* Book some virtual address space + * This function is called at odp_init_global() time to pre-book some + * virtual address space inherited by all odpthreads (i.e. descendant + * processes and threads) and later used to guarantee the unicity the + * the mapping VA address when memory is reserver with the _ODP_ISHM_SINGLE_VA + * flag. + * returns the address of the mapping or NULL on error. + */ +void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align) +{ + void *addr; + + addr = mmap(NULL, len + align, PROT_NONE, + MAP_SHARED | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0); + if (addr == MAP_FAILED) { + ODP_ERR("_ishmphy_book_va failure\n"); + return NULL; + } + + madvise(addr, len, MADV_DONTNEED); + + common_va_address = addr; + common_va_len = len; + + /* return the nearest aligned address: */ + return (void *)(((uintptr_t)addr + align - 1) & (-align)); +} + +/* Un-book some virtual address space + * This function is called at odp_term_global() time to unbook + * the virtual address space booked by _ishmphy_book_va() + */ +int _odp_ishmphy_unbook_va(void) +{ + int ret; + + ret = munmap(common_va_address, common_va_len); + if (ret) + ODP_ERR("_unishmphy_book_va failure\n"); + return ret; +} + +/* + * do a mapping: + * Performs a mapping of the provided file descriptor to the process VA + * space. If the _ODP_ISHM_SINGLE_VA flag is set, 'start' is assumed to be + * the VA address where the mapping is to be done. + * If the flag is not set, a new VA address is taken. + * returns the address of the mapping or NULL on error. + */ +void *_odp_ishmphy_map(int fd, void *start, uint64_t size, + int flags) +{ + void *mapped_addr; + int mmap_flags = 0; + + if (flags & _ODP_ISHM_SINGLE_VA) { + if (!start) { + ODP_ERR("failure: missing address\n"); + return NULL; + } + /* maps over fragment of reserved VA: */ + mapped_addr = mmap(start, size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_FIXED | mmap_flags, fd, 0); + } else { + /* just do a new mapping in the VA space: */ + mapped_addr = mmap(NULL, size, PROT_READ | PROT_WRITE, + MAP_SHARED | mmap_flags, fd, 0); + } + + if (mapped_addr == MAP_FAILED) { + ODP_ERR("mmap failed:%s\n", strerror(errno)); + return NULL; + } + + /* if locking is requested, lock it...*/ + if (flags & _ODP_ISHM_LOCK) { + if (mlock(mapped_addr, size)) { + if (munmap(mapped_addr, size)) + ODP_ERR("munmap failed:%s\n", strerror(errno)); + ODP_ERR("mlock failed:%s\n", strerror(errno)); + return NULL; + } + } + return mapped_addr; +} + +/* free a mapping: + * If the _ODP_ISHM_SINGLE_VA flag was given at creation time the virtual + * address range must be returned to the preoallocated "pool". this is + * done by mapping non accessibly memory there (hence blocking the VA but + * releasing the physical memory). + * If the _ODP_ISHM_SINGLE_VA flag was not given, both physical memory and + * virtual address space are realeased by calling the normal munmap. + * return 0 on success or -1 on error. + */ +int _odp_ishmphy_unmap(void *start, uint64_t len, int flags) +{ + void *addr; + int ret; + int mmap_flgs; + + mmap_flgs = MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS | MAP_NORESERVE; + + /* if locking was requested, unlock...*/ + if (flags & _ODP_ISHM_LOCK) + munlock(start, len); + + if (flags & _ODP_ISHM_SINGLE_VA) { + /* map unnaccessible memory overwrites previous mapping + * and free the physical memory, but guarantees to block + * the VA range from other mappings + */ + addr = mmap(start, len, PROT_NONE, mmap_flgs, -1, 0); + if (addr == MAP_FAILED) { + ODP_ERR("_ishmphy_free failure for ISHM_SINGLE_VA\n"); + return -1; + } + madvise(start, len, MADV_DONTNEED); + return 0; + } + + /* just release the mapping */ + ret = munmap(start, len); + if (ret) + ODP_ERR("_ishmphy_free failure: %s\n", strerror(errno)); + return ret; +} diff --git a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h deleted file mode 120000 index e86e132..0000000 --- a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h +++ /dev/null @@ -1 +0,0 @@ -../../../default/odp/api/cpu_arch.h \ No newline at end of file diff --git a/platform/linux-generic/arch/arm/odp/api/cpu_arch.h b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h new file mode 100644 index 0000000..22b1da2 --- /dev/null +++ b/platform/linux-generic/arch/arm/odp/api/cpu_arch.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ODP_PLAT_CPU_ARCH_H_ +#define ODP_PLAT_CPU_ARCH_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#define _ODP_CACHE_LINE_SIZE 64 + +static inline void odp_cpu_pause(void) +{ +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c b/platform/linux-generic/arch/arm/odp_cpu_arch.c deleted file mode 120000 index deebc47..0000000 --- a/platform/linux-generic/arch/arm/odp_cpu_arch.c +++ /dev/null @@ -1 +0,0 @@ -../default/odp_cpu_arch.c \ No newline at end of file diff --git a/platform/linux-generic/arch/arm/odp_cpu_arch.c b/platform/linux-generic/arch/arm/odp_cpu_arch.c new file mode 100644 index 0000000..2ac223e --- /dev/null +++ b/platform/linux-generic/arch/arm/odp_cpu_arch.c @@ -0,0 +1,48 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_posix_extensions.h> + +#include <stdlib.h> +#include <time.h> + +#include <odp/api/cpu.h> +#include <odp/api/hints.h> +#include <odp/api/system_info.h> +#include <odp_debug_internal.h> + +#define GIGA 1000000000 + +uint64_t odp_cpu_cycles(void) +{ + struct timespec time; + uint64_t sec, ns, hz, cycles; + int ret; + + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time); + + if (ret != 0) + ODP_ABORT("clock_gettime failed\n"); + + hz = odp_cpu_hz_max(); + sec = (uint64_t)time.tv_sec; + ns = (uint64_t)time.tv_nsec; + + cycles = sec * hz; + cycles += (ns * hz) / GIGA; + + return cycles; +} + +uint64_t odp_cpu_cycles_max(void) +{ + return UINT64_MAX; +} + +uint64_t odp_cpu_cycles_resolution(void) +{ + return 1; +} diff --git a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c deleted file mode 120000 index 39962b8..0000000 --- a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c +++ /dev/null @@ -1 +0,0 @@ -../default/odp_sysinfo_parse.c \ No newline at end of file diff --git a/platform/linux-generic/arch/arm/odp_sysinfo_parse.c b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c new file mode 100644 index 0000000..53e2aae --- /dev/null +++ b/platform/linux-generic/arch/arm/odp_sysinfo_parse.c @@ -0,0 +1,27 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_internal.h> +#include <odp_debug_internal.h> +#include <string.h> + +int cpuinfo_parser(FILE *file ODP_UNUSED, system_info_t *sysinfo) +{ + int i; + + ODP_DBG("Warning: use dummy values for freq and model string\n"); + for (i = 0; i < MAX_CPU_NUMBER; i++) { + sysinfo->cpu_hz_max[i] = 1400000000; + strcpy(sysinfo->model_str[i], "UNKNOWN"); + } + + return 0; +} + +uint64_t odp_cpu_hz_current(int id ODP_UNUSED) +{ + return 0; +} diff --git a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c deleted file mode 120000 index deebc47..0000000 --- a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c +++ /dev/null @@ -1 +0,0 @@ -../default/odp_cpu_arch.c \ No newline at end of file diff --git a/platform/linux-generic/arch/powerpc/odp_cpu_arch.c b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c new file mode 100644 index 0000000..2ac223e --- /dev/null +++ b/platform/linux-generic/arch/powerpc/odp_cpu_arch.c @@ -0,0 +1,48 @@ +/* Copyright (c) 2015, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <odp_posix_extensions.h> + +#include <stdlib.h> +#include <time.h> + +#include <odp/api/cpu.h> +#include <odp/api/hints.h> +#include <odp/api/system_info.h> +#include <odp_debug_internal.h> + +#define GIGA 1000000000 + +uint64_t odp_cpu_cycles(void) +{ + struct timespec time; + uint64_t sec, ns, hz, cycles; + int ret; + + ret = clock_gettime(CLOCK_MONOTONIC_RAW, &time); + + if (ret != 0) + ODP_ABORT("clock_gettime failed\n"); + + hz = odp_cpu_hz_max(); + sec = (uint64_t)time.tv_sec; + ns = (uint64_t)time.tv_nsec; + + cycles = sec * hz; + cycles += (ns * hz) / GIGA; + + return cycles; +} + +uint64_t odp_cpu_cycles_max(void) +{ + return UINT64_MAX; +} + +uint64_t odp_cpu_cycles_resolution(void) +{ + return 1; +} diff --git a/platform/linux-generic/include/_fdserver_internal.h b/platform/linux-generic/include/_fdserver_internal.h index 480ac02..22b2802 100644 --- a/platform/linux-generic/include/_fdserver_internal.h +++ b/platform/linux-generic/include/_fdserver_internal.h @@ -23,6 +23,7 @@ extern "C" { */ typedef enum fd_server_context { FD_SRV_CTX_NA, /* Not Applicable */ + FD_SRV_CTX_ISHM, FD_SRV_CTX_END, /* upper enum limit */ } fd_server_context_e; diff --git a/platform/linux-generic/include/_ishm_internal.h b/platform/linux-generic/include/_ishm_internal.h new file mode 100644 index 0000000..7d27477 --- /dev/null +++ b/platform/linux-generic/include/_ishm_internal.h @@ -0,0 +1,45 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef ODP_ISHM_INTERNAL_H_ +#define ODP_ISHM_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* flags available at ishm_reserve: */ +#define _ODP_ISHM_SINGLE_VA 1 +#define _ODP_ISHM_LOCK 2 + +/** + * Shared memory block info + */ +typedef struct _odp_ishm_info_t { + const char *name; /**< Block name */ + void *addr; /**< Block address */ + uint64_t size; /**< Block size in bytes */ + uint64_t page_size; /**< Memory page size */ + uint32_t flags; /**< _ODP_ISHM_* flags */ + uint32_t user_flags;/**< user specific flags */ +} _odp_ishm_info_t; + +int _odp_ishm_reserve(const char *name, uint64_t size, int fd, uint32_t align, + uint32_t flags, uint32_t user_flags); +int _odp_ishm_free_by_index(int block_index); +int _odp_ishm_free_by_name(const char *name); +int _odp_ishm_free_by_address(void *addr); +void *_odp_ishm_lookup_by_index(int block_index); +int _odp_ishm_lookup_by_name(const char *name); +int _odp_ishm_lookup_by_address(void *addr); +void *_odp_ishm_address(int block_index); +int _odp_ishm_info(int block_index, _odp_ishm_info_t *info); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/linux-generic/include/_ishmphy_internal.h b/platform/linux-generic/include/_ishmphy_internal.h new file mode 100644 index 0000000..4fe560f --- /dev/null +++ b/platform/linux-generic/include/_ishmphy_internal.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ISHMPHY_INTERNAL_H +#define _ISHMPHY_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include <stdint.h> + +void *_odp_ishmphy_book_va(uintptr_t len, intptr_t align); +int _odp_ishmphy_unbook_va(void); +void *_odp_ishmphy_map(int fd, void *start, uint64_t size, int flags); +int _odp_ishmphy_unmap(void *start, uint64_t len, int flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/linux-generic/include/ishmphy_internal.h b/platform/linux-generic/include/ishmphy_internal.h new file mode 100644 index 0000000..0bc4207 --- /dev/null +++ b/platform/linux-generic/include/ishmphy_internal.h @@ -0,0 +1,24 @@ +/* Copyright (c) 2016, Linaro Limited + * All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _ISHMPHY_INTERNAL_H_ +#define _ISHMPHY_INTERNAL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +void *_ishmphy_book_va(uint64_t len); +int _ishmphy_unbook_va(void); +void *_ishmphy_map(int fd, void *start, uint64_t size, + int flags, int mmap_flags); +int _ishmphy_unmap(void *start, uint64_t len, int flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/platform/linux-generic/include/odp_config_internal.h b/platform/linux-generic/include/odp_config_internal.h index 989ea08..3eabc7b 100644 --- a/platform/linux-generic/include/odp_config_internal.h +++ b/platform/linux-generic/include/odp_config_internal.h @@ -110,6 +110,16 @@ extern "C" { */ #define ODP_CONFIG_SHM_BLOCKS (ODP_CONFIG_POOLS + 48) +/* + * Size of the virtual address space pre-reserver for ISHM + * + * This is just virtual space preallocation size, not memory allocation. + * This address space is used by ISHM to map things at a common address in + * all ODP threads (when the _ODP_ISHM_SINGLE_VA flag is used). + * In bytes. + */ +#define ODP_CONFIG_ISHM_VA_PREALLOC_SZ (536870912L) + #ifdef __cplusplus } #endif diff --git a/platform/linux-generic/include/odp_internal.h b/platform/linux-generic/include/odp_internal.h index 2b21777..6ea8cf0 100644 --- a/platform/linux-generic/include/odp_internal.h +++ b/platform/linux-generic/include/odp_internal.h @@ -60,6 +60,7 @@ enum init_stage { TIME_INIT, SYSINFO_INIT, FDSERVER_INIT, + ISHM_INIT, SHM_INIT, THREAD_INIT, POOL_INIT, @@ -128,6 +129,11 @@ int _odp_int_name_tbl_term_global(void); int _odp_fdserver_init_global(void); int _odp_fdserver_term_global(void); +int _odp_ishm_init_global(void); +int _odp_ishm_init_local(void); +int _odp_ishm_term_global(void); +int _odp_ishm_term_local(void); + int cpuinfo_parser(FILE *file, system_info_t *sysinfo); uint64_t odp_cpu_hz_current(int id); diff --git a/platform/linux-generic/odp_init.c b/platform/linux-generic/odp_init.c index ec84fd0..d33a3ae 100644 --- a/platform/linux-generic/odp_init.c +++ b/platform/linux-generic/odp_init.c @@ -57,6 +57,12 @@ int odp_init_global(odp_instance_t *instance, } stage = FDSERVER_INIT; + if (_odp_ishm_init_global()) { + ODP_ERR("ODP ishm init failed.\n"); + goto init_failed; + } + stage = ISHM_INIT; + if (odp_shm_init_global()) { ODP_ERR("ODP shm init failed.\n"); goto init_failed; @@ -224,6 +230,13 @@ int _odp_term_global(enum init_stage stage) } /* Fall through */ + case ISHM_INIT: + if (_odp_ishm_term_global()) { + ODP_ERR("ODP ishm term failed.\n"); + rc = -1; + } + /* Fall through */ + case FDSERVER_INIT: if (_odp_fdserver_term_global()) { ODP_ERR("ODP fdserver term failed.\n"); @@ -268,6 +281,12 @@ int odp_init_local(odp_instance_t instance, odp_thread_type_t thr_type) goto init_fail; } + if (_odp_ishm_init_local()) { + ODP_ERR("ODP ishm local init failed.\n"); + goto init_fail; + } + stage = ISHM_INIT; + if (odp_shm_init_local()) { ODP_ERR("ODP shm local init failed.\n"); goto init_fail; @@ -343,6 +362,13 @@ int _odp_term_local(enum init_stage stage) } /* Fall through */ + case ISHM_INIT: + if (_odp_ishm_term_local()) { + ODP_ERR("ODP ishm local term failed.\n"); + rc = -1; + } + /* Fall through */ + default: break; }
A new ODP internal memory allocator, called ishm (for internal shmem) is introduced here. This memory allocator enables the following: - works for odpthreads being linux processes, regardless for fork time. - guarantees the uniqueness of the virtual space mapping address over all ODP threads (even processes and regardless of fork time), when the required _ODP_ISHM_SINGLE_VA flag is used. Signed-off-by: Christophe Milard <christophe.milard@linaro.org> --- platform/linux-generic/Makefile.am | 4 + platform/linux-generic/_ishm.c | 1336 ++++++++++++++++++++ platform/linux-generic/_ishmphy.c | 168 +++ platform/linux-generic/arch/arm/odp/api/cpu_arch.h | 25 +- platform/linux-generic/arch/arm/odp_cpu_arch.c | 49 +- .../linux-generic/arch/arm/odp_sysinfo_parse.c | 28 +- platform/linux-generic/arch/powerpc/odp_cpu_arch.c | 49 +- .../linux-generic/include/_fdserver_internal.h | 1 + platform/linux-generic/include/_ishm_internal.h | 45 + platform/linux-generic/include/_ishmphy_internal.h | 25 + platform/linux-generic/include/ishmphy_internal.h | 24 + .../linux-generic/include/odp_config_internal.h | 10 + platform/linux-generic/include/odp_internal.h | 6 + platform/linux-generic/odp_init.c | 26 + 14 files changed, 1792 insertions(+), 4 deletions(-) create mode 100644 platform/linux-generic/_ishm.c create mode 100644 platform/linux-generic/_ishmphy.c mode change 120000 => 100644 platform/linux-generic/arch/arm/odp/api/cpu_arch.h mode change 120000 => 100644 platform/linux-generic/arch/arm/odp_cpu_arch.c mode change 120000 => 100644 platform/linux-generic/arch/arm/odp_sysinfo_parse.c mode change 120000 => 100644 platform/linux-generic/arch/powerpc/odp_cpu_arch.c create mode 100644 platform/linux-generic/include/_ishm_internal.h create mode 100644 platform/linux-generic/include/_ishmphy_internal.h create mode 100644 platform/linux-generic/include/ishmphy_internal.h -- 2.7.4