/*
* 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 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
* provide the interface to the layered drivers (send request/receive
* response to the RMC
*
*/
/*
* Header files
*/
#include <sys/conf.h>
#include <sys/callb.h>
#include <sys/cyclic.h>
#include <sys/membar.h>
#include <sys/modctl.h>
#include <sys/strlog.h>
#include <sys/sunddi.h>
#include <sys/ddi.h>
#include <sys/types.h>
#include <sys/disp.h>
#include <sys/rmc_comm_dp.h>
#include <sys/rmc_comm_dp_boot.h>
#include <sys/rmc_comm_drvintf.h>
#include <sys/rmc_comm.h>
void dp_reset(struct rmc_comm_state *, uint8_t, boolean_t, boolean_t);
void dp_wake_up_waiter(struct rmc_comm_state *, uint8_t);
static int rmc_comm_send_req_resp(struct rmc_comm_state *rcs,
rmc_comm_msg_t *request, rmc_comm_msg_t *response, uint32_t wait_time);
static int rmc_comm_wait_bp_reply(struct rmc_comm_state *,
rmc_comm_dp_state_t *, dp_req_resp_t *, clock_t);
static void rmc_comm_wait_enable_to_send(struct rmc_comm_state *,
rmc_comm_dp_state_t *);
static void rmc_comm_wake_up_next(struct rmc_comm_state *);
static void rmc_comm_send_pend_req(caddr_t arg);
static int rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs);
static void rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs);
/*
* leaf driver to use this function to send a request to the remote side (RMC)
* and wait for a reply
*/
int
rmc_comm_request_response(rmc_comm_msg_t *request,
rmc_comm_msg_t *response, uint32_t wait_time)
{
struct rmc_comm_state *rcs;
int err;
/*
* get the soft state struct (instance 0)
*/
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response")) == NULL)
return (RCENOSOFTSTATE);
do {
err = rmc_comm_send_req_resp(rcs, request, response, wait_time);
} while (err == RCEGENERIC);
return (err);
}
/*
* leaf driver to use this function to send a request to the remote side (RMC)
* without waiting for a reply. If flag is RMC_COMM_DREQ_URGENT, the request
* message is sent once-off (an eventual pending request is aborted). This
* flag must only be used when try to send a request in critical condition
* (while the system is shutting down for instance and the CPU signature
* has to be sent). Otherwise, the request is stored in a temporary location
* and delivered by a thread.
*/
int
rmc_comm_request_nowait(rmc_comm_msg_t *request, uint8_t flag)
{
struct rmc_comm_state *rcs;
rmc_comm_dp_state_t *dps;
rmc_comm_drvintf_state_t *dis;
dp_message_t req;
int err = RCNOERR;
uint8_t flags = 0;
/*
* get the soft state struct (instance 0)
*/
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response")) == NULL)
return (RCENOSOFTSTATE);
/*
* just a sanity check...
*/
if (request == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "reqnowait, invalid args\n"));
return (RCEINVARG);
}
if (!IS_NUMBERED_MSG(request->msg_type)) {
DPRINTF(rcs, DAPI, (CE_CONT,
"reqnowait, ctrl msg not allowed! req type=%x\n",
request->msg_type));
return (RCEINVARG);
}
if (flag == RMC_COMM_DREQ_URGENT) {
/*
* Send this request with high priority i.e. abort eventual
* request/response pending sessions.
*/
dps = &rcs->dp_state;
DPRINTF(rcs, DAPI, (CE_CONT, "going to send request=%x (URG)\n",
request->msg_type));
/*
* Handle the case where we are called during panic
* processing. If that occurs, then another thread in
* rmc_comm might have been idled by panic() while
* holding dp_mutex. As a result, do not unconditionally
* grab dp_mutex.
*/
if (ddi_in_panic() != 0) {
if (mutex_tryenter(dps->dp_mutex) == 0) {
return (RCENODATALINK);
}
} else {
mutex_enter(dps->dp_mutex);
}
/*
* send the request only if the protocol data link is up.
* it is pointless to send it in the other case.
*/
if (dps->data_link_ok) {
/*
* clean up an eventual pending request/response session
* (save its current status)
*/
if (dps->pending_request) {
flags = dps->req_resp.flags;
rmc_comm_dp_mcleanup(rcs);
}
/*
* send the request message
*/
req.msg_type = request->msg_type;
req.msg_buf = (uint8_t *)request->msg_buf;
req.msg_msglen = (uint16_t)request->msg_len;
DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x (URG)\n",
request->msg_type));
err = rmc_comm_dp_msend(rcs, &req);
/*
* wait for fifos to drain
*/
rmc_comm_serdev_drain(rcs);
/*
* clean up the current session
*/
rmc_comm_dp_mcleanup(rcs);
/*
* abort an old session (if any)
*/
if (dps->pending_request) {
dps->req_resp.flags = flags;
dp_wake_up_waiter(rcs, MSG_ERROR);
}
}
mutex_exit(dps->dp_mutex);
} else {
/*
* Get an 'independent' thread (rmc_comm_send_pend_req)
* to send this request (since the calling thread does not
* want to wait). Copy the request in the drvintf state
* structure and signal the thread.
*/
dis = &rcs->drvi_state;
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
DPRINTF(rcs, DAPI, (CE_CONT, "get to send request=%x\n",
request->msg_type));
/*
* copy the request in a temporary location
* (drvinf_state structure) and signal the thread
* that a request message has to be delivered
*/
if (request->msg_len < DP_MAX_MSGLEN) {
dis->dreq_request.msg_type = request->msg_type;
dis->dreq_request.msg_len = request->msg_len;
dis->dreq_request.msg_buf =
dis->dreq_request_buf;
bcopy(request->msg_buf,
dis->dreq_request.msg_buf,
request->msg_len);
dis->dreq_state = RMC_COMM_DREQ_ST_PROCESS;
cv_signal(dis->dreq_sig_cv);
} else {
/*
* not enough space to hold the request
*/
err = RCEREPTOOBIG;
}
} else {
DPRINTF(rcs, DAPI, (CE_CONT, "cannot get to send "
"request=%x (busy)\n", request->msg_type));
/*
* only one request per time can be processed.
* the thread is either busy (RMC_COMM_DREQ_ST_PROCESS)
* or terminating (RMC_COMM_DREQ_ST_EXIT)
*/
err = RCEGENERIC;
}
mutex_exit(dis->dreq_mutex);
}
return (err);
}
/*
* Function used to send a request and (eventually) wait for a response.
* It can be called from a leaf driver (via rmc_comm_request_response) or
* from the thread in charge of sending 'no-wait' requests
* (rmc_comm_send_pend_req).
*/
static int
rmc_comm_send_req_resp(struct rmc_comm_state *rcs, rmc_comm_msg_t *request,
rmc_comm_msg_t *response, uint32_t wait_time)
{
rmc_comm_dp_state_t *dps;
dp_req_resp_t *drr;
dp_message_t *exp_resp;
dp_message_t req;
clock_t resend_clockt, delta;
clock_t stop_clockt;
int err;
/*
* just a sanity check...
*/
if (request == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "reqresp, invalid args\n"));
return (RCEINVARG);
}
/*
* drivers cannot send control messages at all. They are meant to
* be used at low level only.
*/
if (!IS_NUMBERED_MSG(request->msg_type)) {
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp, ctrl msg not allowed! req type=%x\n",
request->msg_type));
return (RCEINVARG);
}
dps = &rcs->dp_state;
drr = &dps->req_resp;
exp_resp = &drr->response;
/*
* Handle the case where we are called during panic
* processing. If that occurs, then another thread in
* rmc_comm might have been idled by panic() while
* holding dp_mutex. As a result, do not unconditionally
* grab dp_mutex.
*/
if (ddi_in_panic() != 0) {
if (mutex_tryenter(dps->dp_mutex) == 0) {
return (RCENODATALINK);
}
} else {
mutex_enter(dps->dp_mutex);
}
/*
* if the data link set up is suspended, just return.
* the only time that this can happen is during firmware download
* (see rmc_comm_request_response_bp). Basically, the data link is
* down and the timer for setting up the data link is not running.
*/
if (!dps->data_link_ok &&
dps->timer_link_setup == (timeout_id_t)0) {
mutex_exit(dps->dp_mutex);
return (RCENODATALINK);
}
DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d, req type=%x\n",
dps->pending_request, request->msg_type));
rmc_comm_wait_enable_to_send(rcs, dps);
/*
* We now have control of the RMC.
* Place a lower limit on the shortest amount of time to be
* waited before timing out while communicating with the RMC
*/
if (wait_time < DP_MIN_TIMEOUT)
wait_time = DP_MIN_TIMEOUT;
stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
/*
* initialization of the request/response data structure
*/
drr->flags = 0;
drr->error_status = 0;
/*
* set the 'expected reply' buffer: get the buffer already allocated
* for the response (if a reply is expected!)
*/
if (response != NULL) {
exp_resp->msg_type = response->msg_type;
exp_resp->msg_buf = (uint8_t *)response->msg_buf;
exp_resp->msg_msglen = (uint16_t)response->msg_bytes;
exp_resp->msg_bufsiz = (uint16_t)response->msg_len;
} else {
exp_resp->msg_type = DP_NULL_MSG;
exp_resp->msg_buf = (uint8_t)NULL;
exp_resp->msg_bufsiz = (uint16_t)0;
exp_resp->msg_msglen = (uint16_t)0;
}
/*
* send the request message
*/
req.msg_type = request->msg_type;
req.msg_buf = (uint8_t *)request->msg_buf;
req.msg_msglen = (uint16_t)request->msg_len;
/*
* send the message and wait for the reply or ACKnowledgment
* re-send the message if reply/ACK is not received in the
* timeframe defined
*/
DPRINTF(rcs, DAPI, (CE_CONT, "send request=%x\n", request->msg_type));
delta = drv_usectohz(TX_RETRY_TIME * 1000);
while ((err = rmc_comm_dp_msend(rcs, &req)) == RCNOERR) {
resend_clockt = ddi_get_lbolt() + delta;
/*
* wait for a reply or an acknowledgement
*/
(void) cv_reltimedwait(drr->cv_wait_reply, dps->dp_mutex,
delta, TR_CLOCK_TICK);
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp send status: flags=%02x req=%x resp=%x tick=%ld\n",
drr->flags, request->msg_type,
response ? response->msg_type : -1,
stop_clockt - resend_clockt));
/*
* Check for error condition first
* Then, check if the command has been replied/ACKed
* Then, check if it has timeout and if there is any
* time left to resend the message.
*/
if ((drr->flags & MSG_ERROR) != 0) {
if (drr->error_status == 0) {
err = RCEGENERIC;
} else {
err = drr->error_status;
}
break;
} else if (response != NULL &&
(drr->flags & MSG_REPLY_RXED) != 0) {
/*
* yes! here is the reply
*/
/*
* get the actual length of the msg
* a negative value means that the reply message
* was too big for the receiver buffer
*/
response->msg_bytes = exp_resp->msg_msglen;
if (response->msg_bytes < 0)
err = RCEREPTOOBIG;
else
err = RCNOERR;
break;
} else if (response == NULL && (drr->flags & MSG_ACKED) != 0) {
/*
* yes! message has been acknowledged
*/
err = RCNOERR;
break;
} else if ((stop_clockt - resend_clockt) <= 0) {
/*
* no more time left. set the error code,
* exit the loop
*/
err = RCETIMEOUT;
break;
}
}
rmc_comm_dp_mcleanup(rcs);
rmc_comm_wake_up_next(rcs);
mutex_exit(dps->dp_mutex);
DPRINTF(rcs, DAPI, (CE_CONT, "reqresp end: err=%d, request=%x\n",
err, request->msg_type));
return (err);
}
/*
* Function used to send a BP (Boot Prom) message and get the reply.
* BP protocol is provided only to support firmware download.
*
* This function will look for the following key BP protocol commands:
* BP_OBP_BOOTINIT: the data link is brought down so that request/response
* sessions cannot be started. The reason why is that this command will cause
* RMC fw to jump to the boot monitor (BOOTMON_FLASH) and data protocol is not
* operational. In this context, RMC fw will only be using the BP protocol.
* BP_OBP_RESET: data link setup timer is resumed. This command cause the RMC
* to reboot and hence become operational.
*/
int
rmc_comm_request_response_bp(rmc_comm_msg_t *request_bp,
rmc_comm_msg_t *response_bp, uint32_t wait_time)
{
struct rmc_comm_state *rcs;
rmc_comm_dp_state_t *dps;
dp_req_resp_t *drr;
dp_message_t *resp_bp;
bp_msg_t *bp_msg;
clock_t stop_clockt;
int err = RCNOERR;
boolean_t bootinit_sent = 0;
/*
* get the soft state struct (instance 0)
*/
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response_bp")) == NULL)
return (RCENOSOFTSTATE);
/*
* sanity check: request_bp buffer must always be provided
*/
if (request_bp == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "reqresp_bp, invalid args\n"));
return (RCEINVARG);
}
bp_msg = (bp_msg_t *)request_bp->msg_buf;
DPRINTF(rcs, DAPI, (CE_CONT, "send request_bp=%x\n", bp_msg->cmd));
/*
* only BP message can be sent
*/
if (!IS_BOOT_MSG(bp_msg->cmd)) {
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp_bp, only BP msg are allowed! type=%x\n",
bp_msg->cmd));
return (RCEINVARG);
}
dps = &rcs->dp_state;
drr = &dps->req_resp;
resp_bp = &drr->response;
mutex_enter(dps->dp_mutex);
rmc_comm_wait_enable_to_send(rcs, dps);
/*
* Now, before sending the message, just check what it is being sent
* and take action accordingly.
*
* is it BP_OBP_BOOTINIT or BP_OBP_RESET command?
*/
if (bp_msg->cmd == BP_OBP_BOOTINIT) {
/*
* bring down the protocol data link
* (must be done before aborting a request/response session)
*/
dps->data_link_ok = 0;
dps->timer_link_setup = (timeout_id_t)0;
bootinit_sent = 1;
} else if (bp_msg->cmd == BP_OBP_RESET) {
/*
* restart the data link set up timer. RMC is coming up...
*/
dp_reset(rcs, INITIAL_SEQID, 0, 1);
}
/*
* initialization of the request/response data structure
*/
drr->flags = 0;
drr->error_status = 0;
/*
* set the reply buffer: get the buffer already allocated
* for the response
*/
if (response_bp != NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "expect BP reply. len=%d\n",
response_bp->msg_len));
resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
}
/*
* send the BP message and wait for the reply
*/
rmc_comm_bp_msend(rcs, bp_msg);
if (response_bp != NULL) {
/*
* place a lower limit on the shortest amount of time to be
* waited before timing out while communicating with the RMC
*/
if (wait_time < DP_MIN_TIMEOUT)
wait_time = DP_MIN_TIMEOUT;
stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
stop_clockt)) == RCNOERR) {
/*
* get the actual length of the msg
* a negative value means that the reply message
* was too big for the receiver buffer
*/
response_bp->msg_bytes = resp_bp->msg_msglen;
if (response_bp->msg_bytes < 0) {
err = RCEREPTOOBIG;
} else if (bootinit_sent) {
/*
* BOOTINIT cmd may fail. In this is the case,
* the RMC is still operational. Hence, we
* try (once) to set up the data link
* protocol.
*/
bp_msg = (bp_msg_t *)response_bp->msg_buf;
if (bp_msg->cmd == BP_RSC_BOOTFAIL &&
bp_msg->dat1 == BP_DAT1_REJECTED) {
(void) rmc_comm_dp_ctlsend(rcs,
DP_CTL_START);
}
}
}
}
rmc_comm_dp_mcleanup(rcs);
rmc_comm_wake_up_next(rcs);
mutex_exit(dps->dp_mutex);
return (err);
}
/*
* to register for an asynchronous (via soft interrupt) notification
* of a message from the remote side (RMC)
*/
int
rmc_comm_reg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler,
rmc_comm_msg_t *msgbuf, uint_t *state, kmutex_t *lock)
{
struct rmc_comm_state *rcs;
dp_msg_intr_t *msgintr;
int err = RCNOERR;
if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_reg_intr")) == NULL)
return (RCENOSOFTSTATE);
mutex_enter(rcs->dp_state.dp_mutex);
msgintr = &rcs->dp_state.msg_intr;
/*
* lock is required. If it is not defined, the
* interrupt handler routine cannot be registered.
*/
if (lock == NULL) {
mutex_exit(rcs->dp_state.dp_mutex);
return (RCEINVARG);
}
/*
* only one interrupt handler can be registered.
*/
if (msgintr->intr_handler == NULL) {
if (ddi_add_softintr(rcs->dip, DDI_SOFTINT_HIGH,
&msgintr->intr_id, NULL, NULL, intr_handler,
(caddr_t)msgbuf) == DDI_SUCCESS) {
msgintr->intr_handler = intr_handler;
msgintr->intr_lock = lock;
msgintr->intr_state = state;
msgintr->intr_msg_type = msg_type;
msgintr->intr_arg = (caddr_t)msgbuf;
} else {
err = RCECANTREGINTR;
}
} else {
err = RCEALREADYREG;
}
mutex_exit(rcs->dp_state.dp_mutex);
return (err);
}
/*
* To unregister for asynchronous notifications
*/
int
rmc_comm_unreg_intr(uint8_t msg_type, rmc_comm_intrfunc_t intr_handler)
{
struct rmc_comm_state *rcs;
dp_msg_intr_t *msgintr;
int err = RCNOERR;
if ((rcs = rmc_comm_getstate(NULL, 0, "rmc_comm_unreg_intr")) == NULL)
return (RCENOSOFTSTATE);
mutex_enter(rcs->dp_state.dp_mutex);
msgintr = &rcs->dp_state.msg_intr;
if (msgintr->intr_handler != NULL &&
msgintr->intr_msg_type == msg_type &&
msgintr->intr_handler == intr_handler) {
ddi_remove_softintr(msgintr->intr_id);
msgintr->intr_handler = NULL;
msgintr->intr_id = 0;
msgintr->intr_msg_type = 0;
msgintr->intr_arg = NULL;
msgintr->intr_lock = NULL;
msgintr->intr_state = NULL;
} else {
err = RCEGENERIC;
}
mutex_exit(rcs->dp_state.dp_mutex);
return (err);
}
/*
* To send raw data (firmware s-records) down to the RMC.
* It is provided only to support firmware download.
*/
int
rmc_comm_send_srecord_bp(caddr_t buf, int buflen,
rmc_comm_msg_t *response_bp, uint32_t wait_time)
{
struct rmc_comm_state *rcs;
rmc_comm_dp_state_t *dps;
dp_req_resp_t *drr;
dp_message_t *resp_bp;
clock_t stop_clockt;
int err;
/*
* get the soft state struct (instance 0)
*/
if ((rcs = rmc_comm_getstate(NULL, 0,
"rmc_comm_request_response_bp")) == NULL)
return (RCENOSOFTSTATE);
/*
* sanity check: response_bp buffer must always be provided
*/
if (buf == NULL || response_bp == NULL) {
DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp,invalid args\n"));
return (RCEINVARG);
}
DPRINTF(rcs, DAPI, (CE_CONT, "send_srecord_bp, buflen=%d\n", buflen));
dps = &rcs->dp_state;
drr = &dps->req_resp;
resp_bp = &drr->response;
mutex_enter(dps->dp_mutex);
rmc_comm_wait_enable_to_send(rcs, dps);
/*
* initialization of the request/response data structure
*/
drr->flags = 0;
drr->error_status = 0;
/*
* set the reply buffer: get the buffer already allocated
* for the response
*/
resp_bp->msg_buf = (uint8_t *)response_bp->msg_buf;
resp_bp->msg_bufsiz = (uint16_t)response_bp->msg_len;
/*
* send raw data (s-record) and wait for the reply (BP message)
*/
rmc_comm_bp_srecsend(rcs, (char *)buf, buflen);
/*
* place a lower limit on the shortest amount of time to be
* waited before timing out while communicating with the RMC
*/
if (wait_time < DP_MIN_TIMEOUT)
wait_time = DP_MIN_TIMEOUT;
stop_clockt = ddi_get_lbolt() + drv_usectohz(wait_time * 1000);
if ((err = rmc_comm_wait_bp_reply(rcs, dps, drr,
stop_clockt)) == RCNOERR) {
/*
* get the actual length of the msg
* a negative value means that the reply message
* was too big for the receiver buffer
*/
response_bp->msg_bytes = resp_bp->msg_msglen;
if (response_bp->msg_bytes < 0) {
err = RCEREPTOOBIG;
}
}
rmc_comm_dp_mcleanup(rcs);
rmc_comm_wake_up_next(rcs);
mutex_exit(dps->dp_mutex);
return (err);
}
/*
* To wait for (any) BP message to be received.
* (dp_mutex must be held)
*/
static int
rmc_comm_wait_bp_reply(struct rmc_comm_state *rcs, rmc_comm_dp_state_t *dps,
dp_req_resp_t *drr, clock_t stop_clockt)
{
clock_t clockleft = 1;
int err = RCNOERR;
clockleft = cv_timedwait(drr->cv_wait_reply, dps->dp_mutex,
stop_clockt);
DPRINTF(rcs, DAPI, (CE_CONT,
"reqresp_bp, send: flags=%02x, clktick left=%ld\n",
drr->flags, clockleft));
/*
* Check for error condition first.
* Then, check if it has timeout.
* Then, check if the command has been replied.
*/
if ((drr->flags & MSG_ERROR) != 0) {
err = RCEGENERIC;
} else if (clockleft <= 0) {
/*
* timeout
*/
err = RCETIMEOUT;
} else if ((drr->flags & MSG_RXED_BP) == 0) {
err = RCEGENERIC;
}
return (err);
}
/*
* Wait for the pending_request flag to be cleared and acquire it for our
* own use. The caller is then allowed to start a new request/response
* session with the RMC.
* Note that all send-receive actions to the RMC include a time-out, so
* the pending-request must eventually go away - even if the RMC is down.
* Hence there is no need to timeout the wait action of this function.
* (dp_mutex must be held on entry).
*/
static void
rmc_comm_wait_enable_to_send(struct rmc_comm_state *rcs,
rmc_comm_dp_state_t *dps)
{
DPRINTF(rcs, DAPI, (CE_CONT, "pending request=%d\n",
dps->pending_request));
/*
* A new message can actually grab the lock before the thread
* that has just been signaled. Therefore, we need to double
* check to make sure that pending_request is not already set
* after we wake up.
*
* Potentially this could mean starvation for certain unfortunate
* threads that keep getting woken up and putting back to sleep.
* But the window of such contention is very small to begin with.
*/
while (dps->pending_request) {
/*
* just 'sit and wait' until there are no pending requests
*/
cv_wait(dps->cv_ok_to_send, dps->dp_mutex);
}
/*
* now a request/response can be started. Set the flag so that nobody
* else will be able to send anything.
*/
dps->pending_request = 1;
}
/*
* To wake up one of the threads (if any) waiting for starting a
* request/response session.
* (dp_mutex must be held)
*/
static void
rmc_comm_wake_up_next(struct rmc_comm_state *rcs)
{
/*
* wake up eventual waiting threads...
*/
rcs->dp_state.pending_request = 0;
cv_signal(rcs->dp_state.cv_ok_to_send);
}
/*
* thread which delivers pending request message to the rmc. Some leaf drivers
* cannot afford to wait for a request to be replied/ACKed. Hence, a request
* message is stored temporarily in the state structure and this thread
* gets woken up to deliver it.
*/
static void
rmc_comm_send_pend_req(caddr_t arg)
{
struct rmc_comm_state *rcs;
rmc_comm_drvintf_state_t *dis;
callb_cpr_t cprinfo;
if (arg == NULL) {
thread_exit();
/* NOTREACHED */
}
rcs = (struct rmc_comm_state *)arg;
dis = &rcs->drvi_state;
CALLB_CPR_INIT(&cprinfo, dis->dreq_mutex, callb_generic_cpr,
"rmc_comm_send_pend_req");
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state <= RMC_COMM_DREQ_ST_READY)
dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
for (;;) {
/*
* Wait for someone to tell me to continue.
*/
while (dis->dreq_state == RMC_COMM_DREQ_ST_WAIT) {
CALLB_CPR_SAFE_BEGIN(&cprinfo);
cv_wait(dis->dreq_sig_cv, dis->dreq_mutex);
CALLB_CPR_SAFE_END(&cprinfo, dis->dreq_mutex);
}
/* RMC_COMM_DREQ_ST_EXIT implies signal by _detach(). */
if (dis->dreq_state == RMC_COMM_DREQ_ST_EXIT) {
dis->dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
dis->dreq_tid = 0;
/* dis->dreq_mutex is held at this point! */
CALLB_CPR_EXIT(&cprinfo);
thread_exit();
/* NOTREACHED */
}
ASSERT(dis->dreq_state == RMC_COMM_DREQ_ST_PROCESS);
mutex_exit(dis->dreq_mutex);
/*
* deliver the request (and wait...)
*/
while (rmc_comm_send_req_resp(rcs, &dis->dreq_request, NULL,
RMC_COMM_DREQ_DEFAULT_TIME) == RCEGENERIC) {
}
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state != RMC_COMM_DREQ_ST_EXIT)
dis->dreq_state = RMC_COMM_DREQ_ST_WAIT;
}
}
/*
* start thread to deal with pending requests to be delivered asynchronously
* (i.e. leaf driver do not have to/cannot wait for a reply/ACk of a request)
*/
static int
rmc_comm_dreq_thread_start(struct rmc_comm_state *rcs)
{
rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
int err = 0;
kthread_t *tp;
mutex_enter(dis->dreq_mutex);
if (dis->dreq_state == RMC_COMM_DREQ_ST_NOTSTARTED) {
tp = thread_create(NULL, 0, rmc_comm_send_pend_req,
(caddr_t)rcs, 0, &p0, TS_RUN, maxclsyspri);
dis->dreq_state = RMC_COMM_DREQ_ST_READY;
dis->dreq_tid = tp->t_did;
}
mutex_exit(dis->dreq_mutex);
return (err);
}
/*
* stop the thread (to deliver pending request messages)
*/
static void
rmc_comm_dreq_thread_kill(struct rmc_comm_state *rcs)
{
rmc_comm_drvintf_state_t *dis = &rcs->drvi_state;
kt_did_t tid;
mutex_enter(dis->dreq_mutex);
tid = dis->dreq_tid;
if (tid != 0) {
dis->dreq_state = RMC_COMM_DREQ_ST_EXIT;
dis->dreq_tid = 0;
cv_signal(dis->dreq_sig_cv);
}
mutex_exit(dis->dreq_mutex);
/*
* Wait for rmc_comm_send_pend_req() to finish
*/
if (tid != 0)
thread_join(tid);
}
/*
* init function - start thread to deal with pending requests (no-wait requests)
*/
int
rmc_comm_drvintf_init(struct rmc_comm_state *rcs)
{
int err = 0;
DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_init\n"));
rcs->drvi_state.dreq_state = RMC_COMM_DREQ_ST_NOTSTARTED;
rcs->drvi_state.dreq_tid = 0;
mutex_init(rcs->drvi_state.dreq_mutex, NULL, MUTEX_DRIVER, NULL);
cv_init(rcs->drvi_state.dreq_sig_cv, NULL, CV_DRIVER, NULL);
err = rmc_comm_dreq_thread_start(rcs);
if (err != 0) {
cv_destroy(rcs->drvi_state.dreq_sig_cv);
mutex_destroy(rcs->drvi_state.dreq_mutex);
}
DPRINTF(rcs, DGEN, (CE_CONT, "thread started? err=%d\n", err));
return (err);
}
/*
* fini function - kill thread to deal with pending requests (no-wait requests)
*/
void
rmc_comm_drvintf_fini(struct rmc_comm_state *rcs)
{
DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:stop thread\n"));
rmc_comm_dreq_thread_kill(rcs);
DPRINTF(rcs, DGEN, (CE_CONT, "rmc_comm_drvintf_fini:destroy Mx/CVs\n"));
cv_destroy(rcs->drvi_state.dreq_sig_cv);
mutex_destroy(rcs->drvi_state.dreq_mutex);
}