/*
* 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
* 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
*/
/*
*/
/*
*
* Solaris OFED User Verbs kernel agent module
*
*/
#include <sys/semaphore.h>
static void *statep;
void **resultp);
.cb_strategy = nodev,
};
.devo_refcnt = 0,
.devo_identify = nulldev,
.devo_probe = nulldev,
.devo_reset = nodev,
.devo_bus_ops = NULL,
.devo_power = nodev,
};
.drv_modops = &mod_driverops,
.drv_linkinfo = "Solaris User Verbs driver",
};
.ml_linkage = {
[0] = &modldrv,
[1] = NULL,
}
};
/*
* User Object Tables for management of user resources. The tables are driver
* wide, but each user context maintains a list of the objects it has created
* that is used in cleanup.
*/
static void sol_uverbs_user_objects_init(void);
static void sol_uverbs_user_objects_fini(void);
/*
* Open Fabric User Verbs API, command table. See ib_user_verbs.h for
* definitions.
*/
/* TODO - XRC */
};
/*
* Function:
* sol_uverbs_hca_open
* Input:
* mod_ctxt - Pointer to the user verbs module context.
* Output:
* None
* Returns:
* Zero on success, else error code.
* Description:
* Register as a client with the IBT framework and open all of the
* HCA's present.
*/
static int
{
int status;
int hca_ndx;
#ifdef DEBUG
#endif
if (status != IBT_SUCCESS) {
"hca_open:ibt_attach fail %d", status);
goto out_err;
}
if (sol_uverbs_ib_clntp == NULL)
"hca_open: Zero HCAs on this system!");
goto out_err;
}
"hca_open: HCA count %d exceeds max %d",
goto out_err;
}
sizeof (sol_uverbs_hca_t), KM_SLEEP);
/*
* Note: we open these in the reverse order of the guid list, although
* this is technically not required it is done this way so that the
* mapping will be in same order as the interfaces. Also note, that we
* provide a guid property, and the guid should be used to map a verbs
* device to an interface (i.e. don't depend on the order).
*/
if (status != IBT_SUCCESS) {
"hca_open: ibt_open_hca() returned %d",
status);
goto out_err;
}
MUTEX_DRIVER, NULL);
NULL);
MUTEX_DRIVER, NULL);
NULL);
/*
* Get a cached copy of the HCA's attributes for easy access.
*/
if (status != IBT_SUCCESS) {
"hca_open: ibt_query_hca() failed "
"(status=%d)", status);
goto out_err;
}
/* Note : GUID is in host order here */
"hca_open: HCA index %d, HCA GUID: 0x%016llX",
}
#ifdef DEBUG
"HCA list: entry: %p, handle: %p, "
}
#endif
return (0);
/*
* Note, cleanup of hca list and associated resources is done via
* uverbs_hca_close called outside this routine in the case of bad
* status.
*/
return (status);
}
/*
* Function:
* sol_uverbs_hca_close
* Input:
* mod_ctxt - Pointer to the module context.
* Output:
* None
* Returns:
* None
* Description:
* Close all of the IBT HCAs opened by the driver and detach from
* the IBT framework.
*/
static void
{
int hca_ndx;
(void) ibt_close_hca(mod_ctxt->
}
}
}
}
}
}
/*
* Function:
* _init
* Input:
* None
* Output:
* None
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* Perform Solaris OFED user verbs kernel agent driver initialization.
*/
int
_init(void)
{
int error;
sizeof (uverbs_module_context_t), 0);
if (error != 0) {
return (error);
}
if (sol_uverbs_common_hca_init()) {
"uverbs_hca_init() failed");
return (ENODEV);
}
if (error != 0) {
"uverbs: mod_install failed!!");
}
return (error);
}
/*
* Function:
* _info
* Input:
* modinfop - Pointer to an opqque modinfo structure.
* Output:
* modinfop - Updated structure.
* Returns:
* The mod_info() return code.
* Description:
* Return information about the loadable module via the mod_info()
* kernel function call.
*/
int
{
}
/*
* Function:
* _fini
* Input:
* None
* Output:
* None
* Returns:
* DDI_SUCCESS on success, else error code returned by
* mod_remove kernel function.
* Description:
* Perform Solaris OFED user verbs kernel agent driver cleanup.
*/
int
_fini(void)
{
int rc;
if (!rc) {
}
return (rc);
}
int
{
switch (cmd) {
case DDI_ATTACH:
break;
case DDI_RESUME:
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
/*
* Allocate a soft data structure based on this dev info
*/
if (instance != 0) {
"attach: bad instance number %d", instance);
return (DDI_FAILURE);
}
"attach: bad state zalloc");
return (DDI_FAILURE);
}
"attach: cannot get soft state");
return (DDI_FAILURE);
}
/*
* Save off our private context in the dev_info
*/
/*
* Opening of the hca will perform the ibt_attach and build a list of
* devices.
*/
if (rc) {
"attach: sol_uverbs_hca_open() (rc=%d)", rc);
goto error;
}
/*
* Export our ABI revision as a property.
*/
if (rc != DDI_SUCCESS) {
"attach: could not add abi-version property");
}
/*
* Create the filesystem device node for each HCA.
*/
DDI_PSEUDO, 0);
if (rc != DDI_SUCCESS) {
"attach: could not add character node");
goto error;
}
if (rc != DDI_SUCCESS) {
"attach: could not add GUID property");
}
if (rc != DDI_SUCCESS) {
"attach: could not add vendor-id property");
}
if (rc != DDI_SUCCESS) {
"attach: could not add device-id property");
}
}
if (rc != DDI_SUCCESS) {
"attach: could not add minor for ucma");
goto error;
}
if (rc != DDI_SUCCESS) {
"attach: could not add minor for events");
goto error;
}
return (DDI_SUCCESS);
/*
* Cleanup any resources and dettach.
*/
return (rc);
}
/*
* Function:
* sol_uverbs_detach
* Input:
* dip - A pointer to the devices dev_info_t structure.
* cmd - Type of detach (DDI_DETACH or DDI_SUSPEND).
* Output:
* None
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* Detaches thea driver module and will cause the driver to close
* the underlying IBT HCA and detach from the IBT driver. Note
* that this call will fail if user verb consumers or ucma have a
* sol_uverbs device open.
*/
static int
{
int instance;
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
if (instance != 0) {
"detach: bad instance number 0x%x", instance);
return (DDI_FAILURE);
}
if (uverbs_uctxt_uo_tbl.uobj_tbl_uo_cnt > 0) {
"detach(): device in use");
return (DDI_FAILURE);
}
/*
* Sanity check, do not detach if other kernel agents
* are still using sol_uverbs IBT handles.
*/
if (!llist_empty(&sol_uverbs_client_list)) {
"detach: agents still registered");
return (DDI_FAILURE);
}
/*
* Hca close will perform the detach from IBTF.
*/
return (DDI_SUCCESS);
}
/*
* Function:
* sol_uverbs_getinfo
* Input:
* dip - Deprecated, do not use.
* cmd - Command argument (DDI_INFO_DEVT2DEVINFO or
* DDI_INFO_DEVT2INSTANCE).
* arg - Command specific argument.
* resultp - Pointer to place results.
* Output:
* resultp - Location is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* Depending on the request (cmd) return either the dev_info_t pointer
* associated with the dev_info_t specified, or the instance. Note
* that we have only a single instance.
*/
/* ARGSUSED */
static int
void **resultp)
{
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
if (!mod_ctxt) {
return (DDI_FAILURE);
}
return (DDI_SUCCESS);
case DDI_INFO_DEVT2INSTANCE:
*resultp = 0;
return (DDI_SUCCESS);
default:
return (DDI_FAILURE);
}
}
/*
* Function:
* sol_uverbs_prop_op
* Input:
* dev - The device number associated with this device.
* dip - A pointer to the device information structure for this device.
* prop_op - Property operator (PROP_LEN, PROP_LEN_AND_VAL_BUF, or
* PROP_LEN_AND_VAL_ALLOC).
* flags - Only possible flag value is DDI_PROP_DONTPASS.
* name - Pointer to the property to be interrogated.
* valuep - Address of pointer if ALLOC, otherwise a pointer to the
* users buffer.
* lengthp - Pointer to update with property length.
* Output:
* valuep - Updated with the property value.
* lenghtp - Updated with the property length.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* Driver entry point to report the values of certain properties of the
* driver or device.
*/
static int
{
}
/*
* Function:
* sol_uverbs_open
* Input:
* devp - A pointer to the device number.
* flag - Flags specified by caller (FEXCL, FNDELAY, FREAD, FWRITE).
* otyp - Open type (OTYP_BLK, OTYP_CHR, OTYP_LYR).
* cred - Pointer to the callers credentials.
* Output:
* devp - On success devp has been cloned to point to a unique minor
* device.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* Handles a user process open of a specific user verbs minor device by
* allocating a user context user object and creating a unique device
* to identify the user. Note: The first SOL_UVERBS_DRIVER_MAX_MINOR
* minor numbers are reserved for :
* 0 to SOL_UVERBS_DRIVER_MAX_HCA_MINOR - 1 : actual HCA devices
* SOL_UVERBS_DRIVER_MAX_HCA_MINOR : UCMA node
* SOL_UVERBS_DRIVER_EVENT_MINOR :
* Event file for opening an event file for completion
* or async notifications.
*/
/* ARGSUSED */
static int
{
int minor;
/* Char only */
"open: not CHR");
return (EINVAL);
}
"open: get soft state failed");
return (ENXIO);
}
/*
* Special case of ucma module.
*/
if (minor == SOL_UVERBS_DRIVER_MAX_HCA_MINOR) {
"open: ucma_open");
"open: ucma_open non-kernel context");
return (ENOTSUP);
}
return (DDI_SUCCESS);
}
/*
* If this is not an open for sol_uverbs event file,
* A device minor number must be less than the user verb max
* minor device number and the HCA count.
*/
if (minor != SOL_UVERBS_DRIVER_EVENT_MINOR &&
"open: bad minor %d", minor);
return (ENODEV);
}
/*
* Allocate a user context and return a unique ID that can be used
* in identify the new user context object. Create a clone device
* that uses this unique ID as the minor number. Allocation of the
* user context object places one reference against it; which will
* be held until the device is closed.
*
* sol_uverbs_alloc_uctxt() returns a sucessful allocation of uctx
* with the uobj uo_lock held for WRITTER.
*/
if (!uctxt) {
"open: user context alloc failed");
return (ENODEV);
}
/*
* Indicate the object is alive and release the user object write lock
* which was placed on the user context at allocation.
*/
return (DDI_SUCCESS);
}
/*
* Function:
* sol_uverbs_close
* Input:
* dev - Device number.
* flag - File status flag.
* otyp - Open type.
* cred - A pointer to the callers credientials.
* Output:
* None
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* Handles a user process close of a specific user verbs minor device by
* freeing any user objects this process may still have allocated and
* deleting the associated user context object.
*/
/* ARGSUSED */
static int
{
int rc;
/*
* HCA specific device nodes created during attach are been
* closed. Return SUCCESS.
*/
if (id < SOL_UVERBS_DRIVER_MAX_MINOR) {
"uverbs_close: dev_t %x, minor %x < %x",
return (0);
}
/*
* Must be a user or kernel open, i.e. not a minor node that
* that represents a user verbs device. If it is the UCMA
* nothing needs to be done.
*/
if (id == SOL_UVERBS_DRIVER_MAX_HCA_MINOR) {
"uverbs_close: ucma close");
return (DDI_SUCCESS);
}
"uverbs_close: Unknown user context");
return (ENXIO);
}
"uctxt %p", uctxt);
/*
* Remove from the user context resource table, cleanup all
* user resources that may still be hanging around.
*/
/*
* It was already removed, drop the lock held from
* get above and exit.
*/
return (ENXIO);
}
"uverbs_close: Async or Compl user context");
/*
* Verbs uctxt has already been freed, just return.
*/
if (!uctxt->uctxt_verbs_id) {
return (0);
}
/*
* Verbs uctxt has not been freed. Close the ufile. This
* also frees the ufile if reference count is 0.
*/
if (verbs_uctxt &&
} else if (uctxt->comp_evfile) {
}
if (verbs_uctxt)
return (0);
return (0);
}
/*
* Release resources that may still be held by this user context.
* Remove the resources from the associated resource managment
* table and free it.
*/
while (entry) {
}
while (entry) {
/* Free unreaped asynchronous events. */
/*
* If ucma has disabled QP free for this QP, set the
* uqp_free_state to FREE_PENDING. Free QP if not.
*/
} else {
if (rc)
"uqp_free(%p) failed", uqp);
}
}
while (entry) {
/* Free events associated with the CQ. */
if (ucq->active_qp_cnt) {
} else {
if (rc)
"ucq_free(%p) failed", ucq);
}
}
while (entry) {
/* Free unreaped asynchronous events. */
if (usrq->active_qp_cnt) {
} else {
if (rc)
"usrq_free(%p) failed", usrq);
}
}
while (entry) {
}
while (entry) {
if (upd->active_qp_cnt) {
} else {
if (rc)
"upd_free(%p) failed", upd);
}
}
/*
* Release the user file structure to the async file if it
* has not be released yet. The uctxt for async file will
* be closed when the async file is closed.
*/
if (uctxt->async_evfile) {
if (!async_uctxt) {
"uverbs_close: Invalid async_id %x",
return (ENXIO);
}
async_uctxt->uctxt_verbs_id = 0;
}
/*
* Release the write lock and the reference from the get above, and
* release the reference placed on the user context as process open
* to release context.
*/
/*
* If some QPs have not been freed, donot free the uctxt.
* Set uctxt_free_pending flag. This will be freed when
* the QP will be freed.
*/
"close: uctxt %p, has pending uqp", uctxt);
return (0);
}
"close: deallocated user context: %p, ref = %d",
return (0);
}
/*
* Function:
* sol_uverbs_read
* Input:
* dev - Device number.
* uiop - Pointer to the uio structgure where data is to be stored.
* credp - A pointer to the credentials for the I/O transaction.
* Output:
* None
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User process read stub.
*/
static int
{
int rc;
"uverbs_read: Failed get user context");
return (ENXIO);
}
"uverbs_read: Invalid Verbs user context id, %x",
return (ENXIO);
}
if (verbs_uctxt == NULL) {
"uverbs_read: Failed get verbs user context");
return (ENXIO);
}
} else {
"uverbs_read: invalid user context type %x",
uctxt->uctxt_type);
}
return (rc);
}
/*
* Function:
* sol_uverbs_mmap
* Input:
* dev - Device whose memory is to be mapped.
* sol_uverbs_mmap - Offset within the device memory at which mapping
* begins.
* prot - Bitmask specifying protection.
* Output:
* None
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User process mmap stub. Mmap operations are performed directly
* by the underlying IB HCA driver, bypassing the user verbs.
*/
/* ARGSUSED */
static int
{
"sol_uverbs_mmap(%d)- not yet used", mmap_offset);
return (DDI_SUCCESS);
}
/*
* Function:
* sol_uverbs_get_context
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to return the unique user context to the process
* that opened the associated user verb driver instance. Note that upon
* entry a reference will have already been placed on the user
* context user space object, so an additional reference is not
* required here.
*/
int
int out_len)
{
int rc;
"uverbs_get_context() - buf %p, sizeof (cmd) %d",
#ifdef _LP64
#else
#endif
/*
* libibverbs will have passed minor of the async file in
* resp.fd. Use this to determine the uctxt created for
* asyncs.
*/
#ifdef _LP64
#else
#endif
if (rc != 0) {
"get_context: copyin (rc=%d)", rc);
goto out;
}
if (async_id < SOL_UVERBS_DRIVER_MAX_MINOR) {
"get_context: Invalid async user context "
"id %x", async_id);
return (ENXIO);
}
if (async_uctxt == NULL) {
"get_context: Failed get async user context");
return (ENXIO);
}
async_uctxt->uctxt_verbs_id != 0) {
"get_context: Invalid user context - "
"possibly reused");
return (ENXIO);
}
"get_context: uctxt %p, async_uctxt %p, async_id %x",
if (!uctxt->async_evfile) {
"get_context: async event file allocation failed");
goto out;
}
#ifdef _LP64
#else
#endif
if (rc != 0) {
"get_context: copyout (rc=%d)", rc);
goto out;
}
/*
* This unfortunately is Mellanox specific, we need to consider moving
* this directly into the command response as opaque data, instead of
* using this method.
*/
if (rc != 0) {
"get_context: copyout outbuf (rc=%d)", rc);
goto out;
}
rc = DDI_SUCCESS;
out:
return (rc);
}
/*
* Function:
* sol_uverbs_alloc_pd
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing a alloc PD command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to allocate a device protection domain.
*/
/* ARGSUSED */
int
int out_len)
{
int rc;
goto out;
}
if (rc != IBT_SUCCESS) {
"alloc_pd: ibt_alloc_pd() (rc=%d)", rc);
goto alloc_err;
}
"alloc_pd: User object add failed");
goto err_add_uobj;
}
/*
* Query underlying hardware driver for data that may be required
* when using the PD in an OS Bypass creation of UD address vectors.
*/
if (rc != IBT_SUCCESS) {
"alloc_pd: ibt_ci_data_out() (rc=%d)", rc);
goto err_response;
}
#ifdef _LP64
#else
#endif
if (rc != 0) {
"alloc_pd: copyout fail (rc=%d)", rc);
goto err_response;
}
if (!upd->list_entry) {
"alloc_pd: Error adding upd to pd_list\n");
goto err_response;
}
return (DDI_SUCCESS);
/*
* Need to set uo_live, so sol_ofs_uobj_remove() will
* remove the object from the object table.
*/
out:
"alloc_pd:error (rc=%d)", rc);
return (rc);
}
int
{
int rc;
if (rc != IBT_SUCCESS) {
"uverbs_upd_free: ibt_free_pd() failed %d", rc);
return (rc);
}
/*
* Remove from the list of this contexts PD resources, then remove from
* the resource managment table and the reference placed on the user
* object at PD allocation.
*/
if (upd->list_entry) {
}
/*
* list_entry is NULL when called from sol_uverbs_close. Remove
* from upd_uo_tbl and free upd, when called from close also.
*/
return (0);
}
/*
* Function:
* sol_uverbs_dealloc_pd
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing dealloc PD command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to de-allocate a device protection domain.
*/
/* ARGSUSED */
int
int out_len)
{
int rc = 0;
goto err_out1;
}
if (upd->active_qp_cnt) {
} else {
}
return (rc);
return (rc);
}
/*
* Function:
* sol_uverbs_query_device
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing query device command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to query device attributes.
*/
/* ARGSUSED */
int
int out_len)
{
int rc;
if (rc != IBT_SUCCESS) {
"query_device: ibt_query_hca() (rc=%d)", rc);
goto out;
}
/*
* NOTE: node guid and system image guid must be returned in big
* endian (network order). On solaris these are in host
* order, so we swap it back here.
*/
resp.max_ee_rd_atom = 0;
resp.max_ee_init_rd_atom = 0;
} else {
}
resp.max_map_per_fmr = 0;
#ifdef _LP64
#else
#endif
if (rc != 0) {
"query_device: Error writing resp data (rc=%d)", rc);
goto out;
}
rc = DDI_SUCCESS;
out:
return (rc);
}
/*
* Function:
* sol_uverbs_query_port
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing query port command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to query a device port attributes.
*/
/* ARGSUSED */
int
int out_len)
{
int rc;
"query_port: Invalid port specified");
goto out;
}
if (rc != IBT_SUCCESS) {
"query_port: ibt_query_hca_ports() (rc=%d)", rc);
goto out;
}
"port_num %d, port_info %x, lid %x, sm_lid %x",
#ifdef _LP64
#else
#endif
if (rc != 0) {
"query_port : copyout fail %x", rc);
goto out;
}
rc = DDI_SUCCESS;
out:
return (rc);
}
/*
* Function:
* sol_uverbs_query_gid
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing query gid command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to query the device gid for the specified
* port and gid index.
*/
/* ARGSUSED */
int
int out_len)
{
int rc;
"query_gid() : port_num %x, gid_index %x",
"query_gid: Invalid port specified");
goto out;
}
if (rc != IBT_SUCCESS) {
"query_gid: ibt_query_hca_ports() (rc=%d)", rc);
goto out;
}
"query_gid: cmd gid_index %x > port_info sz %x",
goto out;
}
/*
* The gid must be returned as a network ordered byte array, on solaris
* it is a structure in host order so we swap the components as needed.
*/
"0x%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:"
"%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x:",
#ifdef _LP64
#else
#endif
if (rc != 0) {
"query_gid: copyout %d", rc);
goto out;
}
rc = DDI_SUCCESS;
out:
return (rc);
}
/*
* Function:
* sol_uverbs_query_pkey
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing a query pkey command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to query a device for the pkey at the specified
* port and pkey index.
*/
/* ARGSUSED */
int
int out_len)
{
int rc;
"query_pkey: entry, port = %d, pkey index = %d",
"query_pkey: Invalid port specified");
goto out;
}
if (rc != IBT_SUCCESS) {
"query_pkey: ibt_query_hca_ports() %d", rc);
goto out;
}
"query_pkey: port %d, requested index %d, number of pkey entries "
"query_pkey: Invalid index %d, table size = %d",
goto out;
}
#ifdef _LP64
#else
#endif
if (rc != 0) {
"query_pkey: copyout %d", rc);
goto out;
}
rc = DDI_SUCCESS;
out:
return (rc);
}
/*
* Function:
* sol_uverbs_reg_mr
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to register a memory region.
*/
/* ARGSUSED */
int
int out_len)
{
int rc;
"mr_vaddr 0x%0lX, mr_len %d, mr_as %d, mr_flags %d",
}
}
}
}
}
}
"reg_mr: User object mem allocation error");
goto out;
}
"reg_mr: PD invalid");
goto bad_pd;
}
&new_mr_desc);
if (rc != IBT_SUCCESS) {
"reg_mr: ibt_register_mr() (rc=%d)", rc);
goto err_register;
}
"reg_mr: User object add failed");
goto err_add_uobj;
}
#ifdef _LP64
#else
#endif
if (rc != 0) {
"reg_mr: Error writing resp data (rc=%d)", rc);
goto err_response;
}
if (!umr->list_entry) {
"reg_mr: Error adding umr to mr_list\n");
goto err_response;
}
return (DDI_SUCCESS);
/*
* Need to set uo_live, so sol_ofs_uobj_remove() will
* remove the object from the object table.
*/
out:
return (rc);
}
/*
* Function:
* sol_uverbs_dereg_mr
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to de-register a memory region.
*/
/* ARGSUSED */
int
int out_len)
{
int rc;
"dereg_mr: Invalid handle");
goto err_out;
}
if (rc != IBT_SUCCESS) {
"dereg_mr: ibt_deregister_mr() (rc=%d)", rc);
goto err_deregister;
}
/*
* Remove from the list of this contexts MR resources, then remove from
* the resource management table and the reference placed on the user
* object at MR creation.
*/
/*
* Drop the lock and ref held by get_umr_write.
*/
return (DDI_SUCCESS);
/*
* Drop the lock and ref held by get_umr_write.
*/
return (rc);
}
/*
* Function:
* sol_uverbs_create_ah
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to for devices that require kernel AH creation.
*/
/* ARGSUSED */
int
int out_len)
{
"create_ah: kernel user verb not implemented");
return (ENOTSUP);
}
/*
* Function:
* sol_uverbs_destroy_ah
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to for devices that require kernel AH deletion.
*/
/* ARGSUSED */
int
int out_len)
{
"destroy_ah: kernel user verb not implemented");
return (ENOTSUP);
}
/*
* Function:
* sol_uverbs_create_comp_chan
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb entry point to create a completion event channel.
*/
int
{
int rc;
"create_comp_chan: entry, in_len=%d, out_len=%d",
/*
* libibverbs will have passed minor of the compl file in
* resp.fd. Use this to determine the uctxt created for
* completions.
*/
#ifdef _LP64
#else
#endif
if (rc != 0) {
"create_comp: copyin (rc=%d)", rc);
return (rc);
}
if (compl_id < SOL_UVERBS_DRIVER_MAX_MINOR) {
"create_comp: Invalid compl user context id %x",
compl_id);
return (ENXIO);
}
if (compl_uctxt == NULL) {
"create_comp: Failed get compl user context");
return (ENXIO);
}
compl_uctxt->uctxt_verbs_id != 0) {
"create_comp_chan: Invalid user context - "
"possibly reused");
return (ENXIO);
}
"uctxt %p, compl_uctxt %p, compl_id %x", uctxt,
/*
* Allocate an event file to be used for completion
* event notification.
*/
"create_comp_chan: Event file alloc error");
return (rc);
}
/*
* Place an extra reference on the compl event file. These will
* be used to handle the natural race of between the closing of
* the compl event file and uverbs device file that can occur.
*/
#ifdef _LP64
#else
#endif
if (rc != 0) {
"create_comp_chan: copyout %d", rc);
return (rc);
}
return (0);
}
/*
* Function:
* sol_uverbs_dummy_command
* Input:
* uctxt - Pointer to the callers user context.
* buf - Pointer to kernel buffer containing command.
* in_len - Length in bytes of input command buffer.
* out_len - Length in bytes of output response buffer.
* Output:
* The command output buffer is updated with command results.
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb generic place holder stub.
*/
/* ARGSUSED */
int
int out_len)
{
"sol_uverbs_dummy_command invoked");
return (0);
}
/*
* Function:
* sol_uverbs_write
* Input:
* dev - Device number.
* uiop - Pointer to the uio structure that describes the data (i.e.
* Solaris User Verbs command).
* credp - A pointer to the user credentials for the I/O transaction.
* Output:
* uiop -
* Returns:
* DDI_SUCCESS on success, else error code.
* Description:
* User verb write entry point. A user deivce libraries use this
* entry point to execute a kernel agent user verbs call. During
* the course of the call the user process will hold a read reference
* to the associated user context.
*/
/* ARGSUSED */
static int
{
int rc;
"uverbs_write: entry (len=%d)", len);
"uverbs_write: Failed get user context");
return (ENXIO);
}
"uverbs_write: write() on invalid uctxt type %x",
uctxt->uctxt_type);
goto out;
}
"uverbs_write: Header too small");
goto out;
}
"uverbs_write: Error reading header");
goto out;
}
"uverbs_write: Invalid header size");
goto out;
}
sizeof (uverbs_cmd_table)/sizeof (uverbs_cmd_table[0]) ||
goto out;
}
"uverbs_write: Error reading payload");
goto out;
}
#ifdef DEBUG
"payload: %08x, %08x, %08x, %08x",
"payload: %08x, %08x, %08x, %08x",
"payload: %08x, %08x, %08x, %08x",
"payload: %08x, %08x, %08x",
#endif
out:
"uverbs_write: rc = %d", rc);
return (rc);
}
static int
{
int rc;
#ifdef DEBUG
#endif
"uverbs_poll: Failed get user context");
return (ENXIO);
}
"uverbs_poll: Invalid Verbs user context id, %x",
return (ENXIO);
}
if (verbs_uctxt == NULL) {
"uverbs_poll: Failed get verbs user context");
return (ENXIO);
}
} else {
"uverbs_poll: poll user context type %d",
uctxt->uctxt_type);
}
return (rc);
}
/*
* Function:
* sol_uverbs_alloc_uctxt
* Input:
* devp - A pointer to the device number associated with the open.
* mod_ctxt - A pointer to the drivers module context.
* minor - The minor device number.
* Output:
* None.
* Returns:
* On success a new user context user resource object associated with
* the device passed via devp. NULL on error.
* Description:
* Allocate a new user context user resource object and initialize it.
* The users asynchronous event file is created as part of this. On
* successful allocation, the user context is returned with the
* associated write lock enabled.
*/
static uverbs_uctxt_uobj_t *
{
/*
* The initialization routine set's the initial reference,
* we dereference the object here to clean it up.
*/
"alloc_uctxt: Object add failed");
return (NULL);
}
/*
* Create the new clone for this user context using the
* object id as the minor number. Note we offset beyond all
* real minor device numbers.
*/
"uctxt %p, minor %x- alloced", uctxt,
"alloc_uctxt: user context allocated: %p, ref = %d",
if (minor == SOL_UVERBS_DRIVER_EVENT_MINOR) {
} else {
}
/* Return with uobj uo_lock held for WRITTER. */
return (uctxt);
}
/*
* Function:
* sol_uverbs_qpnum2uqpid
* Input:
* qp_num - used to find the user object that mapped to this qp_num
* Output:
* None
* Returns:
* DDI_FAILURE if not found else
* the uo_id in the user object that matches the qp_num
* Description:
* Find the uo_id of the user object which mapped to the input qp_num
*/
{
int i, j;
/*
* Try to find an empty slot for the new user object.
*/
for (i = 0; i < uo_tbl->uobj_tbl_used_blks; i++) {
for (j = 0; j < SOL_OFS_UO_BLKSZ; j++) {
"qpnum2uqpid(%x) ret %x",
}
}
}
}
}
return (DDI_FAILURE);
}
void
{
*iwclnt_hdl = NULL;
}
void *
{
if (uqpid == DDI_FAILURE)
return (NULL);
return (sol_uverbs_uqpid_to_ibt_handle(uqpid));
}
int
{
if (uqpid == DDI_FAILURE)
return (-1);
return (sol_uverbs_disable_user_qp_modify(uqpid));
}
int
{
if (uqpid == DDI_FAILURE)
return (-1);
}
void
{
"sol_uverbs_set_qp_free_state(%x, %x, %p)",
if (qp_free_state == SOL_UVERBS2UCMA_DISABLE_QP_FREE) {
if (uqpid == DDI_FAILURE) {
"set_qp_free_state(%d)-invalid qpnum",
qpnum);
return;
}
"set_qp_free_state(%d)-uqp lookup failure", qpnum);
return;
}
"set_qp_free_state : uqp %p, setting Disable QP Free", uqp);
return;
}
/*
* Enable free flag, so that close or userland free_qp
* call can free this in the future.
*/
"set_qp_free_state : uqp %p, setting Enable QP Free",
uqp);
} else {
/*
* uqp_free_state is set to FREE_PENDING, QP has been freed
* by userland. Call uverbs_uqp_free() to free this.
*/
"set_qp_free_state : uqp %p calling uverbs_uqp_free()",
uqp);
"set_qp_free_state : uverbs_uqp_free(%p) failed",
uqp);
}
}
/*
* Function:
* sol_uverbs_user_objects_init
* Input:
* None
* Output:
* None
* Returns:
* None
* Description:
* Initializes all of the user object resource managment tables.
*/
static void sol_uverbs_user_objects_init()
{
sizeof (uverbs_uctxt_uobj_t));
sizeof (uverbs_upd_uobj_t));
sizeof (uverbs_umr_uobj_t));
sizeof (uverbs_ucq_uobj_t));
sizeof (uverbs_usrq_uobj_t));
sizeof (uverbs_uqp_uobj_t));
sizeof (uverbs_uah_uobj_t));
sizeof (uverbs_ufile_uobj_t));
}
/*
* Function:
* sol_uverbs_user_objects_fini
* Input:
* None
* Output:
* None
* Returns:
* None
* Description:
* Releases all of the user object resource managment tables.
*/
static void sol_uverbs_user_objects_fini()
{
}
/*
* Function:
* sol_uverbs_ibt_to_kernel_status
* Input:
* status - An IBT status code.
* Output:
* None
* Returns:
* The "errno" based kernel error code the IBT status maps to.
* Description:
* Map an IBT status to the "errno" code that should be returned.
*/
int
{
int err;
switch (status) {
case IBT_NOT_SUPPORTED:
break;
case IBT_ILLEGAL_OP:
case IBT_INVALID_PARAM:
break;
case IBT_HCA_IN_USE:
case IBT_HCA_BUSY_DETACHING:
case IBT_HCA_BUSY_CLOSING:
case IBT_CHAN_IN_USE:
case IBT_CQ_BUSY:
case IBT_MR_IN_USE:
case IBT_PD_IN_USE:
case IBT_SRQ_IN_USE:
break;
case IBT_INSUFF_RESOURCE:
case IBT_HCA_WR_EXCEEDED:
case IBT_HCA_SGL_EXCEEDED:
break;
default:
}
return (err);
}
/* ARGSUSED */
if (flags && IBT_HCA_RESIZE_CHAN)
if (flags && IBT_HCA_PKEY_CNTR)
if (flags && IBT_HCA_QKEY_CNTR)
if (flags && IBT_HCA_RAW_MULTICAST)
if (flags && IBT_HCA_AUTO_PATH_MIG)
if (flags && IBT_HCA_SQD_SQD_PORT)
if (flags && IBT_HCA_AH_PORT_CHECK)
if (flags && IBT_HCA_CURRENT_QP_STATE)
if (flags && IBT_HCA_SHUTDOWN_PORT)
if (flags && IBT_HCA_INIT_TYPE)
if (flags && IBT_HCA_PORT_UP)
if (flags && IBT_HCA_SI_GUID)
if (flags && IBT_HCA_RNR_NAK)
if (flags && IBT_HCA_RESIZE_SRQ)
if (flags && IBT_HCA_BASE_QUEUE_MGT)
if (flags && IBT_HCA_ZERO_BASED_VA)
if (flags && IBT_HCA_LOCAL_INVAL_FENCE)
if (flags && IBT_HCA_MEM_WIN_TYPE_2B)
return (of_flags);
}
{
if (page_szs && IBT_PAGE_4K)
if (page_szs && IBT_PAGE_8K)
if (page_szs && IBT_PAGE_16K)
if (page_szs && IBT_PAGE_32K)
if (page_szs && IBT_PAGE_64K)
if (page_szs && IBT_PAGE_128K)
if (page_szs && IBT_PAGE_256K)
if (page_szs && IBT_PAGE_512K)
if (page_szs && IBT_PAGE_1M)
if (page_szs && IBT_PAGE_2M)
if (page_szs && IBT_PAGE_4M)
if (page_szs && IBT_PAGE_8M)
if (page_szs && IBT_PAGE_16M)
if (page_szs && IBT_PAGE_32M)
if (page_szs && IBT_PAGE_64M)
if (page_szs && IBT_PAGE_128M)
if (page_szs && IBT_PAGE_256M)
if (page_szs && IBT_PAGE_512M)
if (page_szs && IBT_PAGE_1G)
if (page_szs && IBT_PAGE_2G)
if (page_szs && IBT_PAGE_4G)
if (page_szs && IBT_PAGE_8G)
if (page_szs && IBT_PAGE_16G)
return (of_page_sz);
}