/*
* 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.
*/
/*
* SBP2 module
*/
/* target routines */
static void sbp2_tgt_init_sobj(sbp2_tgt_t *);
static void sbp2_tgt_fini_sobj(sbp2_tgt_t *);
static int sbp2_tgt_init_params(sbp2_tgt_t *);
static int sbp2_tgt_init_luns(sbp2_tgt_t *, int);
static void sbp2_tgt_fini_luns(sbp2_tgt_t *);
static int sbp2_tgt_init_bus(sbp2_tgt_t *);
static void sbp2_tgt_fini_bus(sbp2_tgt_t *);
static int sbp2_tgt_mgt_request(sbp2_tgt_t *, int *);
int *);
/* lun routines */
/* session routines */
void (*)(void *, sbp2_task_t *), void *);
static void sbp2_ses_fini(sbp2_ses_t *);
static int sbp2_ses_reconnect_orb(sbp2_ses_t *, int *);
/* orb alloc routines */
static void sbp2_orb_freelist_destroy(sbp2_lun_t *);
/* fetch agent routines */
static void sbp2_agent_fini(sbp2_agent_t *);
static void sbp2_agent_acquire_locked(sbp2_agent_t *);
static void sbp2_agent_release_locked(sbp2_agent_t *);
static void sbp2_agent_acquire(sbp2_agent_t *);
static void sbp2_agent_release(sbp2_agent_t *);
static int sbp2_agent_keepalive(sbp2_agent_t *, int *);
static int sbp2_agent_doorbell(sbp2_agent_t *, int *);
static int sbp2_agent_reset(sbp2_agent_t *, int *);
/* callbacks and timeouts */
static void sbp2_task_timeout(void *);
/* other */
static void sbp2_mgt_agent_acquire(sbp2_tgt_t *);
static void sbp2_mgt_agent_release(sbp2_tgt_t *);
static void sbp2_fetch_agent_acquire(sbp2_ses_t *);
static void sbp2_fetch_agent_release(sbp2_ses_t *);
extern struct mod_ops mod_miscops;
&mod_miscops, /* module type */
"Serial Bus Protocol 2 module" /* module name */
};
};
/* tunables */
/*
*
* --- loadable module entry points
*
*/
int
_init(void)
{
return (mod_install(&sbp2_modlinkage));
}
int
_fini(void)
{
return (mod_remove(&sbp2_modlinkage));
}
int
{
}
/*
*
* --- target routines
*
*/
int
{
int ret;
return (SBP2_ECFGROM);
}
return (ret);
}
return (ret);
}
return (ret);
}
return (SBP2_SUCCESS);
}
void
{
}
static void
{
}
static void
{
}
static int
{
uint32_t q;
/* MANAGEMENT_AGENT */
SBP2_KV_MGT_AGENT, 0)) == NULL) {
return (SBP2_ECFGROM);
}
/* Unit_Characteristics */
SBP2_KV_UNCHAR, 0)) == NULL) {
return (SBP2_ECFGROM);
}
/* units of 500 ms -> ms */
/* quadlets -> bytes */
/* some devices return incorrect values */
}
}
return (SBP2_SUCCESS);
}
/*ARGSUSED*/
static int
{
uint32_t q;
/* search for Logical_Unit_Number's */
break;
}
NULL);
}
return (SBP2_SUCCESS);
} else {
return (SBP2_ECFGROM);
}
}
static void
{
int i;
/* destroy each lun */
}
}
/*
* initialize bus buffers and commands
*/
static int
{
int ret;
/*
* We serialize management requests and reuse the same buffers.
*
* mgt ORB
*/
return (ret);
}
/*
* mgt status FIFO
*/
SBP2_SUCCESS) {
return (ret);
}
/*
* login response
*/
/*
* read-only should have been sufficient here, but it causes
*/
SBP2_SUCCESS) {
return (ret);
}
/*
* allocate bus commands
*/
return (ret);
}
return (SBP2_ENOMEM);
}
return (SBP2_SUCCESS);
}
static void
{
}
}
}
}
if (tp->t_mgt_cmd_data) {
}
}
void
{
}
int
{
return (sbp2_tgt_init_bus(tp));
}
/*
* send mgt ORB and wait for status
*
* mgt agent should be acquired
*/
static int
{
int ret;
/*
* When a ctl operation happens from HAL - this could be 0!
* This will happen when a device is disconected and then
* reconnected. Note there are problems with not being able
* this to happen... This problem needs some work elseware!
* This just prevents a needless panic. If we return failure
* the target ultimatly will recover and is usable.
*/
if (tp->t_mgt_cmd_data == 0) {
return (SBP2_FAILURE);
}
/* write ORB address into MANAGEMENT_AGENT */
0);
return (ret);
}
/* wait for login response */
ret = 1;
}
if (!tp->t_mgt_status_rcvd) {
ret = SBP2_ETIMEOUT;
ret = SBP2_SUCCESS;
} else {
ret = SBP2_FAILURE;
}
return (ret);
}
/*
* Send task management request, one of:
*
* ABORT TASK, ABORT TASK SET, LOGICAL UNIT RESET, TARGET RESET
*/
static int
int *berr)
{
int ret;
0);
return (ret);
}
int
{
int ret;
/* issue TARGET RESET */
return (ret);
}
return (SBP2_SUCCESS);
}
int
{
return (SBP2_SUCCESS);
}
int
{
}
{
} else {
return (NULL);
}
}
/*
*
* --- lun routines
*
*/
int
{
int ret;
/* issue LOGICAL UNIT RESET */
return (ret);
}
/* mark all pending tasks reset and notify the driver */
}
}
return (SBP2_SUCCESS);
}
int
{
int ret;
return (SBP2_EINVAL);
}
/* multiple sessions not supported yet */
return (SBP2_EALREADY);
}
return (ret);
}
/* prepare login ORB */
/* send request */
return (ret);
}
/* retrieve response data (XXX sanity checks?) */
sizeof (sbp2_login_resp_t));
/* convert from BE to native endianness */
SBP2_SUCCESS) {
return (ret);
}
return (SBP2_SUCCESS);
}
/*ARGSUSED*/
int
{
if (lp->l_logged_in) {
/* do physical LOGOUT if requested */
if (phys) {
}
}
return (SBP2_SUCCESS);
}
/*
* Issue LOGOUT mgt orb and wait for response. We are not interested in
* the success at the time, since the device may be disconnected or hung,
* just trying to make the best effort.
*/
static void
{
/* prepare logout ORB */
0);
/* send request */
}
static boolean_t
{
return (ret);
}
/*
*
* --- session routines
*
*/
static int
{
int ret;
/*
* status FIFO for block requests
*/
SBP2_SUCCESS) {
return (ret);
}
return (SBP2_SUCCESS);
}
static void
{
}
}
int
{
int ret;
/* prevent new tasks from being submitted */
/*
* From 10.5 Task management event matrix:
* Immediately upon detection of a bus reset, all command
* block fetch agents transition to the reset state and
* their associated task sets are cleared without
* the return of completion status.
*
* Reset pending tasks so we can retry them later.
*/
return (ret);
}
/*
* Send reconnect ORB. If operation fails, set lp->l_logged_in = B_FALSE.
*/
static int
{
int ret;
/* prepare login ORB */
0);
/* send request */
} else {
/* after successful reset fetch agent is in RESET state */
}
return (ret);
}
static sbp2_task_t *
{
break;
}
}
return (task);
}
/*
* This is where tasks (command ORB's) are signalled to the target.
* 'task' argument is allowed to be NULL, in which case the task will be
* taken from the current task list.
*
* Tasks are signalled one at a time by writing into ORB_POINTER register.
* While SBP-2 allows dynamic task list updates and using DOORBELL register,
* some devices have bugs that prevent using this strategy: e.g. some LaCie
* HDD's can corrupt data. Data integrity is more important than performance.
*/
int
{
int ret;
return (SBP2_ENODEV);
}
/* if task provided, append it to the list */
}
/* if there is already a task in flight, exit */
return (SBP2_SUCCESS);
}
/*
* cannot submit tasks from interrupt context,
* upper layer driver is responsible to call nudge
*/
if (servicing_interrupt()) {
return (SBP2_ECONTEXT);
}
/* no active task, grab the first one on the list in INIT state */
return (SBP2_SUCCESS);
}
/* can't work with a dead agent */
goto error;
}
/*
* In theory, we should schedule task timeout after it's been submitted.
* However, some fast tasks complete even before timeout is scheduled.
* To avoid additional complications in the code, schedule timeout now.
*/
if (task->ts_timeout > 0) {
}
/* notify fetch agent */
&task->ts_bus_error);
if (ret != SBP2_SUCCESS) {
if (task->ts_timeout_id != 0) {
task->ts_timeout_id = 0;
(void) untimeout(timeout_id);
}
goto error;
}
return (SBP2_SUCCESS);
/*
* Return immediate error if failed task is the one being submitted,
* otherwise use callback.
*/
/*
* Remove task from the list. It is important not to change task state
* to SBP2_TASK_COMP while it's still on the list, to avoid race with
* upper layer driver (e.g. scsa1394).
*/
if (callback) {
return (SBP2_SUCCESS);
} else {
/* upper layer driver is responsible to call nudge */
return (SBP2_FAILURE);
}
}
void
{
}
/*
* append task to the task list
*/
static void
{
} else {
}
sp->s_task_cnt++;
}
}
/*
* remove task from the task list
*/
static int
{
sp->s_task_cnt--;
} else { /* but not last */
}
} else { /* in the middle */
}
return (SBP2_SUCCESS);
}
int
{
int ret;
return (ret);
}
/*
* Return first task on the list in specified state.
*/
{
break;
}
}
return (task);
}
/*
* Remove first task on the list. Returns pointer to the removed task or NULL.
*/
{
}
return (task);
}
/*
* Remove first task on the list only if it's in specified state.
* Returns pointer to the removed task or NULL.
*/
{
}
return (task);
}
/*
* Remove first task on the list. If there's timeout, untimeout it.
* Returns pointer to the removed task or NULL.
*/
{
}
task->ts_timeout_id = 0;
(void) untimeout(timeout_id);
}
return (task);
}
/*
* Reset pending tasks on the list to their initial state.
*/
static void
{
/* cancel timeout */
task->ts_timeout_id = 0;
(void) untimeout(timeout_id);
}
/* update ORB nodeID */
}
}
int
{
}
int
{
/* mark ORB as dummy ORB */
return (ret);
}
int
{
int ret;
return (ret);
}
/*
*
* ORB functions
*
* allocate ORB resources
*
* we maintain a freelist of ORB's for faster allocation
*/
/*ARGSUSED*/
static sbp2_bus_buf_t *
{
}
}
return (buf);
}
static int
{
int ret;
} else {
}
ret = SBP2_SUCCESS;
} else {
ret = SBP2_FAILURE;
}
return (ret);
}
static void
{
}
}
int
{
int buf_len;
int ret;
/* try freelist first */
return (SBP2_SUCCESS);
}
/* if no free buffers, allocate new */
}
return (ret);
}
void
{
}
}
}
void *
{
}
void
{
}
/*
*
* --- fetch agent routines
*
*/
static int
{
int ret;
/* paranoia */
if (offset == 0) {
return (SBP2_FAILURE);
}
/*
* allocate bus commands
*/
return (ret);
}
return (SBP2_ENOMEM);
}
#ifndef __lock_lint
#endif
return (SBP2_SUCCESS);
}
static void
{
/* free bus commands */
}
}
}
static void
{
while (ap->a_acquired) {
}
}
static void
{
}
static void
{
}
static void
{
}
static int
{
if (!acquired) {
}
if (!acquired) {
}
}
return (ret);
}
#ifndef __lock_lint
static int
{
}
#endif
/*
* write into ORB_POINTER register and make sure it reached target
*
* From E.2: "If no acknowledgement is received by the initiator after a write
* to the ORB_POINTER register, the initiator should not retry the write.
* The recommended method for error recovery is a write to the AGENT_RESET
* register." So we can retry, but not in case of timeout.
*/
static int
{
int i = 0;
int ret;
for (;;) {
return (ret);
}
if ((ret == SBP2_ETIMEOUT) ||
(++i > sbp2_write_orbp_nretries)) {
break;
}
if (sbp2_write_orbp_delay > 0) {
}
}
return (ret);
}
/*
* reset fetch agent by writing into AGENT_RESET register
*/
static int
{
int i = 0;
int ret;
for (;;) {
0, berr)) == SBP2_SUCCESS) {
break;
}
if (++i > sbp2_submit_reset_nretries) {
break;
}
if (sbp2_submit_reset_delay > 0) {
}
}
return (ret);
}
/*
*
* --- callbacks and timeouts
*
*/
/*
* Status FIFO callback for mgt ORB's.
*/
/*ARGSUSED*/
static void
{
int len;
/* 8 bytes minimum */
if (len < 8) {
return;
}
/* convert 2-quadlet header from BE to native endianness */
return;
}
/* make a local copy of status block */
/* wake up waiter */
}
static void
{
/* cancelled? */
if (task->ts_timeout_id == 0) {
return;
}
task->ts_timeout_id = 0;
/* avoid race with other callbacks */
return;
}
}
/* we mark agent DEAD so it's reset before next task is submitted */
}
/*
* Status FIFO callback for command ORB's. Also used for login ORB.
*/
/*ARGSUSED*/
static void
{
int len;
/* 8 bytes minimum */
if (len < 8) {
return;
}
/* convert 2-quadlet header from BE32 to native endianness */
/* login ORB status? */
/* wake up waiter */
return;
}
/* dismiss unsolicited status */
if (src == SBP2_ST_SRC_UNSOLICITED) {
return;
}
/* find task corresponding to this ORB pointer */
return;
}
/*
* Copy status block into a local buffer.
*
* Note: (ref: B.2) "SBP-2 permits the return of a status block between
* two and eight quadlets in length. When a truncated status block
* is stored, the omited quadlets shall be interpreted as if zero
* values were stored."
*/
task->ts_timeout_id = 0;
(void) untimeout(timeout_id);
}
/* determine agent state */
}
/* avoid race with other callbacks */
return;
}
}
}
/*
*
* --- other
*
* since mgt agent is shared between LUNs and login sessions,
* it is safer to serialize mgt requests
*/
static void
{
while (tp->t_mgt_agent_acquired) {
}
}
static void
{
}