sol_uverbs_event.c revision c39526b769298791ff5b0b6c5e761f49aabaeb4e
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
*/
/*
* sol_uverbs_event.c
*
* OFED User Verbs Kernel Async Event funtions
*
*/
#include <sys/file.h>
#include <sys/fcntl.h>
#include <sys/vfs.h>
#include <sys/errno.h>
#include <sys/cred.h>
#include <sys/uio.h>
#include <sys/semaphore.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#include <sys/ib/ibtl/ibvti.h>
#include <sys/ib/clients/of/ofa_solaris.h>
#include <sys/ib/clients/of/sol_ofs/sol_ofs_common.h>
#include <sys/ib/clients/of/ofed_kernel.h>
#include <sys/ib/clients/of/sol_uverbs/sol_uverbs.h>
#include <sys/ib/clients/of/sol_uverbs/sol_uverbs_event.h>
extern char *sol_uverbs_dbg_str;
static void
uverbs_async_event_common(uverbs_uctxt_uobj_t *, uint64_t, uint32_t,
llist_head_t *, uint32_t *);
/*
* Function:
* sol_uverbs_event_file_close
* Input:
* ufile - Pointer to the event ufile
*
* Output:
* None
* Returns:
* Zero on success, else error code.
* Description:
* Called when all kernel references to the event file have been
* removed and the kernel (asynchronous) or user library (completion)
* have closed the file.
*/
void
sol_uverbs_event_file_close(uverbs_ufile_uobj_t *ufile)
{
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"UFILE CLOSE: Is async? %s",
ufile->is_async ? "yes" : "no");
if (!ufile) {
SOL_OFS_DPRINTF_L3(sol_uverbs_dbg_str,
"UFILE CLOSE: Ufile NULL\n");
return;
}
/*
* Remove the user file from the user object table and
* releases appropriate references. The object resources
* are freed when it is no longer referenced.
*
* If sol_ofs_uobj_remove() returns NULL then the obj was already
* removed.
*/
rw_enter(&(ufile->uobj.uo_lock), RW_WRITER);
if (sol_ofs_uobj_remove(&uverbs_ufile_uo_tbl, &ufile->uobj)) {
rw_exit(&(ufile->uobj.uo_lock));
sol_ofs_uobj_deref(&ufile->uobj, uverbs_release_event_file);
} else {
rw_exit(&(ufile->uobj.uo_lock));
}
}
/*
* Function:
* sol_uverbs_event_file_read
* Input:
* ufile - The user file pointer of the event channel.
* uiop - The user I/O pointer in which to place the event.
* cred - Pointer to the callers credentials.
* Output:
* uiop - Upon success the caller's buffer has been updated with
* the event details.
* Returns:
* Zero on success, else error code.
* EAGAIN - No event available and caller is non-
* blocking.
* ERESTART- A signal was received.
* EINVAL - Caller parameter/user buffer invalid.
* EFAULT - Failure copying data to user.
* Description:
* Perfrom a blocking read to retrieve an event (asynchronous or
* completion) for the user file specified. If an event is available it
* is immediately returned. If an event is not available, then the
* caller will block unless the open flags indicate it is a non-
* blocking open. If the caller does block it does so interruptable
* and therefore will return upon an event or receipt of a signal.
*/
/* ARGSUSED */
int
sol_uverbs_event_file_read(uverbs_ufile_uobj_t *ufile, struct uio *uiop,
cred_t *cred)
{
int rc = 0;
uverbs_event_t *evt;
llist_head_t *entry;
int eventsz;
int ioflag;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "event_file_read(%p), "
"ufile = %p, is_async =%d, uio_resid=%d",
ufile, ufile->is_async, uiop->uio_resid);
ioflag = uiop->uio_fmode & (FNONBLOCK | FNDELAY);
mutex_enter(&ufile->lock);
/*
* If Event list not empty and CQ event notification is disabled
* by sol_ucma, do not return events. Either return EAGAIN (if
* flag is O_NONBLOCK, or wait using cv_wait_sig().
*/
if (ufile->ufile_notify_enabled == SOL_UVERBS2UCMA_CQ_NOTIFY_DISABLE &&
llist_empty(&ufile->event_list) != 0) {
if (ioflag) {
mutex_exit(&ufile->lock);
SOL_OFS_DPRINTF_L3(sol_uverbs_dbg_str,
"event_file_read - notify disabled, no block");
return (EAGAIN);
}
if (!cv_wait_sig(&ufile->poll_wait, &ufile->lock)) {
mutex_exit(&ufile->lock);
SOL_OFS_DPRINTF_L3(sol_uverbs_dbg_str,
"event_file_read - sig_wakeup");
return (ERESTART);
}
}
while (llist_empty(&ufile->event_list)) {
if (ioflag) {
mutex_exit(&ufile->lock);
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"event_file_read - no events, no block");
return (EAGAIN);
}
if (!cv_wait_sig(&ufile->poll_wait, &ufile->lock)) {
mutex_exit(&ufile->lock);
SOL_OFS_DPRINTF_L3(sol_uverbs_dbg_str,
"event_file_read - sig_wakeup");
return (ERESTART);
}
}
entry = ufile->event_list.nxt;
evt = entry->ptr;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "event_file_read: "
"Event entry found: entry:%p, event:%p, evt_list %p",
entry, evt, &evt->ev_list);
if (ufile->is_async) {
eventsz = sizeof (struct ib_uverbs_async_event_desc);
} else {
eventsz = sizeof (struct ib_uverbs_comp_event_desc);
}
if (eventsz > uiop->uio_resid) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"event_file_read - Event too big");
rc = EINVAL;
evt = NULL;
} else {
llist_del(ufile->event_list.nxt);
if (evt->ev_counter) {
++(*evt->ev_counter);
llist_del(&evt->ev_obj_list);
}
}
mutex_exit(&ufile->lock);
if (evt && (uiomove(evt, eventsz, UIO_READ, uiop) != 0)) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"EVENT FILE READ: Error writing ev");
rc = EFAULT;
}
if (evt) {
kmem_free(evt, sizeof (*evt));
}
return (rc);
}
/*
* Function:
* sol_uverbs_event_file_poll
* Input:
* ufile - user file for desired completion channel event file
* events - The events that may occur.
* anyyet - A flag that is non-zero if any files in the set
* of descriptors has an event waiting.
* ct - Pointer to the callers context.
* Output:
* reventssp - A pointer updated to return a bitmask of events specified.
* phpp - A pointer to a pollhead pointer, updated to reflect the
* the event file's pollhead used for synchronization.
* Returns:
* Zero on success, else error code.
* EINVAL - Vnode does not point to valid event file.
* Description:
* Support for event channel polling interface, allows use of completion
* channel in asynchronous type environment. If events may be read
* without blocking indicate a POLLIN | POLLRDNORM event; otherwise if
* no other descriptors in the set have data waiting, set the pollhead
* pointer to our the associated completion event file's pollhead.
*/
int
sol_uverbs_event_file_poll(uverbs_ufile_uobj_t *ufile, short events,
int anyyet, short *reventsp, pollhead_t **phpp)
{
short revent = 0;
#ifdef DEBUG
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "event_file_poll(%p, %x)",
ufile, events);
#endif
if (!ufile) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str, "event_file_poll ",
"ufile %p", ufile);
return (EINVAL);
}
#ifdef DEBUG
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "event_file_poll "
"ufile = %p, is_async =%d", ufile, ufile->is_async);
#endif
mutex_enter(&ufile->lock);
/*
* If poll request and event is ready.
*/
if ((events & (POLLIN | POLLRDNORM)) &&
!llist_empty(&ufile->event_list)) {
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "event_file_poll "
"Event entry available");
revent |= POLLIN | POLLRDNORM;
}
/*
* If we didn't get an event
*/
if (revent == 0 && !anyyet) {
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "event_file_poll "
"Event entry NOT available");
*phpp = &ufile->poll_head;
}
mutex_exit(&ufile->lock);
*reventsp = revent;
return (0);
}
/*
* Function:
* uverbs_alloc_event_file
* Input:
* uctxt - The Solaris User Verbs user context associated with the
* event channel.
* is_async - Indicates the file is for asynchronous events if non-zero;
* other wise it is for completion events.
* Output:
* None.
* Returns:
* New user verb event file object or NULL on error.
* Description:
* Allocate an asynchronous or completion event file
*/
uverbs_ufile_uobj_t *
uverbs_alloc_event_file(uverbs_uctxt_uobj_t *uctxt, int is_async)
{
uverbs_ufile_uobj_t *ufile;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "alloc_event_file(%p, %x)",
uctxt, is_async);
ufile = kmem_zalloc(sizeof (*ufile), KM_NOSLEEP);
if (!ufile) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"alloc_event_file: mem alloc fail");
return (NULL);
}
ufile->ufile_notify_enabled = SOL_UVERBS2UCMA_CQ_NOTIFY_ENABLE;
sol_ofs_uobj_init(&ufile->uobj, 0, SOL_UVERBS_UFILE_UOBJ_TYPE);
rw_enter(&ufile->uobj.uo_lock, RW_WRITER);
if (sol_ofs_uobj_add(&uverbs_ufile_uo_tbl, &ufile->uobj) != 0) {
/*
* The initialization routine set's the initial reference,
* we dereference the object here to clean it up.
*/
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"ALLOC UFILE: Object add failed");
rw_exit(&ufile->uobj.uo_lock);
ufile->uobj.uo_uobj_sz = sizeof (uverbs_ufile_uobj_t);
sol_ofs_uobj_deref(&ufile->uobj, sol_ofs_uobj_free);
return (NULL);
}
ufile->is_async = is_async ? 1 : 0;
llist_head_init(&ufile->event_list, NULL);
mutex_init(&ufile->lock, NULL, MUTEX_DRIVER, NULL);
cv_init(&ufile->poll_wait, NULL, CV_DRIVER, NULL);
ufile->uctxt = uctxt;
ufile->uobj.uo_live = 1;
rw_exit(&ufile->uobj.uo_lock);
return (ufile);
free_uobj:
/*
* Need to set uo_live, so sol_ofs_uobj_remove() will
* remove the object from the object table.
*/
ufile->uobj.uo_live = 1;
(void) sol_ofs_uobj_remove(&uverbs_ufile_uo_tbl, &ufile->uobj);
rw_exit(&ufile->uobj.uo_lock);
sol_ofs_uobj_deref(&ufile->uobj, sol_ofs_uobj_free);
return (NULL);
}
/*
* Function:
* uverbs_release_event_file
* Input:
* ufile - Pointer to the ufile user object that is being freed.
* Output:
* None.
* Returns:
* None.
* Description:
* Release/destroy event file resources before freeing memory. This
* routine should only be used if the event file is successfully
* created.
*/
void
uverbs_release_event_file(sol_ofs_uobj_t *uobj)
{
uverbs_ufile_uobj_t *ufile = (uverbs_ufile_uobj_t *)uobj;
uverbs_event_t *evt;
llist_head_t *entry;
llist_head_t *list_tmp;
if (!ufile) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"UFILE RELEASE: Ufile NULL\n");
return;
}
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"UFILE RELEASE: Event file=%p, is async = %s",
ufile, ufile->is_async ? "yes" : "no");
/*
* Release any events still queued to the event file.
*/
mutex_enter(&ufile->lock);
entry = ufile->event_list.nxt;
list_tmp = entry->nxt;
while (entry != &ufile->event_list) {
ASSERT(entry);
evt = (uverbs_event_t *)entry->ptr;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"UFILE RELEASE: Deleting event %p on event file %p",
evt, ufile);
llist_del(&evt->ev_list);
kmem_free(evt, sizeof (*evt));
entry = list_tmp;
list_tmp = entry->nxt;
}
mutex_exit(&ufile->lock);
cv_destroy(&ufile->poll_wait);
mutex_destroy(&ufile->lock);
sol_ofs_uobj_free(uobj);
}
/*
* Function:
* uverbs_ibt_to_ofa_event_code
* Input:
* code - The OFA event code.
* Output:
* The OFED event code.
* Returns:
* Returns the OFA equivalent of an IBT Asynchronous Event code, -1 if
* a valid translation does not exist.
* Description:
* Map an IBT asynchronous event code to an OFED event code.
*/
enum ib_event_type
uverbs_ibt_to_ofa_event_code(ibt_async_code_t code)
{
enum ib_event_type ofa_code;
switch (code) {
case IBT_EVENT_PATH_MIGRATED:
ofa_code = IB_EVENT_PATH_MIG;
break;
case IBT_EVENT_SQD:
ofa_code = IB_EVENT_SQ_DRAINED;
break;
case IBT_EVENT_COM_EST:
break;
case IBT_ERROR_CATASTROPHIC_CHAN:
case IBT_ERROR_LOCAL_CATASTROPHIC:
ofa_code = IB_EVENT_QP_FATAL;
break;
case IBT_ERROR_INVALID_REQUEST_CHAN:
ofa_code = IB_EVENT_QP_REQ_ERR;
break;
case IBT_ERROR_ACCESS_VIOLATION_CHAN:
ofa_code = IB_EVENT_QP_ACCESS_ERR;
break;
case IBT_ERROR_PATH_MIGRATE_REQ:
ofa_code = IB_EVENT_PATH_MIG_ERR;
break;
case IBT_ERROR_CQ:
ofa_code = IB_EVENT_CQ_ERR;
break;
case IBT_EVENT_PORT_UP:
ofa_code = IB_EVENT_PORT_ACTIVE;
break;
case IBT_ERROR_PORT_DOWN:
ofa_code = IB_EVENT_PORT_ERR;
break;
case IBT_HCA_ATTACH_EVENT:
ofa_code = IB_EVENT_CLIENT_REREGISTER;
break;
case IBT_EVENT_LIMIT_REACHED_SRQ:
ofa_code = IB_EVENT_SRQ_LIMIT_REACHED;
break;
case IBT_ERROR_CATASTROPHIC_SRQ:
ofa_code = IB_EVENT_SRQ_ERR;
break;
case IBT_EVENT_EMPTY_CHAN:
ofa_code = IB_EVENT_QP_LAST_WQE_REACHED;
break;
/*
* No mapping exists.
*/
case IBT_ASYNC_OPAQUE1:
case IBT_ASYNC_OPAQUE2:
case IBT_ASYNC_OPAQUE3:
case IBT_ASYNC_OPAQUE4:
case IBT_HCA_DETACH_EVENT:
default:
ofa_code = -1;
}
return (ofa_code);
}
/*
* Function:
* uverbs_async_qp_event_handler
* Input:
* clnt_private - The IBT attach client handle.
* hca_hdl - The IBT hca handle associated with the notification.
* code - The OFED event identifier.
* event - The IBT event.
* Output:
* None
* Returns:
* None
* Description:
* Handle QP affiliated asynchronous event noficiation.
*/
/* ARGSUSED */
void
uverbs_async_qp_event_handler(void *clnt_private, ibt_hca_hdl_t hca_hdl,
enum ib_event_type code, ibt_async_event_t *event)
{
uverbs_uqp_uobj_t *uqp;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"async_qp_event_handler()");
if (event->ev_chan_hdl == NULL) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"async_qp_event_handler: event handle NULL");
return;
}
uqp = ibt_get_qp_private(event->ev_chan_hdl);
ASSERT(uqp);
if (uqp->uqp_free_state == SOL_UVERBS2UCMA_FREE_PENDING) {
SOL_OFS_DPRINTF_L3(sol_uverbs_dbg_str,
"async_qp_event_handler: User QP context has been freed");
return;
}
if (uqp->qp != event->ev_chan_hdl) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"async_qp_event_handler: QP handle mismatch");
return;
}
uverbs_async_event_common(uqp->uctxt, uqp->uobj.uo_user_handle,
code, &uqp->async_list, &uqp->async_events_reported);
}
/*
* Function:
* uverbs_async_cq_event_handler
* Input:
* clnt_private - The IBT attach client handle.
* hca_hdl - The IBT hca handle associated with the notification.
* code - The OFED event identifier.
* event - The IBT event.
* Output:
* None
* Returns:
* None
* Description:
* Handle a CQ affiliated asynchronous event notification.
*/
/* ARGSUSED */
void
uverbs_async_cq_event_handler(void *clnt_private, ibt_hca_hdl_t hca_hdl,
enum ib_event_type code, ibt_async_event_t *event)
{
uverbs_ucq_uobj_t *ucq;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"ASYNC CQ EVENT HANDLER:");
if (event->ev_cq_hdl == NULL) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"ASYNC CQ EVENT HANDLER: event handle is NULL");
return;
}
ucq = ibt_get_cq_private(event->ev_cq_hdl);
if (ucq->cq != event->ev_cq_hdl) {
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"ASYNC CQ EVENT HANDLER: CQ handle mismatch");
return;
}
uverbs_async_event_common(ucq->uctxt, ucq->uobj.uo_user_handle,
code, &ucq->async_list, &ucq->async_events_reported);
}
/*
* Function:
* uverbs_async_srq_event_handler
* Input:
* clnt_private - The IBT attach client handle.
* hca_hdl - The IBT hca handle associated with the notification.
* code - The OFED event identifier.
* event - The IBT event.
* Output:
* None
* Returns:
* None
* Description:
* Handle a shared receive queue asynchronous event notification.
*/
/* ARGSUSED */
void
uverbs_async_srq_event_handler(void *clnt_private, ibt_hca_hdl_t hca_hdl,
enum ib_event_type code, ibt_async_event_t *event)
{
uverbs_usrq_uobj_t *usrq;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"ASYNC SRQ EVENT HANDLER:");
if (event->ev_srq_hdl == NULL) {
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"ASYNC SRQ EVENT HANDLER: event handle is NULL");
return;
}
usrq = ibt_get_srq_private(event->ev_srq_hdl);
if (usrq->srq != event->ev_srq_hdl) {
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"ASYNC SRQ EVENT HANDLER: SRQ handle mismatch");
return;
}
uverbs_async_event_common(usrq->uctxt, usrq->uobj.uo_user_handle,
code, &usrq->async_list, &usrq->async_events_reported);
}
/*
* Function:
* uverbs_async_unaff_event_handler
* Input:
* clnt_private - The IBT attach client handle.
* hca_hdl - The IBT hca handle associated with the notification.
* code - The OFED event identifier.
* event - The IBT event.
* Output:
* None
* Returns:
* None
* Description:
* Handle an unaffiliated asynchronous event notification.
*/
/* ARGSUSED */
void
uverbs_async_unaff_event_handler(void *clnt_private, ibt_hca_hdl_t hca_hdl,
enum ib_event_type code, ibt_async_event_t *event)
{
sol_ofs_uobj_table_t *uo_tbl = &uverbs_uctxt_uo_tbl;
sol_ofs_uobj_blk_t *blk;
uverbs_uctxt_uobj_t *uctxt;
int i, j;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"ASYNC UNAFF EVENT HANDLER:");
/*
* Unaffiliated events are returned at the IBT client level. We must
* return the event to all user context allocated for the specific
* HCA device specified.
*/
rw_enter(&uo_tbl->uobj_tbl_lock, RW_READER);
for (i = 0; i < uo_tbl->uobj_tbl_used_blks; i++) {
blk = uo_tbl->uobj_tbl_uo_root[i];
if (blk == NULL) {
continue;
}
for (j = 0; j < SOL_OFS_UO_BLKSZ; j++) {
uctxt = (uverbs_uctxt_uobj_t *)blk->ofs_uoblk_blks[j];
if (uctxt == NULL) {
continue;
}
/*
* OK, check to see if this user context belongs
* to the idicated hca.
*/
if (uctxt->hca->hdl == hca_hdl && uctxt->async_evfile) {
uverbs_async_event_common(uctxt,
event->ev_port, code, NULL, NULL);
}
}
}
rw_exit(&uo_tbl->uobj_tbl_lock);
}
/*
* Function:
* uverbs_async_event_handler
* Input:
* clnt_private - The IBT attach client handle.
* hca_hdl - The IBT hca handle associated with the notification.
* code - The OFED event identifier.
* event - The IBT event.
* Output:
* None
* Returns:
* None
* Description:
* Main IBT asynchronous event handler registered at ibt_attach.
* Convert to OFA event type and forward to the appropriate
* asynchronous handler.
*/
void
uverbs_async_event_handler(void *clnt_private, ibt_hca_hdl_t hca_hdl,
ibt_async_code_t code, ibt_async_event_t *event)
{
enum ib_event_type ofa_type;
sol_uverbs_ib_event_handler_t *handler;
llist_head_t *entry;
sol_uverbs_hca_t *hca;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"ASYNNC EVENT HANDLER: entry, code=%d", code);
ofa_type = uverbs_ibt_to_ofa_event_code(code);
if ((int)ofa_type < 0) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str,
"ASYNC EVENT HANDLER:Event %d did not map to OFA "
"Event", code);
return;
}
switch (ofa_type) {
case IB_EVENT_QP_FATAL:
case IB_EVENT_QP_REQ_ERR:
case IB_EVENT_QP_ACCESS_ERR:
case IB_EVENT_QP_LAST_WQE_REACHED:
case IB_EVENT_SQ_DRAINED:
case IB_EVENT_PATH_MIG:
/*
* These events are related with a QP
*/
uverbs_async_qp_event_handler(clnt_private, hca_hdl,
ofa_type, event);
break;
case IB_EVENT_CQ_ERR:
/*
* These events are related with a CQ
*/
uverbs_async_cq_event_handler(clnt_private, hca_hdl,
ofa_type, event);
break;
case IB_EVENT_SRQ_ERR:
case IB_EVENT_SRQ_LIMIT_REACHED:
/*
* These events are related with a SRQ
*/
uverbs_async_srq_event_handler(clnt_private, hca_hdl,
ofa_type, event);
break;
case IB_EVENT_PORT_ERR:
case IB_EVENT_PORT_ACTIVE:
case IB_EVENT_LID_CHANGE:
case IB_EVENT_PKEY_CHANGE:
case IB_EVENT_SM_CHANGE:
case IB_EVENT_CLIENT_REREGISTER:
case IB_EVENT_DEVICE_FATAL:
case IB_EVENT_PATH_MIG_ERR:
/*
* Unaffiliated asynchronous notifications.
*/
uverbs_async_unaff_event_handler(clnt_private, hca_hdl,
ofa_type, event);
break;
default:
break;
}
/*
* Give other kernel agents a notification.
*/
hca = sol_uverbs_ibt_hdl_to_hca(hca_hdl);
if (hca) {
mutex_enter(&hca->event_handler_lock);
list_for_each(entry, &hca->event_handler_list) {
handler = (sol_uverbs_ib_event_handler_t *)entry->ptr;
ASSERT(handler != NULL);
handler->handler(handler, hca_hdl, code, event);
}
mutex_exit(&hca->event_handler_lock);
}
}
/*
* Function:
* uverbs_async_event_common
* Input:
* uctxt - Pointer to the user context associated with the
* affiliated event.
* element - The users handle to the associated object.
* event - The event type.
* uobj_list - The list to enqueue the asynchronous event.
* counter - The counter to track the event delivery.
* Output:
* None
* Returns:
* None
* Description:
* Create an asyncronous event and enqueue it on the specified list.
* Then wake callers that may be blocked on the list.
*/
static void
uverbs_async_event_common(uverbs_uctxt_uobj_t *uctxt, uint64_t element,
uint32_t event, llist_head_t *obj_list, uint32_t *counter)
{
uverbs_ufile_uobj_t *ufile = uctxt->async_evfile;
uverbs_event_t *entry;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "async_event_common(%p, "
"%llx, %llx, %p, %p)", uctxt, element, event, obj_list, counter);
if (!ufile) {
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str, "async_event_common "
"ufile %p", ufile);
return;
}
mutex_enter(&ufile->lock);
entry = kmem_zalloc(sizeof (*entry), KM_NOSLEEP);
if (!entry) {
mutex_exit(&ufile->lock);
SOL_OFS_DPRINTF_L2(sol_uverbs_dbg_str, "async_event_common "
"kmem_zalloc failed");
return;
}
entry->ev_desc.async.element = element;
entry->ev_desc.async.event_type = event;
entry->ev_counter = counter;
llist_head_init(&entry->ev_list, entry);
llist_head_init(&entry->ev_obj_list, entry);
llist_add_tail(&entry->ev_list, &ufile->event_list);
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "async_event_common "
"adding ASYNC entry-ev_list=%p, entry %p",
&entry->ev_list, entry);
if (obj_list) {
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "async_event_common "
"adding ASYNC entry-ev_obj_list=%p, entry=%p",
&entry->ev_obj_list, entry);
llist_add_tail(&entry->ev_obj_list, obj_list);
}
mutex_exit(&ufile->lock);
cv_signal(&ufile->poll_wait);
pollwakeup(&ufile->poll_head, POLLIN | POLLRDNORM);
}
/*
* Function:
* uverbs_release_ucq_channel
* Input:
* uctxt - A pointer to the callers user context.
* ufile - A pointer to the event file associated with a CQ.
* ucq - A pointer to the user CQ object.
* Output:
* None
* Returns:
* None
* Description:
* Release any completion and asynchronous events that may
* be queued to the specified completion channel/UCQ but not
* yet reaped.
*/
void
uverbs_release_ucq_channel(uverbs_uctxt_uobj_t *uctxt,
uverbs_ufile_uobj_t *ufile, uverbs_ucq_uobj_t *ucq)
{
uverbs_event_t *evt;
llist_head_t *entry;
llist_head_t *list_tmp;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE UCQ CHANNEL: uctxt=%p, ufile=%p, ucq=%p",
uctxt, ufile, ucq);
/*
* Release completion events that have been queued on the CQ completion
* eventlist.
*/
if (ufile) {
rw_enter(&ufile->uobj.uo_lock, RW_WRITER);
ufile->ufile_cq_cnt--;
if (ufile->ufile_cq_cnt) {
rw_exit(&ufile->uobj.uo_lock);
uverbs_release_ucq_uevents(ufile, ucq);
return;
}
rw_exit(&ufile->uobj.uo_lock);
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"release_ucq_chan : comp_list %p, prv %p, nxt %p",
&ucq->comp_list, ucq->comp_list.prv,
ucq->comp_list.nxt);
mutex_enter(&ufile->lock);
entry = ucq->comp_list.nxt;
list_tmp = entry->nxt;
while (entry != &ucq->comp_list) {
ASSERT(entry);
evt = (uverbs_event_t *)entry->ptr;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE UCQ CHANNEL:Deleting event "
"on CQ comp list: %p", evt);
llist_del(&evt->ev_list);
llist_del(&evt->ev_obj_list);
kmem_free(evt, sizeof (*evt));
entry = list_tmp;
list_tmp = entry->nxt;
}
mutex_exit(&ufile->lock);
uverbs_release_ucq_uevents(ufile, ucq);
}
}
/*
* Function:
* uverbs_release_ucq_uevents
* Input:
* ufile - A pointer to the asynchronous event file associated with a QP.
* ucq - A pointer to the user CQ object.
* Output:
* None
* Returns:
* None
* Description:
* Free any user asynchronous events that have been queued for the
* user CQ object specified.
*/
void
uverbs_release_ucq_uevents(uverbs_ufile_uobj_t *ufile, uverbs_ucq_uobj_t *ucq)
{
uverbs_event_t *evt;
llist_head_t *entry;
llist_head_t *list_tmp;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE UCQ ASYNC EVENTS: ufile=%p, ucq=%p", ufile, ucq);
if (ufile) {
mutex_enter(&ufile->lock);
entry = ucq->async_list.nxt;
list_tmp = entry->nxt;
while (entry != &ucq->async_list) {
ASSERT(entry);
evt = (uverbs_event_t *)entry->ptr;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE UCQ EVENTS: Deleting event "
"on CQ async list: %p", evt);
llist_del(&evt->ev_list);
llist_del(&evt->ev_obj_list);
kmem_free(evt, sizeof (*evt));
entry = list_tmp;
list_tmp = entry->nxt;
}
mutex_exit(&ufile->lock);
}
}
/*
* Function:
* uverbs_release_uqp_uevents
* Input:
* ufile - A pointer to the asynchronous event file associated with a QP.
* uqp - A pointer to the user QP object.
* Output:
* None
* Returns:
* None
* Description:
* Free any user asynchronous events that have been queued for the
* user QP object specified.
*/
void
uverbs_release_uqp_uevents(uverbs_ufile_uobj_t *ufile, uverbs_uqp_uobj_t *uqp)
{
uverbs_event_t *evt;
llist_head_t *entry;
llist_head_t *list_tmp;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE UQP EVENTS: ufile=%p, uqp=%p", ufile, uqp);
if (ufile) {
mutex_enter(&ufile->lock);
entry = uqp->async_list.nxt;
list_tmp = entry->nxt;
while (entry != &uqp->async_list) {
ASSERT(entry);
evt = (uverbs_event_t *)entry->ptr;
ASSERT(evt);
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE UQP EVENTS: Deleting event "
"ON qp async list: %p", evt);
llist_del(&evt->ev_list);
llist_del(&evt->ev_obj_list);
kmem_free(evt, sizeof (*evt));
entry = list_tmp;
list_tmp = entry->nxt;
}
mutex_exit(&ufile->lock);
}
}
/*
* Function:
* uverbs_release_usrq_uevents
* Input:
* ufile - A pointer to the asynchronous event file associated with a
* SRQ.
* uqp - A pointer to the user SRQ object.
* Output:
* None
* Returns:
* None
* Description:
* Free any user asynchronous events that have been queued for the
* user SRQ object specified.
*/
void
uverbs_release_usrq_uevents(uverbs_ufile_uobj_t *ufile,
uverbs_usrq_uobj_t *usrq)
{
uverbs_event_t *evt;
llist_head_t *entry;
llist_head_t *list_tmp;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE USRQ EVENTS: ufile=%p, usrq=%p", ufile, usrq);
if (ufile) {
mutex_enter(&ufile->lock);
entry = usrq->async_list.nxt;
list_tmp = entry->nxt;
while (entry != &usrq->async_list) {
ASSERT(entry);
evt = (uverbs_event_t *)entry->ptr;
SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str,
"RELEASE SRQ EVENTS: Deleting event "
"on SRQ async list: %p", evt);
llist_del(&evt->ev_list);
llist_del(&evt->ev_obj_list);
kmem_free(evt, sizeof (*evt));
entry = list_tmp;
list_tmp = entry->nxt;
}
mutex_exit(&ufile->lock);
}
}