/* * Copyright (c) 2016, Cisco Systems, Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * BSD license below: * * 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. * * 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. */ /* This needs to be included for usdf.h */ #include "ofi.h" #include "ofi_enosys.h" #include "ofi_util.h" #include "usdf.h" #include "usdf_cq.h" #include "usdf_wait.h" /* Necessary to support top-of-file struct declarations. */ static int usdf_wait_wait(struct fid_wait *wait_fid, int timeout); static int usdf_wait_close(struct fid *waitset); static int usdf_wait_control(struct fid *fid, int command, void *arg); static struct fi_ops_wait usdf_wait_ops = { .size = sizeof(struct fi_ops_wait), .wait = usdf_wait_wait, }; static struct fi_ops usdf_wait_fi_ops = { .size = sizeof(struct fi_ops), .close = usdf_wait_close, .bind = fi_no_bind, .control = usdf_wait_control, .ops_open = fi_no_ops_open }; static int usdf_wait_trywait(struct fid *fwait) { struct usdf_wait *wait; struct dlist_entry *item; struct fid_list_entry *entry; int ret = FI_SUCCESS; wait = wait_fidtou(fwait); dlist_foreach(&wait->list, item) { entry = container_of(item, struct fid_list_entry, entry); switch (entry->fid->fclass) { case FI_CLASS_EQ: continue; case FI_CLASS_CQ: ret = usdf_cq_trywait(entry->fid); if (ret) return ret; break; default: USDF_DBG_SYS(FABRIC, "invalid fid %p\n", entry->fid); return -FI_EINVAL; } } return ret; } int usdf_trywait(struct fid_fabric *fabric, struct fid **fids, int count) { size_t i; int ret; for (i = 0; i < count; i++) { assert(fids[i]); switch (fids[i]->fclass) { case FI_CLASS_EQ: continue; case FI_CLASS_CQ: ret = usdf_cq_trywait(fids[i]); break; case FI_CLASS_WAIT: ret = usdf_wait_trywait(fids[i]); break; default: USDF_DBG_SYS(FABRIC, "invalid fid\n"); return -FI_EINVAL; } if (ret) return ret; } return FI_SUCCESS; } /* Since a domain hasn't been opened at the time of wait object creation, open a * device temporarily to check for the group interrupt capability. */ static int usdf_wait_check_support(struct usdf_fabric *fabric_priv) { struct usd_open_params params = { .flags = UOPF_SKIP_PD_ALLOC, .cmd_fd = -1, .context = NULL }; struct usd_device *dev; int ret; ret = usd_open_with_params(fabric_priv->fab_dev_attrs->uda_devname, ¶ms, &dev); if (ret) { USDF_DBG_SYS(FABRIC, "opening device to check fd support failed.\n"); return ret; } if (!usd_get_cap(dev, USD_CAP_GRP_INTR)) { USDF_WARN_SYS(FABRIC, "FD request invalid.\n"); USDF_WARN_SYS(FABRIC, "group interrupts not supported.\n"); ret = usd_close(dev); if (ret) USDF_WARN_SYS(FABRIC, "closing usd device failed: %s\n", strerror(ret)); return -FI_EOPNOTSUPP; } return usd_close(dev); } /* Non-static because this is exported due to being returned as a callback for * fabric ops. * * Supporting wait objects in the usNIC provider is done using an epoll * context. When fi_wait_open is called an epoll context is created using * epoll_create1. This simplifies multi-CQ support and also allows us to get * around a limitation of the usNIC provider. IB completion channels are opened * on the domain because we have a context associated with the domain. At * fi_wait_open time, we only have access to the fabric. It isn't guaranteed * that a domain has been opened yet. The epoll context approach allows us to * defer creating the completion channel for the CQ until CQ open time. */ int usdf_wait_open(struct fid_fabric *fabric, struct fi_wait_attr *attr, struct fid_wait **waitset) { struct usdf_wait *wait_priv; struct usdf_fabric *fabric_priv; ofi_epoll_t epfd; int ret; USDF_TRACE_SYS(FABRIC, "\n"); switch (attr->wait_obj) { case FI_WAIT_UNSPEC: case FI_WAIT_FD: break; default: USDF_WARN_SYS(FABRIC, "unsupported wait object type\n"); ret = -FI_EINVAL; goto error; } fabric_priv = fab_fidtou(fabric); ret = usdf_wait_check_support(fabric_priv); if (ret) goto error; ret = ofi_epoll_create(&epfd); if (ret) { USDF_WARN_SYS(FABRIC, "failed to create epoll fd[%d]\n", errno); goto error; } USDF_DBG_SYS(FABRIC, "successfully created epoll fd: %d\n", epfd); wait_priv = calloc(1, sizeof(*wait_priv)); if (!wait_priv) { USDF_WARN_SYS(FABRIC, "unable to allocate memory for usdf_wait obj"); ret = -FI_ENOMEM; goto calloc_fail; } wait_priv->wait_fid.fid.fclass = FI_CLASS_WAIT; wait_priv->wait_fid.fid.ops = &usdf_wait_fi_ops; wait_priv->wait_fid.ops = &usdf_wait_ops; wait_priv->wait_fid.fid.context = 0; wait_priv->wait_fabric = fabric_priv; wait_priv->wait_obj = attr->wait_obj; wait_priv->object.epfd = epfd; ofi_atomic_initialize32(&wait_priv->wait_refcnt, 0); fastlock_init(&wait_priv->lock); dlist_init(&wait_priv->list); ofi_atomic_inc32(&wait_priv->wait_fabric->fab_refcnt); *waitset = &wait_priv->wait_fid; return FI_SUCCESS; calloc_fail: ofi_epoll_close(epfd); error: *waitset = NULL; return ret; } /* Close a wait object. Make sure all resources associated with the wait object * have been closed. */ static int usdf_wait_close(struct fid *waitset) { struct usdf_wait *wait_priv; USDF_TRACE_SYS(FABRIC, "\n"); if (!waitset) { USDF_WARN_SYS(FABRIC, "invalid input.\n"); return -FI_EINVAL; } wait_priv = wait_ftou(waitset); if (ofi_atomic_get32(&wait_priv->wait_refcnt) > 0) { USDF_DBG_SYS(FABRIC, "failed to close waitset with non-zero refcnt"); return -FI_EBUSY; } switch (wait_priv->wait_obj) { case FI_WAIT_UNSPEC: case FI_WAIT_FD: ofi_epoll_close(wait_priv->object.epfd); break; default: USDF_WARN_SYS(FABRIC, "unsupported wait object type\n"); return -FI_EINVAL; } ofi_atomic_dec32(&wait_priv->wait_fabric->fab_refcnt); free(wait_priv); return FI_SUCCESS; } static int usdf_wait_wait(struct fid_wait *fwait, int timeout) { struct usdf_wait *wait; void *context; int ret = FI_SUCCESS; int nevents; USDF_TRACE_SYS(FABRIC, "\n"); wait = wait_ftou(fwait); ret = usdf_wait_trywait(&fwait->fid); if (ret) { if (ret == -FI_EAGAIN) return FI_SUCCESS; return ret; } nevents = ofi_epoll_wait(wait->object.epfd, &context, 1, timeout); if (nevents == 0) { ret = -FI_ETIMEDOUT; } else if (nevents < 0) { USDF_DBG_SYS(FABRIC, "epoll wait failed\n"); ret = nevents; } return ret; } static int usdf_wait_get_wait(struct usdf_wait *wait_priv, void *arg) { USDF_TRACE_SYS(FABRIC, "\n"); if (!arg || !wait_priv) { USDF_WARN_SYS(FABRIC, "invalid input\n"); return -FI_EINVAL; } switch (wait_priv->wait_obj) { case FI_WAIT_UNSPEC: case FI_WAIT_FD: #ifdef HAVE_EPOLL *(int *) arg = wait_priv->object.epfd; #else return -FI_ENOSYS; #endif break; default: USDF_DBG_SYS(FABRIC, "unsupported wait type\n"); return -FI_EINVAL; } return FI_SUCCESS; } static int usdf_wait_control(struct fid *fid, int command, void *arg) { struct usdf_wait *wait_priv; USDF_TRACE_SYS(FABRIC, "\n"); wait_priv = container_of(fid, struct usdf_wait, wait_fid.fid); switch (command) { case FI_GETWAIT: break; default: USDF_DBG_SYS(FABRIC, "unsupported control command\n"); return -FI_EINVAL; } return usdf_wait_get_wait(wait_priv, arg); }