/*
* 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
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* This file contains the implementation of the mboxsc module, a mailbox layer
* built upon the Starcat IOSRAM driver.
*/
#include <sys/sysmacros.h>
#include <sys/iosramreg.h>
#include <sys/iosramio.h>
#include <sys/mboxsc_impl.h>
/*
* Debugging facility
*/
#ifdef DEBUG
(arg1))
#else /* DEBUG */
#endif /* DEBUG */
/*
* Basic constants
*/
#ifndef TRUE
#endif /* TRUE */
#ifndef FALSE
#define FALSE (0)
#endif /* FALSE */
/*
* Whenever mboxsc_init is called to create a new mailbox, an instance of
* mboxsc_mbox_t is created and inserted into a hash table to maintain
* various information about the mailbox. The mbox_state, mbox_refcount, and
* mbox_wait fields are all protected by the global mboxsc_lock mutex.
* If lock contention between mailboxes becomes an issue, each mailbox will
* need to be given its own mutex to protect the mbox_wait, mbox_state,
* and mbox_update_wait fields. The mbox_refcount field will probably need to
* remain under global protection, however, since it is used to keep track of
* the number of threads sleeping inside the mailbox's various synchronization
* mechanisms and would consequently be difficult to protect using those same
* mechanisms.
*/
typedef struct mboxsc_mbox {
int mbox_direction;
void (*mbox_callback)(void);
/*
* Various state flags that can be set on a mailbox. Multiple states may
* be active at the same time.
*/
/*
* Timeout periods for mboxsc_putmsg and mboxsc_getmsg, converted to ticks
* from the microsecond values found in mboxsc_impl.h.
*/
/*
* Various tests that are performed on message header fields.
*/
/*
* This macro can be used to determine the size of any field in the message
* header (or any other struct, for that matter).
*/
/*
* Mask used when generating unique transaction ID values.
* This arbitrarily chosen value will be OR'd together with
* a counter for each successive internally-generated transaction ID.
*/
/*
* All existing mailboxes are stored in a hash table with HASHTBL_SIZE
* entries so they can be rapidly accessed by their key values.
*/
/*
* Unfortunately, it is necessary to calculate checksums on data split up
* amongst different buffers in some cases. Consequently, mboxsc_checksum
* accepts a "seed" value as one of its parameters. When first starting a
* checksum calculation, the seed should be 0.
*/
#define CHKSUM_INIT (0)
/*
* local variables
*/
/*
* Structures from modctl.h used for loadable module support.
* The mboxsc API is a "miscellaneous" module.
*/
extern struct mod_ops mod_miscops;
"IOSRAM Mailbox API 'mboxsc'",
};
(void *)&modlmisc,
};
/*
* Prototypes for local functions
*/
static void mboxsc_iosram_callback(void *arg);
static void mboxsc_hdrchange_callback(void);
#ifdef DEBUG
/*PRINTFLIKE5*/
#endif /* DEBUG */
/*
* _init
*
* Loadable module support routine. Initializes global lock and hash table.
*/
int
_init(void)
{
int i;
int error = 0;
/*
* Initialize all module resources.
*/
for (i = 0; i < HASHTBL_SIZE; i++) {
mboxsc_hash_table[i] = NULL;
}
if (mod_install(&modlinkage) != 0) {
goto failed;
}
/*
* Set the os_mbox_version field in the IOSRAM header to indicate the
* highest Mailbox Protocol version we support
*/
(void *)MBOXSC_PROTOCOL_VERSION);
if (error != 0) {
goto failed;
}
/*
* Read the sms_mbox_version field in the IOSRAM header to determine
* what the greatest commonly supported version is.
*/
(void *)&sms_version);
if (error != 0) {
goto failed;
}
"sms version: %d, active version: %d\n", sms_version,
/*
* Register a callback with the IOSRAM driver to receive notification of
* changes to the IOSRAM header, in case the sms_mbox_version field
* changes.
*/
(void *)mboxsc_hdrchange_callback);
if (error != 0) {
goto failed;
}
return (0);
/*
* If initialization fails, uninitialize resources.
*/
return (error);
}
/*
* _fini
*
* Loadable module support routine. Closes all mailboxes and releases all
* resources.
*/
int
_fini(void)
{
int i;
int error = 0;
/*
* Attempt to remove the module. If successful, close all mailboxes
* and deallocate the global lock.
*/
if (error == 0) {
for (i = 0; i < HASHTBL_SIZE; i++) {
while (mboxsc_hash_table[i] != NULL) {
mailboxp = mboxsc_hash_table[i];
}
}
}
return (error);
}
/*
* _info
*
* Loadable module support routine.
*/
int
{
int error = 0;
return (error);
}
/*
* mboxsc_init
*
* Attempts to create a new mailbox.
*/
int
{
int error = 0;
(void *)event_handler);
/*
* Check for valid direction and callback specification.
*/
EINVAL);
return (EINVAL);
}
/*
* Allocate memory for the mailbox structure and initialize all
* caller-provided fields.
*/
KM_SLEEP);
sizeof (mboxsc_mbox_t), (void *)mailboxp);
/*
* Attempt to add the mailbox. If unsuccessful, free the allocated
* memory.
*/
if (error != 0) {
(void *)mailboxp, sizeof (mboxsc_mbox_t));
}
return (error);
}
/*
* mboxsc_fini
*
* Closes the mailbox with the indicated key, if it exists.
*/
int
{
int error = 0;
/*
* Attempt to close the mailbox.
*/
} else {
while (mailboxp->mbox_refcount != 0) {
}
}
return (error);
}
/*
* mboxsc_putmsg
*
* Attempt to place a message into an outbound mailbox and signal the
* recipient. A successful return (0) indicates that the message was
* successfully delivered.
*/
int
{
int i;
int error = 0;
int result;
int lock_held = 0;
int unlock_err;
#ifdef DEBUG /* because lint whines about if stmts without consequents */
*transidp);
}
#endif /* DEBUG */
/*
* Perform some basic sanity checks on the message.
*/
for (i = 0; i < MBOXSC_NUM_MSG_TYPES; i++) {
if (type == (1 << i)) {
break;
}
}
if ((i == MBOXSC_NUM_MSG_TYPES) || (cmd == 0) ||
"mboxsc_putmsg ret: 0x%08x\n", EINVAL);
return (EINVAL);
}
/*
* Initialize the header structure with values provided by the caller.
*/
} else {
header.msg_transid = 0;
}
/*
* Perform additional sanity checks on the mailbox and message.
* Make sure that the specified mailbox really exists, that the
* given message will fit in it, and that the current message's
* transaction ID isn't the same as the last message's transaction
* ID unless both messages are replies (it's okay, necessary even,
* to reuse a transaction ID when resending a failed reply message,
* but that is the only case in which it is permissible).
*/
(header.msg_transid != 0))) {
}
if (error != 0) {
"mboxsc_putmsg ret: 0x%08x\n", error);
return (error);
}
/*
* If the message's transaction ID is set to 0, generate a unique
* transaction ID and copy it into the message header. If the message
* is successfully delivered and transidp != NULL, we'll copy this new
* transid into *transidp later.
*/
if (header.msg_transid == 0) {
}
/*
* Don't allow mboxsc_putmsg to attempt to place a message for
* longer than the caller's timeout.
*/
deadline = ddi_get_lbolt() +
/*
* Increment the reference count on the mailbox to keep it from being
* closed, and wait for it to become available.
*/
remainder = 1;
(remainder > 0)) {
&mboxsc_lock, deadline);
}
/*
* Check to see whether or not the mailbox became available. If it
* did not, decrement its reference count and return an error to the
* caller.
*/
if (remainder == -1) {
} else if (remainder == 0) {
}
if (error != 0) {
"mboxsc_putmsg ret: 0x%08x\n", error);
return (error);
}
/*
* Since the message is valid and we're going to try to write it to
* IOSRAM, record its header for future reference (e.g. to make sure the
* next message doesn't incorrectly use the same transID).
*/
/*
* Flag the mailbox as being in use and release the global lock.
*/
/*
* Calculate the message checksum using the header and the data.
*/
/*
* Attempt to write the message and checksum to IOSRAM until successful,
* or as long as time remains and no errors other than EAGAIN are
* returned from any call to the IOSRAM driver in case there is a tunnel
* switch in progress.
*/
if (error == 0) {
}
if (error == 0) {
}
/*
* Lock the flags before setting data_valid. This isn't strictly
* necessary for correct protocol operation, but it gives us a chance to
* verify that the flags lock is functional before we commit to sending
* the message.
*/
if (error == 0) {
if (error == 0) {
lock_held = 1;
}
}
if (error == 0) {
}
/*
* Unlock the flags. If an error is encountered, only return it if
* another error hasn't been encountered previously.
*/
if (lock_held) {
error = unlock_err;
}
}
/*
* If time ran out or an IOSRAM call failed, notify other callers that
* the mailbox is available, decrement its reference count, and return
* an error.
*/
if (error != 0) {
"mboxsc_putmsg ret: 0x%08x\n", error);
return (error);
}
/*
* Send an interrupt to the remote mailbox interface to announce the
* presence of a new, valid message.
*/
/*
* Wait until either the data_valid flag is set INVALID by the
* remote client or time runs out. Since we're calling delay as
* a part of polling the flag anyway, we don't really need to do
* the usual continuous retry if iosram_get_flag returns EAGAIN.
*/
if (error == DDI_SUCCESS) {
do {
} while ((data_valid == IOSRAM_DATA_VALID) &&
(deadline - ddi_get_lbolt() >= 0));
}
/*
* If the data_valid flag was set to INVALID by the other side, the
* message was successfully transmitted. If it wasn't, but there
* weren't any IOSRAM errors, the operation timed out. If there was a
* problem with the IOSRAM, pass that info back to the caller.
*/
if (data_valid == IOSRAM_DATA_INVALID) {
result = 0;
} else {
}
/*
* If the message has not been picked up, expire it. Note that this may
* actually result in detecting successful message delivery if the SC
* picks it up at the last moment. If expiration fails due to an error,
* return an error to the user even if the message appears to have
* been successfully delivered.
*/
if (data_valid == IOSRAM_DATA_VALID) {
}
}
/*
* If the message was successfully delivered, and we generated a
* transaction ID for the caller, and the caller wants to know what it
* was, give it to them.
*/
}
/*
* Regardless of whether the message was successfully transmitted or
* not, notify other callers that the mailbox is available and decrement
* its reference count.
*/
result);
return (result);
}
/*
* mboxsc_getmsg
*
* Attempt to retrieve a message from the mailbox with the given key that
* matches values provided in msgp. A successful return (0) indicates that
* a message matching the caller's request was successfully received within
* timeout milliseconds. If a message matching the caller's request is
* detected, but can't be successfully read, an error will be returned even
* if the caller's timeout hasn't expired.
*/
int
{
int error = 0;
#ifdef DEBUG /* because lint whines about if stmts without consequents */
}
}
*transidp);
}
*lengthp);
}
#endif /* DEBUG */
/*
* Perform basic sanity checks on the caller's request.
*/
"mboxsc_getmsg ret: 0x%08x\n", EINVAL);
return (EINVAL);
}
/*
* Don't allow mboxsc_getmsg to attempt to receive a message for
* longer than the caller's timeout.
*/
deadline = ddi_get_lbolt() +
/*
* Perform additional sanity checks on the client's request and the
* associated mailbox.
*/
}
if (error != 0) {
"mboxsc_getmsg ret: 0x%08x\n", error);
return (error);
}
/*
* The request is okay, so reference the mailbox (to keep it from being
* closed), and proceed with the real work.
*/
/*
* Certain failures that may occur late in the process of getting a
* message (e.g. checksum error, cancellation by the sender) are
* supposed to leave the recipient waiting for the next message to
* arrive rather than returning an error. To facilitate restarting
* the message acquisition process, the following label is provided
* as a target for a very few judiciously-placed "goto"s.
*
* The mboxsc_lock mutex MUST be held when jumping to this point.
*/
;
/*
* If there is a valid message in the mailbox right now, check to
* see if it matches the caller's request. If not, or if another
* caller is already reading it, wait for either the arrival of the
* next message or the expiration of the caller's specified timeout.
*/
error = 0;
&mboxsc_lock, deadline);
if (remainder == -1) {
} else if (remainder == 0) {
}
if (error != 0) {
"mboxsc_getmsg ret: 0x%08x\n", error);
return (error);
}
}
/*
* If somebody sends us a message using a Mailbox Protocol version
* greater than the highest one we understand, invalidate the message,
* because we can't safely interpret anything beyond the version field.
*/
"incoming message with unsupported version %d\n",
goto mboxsc_getmsg_retry;
}
/*
* At this point, there is a stored message header that matches the
* caller's request, but the actual message may no longer be valid
* in IOSRAM. Check the data_valid flag to see whether or not
* this is the case. If the message has expired, go start over.
*
* The global mutex is held while reading flag data from IOSRAM to
* avoid certain race conditions. One race condition is still
* possible (i.e. SC-side has just set the data_valid flag for a
* new message, but the stored message header hasn't been updated
* yet), but it won't cause incorrect behavior (just some wasted work).
*/
if (error == 0) {
if (data_valid != IOSRAM_DATA_VALID) {
goto mboxsc_getmsg_retry;
}
goto mboxsc_getmsg_retry;
}
/*
* If the message is larger than the caller's buffer, provide the caller
* with the length of the message and return an error.
*/
}
/*
* Note that there's no need to check STATE_HDRVALID before broadcasting
* here because the header is guaranteed to be valid at this point.
*/
if (error != 0) {
"mboxsc_getmsg ret: 0x%08x\n", error);
return (error);
}
/*
* Store a copy of the current message header, flag the mailbox to
* indicate that it is being read and attempt to read the message data
* and checksum.
*/
if (datalen > 0) {
}
if (error == 0) {
}
/*
* Check for errors that may have occurred while accessing IOSRAM.
*/
if (error != 0) {
}
"mboxsc_getmsg ret: 0x%08x\n", error);
return (error);
}
/*
* Calculate the checksum for the header and data that was read from
* IOSRAM.
*/
datalen);
/*
* If the message header has been invalidated, note the change.
* If a the checksum verification fails, invalidate the message
* header. In either case, go back to the beginning and wait
* for a new message.
*/
error = -1;
"mboxsc_getmsg - message invalidated while reading\n");
} else if (read_checksum != calc_checksum) {
error = -1;
"mboxsc_getmsg - message failed checksum\n");
"mboxsc_getmsg - message failed checksum\n");
}
if (error == -1) {
goto mboxsc_getmsg_retry;
}
/*
* Acquire the hardware lock used for synchronization of data_valid flag
* access to avoid race conditions. If it is acquired, try to check the
* current data_valid flag and transaction ID to verify that the message
* is still valid.
*/
/*
* We don't "do" EBUSY here, so treat it as EAGAIN.
*/
}
} else {
}
if (error == 0) {
}
(caddr_t)&read_transid);
}
/*
* If something failed along the way, either the error is unrecoverable
* or we're just plain out of time, so unlock the flags if they were
* locked, release the mailbox, wake up other potential readers if
* there's still a message around, and return.
*/
if (error != 0) {
if (lock_held) {
(void) mboxsc_unlock_flags(TRUE);
}
}
"mboxsc_getmsg ret: 0x%08x\n", error);
return (error);
}
/*
* If the data_valid flag isn't set to IOSRAM_DATA_VALID, or the
* message transaction ID in IOSRAM has changed, the message being
* read was timed out by its sender. Since the data_valid flag can't
* change as long as we have the flags locked, we can safely mark the
* stored message header invalid if either the data_valid flag isn't set
* or the stored transaction ID doesn't match the one we read. (If
* data_valid is set, the transaction ID shouldn't be changing
* underneath us.) On the other hand, if there may still be a valid
* message, wake up any pending readers.
*/
if ((data_valid != IOSRAM_DATA_VALID) ||
if ((data_valid != IOSRAM_DATA_VALID) ||
}
/*
* Unfortunately, we can't be holding mboxsc_lock when we unlock
* the flags. However, we have to hold the flags until here to
* make sure the SC doesn't change the message's state while
* we're checking to see if we should invalidate our stored
* header.
*/
"mboxsc_getmsg() - message invalidated by sender\n");
goto mboxsc_getmsg_retry;
}
/*
* If everything has worked up to this point, all that remains is
* to set the data_valid flag to IOSRAM_DATA_INVALID, tidy up, and
* return the message. If the flag can't be set, the message can't
* be received, so keep trying as long as there is time.
*/
(void) mboxsc_unlock_flags(TRUE);
if (error != 0) {
}
"mboxsc_getmsg ret: 0x%08x\n", error);
return (error);
}
/*
* If the message was read 100% successfully and the stored message
* header for the mailbox still matches the message that was read,
* invalidate it to prevent other readers from trying to read it.
*/
}
/*
* Since we're successfully returning a message, we need to provide the
* caller with all of the interesting header information.
*/
return (0);
}
/*
* mboxsc_ctrl
*
* This routine provides access to a variety of services not available through
* the basic API.
*/
int
{
int error = 0;
EBADF);
return (EBADF);
}
switch (cmd) {
case MBOXSC_CMD_VERSION:
/*
* Return the Protocol version currently in use. Since
* there is only one version that exists right now, we
* can't be using anything else.
*/
break;
}
break;
case MBOXSC_CMD_MAXVERSION:
/*
* Return the highest Protocol version that we support.
*/
break;
}
break;
case MBOXSC_CMD_MAXDATALEN:
/*
* Return the amount of space available for client data
* in the indicated mailbox.
*/
break;
}
break;
{
/*
* Return the range of acceptable timeout values for
* mboxsc_putmsg, expressed in milliseconds.
*/
break;
}
break;
}
{
/*
* Return the range of acceptable timeout values for
* mboxsc_getmsg, expressed in milliseconds.
*/
break;
}
break;
}
default:
break;
}
return (error);
}
/*
* mboxsc_putmsg_def_timeout
*
* This routine returns the default mboxsc_putmsg timeout provided for the
* convenience of clients.
*/
{
return (MBOXSC_PUTMSG_DEF_TIMEOUT_MSECS);
}
/*
* mboxsc_iosram_callback
*
* This routine is registered with the IOSRAM driver for all inbound mailboxes,
* and performs preliminary processing of all new messages.
*/
static void
{
int error = 0;
/*
* We shouldn't ever receive a callback for a mailbox that doesn't
* exist or for an output mailbox.
*/
/*
* Attempt to read the header of the mailbox. If the IOSRAM returns
* EAGAIN, indicating a tunnel switch is in progress, do not retry
* the operation.
*/
/*
* If somebody sends us a message using a Mailbox Protocol version
* greater than the highest one we understand, ignore the message,
* because we can't safely interpret anything beyond the version field.
*/
error = -1;
"incoming message with unsupported version %d\n",
}
/*
* If this message is a repeat of a previous message (which should
* only happen with reply messages), it is conceivable that a client
* already executing in mboxsc_getmsg for the previous message could
* end up receiving the new message before this callback gets a chance
* to execute. If that happens, the data_valid flag will already have
* been cleared. Call iosram_get_flag to see if that is the case, and
* do not process the message if it is.
*/
if (error == 0) {
error = -1;
}
}
/*
* If the iosram_rd call failed, return.
*/
if (error != 0) {
"mboxsc_iosram_callback ret (0x%08x)\n", error);
return;
}
/*
* If the message read from IOSRAM was unsolicited, invoke
* its callback. Otherwise, wake all threads that are waiting
* in mboxsc_getmsg.
*/
(*(mailboxp->mbox_callback))();
} else {
}
}
/*
* mboxsc_hdrchange_callback
*
* This routine is registered with the IOSRAM driver to react to any changes SMS
* makes to the IOSRAM header.
*/
static void
{
int error;
"mboxsc_hdrchange_callback called\n");
(void *)&sms_version);
if (error == 0) {
"sms mailbox version = %d\n", sms_version);
}
}
/*
* mboxsc_add_mailbox
*
* If no other mailbox exists with the same key as this mailbox, attempt to
* retrieve its length from the IOSRAM driver and register the mboxsc callback
* for the associated IOSRAM chunk. If successful, initialize the
* non-client-supplied mailbox fields and insert it into the hash table.
* NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
*/
static int
{
int error = 0;
/*
* The global lock must be held by the caller.
*/
/*
* Don't create the mailbox if it already exists.
*/
"mboxsc_add_mailbox ret: 0x%08x\n", EEXIST);
return (EEXIST);
}
/*
* Obtain the mailbox length and register the mboxsc callback with the
* IOSRAM driver. If either call to the IOSRAM driver fails, or the
* chunk is too small to be used as a mailbox, return an error to the
* caller.
*/
}
}
}
if (error != 0) {
"mboxsc_add_mailbox ret: 0x%08x\n", error);
return (error);
}
/*
* Initialize remaining mailbox fields and insert mailbox into
* hash table.
*/
mailboxp->mbox_refcount = 0;
0);
return (0);
}
/*
* mboxsc_close_mailbox
*
* Remove a mailbox from the hash table, unregister its IOSRAM callback, and
* deallocate its resources.
* NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
*/
static void
{
int error = 0;
/*
* The global lock must be held by the caller.
*/
/*
* Unregister the mboxsc callback for this particular mailbox.
*/
" reported in mboxsc_close_mailbox.\n", key);
error = 0;
}
}
/*
* Remove the mailbox from the hash table and deallocate its resources.
*/
(void) mboxsc_hashremove_mailbox_by_key(key);
(void *)mailboxp, sizeof (mboxsc_mbox_t));
}
/*
* mboxsc_hashinsert_mailbox
*
* Insert a fully initialized mailbox into the hash table. No duplicate
* checking is performed at this point, so the caller is responsible for
* duplicate prevention if it is desired.
* NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
*/
static void
{
"mboxsc_hashinsert_mailbox called\n");
/*
* The global lock must be held by the caller.
*/
"mboxsc_hashinsert_mailbox ret\n");
}
/*
* mboxsc_hashfind_mailbox_by_key
*
* Locate a mailbox with the given key in the hash table. Return a pointer
* to the mailbox if it exists, or NULL if no matching mailbox is found.
* NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
*/
static mboxsc_mbox_t *
{
"mboxsc_hashfind_mailbox_by_key called\n");
/*
* The global lock must be held by the caller.
*/
break;
}
}
"mboxsc_hashfind_mailbox_by_key ret: %p\n", (void *)mailboxp);
return (mailboxp);
}
/*
* mboxsc_hashremove_mailbox_by_key
*
* Locate a mailbox with the given key in the hash table. If it exists,
* remove it from the hash table and return a pointer to it. Otherwise,
* return NULL.
* NOTE: The caller MUST hold mboxsc_lock to avoid corrupting the hash table.
*/
static mboxsc_mbox_t *
{
"mboxsc_hashremove_mailbox_by_key called\n");
/*
* The global lock must be held by the caller.
*/
break;
}
}
/*
* If a mailbox was found, remove it from the hash table.
*/
} else {
}
}
"mboxsc_hashremove_mailbox_by_key ret: %p\n", (void *)mailboxp);
return (mailboxp);
}
/*
* mboxsc_checksum
*
* Given a pointer to a data buffer and its length, calculate the checksum of
* the data contained therein.
*/
static mboxsc_chksum_t
{
while (length-- > 0) {
}
seed);
return (seed);
}
/*
* mboxsc_lock_flags
*
* Acquire the hardware lock used for data_valid flag synchronization. If the
* lock is currently held by SMS and acquisition is mandatory, just keep on
* trying until it is acquired. If acquisition is not mandatory, keep trying
* until the given deadline has been reached. To avoid loading the system
* unreasonably on EBUSY or EAGAIN, sleep for an appropriate amount of time
* before retrying. If a hardware error is encountered return it to the caller.
*
* If the lock is held, but not by SMS, clear it and acquire it. Nobody
* else should be grabbing that lock.
*/
static int
{
int error;
int warned = 0;
/*
* Keep trying to acquire the lock until successful or (if acquisition
* is not mandatory) time runs out. If EBUSY (lock is already held) or
* EAGAIN (tunnel switch in progress) is encountered, sleep for an
* appropriate amount of time before retrying. Any other error is
* unrecoverable.
*/
do {
pause = 0;
/*
* Since multiple threads could conceivably want the flag lock
* at the same time, we place the lock under a mutex and keep a
* counter indicating how many threads have the flags locked at
* the moment.
*/
if ((mboxsc_flaglock_count > 0) ||
if (warned) {
}
"mboxsc_lock_flags ret: 0\n");
return (0);
}
/*
* If iosram_sema_acquire returned EBUSY (lock already held),
* make sure the lock is held by SMS, since nobody else should
* ever be holding it. If EBUSY or EAGAIN (tunnel switch in
* progress) was returned, determine the appropriate amount of
* time to sleep before trying again.
*/
(void) iosram_sema_release();
"Incorrect flag lock value read (0x%08x)",
sema);
} else {
ddi_get_lbolt()));
}
deadline - ddi_get_lbolt()));
}
/*
* We had to hold the lock until now to protect the potential
* iosram_sema_release call above.
*/
/*
* If EAGAIN or EBUSY was encountered, we're looping.
*/
/*
* If we've been looping here for a while, something is
* probably wrong, so we should generated a warning.
*/
if (warning_time - ddi_get_lbolt() <= 0) {
if (!warned) {
warned = 1;
"Unable to lock flags (0x%08x)",
error);
} else {
"Still unable to lock flags");
}
warning_time = ddi_get_lbolt() +
}
/*
* Sleep a while before trying again.
*/
}
/*
* If something really bad has happened, generate a warning.
*/
}
error);
return (error);
}
/*
* mboxsc_unlock_flags
*
* Release the hardware lock used for data_valid flag synchronization.
* If a hardware error is encountered, return it to the caller. If the
* mandatory flag is set, loop and retry if EAGAIN is encountered.
*/
static int
{
int error;
int warned = 0;
ASSERT(mboxsc_flaglock_count != 0);
do {
/*
* Since multiple threads could conceivably want the flag lock
* at the same time, we place the lock under a mutex and keep a
* counter indicating how many threads have the flags locked at
* the moment.
*/
if ((mboxsc_flaglock_count > 1) ||
((error = iosram_sema_release()) == 0)) {
if (warned) {
}
"mboxsc_unlock_flags ret: 0\n");
return (0);
}
/*
* If iosram_sema_release returned EAGAIN (tunnel switch in
* progress) and unlocking the flags is mandatory, sleep before
* trying again. If we've been trying for a while, display a
* warning message too.
*/
if (warning_time - ddi_get_lbolt() <= 0) {
if (!warned) {
warned = 1;
"flags (iosram EAGAIN)");
} else {
"Still unable to unlock flags");
}
warning_time = ddi_get_lbolt() +
}
}
error);
return (error);
}
/*
* mboxsc_timed_read
*
* This function is just a wrapper around iosram_rd that will keep sleeping
* and retrying, up to a given deadline, if iosram_rd returns EAGAIN
* (presumably due to a tunnel switch).
*/
static int
{
int error;
do {
}
"mboxsc_timed_read ret: 0x%08x\n", error);
return (error);
}
/*
* mboxsc_timed_write
*
* This function is just a wrapper around iosram_wr that will keep sleeping
* and retrying, up to a given deadline, if iosram_wr returns EAGAIN
* (presumably due to a tunnel switch).
*/
static int
{
int error;
do {
}
"mboxsc_timed_write ret: 0x%08x\n", error);
return (error);
}
/*
* mboxsc_timed_get_flag
*
* This function is just a wrapper around iosram_get_flag that will keep
* sleeping and retrying, up to a given deadline, if iosram_get_flag returns
* EAGAIN (presumably due to a tunnel switch).
*/
static int
{
int error;
(void *)data_validp);
(void *)int_pendingp);
do {
}
"mboxsc_timed_get_flag ret: 0x%08x\n", error);
return (error);
}
/*
* mboxsc_timed_set_flag
*
* This function is just a wrapper around iosram_set_flag that will keep
* sleeping and retrying, up to a given deadline, if iosram_set_flag returns
* EAGAIN (presumably due to a tunnel switch).
*/
static int
{
int error;
do {
}
"mboxsc_timed_set_flag ret: 0x%08x\n", error);
return (error);
}
/*
* mboxsc_timed_send_intr
*
* This function is just a wrapper around iosram_send_intr that will keep
* sleeping and retrying, up to a given deadline, if iosram_send_intr returns
* EAGAIN (presumably due to a tunnel switch).
*/
static int
{
int error;
do {
error = iosram_send_intr();
if (error == DDI_FAILURE) {
}
"mboxsc_timed_send_intr ret: 0x%08x\n", error);
return (error);
}
/*
* mboxsc_expire_message
*
* This function is called by mboxsc_putmsg to handle expiration of messages
* that weren't picked up before they timed out. It will not return until the
* message has been picked up (which isn't expected), the message has been
* successfully expired, or a serious error has been encountered. If the
* message is finally picked up, it will set the value pointed to by "resultp"
* to 0. Unlike other sections of code, this function will never time out on
* EAGAIN from the iosram driver, since it is important that both sides of the
* IOSRAM agree on whether or not a message was delivered successfully.
*/
static int
{
int error = 0;
int lock_held = 0;
int warned = 0;
do {
error = 0;
/*
* Lock the flags if they aren't locked already.
*/
if (!lock_held) {
if (error == 0) {
lock_held = 1;
}
}
/*
* If the flags were locked successfully, reread the data-valid
* flag.
*/
if (error == 0) {
}
/*
* If the data-valid flag was read successfully, see if it has
* been cleared or not, as the other side may have finally read
* the message.
*/
if (error == 0) {
if (data_valid == IOSRAM_DATA_INVALID) {
/*
* Surprise! The SC finally picked up the
* message, so delivery succeeded after all.
*/
*resultp = 0;
}
} else {
/*
* The message still hasn't been read, so try to
* clear the data-valid flag.
*/
}
}
/*
* If the flags were locked, unlock them, no matter what else
* has or has not succeeded. Don't overwrite the existing value
* of "error" unless no errors other than EAGAIN have been
* encountered previously. If we hit EAGAIN at some point,
* unlocking the flags here is optional. In all other cases, it
* is mandatory.
*/
if (lock_held) {
int unlock_err;
} else {
}
if (unlock_err == 0) {
lock_held = 0;
error = unlock_err;
}
}
/*
* Did we hit a tunnel switch? (iosram driver returns EAGAIN)
* If so, sleep for a while before trying the whole process
* again.
*/
/*
* If we've been stuck in this loop for a while,
* something is probably wrong, and we should display a
* warning.
*/
if (warning_time - ddi_get_lbolt() <= 0) {
if (!warned) {
warned = 1;
"(iosram EAGAIN)");
} else {
"Still unable to clear flag");
}
warning_time = ddi_get_lbolt() +
}
}
/*
* If the data-valid flag was not successfully cleared due to some sort
* of problem, report it. Otherwise, if we looped for a while on EAGAIN
* and generated a warning about it, indicate that everything is okay
* now.
*/
if (error != 0) {
} else if (warned) {
}
"mboxsc_expire_message ret: 0x%08x\n", error);
return (error);
}
/*
* mboxsc_generate_transid
*
* This function generates unique transaction IDs using an incrementing counter.
* The value generated is guaranteed not to be the same as the prev_transid
* value passed in by the caller.
*/
static uint64_t
{
do {
if (transid_counter & TRANSID_GEN_MASK) {
transid_counter = 0;
}
} while (new_transid == prev_transid);
"mboxsc_generate_transid ret: 0x%016lx", new_transid);
return (new_transid);
}
/*
* mboxsc_reference_mailbox
*
* Increment the mailbox's reference count to prevent it from being closed.
* This really doesn't deserve to be a function, but since a dereference
* function is needed, having a corresponding reference function makes the code
* clearer.
*/
static void
{
(void *)mailboxp);
}
/*
* mboxsc_dereference_mailbox
*
* Decrement the mailbox's reference count, and if the count has gone to zero,
* signal any threads waiting for mailboxes to be completely dereferenced.
*/
static void
{
"mboxsc_dereference_mailbox called");
(void *)mailboxp);
if (mailboxp->mbox_refcount == 0) {
}
}
#ifndef DEBUG
/* ARGSUSED */
int
{
return (ENOTSUP);
}
#else /* DEBUG */
static void print_hash_table(void);
int
{
int error = 0;
switch (cmd) {
case MBOXSC_PRNMBOX:
break;
case MBOXSC_PRNHASHTBL:
break;
case MBOXSC_SETDBGMASK:
break;
default:
"Error: unknown mboxsc debug cmd (%d)\n", cmd);
break;
}
return (error);
}
/*PRINTFLIKE5*/
static void
const char *file,
int line,
const char *fmt,
...)
{
int i;
if (action & DBGACT_SHOWPOS) {
}
indent--;
}
if (class & mboxsc_debug_mask) {
indent_buf[0] = '\0';
for (i = 0; i < indent; i++) {
}
}
indent++;
}
if (action & DBGACT_BREAK) {
debug_enter("");
}
}
static void
print_hash_table(void)
{
int i;
for (i = 0; i < HASHTBL_SIZE; i++) {
}
}
}
static int
{
int error = 0;
error = 0;
} else {
"print_mailbox_by_key: no such mbox 0x%08x\n", key);
}
"print_mailbox_by_key ret: 0x%08x\n", error);
return (error);
}
/* ARGSUSED */
static void
{
(void *)mailboxp);
"key = 0x%08x, dir = %d, callback = %p\n",
(void *)mailboxp->mbox_callback);
} else {
}
"length = %d, refcount = %d, state = %d\n",
/* LINTED E_BAD_FORMAT_ARG_TYPE2 */
}
}
#endif /* DEBUG */