| /* | |
| Copyright (c) 2014-2016 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 <set> | |
| #include <map> | |
| #include <queue> | |
| #include "coi_host.h" | |
| #include "coi_version_asm.h" | |
| #define CYCLE_FREQUENCY 1000000000 | |
| enum buffer_t | |
| { | |
| BUFFER_NORMAL, | |
| BUFFER_MEMORY | |
| }; | |
| struct Engine | |
| { | |
| COI_ISA_TYPE type; | |
| uint32_t index; | |
| char *dir; | |
| }; | |
| struct Function | |
| { | |
| void *ptr; | |
| uint32_t num_buffers; | |
| uint64_t *bufs_size; | |
| void * *bufs_data_target; | |
| uint16_t misc_data_len; | |
| void *misc_data; | |
| uint16_t return_value_len; | |
| void *return_value; | |
| COIEVENT completion_event; | |
| }; | |
| struct Callback | |
| { | |
| COI_EVENT_CALLBACK ptr; | |
| const void *data; | |
| }; | |
| struct Process | |
| { | |
| pid_t pid; | |
| int pipe_host2tgt; | |
| int pipe_tgt2host; | |
| Engine *engine; | |
| void **functions; | |
| }; | |
| struct Pipeline | |
| { | |
| pthread_t thread; | |
| bool destroy; | |
| bool is_destroyed; | |
| char *pipe_host2tgt_path; | |
| char *pipe_tgt2host_path; | |
| int pipe_host2tgt; | |
| int pipe_tgt2host; | |
| std::queue<Function> queue; | |
| Process *process; | |
| }; | |
| struct Buffer | |
| { | |
| buffer_t type; | |
| char *name; | |
| int fd; | |
| int fd_target; | |
| uint64_t size; | |
| void *data; | |
| void *data_target; | |
| Process *process; | |
| }; | |
| /* Environment variables. */ | |
| extern char **environ; | |
| /* List of directories for removing on exit. */ | |
| static char **tmp_dirs; | |
| static unsigned tmp_dirs_num; | |
| /* Number of emulated MIC engines. */ | |
| static long num_engines; | |
| /* Number of the last COI pipeline. */ | |
| static uint32_t max_pipeline_num; | |
| /* Set of undestroyed pipelines. */ | |
| static std::set<Pipeline *> pipelines; | |
| /* Number of the last COI event, the event #0 is always signalled. */ | |
| static uint64_t max_event_num = 1; | |
| /* Set of created COI events, which are not signalled. */ | |
| static std::set<uint64_t> non_signalled_events; | |
| /* Set of COI events, which encountered errors. */ | |
| static std::map<uint64_t, COIRESULT> errored_events; | |
| /* Set of registered callbacks, indexed by event number. */ | |
| static std::map<uint64_t, Callback> callbacks; | |
| /* Mutex to sync parallel execution. */ | |
| static pthread_mutex_t mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; | |
| 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_NUM_ENV, &num_engines, 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 () | |
| { | |
| for (unsigned i = 0; i < tmp_dirs_num; i++) | |
| { | |
| remove_directory (tmp_dirs[i]); | |
| free (tmp_dirs[i]); | |
| } | |
| free (tmp_dirs); | |
| } | |
| static COIRESULT | |
| start_critical_section () | |
| { | |
| if (pthread_mutex_lock (&mutex) != 0) | |
| COIERROR ("Cannot lock mutex."); | |
| return COI_SUCCESS; | |
| } | |
| static COIRESULT | |
| finish_critical_section () | |
| { | |
| if (pthread_mutex_unlock (&mutex) != 0) | |
| COIERROR ("Cannot unlock mutex."); | |
| return COI_SUCCESS; | |
| } | |
| static bool | |
| pipeline_is_destroyed (const Pipeline *pipeline) | |
| { | |
| start_critical_section (); | |
| bool res = pipeline->is_destroyed; | |
| finish_critical_section (); | |
| return res; | |
| } | |
| static void | |
| maybe_invoke_callback (const COIEVENT event, const COIRESULT result) | |
| { | |
| std::map<uint64_t, Callback>::iterator cb = callbacks.find (event.opaque[0]); | |
| if (cb != callbacks.end ()) | |
| { | |
| Callback callback = cb->second; | |
| callback.ptr (event, result, callback.data); | |
| callbacks.erase (cb); | |
| } | |
| } | |
| static void | |
| signal_event (const COIEVENT event, const COIRESULT result) | |
| { | |
| if (result != COI_SUCCESS) | |
| errored_events.insert (std::pair <uint64_t, COIRESULT> (event.opaque[0], | |
| result)); | |
| non_signalled_events.erase (event.opaque[0]); | |
| maybe_invoke_callback (event, result); | |
| } | |
| static COIRESULT | |
| get_event_result (const COIEVENT event) | |
| { | |
| COIRESULT res = COI_SUCCESS; | |
| std::map<uint64_t, COIRESULT>::iterator ee | |
| = errored_events.find (event.opaque[0]); | |
| if (ee != errored_events.end ()) | |
| res = ee->second; | |
| return res; | |
| } | |
| extern "C" | |
| { | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferCopy, 1) (COIBUFFER in_DestBuffer, | |
| COIBUFFER in_SourceBuffer, | |
| uint64_t in_DestOffset, | |
| uint64_t in_SourceOffset, | |
| uint64_t in_Length, | |
| COI_COPY_TYPE in_Type, | |
| uint32_t in_NumDependencies, | |
| const COIEVENT *in_pDependencies, // Ignored | |
| COIEVENT *out_pCompletion) | |
| { | |
| COITRACE ("COIBufferCopy"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_DestBuffer != NULL); | |
| assert (in_SourceBuffer != NULL); | |
| assert (in_Type == COI_COPY_UNSPECIFIED); | |
| assert (in_NumDependencies == 0); | |
| /* Convert input arguments. */ | |
| Buffer *dest = (Buffer *) in_DestBuffer; | |
| Buffer *source = (Buffer *) in_SourceBuffer; | |
| start_critical_section (); | |
| /* Map buffers if needed. */ | |
| if (dest->data == 0 && dest->type == BUFFER_NORMAL) | |
| if (COIBufferMap (in_DestBuffer, 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 (in_SourceBuffer, 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 + in_DestOffset), | |
| (void *) ((uintptr_t) source->data + in_SourceOffset), in_Length); | |
| else | |
| { | |
| assert (dest->process == source->process); | |
| Buffer *buffer; | |
| cmd_t cmd = CMD_BUFFER_COPY; | |
| /* Create intermediary buffer. */ | |
| if (COIBufferCreate (in_Length, COI_BUFFER_NORMAL, 0, 0, 1, | |
| (COIPROCESS*) &dest->process, | |
| (COIBUFFER *) &buffer) == COI_ERROR) | |
| return COI_ERROR; | |
| int pipe_host2tgt = dest->process->pipe_host2tgt; | |
| int pipe_tgt2host = dest->process->pipe_tgt2host; | |
| /* Copy from source to intermediary buffer. */ | |
| if (source->data == 0) | |
| { | |
| assert (source->data_target != 0); | |
| /* Send data to target. */ | |
| WRITE (pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (pipe_host2tgt, &buffer->data_target, sizeof (void *)); | |
| WRITE (pipe_host2tgt, &source->data_target, sizeof (void *)); | |
| WRITE (pipe_host2tgt, &buffer->size, sizeof (uint64_t)); | |
| /* Receive data from target. */ | |
| READ (pipe_tgt2host, &cmd, sizeof (cmd_t)); | |
| } | |
| else | |
| { | |
| if (COIBufferCopy ((COIBUFFER) buffer, in_SourceBuffer, 0, | |
| in_SourceOffset, in_Length, in_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 (pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (pipe_host2tgt, &dest->data_target, sizeof (void *)); | |
| WRITE (pipe_host2tgt, &buffer->data_target, sizeof (void *)); | |
| WRITE (pipe_host2tgt, &buffer->size, sizeof (uint64_t)); | |
| /* Receive data from target. */ | |
| READ (pipe_tgt2host, &cmd, sizeof (cmd_t)); | |
| } | |
| else | |
| { | |
| if (COIBufferCopy (in_DestBuffer, (COIBUFFER) buffer, in_DestOffset, | |
| 0, in_Length, in_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 (out_pCompletion) | |
| out_pCompletion->opaque[0] = 0; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferCreate, 1) (uint64_t in_Size, | |
| COI_BUFFER_TYPE in_Type, | |
| uint32_t in_Flags, | |
| const void *in_pInitData, | |
| uint32_t in_NumProcesses, | |
| const COIPROCESS *in_pProcesses, | |
| COIBUFFER *out_pBuffer) | |
| { | |
| COITRACE ("COIBufferCreate"); | |
| char *shm_name; | |
| int shm_fd; | |
| const int ullong_max_len = 20; | |
| /* Features of liboffloadmic. */ | |
| assert (in_Type == COI_BUFFER_NORMAL || in_Type == COI_BUFFER_OPENCL); | |
| assert ((in_Flags & COI_SINK_MEMORY) == 0); | |
| assert ((in_Flags & COI_SAME_ADDRESS_SINKS) == 0); | |
| assert ((in_Flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0); | |
| assert (in_pInitData == NULL); | |
| assert (in_NumProcesses == 1); | |
| assert (in_pProcesses != NULL); | |
| assert (out_pBuffer != NULL); | |
| /* Create shared memory with an unique name. */ | |
| MALLOC (char *, shm_name, strlen (SHM_NAME) + ullong_max_len + 1); | |
| for (unsigned long long 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, in_Size) < 0) | |
| COIERROR ("Cannot truncate shared memory file."); | |
| /* Create buffer. */ | |
| Buffer *buf = new Buffer; | |
| buf->data = 0; | |
| buf->fd = shm_fd; | |
| buf->process = (Process *) in_pProcesses[0]; | |
| buf->size = in_Size; | |
| buf->type = BUFFER_NORMAL; | |
| STRDUP (buf->name, shm_name); | |
| /* Map buffer on target. */ | |
| size_t len = strlen (buf->name) + 1; | |
| start_critical_section (); | |
| /* Send data to target. */ | |
| const cmd_t cmd = CMD_BUFFER_MAP; | |
| int pipe_host2tgt = buf->process->pipe_host2tgt; | |
| WRITE (pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (pipe_host2tgt, &len, sizeof (size_t)); | |
| WRITE (pipe_host2tgt, buf->name, len); | |
| WRITE (pipe_host2tgt, &buf->size, sizeof (uint64_t)); | |
| /* Receive data from target. */ | |
| int pipe_tgt2host = buf->process->pipe_tgt2host; | |
| READ (pipe_tgt2host, &buf->fd_target, sizeof (int)); | |
| READ (pipe_tgt2host, &buf->data_target, sizeof (void *)); | |
| finish_critical_section (); | |
| /* Prepare output arguments. */ | |
| *out_pBuffer = (COIBUFFER) buf; | |
| /* Clean up. */ | |
| free (shm_name); | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferCreateFromMemory, 1) (uint64_t in_Size, | |
| COI_BUFFER_TYPE in_Type, | |
| uint32_t in_Flags, | |
| void *in_Memory, | |
| uint32_t in_NumProcesses, | |
| const COIPROCESS *in_pProcesses, | |
| COIBUFFER *out_pBuffer) | |
| { | |
| COITRACE ("COIBufferCreateFromMemory"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_Type == COI_BUFFER_NORMAL); | |
| assert ((in_Flags & COI_SAME_ADDRESS_SINKS) == 0); | |
| assert ((in_Flags & COI_SAME_ADDRESS_SINKS_AND_SOURCE) == 0); | |
| assert (in_NumProcesses == 1); | |
| assert (in_pProcesses != NULL); | |
| assert (out_pBuffer != NULL); | |
| /* Create buffer. */ | |
| Buffer *buf = new Buffer; | |
| buf->data = (in_Flags & COI_SINK_MEMORY) == 0 ? in_Memory : 0; | |
| buf->data_target = (in_Flags & COI_SINK_MEMORY) != 0 ? in_Memory : 0; | |
| buf->process = (Process *) in_pProcesses[0]; | |
| buf->size = in_Size; | |
| buf->type = BUFFER_MEMORY; | |
| /* Prepare output argument. */ | |
| *out_pBuffer = (COIBUFFER) buf; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferDestroy, 1) (COIBUFFER in_Buffer) | |
| { | |
| COITRACE ("COIBufferDestroy"); | |
| cmd_t cmd = CMD_BUFFER_UNMAP; | |
| assert (in_Buffer != NULL); | |
| /* Convert input arguments. */ | |
| Buffer *buf = (Buffer *) in_Buffer; | |
| /* Unmap buffer on host. */ | |
| if (buf->data != 0 && buf->type == BUFFER_NORMAL) | |
| if (COIBufferUnmap ((COIMAPINSTANCE) in_Buffer, 0, 0, 0) == COI_ERROR) | |
| return COI_ERROR; | |
| /* Unmap buffer on target. */ | |
| if (buf->data_target != 0) | |
| { | |
| start_critical_section (); | |
| /* Send data to target. */ | |
| int pipe_host2tgt = buf->process->pipe_host2tgt; | |
| WRITE (pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (pipe_host2tgt, &buf->fd_target, sizeof (int)); | |
| WRITE (pipe_host2tgt, &buf->data_target, sizeof (void *)); | |
| WRITE (pipe_host2tgt, &buf->size, sizeof (uint64_t)); | |
| /* Receive data from target. */ | |
| READ (buf->process->pipe_tgt2host, &cmd, sizeof (cmd_t)); | |
| finish_critical_section (); | |
| } | |
| /* 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. */ | |
| delete buf; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferGetSinkAddress, 1) (COIBUFFER in_Buffer, | |
| uint64_t *out_pAddress) | |
| { | |
| COITRACE ("COIBufferGetSinkAddress"); | |
| assert (in_Buffer != NULL); | |
| assert (out_pAddress != NULL); | |
| /* Convert input arguments. */ | |
| Buffer *buf = (Buffer *) in_Buffer; | |
| /* Here should come BUFFER_NORMAL buffer. */ | |
| assert (buf->type == BUFFER_NORMAL); | |
| /* Prepare output argument. */ | |
| *out_pAddress = (uint64_t) buf->data_target; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferMap, 1) (COIBUFFER in_Buffer, | |
| uint64_t in_Offset, | |
| uint64_t in_Length, // Ignored | |
| COI_MAP_TYPE in_Type, // Ignored | |
| uint32_t in_NumDependencies, | |
| const COIEVENT *in_pDependencies, // Ignored | |
| COIEVENT *out_pCompletion, | |
| COIMAPINSTANCE *out_pMapInstance, | |
| void **out_ppData) | |
| { | |
| COITRACE ("COIBufferMap"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_Offset == 0); | |
| assert (in_NumDependencies == 0); | |
| /* Convert input arguments. */ | |
| Buffer *buf = (Buffer *) in_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 (out_pMapInstance != 0) | |
| *out_pMapInstance = (COIMAPINSTANCE) buf; | |
| if (out_ppData != 0) | |
| *out_ppData = buf->data; | |
| if (out_pCompletion) | |
| out_pCompletion->opaque[0] = 0; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferRead, 1) (COIBUFFER in_SourceBuffer, | |
| uint64_t in_Offset, | |
| void *in_pDestData, | |
| uint64_t in_Length, | |
| COI_COPY_TYPE in_Type, | |
| uint32_t in_NumDependencies, | |
| const COIEVENT *in_pDependencies, // Ignored | |
| COIEVENT *out_pCompletion) | |
| { | |
| COITRACE ("COIBufferRead"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_pDestData != NULL); | |
| assert (in_Type == COI_COPY_UNSPECIFIED); | |
| assert (in_NumDependencies == 0); | |
| /* Convert input arguments. */ | |
| Buffer *buf = (Buffer *) in_SourceBuffer; | |
| start_critical_section (); | |
| /* Map buffers if needed. */ | |
| if (buf->data == 0 && buf->type == BUFFER_NORMAL) | |
| if (COIBufferMap (in_SourceBuffer, 0, buf->size, (COI_MAP_TYPE) 0, 0, 0, 0, | |
| 0, 0) == COI_ERROR) | |
| return COI_ERROR; | |
| /* Copy data. */ | |
| memcpy (in_pDestData, (void *) ((uintptr_t) buf->data + in_Offset), | |
| in_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 (out_pCompletion) | |
| out_pCompletion->opaque[0] = 0; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferSetState, 1) (COIBUFFER in_Buffer, // Ignored | |
| COIPROCESS in_Process, // Ignored | |
| COI_BUFFER_STATE in_State, // Ignored | |
| COI_BUFFER_MOVE_FLAG in_DataMove, | |
| uint32_t in_NumDependencies, | |
| const COIEVENT *in_pDependencies, // Ignored | |
| COIEVENT *out_pCompletion) | |
| { | |
| COITRACE ("COIBufferSetState"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_DataMove == COI_BUFFER_NO_MOVE); | |
| assert (in_NumDependencies == 0); | |
| /* Looks like we have nothing to do here. */ | |
| if (out_pCompletion) | |
| out_pCompletion->opaque[0] = 0; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferUnmap, 1) (COIMAPINSTANCE in_MapInstance, | |
| uint32_t in_NumDependencies, | |
| const COIEVENT *in_pDependencies, // Ignored | |
| COIEVENT *out_pCompletion) | |
| { | |
| COITRACE ("COIBufferUnmap"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_MapInstance != NULL); | |
| assert (in_NumDependencies == 0); | |
| /* Convert input arguments. */ | |
| Buffer *buffer = (Buffer *) in_MapInstance; | |
| /* 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; | |
| if (out_pCompletion) | |
| out_pCompletion->opaque[0] = 0; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIBufferWrite, 1) (COIBUFFER in_DestBuffer, | |
| uint64_t in_Offset, | |
| const void *in_pSourceData, | |
| uint64_t in_Length, | |
| COI_COPY_TYPE in_Type, | |
| uint32_t in_NumDependencies, | |
| const COIEVENT *in_pDependencies, // Ignored | |
| COIEVENT *out_pCompletion) | |
| { | |
| COITRACE ("COIBufferWrite"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_DestBuffer != NULL); | |
| assert (in_pSourceData != NULL); | |
| assert (in_Type == COI_COPY_UNSPECIFIED); | |
| assert (in_NumDependencies == 0); | |
| /* Convert input arguments. */ | |
| Buffer *buf = (Buffer *) in_DestBuffer; | |
| start_critical_section (); | |
| /* Map buffers if needed. */ | |
| if (buf->data == 0 && buf->type == BUFFER_NORMAL) | |
| if (COIBufferMap (in_DestBuffer, 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 + in_Offset), in_pSourceData, | |
| in_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 (out_pCompletion) | |
| out_pCompletion->opaque[0] = 0; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIEngineGetCount, 1) (COI_ISA_TYPE isa, | |
| uint32_t *count) | |
| { | |
| COITRACE ("COIEngineGetCount"); | |
| /* Features of liboffloadmic. */ | |
| assert (isa == COI_ISA_MIC); | |
| assert (count != NULL); | |
| /* Prepare output arguments. */ | |
| *count = num_engines; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIEngineGetHandle, 1) (COI_ISA_TYPE in_ISA, | |
| uint32_t in_EngineIndex, | |
| COIENGINE *out_pEngineHandle) | |
| { | |
| COITRACE ("COIEngineGetHandle"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_ISA == COI_ISA_MIC); | |
| assert (out_pEngineHandle != NULL); | |
| /* Check engine index. */ | |
| if (in_EngineIndex >= num_engines) | |
| COIERROR ("Wrong engine index."); | |
| /* Create engine handle. */ | |
| Engine *engine = new Engine; | |
| engine->dir = NULL; | |
| engine->index = in_EngineIndex; | |
| engine->type = in_ISA; | |
| /* Prepare output argument. */ | |
| *out_pEngineHandle = (COIENGINE) engine; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIEventWait, 1) (uint16_t in_NumEvents, | |
| const COIEVENT *in_pEvents, | |
| int32_t in_TimeoutMilliseconds, | |
| uint8_t in_WaitForAll, | |
| uint32_t *out_pNumSignaled, | |
| uint32_t *out_pSignaledIndices) | |
| { | |
| COITRACE ("COIEventWait"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_pEvents != NULL); | |
| assert (in_TimeoutMilliseconds == 0 || in_TimeoutMilliseconds == -1); | |
| assert (in_WaitForAll == 1); | |
| assert (out_pNumSignaled == NULL); | |
| assert (out_pSignaledIndices == NULL); | |
| if (in_TimeoutMilliseconds == 0) | |
| { | |
| /* If some event is not signalled, return timeout error. */ | |
| for (uint16_t i = 0; i < in_NumEvents; i++) | |
| if (non_signalled_events.count (in_pEvents[i].opaque[0]) > 0) | |
| return COI_TIME_OUT_REACHED; | |
| else | |
| { | |
| /* If the event signalled with an error, return that error. */ | |
| start_critical_section (); | |
| COIRESULT res = get_event_result (in_pEvents[i]); | |
| finish_critical_section (); | |
| if (res != COI_SUCCESS) | |
| return res; | |
| } | |
| } | |
| else | |
| { | |
| /* Wait indefinitely for all events. */ | |
| for (uint16_t i = 0; i < in_NumEvents; i++) | |
| { | |
| while (non_signalled_events.count (in_pEvents[i].opaque[0]) > 0) | |
| usleep (1000); | |
| /* If the event signalled with an error, return that error. */ | |
| start_critical_section (); | |
| COIRESULT res = get_event_result (in_pEvents[i]); | |
| finish_critical_section (); | |
| if (res != COI_SUCCESS) | |
| return res; | |
| } | |
| } | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIEventRegisterCallback, 1) (const COIEVENT in_Event, | |
| COI_EVENT_CALLBACK in_Callback, | |
| const void *in_UserData, | |
| const uint64_t in_Flags) | |
| { | |
| COITRACE ("COIEventRegisterCallback"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_Callback != NULL); | |
| assert (in_UserData != NULL); | |
| assert (in_Flags == 0); | |
| start_critical_section (); | |
| if (non_signalled_events.count (in_Event.opaque[0]) == 0) | |
| { | |
| /* If the event is already signalled, invoke the callback immediately. */ | |
| COIRESULT res = get_event_result (in_Event); | |
| in_Callback (in_Event, res, in_UserData); | |
| } | |
| else | |
| { | |
| Callback callback; | |
| callback.ptr = in_Callback; | |
| callback.data = in_UserData; | |
| callbacks.insert (std::pair <uint64_t, Callback> (in_Event.opaque[0], | |
| callback)); | |
| } | |
| finish_critical_section (); | |
| return COI_SUCCESS; | |
| } | |
| /* The start routine for the COI pipeline thread. */ | |
| static void * | |
| pipeline_thread_routine (void *in_Pipeline) | |
| { | |
| /* Convert input arguments. */ | |
| Pipeline *pipeline = (Pipeline *) in_Pipeline; | |
| /* Open pipes. */ | |
| pipeline->pipe_host2tgt | |
| = open (pipeline->pipe_host2tgt_path, O_CLOEXEC | O_WRONLY); | |
| if (pipeline->pipe_host2tgt < 0) | |
| COIERRORN ("Cannot open host-to-target pipe."); | |
| pipeline->pipe_tgt2host | |
| = open (pipeline->pipe_tgt2host_path, O_CLOEXEC | O_RDONLY); | |
| if (pipeline->pipe_tgt2host < 0) | |
| COIERRORN ("Cannot open target-to-host pipe."); | |
| free (pipeline->pipe_host2tgt_path); | |
| free (pipeline->pipe_tgt2host_path); | |
| pipeline->pipe_host2tgt_path = NULL; | |
| pipeline->pipe_tgt2host_path = NULL; | |
| while (!pipeline->destroy) | |
| if (pipeline->queue.empty ()) | |
| usleep (1000); | |
| else | |
| { | |
| Function func = pipeline->queue.front (); | |
| start_critical_section (); | |
| pipeline->queue.pop (); | |
| finish_critical_section (); | |
| /* Send data to target. */ | |
| cmd_t cmd = CMD_PIPELINE_RUN_FUNCTION; | |
| WRITEN (pipeline->pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITEN (pipeline->pipe_host2tgt, &func.ptr, sizeof (void *)); | |
| WRITEN (pipeline->pipe_host2tgt, &func.num_buffers, sizeof (uint32_t)); | |
| for (uint32_t i = 0; i < func.num_buffers; i++) | |
| { | |
| WRITEN (pipeline->pipe_host2tgt, &func.bufs_size[i], | |
| sizeof (uint64_t)); | |
| WRITEN (pipeline->pipe_host2tgt, &func.bufs_data_target[i], | |
| sizeof (void *)); | |
| } | |
| WRITEN (pipeline->pipe_host2tgt, &func.misc_data_len, | |
| sizeof (uint16_t)); | |
| if (func.misc_data_len > 0) | |
| WRITEN (pipeline->pipe_host2tgt, func.misc_data, func.misc_data_len); | |
| WRITEN (pipeline->pipe_host2tgt, &func.return_value_len, | |
| sizeof (uint16_t)); | |
| delete [] func.bufs_size; | |
| delete [] func.bufs_data_target; | |
| /* Receive data from target. Wait for target function to complete, | |
| whether it has any data to return or not. */ | |
| bool has_return_value = func.return_value_len > 0; | |
| int ret_len | |
| = read (pipeline->pipe_tgt2host, | |
| has_return_value ? func.return_value : &cmd, | |
| has_return_value ? func.return_value_len : sizeof (cmd_t)); | |
| if (ret_len == 0) | |
| { | |
| start_critical_section (); | |
| signal_event (func.completion_event, COI_PROCESS_DIED); | |
| pipeline->is_destroyed = true; | |
| finish_critical_section (); | |
| return NULL; | |
| } | |
| else if (ret_len != (has_return_value ? func.return_value_len | |
| : sizeof (cmd_t))) | |
| COIERRORN ("Cannot read from pipe."); | |
| start_critical_section (); | |
| signal_event (func.completion_event, COI_SUCCESS); | |
| finish_critical_section (); | |
| } | |
| /* Send data to target. */ | |
| const cmd_t cmd = CMD_PIPELINE_DESTROY; | |
| WRITEN (pipeline->pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| /* Close pipes. */ | |
| if (close (pipeline->pipe_host2tgt) < 0) | |
| COIERRORN ("Cannot close host-to-target pipe."); | |
| if (close (pipeline->pipe_tgt2host) < 0) | |
| COIERRORN ("Cannot close target-to-host pipe."); | |
| start_critical_section (); | |
| pipeline->is_destroyed = true; | |
| finish_critical_section (); | |
| return NULL; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIPipelineCreate, 1) (COIPROCESS in_Process, | |
| COI_CPU_MASK in_Mask, | |
| uint32_t in_StackSize, // Ignored | |
| COIPIPELINE *out_pPipeline) | |
| { | |
| COITRACE ("COIPipelineCreate"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_Process != NULL); | |
| assert (in_Mask == 0); | |
| assert (out_pPipeline != NULL); | |
| /* Convert input arguments. */ | |
| Process *proc = (Process *) in_Process; | |
| start_critical_section (); | |
| /* Create pipeline handle. */ | |
| Pipeline *pipeline = new Pipeline; | |
| pipeline->destroy = false; | |
| pipeline->is_destroyed = false; | |
| pipeline->process = proc; | |
| pipelines.insert (pipeline); | |
| /* Create pipes. */ | |
| uint32_t pipeline_num = max_pipeline_num++; | |
| char *eng_dir = pipeline->process->engine->dir; | |
| MALLOC (char *, pipeline->pipe_host2tgt_path, | |
| strlen (eng_dir) + sizeof (PIPE_HOST2TGT_NAME "0000000000")); | |
| MALLOC (char *, pipeline->pipe_tgt2host_path, | |
| strlen (eng_dir) + sizeof (PIPE_TGT2HOST_NAME "0000000000")); | |
| sprintf (pipeline->pipe_host2tgt_path, "%s" PIPE_HOST2TGT_NAME "%010d", | |
| eng_dir, pipeline_num); | |
| sprintf (pipeline->pipe_tgt2host_path, "%s" PIPE_TGT2HOST_NAME "%010d", | |
| eng_dir, pipeline_num); | |
| if (mkfifo (pipeline->pipe_host2tgt_path, S_IRUSR | S_IWUSR) < 0) | |
| COIERROR ("Cannot create pipe %s.", pipeline->pipe_host2tgt_path); | |
| if (mkfifo (pipeline->pipe_tgt2host_path, S_IRUSR | S_IWUSR) < 0) | |
| COIERROR ("Cannot create pipe %s.", pipeline->pipe_tgt2host_path); | |
| /* Send data to target. */ | |
| const cmd_t cmd = CMD_PIPELINE_CREATE; | |
| WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (proc->pipe_host2tgt, &pipeline_num, sizeof (pipeline_num)); | |
| /* Create a new thread for the pipeline. */ | |
| if (pthread_create (&pipeline->thread, NULL, pipeline_thread_routine, | |
| pipeline)) | |
| COIERROR ("Cannot create new thread."); | |
| finish_critical_section (); | |
| /* Prepare output arguments. */ | |
| *out_pPipeline = (COIPIPELINE) pipeline; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIPipelineDestroy, 1) (COIPIPELINE in_Pipeline) | |
| { | |
| COITRACE ("COIPipelineDestroy"); | |
| assert (in_Pipeline != NULL); | |
| /* Convert input arguments. */ | |
| Pipeline *pipeline = (Pipeline *) in_Pipeline; | |
| start_critical_section (); | |
| /* Remove pipeline from the set of undestroyed pipelines. */ | |
| pipelines.erase (pipeline); | |
| /* Exit pipeline thread. */ | |
| pipeline->destroy = true; | |
| finish_critical_section (); | |
| while (!pipeline_is_destroyed (pipeline)) | |
| usleep (1000); | |
| /* Join with a destroyed thread. */ | |
| if (pthread_join (pipeline->thread, NULL)) | |
| COIERROR ("Cannot join with a thread."); | |
| delete pipeline; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIPipelineRunFunction, 1) (COIPIPELINE in_Pipeline, | |
| COIFUNCTION in_Function, | |
| uint32_t in_NumBuffers, | |
| const COIBUFFER *in_Buffers, | |
| const COI_ACCESS_FLAGS *in_pBufferAccessFlags, // Ignored | |
| uint32_t in_NumDependencies, | |
| const COIEVENT *in_pDependencies, // Ignored | |
| const void *in_pMiscData, | |
| uint16_t in_MiscDataLen, | |
| void *out_pAsyncReturnValue, | |
| uint16_t in_AsyncReturnValueLen, | |
| COIEVENT *out_pCompletion) | |
| { | |
| COITRACE ("COIPipelineRunFunction"); | |
| /* Features of liboffloadmic. */ | |
| assert (in_Pipeline != NULL); | |
| assert (in_Function != NULL); | |
| assert (in_NumDependencies == 0); | |
| Function func; | |
| func.ptr = (void *) in_Function; | |
| func.num_buffers = in_NumBuffers; | |
| func.bufs_size = new uint64_t [in_NumBuffers]; | |
| func.bufs_data_target = new void * [in_NumBuffers]; | |
| for (uint32_t i = 0; i < in_NumBuffers; i++) | |
| { | |
| Buffer **bufs = (Buffer **) in_Buffers; | |
| func.bufs_size[i] = bufs[i]->size; | |
| func.bufs_data_target[i] = bufs[i]->data_target; | |
| } | |
| func.misc_data = (void *) in_pMiscData; | |
| func.misc_data_len = in_MiscDataLen; | |
| func.return_value = out_pAsyncReturnValue; | |
| func.return_value_len = in_AsyncReturnValueLen; | |
| start_critical_section (); | |
| func.completion_event.opaque[0] = max_event_num++; | |
| non_signalled_events.insert (func.completion_event.opaque[0]); | |
| ((Pipeline *) in_Pipeline)->queue.push (func); | |
| finish_critical_section (); | |
| /* In case of synchronous execution we have to wait for target. */ | |
| if (out_pCompletion == NULL) | |
| COIEventWait (1, &func.completion_event, -1, 1, NULL, NULL); | |
| else | |
| *out_pCompletion = func.completion_event; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIProcessCreateFromMemory, 1) (COIENGINE in_Engine, | |
| const char *in_pBinaryName, | |
| const void *in_pBinaryBuffer, | |
| uint64_t in_BinaryBufferLength, | |
| int in_Argc, | |
| const char **in_ppArgv, | |
| uint8_t in_DupEnv, | |
| const char **in_ppAdditionalEnv, | |
| uint8_t in_ProxyActive, // Ignored | |
| const char *in_Reserved, // Ignored | |
| uint64_t in_InitialBufferSpace, // Ignored | |
| const char *in_LibrarySearchPath, | |
| const char *in_FileOfOrigin, // Ignored | |
| uint64_t in_FileOfOriginOffset, // Ignored | |
| COIPROCESS *out_pProcess) | |
| { | |
| COITRACE ("COIProcessCreateFromMemory"); | |
| const int run_max_args_num = 128; | |
| char *run_argv[run_max_args_num]; | |
| char *emul_run = getenv (OFFLOAD_EMUL_RUN_ENV); | |
| const int uint_max_len = 11; | |
| /* Features of liboffloadmic. */ | |
| assert (in_Engine != NULL); | |
| assert (in_pBinaryName != NULL); | |
| assert (in_pBinaryBuffer != NULL); | |
| assert (in_Argc == 0); | |
| assert (in_ppArgv == NULL); | |
| assert (in_ppAdditionalEnv == NULL); | |
| assert (in_LibrarySearchPath != NULL); | |
| assert (out_pProcess != NULL); | |
| /* Convert input arguments. */ | |
| Engine *eng = (Engine *) in_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. */ | |
| char *target_exe; | |
| MALLOC (char *, target_exe, strlen (eng->dir) + strlen (in_pBinaryName) + 2); | |
| sprintf (target_exe, "%s/%s", eng->dir, in_pBinaryName); | |
| int 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 *file = fdopen (fd, "wb"); | |
| if (file == NULL) | |
| COIERROR ("Cannot associate stream with file descriptor."); | |
| if (fwrite (in_pBinaryBuffer, 1, in_BinaryBufferLength, file) | |
| != in_BinaryBufferLength) | |
| 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. */ | |
| char *pipes_path; | |
| MALLOC (char *, pipes_path, strlen (eng->dir) + sizeof (PIPES_PATH)); | |
| sprintf (pipes_path, "%s" PIPES_PATH, eng->dir); | |
| if (mkdir (pipes_path, S_IRWXU) < 0) | |
| COIERROR ("Cannot create folder %s.", pipes_path); | |
| /* Create 2 main pipes for inter-process communication. */ | |
| char *pipe_host2tgt_path, *pipe_tgt2host_path; | |
| MALLOC (char *, pipe_host2tgt_path, | |
| strlen (eng->dir) + sizeof (PIPE_HOST2TGT_NAME "mainpipe")); | |
| MALLOC (char *, pipe_tgt2host_path, | |
| strlen (eng->dir) + sizeof (PIPE_TGT2HOST_NAME "mainpipe")); | |
| sprintf (pipe_host2tgt_path, "%s" PIPE_HOST2TGT_NAME "mainpipe", eng->dir); | |
| sprintf (pipe_tgt2host_path, "%s" PIPE_TGT2HOST_NAME "mainpipe", eng->dir); | |
| if (mkfifo (pipe_host2tgt_path, S_IRUSR | S_IWUSR) < 0) | |
| COIERROR ("Cannot create main pipe %s.", pipe_host2tgt_path); | |
| if (mkfifo (pipe_tgt2host_path, S_IRUSR | S_IWUSR) < 0) | |
| COIERROR ("Cannot create main pipe %s.", pipe_tgt2host_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; | |
| int i = 0; | |
| STRDUP (tmp, emul_run); | |
| char *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. */ | |
| int env_num = 0; | |
| if (in_DupEnv == true) | |
| while (environ[env_num++]); | |
| env_num += 4; // LD_LIBRARY_PATH, MIC_DIR, MIC_INDEX, NULL | |
| char **envp; | |
| MALLOC (char **, envp, env_num * sizeof (char *)); | |
| int env_i = 0; | |
| if (in_DupEnv == true) | |
| for (unsigned i = 0; environ[i] != NULL; i++) | |
| { | |
| unsigned j; | |
| char *env_name; | |
| 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 (in_LibrarySearchPath) + 1); | |
| sprintf (envp[env_i + 2], "LD_LIBRARY_PATH=%s", in_LibrarySearchPath); | |
| envp[env_i + 3] = (char *) NULL; | |
| /* Create target process. */ | |
| pid_t 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 main pipes. */ | |
| int pipe_host2tgt = open (pipe_host2tgt_path, O_CLOEXEC | O_WRONLY); | |
| if (pipe_host2tgt < 0) | |
| COIERROR ("Cannot open host-to-target main pipe."); | |
| int pipe_tgt2host = open (pipe_tgt2host_path, O_CLOEXEC | O_RDONLY); | |
| if (pipe_tgt2host < 0) | |
| COIERROR ("Cannot open target-to-host main pipe."); | |
| /* Create process handle. */ | |
| Process *proc = new Process; | |
| proc->pid = pid; | |
| proc->pipe_host2tgt = pipe_host2tgt; | |
| proc->pipe_tgt2host = pipe_tgt2host; | |
| proc->engine = eng; | |
| proc->functions = NULL; | |
| /* Prepare output arguments. */ | |
| *out_pProcess = (COIPROCESS) proc; | |
| /* Clean up. */ | |
| for (unsigned i = 0; run_argv[i] != NULL; i++) | |
| free (run_argv[i]); | |
| for (unsigned i = 0; envp[i] != NULL; i++) | |
| free (envp[i]); | |
| free (envp); | |
| free (pipe_host2tgt_path); | |
| free (pipe_tgt2host_path); | |
| free (pipes_path); | |
| free (target_exe); | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIProcessCreateFromFile, 1) (COIENGINE in_Engine, | |
| const char *in_pBinaryName, | |
| int in_Argc, | |
| const char **in_ppArgv, | |
| uint8_t in_DupEnv, | |
| const char **in_ppAdditionalEnv, | |
| uint8_t in_ProxyActive, | |
| const char *in_Reserved, | |
| uint64_t in_BufferSpace, | |
| const char *in_LibrarySearchPath, | |
| COIPROCESS *out_pProcess) | |
| { | |
| COITRACE ("COIProcessCreateFromFile"); | |
| /* liboffloadmic with GCC compiled binaries should never go here. */ | |
| assert (false); | |
| return COI_ERROR; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIProcessDestroy, 1) (COIPROCESS in_Process, | |
| int32_t in_WaitForMainTimeout, // Ignored | |
| uint8_t in_ForceDestroy, | |
| int8_t *out_pProcessReturn, | |
| uint32_t *out_pTerminationCode) | |
| { | |
| COITRACE ("COIProcessDestroy"); | |
| assert (in_Process != NULL); | |
| assert (out_pProcessReturn != NULL); | |
| assert (out_pTerminationCode != NULL); | |
| /* Convert input arguments. */ | |
| Process *proc = (Process *) in_Process; | |
| /* Destroy all undestroyed pipelines. */ | |
| while (!pipelines.empty ()) | |
| { | |
| std::set<Pipeline *>::iterator p = pipelines.begin (); | |
| COIPipelineDestroy ((COIPIPELINE) *p); | |
| } | |
| /* Close main pipes. */ | |
| if (close (proc->pipe_host2tgt) < 0) | |
| COIERROR ("Cannot close host-to-target main pipe."); | |
| if (close (proc->pipe_tgt2host) < 0) | |
| COIERROR ("Cannot close target-to-host main pipe."); | |
| /* Shutdown target process by force. */ | |
| if (in_ForceDestroy) | |
| kill (proc->pid, SIGTERM); | |
| /* Clean up. */ | |
| free (proc->engine->dir); | |
| free (proc->functions); | |
| delete proc->engine; | |
| delete proc; | |
| /* Prepare output arguments. */ | |
| *out_pProcessReturn = 0; | |
| *out_pTerminationCode = 0; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIProcessGetFunctionHandles, 1) (COIPROCESS in_Process, | |
| uint32_t in_NumFunctions, | |
| const char **in_ppFunctionNameArray, | |
| COIFUNCTION *out_pFunctionHandleArray) | |
| { | |
| COITRACE ("COIProcessGetFunctionHandles"); | |
| assert (in_Process != NULL); | |
| assert (in_ppFunctionNameArray != NULL); | |
| assert (out_pFunctionHandleArray != NULL); | |
| /* Convert input arguments. */ | |
| Process *proc = (Process *) in_Process; | |
| /* This function should be called once for the process. */ | |
| assert (proc->functions == NULL); | |
| /* 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 = (void **) calloc (in_NumFunctions + 1, sizeof (void *)); | |
| if (proc->functions == NULL) | |
| COIERROR ("Cannot allocate memory."); | |
| /* Get handles for functions. */ | |
| for (uint32_t i = 0; i < in_NumFunctions; i++) | |
| { | |
| size_t len = strlen (in_ppFunctionNameArray[i]) + 1; | |
| start_critical_section (); | |
| /* Send data to target. */ | |
| const cmd_t cmd = CMD_GET_FUNCTION_HANDLE; | |
| WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (proc->pipe_host2tgt, &len, sizeof (size_t)); | |
| WRITE (proc->pipe_host2tgt, in_ppFunctionNameArray[i], len); | |
| /* Receive data from target. */ | |
| void *fn_ptr; | |
| READ (proc->pipe_tgt2host, &fn_ptr, sizeof (void *)); | |
| finish_critical_section (); | |
| /* Save function pointer. */ | |
| proc->functions[i] = fn_ptr; | |
| /* Prepare output arguments. */ | |
| out_pFunctionHandleArray[i] = (COIFUNCTION) fn_ptr; | |
| } | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIProcessLoadLibraryFromMemory, 2) (COIPROCESS in_Process, | |
| const void *in_pLibraryBuffer, | |
| uint64_t in_LibraryBufferLength, | |
| const char *in_pLibraryName, | |
| const char *in_LibrarySearchPath, // Ignored | |
| const char *in_FileOfOrigin, // Ignored | |
| uint64_t in_FileOfOriginOffset, // Ignored | |
| uint32_t in_Flags, // Ignored | |
| COILIBRARY *out_pLibrary) | |
| { | |
| COITRACE ("COIProcessLoadLibraryFromMemory"); | |
| assert (in_Process != NULL); | |
| assert (in_pLibraryBuffer != NULL); | |
| assert (out_pLibrary != NULL); | |
| /* Convert input arguments. */ | |
| Process *proc = (Process *) in_Process; | |
| /* Create target library file. */ | |
| char *lib_path; | |
| size_t len = strlen (proc->engine->dir) + strlen (in_pLibraryName) + 2; | |
| MALLOC (char *, lib_path, len); | |
| sprintf (lib_path, "%s/%s", proc->engine->dir, in_pLibraryName); | |
| int 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 *file = fdopen (fd, "wb"); | |
| if (file == NULL) | |
| COIERROR ("Cannot associate stream with file descriptor."); | |
| if (fwrite (in_pLibraryBuffer, 1, in_LibraryBufferLength, file) | |
| != in_LibraryBufferLength) | |
| COIERROR ("Cannot write in file %s.", lib_path); | |
| if (fclose (file) != 0) | |
| COIERROR ("Cannot close file %s.", lib_path); | |
| start_critical_section (); | |
| /* Make target open library. */ | |
| const cmd_t cmd = CMD_OPEN_LIBRARY; | |
| WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (proc->pipe_host2tgt, &len, sizeof (size_t)); | |
| WRITE (proc->pipe_host2tgt, lib_path, len); | |
| /* Receive data from target. */ | |
| void *handle; | |
| READ (proc->pipe_tgt2host, &handle, sizeof (void *)); | |
| finish_critical_section (); | |
| /* Clean up. */ | |
| free (lib_path); | |
| *out_pLibrary = (COILIBRARY) handle; | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIProcessRegisterLibraries, 1) (uint32_t in_NumLibraries, // Ignored | |
| const void **in_ppLibraryArray, // Ignored | |
| const uint64_t *in_pLibrarySizeArray, // Ignored | |
| const char **in_ppFileOfOriginArray, // Ignored | |
| const uint64_t *in_pFileOfOriginOffSetArray) // Ignored | |
| { | |
| COITRACE ("COIProcessRegisterLibraries"); | |
| /* Looks like we have nothing to do here. */ | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIProcessUnloadLibrary, 1) (COIPROCESS in_Process, | |
| COILIBRARY in_Library) | |
| { | |
| COITRACE ("COIProcessUnloadLibrary"); | |
| assert (in_Process != NULL); | |
| assert (in_Library != NULL); | |
| const cmd_t cmd = CMD_CLOSE_LIBRARY; | |
| /* Convert input arguments. */ | |
| Process *proc = (Process *) in_Process; | |
| start_critical_section (); | |
| /* Make target close library. */ | |
| WRITE (proc->pipe_host2tgt, &cmd, sizeof (cmd_t)); | |
| WRITE (proc->pipe_host2tgt, &in_Library, sizeof (void *)); | |
| finish_critical_section (); | |
| return COI_SUCCESS; | |
| } | |
| uint64_t | |
| SYMBOL_VERSION (COIPerfGetCycleFrequency, 1) () | |
| { | |
| COITRACE ("COIPerfGetCycleFrequency"); | |
| return (uint64_t) CYCLE_FREQUENCY; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIPipelineClearCPUMask, 1) (COI_CPU_MASK *in_Mask) | |
| { | |
| COITRACE ("COIPipelineClearCPUMask"); | |
| /* Looks like we have nothing to do here. */ | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIPipelineSetCPUMask, 1) (COIPROCESS in_Process, | |
| uint32_t in_CoreID, | |
| uint8_t in_ThreadID, | |
| COI_CPU_MASK *out_pMask) | |
| { | |
| COITRACE ("COIPipelineSetCPUMask"); | |
| /* Looks like we have nothing to do here. */ | |
| return COI_SUCCESS; | |
| } | |
| COIRESULT | |
| SYMBOL_VERSION (COIEngineGetInfo, 1) (COIENGINE in_EngineHandle, // Ignored | |
| uint32_t in_EngineInfoSize, // Ignored | |
| COI_ENGINE_INFO *out_pEngineInfo) | |
| { | |
| COITRACE ("COIEngineGetInfo"); | |
| assert (out_pEngineInfo != NULL); | |
| out_pEngineInfo->ISA = COI_DEVICE_KNL; | |
| out_pEngineInfo->NumCores = 1; | |
| out_pEngineInfo->NumThreads = 8; | |
| out_pEngineInfo->CoreMaxFrequency = SYMBOL_VERSION(COIPerfGetCycleFrequency,1)() / 1000000; | |
| out_pEngineInfo->PhysicalMemory = 1024; | |
| out_pEngineInfo->PhysicalMemoryFree = 1024; | |
| out_pEngineInfo->SwapMemory = 1024; | |
| out_pEngineInfo->SwapMemoryFree = 1024; | |
| out_pEngineInfo->MiscFlags = COI_ENG_ECC_DISABLED; | |
| return COI_SUCCESS; | |
| } | |
| } // extern "C" | |