// Copyright(c) 2017-2020, Intel Corporation // // 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 OWNER 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. #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif // _GNU_SOURCE #include #include #include #include #include #include #include "xfpga.h" #include "common_int.h" #include "opae_drv.h" #include "types_int.h" #include "intel-fpga.h" #define EVENT_SOCKET_NAME "/tmp/fpga_event_socket" #define EVENT_SOCKET_NAME_LEN 23 enum request_type { REGISTER_EVENT = 0, UNREGISTER_EVENT = 1 }; struct event_request { enum request_type type; fpga_event_type event; uint64_t object_id; }; fpga_result send_event_request(int conn_socket, int fd, struct event_request *req) { struct msghdr mh; struct cmsghdr *cmh; struct iovec iov[1]; char buf[CMSG_SPACE(sizeof(int))]; ssize_t n; int *fd_ptr; /* set up ancillary data message header */ iov[0].iov_base = req; iov[0].iov_len = sizeof(*req); memset(buf, 0, sizeof(buf)); mh.msg_name = NULL; mh.msg_namelen = 0; mh.msg_iov = iov; mh.msg_iovlen = sizeof(iov) / sizeof(iov[0]); mh.msg_control = buf; mh.msg_controllen = CMSG_LEN(sizeof(int)); mh.msg_flags = 0; cmh = CMSG_FIRSTHDR(&mh); cmh->cmsg_len = CMSG_LEN(sizeof(int)); cmh->cmsg_level = SOL_SOCKET; cmh->cmsg_type = SCM_RIGHTS; fd_ptr = (int *)CMSG_DATA(cmh); *fd_ptr = fd; /* send ancillary data */ n = sendmsg(conn_socket, &mh, 0); if (n < 0) { OPAE_ERR("sendmsg failed: %s", strerror(errno)); return FPGA_EXCEPTION; } return FPGA_OK; } STATIC fpga_result send_fme_event_request(fpga_handle handle, fpga_event_handle event_handle, int fme_operation) { int fd = FILE_DESCRIPTOR(event_handle); struct _fpga_handle *_handle = (struct _fpga_handle *)handle; fpga_result res = FPGA_OK; opae_fme_info fme_info = { 0 }; if (fme_operation != FPGA_IRQ_ASSIGN && fme_operation != FPGA_IRQ_DEASSIGN) { OPAE_ERR("Invalid FME operation requested"); return FPGA_INVALID_PARAM; } res = opae_get_fme_info(_handle->fddev, &fme_info); if (res) { return res; } /*capability field is set to 1 if the platform supports interrupts*/ if (fme_info.capability & FPGA_FME_CAP_ERR_IRQ) { res = opae_fme_set_err_irq(_handle->fddev, 0, fme_operation == FPGA_IRQ_ASSIGN ? fd : -1); if (res) { OPAE_ERR("Could not set eventfd %s", strerror(errno)); } } else { OPAE_ERR("FME interrupts not supported in hw"); res = FPGA_NOT_SUPPORTED; } return res; } STATIC fpga_result send_port_event_request(fpga_handle handle, fpga_event_handle event_handle, int port_operation) { fpga_result res = FPGA_OK; int fd = FILE_DESCRIPTOR(event_handle); struct _fpga_handle *_handle = (struct _fpga_handle *)handle; opae_port_info port_info = { 0 }; if (port_operation != FPGA_IRQ_ASSIGN && port_operation != FPGA_IRQ_DEASSIGN) { OPAE_ERR("Invalid PORT operation requested"); return FPGA_INVALID_PARAM; } res = opae_get_port_info(_handle->fddev, &port_info); if (res) { return res; } /*capability field is set to 1 if the platform supports interrupts*/ if (port_info.capability & FPGA_PORT_CAP_ERR_IRQ) { res = opae_port_set_err_irq(_handle->fddev, 0, port_operation == FPGA_IRQ_ASSIGN ? fd : -1); if (res) { OPAE_ERR("Could not set eventfd"); } } else { OPAE_ERR("PORT interrupts not supported in hw"); res = FPGA_NOT_SUPPORTED; } return res; } STATIC fpga_result send_uafu_event_request(fpga_handle handle, fpga_event_handle event_handle, uint32_t flags, int uafu_operation) { int res = FPGA_OK; int fd = FILE_DESCRIPTOR(event_handle); struct _fpga_event_handle *_eh = (struct _fpga_event_handle *)event_handle; struct _fpga_handle *_handle = (struct _fpga_handle *)handle; opae_port_info port_info = { 0 }; int32_t neg = -1; if (uafu_operation != FPGA_IRQ_ASSIGN && uafu_operation != FPGA_IRQ_DEASSIGN) { OPAE_ERR("Invalid UAFU operation requested"); return FPGA_INVALID_PARAM; } res = opae_get_port_info(_handle->fddev, &port_info); if (res) { return res; } /*capability field is set to 1 if the platform supports interrupts*/ if (port_info.capability & FPGA_PORT_CAP_UAFU_IRQ) { if (flags >= port_info.num_uafu_irqs) { OPAE_ERR("Invalid User Interrupt vector id"); return FPGA_INVALID_PARAM; } if (uafu_operation == FPGA_IRQ_ASSIGN) { res = opae_port_set_user_irq(_handle->fddev, 0, flags, 1, &fd); _eh->flags = flags; } else { res = opae_port_set_user_irq(_handle->fddev, 0, _eh->flags, 1, &neg); } if (res) { OPAE_ERR("Could not set eventfd"); res = FPGA_EXCEPTION; } } else { OPAE_ERR("UAFU interrupts not supported in hw"); res = FPGA_NOT_SUPPORTED; } return res; } /* * Uses driver ioctls to determine whether the driver supports interrupts * on this platform. objtype is an output parameter. */ STATIC fpga_result check_interrupts_supported(fpga_handle handle, fpga_objtype *objtype) { fpga_result res = FPGA_OK; fpga_result destroy_res = FPGA_OK; fpga_properties prop = NULL; struct _fpga_handle *_handle = (struct _fpga_handle *)handle; opae_fme_info fme_info = { 0 }; opae_port_info port_info = { 0 }; res = xfpga_fpgaGetPropertiesFromHandle(handle, &prop); if (res != FPGA_OK) { OPAE_MSG("Could not get FPGA properties from handle"); return res; } res = fpgaPropertiesGetObjectType(prop, objtype); if (res != FPGA_OK) { OPAE_MSG("Could not determine FPGA object type"); goto destroy_prop; } if (*objtype == FPGA_DEVICE) { res = opae_get_fme_info(_handle->fddev, &fme_info); if (res) { res = FPGA_EXCEPTION; goto destroy_prop; } if (fme_info.capability & FPGA_FME_CAP_ERR_IRQ) { res = FPGA_OK; } else { OPAE_MSG("Interrupts not supported in hw"); res = FPGA_NOT_SUPPORTED; } } else if (*objtype == FPGA_ACCELERATOR) { res = opae_get_port_info(_handle->fddev, &port_info); if (res) { OPAE_ERR("Could not get PORT info: %s", strerror(errno)); goto destroy_prop; } if (port_info.capability & FPGA_PORT_CAP_ERR_IRQ) { res = FPGA_OK; } else { OPAE_MSG("Interrupts not supported in hw"); res = FPGA_NOT_SUPPORTED; } } destroy_prop: destroy_res = fpgaDestroyProperties(&prop); if (destroy_res != FPGA_OK) { OPAE_MSG("Could not destroy FPGA properties"); return destroy_res; } return res; } STATIC fpga_result driver_register_event(fpga_handle handle, fpga_event_type event_type, fpga_event_handle event_handle, uint32_t flags) { fpga_objtype objtype; fpga_result res = FPGA_OK; res = check_interrupts_supported(handle, &objtype); if (res != FPGA_OK) { OPAE_MSG( "Could not determine whether interrupts are supported"); return FPGA_NOT_SUPPORTED; } switch (event_type) { case FPGA_EVENT_ERROR: if (objtype == FPGA_DEVICE) { return send_fme_event_request(handle, event_handle, FPGA_IRQ_ASSIGN); } else if (objtype == FPGA_ACCELERATOR) { return send_port_event_request(handle, event_handle, FPGA_IRQ_ASSIGN); } OPAE_ERR("Invalid objtype: %d", objtype); return FPGA_EXCEPTION; case FPGA_EVENT_INTERRUPT: if (objtype != FPGA_ACCELERATOR) { OPAE_MSG("User events need an accelerator object"); return FPGA_INVALID_PARAM; } return send_uafu_event_request(handle, event_handle, flags, FPGA_IRQ_ASSIGN); case FPGA_EVENT_POWER_THERMAL: OPAE_MSG("Thermal interrupts not supported"); return FPGA_NOT_SUPPORTED; default: OPAE_ERR("Invalid event type"); return FPGA_EXCEPTION; } } STATIC fpga_result driver_unregister_event(fpga_handle handle, fpga_event_type event_type, fpga_event_handle event_handle) { fpga_objtype objtype; fpga_result res = FPGA_OK; res = check_interrupts_supported(handle, &objtype); if (res != FPGA_OK) { OPAE_MSG( "Could not determine whether interrupts are supported"); return FPGA_NOT_SUPPORTED; } switch (event_type) { case FPGA_EVENT_ERROR: if (objtype == FPGA_DEVICE) { return send_fme_event_request(handle, event_handle, FPGA_IRQ_DEASSIGN); } else if (objtype == FPGA_ACCELERATOR) { return send_port_event_request(handle, event_handle, FPGA_IRQ_DEASSIGN); } OPAE_ERR("Invalid objtype: %d", objtype); return FPGA_EXCEPTION; case FPGA_EVENT_INTERRUPT: if (objtype != FPGA_ACCELERATOR) { OPAE_MSG("User events need an Accelerator object"); return FPGA_INVALID_PARAM; } return send_uafu_event_request(handle, event_handle, 0, FPGA_IRQ_DEASSIGN); case FPGA_EVENT_POWER_THERMAL: OPAE_MSG("Thermal interrupts not supported"); return FPGA_NOT_SUPPORTED; default: OPAE_ERR("Invalid event type"); return FPGA_EXCEPTION; } } STATIC fpga_result daemon_register_event(fpga_handle handle, fpga_event_type event_type, fpga_event_handle event_handle, uint32_t flags) { int fd = FILE_DESCRIPTOR(event_handle); fpga_result result = FPGA_OK; struct sockaddr_un addr; struct event_request req; struct _fpga_handle *_handle = (struct _fpga_handle *)handle; fpga_properties prop = NULL; uint64_t object_id = (uint64_t) -1; UNUSED_PARAM(flags); if (_handle->fdfpgad < 0) { /* connect to event socket */ _handle->fdfpgad = socket(AF_UNIX, SOCK_STREAM, 0); if (_handle->fdfpgad < 0) { OPAE_ERR("socket: %s", strerror(errno)); return FPGA_EXCEPTION; } addr.sun_family = AF_UNIX; strncpy(addr.sun_path, EVENT_SOCKET_NAME, EVENT_SOCKET_NAME_LEN); if (connect(_handle->fdfpgad, (struct sockaddr *)&addr, sizeof(addr)) < 0) { OPAE_DBG("connect: %s", strerror(errno)); result = FPGA_NO_DAEMON; goto out_close_conn; } } /* get the requestor's object ID */ result = xfpga_fpgaGetPropertiesFromHandle(handle, &prop); if (result != FPGA_OK) { OPAE_ERR("failed to get props"); goto out_close_conn; } result = fpgaPropertiesGetObjectID(prop, &object_id); if (result != FPGA_OK) { fpgaDestroyProperties(&prop); OPAE_ERR("failed to get object ID"); goto out_close_conn; } result = fpgaDestroyProperties(&prop); if (result != FPGA_OK) { OPAE_ERR("failed to destroy props"); goto out_close_conn; } /* create event registration request */ req.type = REGISTER_EVENT; req.event = event_type; req.object_id = object_id; /* send event packet */ result = send_event_request(_handle->fdfpgad, fd, &req); if (result != FPGA_OK) { OPAE_ERR("send_event_request failed"); goto out_close_conn; } return result; out_close_conn: close(_handle->fdfpgad); _handle->fdfpgad = -1; return result; } STATIC fpga_result daemon_unregister_event(fpga_handle handle, fpga_event_type event_type) { fpga_result result = FPGA_OK; struct _fpga_handle *_handle = (struct _fpga_handle *)handle; struct event_request req; ssize_t n; fpga_properties prop = NULL; uint64_t object_id = (uint64_t) -1; if (_handle->fdfpgad < 0) { OPAE_MSG("No fpgad connection"); return FPGA_INVALID_PARAM; } /* get the requestor's object ID */ result = xfpga_fpgaGetPropertiesFromHandle(handle, &prop); if (result != FPGA_OK) { OPAE_ERR("failed to get properties"); goto out_close_conn; } result = fpgaPropertiesGetObjectID(prop, &object_id); if (result != FPGA_OK) { fpgaDestroyProperties(&prop); OPAE_ERR("failed to get object ID"); goto out_close_conn; } result = fpgaDestroyProperties(&prop); if (result != FPGA_OK) { OPAE_ERR("failed to destroy properties"); goto out_close_conn; } req.type = UNREGISTER_EVENT; req.event = event_type; req.object_id = object_id; n = send(_handle->fdfpgad, &req, sizeof(req), 0); if (n < 0) { OPAE_ERR("send : %s", strerror(errno)); result = FPGA_EXCEPTION; goto out_close_conn; } return result; out_close_conn: close(_handle->fdfpgad); _handle->fdfpgad = -1; return result; } fpga_result __XFPGA_API__ xfpga_fpgaCreateEventHandle(fpga_event_handle *event_handle) { struct _fpga_event_handle *_eh; fpga_result result = FPGA_OK; pthread_mutexattr_t mattr; int err = 0; ASSERT_NOT_NULL(event_handle); _eh = malloc(sizeof(struct _fpga_event_handle)); if (NULL == _eh) { OPAE_ERR("Could not allocate memory for event handle"); return FPGA_NO_MEMORY; } _eh->magic = FPGA_EVENT_HANDLE_MAGIC; /* create eventfd */ _eh->fd = eventfd(0, 0); if (_eh->fd < 0) { OPAE_ERR("eventfd : %s", strerror(errno)); result = FPGA_EXCEPTION; goto out_free; } if (pthread_mutexattr_init(&mattr)) { OPAE_MSG("Failed to initialized event handle mutex attributes"); result = FPGA_EXCEPTION; goto out_free; } if (pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE)) { OPAE_MSG("Failed to initialize event handle mutex attributes"); result = FPGA_EXCEPTION; goto out_attr_destroy; } if (pthread_mutex_init(&_eh->lock, &mattr)) { OPAE_MSG("Failed to initialize event handle mutex"); result = FPGA_EXCEPTION; goto out_attr_destroy; } pthread_mutexattr_destroy(&mattr); *event_handle = (fpga_event_handle)_eh; return FPGA_OK; out_attr_destroy: err = pthread_mutexattr_destroy(&mattr); if (err) OPAE_ERR("pthread_mutexatr_destroy() failed: %s", strerror(err)); out_free: free(_eh); return result; } fpga_result __XFPGA_API__ xfpga_fpgaDestroyEventHandle(fpga_event_handle *event_handle) { struct _fpga_event_handle *_eh; fpga_result result = FPGA_OK; int err = 0; // sanity check if (!event_handle) { return FPGA_INVALID_PARAM; } _eh = (struct _fpga_event_handle *)*event_handle; result = event_handle_check_and_lock(_eh); if (result) return result; if (close(_eh->fd) < 0) { OPAE_ERR("eventfd : %s", strerror(errno)); err = pthread_mutex_unlock(&_eh->lock); if (err) OPAE_ERR("pthread_mutex_unlock() failed: %S", strerror(err)); if (errno == EBADF) return FPGA_INVALID_PARAM; else return FPGA_EXCEPTION; } _eh->magic = FPGA_INVALID_MAGIC; err = pthread_mutex_unlock(&_eh->lock); if (err) OPAE_ERR("pthread_mutex_unlock() failed: %S", strerror(err)); err = pthread_mutex_destroy(&_eh->lock); if (err) OPAE_ERR("pthread_mutex_destroy() failed: %S", strerror(err)); free(*event_handle); *event_handle = NULL; return FPGA_OK; } fpga_result __XFPGA_API__ xfpga_fpgaGetOSObjectFromEventHandle(const fpga_event_handle eh, int *fd) { struct _fpga_event_handle *_eh = (struct _fpga_event_handle *)eh; fpga_result result = FPGA_OK; int err = 0; result = event_handle_check_and_lock(_eh); if (result) return result; *fd = _eh->fd; err = pthread_mutex_unlock(&_eh->lock); if (err) OPAE_ERR("pthread_mutex_unlock() failed: %s", strerror(err)); return FPGA_OK; } fpga_result __XFPGA_API__ xfpga_fpgaRegisterEvent(fpga_handle handle, fpga_event_type event_type, fpga_event_handle event_handle, uint32_t flags) { fpga_result result = FPGA_OK; struct _fpga_handle *_handle = (struct _fpga_handle *)handle; struct _fpga_event_handle *_eh = (struct _fpga_event_handle *)event_handle; struct _fpga_token *_token; int err; result = handle_check_and_lock(_handle); if (result) return result; result = event_handle_check_and_lock(_eh); if (result) goto out_unlock_handle; _token = (struct _fpga_token *)_handle->token; if (_token->magic != FPGA_TOKEN_MAGIC) { OPAE_MSG("Invalid token found in handle"); result = FPGA_INVALID_PARAM; goto out_unlock; } switch (event_type) { case FPGA_EVENT_INTERRUPT: if (!strstr(_token->devpath, "port")) { OPAE_MSG("Handle does not refer to accelerator object"); result = FPGA_INVALID_PARAM; goto out_unlock; } break; case FPGA_EVENT_ERROR: /* fall through */ case FPGA_EVENT_POWER_THERMAL: break; } /* TODO: reject unknown flags */ /* try driver first */ result = driver_register_event(handle, event_type, event_handle, flags); if (result == FPGA_NOT_SUPPORTED) { result = daemon_register_event(handle, event_type, event_handle, flags); } out_unlock: err = pthread_mutex_unlock(&_eh->lock); if (err) OPAE_ERR("pthread_mutex_unlock() failed: %s", strerror(err)); out_unlock_handle: err = pthread_mutex_unlock(&_handle->lock); if (err) OPAE_ERR("pthread_mutex_unlock() failed: %s", strerror(err)); return result; } fpga_result __XFPGA_API__ xfpga_fpgaUnregisterEvent(fpga_handle handle, fpga_event_type event_type, fpga_event_handle event_handle) { fpga_result result = FPGA_OK; int err; struct _fpga_handle *_handle = (struct _fpga_handle *)handle; struct _fpga_event_handle *_eh = (struct _fpga_event_handle *)event_handle; struct _fpga_token *_token; result = handle_check_and_lock(_handle); if (result) return result; result = event_handle_check_and_lock(_eh); if (result) goto out_unlock_handle; _token = (struct _fpga_token *)_handle->token; if (_token->magic != FPGA_TOKEN_MAGIC) { OPAE_MSG("Invalid token found in handle"); result = FPGA_INVALID_PARAM; goto out_unlock; } switch (event_type) { case FPGA_EVENT_INTERRUPT: if (!strstr(_token->devpath, "port")) { OPAE_MSG("Handle does not refer to accelerator object"); result = FPGA_INVALID_PARAM; goto out_unlock; } break; case FPGA_EVENT_ERROR: /* fall through */ case FPGA_EVENT_POWER_THERMAL: break; } /* try driver first */ result = driver_unregister_event(handle, event_type, event_handle); if (result == FPGA_NOT_SUPPORTED) { result = daemon_unregister_event(handle, event_type); } out_unlock: err = pthread_mutex_unlock(&_eh->lock); if (err) OPAE_ERR("pthread_mutex_unlock() failed: %s", strerror(err)); out_unlock_handle: err = pthread_mutex_unlock(&_handle->lock); if (err) OPAE_ERR("pthread_mutex_unlock() failed: %s", strerror(err)); return result; }