| /* |
| Copyright (c) 2014 Intel Corporation. All Rights Reserved. |
| |
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the following conditions |
| are met: |
| |
| * Redistributions of source code must retain the above copyright |
| notice, this list of conditions and the following disclaimer. |
| * Redistributions in binary form must reproduce the above copyright |
| notice, this list of conditions and the following disclaimer in the |
| documentation and/or other materials provided with the distribution. |
| * Neither the name of Intel Corporation nor the names of its |
| contributors may be used to endorse or promote products derived |
| from this software without specific prior written permission. |
| |
| THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "coi_host.h" |
| |
| #include "coi_version_asm.h" |
| |
| #define CYCLE_FREQUENCY 1000000000 |
| |
| /* Environment variables. */ |
| extern char **environ; |
| |
| /* List of directories for removing on exit. */ |
| char **tmp_dirs; |
| unsigned tmp_dirs_num = 0; |
| |
| /* Number of KNC engines. */ |
| long knc_engines_num; |
| |
| /* Mutex to sync parallel execution. */ |
| pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; |
| |
| |
| typedef enum |
| { |
| BUFFER_NORMAL, |
| BUFFER_MEMORY |
| } buffer_t; |
| |
| typedef struct |
| { |
| COI_ISA_TYPE type; |
| uint32_t index; |
| char *dir; |
| } Engine; |
| |
| typedef struct |
| { |
| char *name; |
| void *ptr; |
| } Function; |
| |
| typedef struct |
| { |
| int pipe_host; |
| int pipe_target; |
| } Pipeline; |
| |
| typedef struct |
| { |
| pid_t pid; |
| Engine *engine; |
| Function **functions; |
| Pipeline *pipeline; |
| } Process; |
| |
| typedef struct |
| { |
| buffer_t type; |
| char *name; |
| int fd; |
| int fd_target; |
| uint64_t size; |
| void *data; |
| void *data_target; |
| Process *process; |
| } Buffer; |
| |
| |
| static COIRESULT |
| read_long_env (const char *env_name, long *var, long var_default) |
| { |
| char *str = getenv (env_name); |
| char *s; |
| |
| if (!str || *str == '\0') |
| *var = var_default; |
| else |
| { |
| errno = 0; |
| *var = strtol (str, &s, 0); |
| if (errno != 0 || s == str || *s != '\0') |
| COIERROR ("Variable %s has invalid value.", env_name); |
| } |
| |
| return COI_SUCCESS; |
| } |
| |
| __attribute__((constructor)) |
| static void |
| init () |
| { |
| if (read_long_env (OFFLOAD_EMUL_KNC_NUM_ENV, &knc_engines_num, 1) |
| == COI_ERROR) |
| exit (0); |
| } |
| |
| |
| /* Helper function for directory removing. */ |
| static COIRESULT remove_directory (char *path) |
| { |
| char *file; |
| struct dirent *entry; |
| struct stat statfile; |
| DIR *dir = opendir (path); |
| if (dir == NULL) |
| COIERROR ("Cannot open directory %s.", dir); |
| |
| while (entry = readdir (dir)) |
| { |
| if (!strcmp (entry->d_name, ".") || !strcmp (entry->d_name, "..")) |
| continue; |
| |
| MALLOC (char *, file, strlen (path) + strlen (entry->d_name) + 2); |
| sprintf (file, "%s/%s", path, entry->d_name); |
| |
| if (stat (file, &statfile) < 0) |
| COIERROR ("Cannot retrieve information about file %s.", file); |
| |
| if (S_ISDIR (statfile.st_mode)) |
| { |
| if (remove_directory (file) == COI_ERROR) |
| return COI_ERROR; |
| } |
| else |
| { |
| if (unlink (file) < 0) |
| COIERROR ("Cannot unlink file %s.", file); |
| } |
| |
| free (file); |
| } |
| |
| if (closedir (dir) < 0) |
| COIERROR ("Cannot close directory %s.", path); |
| if (rmdir (path) < 0) |
| COIERROR ("Cannot remove directory %s.", path); |
| |
| return COI_SUCCESS; |
| } |
| |
| __attribute__((destructor)) |
| static void |
| cleanup () |
| { |
| unsigned i; |
| for (i = 0; i < tmp_dirs_num; i++) |
| { |
| remove_directory (tmp_dirs[i]); |
| free (tmp_dirs[i]); |
| } |
| if (tmp_dirs) |
| free (tmp_dirs); |
| } |
| |
| |
| extern "C" |
| { |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferCopy, 1) (COIBUFFER dest_buffer, |
| COIBUFFER source_buffer, |
| uint64_t dest_offset, |
| uint64_t source_offset, |
| uint64_t length, |
| COI_COPY_TYPE type, |
| uint32_t dependencies_num, // Ignored |
| const COIEVENT *dependencies, // Ignored |
| COIEVENT *completion) // Ignored |
| { |
| COITRACE ("COIBufferCopy"); |
| |
| /* Convert input arguments. */ |
| Buffer *dest = (Buffer *) dest_buffer; |
| Buffer *source = (Buffer *) source_buffer; |
| |
| /* Features of liboffload. */ |
| assert (type == COI_COPY_UNSPECIFIED); |
| |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Map buffers if needed. */ |
| if (dest->data == 0 && dest->type == BUFFER_NORMAL) |
| if (COIBufferMap (dest_buffer, 0, dest->size, (COI_MAP_TYPE) 0, |
| 0, 0, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| if (source->data == 0 && source->type == BUFFER_NORMAL) |
| if (COIBufferMap (source_buffer, 0, source->size, (COI_MAP_TYPE) 0, |
| 0, 0, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Copy data. */ |
| if (source->data != 0 && dest->data != 0) |
| memcpy ((void *) ((uintptr_t) dest->data+dest_offset), |
| (void *) ((uintptr_t) source->data+source_offset), length); |
| else |
| { |
| assert (dest->process == source->process); |
| |
| Buffer *buffer; |
| cmd_t cmd = CMD_BUFFER_COPY; |
| Pipeline *pipeline = dest->process->pipeline; |
| |
| /* Create intermediary buffer. */ |
| if (COIBufferCreate (length, COI_BUFFER_NORMAL, 0, 0, 1, |
| (COIPROCESS*) &dest->process, |
| (COIBUFFER *) &buffer) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Copy from source to intermediary buffer. */ |
| if (source->data == 0) |
| { |
| assert (source->data_target != 0); |
| |
| /* Send data to target. */ |
| WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t)); |
| WRITE (pipeline->pipe_target, &(buffer->data_target), sizeof (void *)); |
| WRITE (pipeline->pipe_target, &(source->data_target), sizeof (void *)); |
| WRITE (pipeline->pipe_target, &(buffer->size), sizeof (uint64_t)); |
| |
| /* Receive data from target. */ |
| READ (pipeline->pipe_host, &cmd, sizeof (cmd_t)); |
| } |
| else |
| { |
| if (COIBufferCopy ((COIBUFFER) buffer, source_buffer, 0, source_offset, |
| length, type, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| } |
| |
| /* Copy from intermediary buffer to dest. */ |
| if (dest->data == 0) |
| { |
| assert (dest->data_target != 0); |
| |
| /* Send data to target. */ |
| WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t)); |
| WRITE (pipeline->pipe_target, &(dest->data_target), sizeof (void *)); |
| WRITE (pipeline->pipe_target, &(buffer->data_target), sizeof (void *)); |
| WRITE (pipeline->pipe_target, &(buffer->size), sizeof (uint64_t)); |
| |
| /* Receive data from target. */ |
| READ (pipeline->pipe_host, &cmd, sizeof (cmd_t)); |
| } |
| else |
| { |
| if (COIBufferCopy (dest_buffer, (COIBUFFER) buffer, dest_offset, |
| 0, length, type, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| } |
| |
| /* Unmap on target and destroy intermediary buffer. */ |
| if (COIBufferDestroy ((COIBUFFER) buffer) == COI_ERROR) |
| return COI_ERROR; |
| } |
| |
| /* Unmap buffers if needed. */ |
| if (dest->type == BUFFER_NORMAL) |
| if (COIBufferUnmap ((COIMAPINSTANCE) dest, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| if (source->type == BUFFER_NORMAL) |
| if (COIBufferUnmap ((COIMAPINSTANCE) source, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferCreate, 1) (uint64_t size, |
| COI_BUFFER_TYPE type, |
| uint32_t flags, |
| const void *init_data, |
| uint32_t processes_num, |
| const COIPROCESS *processes, |
| COIBUFFER *buffer) |
| { |
| COITRACE ("COIBufferCreate"); |
| |
| char *shm_name; |
| cmd_t cmd = CMD_BUFFER_MAP; |
| int shm_fd; |
| const int ullong_max_len = 20; |
| size_t len; |
| unsigned long long i; |
| |
| Buffer *buf; |
| Pipeline *pipeline; |
| |
| /* Features of liboffload. */ |
| assert (type == COI_BUFFER_NORMAL); |
| assert ((flags & COI_SINK_MEMORY) == 0); |
| assert ((flags & COI_SAME_ADDRESS_SINKS) == 0); |
| assert ((flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0); |
| assert (init_data == 0); |
| assert (processes_num == 1); |
| |
| /* Create shared memory with an unique name. */ |
| MALLOC (char *, shm_name, strlen (SHM_NAME) + ullong_max_len + 1); |
| for (i = 0; i >= 0; i++) |
| { |
| sprintf (shm_name, SHM_NAME"%lu", i); |
| shm_fd = shm_open (shm_name, O_CLOEXEC | O_CREAT | O_EXCL | O_RDWR, |
| S_IRUSR | S_IWUSR); |
| if (shm_fd > 0) |
| break; |
| } |
| if (ftruncate (shm_fd, size) < 0) |
| COIERROR ("Cannot truncate shared memory file."); |
| |
| /* Create buffer. */ |
| MALLOC (Buffer *, buf, sizeof (Buffer)); |
| buf->data = 0; |
| buf->fd = shm_fd; |
| buf->process = (Process *) processes[0]; |
| buf->size = size; |
| buf->type = BUFFER_NORMAL; |
| STRDUP (buf->name, shm_name); |
| |
| /* Map buffer on target. */ |
| len = strlen (buf->name) + 1; |
| pipeline = buf->process->pipeline; |
| |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Send data to target. */ |
| WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t)); |
| WRITE (pipeline->pipe_target, &len, sizeof (size_t)); |
| WRITE (pipeline->pipe_target, buf->name, len); |
| WRITE (pipeline->pipe_target, &(buf->size), sizeof (uint64_t)); |
| |
| /* Receive data from target. */ |
| READ (pipeline->pipe_host, &(buf->fd_target), sizeof (int)); |
| READ (pipeline->pipe_host, &(buf->data_target), sizeof (void *)); |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| |
| /* Prepare output arguments. */ |
| *buffer = (COIBUFFER) buf; |
| |
| /* Clean up. */ |
| free (shm_name); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferCreateFromMemory, 1) (uint64_t size, |
| COI_BUFFER_TYPE type, |
| uint32_t flags, |
| void *memory, |
| uint32_t processes_num, |
| const COIPROCESS *processes, |
| COIBUFFER *buffer) |
| { |
| COITRACE ("COIBufferCreateFromMemory"); |
| |
| Buffer *buf; |
| |
| /* Features of liboffload. */ |
| assert (type == COI_BUFFER_NORMAL); |
| assert ((flags & COI_SAME_ADDRESS_SINKS) == 0); |
| assert ((flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0); |
| assert (processes_num == 1); |
| |
| /* Create buffer. */ |
| MALLOC (Buffer *, buf, sizeof (Buffer)); |
| buf->data = (flags & COI_SINK_MEMORY) == 0 ? memory : 0; |
| buf->data_target = (flags & COI_SINK_MEMORY) != 0 ? memory : 0; |
| buf->process = (Process *) processes[0]; |
| buf->size = size; |
| buf->type = BUFFER_MEMORY; |
| |
| /* Prepare output argument. */ |
| *buffer = (COIBUFFER) buf; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferDestroy, 1) (COIBUFFER buffer) |
| { |
| COITRACE ("COIBufferDestroy"); |
| |
| cmd_t cmd = CMD_BUFFER_UNMAP; |
| |
| /* Convert input arguments. */ |
| Buffer *buf = (Buffer *) buffer; |
| Pipeline *pipeline = buf->process->pipeline; |
| |
| /* Unmap buffer on host. */ |
| if (buf->data != 0 && buf->type == BUFFER_NORMAL) |
| if (COIBufferUnmap ((COIMAPINSTANCE) buffer, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Unmap buffer on target. */ |
| if (buf->data_target != 0) |
| { |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Send data to target. */ |
| WRITE (pipeline->pipe_target, &cmd, sizeof (cmd_t)); |
| WRITE (pipeline->pipe_target, &(buf->fd_target), sizeof (int)); |
| WRITE (pipeline->pipe_target, &(buf->data_target), sizeof (void *)); |
| WRITE (pipeline->pipe_target, &(buf->size), sizeof (uint64_t)); |
| |
| /* Receive data from target. */ |
| READ (pipeline->pipe_host, &cmd, sizeof (cmd_t)); |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| } |
| |
| /* Unlink shared memory. */ |
| if (buf->type == BUFFER_NORMAL) |
| { |
| if (close (buf->fd) < 0) |
| COIERROR ("Cannot close shared memory file."); |
| if (shm_unlink (buf->name) < 0) |
| COIERROR ("Cannot unlink shared memory."); |
| free (buf->name); |
| } |
| |
| /* Clean up. */ |
| free (buf); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferGetSinkAddress, 1) (COIBUFFER buffer, |
| uint64_t *data) |
| { |
| COITRACE ("COIBufferGetSinkAddress"); |
| |
| /* Convert input arguments. */ |
| Buffer *buf = (Buffer *) buffer; |
| |
| /* Here should come BUFFER_NORMAL buffer. */ |
| assert (buf->type == BUFFER_NORMAL); |
| |
| /* Prepare output argument. */ |
| *data = (uint64_t) buf->data_target; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferMap, 1) (COIBUFFER buffer, |
| uint64_t offset, |
| uint64_t length, |
| COI_MAP_TYPE type, // Ignored |
| uint32_t dependencies_num, // Ignored |
| const COIEVENT *dependencies, // Ignored |
| COIEVENT *completion, // Ignored |
| COIMAPINSTANCE *map_instance, |
| void **data) |
| { |
| COITRACE ("COIBufferMap"); |
| |
| /* Features of liboffload. */ |
| assert (offset == 0); |
| |
| /* Convert input arguments. */ |
| Buffer *buf = (Buffer *) buffer; |
| |
| /* Only BUFFER_NORMAL buffers should come here. */ |
| assert (buf->type == BUFFER_NORMAL); |
| |
| /* Map shared memory. */ |
| buf->data = mmap (NULL, buf->size, PROT_READ | PROT_WRITE, |
| MAP_SHARED, buf->fd, 0); |
| if (buf->data == NULL) |
| COIERROR ("Cannot map shared memory."); |
| |
| /* Prepare output arguments. */ |
| if (map_instance != 0) |
| *map_instance = (COIMAPINSTANCE) buf; |
| if (data != 0) |
| *data = buf->data; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferRead, 1) (COIBUFFER buffer, |
| uint64_t offset, |
| void *data, |
| uint64_t length, |
| COI_COPY_TYPE type, |
| uint32_t dependencies_num, // Ignored |
| const COIEVENT *dependencies, // Ignored |
| COIEVENT *completion) // Ignored |
| { |
| COITRACE ("COIBufferRead"); |
| |
| /* Convert input arguments. */ |
| Buffer *buf = (Buffer *) buffer; |
| |
| /* Features of liboffload. */ |
| assert (type == COI_COPY_UNSPECIFIED); |
| |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Map buffers if needed. */ |
| if (buf->data == 0 && buf->type == BUFFER_NORMAL) |
| if (COIBufferMap (buffer, 0, buf->size, (COI_MAP_TYPE) 0, |
| 0, 0, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Copy data. */ |
| memcpy (data, (void *) ((uintptr_t) buf->data+offset), length); |
| |
| /* Unmap buffers if needed. */ |
| if (buf->type == BUFFER_NORMAL) |
| if (COIBufferUnmap ((COIMAPINSTANCE) buf, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferSetState, 1) (COIBUFFER buffer, |
| COIPROCESS process, |
| COI_BUFFER_STATE state, |
| COI_BUFFER_MOVE_FLAG flag, |
| uint32_t dependencies_num, // Ignored |
| const COIEVENT *dependencies, // Ignored |
| COIEVENT *completion) // Ignored |
| { |
| COITRACE ("COIBufferSetState"); |
| |
| /* Features of liboffload. */ |
| assert (flag == COI_BUFFER_NO_MOVE); |
| |
| /* Looks like we have nothing to do here. */ |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferUnmap, 1) (COIMAPINSTANCE map_instance, |
| uint32_t dependencies_num, // Ignored |
| const COIEVENT *dependencies, // Ignored |
| COIEVENT *completion) // Ignored |
| { |
| COITRACE ("COIBufferUnmap"); |
| |
| /* Convert input arguments. */ |
| Buffer *buffer = (Buffer *) map_instance; |
| |
| /* Only BUFFER_NORMAL buffers should come here. */ |
| assert (buffer->type == BUFFER_NORMAL); |
| |
| /* Unmap shared memory. */ |
| if (munmap (buffer->data, buffer->size) < 0) |
| COIERROR ("Cannot unmap shared memory."); |
| |
| buffer->data = 0; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIBufferWrite, 1) (COIBUFFER buffer, |
| uint64_t offset, |
| const void *data, |
| uint64_t length, |
| COI_COPY_TYPE type, |
| uint32_t dependencies_num, // Ignored |
| const COIEVENT *dependencies, // Ignored |
| COIEVENT *completion) // Ignored |
| { |
| COITRACE ("COIBufferWrite"); |
| |
| /* Convert input arguments. */ |
| Buffer *buf = (Buffer *) buffer; |
| |
| /* Features of liboffload. */ |
| assert (type == COI_COPY_UNSPECIFIED); |
| |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Map buffers if needed. */ |
| if (buf->data == 0 && buf->type == BUFFER_NORMAL) |
| if (COIBufferMap (buffer, 0, buf->size, (COI_MAP_TYPE) 0, |
| 0, 0, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Copy data. */ |
| memcpy ((void *) ((uintptr_t) buf->data+offset), data, length); |
| |
| /* Unmap buffers if needed. */ |
| if (buf->type == BUFFER_NORMAL) |
| if (COIBufferUnmap ((COIMAPINSTANCE) buf, 0, 0, 0) == COI_ERROR) |
| return COI_ERROR; |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIEngineGetCount, 1) (COI_ISA_TYPE isa, |
| uint32_t *count) |
| { |
| COITRACE ("COIEngineGetCount"); |
| |
| /* Features of liboffload. */ |
| assert (isa == COI_ISA_KNC); |
| |
| /* Prepare output arguments. */ |
| *count = knc_engines_num; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIEngineGetHandle, 1) (COI_ISA_TYPE isa, |
| uint32_t index, |
| COIENGINE *handle) |
| { |
| COITRACE ("COIEngineGetHandle"); |
| |
| Engine *engine; |
| |
| /* Features of liboffload. */ |
| assert (isa == COI_ISA_KNC); |
| |
| /* Check engine index. */ |
| if (index >= knc_engines_num) |
| COIERROR ("Wrong engine index."); |
| |
| /* Create engine handle. */ |
| MALLOC (Engine *, engine, sizeof (Engine)); |
| engine->dir = NULL; |
| engine->index = index; |
| engine->type = isa; |
| |
| /* Prepare output argument. */ |
| *handle = (COIENGINE) engine; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIEventWait, 1) (uint16_t events_num, // Ignored |
| const COIEVENT *events, // Ignored |
| int32_t timeout, // Ignored |
| uint8_t wait_all, |
| uint32_t *signaled_num, |
| uint32_t *signaled_indices) |
| { |
| COITRACE ("COIEventWait"); |
| |
| /* Features of liboffload. */ |
| assert (wait_all == 1); |
| assert (signaled_num == 0); |
| assert (signaled_indices == 0); |
| |
| /* Looks like we have nothing to do here. */ |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIPipelineCreate, 1) (COIPROCESS process, |
| COI_CPU_MASK mask, |
| uint32_t stack_size, // Ignored |
| COIPIPELINE *pipeline) |
| { |
| COITRACE ("COIPipelineCreate"); |
| |
| /* Features of liboffload. */ |
| assert (mask == 0); |
| |
| /* Prepare output arguments. */ |
| *pipeline = (COIPIPELINE) ((Process *) process)->pipeline; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIPipelineDestroy, 1) (COIPIPELINE pipeline) |
| { |
| COITRACE ("COIPipelineDestroy"); |
| |
| /* Do nothing here. Pipeline will be closed during COIProcessDestroy. */ |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIPipelineRunFunction, 1) (COIPIPELINE pipeline, |
| COIFUNCTION function, |
| uint32_t buffers_num, |
| const COIBUFFER *buffers, |
| const COI_ACCESS_FLAGS *access_flags, // Ignored |
| uint32_t dependencies_num, // Ignored |
| const COIEVENT *dependencies, // Ignored |
| const void *misc_data, |
| uint16_t misc_data_len, |
| void *return_data, |
| uint16_t return_data_len, |
| COIEVENT *completion) // Ignored |
| { |
| COITRACE ("COIPipelineRunFunction"); |
| |
| cmd_t cmd = CMD_RUN_FUNCTION; |
| int ret_len; |
| uint32_t i; |
| uint64_t size; |
| void *ptr; |
| |
| /* Convert input arguments. */ |
| Buffer **bufs = (Buffer **) buffers; |
| Function *func = (Function *) function; |
| Pipeline *pipe = (Pipeline *) pipeline; |
| |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Send data to target. */ |
| WRITE (pipe->pipe_target, &cmd, sizeof (cmd_t)); |
| WRITE (pipe->pipe_target, &(func->ptr), sizeof (void *)); |
| WRITE (pipe->pipe_target, &buffers_num, sizeof (uint32_t)); |
| for (i = 0; i < buffers_num; i++) |
| { |
| WRITE (pipe->pipe_target, &(bufs[i]->size), sizeof (uint64_t)); |
| WRITE (pipe->pipe_target, &(bufs[i]->data_target), sizeof (void *)); |
| } |
| WRITE (pipe->pipe_target, &misc_data_len, sizeof (uint16_t)); |
| if (misc_data_len > 0) |
| WRITE (pipe->pipe_target, misc_data, misc_data_len); |
| WRITE (pipe->pipe_target, &return_data_len, sizeof (uint16_t)); |
| |
| /* Receive data from target. In emulator we don't need any asynchronous data |
| transfer, so we wait for target process whether it has any data or not. */ |
| ret_len = read (pipe->pipe_host, return_data_len > 0 ? return_data : &cmd, |
| return_data_len > 0 ? return_data_len : sizeof (cmd_t)); |
| if (ret_len == 0) |
| return COI_PROCESS_DIED; |
| else if (ret_len != (return_data_len > 0 ? return_data_len : sizeof (cmd_t))) |
| COIERROR ("Cannot read from pipe."); |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIProcessCreateFromMemory, 1) (COIENGINE engine, |
| const char *bin_name, |
| const void *bin_buffer, |
| uint64_t bin_buffer_len, |
| int argc, |
| const char **argv, |
| uint8_t inherit_env, |
| const char **additional_env, |
| uint8_t proxy_active, // Ignored |
| const char *proxyfs_root, // Ignored |
| uint64_t buffer_space, // Ignored |
| const char *lib_search_path, |
| const char *file_of_origin, // Ignored |
| uint64_t file_of_origin_offset, // Ignored |
| COIPROCESS *process) |
| { |
| COITRACE ("COIProcessCreateFromMemory"); |
| |
| const int run_max_args_num = 128; |
| char **envp; |
| char *run_argv[run_max_args_num]; |
| char *emul_run = getenv (OFFLOAD_EMUL_RUN_ENV); |
| char *env_name, *tok; |
| char *pipe_host_path, *pipe_target_path, *pipes_path, *target_exe; |
| FILE *file; |
| int fd; |
| int i, j, env_i, env_num; |
| int pipe_host, pipe_target; |
| const int uint_max_len = 11; |
| pid_t pid; |
| Pipeline *pipeline; |
| Process *proc; |
| |
| /* Features of liboffload. */ |
| assert (argc == 0); |
| assert (argv == 0); |
| |
| /* Convert input arguments. */ |
| Engine *eng = (Engine *) engine; |
| |
| /* Create temporary directory for engine files. */ |
| assert (eng->dir == NULL); |
| STRDUP (eng->dir, ENGINE_PATH); |
| if (mkdtemp (eng->dir) == NULL) |
| COIERROR ("Cannot create temporary directory %s.", eng->dir); |
| |
| /* Save path to engine directory for clean up on exit. */ |
| tmp_dirs_num++; |
| tmp_dirs = (char **) realloc (tmp_dirs, tmp_dirs_num * sizeof (char *)); |
| if (!tmp_dirs) |
| COIERROR ("Cannot allocate memory."); |
| STRDUP (tmp_dirs[tmp_dirs_num - 1], eng->dir); |
| |
| /* Create target executable file. */ |
| MALLOC (char *, target_exe, strlen (eng->dir) + strlen (bin_name) + 2); |
| sprintf (target_exe, "%s/%s", eng->dir, bin_name); |
| fd = open (target_exe, O_CLOEXEC | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); |
| if (fd < 0) |
| COIERROR ("Cannot create file %s.", target_exe); |
| file = fdopen (fd, "wb"); |
| if (file == NULL) |
| COIERROR ("Cannot associate stream with file descriptor."); |
| if (fwrite (bin_buffer, 1, bin_buffer_len, file) != bin_buffer_len) |
| COIERROR ("Cannot write in file %s.", target_exe); |
| if (fclose (file) != 0) |
| COIERROR ("Cannot close file %s.", target_exe); |
| |
| /* Fix file permissions. */ |
| if (chmod (target_exe, S_IRWXU) < 0) |
| COIERROR ("Cannot change permissions for file %s.", target_exe); |
| |
| /* Create directory for pipes to prevent names collision. */ |
| MALLOC (char *, pipes_path, strlen (PIPES_PATH) + strlen (eng->dir) + 1); |
| sprintf (pipes_path, "%s"PIPES_PATH, eng->dir); |
| if (mkdir (pipes_path, S_IRWXU) < 0) |
| COIERROR ("Cannot create folder %s.", pipes_path); |
| |
| /* Create pipes. */ |
| MALLOC (char *, pipe_host_path, |
| strlen (PIPE_HOST_PATH) + strlen (eng->dir) + 1); |
| MALLOC (char *, pipe_target_path, |
| strlen (PIPE_TARGET_PATH) + strlen (eng->dir) + 1); |
| if (pipe_target_path == NULL) |
| COIERROR ("Cannot allocate memory."); |
| sprintf (pipe_host_path, "%s"PIPE_HOST_PATH, eng->dir); |
| sprintf (pipe_target_path, "%s"PIPE_TARGET_PATH, eng->dir); |
| if (mkfifo (pipe_host_path, S_IRUSR | S_IWUSR) < 0) |
| COIERROR ("Cannot create pipe %s.", pipe_host_path); |
| if (mkfifo (pipe_target_path, S_IRUSR | S_IWUSR) < 0) |
| COIERROR ("Cannot create pipe %s.", pipe_target_path); |
| |
| /* Prepare argv. */ |
| if (emul_run == NULL || strcmp (emul_run, "") == 0) |
| { |
| STRDUP (run_argv[0], target_exe); |
| run_argv[1] = (char *) NULL; |
| } |
| else |
| { |
| char *ptr, *tmp; |
| i = 0; |
| STRDUP (tmp, emul_run); |
| tok = strtok_r (tmp, " ", &ptr); |
| while (tok != NULL) |
| { |
| if (i >= run_max_args_num) |
| COIERROR ("Run command has too many arguments."); |
| STRDUP (run_argv[i++], tok); |
| tok = strtok_r (NULL, " ", &ptr); |
| } |
| STRDUP (run_argv[i], target_exe); |
| run_argv[i+1] = (char *) NULL; |
| free (tmp); |
| } |
| |
| /* Prepare envp. */ |
| /* FIXME: take into account additional_env. */ |
| assert (additional_env == NULL); |
| |
| env_num = 0; |
| if (inherit_env == true) |
| while (environ[env_num++]); |
| env_num += 4; // LD_LIBRARY_PATH, MIC_DIR, MIC_INDEX, NULL |
| |
| MALLOC (char **, envp, env_num * sizeof (char *)); |
| |
| env_i = 0; |
| if (inherit_env == true) |
| for (i = 0; environ[i] != NULL; i++) |
| { |
| STRDUP (env_name, environ[i]); |
| for (j = 0; env_name[j] != '=' && env_name[j] != '\0'; j++); |
| env_name[j] = '\0'; |
| if (strcmp (env_name, "LD_LIBRARY_PATH") != 0 |
| && strcmp (env_name, MIC_DIR_ENV) != 0 |
| && strcmp (env_name, MIC_INDEX_ENV) != 0) |
| STRDUP (envp[env_i++], environ[i]); |
| free (env_name); |
| } |
| |
| MALLOC (char *, envp[env_i], strlen (MIC_DIR_ENV) + strlen (eng->dir) + 2); |
| sprintf (envp[env_i], "%s=%s", MIC_DIR_ENV, eng->dir); |
| |
| MALLOC (char *, envp[env_i+1], strlen (MIC_INDEX_ENV) + uint_max_len + 1); |
| sprintf (envp[env_i+1], "%s=%u", MIC_INDEX_ENV, eng->index); |
| |
| MALLOC (char *, envp[env_i+2], |
| strlen ("LD_LIBRARY_PATH=") + strlen (lib_search_path) + 1); |
| sprintf (envp[env_i+2], "LD_LIBRARY_PATH=%s", lib_search_path); |
| |
| envp[env_i+3] = (char *) NULL; |
| |
| /* Create target process. */ |
| pid = vfork (); |
| if (pid < 0) |
| COIERROR ("Cannot create child process."); |
| |
| if (pid == 0) |
| { |
| /* Run target executable. */ |
| if (execvpe (run_argv[0], run_argv, envp) == -1) |
| COIERROR ("Cannot execute file %s.", target_exe); |
| } |
| |
| /* Open pipes. */ |
| pipe_host = open (pipe_host_path, O_CLOEXEC | O_RDONLY); |
| if (pipe_host < 0) |
| COIERROR ("Cannot open target-to-host pipe."); |
| pipe_target = open (pipe_target_path, O_CLOEXEC | O_WRONLY); |
| if (pipe_target < 0) |
| COIERROR ("Cannot open host-to-target pipe."); |
| |
| /* Create pipeline handle. */ |
| MALLOC (Pipeline *, pipeline, sizeof (Pipeline)); |
| pipeline->pipe_host = pipe_host; |
| pipeline->pipe_target = pipe_target; |
| |
| /* Create process handle. */ |
| MALLOC (Process *, proc, sizeof (Process)); |
| proc->pid = pid; |
| proc->engine = eng; |
| proc->functions = 0; |
| proc->pipeline = pipeline; |
| |
| /* Prepare output arguments. */ |
| *process = (COIPROCESS) proc; |
| |
| /* Clean up. */ |
| for (i = 0; run_argv[i] != NULL; i++) |
| free (run_argv[i]); |
| for (i = 0; envp[i] != NULL; i++) |
| free (envp[i]); |
| free (envp); |
| free (pipe_host_path); |
| free (pipe_target_path); |
| free (pipes_path); |
| free (target_exe); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIProcessDestroy, 1) (COIPROCESS process, |
| int32_t wait_timeout, // Ignored |
| uint8_t force, |
| int8_t *proc_return, |
| uint32_t *reason) |
| { |
| COITRACE ("COIProcessDestroy"); |
| |
| int i; |
| |
| /* Convert input arguments. */ |
| Process *proc = (Process *) process; |
| |
| /* Close pipeline. */ |
| if (close (proc->pipeline->pipe_host) < 0) |
| COIERROR ("Cannot close target-to-host pipe."); |
| if (close (proc->pipeline->pipe_target) < 0) |
| COIERROR ("Cannot close host-to-target pipe."); |
| free (proc->pipeline); |
| |
| /* Shutdown target process by force. */ |
| if (force) |
| kill (proc->pid, SIGTERM); |
| |
| /* Clean up. */ |
| for (i = 0; proc->functions[i] != 0; i++) |
| { |
| free (proc->functions[i]->name); |
| free (proc->functions[i]); |
| } |
| free (proc->engine->dir); |
| free (proc->engine); |
| free (proc->functions); |
| free (proc); |
| |
| /* Prepare output arguments. */ |
| *proc_return = 0; |
| *reason = 0; |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIProcessGetFunctionHandles, 1) (COIPROCESS process, |
| uint32_t functions_num, |
| const char **function_names, |
| COIFUNCTION *function_handles) |
| { |
| COITRACE ("COIProcessGetFunctionHandles"); |
| |
| cmd_t cmd = CMD_GET_FUNCTION_HANDLE; |
| Function *function; |
| size_t len; |
| void *ptr; |
| uint32_t i; |
| |
| /* Convert input arguments. */ |
| Process *proc = (Process *) process; |
| |
| /* This function should be called once for the process. */ |
| assert (proc->functions == 0); |
| |
| /* Create array of function pointers. Last element is 0, what shows |
| the end of the array. This array is used to free memory when process |
| is destroyed. */ |
| proc->functions = (Function **) calloc (functions_num + 1, |
| sizeof (Function *)); |
| if (proc->functions == NULL) |
| COIERROR ("Cannot allocate memory."); |
| |
| /* Get handles for functions. */ |
| for (i = 0; i < functions_num; i++) |
| { |
| MALLOC (Function *, function, sizeof (Function)); |
| |
| len = strlen (function_names[i]) + 1; |
| |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Send data to target. */ |
| WRITE (proc->pipeline->pipe_target, &cmd, sizeof (cmd_t)); |
| WRITE (proc->pipeline->pipe_target, &len, sizeof (size_t)); |
| WRITE (proc->pipeline->pipe_target, function_names[i], len); |
| |
| /* Receive data from target. */ |
| READ (proc->pipeline->pipe_host, &ptr, sizeof (void *)); |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| |
| /* Prepare output arguments. */ |
| STRDUP (function->name, function_names[i]); |
| if (function->name == NULL) |
| COIERROR ("Cannot allocate memory."); |
| function->ptr = ptr; |
| function_handles[i] = (COIFUNCTION) function; |
| |
| /* Save function pointer. */ |
| proc->functions[i] = function; |
| } |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIProcessLoadLibraryFromMemory, 2) (COIPROCESS process, |
| const void *lib_buffer, |
| uint64_t lib_buffer_len, |
| const char *lib_name, |
| const char *lib_search_path, |
| const char *file_of_origin, // Ignored |
| uint64_t file_from_origin_offset, // Ignored |
| uint32_t flags, // Ignored |
| COILIBRARY *library) // Ignored |
| { |
| COITRACE ("COIProcessLoadLibraryFromMemory"); |
| |
| char *lib_path; |
| cmd_t cmd = CMD_OPEN_LIBRARY; |
| int fd; |
| FILE *file; |
| size_t len; |
| |
| /* Convert input arguments. */ |
| Process *proc = (Process *) process; |
| |
| /* Create target library file. */ |
| MALLOC (char *, lib_path, |
| strlen (proc->engine->dir) + strlen (lib_name) + 2); |
| sprintf (lib_path, "%s/%s", proc->engine->dir, lib_name); |
| fd = open (lib_path, O_CLOEXEC | O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); |
| if (fd < 0) |
| COIERROR ("Cannot create file %s.", lib_path); |
| file = fdopen (fd, "wb"); |
| if (file == NULL) |
| COIERROR ("Cannot associate stream with file descriptor."); |
| if (fwrite (lib_buffer, 1, lib_buffer_len, file) != lib_buffer_len) |
| COIERROR ("Cannot write in file %s.", lib_path); |
| if (fclose (file) != 0) |
| COIERROR ("Cannot close file %s.", lib_path); |
| |
| len = strlen (lib_path) + 1; |
| |
| /* Start critical section. */ |
| if (pthread_mutex_lock (&mutex) != 0) |
| COIERROR ("Cannot lock mutex."); |
| |
| /* Make target open library. */ |
| WRITE (proc->pipeline->pipe_target, &cmd, sizeof (cmd_t)); |
| WRITE (proc->pipeline->pipe_target, &len, sizeof (size_t)); |
| WRITE (proc->pipeline->pipe_target, lib_path, len); |
| |
| /* Finish critical section. */ |
| if (pthread_mutex_unlock (&mutex) != 0) |
| COIERROR ("Cannot unlock mutex."); |
| |
| /* Clean up. */ |
| free (lib_path); |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| COIRESULT |
| SYMBOL_VERSION (COIProcessRegisterLibraries, 1) (uint32_t libraries_num, |
| const void **libraries, |
| const uint64_t *library_sizes, |
| const char **files_of_origin, |
| const uint64_t *file_of_origin_offsets) |
| { |
| COITRACE ("COIProcessRegisterLibraries"); |
| |
| /* Looks like we have nothing to do here. */ |
| |
| return COI_SUCCESS; |
| } |
| |
| |
| uint64_t |
| SYMBOL_VERSION (COIPerfGetCycleFrequency, 1) () |
| { |
| COITRACE ("COIPerfGetCycleFrequency"); |
| |
| return (uint64_t) CYCLE_FREQUENCY; |
| } |
| |
| } // extern "C" |
| |