/* * 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_hca.c * * Provides the Solaris OFED User Verbs thin common hca interface for * sharing of IBT client handle, device list, and asynchronous event * delivery. */ #include #ifdef VFS_OPS #include #include #endif #include #include #include #include #include #include #include #include #include #include #include extern char *sol_uverbs_dbg_str; /* * Globals for managing the list of HCA's and the registered clients. */ kmutex_t sol_uverbs_hca_lock; llist_head_t sol_uverbs_hca_list; llist_head_t sol_uverbs_client_list; static uint32_t sol_uverbs_common_hca_initialized = 0; typedef struct sol_uverbs_hca_client_data { llist_head_t list; sol_uverbs_ib_client_t *client; void *data; } sol_uverbs_hca_client_data_t; static int sol_uverbs_hca_add_client_context(sol_uverbs_hca_t *hca, sol_uverbs_ib_client_t *client); /* * Function: * sol_uverbs_ib_register_client * Input: * client - Pointer to the client structure * Output: * None * Returns: * Zero on success, else error code. * Description: * The Solaris User Verbs kernel agent provides a single * common view of the IBTF devices. This function allows * Solaris OFA kernel implementations to share this view * by registerng a client callback for notification of HCA * addtion and removal. Note that when this function is * called, the client will get an "add" callback for all * existing devices. */ int sol_uverbs_ib_register_client(sol_uverbs_ib_client_t *client) { llist_head_t *entry; sol_uverbs_hca_t *hca; ASSERT(client != NULL); mutex_enter(&sol_uverbs_hca_lock); llist_head_init(&client->list, client); llist_add_tail(&client->list, &sol_uverbs_client_list); list_for_each(entry, &sol_uverbs_hca_list) { hca = (sol_uverbs_hca_t *)entry->ptr; if (client->add && !sol_uverbs_hca_add_client_context(hca, client)) { client->add(hca); } } mutex_exit(&sol_uverbs_hca_lock); return (0); } /* * Function: * sol_uverbs_ib_unregister_client * Input: * client - Pointer to the client structure * Output: * None * Returns: * None * Description: * Removes a client registration previously created with * the sol_uverbs_ib_register_client() call. */ void sol_uverbs_ib_unregister_client(sol_uverbs_ib_client_t *client) { llist_head_t *entry, *centry, *tmp; sol_uverbs_hca_t *hca; sol_uverbs_hca_client_data_t *context; ASSERT(client != NULL); mutex_enter(&sol_uverbs_hca_lock); list_for_each(entry, &sol_uverbs_hca_list) { hca = (sol_uverbs_hca_t *)entry->ptr; ASSERT(hca != NULL); if (client->remove) { client->remove(hca); } mutex_enter(&hca->client_data_lock); centry = hca->client_data_list.nxt; tmp = centry->nxt; while (centry != &hca->client_data_list) { ASSERT(centry); context = (sol_uverbs_hca_client_data_t *)centry->ptr; ASSERT(context != NULL); if (context->client == client) { llist_del(centry); kmem_free(context, sizeof (*context)); } centry = tmp; tmp = centry->nxt; } mutex_exit(&hca->client_data_lock); } llist_del(&client->list); mutex_exit(&sol_uverbs_hca_lock); } /* * Function: * sol_uverbs_ib_get_client_data * Input: * hca - Pointer to HCA struct passed in the client * add function callback. * client - A pointer to the client structure. * Output: * None * Returns: * The client data, or NULL on error. * Description: * Returns the client data associated with the given * HCA. The data is set/specified via the * sol_uverbs_ib_set_client_data() function. */ void * sol_uverbs_ib_get_client_data(sol_uverbs_hca_t *hca, sol_uverbs_ib_client_t *client) { llist_head_t *entry; sol_uverbs_hca_client_data_t *context; void *data = NULL; ASSERT(hca != NULL); ASSERT(client != NULL); mutex_enter(&hca->client_data_lock); list_for_each(entry, &hca->client_data_list) { context = (sol_uverbs_hca_client_data_t *)entry->ptr; ASSERT(context != NULL); if (context->client == client) { data = context->data; break; } } mutex_exit(&hca->client_data_lock); return (data); } /* * Function: * sol_uverbs_ib_set_client_data * Input: * hca - Pointer to HCA struct passed in the client * add function. * client - A pointer to the client structure. * data - The client data to associate with the HCA. * Output: * None * Returns: * None * Description: * Sets the client data associated with the given * HCA. */ void sol_uverbs_ib_set_client_data(sol_uverbs_hca_t *hca, sol_uverbs_ib_client_t *client, void *data) { llist_head_t *entry; sol_uverbs_hca_client_data_t *context; ASSERT(hca != NULL); ASSERT(client != NULL); mutex_enter(&hca->client_data_lock); list_for_each(entry, &hca->client_data_list) { context = (sol_uverbs_hca_client_data_t *)entry->ptr; ASSERT(context != NULL); if (context->client == client) { context->data = data; goto out; } } SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "HCA SET CLIENT DATA: No client found for %s\n", client->name != NULL ? client->name : "NULL Client Name"); out: mutex_exit(&hca->client_data_lock); } /* * Function: * sol_uverbs_ib_register_event_handler * Input: * handler - Pointer to handler structure * Output: * None * Returns: * Zero * Description: * Register to receive ansynchronous notifications * for the HCA defined in the handler struct. The notifications * are delivered via the callback function defined in the handler * struct. */ int sol_uverbs_ib_register_event_handler(sol_uverbs_ib_event_handler_t *handler) { ASSERT(handler != NULL); ASSERT(handler->hca != NULL); mutex_enter(&handler->hca->event_handler_lock); llist_head_init(&handler->list, handler); llist_add_tail(&handler->list, &handler->hca->event_handler_list); mutex_exit(&handler->hca->event_handler_lock); return (0); } /* * Function: * sol_uverbs_ib_unregister_event_handler * Input: * handler - Pointer to handler structure * Output: * None * Returns: * Zero * Description: * Unregister a ansynchronous notification handler previously * registered via the osl_uverbs_ib_register_event_handler() call. */ int sol_uverbs_ib_unregister_event_handler(sol_uverbs_ib_event_handler_t *handler) { ASSERT(handler != NULL); ASSERT(handler->hca != NULL); mutex_enter(&handler->hca->event_handler_lock); llist_del(&handler->list); mutex_exit(&handler->hca->event_handler_lock); return (0); } /* * Function: * sol_uverbs_common_hca_init * Input: * None * Output: * None * Returns: * Zero * Description: * Perform initialization required by the common hca client API. */ int sol_uverbs_common_hca_init() { llist_head_init(&sol_uverbs_hca_list, NULL); llist_head_init(&sol_uverbs_client_list, NULL); mutex_init(&sol_uverbs_hca_lock, NULL, MUTEX_DRIVER, NULL); sol_uverbs_common_hca_initialized = 1; return (0); } /* * Function: * sol_uverbs_common_hca_fini * Input: * None * Output: * None * Returns: * None * Description: * Perform cleanup required by the common hca client API. */ void sol_uverbs_common_hca_fini() { ASSERT(llist_empty(&sol_uverbs_client_list)); sol_uverbs_common_hca_initialized = 0; mutex_destroy(&sol_uverbs_hca_lock); } /* * Helpers for internal use only */ /* * Function: * sol_uverbs_hca_add_client_context * Input: * hca - Pointer to the hca struct to add a client context. * client - Pointer to the client. * Output: * None * Returns: * 0 on success, else the error. * Description: * Create a context for the specified client and attach it to * the specified hca. */ static int sol_uverbs_hca_add_client_context(sol_uverbs_hca_t *hca, sol_uverbs_ib_client_t *client) { sol_uverbs_hca_client_data_t *context; context = kmem_zalloc(sizeof (*context), KM_NOSLEEP); if (!context) { SOL_OFS_DPRINTF_L5(sol_uverbs_dbg_str, "HCA: Couldn't allocate client context for %s", client->name ? client->name : "Name is NULL"); return (ENOMEM); } context->client = client; context->data = NULL; llist_head_init(&context->list, context); mutex_enter(&hca->client_data_lock); llist_add(&context->list, &hca->client_data_list); mutex_exit(&hca->client_data_lock); return (0); } /* * Function: * sol_uverbs_ibt_hdl_to_hca * Input: * hca_hdl - IBT handle to an HCA. * Output: * None * Returns: * A pointer to the sol_uverbs HCA structure associated with the handle, * or NULL if no associated HCA is found. * Description: * Given an IBT hca handle, return the user verbs HCA structure associated * with that handle. */ sol_uverbs_hca_t * sol_uverbs_ibt_hdl_to_hca(ibt_hca_hdl_t hca_hdl) { llist_head_t *entry; sol_uverbs_hca_t *hca; sol_uverbs_hca_t *ret = NULL; mutex_enter(&sol_uverbs_hca_lock); list_for_each(entry, &sol_uverbs_hca_list) { hca = (sol_uverbs_hca_t *)entry->ptr; if (hca->hdl == hca_hdl) { ret = hca; break; } } mutex_exit(&sol_uverbs_hca_lock); return (ret); }