if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { if (se_cmd->data_direction == DMA_TO_DEVICE) { /* residual data from an underflow write */
rsp->flags = SRP_RSP_FLAG_DOUNDER;
rsp->data_out_res_cnt = cpu_to_be32(residual_count);
} elseif (se_cmd->data_direction == DMA_FROM_DEVICE) { /* residual data from an underflow read */
rsp->flags = SRP_RSP_FLAG_DIUNDER;
rsp->data_in_res_cnt = cpu_to_be32(residual_count);
}
} elseif (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { if (se_cmd->data_direction == DMA_TO_DEVICE) { /* residual data from an overflow write */
rsp->flags = SRP_RSP_FLAG_DOOVER;
rsp->data_out_res_cnt = cpu_to_be32(residual_count);
} elseif (se_cmd->data_direction == DMA_FROM_DEVICE) { /* residual data from an overflow read */
rsp->flags = SRP_RSP_FLAG_DIOVER;
rsp->data_in_res_cnt = cpu_to_be32(residual_count);
}
}
}
/** * connection_broken() - Determine if the connection to the client is good * @vscsi: Pointer to our adapter structure * * This function attempts to send a ping MAD to the client. If the call to * queue the request returns H_CLOSED then the connection has been broken * and the function returns TRUE. * * EXECUTION ENVIRONMENT: * Interrupt or Process environment
*/ staticbool connection_broken(struct scsi_info *vscsi)
{ struct viosrp_crq *crq;
u64 buffer[2] = { 0, 0 }; long h_return_code; bool rc = false;
/** * ibmvscsis_unregister_command_q() - Helper Function-Unregister Command Queue * @vscsi: Pointer to our adapter structure * * This function calls h_free_q then frees the interrupt bit etc. * It must release the lock before doing so because of the time it can take * for h_free_crq in PHYP * NOTE: * the caller must make sure that state and or flags will prevent * interrupt handler from scheduling work. * * anyone calling this function may need to set the CRQ_CLOSED flag * we can't do it here, because we don't have the lock * * EXECUTION ENVIRONMENT: * Process level
*/ staticlong ibmvscsis_unregister_command_q(struct scsi_info *vscsi)
{ long qrc; long rc = ADAPT_SUCCESS; int ticks = 0;
do {
qrc = h_free_crq(vscsi->dds.unit_id); switch (qrc) { case H_SUCCESS:
spin_lock_bh(&vscsi->intr_lock);
vscsi->flags &= ~PREP_FOR_SUSPEND_FLAGS;
spin_unlock_bh(&vscsi->intr_lock); break;
case H_HARDWARE: case H_PARAMETER:
dev_err(&vscsi->dev, "unregister_command_q: error from h_free_crq %ld\n",
qrc);
rc = ERROR; break;
case H_BUSY: case H_LONG_BUSY_ORDER_1_MSEC: /* msleep not good for small values */
usleep_range(1000, 2000);
ticks += 1; break; case H_LONG_BUSY_ORDER_10_MSEC:
usleep_range(10000, 20000);
ticks += 10; break; case H_LONG_BUSY_ORDER_100_MSEC:
msleep(100);
ticks += 100; break; case H_LONG_BUSY_ORDER_1_SEC:
ssleep(1);
ticks += 1000; break; case H_LONG_BUSY_ORDER_10_SEC:
ssleep(10);
ticks += 10000; break; case H_LONG_BUSY_ORDER_100_SEC:
ssleep(100);
ticks += 100000; break; default:
dev_err(&vscsi->dev, "unregister_command_q: unknown error %ld from h_free_crq\n",
qrc);
rc = ERROR; break;
}
/* * dont wait more then 300 seconds * ticks are in milliseconds more or less
*/ if (ticks > 300000 && qrc != H_SUCCESS) {
rc = ERROR;
dev_err(&vscsi->dev, "Excessive wait for h_free_crq\n");
}
} while (qrc != H_SUCCESS && rc == ADAPT_SUCCESS);
/** * ibmvscsis_delete_client_info() - Helper function to Delete Client Info * @vscsi: Pointer to our adapter structure * @client_closed: True if client closed its queue * * Deletes information specific to the client when the client goes away * * EXECUTION ENVIRONMENT: * Interrupt or Process
*/ staticvoid ibmvscsis_delete_client_info(struct scsi_info *vscsi, bool client_closed)
{
vscsi->client_cap = 0;
/* * Some things we don't want to clear if we're closing the queue, * because some clients don't resend the host handshake when they * get a transport event.
*/ if (client_closed)
vscsi->client_data.os_type = 0;
}
/** * ibmvscsis_free_command_q() - Free Command Queue * @vscsi: Pointer to our adapter structure * * This function calls unregister_command_q, then clears interrupts and * any pending interrupt acknowledgments associated with the command q. * It also clears memory if there is no error. * * PHYP did not meet the PAPR architecture so that we must give up the * lock. This causes a timing hole regarding state change. To close the * hole this routine does accounting on any change that occurred during * the time the lock is not held. * NOTE: must give up and then acquire the interrupt lock, the caller must * make sure that state and or flags will prevent interrupt handler from * scheduling work. * * EXECUTION ENVIRONMENT: * Process level, interrupt lock is held
*/ staticlong ibmvscsis_free_command_q(struct scsi_info *vscsi)
{ int bytes;
u32 flags_under_lock;
u16 state_under_lock; long rc = ADAPT_SUCCESS;
if (!(vscsi->flags & CRQ_CLOSED)) {
vio_disable_interrupts(vscsi->dma_dev);
/** * ibmvscsis_cmd_q_dequeue() - Get valid Command element * @mask: Mask to use in case index wraps * @current_index: Current index into command queue * @base_addr: Pointer to start of command queue * * Returns a pointer to a valid command element or NULL, if the command * queue is empty * * EXECUTION ENVIRONMENT: * Interrupt environment, interrupt lock held
*/ staticstruct viosrp_crq *ibmvscsis_cmd_q_dequeue(uint mask,
uint *current_index, struct viosrp_crq *base_addr)
{ struct viosrp_crq *ptr;
/** * ibmvscsis_check_init_msg() - Check init message valid * @vscsi: Pointer to our adapter structure * @format: Pointer to return format of Init Message, if any. * Set to UNUSED_FORMAT if no Init Message in queue. * * Checks if an initialize message was queued by the initiatior * after the queue was created and before the interrupt was enabled. * * EXECUTION ENVIRONMENT: * Process level only, interrupt lock held
*/ staticlong ibmvscsis_check_init_msg(struct scsi_info *vscsi, uint *format)
{ struct viosrp_crq *crq; long rc = ADAPT_SUCCESS;
/* * the caller has ensured no initialize message was * sent after the queue was * created so there should be no other message on the queue.
*/
crq = ibmvscsis_cmd_q_dequeue(vscsi->cmd_q.mask,
&vscsi->cmd_q.index,
vscsi->cmd_q.base_addr); if (crq) {
*format = (uint)(crq->format);
rc = ERROR;
crq->valid = INVALIDATE_CMD_RESP_EL;
dma_rmb();
}
} else {
*format = (uint)(crq->format);
rc = ERROR;
crq->valid = INVALIDATE_CMD_RESP_EL;
dma_rmb();
}
return rc;
}
/** * ibmvscsis_disconnect() - Helper function to disconnect * @work: Pointer to work_struct, gives access to our adapter structure * * An error has occurred or the driver received a Transport event, * and the driver is requesting that the command queue be de-registered * in a safe manner. If there is no outstanding I/O then we can stop the * queue. If we are restarting the queue it will be reflected in the * the state of the adapter. * * EXECUTION ENVIRONMENT: * Process environment
*/ staticvoid ibmvscsis_disconnect(struct work_struct *work)
{ struct scsi_info *vscsi = container_of(work, struct scsi_info,
proc_work);
u16 new_state; bool wait_idle = false;
dev_dbg(&vscsi->dev, "disconnect: flags 0x%x, state 0x%hx\n",
vscsi->flags, vscsi->state);
/* * check which state we are in and see if we * should transition to the new state
*/ switch (vscsi->state) { /* Should never be called while in this state. */ case NO_QUEUE: /* * Can never transition from this state; * igonore errors and logout.
*/ case UNCONFIGURING: break;
/* can transition from this state to UNCONFIGURING */ case ERR_DISCONNECT: if (new_state == UNCONFIGURING)
vscsi->state = new_state; break;
/* * Can transition from this state to unconfiguring * or err disconnect.
*/ case ERR_DISCONNECT_RECONNECT: switch (new_state) { case UNCONFIGURING: case ERR_DISCONNECT:
vscsi->state = new_state; break;
case WAIT_IDLE: break; default: break;
} break;
/* can transition from this state to UNCONFIGURING */ case ERR_DISCONNECTED: if (new_state == UNCONFIGURING)
vscsi->state = new_state; break;
case WAIT_ENABLED: switch (new_state) { case UNCONFIGURING:
vscsi->state = new_state;
vscsi->flags |= RESPONSE_Q_DOWN;
vscsi->flags &= ~(SCHEDULE_DISCONNECT |
DISCONNECT_SCHEDULED);
dma_rmb(); if (vscsi->flags & CFG_SLEEPING) {
vscsi->flags &= ~CFG_SLEEPING;
complete(&vscsi->unconfig);
} break;
/* should never happen */ case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT: case WAIT_IDLE:
dev_err(&vscsi->dev, "disconnect: invalid state %d for WAIT_IDLE\n",
vscsi->state); break;
} break;
case WAIT_IDLE: switch (new_state) { case UNCONFIGURING:
vscsi->flags |= RESPONSE_Q_DOWN;
vscsi->state = new_state;
vscsi->flags &= ~(SCHEDULE_DISCONNECT |
DISCONNECT_SCHEDULED);
ibmvscsis_free_command_q(vscsi); break; case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT:
vscsi->state = new_state; break;
} break;
/* * Initiator has not done a successful srp login * or has done a successful srp logout ( adapter was not * busy). In the first case there can be responses queued * waiting for space on the initiators response queue (MAD) * The second case the adapter is idle. Assume the worse case, * i.e. the second case.
*/ case WAIT_CONNECTION: case CONNECTED: case SRP_PROCESSING:
wait_idle = true;
vscsi->state = new_state; break;
/* can transition from this state to UNCONFIGURING */ case UNDEFINED: if (new_state == UNCONFIGURING)
vscsi->state = new_state; break; default: break;
}
if (wait_idle) {
dev_dbg(&vscsi->dev, "disconnect start wait, active %d, sched %d\n",
(int)list_empty(&vscsi->active_q),
(int)list_empty(&vscsi->schedule_q)); if (!list_empty(&vscsi->active_q) ||
!list_empty(&vscsi->schedule_q)) {
vscsi->flags |= WAIT_FOR_IDLE;
dev_dbg(&vscsi->dev, "disconnect flags 0x%x\n",
vscsi->flags); /* * This routine is can not be called with the interrupt * lock held.
*/
spin_unlock_bh(&vscsi->intr_lock);
wait_for_completion(&vscsi->wait_idle);
spin_lock_bh(&vscsi->intr_lock);
}
dev_dbg(&vscsi->dev, "disconnect stop wait\n");
ibmvscsis_adapter_idle(vscsi);
}
spin_unlock_bh(&vscsi->intr_lock);
}
/** * ibmvscsis_post_disconnect() - Schedule the disconnect * @vscsi: Pointer to our adapter structure * @new_state: State to move to after disconnecting * @flag_bits: Flags to turn on in adapter structure * * If it's already been scheduled, then see if we need to "upgrade" * the new state (if the one passed in is more "severe" than the * previous one). * * PRECONDITION: * interrupt lock is held
*/ staticvoid ibmvscsis_post_disconnect(struct scsi_info *vscsi, uint new_state,
uint flag_bits)
{
uint state;
/* check the validity of the new state */ switch (new_state) { case UNCONFIGURING: case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT: case WAIT_IDLE: break;
default:
dev_err(&vscsi->dev, "post_disconnect: Invalid new state %d\n",
new_state); return;
}
/** * ibmvscsis_handle_init_compl_msg() - Respond to an Init Complete Message * @vscsi: Pointer to our adapter structure * * Must be called with interrupt lock held.
*/ staticlong ibmvscsis_handle_init_compl_msg(struct scsi_info *vscsi)
{ long rc = ADAPT_SUCCESS;
switch (vscsi->state) { case NO_QUEUE: case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT: case ERR_DISCONNECTED: case UNCONFIGURING: case UNDEFINED:
rc = ERROR; break;
case WAIT_CONNECTION:
vscsi->state = CONNECTED; break;
case WAIT_IDLE: case SRP_PROCESSING: case CONNECTED: case WAIT_ENABLED: default:
rc = ERROR;
dev_err(&vscsi->dev, "init_msg: invalid state %d to get init compl msg\n",
vscsi->state);
ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); break;
}
return rc;
}
/** * ibmvscsis_handle_init_msg() - Respond to an Init Message * @vscsi: Pointer to our adapter structure * * Must be called with interrupt lock held.
*/ staticlong ibmvscsis_handle_init_msg(struct scsi_info *vscsi)
{ long rc = ADAPT_SUCCESS;
switch (vscsi->state) { case WAIT_CONNECTION:
rc = ibmvscsis_send_init_message(vscsi, INIT_COMPLETE_MSG); switch (rc) { case H_SUCCESS:
vscsi->state = CONNECTED; break;
case H_PARAMETER:
dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
rc);
ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT, 0); break;
case H_DROPPED:
dev_err(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
rc);
rc = ERROR;
ibmvscsis_post_disconnect(vscsi,
ERR_DISCONNECT_RECONNECT, 0); break;
case H_CLOSED:
dev_warn(&vscsi->dev, "init_msg: failed to send, rc %ld\n",
rc);
rc = 0; break;
} break;
case UNDEFINED:
rc = ERROR; break;
case UNCONFIGURING: break;
case WAIT_ENABLED: case CONNECTED: case SRP_PROCESSING: case WAIT_IDLE: case NO_QUEUE: case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT: case ERR_DISCONNECTED: default:
rc = ERROR;
dev_err(&vscsi->dev, "init_msg: invalid state %d to get init msg\n",
vscsi->state);
ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); break;
}
return rc;
}
/** * ibmvscsis_init_msg() - Respond to an init message * @vscsi: Pointer to our adapter structure * @crq: Pointer to CRQ element containing the Init Message * * EXECUTION ENVIRONMENT: * Interrupt, interrupt lock held
*/ staticlong ibmvscsis_init_msg(struct scsi_info *vscsi, struct viosrp_crq *crq)
{ long rc = ADAPT_SUCCESS;
dev_dbg(&vscsi->dev, "init_msg: state 0x%hx\n", vscsi->state);
/** * ibmvscsis_reset_queue() - Reset CRQ Queue * @vscsi: Pointer to our adapter structure * * This function calls h_free_q and then calls h_reg_q and does all * of the bookkeeping to get us back to where we can communicate. * * Actually, we don't always call h_free_crq. A problem was discovered * where one partition would close and reopen his queue, which would * cause his partner to get a transport event, which would cause him to * close and reopen his queue, which would cause the original partition * to get a transport event, etc., etc. To prevent this, we don't * actually close our queue if the client initiated the reset, (i.e. * either we got a transport event or we have detected that the client's * queue is gone) * * EXECUTION ENVIRONMENT: * Process environment, called with interrupt lock held
*/ staticvoid ibmvscsis_reset_queue(struct scsi_info *vscsi)
{ int bytes; long rc = ADAPT_SUCCESS;
/** * ibmvscsis_free_cmd_resources() - Free command resources * @vscsi: Pointer to our adapter structure * @cmd: Command which is not longer in use * * Must be called with interrupt lock held.
*/ staticvoid ibmvscsis_free_cmd_resources(struct scsi_info *vscsi, struct ibmvscsis_cmd *cmd)
{ struct iu_entry *iue = cmd->iue;
switch (cmd->type) { case TASK_MANAGEMENT: case SCSI_CDB: /* * When the queue goes down this value is cleared, so it * cannot be cleared in this general purpose function.
*/ if (vscsi->debit)
vscsi->debit -= 1; break; case ADAPTER_MAD:
vscsi->flags &= ~PROCESSING_MAD; break; case UNSET_TYPE: break; default:
dev_err(&vscsi->dev, "free_cmd_resources unknown type %d\n",
cmd->type); break;
}
/** * ibmvscsis_ready_for_suspend() - Helper function to call VIOCTL * @vscsi: Pointer to our adapter structure * @idle: Indicates whether we were called from adapter_idle. This * is important to know if we need to do a disconnect, since if * we're called from adapter_idle, we're still processing the * current disconnect, so we can't just call post_disconnect. * * This function is called when the adapter is idle when phyp has sent * us a Prepare for Suspend Transport Event. * * EXECUTION ENVIRONMENT: * Process or interrupt environment called with interrupt lock held
*/ staticlong ibmvscsis_ready_for_suspend(struct scsi_info *vscsi, bool idle)
{ long rc = 0; struct viosrp_crq *crq;
/* See if there is a Resume event in the queue */
crq = vscsi->cmd_q.base_addr + vscsi->cmd_q.index;
dev_dbg(&vscsi->dev, "ready_suspend: flags 0x%x, state 0x%hx crq_valid:%x\n",
vscsi->flags, vscsi->state, (int)crq->valid);
if ((crq->valid) && ((crq->valid != VALID_TRANS_EVENT) ||
(crq->format != RESUME_FROM_SUSP)))
dev_err(&vscsi->dev, "Invalid element in CRQ after Prepare for Suspend");
}
/** * ibmvscsis_trans_event() - Handle a Transport Event * @vscsi: Pointer to our adapter structure * @crq: Pointer to CRQ entry containing the Transport Event * * Do the logic to close the I_T nexus. This function may not * behave to specification. * * EXECUTION ENVIRONMENT: * Interrupt, interrupt lock held
*/ staticlong ibmvscsis_trans_event(struct scsi_info *vscsi, struct viosrp_crq *crq)
{ long rc = ADAPT_SUCCESS;
dev_dbg(&vscsi->dev, "trans_event: format %d, flags 0x%x, state 0x%hx\n",
(int)crq->format, vscsi->flags, vscsi->state);
switch (crq->format) { case MIGRATED: case PARTNER_FAILED: case PARTNER_DEREGISTER:
ibmvscsis_delete_client_info(vscsi, true); if (crq->format == MIGRATED)
vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE; switch (vscsi->state) { case NO_QUEUE: case ERR_DISCONNECTED: case UNDEFINED: break;
case UNCONFIGURING:
vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT); break;
case WAIT_ENABLED: break;
case WAIT_CONNECTION: break;
case CONNECTED:
ibmvscsis_post_disconnect(vscsi, WAIT_IDLE,
(RESPONSE_Q_DOWN |
TRANS_EVENT)); break;
case SRP_PROCESSING: if ((vscsi->debit > 0) ||
!list_empty(&vscsi->schedule_q) ||
!list_empty(&vscsi->waiting_rsp) ||
!list_empty(&vscsi->active_q)) {
dev_dbg(&vscsi->dev, "debit %d, sched %d, wait %d, active %d\n",
vscsi->debit,
(int)list_empty(&vscsi->schedule_q),
(int)list_empty(&vscsi->waiting_rsp),
(int)list_empty(&vscsi->active_q));
dev_warn(&vscsi->dev, "connection lost with outstanding work\n");
} else {
dev_dbg(&vscsi->dev, "trans_event: SRP Processing, but no outstanding work\n");
}
case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT: case WAIT_IDLE:
vscsi->flags |= (RESPONSE_Q_DOWN | TRANS_EVENT); break;
} break;
case PREPARE_FOR_SUSPEND:
dev_dbg(&vscsi->dev, "Prep for Suspend, crq status = 0x%x\n",
(int)crq->status); switch (vscsi->state) { case ERR_DISCONNECTED: case WAIT_CONNECTION: case CONNECTED:
ibmvscsis_ready_for_suspend(vscsi, false); break; case SRP_PROCESSING:
vscsi->resume_state = vscsi->state;
vscsi->flags |= PREP_FOR_SUSPEND_PENDING; if (crq->status == CRQ_ENTRY_OVERWRITTEN)
vscsi->flags |= PREP_FOR_SUSPEND_OVERWRITE;
ibmvscsis_post_disconnect(vscsi, WAIT_IDLE, 0); break; case NO_QUEUE: case UNDEFINED: case UNCONFIGURING: case WAIT_ENABLED: case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT: case WAIT_IDLE:
dev_err(&vscsi->dev, "Invalid state for Prepare for Suspend Trans Event: 0x%x\n",
vscsi->state); break;
} break;
case RESUME_FROM_SUSP:
dev_dbg(&vscsi->dev, "Resume from Suspend, crq status = 0x%x\n",
(int)crq->status); if (vscsi->flags & PREP_FOR_SUSPEND_PENDING) {
vscsi->flags |= PREP_FOR_SUSPEND_ABORTED;
} else { if ((crq->status == CRQ_ENTRY_OVERWRITTEN) ||
(vscsi->flags & PREP_FOR_SUSPEND_OVERWRITE)) {
ibmvscsis_post_disconnect(vscsi,
ERR_DISCONNECT_RECONNECT,
0);
vscsi->flags &= ~PREP_FOR_SUSPEND_OVERWRITE;
}
} break;
/** * ibmvscsis_poll_cmd_q() - Poll Command Queue * @vscsi: Pointer to our adapter structure * * Called to handle command elements that may have arrived while * interrupts were disabled. * * EXECUTION ENVIRONMENT: * intr_lock must be held
*/ staticvoid ibmvscsis_poll_cmd_q(struct scsi_info *vscsi)
{ struct viosrp_crq *crq; long rc; bool ack = true; volatile u8 valid;
dev_dbg(&vscsi->dev, "poll_cmd_q: flags 0x%x, state 0x%hx, q index %ud\n",
vscsi->flags, vscsi->state, vscsi->cmd_q.index);
if (!rc) {
rc = ibmvscsis_parse_command(vscsi, crq);
} else { if ((uint)crq->valid == VALID_TRANS_EVENT) { /* * must service the transport layer events even * in an error state, dont break out until all * the consecutive transport events have been * processed
*/
rc = ibmvscsis_trans_event(vscsi, crq);
} elseif (vscsi->flags & TRANS_EVENT) { /* * if a tranport event has occurred leave * everything but transport events on the queue
*/
dev_dbg(&vscsi->dev, "poll_cmd_q, ignoring\n");
/* * need to decrement the queue index so we can * look at the elment again
*/ if (vscsi->cmd_q.index)
vscsi->cmd_q.index -= 1; else /* * index is at 0 it just wrapped. * have it index last element in q
*/
vscsi->cmd_q.index = vscsi->cmd_q.mask; break;
}
}
/** * ibmvscsis_free_cmd_qs() - Free elements in queue * @vscsi: Pointer to our adapter structure * * Free all of the elements on all queues that are waiting for * whatever reason. * * PRECONDITION: * Called with interrupt lock held
*/ staticvoid ibmvscsis_free_cmd_qs(struct scsi_info *vscsi)
{ struct ibmvscsis_cmd *cmd, *nxt;
/** * ibmvscsis_get_free_cmd() - Get free command from list * @vscsi: Pointer to our adapter structure * * Must be called with interrupt lock held.
*/ staticstruct ibmvscsis_cmd *ibmvscsis_get_free_cmd(struct scsi_info *vscsi)
{ struct ibmvscsis_cmd *cmd = NULL; struct iu_entry *iue;
/** * ibmvscsis_adapter_idle() - Helper function to handle idle adapter * @vscsi: Pointer to our adapter structure * * This function is called when the adapter is idle when the driver * is attempting to clear an error condition. * The adapter is considered busy if any of its cmd queues * are non-empty. This function can be invoked * from the off level disconnect function. * * EXECUTION ENVIRONMENT: * Process environment called with interrupt lock held
*/ staticvoid ibmvscsis_adapter_idle(struct scsi_info *vscsi)
{ int free_qs = false; long rc = 0;
dev_dbg(&vscsi->dev, "adapter_idle: flags 0x%x, state 0x%hx\n",
vscsi->flags, vscsi->state);
/* Only need to free qs if we're disconnecting from client */ if (vscsi->state != WAIT_CONNECTION || vscsi->flags & TRANS_EVENT)
free_qs = true;
switch (vscsi->state) { case UNCONFIGURING:
ibmvscsis_free_command_q(vscsi);
dma_rmb();
isync(); if (vscsi->flags & CFG_SLEEPING) {
vscsi->flags &= ~CFG_SLEEPING;
complete(&vscsi->unconfig);
} break; case ERR_DISCONNECT_RECONNECT:
ibmvscsis_reset_queue(vscsi);
dev_dbg(&vscsi->dev, "adapter_idle, disc_rec: flags 0x%x\n",
vscsi->flags); break;
case ERR_DISCONNECT:
ibmvscsis_free_command_q(vscsi);
vscsi->flags &= ~(SCHEDULE_DISCONNECT | DISCONNECT_SCHEDULED);
vscsi->flags |= RESPONSE_Q_DOWN; if (vscsi->tport.enabled)
vscsi->state = ERR_DISCONNECTED; else
vscsi->state = WAIT_ENABLED;
dev_dbg(&vscsi->dev, "adapter_idle, disc: flags 0x%x, state 0x%hx\n",
vscsi->flags, vscsi->state); break;
case ERR_DISCONNECTED:
vscsi->flags &= ~DISCONNECT_SCHEDULED;
dev_dbg(&vscsi->dev, "adapter_idle, disconnected: flags 0x%x, state 0x%hx\n",
vscsi->flags, vscsi->state); break;
default:
dev_err(&vscsi->dev, "adapter_idle: in invalid state %d\n",
vscsi->state); break;
}
if (free_qs)
ibmvscsis_free_cmd_qs(vscsi);
/* * There is a timing window where we could lose a disconnect request. * The known path to this window occurs during the DISCONNECT_RECONNECT * case above: reset_queue calls free_command_q, which will release the * interrupt lock. During that time, a new post_disconnect call can be * made with a "more severe" state (DISCONNECT or UNCONFIGURING). * Because the DISCONNECT_SCHEDULED flag is already set, post_disconnect * will only set the new_state. Now free_command_q reacquires the intr * lock and clears the DISCONNECT_SCHEDULED flag (using PRESERVE_FLAG_ * FIELDS), and the disconnect is lost. This is particularly bad when * the new disconnect was for UNCONFIGURING, since the unconfigure hangs * forever. * Fix is that free command queue sets acr state and acr flags if there * is a change under the lock * note free command queue writes to this state it clears it * before releasing the lock, different drivers call the free command * queue different times so dont initialize above
*/ if (vscsi->phyp_acr_state != 0) { /* * set any bits in flags that may have been cleared by * a call to free command queue in switch statement * or reset queue
*/
vscsi->flags |= vscsi->phyp_acr_flags;
ibmvscsis_post_disconnect(vscsi, vscsi->phyp_acr_state, 0);
vscsi->phyp_acr_state = 0;
vscsi->phyp_acr_flags = 0;
/** * ibmvscsis_copy_crq_packet() - Copy CRQ Packet * @vscsi: Pointer to our adapter structure * @cmd: Pointer to command element to use to process the request * @crq: Pointer to CRQ entry containing the request * * Copy the srp information unit from the hosted * partition using remote dma * * EXECUTION ENVIRONMENT: * Interrupt, interrupt lock held
*/ staticlong ibmvscsis_copy_crq_packet(struct scsi_info *vscsi, struct ibmvscsis_cmd *cmd, struct viosrp_crq *crq)
{ struct iu_entry *iue = cmd->iue; long rc = 0;
u16 len;
len = be16_to_cpu(crq->IU_length); if ((len > SRP_MAX_IU_LEN) || (len == 0)) {
dev_err(&vscsi->dev, "copy_crq: Invalid len %d passed", len);
ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); return SRP_VIOLATION;
}
/* * Copy client info, but ignore partition number, which we * already got from phyp - unless we failed to get it from * phyp (e.g. if we're running on a p5 system).
*/ if (vscsi->client_data.partition_number == 0)
vscsi->client_data.partition_number =
be32_to_cpu(info->partition_number);
strscpy(vscsi->client_data.srp_version, info->srp_version, sizeof(vscsi->client_data.srp_version));
strscpy(vscsi->client_data.partition_name, info->partition_name, sizeof(vscsi->client_data.partition_name));
vscsi->client_data.mad_version = be32_to_cpu(info->mad_version);
vscsi->client_data.os_type = be32_to_cpu(info->os_type);
/** * ibmvscsis_cap_mad() - Service a Capabilities MAnagement Data gram * @vscsi: Pointer to our adapter structure * @iue: Information Unit containing the Capabilities MAD request * * NOTE: if you return an error from this routine you must be * disconnecting or you will cause a hang * * EXECUTION ENVIRONMENT: * Interrupt called with adapter lock held
*/ staticint ibmvscsis_cap_mad(struct scsi_info *vscsi, struct iu_entry *iue)
{ struct viosrp_capabilities *mad = &vio_iu(iue)->mad.capabilities; struct capabilities *cap; struct mad_capability_common *common;
dma_addr_t token;
u16 olen, len, status, min_len, cap_len;
u32 flag;
uint flag_bits = 0; long rc = 0;
olen = be16_to_cpu(mad->common.length); /* * struct capabilities hardcodes a couple capabilities after the * header, but the capabilities can actually be in any order.
*/
min_len = offsetof(struct capabilities, migration); if ((olen < min_len) || (olen > PAGE_SIZE)) {
dev_warn(&vscsi->dev, "cap_mad: invalid len %d\n", olen);
mad->common.status = cpu_to_be16(VIOSRP_MAD_FAILED); return 0;
}
len = olen - min_len;
status = VIOSRP_MAD_SUCCESS;
common = (struct mad_capability_common *)&cap->migration;
while ((len > 0) && (status == VIOSRP_MAD_SUCCESS) && !rc) {
dev_dbg(&vscsi->dev, "cap_mad: len left %hd, cap type %d, cap len %hd\n",
len, be32_to_cpu(common->cap_type),
be16_to_cpu(common->length));
cap_len = be16_to_cpu(common->length); if (cap_len > len) {
dev_err(&vscsi->dev, "cap_mad: cap len mismatch with total len\n");
status = VIOSRP_MAD_FAILED; break;
}
if (cap_len == 0) {
dev_err(&vscsi->dev, "cap_mad: cap len is 0\n");
status = VIOSRP_MAD_FAILED; break;
}
/** * ibmvscsis_process_mad() - Service a MAnagement Data gram * @vscsi: Pointer to our adapter structure * @iue: Information Unit containing the MAD request * * Must be called with interrupt lock held.
*/ staticlong ibmvscsis_process_mad(struct scsi_info *vscsi, struct iu_entry *iue)
{ struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad; struct viosrp_empty_iu *empty; long rc = ADAPT_SUCCESS;
switch (be32_to_cpu(mad->type)) { case VIOSRP_EMPTY_IU_TYPE:
empty = &vio_iu(iue)->mad.empty_iu;
vscsi->empty_iu_id = be64_to_cpu(empty->buffer);
vscsi->empty_iu_tag = be64_to_cpu(empty->common.tag);
mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS); break; case VIOSRP_ADAPTER_INFO_TYPE:
rc = ibmvscsis_adapter_info(vscsi, iue); break; case VIOSRP_CAPABILITIES_TYPE:
rc = ibmvscsis_cap_mad(vscsi, iue); break; case VIOSRP_ENABLE_FAST_FAIL: if (vscsi->state == CONNECTED) {
vscsi->fast_fail = true;
mad->status = cpu_to_be16(VIOSRP_MAD_SUCCESS);
} else {
dev_warn(&vscsi->dev, "fast fail mad sent after login\n");
mad->status = cpu_to_be16(VIOSRP_MAD_FAILED);
} break; default:
mad->status = cpu_to_be16(VIOSRP_MAD_NOT_SUPPORTED); break;
}
return rc;
}
/** * srp_snd_msg_failed() - Handle an error when sending a response * @vscsi: Pointer to our adapter structure * @rc: The return code from the h_send_crq command * * Must be called with interrupt lock held.
*/ staticvoid srp_snd_msg_failed(struct scsi_info *vscsi, long rc)
{
ktime_t kt;
if (rc != H_DROPPED) {
ibmvscsis_free_cmd_qs(vscsi);
if (rc == H_CLOSED)
vscsi->flags |= CLIENT_FAILED;
/* don't flag the same problem multiple times */ if (!(vscsi->flags & RESPONSE_Q_DOWN)) {
vscsi->flags |= RESPONSE_Q_DOWN; if (!(vscsi->state & (ERR_DISCONNECT |
ERR_DISCONNECT_RECONNECT |
ERR_DISCONNECTED | UNDEFINED))) {
dev_err(&vscsi->dev, "snd_msg_failed: setting RESPONSE_Q_DOWN, state 0x%hx, flags 0x%x, rc %ld\n",
vscsi->state, vscsi->flags, rc);
}
ibmvscsis_post_disconnect(vscsi,
ERR_DISCONNECT_RECONNECT, 0);
} return;
}
/* * The response queue is full. * If the server is processing SRP requests, i.e. * the client has successfully done an * SRP_LOGIN, then it will wait forever for room in * the queue. However if the system admin * is attempting to unconfigure the server then one * or more children will be in a state where * they are being removed. So if there is even one * child being removed then the driver assumes * the system admin is attempting to break the * connection with the client and MAX_TIMER_POPS * is honored.
*/ if ((vscsi->rsp_q_timer.timer_pops < MAX_TIMER_POPS) ||
(vscsi->state == SRP_PROCESSING)) {
dev_dbg(&vscsi->dev, "snd_msg_failed: response queue full, flags 0x%x, timer started %d, pops %d\n",
vscsi->flags, (int)vscsi->rsp_q_timer.started,
vscsi->rsp_q_timer.timer_pops);
/* * Check if the timer is running; if it * is not then start it up.
*/ if (!vscsi->rsp_q_timer.started) { if (vscsi->rsp_q_timer.timer_pops <
MAX_TIMER_POPS) {
kt = WAIT_NANO_SECONDS;
} else { /* * slide the timeslice if the maximum * timer pops have already happened
*/
kt = ktime_set(WAIT_SECONDS, 0);
}
vscsi->rsp_q_timer.started = true;
hrtimer_start(&vscsi->rsp_q_timer.timer, kt,
HRTIMER_MODE_REL);
}
} else { /* * TBD: Do we need to worry about this? Need to get * remove working.
*/ /* * waited a long time and it appears the system admin * is bring this driver down
*/
vscsi->flags |= RESPONSE_Q_DOWN;
ibmvscsis_free_cmd_qs(vscsi); /* * if the driver is already attempting to disconnect * from the client and has already logged an error * trace this event but don't put it in the error log
*/ if (!(vscsi->state & (ERR_DISCONNECT |
ERR_DISCONNECT_RECONNECT |
ERR_DISCONNECTED | UNDEFINED))) {
dev_err(&vscsi->dev, "client crq full too long\n");
ibmvscsis_post_disconnect(vscsi,
ERR_DISCONNECT_RECONNECT,
0);
}
}
}
/** * ibmvscsis_send_messages() - Send a Response * @vscsi: Pointer to our adapter structure * * Send a response, first checking the waiting queue. Responses are * sent in order they are received. If the response cannot be sent, * because the client queue is full, it stays on the waiting queue. * * PRECONDITION: * Called with interrupt lock held
*/ staticvoid ibmvscsis_send_messages(struct scsi_info *vscsi)
{ struct viosrp_crq empty_crq = { }; struct viosrp_crq *crq = &empty_crq; struct ibmvscsis_cmd *cmd, *nxt; long rc = ADAPT_SUCCESS; bool retry = false;
if (!(vscsi->flags & RESPONSE_Q_DOWN)) { do {
retry = false;
list_for_each_entry_safe(cmd, nxt, &vscsi->waiting_rsp,
list) { /* * Check to make sure abort cmd gets processed * prior to the abort tmr cmd
*/ if (cmd->flags & DELAY_SEND) continue;
/* * If CMD_T_ABORTED w/o CMD_T_TAS scenarios and * the case where LIO issued a * ABORT_TASK: Sending TMR_TASK_DOES_NOT_EXIST * case then we dont send a response, since it * was already done.
*/ if (cmd->se_cmd.transport_state & CMD_T_ABORTED &&
!(cmd->se_cmd.transport_state & CMD_T_TAS)) {
list_del(&cmd->list);
ibmvscsis_free_cmd_resources(vscsi,
cmd); /* * With a successfully aborted op * through LIO we want to increment the * the vscsi credit so that when we dont * send a rsp to the original scsi abort * op (h_send_crq), but the tm rsp to * the abort is sent, the credit is * correctly sent with the abort tm rsp. * We would need 1 for the abort tm rsp * and 1 credit for the aborted scsi op. * Thus we need to increment here. * Also we want to increment the credit * here because we want to make sure * cmd is actually released first * otherwise the client will think it * it can send a new cmd, and we could * find ourselves short of cmd elements.
*/
vscsi->credit += 1;
} else {
crq->valid = VALID_CMD_RESP_EL;
crq->format = cmd->rsp.format;
if (cmd->flags & CMD_FAST_FAIL)
crq->status = VIOSRP_ADAPTER_FAIL;
/* if all ok free up the command * element resources
*/ if (rc == H_SUCCESS) { /* some movement has occurred */
vscsi->rsp_q_timer.timer_pops = 0;
list_del(&cmd->list);
if (!rc) { /* * The timer could pop with the queue empty. If * this happens, rc will always indicate a * success; clear the pop count.
*/
vscsi->rsp_q_timer.timer_pops = 0;
}
} else {
ibmvscsis_free_cmd_qs(vscsi);
}
}
/* Called with intr lock held */ staticvoid ibmvscsis_send_mad_resp(struct scsi_info *vscsi, struct ibmvscsis_cmd *cmd, struct viosrp_crq *crq)
{ struct iu_entry *iue = cmd->iue; struct mad_common *mad = (struct mad_common *)&vio_iu(iue)->mad;
uint flag_bits = 0; long rc;
/** * ibmvscsis_mad() - Service a MAnagement Data gram. * @vscsi: Pointer to our adapter structure * @crq: Pointer to the CRQ entry containing the MAD request * * EXECUTION ENVIRONMENT: * Interrupt, called with adapter lock held
*/ staticlong ibmvscsis_mad(struct scsi_info *vscsi, struct viosrp_crq *crq)
{ struct iu_entry *iue; struct ibmvscsis_cmd *cmd; struct mad_common *mad; long rc = ADAPT_SUCCESS;
switch (vscsi->state) { /* * We have not exchanged Init Msgs yet, so this MAD was sent * before the last Transport Event; client will not be * expecting a response.
*/ case WAIT_CONNECTION:
dev_dbg(&vscsi->dev, "mad: in Wait Connection state, ignoring MAD, flags %d\n",
vscsi->flags); return ADAPT_SUCCESS;
case SRP_PROCESSING: case CONNECTED: break;
/* * We should never get here while we're in these states. * Just log an error and get out.
*/ case UNCONFIGURING: case WAIT_IDLE: case ERR_DISCONNECT: case ERR_DISCONNECT_RECONNECT: default:
dev_err(&vscsi->dev, "mad: invalid adapter state %d for mad\n",
vscsi->state); return ADAPT_SUCCESS;
}
cmd = ibmvscsis_get_free_cmd(vscsi); if (!cmd) {
dev_err(&vscsi->dev, "mad: failed to get cmd, debit %d\n",
vscsi->debit);
ibmvscsis_post_disconnect(vscsi, ERR_DISCONNECT_RECONNECT, 0); return ERROR;
}
iue = cmd->iue;
cmd->type = ADAPTER_MAD;
/** * ibmvscsis_srp_i_logout() - Helper Function to close I_T Nexus * @vscsi: Pointer to our adapter structure * @cmd: Command element to use to process the Implicit Logout request * @crq: Pointer to CRQ entry containing the Implicit Logout request * * Do the logic to close the I_T nexus. This function may not * behave to specification. * * EXECUTION ENVIRONMENT: * Interrupt, interrupt lock held
*/ staticlong ibmvscsis_srp_i_logout(struct scsi_info *vscsi, struct ibmvscsis_cmd *cmd, struct viosrp_crq *crq)
{ struct iu_entry *iue = cmd->iue; struct srp_i_logout *log_out = &vio_iu(iue)->srp.i_logout;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.