#include "src/util/pmix_shmem.h"
#include "pmix_common.h"
#include "pmix_output.h"
#include "src/include/pmix_globals.h"
#include "src/mca/gds/base/base.h"
#include "src/util/pmix_error.h"
#include "src/util/pmix_show_help.h"
#include "src/util/pmix_string_copy.h"
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include <sys/mman.h>
typedef struct pmix_shmem_header_t {
pmix_atomic_int32_t ref_count;
} pmix_shmem_header_t;
static void *
data_addr_from_base(
void *base_addr
) {
const size_t header_offset = pmix_shmem_utils_pad_to_page(
sizeof(pmix_shmem_header_t)
);
return (void *)((uintptr_t)base_addr + header_offset);
}
static inline void
inc_ref_count(
pmix_shmem_header_t *header
) {
(void)pmix_atomic_fetch_add_32(&header->ref_count, 1);
}
static inline bool
dec_ref_count(
pmix_shmem_header_t *header
) {
return pmix_atomic_sub_fetch_32(&header->ref_count, 1) == 0;
}
static pmix_status_t
segment_attach(
pmix_shmem_t *shmem,
uintptr_t desired_base_address,
pmix_shmem_flags_t flags
) {
pmix_status_t rc = PMIX_SUCCESS;
void *mmap_addr = MAP_FAILED;
const int fd = open(shmem->backing_path, O_RDWR);
if (fd == -1) {
rc = PMIX_ERR_FILE_OPEN_FAILURE;
if (0 < pmix_output_get_verbosity(pmix_gds_base_framework.framework_output)) {
pmix_show_help("help-pmix-util.txt", "failed-file-open", true,
shmem->backing_path, strerror(errno));
}
goto out;
}
mmap_addr = mmap(
(void *)desired_base_address, shmem->size,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0
);
if (MAP_FAILED == mmap_addr) {
rc = PMIX_ERR_NOMEM;
goto out;
}
if ((flags & PMIX_SHMEM_MUST_MAP_AT_RADDR) &&
desired_base_address != (uintptr_t)NULL) {
if (desired_base_address != (uintptr_t)mmap_addr) {
rc = PMIX_ERR_NOT_AVAILABLE;
goto out;
}
}
out:
if (-1 != fd) {
(void)close(fd);
}
if (PMIX_SUCCESS != rc) {
(void)pmix_shmem_segment_detach(shmem);
}
else {
shmem->attached = true;
}
shmem->hdr_address = mmap_addr;
shmem->data_address = data_addr_from_base(mmap_addr);
return rc;
}
static pmix_status_t
add_internal_segment_header(
pmix_shmem_t *shmem
) {
int rc = PMIX_SUCCESS;
rc = segment_attach(shmem, (uintptr_t)NULL, 0);
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
return rc;
}
pmix_shmem_header_t shmem_header = {
.ref_count = 0
};
memmove(shmem->hdr_address, &shmem_header, sizeof(shmem_header));
return pmix_shmem_segment_detach(shmem);
}
pmix_status_t
pmix_shmem_segment_create(
pmix_shmem_t *shmem,
size_t size,
const char *backing_path
) {
int rc = PMIX_SUCCESS;
const size_t real_size = pmix_shmem_utils_pad_to_page(
size + sizeof(pmix_shmem_header_t)
);
const int fd = open(backing_path, O_CREAT | O_RDWR, 0600);
if (fd == -1) {
rc = PMIX_ERR_FILE_OPEN_FAILURE;
goto out;
}
if (0 != ftruncate(fd, real_size)) {
rc = PMIX_ERROR;
goto out;
}
shmem->size = real_size;
pmix_string_copy(shmem->backing_path, backing_path, PMIX_PATH_MAX);
rc = add_internal_segment_header(shmem);
out:
if (-1 != fd) {
(void)close(fd);
}
if (PMIX_SUCCESS != rc) {
PMIX_ERROR_LOG(rc);
}
return rc;
}
pmix_status_t
pmix_shmem_segment_attach(
pmix_shmem_t *shmem,
uintptr_t desired_base_address,
pmix_shmem_flags_t flags
) {
const pmix_status_t rc = segment_attach(
shmem, desired_base_address, flags
);
if (PMIX_SUCCESS == rc) {
inc_ref_count(shmem->hdr_address);
}
return rc;
}
pmix_status_t
pmix_shmem_segment_detach(
pmix_shmem_t *shmem
) {
int rc = 0;
if (shmem && shmem->attached) {
rc = munmap(shmem->hdr_address, shmem->size);
shmem->attached = false;
shmem->hdr_address = NULL;
shmem->data_address = NULL;
}
return (0 == rc) ? PMIX_SUCCESS : PMIX_ERROR;
}
pmix_status_t
pmix_shmem_segment_chown(
pmix_shmem_t *shmem,
uid_t owner,
gid_t group
) {
pmix_status_t rc = PMIX_SUCCESS;
if (lchown(shmem->backing_path, owner, group) != 0) { rc = PMIX_ERROR;
PMIX_ERROR_LOG(rc);
}
return rc;
}
pmix_status_t
pmix_shmem_segment_chmod(
pmix_shmem_t *shmem,
mode_t mode
) {
pmix_status_t rc = PMIX_SUCCESS;
if (chmod(shmem->backing_path, mode) != 0) {
rc = PMIX_ERROR;
PMIX_ERROR_LOG(rc);
}
return rc;
}
pmix_status_t
pmix_shmem_segment_unlink(
pmix_shmem_t *shmem
) {
const int rc = unlink(shmem->backing_path);
memset(shmem->backing_path, 0, PMIX_PATH_MAX);
return (0 == rc) ? PMIX_SUCCESS : PMIX_ERROR;
}
static size_t
get_page_size(void)
{
const long i = sysconf(_SC_PAGE_SIZE);
if (-1 == i) {
PMIX_ERROR_LOG(PMIX_ERROR);
return 0;
}
return i;
}
size_t
pmix_shmem_utils_pad_to_page(
size_t size
) {
const size_t page_size = get_page_size();
const size_t pad = ((~size) + page_size + 1) & (page_size - 1);
return size + pad;
}
static void
shmem_construct(
pmix_shmem_t *s
) {
s->attached = false;
s->size = 0;
s->hdr_address = NULL;
s->data_address = NULL;
memset(s->backing_path, 0, PMIX_PATH_MAX);
}
static void
shmem_destruct(
pmix_shmem_t *s
) {
if (!s->attached) {
return;
}
if (dec_ref_count(s->hdr_address)) {
(void)pmix_shmem_segment_unlink(s);
}
(void)pmix_shmem_segment_detach(s);
}
PMIX_EXPORT PMIX_CLASS_INSTANCE(
pmix_shmem_t,
pmix_object_t,
shmem_construct,
shmem_destruct
);