diff options
Diffstat (limited to 'platform/linux-generic/test/validation/api/shmem/shmem_linux.c')
-rw-r--r-- | platform/linux-generic/test/validation/api/shmem/shmem_linux.c | 331 |
1 files changed, 331 insertions, 0 deletions
diff --git a/platform/linux-generic/test/validation/api/shmem/shmem_linux.c b/platform/linux-generic/test/validation/api/shmem/shmem_linux.c new file mode 100644 index 000000000..061d86d6e --- /dev/null +++ b/platform/linux-generic/test/validation/api/shmem/shmem_linux.c @@ -0,0 +1,331 @@ +/* SPDX-License-Identifier: BSD-3-Clause + * Copyright (c) 2016-2018 Linaro Limited + */ + +/* this test makes sure that odp shared memory created with the ODP_SHM_PROC + * flag is visible under linux, and checks that memory created with the + * ODP_SHM_EXPORT flag is visible by other ODP instances. + * It therefore checks both that the link + * name under /dev/shm is correct, and also checks that the memory contents + * is indeed shared. + * we want: + * -the odp test to run using C UNIT + * -the main process to return the correct return code. + * (for the autotools test harness) + * + * To achieve this, the flow of operations is as follows: + * + * linux process (main, non odp) | + * (shmem_linux.c) | + * | + * | + * | + * main() | + * forks odp_app1 process | + * wait for named pipe creation | + * | + * | ODP_APP1 process + * | (shmem_odp1.c) + * | + * | allocate shmem + * | populate shmem + * | create named pipe + * | wait for test report in fifo... + * read shared memory | + * check if memory contents is OK | + * If not OK, write "F" in fifo and | + * exit with failure code. | ------------------- + * | + * forks odp app2 process | ODP APP2 process + * wait for child termination & status| (shmem_odp2.c) + * | lookup ODP_APP1 shared memory, + * | check if memory contents is OK + * | Exit(0) on success, exit(1) on fail + * If child failed, write "F" in fifo | + * exit with failure code. | ------------------- + * | + * OK, write "S" in fifo, | + * wait for child termination & status| + * terminate with same status as child| + * | ODP APP1 process + * | (shmem_odp1.c) + * | + * | ...(continued) + * | read S(success) or F(fail) from fifo + * | report success or failure to C-Unit + * | Exit(0) on success, exit(1) on fail + * wait for child termination & status | + * terminate with same status as child | + * | + * \|/ + * time + */ + +#include <stdint.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <linux/limits.h> +#include <stdio.h> +#include <errno.h> +#include <sys/mman.h> +#include <libgen.h> +#include <linux/limits.h> +#include <inttypes.h> +#include <pwd.h> +#include <stdlib.h> +#include "shmem_linux.h" +#include "shmem_common.h" + +#define ODP_APP1_NAME "shmem_odp1" /* name of the odp1 program, in this dir */ +#define ODP_APP2_NAME "shmem_odp2" /* name of the odp2 program, in this dir */ +/* odp-<pid>-shm-<name> */ +#define DEVNAME_DEFAULT_DIR "/dev/shm" +#define DEVNAME_FMT "%s/%d/odp-%" PRIu64 "-shm-%s" +#define MAX_FIFO_WAIT 30 /* Max time waiting for the fifo (sec) */ + +/* + * read the attributes of an externally shared mem object: + * input: ext_odp_pid, blockname: the remote ODP instance and the exported + * block name to be searched. + * Output: filename: the memory block underlying file to be opened + * (the given buffer should be big enough i.e. at + * least ISHM_FILENAME_MAXLEN bytes) + * The 3 following parameters are really here for debug + * as they are really meaningless in a non-odp process: + * len: the block real length (bytes, multiple of page sz) + * flags: the _ishm flags setting the block was created with + * align: the alignment setting the block was created with + * + * return 0 on success, non zero on error + */ +static int read_shmem_attributes(uint64_t ext_odp_pid, const char *blockname, + char *filename, uint64_t *len, + uint32_t *flags, uint64_t *user_len, + uint32_t *user_flags, uint32_t *align, + uint64_t *offset) +{ + char shm_attr_filename[PATH_MAX]; + FILE *export_file; + char *shm_dir = getenv("ODP_SHM_DIR"); + + sprintf(shm_attr_filename, DEVNAME_FMT, + shm_dir ? shm_dir : DEVNAME_DEFAULT_DIR, + getuid(), + ext_odp_pid, blockname); + + /* O_CREAT flag not given => failure if shm_attr_filename does not + * already exist */ + export_file = fopen(shm_attr_filename, "r"); + if (export_file == NULL) + return -1; + + if (fscanf(export_file, "ODP exported shm block info: ") != 0) + goto export_file_read_err; + + if (fscanf(export_file, "ishm_blockname: %*s ") != 0) + goto export_file_read_err; + + if (fscanf(export_file, "file: %s ", filename) != 1) + goto export_file_read_err; + + if (fscanf(export_file, "length: %" PRIu64 " ", len) != 1) + goto export_file_read_err; + + if (fscanf(export_file, "flags: %" PRIu32 " ", flags) != 1) + goto export_file_read_err; + + if (fscanf(export_file, "user_length: %" PRIu64 " ", user_len) != 1) + goto export_file_read_err; + + if (fscanf(export_file, "user_flags: %" PRIu32 " ", user_flags) != 1) + goto export_file_read_err; + + if (fscanf(export_file, "align: %" PRIu32 " ", align) != 1) + goto export_file_read_err; + + if (fscanf(export_file, "offset: %" PRIu64 " ", offset) != 1) + goto export_file_read_err; + + fclose(export_file); + return 0; + +export_file_read_err: + fclose(export_file); + return -1; +} + +void test_success(char *fifo_name, int fd, pid_t odp_app) +{ + int status; + int nb_char; + char result = TEST_SUCCESS; + /* write "Success" to the FIFO */ + nb_char = write(fd, &result, sizeof(char)); + close(fd); + /* wait for the odp app1 to terminate */ + waitpid(odp_app, &status, 0); + /* if the write failed, report an error anyway */ + if (nb_char != 1) + status = 1; + unlink(fifo_name); + exit(status); /* the status reported by the odp side is returned */ +} + +void test_failure(char *fifo_name, int fd, pid_t odp_app) +{ + int status; + char result; + + int nb_char __attribute__((unused)); /*ignored: we fail anyway */ + + result = TEST_FAILURE; + /* write "Failure" to the FIFO */ + nb_char = write(fd, &result, sizeof(char)); + close(fd); + /* wait for the odp app1 to terminate */ + waitpid(odp_app, &status, 0); + unlink(fifo_name); + exit(1); /* error */ +} + +int main(int argc __attribute__((unused)), char *argv[]) +{ + char prg_name[PATH_MAX]; + char odp_name1[PATH_MAX]; + char odp_name2[PATH_MAX]; + int nb_sec; + int size; + pid_t odp_app1; + pid_t odp_app2; + char *odp_params1 = NULL; + char *odp_params2[3]; + char pid1[10]; + char fifo_name[PATH_MAX]; /* fifo for linux->odp feedback */ + int fifo_fd = -1; + char shm_filename[PATH_MAX];/* shared mem device name, under /dev/shm */ + uint64_t len; + uint64_t offset; + uint32_t flags; + uint64_t user_len; + uint32_t user_flags; + uint32_t align; + int shm_fd; + test_shared_linux_data_t *addr; + int app2_status; + uid_t uid = getuid(); + char *shm_dir = getenv("ODP_SHM_DIR"); + const char *exeext = getenv("EXEEXT"); + char *dir_name; + + if (exeext == NULL) + exeext = ""; + + strncpy(prg_name, argv[0], PATH_MAX - 1); + prg_name[PATH_MAX - 1] = 0; + dir_name = dirname(prg_name); + + /* odp_app1 is in the same directory as this file: */ + snprintf(odp_name1, sizeof(odp_name1), "%s/%s%s", dir_name, ODP_APP1_NAME, exeext); + + /* start the ODP application: */ + odp_app1 = fork(); + if (odp_app1 < 0) /* error */ + exit(1); + + if (odp_app1 == 0) { /* child */ + execv(odp_name1, &odp_params1); /* no return unless error */ + fprintf(stderr, "execv failed: %s\n", strerror(errno)); + } + + /* wait max 30 sec for the fifo to be created by the ODP side. + * Just die if time expire as there is no fifo to communicate + * through... */ + sprintf(fifo_name, FIFO_NAME_FMT, + shm_dir ? shm_dir : DEFAULT_SHM_DIR, + uid, odp_app1); + for (nb_sec = 0; nb_sec < MAX_FIFO_WAIT; nb_sec++) { + fifo_fd = open(fifo_name, O_WRONLY); + if (fifo_fd >= 0) + break; + sleep(1); + } + if (fifo_fd < 0) + exit(1); + printf("pipe found\n"); + + /* the linux named pipe has now been found, meaning that the + * ODP application is up and running, and has allocated shmem. + * check to see if linux can see the created shared memory: */ + + /* read the shared memory attributes (includes the shm filename): */ + if (read_shmem_attributes(odp_app1, SHM_NAME, + shm_filename, &len, &flags, + &user_len, &user_flags, &align, + &offset) != 0) { + printf("error read_shmem_attributes\n"); + test_failure(fifo_name, fifo_fd, odp_app1); + } + + /* open the shm filename (which is either on /dev/shm/ or on hugetlbfs) + * O_CREAT flag not given => failure if shm_devname does not already + * exist */ + shm_fd = open(shm_filename, O_RDONLY, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (shm_fd == -1) { + fprintf(stderr, "unable to open %s\n", shm_filename); + test_failure(fifo_name, fifo_fd, odp_app1); /* no return */ + } + + /* linux ODP guarantees page size alignment. Larger alignment may + * fail as 2 different processes will have fully unrelated + * virtual spaces. + */ + size = sizeof(test_shared_linux_data_t); + + addr = mmap(NULL, size, PROT_READ, MAP_SHARED, shm_fd, offset); + if (addr == MAP_FAILED) { + fprintf(stderr, "shmem_linux: mmap failed: %s\n", + strerror(errno)); + test_failure(fifo_name, fifo_fd, odp_app1); + } + + /* check that we see what the ODP application wrote in the memory */ + if ((addr->foo != TEST_SHARE_FOO) || (addr->bar != TEST_SHARE_BAR)) { + fprintf(stderr, "ERROR: addr->foo %x addr->bar %x\n", + addr->foo, addr->bar); + test_failure(fifo_name, fifo_fd, odp_app1); /* no return */ + } + + /* odp_app2 is in the same directory as this file: */ + snprintf(odp_name2, sizeof(odp_name2), "%s/%s%s", dir_name, ODP_APP2_NAME, exeext); + + /* start the second ODP application with pid of ODP_APP1 as parameter:*/ + sprintf(pid1, "%d", odp_app1); + odp_params2[0] = odp_name2; + odp_params2[1] = pid1; + odp_params2[2] = NULL; + odp_app2 = fork(); + if (odp_app2 < 0) /* error */ + exit(1); + + if (odp_app2 == 0) { /* child */ + execv(odp_name2, odp_params2); /* no return unless error */ + fprintf(stderr, "execv failed: %s\n", strerror(errno)); + } + + /* wait for the second ODP application to terminate: + * status is OK if that second ODP application could see the + * memory shared by the first one. */ + waitpid(odp_app2, &app2_status, 0); + + if (app2_status) + test_failure(fifo_name, fifo_fd, odp_app1); /* no return */ + + /* everything looked good: */ + test_success(fifo_name, fifo_fd, odp_app1); +} |