wrsm_intr.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* This file implements the RSMPI sendq functions in the Wildcat RSM
* driver. In addition, the driver uses Wildcat interrupts to communicate;
* this file also implements the driver internal interrupt generation and
* receiving functions.
*/
#include <sys/processor.h>
#include <sys/machsystm.h>
#include <sys/nexusintr_impl.h>
#include <sys/cpu_module.h>
#include <sys/wrsm_intr.h>
#include <sys/wrsm_intr_impl.h>
#include <sys/wrsm_cmmu.h>
#include <sys/wrsm_plat.h>
#include <sys/sysmacros.h>
#include <sys/wci_regs.h>
#include <sys/wci_common.h>
#include <sys/wrsm_session.h>
#include <sys/wrsm_transport.h>
#include <sys/wrsm_memseg_impl.h>
#ifdef DEBUG
extern char platform[];
#endif /* DEBUG */
/*
* Manifest Constants and Macros
*/
#define WRSM_INTR_MAX_DRAINERS 8
#define WRSM_INTR_MAX_TARGETS 8
#define RECVQ_HIGHWATER_MARGIN 10
#define NULL_INTR_DIST_MONDO 0 /* Valid mondo has bit 31 set */
#define INVALID_SAFARI_ID -1
extern kmutex_t intr_dist_lock;
extern int servicing_interrupt(void);
/*
* The following macros define a DPRINTF macro which can be used to enable
* or disable various levels of logging for this module.
*/
#ifdef DEBUG
#define INTRDBG 0x1
#define INTRWARN 0x2
#define INTRERR 0x4
#define INTRTRACE 0x8
#define INTRCESR 0x10
#else /* DEBUG */
#define DPRINTF(a, b)
#endif /* DEBUG */
/*
* The recvq_table entries can contain recvq pointers, or when not in use,
* contain an overlaid free list. As a result, you can't just compare
* the recvq pointer to NULL, since it may in fact be a free list index.
* To differentiate, we purposely set the LSB of the entry while it's
* free (and store the free list info in the upper bits).
* in the aforementioned format.
* Macro RECVQ2MONDO extracts the mondo value from the aforementioned format.
* Macro RECVQ_VALID tests to see if the item is indeed a valid recvq pointer.
*/
/*
* Message Structures
*/
/* Format of message for WRSM_MSG_INTR_RECVQ_CREATE request */
typedef struct recvq_create_req {
/* Format of message for WRSM_MSG_INTR_RECVQ_CREATE_RESPONSE */
typedef struct recvq_create_rsp {
int retval; /* RSM_SUCCESS or errno */
/* Format of message for WRSM_MSG_INTR_RECVQ_CONFIG request */
typedef struct recvq_config_req {
/* Format of message for WRSM_MSG_INTR_RECVQ_CONFIG_RESPONSE */
typedef struct recvq_config_rsp {
int retval; /* RSM_SUCCESS or errno */
/* Format of message for WRSM_MSG_INTR_RECVQ_DESTROY request */
typedef struct recvq_destroy {
/*
* Local functions
*/
/* Handler functions */
static void
{
DTRC("handler_init");
}
/* ARGSUSED */
static void
{
DTRC("handler_fini");
/* Nothing to do */
}
/* ARGSUSED */
static void
{
" handler 0x%p func 0x%p arg 0x%p ctlr-obj 0x%p next 0x%p",
(void *)handler,
(void *)handler->controller_obj,
}
/* Calls back the client with an interrupt event */
static rsm_intr_hand_ret_t
{
DTRC("handler_callback");
/* If source is allowed to send to this handler, do callback */
q_op,
}
return (retval);
}
/* Service functions */
/* Initializes a service */
static void
{
DTRC("service_init");
}
static void
{
/* Destroy the handler list */
DTRC("service_fini");
while (p) {
handler_fini(p);
kmem_free(p, sizeof (wrsm_intr_handler_t));
p = next;
}
}
static void
{
(void *)service,
handler_print(h);
}
}
/* Adds a recvq to the service's linked list of recvqs */
static void
{
DTRC("service_add_recvq");
}
/* Finds and removes a recvq from the service's linked list of recvqs */
static void
{
DTRC("service_rem_recvq");
/* If recvq is first on list, handle special */
} else {
if (p->service_next == recvq) {
break;
}
}
}
}
/* Adds a handler to the linked list of handlers for this service */
static void
{
DTRC("service_add_handler");
/* Add to end of list */
} else {
/* Advance to last node in list */
;
}
}
/* Removes and returns a handler from the service's handler list */
static wrsm_intr_handler_t *
{
DTRC("service_rem_handler");
/* If this is the first handler in the list, handle special */
} else {
break;
}
}
}
return (handler);
}
/* Distributes a packet to all handlers registered for this service. */
static void
{
DTRC("service_callback");
break;
}
}
}
/* Initializes the service list */
static void
{
uint_t i;
DTRC("service_list_init");
for (i = 0; i < WRSM_INTR_TYPE_MAX; i++) {
}
}
/* Destroys the service list */
static void
{
uint_t i;
DTRC("service_list_fini");
for (i = 0; i < WRSM_INTR_TYPE_MAX; i++) {
}
}
/* Recvq Functions */
/* Initializes/Refreshes the recvq CMMU entry */
static void
{
/*
* If the high water mark is 0, then the user must have created
* a queue of length 0. Force the user error to be set right
* from the start. This would probably only be done in testing,
* but the spec doesn't say it's not allowed.
*/
}
/* Destroys the receive queue contents */
static void
{
DTRC("recvq_fini");
/* Set the CMMU entry to invalid */
/* Cross trap to make sure trap handler isn't running */
/* Take recvq mutex to ensure drainer isn't using it */
/* Use cmmu_mondo to free recvq table entry */
/* Remove from target and service lists */
/* Delete packet ring */
if (recvq->packet_ring) {
sizeof (wrsm_intr_packet_t));
}
/* If it's on a drainer list, can't free yet, let drainer do it */
} else {
"freeing recvq 0x%p\n", (void *)recvq));
/* else, free the receive queue now */
}
/* Free the CMMU tuples */
}
/* Retrieves a packet from the recvq. Returns size of packet */
static size_t
{
*num_packets = 0;
/* If packet ring is empty, return 0 */
return (0);
}
/*
* For user interrupts, first word contains size in
* lower 6 bits, garbage in rest of word. Advance
* start of data by 1 64-bit word so user sees only
* the user data portion of the interrupt.
*/
if (recvq->user_interrupt) {
data++;
}
/* Copy data from recvq to a caller's buffer */
/* Atomically increment the tail */
do {
/* Keep reading info until lock bit not set */
continue;
}
break;
/* LINTED: E_CONST_EXPR */
} while (1);
return (size);
}
static void
{
/* recvq_was deleted while on the pending service list */
"freeing recvq in recvq_callback 0x%p\n",
(void *)recvq));
return;
}
/*
* in_use prevents recvq_fini from removing recvq even after the
* last packet is removed from the packet ring and drainer_next is
* NULL.
*/
/*
* Allow recvq to be queued to pending service queue again by
* setting drainer_next to NULL. This must be allowed prior to
* emptying the packet ring. If drainer_next were NULLed after
* processing the packet ring, we could have a race, where
* recvq_callback thinks it has processed all packets, then a trap
* comes in and queues a packet, then recvq_callback sets
* drainer_next to NULL. This would leave the new packet
* unprocessed, with recvq not in the pending service queue.
*/
NOTE("Servicing recvq: ");
num_packets > 0;
/* Callback handlers for this service */
/*
* At the point where recvq->delete_me is set, it is
* guaranteed that no further traps for this recvq will
* arrive, as the cmmu entry was invalidated and traps were
* flushed. This means the recvq won't be added to the
* pending service queue, and the state of drainer_next
* will not change to non-NULL after revq->delete_me is
* set. This means we can check the value of drainer_next
* here.
*
* If drainer_next is not NULL, this means this recvq was
* added back onto the pending service queue since this
* function set drainer_next to NULL. In this case, the
* recvq will be deleted during the servicing of this recvq
* the next time it is taken off the pending service queue.
*/
/* recvq was deleted during the callback */
"freeing recvq post service_callback "
"in recvq_callback 0x%p\n",
(void *)recvq));
return;
} else {
/* don't process any more packets */
break;
}
}
}
}
/* ARGSUSED */
static void
{
" recvq 0x%X from=%d head=%d tail=%d target=%d drainer=0x%X "
"drainer_next=%p",
(void *)recvq->drainer_next));
}
/* Recvq Table Functions */
/* Initialized the recvq tables */
/* ARGSUSED */
static void
{
DTRC("recvq_table_init");
/* Allocate first table, since we'll eventually need it */
}
/* Deletes the recvq table and all the recvqs */
static void
{
uint_t i;
uint_t j;
DTRC("recvq_table_fini");
for (i = 0; i < WRSM_INTR_RECVQ_TABLES; i++) {
/* Once we hit a null table, we can break */
break;
}
/* For each pointer in table, kill it */
for (j = 0; j < WRSM_INTR_RECVQ_TABLE_SIZE; j++) {
if (RECVQ_VALID(recvq)) {
}
}
}
}
/* Prints all recvqs */
static void
{
uint_t i;
uint_t j;
for (i = 0; i < WRSM_INTR_RECVQ_TABLES; i++) {
/* Once we hit a null table, we can break */
break;
}
/* For each pointer in table, kill it */
for (j = 0; j < WRSM_INTR_RECVQ_TABLE_SIZE; j++) {
if (RECVQ_VALID(recvq)) {
}
}
}
}
/* Allocates and initializes a recvq_table array */
static void
{
unsigned i;
int mondo;
/*
* In order to provide fast identification of the next freerecvq_table
* entry, the unused entries in the recvq_table contain the cmmu_mondo
* of the next free entry in a linked free list sort of fashion. In
* other words, at initialization, entry 0 contains the number 1,
* entry 1 contains the number 2, etc. When entry 1 is allocated,
* for example, entry 0 is updated to contain a 2 since 2 is now the
* first free entry. Since it is not allowed to use
* cmmu_mondo == 0, entry 0 is used as the implicit head of the linked
* list. When someone wants to allocate a cmmu entry, we look at the
* value in entry 0, which is the mondo of the first free entry. We
* then allocate that entry, and whatever value was in that location
* goes into entry 0 to maintain the free list.
*/
/* Calculate mondo of first entry in this table */
for (i = 0; i < WRSM_INTR_RECVQ_TABLE_SIZE; i++) {
mondo++; /* Point to next (free) entry for free list */
}
}
/* Allocates a recvq table entry. Returns cmmu_mondo, or 0 if out of space. */
static int
{
int free_mondo;
unsigned table;
unsigned index;
DTRC("recvq_table_alloc_entry");
/*
* The recvq table entries are overlaid with a linked list. Entry
* (0,0) contains the mondo of the next free entry. That
* entry contains the mondo of the second free entry, and so forth.
* If the next free mondo points beyond the last table, then we're
* out of entries.
*/
/* Check for empty */
if (table >= WRSM_INTR_RECVQ_TABLES) {
WARN("recvq_table_alloc_entry: out of memory");
return (NULL);
}
/*
* We only allocate tables when needed, so it's possible the table
* doesn't exist yet. If it doesn't, allocate it.
*/
#ifdef DEBUG
if (index != 0) {
"alloc_entry table %d: index = %d != 0",
}
#endif /* DEBUG */
}
/* Make head of list point at "next" free entry */
/* Be nice and null the new entry */
return (free_mondo);
}
/* Places a recvq_table_entry back on the free list */
static void
{
#ifdef DEBUG
"attempt to free an unused recvq entry: %d",
cmmu_mondo));
return;
}
#endif /* DEBUG */
/* Places this entry at head of free list */
}
/* Sets the recvq table entry based on the cmmu_mondo */
static void
{
DTRC("recvq_table_set");
#ifdef DEBUG
"attempt to set in-use entry %d", cmmu_mondo));
}
#endif /* DEBUG */
}
/* Target functions */
/* Initializes a target structure */
static void
{
drainer->drainer_inum));
}
/* Destroys a target struct */
static void
{
}
/*
* Repeats the intr_dist_cpu process if the safari port id of the WCI
* changes, thus changing the interrupt distribution mondo. This
* may happen if the WCI is removed from the controller. If we didn't
* repeat the intr_dist_cpu with a new intr_dist_mondo, and the WCI
* is moved to a new controller, that new controller would have
* conflicting intr_dist_mondos. Also, we must use a valid WCI
* distribution mechanism.
*/
static void
{
if (safid == INVALID_SAFARI_ID) {
} else {
}
/* Retarget all the CMMU entries for this target */
}
/*
* This function is called by OS when a CPU we're targeted at is removed.
*/
void
wrsm_redist(void *dip)
{
/* Use dip to get wrsm_network_t, then interrupt structure */
return;
}
return;
}
return;
}
return;
}
do {
/* Save new cpu id */
/* Retarget all the CMMU entries for this target */
}
/*
* Updates the CMMU entries for a given target, presumable because the
* target->cpu_id has changed.
*/
static void
{
/*
* Walk list of recvq's pointed at this target, and change cmmu
* entries to point to new cpu
*/
p->cmmu_index));
/* Read the current cmmu entry */
/* Set user error bit */
/*
* Do a cross trap to the target CPU to make sure that any
* in-process interrupts are complete.
*/
/* Update CMMU to the new target cpu and clear user err bit */
}
}
/* Adds a recvq to the target's linked list of recvqs. Must own intr->mutex */
static void
{
DTRC("target_add_recvq");
}
/* Finds and removes a recvq from linked list. Must own intr->mutex */
static void
{
DTRC("target_rem_recvq");
/* If recvq is first on list, handle special */
} else {
if (p->target_next == recvq) {
break;
}
}
}
}
/* Prints interesting info about the target, mostly for debug */
/* ARGSUSED */
static void
{
" target %d cpuid=%d mondo=0x%X drainer=0x%X",
}
/* Builds the list of targets and places it in the interrupt structure */
static void
{
int i;
DTRC("target_list_init");
for (i = 0; i < num_targets; i++) {
/* Allocate space for each target */
/* Pick a drainer */
/* Init the target structure */
/* If list is empty, point at this item */
} else {
/* Otherwise, point at whoever was next */
}
/* Point head of list at this new item */
}
}
/* Deletes the entire target list */
static void
{
DTRC("target_list_fini");
/* Repeat until we loop around to first again */
do {
/* Remember which comes next */
/* Now it's safe to kill it */
/* We're done when next points back to original target */
}
/* Repeats the intr_dist_cpu process for all targets with new wci */
static void
{
DTRC("target_list_readd");
/* Repeat until we loop around to first again */
do {
}
static wrsm_intr_target_t *
{
DTRC("target_list_get_next");
return (target);
}
/* Prints the entire target list */
static void
{
do {
}
/* Drainer functions */
static int
{
DTRC("drainer_init");
if (drainer->drainer_inum == 0) {
return (EAGAIN);
}
return (0);
}
/* Prints interesting info about a drainer */
/* ARGSUSED */
static void
{
}
/* Destroys a drainer */
static void
{
DTRC("drainer_fini");
}
/* Returns the old psl pointer, atomically setting new psl to empty */
static wrsm_intr_recvq_t *
{
do {
/* Copy psl to sl */
/* If psl hasn't changed, oldval will be sl, psl will be 0 */
sl,
/* The following shouldn't happen, but just to be sure... */
WARN("drainer_get_psl: psl was IDLE");
}
return (sl);
}
/* If psl is empty, set it to idle and return true; else return false */
static boolean_t
{
return (WRSM_INTR_PSL_EMPTY ==
(void *)WRSM_INTR_PSL_IDLE));
}
/* Drainer soft interrupt handler */
static uint_t
{
DTRC("drainer_handler");
/* Swap service list with pending service list */
do {
/* While there's something on the service list */
while (sl != WRSM_INTR_PSL_EMPTY) {
/* Get head of service list */
/* Update service list to point to next recvq */
/* If we've reached the end, set to empty */
}
/*
* Process the recvq - this also sets drainer_next
* to NULL
*/
}
} while (!drainer_psl_empty(drainer));
return (DDI_INTR_CLAIMED);
}
/* Drainer List functions */
/* Builds the list of drainers and places it in the interrupt structure */
static int
{
int retval = 0;
int i;
DTRC("drainer_list_init");
for (i = 0; i < num_drainers; i++) {
if (retval) {
break;
}
/* If list is empty, point at this item */
} else {
/* Otherwise, point at whoever was next */
}
/* Point head of list at this new item */
}
/* If we weren't able to allocate ANY soft interrupts... */
if (i == 0) {
}
return (retval);
}
/* Deletes the entire drainer list */
static void
{
DTRC("drainer_list_fini");
/* Repeat until we loop around to first again */
do {
/* Remember which comes next */
/* Now it's safe to kill it */
/* We're done when next points back to original drainer */
}
static wrsm_intr_drainer_t *
{
DTRC("drainer_list_get_next");
/* Don't need lock since this is only called building target list */
return (drainer);
}
/* Prints the entire drainer list */
static void
{
do {
}
/* Prints all members of the interrupt component */
void
{
}
/*
* Message Handlers
*/
/* Handler for the recvq create request from a remote node */
static boolean_t
{
int retval;
unsigned ntuples;
/* Find the service */
/* Search for a handler willing to accept messages from this node */
break;
}
}
/* No handler -- send an error response */
WARN("msg_recvq_create: no message handler");
return (B_TRUE);
}
/* Allocate a CMMU entry for this recvq */
1, /* nentries */
&tuples,
&ntuples,
B_FALSE);
if (retval) {
WARN("msg_recvq_create: unable to allocate cmmu entry");
return (B_TRUE);
}
/* Create the recvq */
&recvq,
NULL,
if (retval) {
WARN("msg_recvq_create: unable to create recvq");
return (B_TRUE);
}
/* Link this new recvq into recvq_list for from_cnode */
/* Since creator is remote, store tuple pointer in recvq */
/* Tell the handlers that a new recvq exists */
}
/* Recvq has been created. Notify requestor */
/* If response fails, destroy the recvq */
/* LINTED */
/*
* Don't need to destroy the recvq since, if rsp fails,
* TL tears down the session and the recvq gets
* destroyed anyway.
*/
WARN("msg_recvq_create: Unable to send response");
}
return (B_TRUE);
}
/* Handler for the recvq config request from a remote node */
static boolean_t
{
unsigned table;
unsigned index;
DTRC("msg_recvq_config");
/* Validate that the CMMU mondo and recvq pointer match */
/* The recvq doesn't exist or is invalid */
WARN("msg_recvq_config: Recvq doesn't exist or is invalid");
} else {
}
/*
* Since the sender will take a lock before sending the
* recvq_config request, there should be no more interrupts
* arriving; however, set user_error bit in cmmu just to
* play it safe.
*/
/*
* Now all we have to do is wait for the trap handler to
* finish any outstanding interrupts. This can be done with
* a cross-trap sync (xt_sync).
*/
/* Lock servicing of recvq */
/* Calculate size of new packet ring */
/* Alloc new packet ring if required */
new_size * sizeof (wrsm_intr_packet_t),
KM_SLEEP);
}
/* If we've allocated a new ring, and we have an old ring... */
/* Copy old ring contents to new ring */
sizeof (wrsm_intr_packet_t));
/* And free the old ring */
sizeof (wrsm_intr_packet_t));
}
/* If we've allocated a new ring, update recvq */
if (new_ring) {
}
}
/* LINTED */
/* Nothing to do - a failed rsp will cause seesion to end */
WARN("msg_recvq_config: Unable to send response");
}
return (B_TRUE);
}
/* Handler for the recvq destroy request from a remote node */
static boolean_t
{
unsigned table;
unsigned index;
/* Validate that the CMMU mondo and recvq pointer match */
return (B_TRUE);
}
return (B_TRUE);
}
/* Validate that this is the right source cnode */
return (B_TRUE);
}
/* Tell the handlers that the recvq is destroyed */
}
/* Unlink this recvq from that node's recvq_list */
} else while (rq->recvq_next) {
break;
}
}
/* Destroy the recvq */
return (B_TRUE);
}
/*
* Client functions
*/
static safari_port_t
{
int i;
for (i = 0; i < WRSM_MAX_WCIS; i++) {
}
}
return (INVALID_SAFARI_ID);
}
/* Initialize the interrupt component for this network */
int
{
int i;
int retval = 0;
DTRC("wrsm_intr_init");
if (retval) {
return (retval);
}
/* We don't yet know the safari ids of any of our wcis... */
for (i = 0; i < WRSM_MAX_WCIS; i++) {
}
/* Initialize the other interrupt components */
#ifdef DEBUG
#endif /* DEBUG */
return (retval);
}
/* Destroy the interrupt component for this network */
void
{
DTRC("wrsm_intr_fini");
/* Fini all our children */
/* Remove us from the network structure */
/* Free the memory */
#ifdef DEBUG
#endif /* DEBUG */
}
/*
*/
/* Inform the interrupt component of a new WCI */
int
{
int retval;
void *arg;
int i;
DTRC("wrsm_intr_newwci");
if (retval) {
"dmv_add_intr() failed for device %d: %d",
}
/* Store phys addr of cmmu sram for trap handler, and safari port id */
for (i = 0; i < WRSM_MAX_WCIS; i++) {
break;
}
}
ASSERT(i < WRSM_MAX_WCIS);
/*
* If we don't have a valid wci_safari_port, this must be the first
* WCI, so notify the targets.
*/
safid));
}
return (retval);
}
/* Inform the interrupt component that a WCI is going away */
void
{
int i;
DTRC("wrsm_intr_delwci");
/* Remove the dmv interrupt registration */
(void) dmv_rem_intr(safid);
/* Clear phys addr of cmmu sram */
for (i = 0; i < WRSM_MAX_WCIS; i++) {
break;
}
}
/*
* If this was the WCI we were using for interrupt target
* redistribution, we need to repeat the intr_dist_cpu process
* for all the targets with a different WCI, if one exists.
*/
if (new_wci != INVALID_SAFARI_ID) {
"Changing golden wci from %d to %d",
}
}
}
/*
* Handler Management Functions
*/
/* Registers a handler for a specific interrupt type - rsmpi interface */
int
{
int retval;
if (type < RSM_INTR_T_SUN_BASE) {
} else {
}
return (retval);
}
/* Registers a handler for a specific interrupt type - driver interface */
int
{
uint_t i;
/* Assume all senders, unless a senders_list is provided */
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff};
if (type > RSM_INTR_T_USR_END) {
return (RSMERR_PERM_DENIED);
}
/* If a sender's list is provided, record it */
if (senders_list_length != 0) {
for (i = 0; i < senders_list_length; i++) {
if (senders_list[i] >= WRSM_MAX_CNODES) {
return (RSMERR_UNKNOWN_RSM_ADDR);
}
}
}
/* Allocate and initialize handler structure */
/* Add this handler to the list of handlers for this service */
/* Count non-driver handlers */
if (type > RSM_INTR_T_DRV_END) {
net->handler_num++;
}
return (RSM_SUCCESS);
}
/* Unregisters a handler - rsmpi interface */
int
{
int retval;
if (type < RSM_INTR_T_SUN_BASE) {
} else {
}
return (retval);
}
/* Unregisters a handler - driver interface */
int
{
int num_unregistered_client_handlers = 0;
int retval = RSM_SUCCESS;
DTRC("wrsm_unregister_handler");
"Unregister all handlers for service %d", type));
/* If handler func is null, remove all handlers of that type */
while (handler) {
/* Update count of user handlers */
if (type > RSM_INTR_T_DRV_END) {
}
}
} else {
if (handler) {
/* Update count of user handlers */
if (type > RSM_INTR_T_DRV_END) {
}
} else {
"Handler not found"));
}
}
return (retval);
}
/*
* Receive Queue Functions
*/
/* Creates a receive queue of a given type */
int
void *exportseg,
int flags)
{
unsigned cmmu_mondo;
int retval;
int ring_size;
DTRC("wrsm_intr_create_recvq");
/* First, find if there's space to allocate a new recvq */
if (cmmu_mondo <= 0) {
WARN("wrsm_intr_create_recvq: Unable to allocate a mondo");
if (flags & WRSM_CREATE_RECVQ_SLEEP) {
WARN("wrsm_intr_create_recvq: waiting for resources");
if (retval > 0) {
goto retry;
} else {
/* got a signal */
return (EINTR);
}
} else {
return (EAGAIN);
}
}
/* Create and initialize recvq structure */
"created recvq 0x%p\n", (void *)*recvq));
if (qdepth > 0) {
/* Alloc extra space to give margin for flow ctrl */
(*recvq)->packet_ring =
KM_SLEEP);
}
/* Get next target */
/* Remember it's drainer */
/* Add this recvq to target and service lists */
/* Use cmmu_mondo to store recvq */
/* Update CMMU entry to enable the interrupt. */
return (RSM_SUCCESS);
}
/* Destroys the receive queue */
void
{
DTRC("wrsm_destroy_recvq");
}
/* Flushes the receive queue */
void
{
DTRC("wrsm_flush_recvq");
/*
* Do a cross trap to the target CPU to make sure that any
* in-process interrupts are complete.
*/
}
/*
* Send Functions
*/
/*
* Reads the CESR pointed to by p, until the not_valid flag is clear, then
* sets the CESR to the new value. Returns EIO if the not_valid flag never
* clears (implies the WCI is hung).
*/
#define CESR_FAILFAST_DISABLED 0x20
static int
{
uint_t i = 0;
do {
if (i++ > CESR_READ_RETRY_LIMIT) {
return (EIO);
}
}
/*
* Put new value in both half cachelines -- don't know
* which one will actually get stored into wci.
*/
/* Read back to force flush to physical memory */
block[0]));
return (0);
}
/* Does the send, with CESR swapping and kpreemt disabling */
static int
{
/*
* With preemption disabled, we want to clear the CESR to
* disable fail-fast, send the interrupt, then restore the
* CESR. If the CESR was set during the transaction, the
* interupt failed. If the error was destination CPU busy,
* then we should retry.
*/
/* Read CESR and replace with a value of 0 and failfast disabled */
&cesr_before.val)) {
return (WCI_CESR_BUSY_TOO_LONG);
}
/* Send interrupt */
/* Read CESR and restore original value */
&cesr_after.val)) {
return (WCI_CESR_BUSY_TOO_LONG);
}
}
/*
* wrsm_intr_send -- The parameters is_flags, is_wait and sendq_flags are
* normally provided by wrsm_send from the sendq or is data structures.
* To allow this function to be called from the driver, make sure that:
* is_flags of 0 => don't sleep
* sendq_flags of 0 => don't fail on full
* Since is_wait of 0 is a valid input, the caller must provide a valid
* value for is_wait, or WRSM_INTR_WAIT_DEFAULT to use a default driver
* timeout.
*/
int
void *remote_addr,
void *aligned_buf,
int is_flags,
int sendq_flags)
{
int cesr;
int retval = RSM_SUCCESS;
const int min_retries = 10;
int retries = 0;
int was_busy = 0;
if (is_wait == WRSM_INTR_WAIT_DEFAULT) {
}
/* Calculate time to wait when trying to send */
"wrsm_blkwrite(src=0x%p, dst=0x%p) from cpu %d",
/*
* Keep retrying until we exceed the time limit. However, even
* if we run out of time, we should at least make min_retries
* attempts before giving up.
*/
if (cesr == WCI_CESR_INTR_DEST_BUSY) {
was_busy++;
/* If "inter dest busy", retry */
} else if (cesr == WCI_CESR_USER_ERROR_BIT_SET) {
/*
* User error means overflow, retry unless sendq
* was created with full-fail flag set or no
* wait time was specified.
*/
if ((sendq_flags & RSM_INTR_SEND_Q_FULL_FAIL) ||
(is_wait == 0)) {
break;
} else {
/* Assume we might timeout */
}
} else if (cesr != 0) {
cesr));
/* All other comm errors, break from retrying */
break;
} else {
/* Success! */
break;
}
/* If we got here, we've failed and may retry */
if ((is_flags & RSM_INTR_SEND_SLEEP) &&
!servicing_interrupt()) {
} else {
}
}
"wrsm_intr_send: intr dest busy %d times", was_busy));
return (retval);
}
/* Callback from session, sets net_reset flag when session goes away */
{
if (state == SESSION_DOWN) {
/*
* Mark "net reset" on all sendq's to remote node, then
* unlink from sendq_list and forget about them. Could move
* them to a "zombie" list and free in wrsm_intr_fini.
*/
}
/* Delete all recvq's from remote node */
/* Tell the handlers that the recvq is destroyed */
(void) handler_callback(h,
}
}
}
return (B_TRUE);
}
/* Initializes the rsmpi portion of interrupts */
void
{
DTRC("wrsm_intr_rsminit");
/* Register sendq create message handler */
/* Register sendq config message handler */
/* Register sendq destroy message handler */
/* Register with Session for callbacks */
}
/* Cleans-up the rsmpi portion of interrupts */
void
{
DTRC("wrsm_intr_rsmfini");
/* Unregister with session */
/* Make sure everything's been cleaned up */
}
#ifdef DEBUG
/* Check sendq's are marked invalid */
"sendq_list[%d] not empty!", cnode));
}
/* Make sure all recvq's have been deleted */
"revq_list[%d] not empty!", cnode));
}
}
#endif /* DEBUG */
/* Unregister sendq create message handler */
/* Unregister sendq destroy message handler */
}
/*
* RSMPI public functions
*/
/* Creates a send queue */
/* ARGSUSED */
int
{
dest_cnode));
"invalid callback 0x%p", (void *)fn));
return (RSMERR_CALLBACKS_NOT_SUPPORTED);
}
if (addr >= WRSM_MAX_CNODES) {
"invalid cnode"));
return (RSMERR_RSM_ADDR_UNREACHABLE);
}
/* Send request to remote node to create a receive queue */
do {
(wrsm_message_t *)rsp)) {
"rpc failed"));
return (RSMERR_RSM_ADDR_UNREACHABLE);
}
return (RSMERR_NO_HANDLER);
}
fn != RSM_RESOURCE_SLEEP) {
/* Temporary failure, but user said don't sleep */
"remote reject: EAGAIN"));
return (RSMERR_INSUFFICIENT_RESOURCES);
}
/* Map-in interrupt page */
&vaddr,
PAGESIZE) != DDI_SUCCESS) {
/* Send recvq_destroy message */
"ddi_map_regs failed"));
return (RSMERR_INSUFFICIENT_RESOURCES);
}
/* Create sendq structure and pass back result */
/* Add sendq to linked list of sendqs */
net->sendqs_num++;
return (RSM_SUCCESS);
}
/* Reconfigure some of the attributes of an interrupt queue */
/* ARGSUSED */
int
{
int retval = RSM_SUCCESS;
DTRC("wrsm_sendq_config");
"invalid callback 0x%p", (void *)fn));
return (RSMERR_CALLBACKS_NOT_SUPPORTED);
}
WARN("Attempt to sendq_config after net reset");
return (RSMERR_CONN_ABORTED);
}
/* If fence was up, and we're getting rid of fence, then lower it */
}
/* Request remote node reconfigure the receive queue */
NOTE(" Sending recvq_config rpc to remote node");
(wrsm_message_t *)rsp)) {
"rpc failed"));
} else {
/* Update the qdepth */
}
}
return (retval);
}
/* Destroys an interrupt queue, freeing all resources allocated */
/* ARGSUSED */
int
{
DTRC("wrsm_sendq_destroy");
/* Remove from sendq_list */
break;
}
}
/* Send message to remove node */
(wrsm_message_t *)msg);
}
PAGESIZE);
return (RSM_SUCCESS);
}
/* Enqueues a datagram on an interrupt queue */
/* ARGSUSED */
int
rsm_send_t *is,
{
int retval;
int sendq_flags;
if (barrier) {
return (RSMERR_BAD_BARRIER_HNDL);
}
return (RSMERR_CONN_ABORTED);
}
}
return (RSMERR_QUEUE_FENCE_UP);
}
(WRSM_TL_MSG_SIZE - sizeof (uint64_t)));
return (RSMERR_BAD_BARRIER_HNDL);
}
/* Copy data to an aligned buffer */
#ifdef DEBUG
#endif /* DEBUG */
/* Increment the offset, to take advantage of striping */
/* If method-of-wait is SLEEP, is_wait of 0 means "wait forever" */
}
/* Send the packet */
buf,
}
return (retval);
}