/* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil -*- */
/*
 * Copyright (c) 2004-2007 The Trustees of Indiana University and Indiana
 *                         University Research and Technology
 *                         Corporation.  All rights reserved.
 * Copyright (c) 2004-2005 The University of Tennessee and The University
 *                         of Tennessee Research Foundation.  All rights
 *                         reserved.
 * Copyright (c) 2004-2005 High Performance Computing Center Stuttgart,
 *                         University of Stuttgart.  All rights reserved.
 * Copyright (c) 2004-2005 The Regents of the University of California.
 *                         All rights reserved.
 * Copyright (c) 2007-2011 Oracle and/or its affiliates.  All rights reserved.
 * Copyright (c) 2009-2011 Cisco Systems, Inc.  All rights reserved.
 * Copyright (c) 2015-2016 Los Alamos National Security, LLC.  All rights
 *                         reserved.
 * Copyright (c) 2016      Research Organization for Information Science
 *                         and Technology (RIST). All rights reserved.
 * $COPYRIGHT$
 *
 * Additional copyrights may follow
 *
 * $HEADER$
 */

#include "opal_config.h"
#include "opal/mca/memory/memory.h"
#include "opal/mca/memory/base/empty.h"
#include "opal/memoryhooks/memory_internal.h"
#include "opal/constants.h"

#include <sys/types.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <dlfcn.h>
#if defined(HAVE___MUNMAP)
/* here so we only include others if we absolutely have to */
#elif defined(HAVE_SYSCALL)
#include <sys/syscall.h>
#include <unistd.h>
#endif


#if defined(HAVE___MUNMAP)
int  __munmap(caddr_t addr, size_t len);
#endif

static int opal_memory_malloc_open(void);
static int opal_memory_malloc_query(int *);

const opal_memory_base_component_2_0_0_t mca_memory_malloc_solaris_component = {
    /* First, the mca_component_t struct containing meta information
       about the component itself */
    .memoryc_version = {
        OPAL_MEMORY_BASE_VERSION_2_0_0,

        /* Component name and version */
        .mca_component_name = "malloc_solaris",
        MCA_BASE_MAKE_VERSION(component, OPAL_MAJOR_VERSION, OPAL_MINOR_VERSION,
                              OPAL_RELEASE_VERSION),

        /* Component open and close functions */
        .mca_open_component = opal_memory_malloc_open,
    },
    .memoryc_data = {
        /* The component is checkpoint ready */
        MCA_BASE_METADATA_PARAM_CHECKPOINT
    },

    /* This component doesn't need these functions, but need to
       provide safe/empty register/deregister functions to call */
    .memoryc_query = opal_memory_malloc_query,
    .memoryc_register = opal_memory_base_component_register_empty,
    .memoryc_deregister = opal_memory_base_component_deregister_empty,
    .memoryc_set_alignment = opal_memory_base_component_set_alignment_empty,
};

/*
 * This component exists to establish the memory hook support
 * level available on Solaris. By default Solaris does not
 * return memory to the system, i.e. does not unmap memory
 * from the process space, when a user calls free(). This allows
 * us to declare OPAL_MEMORY_FREE_SUPPORT. Additionally, by
 * intercepting munmap we can declare OPAL_MEMORY_MUNMAP_SUPPORT.
 *
 * NOTE: Not releasing memory back to the system when calling
 * free() may be unique to Solaris which is why this component
 * was created.
 */
static int
opal_memory_malloc_open(void)
{
    opal_mem_hooks_set_support(
        (OPAL_MEMORY_FREE_SUPPORT | OPAL_MEMORY_MUNMAP_SUPPORT));
    return OPAL_SUCCESS;
}

static int opal_memory_malloc_query (int *priority)
{
    *priority = 79;
    return OPAL_SUCCESS;
}

/*
 * Three ways to call munmap.  Prefered is to call __munmap, which
 * will exist if munmap is a weak symbol.  If not available next try
 * the syscall, and if that doesn't work, try looking in the dynamic
 * libc.
 */
#if USE_SOLARIS_LEGACY_MUNMAP_PROTOTYPE
/* We are compiling using S10 so use its munmap prototype */
int
munmap(caddr_t addr, size_t len)
#else
/* From S11 on forward munmap's addr is void * */
int
munmap(void *addr, size_t len)
#endif
{
#if !defined(HAVE___MUNMAP) && \
    !defined(HAVE_SYSCALL) && defined(HAVE_DLSYM)
    static int (*realmunmap)(void*, size_t);
#endif

    opal_mem_hooks_release_hook(addr, len, 0);

#if defined(HAVE___MUNMAP)
    return __munmap(addr, len);
#elif defined(HAVE_SYSCALL)
    return syscall(SYS_munmap, addr, len);
#elif defined(HAVE_DLSYM)
    if (NULL == realmunmap) {
        union {
            int (*munmap_fp)(void*, size_t);
            void *munmap_p;
        } tmp;

        tmp.munmap_p = dlsym(RTLD_NEXT, "munmap");
        realmunmap = tmp.munmap_fp;
    }
    return realmunmap(addr, len);
#else
    #error "Can not determine how to call munmap"
#endif
}