📄 sbp2.c
字号:
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = cmd->command_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
length = 8;
} else {
/*
* last_orb != NULL means: We know that the target's fetch agent
* is (very probably) not dead or in reset state right now.
* We have an ORB already sent that we can append a new one to.
* The target's fetch agent may or may not have read this
* previous ORB yet.
*/
dma_sync_single_for_cpu(hi->host->device.parent, last_orb_dma,
sizeof(struct sbp2_command_orb),
DMA_TO_DEVICE);
last_orb->next_ORB_lo = cpu_to_be32(cmd->command_orb_dma);
wmb();
/* Tells hardware that this pointer is valid */
last_orb->next_ORB_hi = 0;
dma_sync_single_for_device(hi->host->device.parent,
last_orb_dma,
sizeof(struct sbp2_command_orb),
DMA_TO_DEVICE);
addr += SBP2_DOORBELL_OFFSET;
data[0] = 0;
length = 4;
}
lu->last_orb = &cmd->command_orb;
lu->last_orb_dma = cmd->command_orb_dma;
spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
if (sbp2util_node_write_no_wait(lu->ne, addr, data, length)) {
/*
* sbp2util_node_write_no_wait failed. We certainly ran out
* of transaction labels, perhaps just because there were no
* context switches which gave khpsbpkt a chance to collect
* free tlabels. Try again in non-atomic context. If necessary,
* the workqueue job will sleep to guaranteedly get a tlabel.
* We do not accept new commands until the job is over.
*/
scsi_block_requests(lu->shost);
PREPARE_WORK(&lu->protocol_work,
last_orb ? sbp2util_write_doorbell:
sbp2util_write_orb_pointer);
schedule_work(&lu->protocol_work);
}
}
static int sbp2_send_command(struct sbp2_lu *lu, struct scsi_cmnd *SCpnt,
void (*done)(struct scsi_cmnd *))
{
unchar *scsi_cmd = (unchar *)SCpnt->cmnd;
unsigned int request_bufflen = SCpnt->request_bufflen;
struct sbp2_command_info *cmd;
cmd = sbp2util_allocate_command_orb(lu, SCpnt, done);
if (!cmd)
return -EIO;
sbp2_create_command_orb(lu, cmd, scsi_cmd, SCpnt->use_sg,
request_bufflen, SCpnt->request_buffer,
SCpnt->sc_data_direction);
sbp2_link_orb_command(lu, cmd);
return 0;
}
/*
* Translates SBP-2 status into SCSI sense data for check conditions
*/
static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status,
unchar *sense_data)
{
/* OK, it's pretty ugly... ;-) */
sense_data[0] = 0x70;
sense_data[1] = 0x0;
sense_data[2] = sbp2_status[9];
sense_data[3] = sbp2_status[12];
sense_data[4] = sbp2_status[13];
sense_data[5] = sbp2_status[14];
sense_data[6] = sbp2_status[15];
sense_data[7] = 10;
sense_data[8] = sbp2_status[16];
sense_data[9] = sbp2_status[17];
sense_data[10] = sbp2_status[18];
sense_data[11] = sbp2_status[19];
sense_data[12] = sbp2_status[10];
sense_data[13] = sbp2_status[11];
sense_data[14] = sbp2_status[20];
sense_data[15] = sbp2_status[21];
return sbp2_status[8] & 0x3f;
}
static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid,
int destid, quadlet_t *data, u64 addr,
size_t length, u16 fl)
{
struct sbp2_fwhost_info *hi;
struct sbp2_lu *lu = NULL, *lu_tmp;
struct scsi_cmnd *SCpnt = NULL;
struct sbp2_status_block *sb;
u32 scsi_status = SBP2_SCSI_STATUS_GOOD;
struct sbp2_command_info *cmd;
unsigned long flags;
if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) {
SBP2_ERR("Wrong size of status block");
return RCODE_ADDRESS_ERROR;
}
if (unlikely(!host)) {
SBP2_ERR("host is NULL - this is bad!");
return RCODE_ADDRESS_ERROR;
}
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
if (unlikely(!hi)) {
SBP2_ERR("host info is NULL - this is bad!");
return RCODE_ADDRESS_ERROR;
}
/* Find the unit which wrote the status. */
list_for_each_entry(lu_tmp, &hi->logical_units, lu_list) {
if (lu_tmp->ne->nodeid == nodeid &&
lu_tmp->status_fifo_addr == addr) {
lu = lu_tmp;
break;
}
}
if (unlikely(!lu)) {
SBP2_ERR("lu is NULL - device is gone?");
return RCODE_ADDRESS_ERROR;
}
/* Put response into lu status fifo buffer. The first two bytes
* come in big endian bit order. Often the target writes only a
* truncated status block, minimally the first two quadlets. The rest
* is implied to be zeros. */
sb = &lu->status_block;
memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent));
memcpy(sb, data, length);
sbp2util_be32_to_cpu_buffer(sb, 8);
/* Ignore unsolicited status. Handle command ORB status. */
if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2))
cmd = NULL;
else
cmd = sbp2util_find_command_for_orb(lu, sb->ORB_offset_lo);
if (cmd) {
dma_sync_single_for_cpu(hi->host->device.parent,
cmd->command_orb_dma,
sizeof(struct sbp2_command_orb),
DMA_TO_DEVICE);
dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma,
sizeof(cmd->scatter_gather_element),
DMA_TO_DEVICE);
/* Grab SCSI command pointers and check status. */
/*
* FIXME: If the src field in the status is 1, the ORB DMA must
* not be reused until status for a subsequent ORB is received.
*/
SCpnt = cmd->Current_SCpnt;
spin_lock_irqsave(&lu->cmd_orb_lock, flags);
sbp2util_mark_command_completed(lu, cmd);
spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
if (SCpnt) {
u32 h = sb->ORB_offset_hi_misc;
u32 r = STATUS_GET_RESP(h);
if (r != RESP_STATUS_REQUEST_COMPLETE) {
SBP2_INFO("resp 0x%x, sbp_status 0x%x",
r, STATUS_GET_SBP_STATUS(h));
scsi_status =
r == RESP_STATUS_TRANSPORT_FAILURE ?
SBP2_SCSI_STATUS_BUSY :
SBP2_SCSI_STATUS_COMMAND_TERMINATED;
}
if (STATUS_GET_LEN(h) > 1)
scsi_status = sbp2_status_to_sense_data(
(unchar *)sb, SCpnt->sense_buffer);
if (STATUS_TEST_DEAD(h))
sbp2_agent_reset(lu, 0);
}
/* Check here to see if there are no commands in-use. If there
* are none, we know that the fetch agent left the active state
* _and_ that we did not reactivate it yet. Therefore clear
* last_orb so that next time we write directly to the
* ORB_POINTER register. That way the fetch agent does not need
* to refetch the next_ORB. */
spin_lock_irqsave(&lu->cmd_orb_lock, flags);
if (list_empty(&lu->cmd_orb_inuse))
lu->last_orb = NULL;
spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
} else {
/* It's probably status after a management request. */
if ((sb->ORB_offset_lo == lu->reconnect_orb_dma) ||
(sb->ORB_offset_lo == lu->login_orb_dma) ||
(sb->ORB_offset_lo == lu->query_logins_orb_dma) ||
(sb->ORB_offset_lo == lu->logout_orb_dma)) {
lu->access_complete = 1;
wake_up_interruptible(&sbp2_access_wq);
}
}
if (SCpnt)
sbp2scsi_complete_command(lu, scsi_status, SCpnt,
cmd->Current_done);
return RCODE_COMPLETE;
}
/**************************************
* SCSI interface related section
**************************************/
static int sbp2scsi_queuecommand(struct scsi_cmnd *SCpnt,
void (*done)(struct scsi_cmnd *))
{
struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
struct sbp2_fwhost_info *hi;
int result = DID_NO_CONNECT << 16;
if (unlikely(!sbp2util_node_is_available(lu)))
goto done;
hi = lu->hi;
if (unlikely(!hi)) {
SBP2_ERR("sbp2_fwhost_info is NULL - this is bad!");
goto done;
}
/* Multiple units are currently represented to the SCSI core as separate
* targets, not as one target with multiple LUs. Therefore return
* selection time-out to any IO directed at non-zero LUNs. */
if (unlikely(SCpnt->device->lun))
goto done;
if (unlikely(!hpsb_node_entry_valid(lu->ne))) {
SBP2_ERR("Bus reset in progress - rejecting command");
result = DID_BUS_BUSY << 16;
goto done;
}
/* Bidirectional commands are not yet implemented,
* and unknown transfer direction not handled. */
if (unlikely(SCpnt->sc_data_direction == DMA_BIDIRECTIONAL)) {
SBP2_ERR("Cannot handle DMA_BIDIRECTIONAL - rejecting command");
result = DID_ERROR << 16;
goto done;
}
if (sbp2_send_command(lu, SCpnt, done)) {
SBP2_ERR("Error sending SCSI command");
sbp2scsi_complete_command(lu,
SBP2_SCSI_STATUS_SELECTION_TIMEOUT,
SCpnt, done);
}
return 0;
done:
SCpnt->result = result;
done(SCpnt);
return 0;
}
static void sbp2scsi_complete_all_commands(struct sbp2_lu *lu, u32 status)
{
struct sbp2_fwhost_info *hi = lu->hi;
struct list_head *lh;
struct sbp2_command_info *cmd;
unsigned long flags;
spin_lock_irqsave(&lu->cmd_orb_lock, flags);
while (!list_empty(&lu->cmd_orb_inuse)) {
lh = lu->cmd_orb_inuse.next;
cmd = list_entry(lh, struct sbp2_command_info, list);
dma_sync_single_for_cpu(hi->host->device.parent,
cmd->command_orb_dma,
sizeof(struct sbp2_command_orb),
DMA_TO_DEVICE);
dma_sync_single_for_cpu(hi->host->device.parent, cmd->sge_dma,
sizeof(cmd->scatter_gather_element),
DMA_TO_DEVICE);
sbp2util_mark_command_completed(lu, cmd);
if (cmd->Current_SCpnt) {
cmd->Current_SCpnt->result = status << 16;
cmd->Current_done(cmd->Current_SCpnt);
}
}
spin_unlock_irqrestore(&lu->cmd_orb_lock, flags);
return;
}
/*
* Complete a regular SCSI command. Can be called in atomic context.
*/
static void sbp2scsi_complete_command(struct sbp2_lu *lu, u32 scsi_status,
struct scsi_cmnd *SCpnt,
void (*done)(struct scsi_cmnd *))
{
if (!SCpnt) {
SBP2_ERR("SCpnt is NULL");
return;
}
switch (scsi_status) {
case SBP2_SCSI_STATUS_GOOD:
SCpnt->result = DID_OK << 16;
break;
case SBP2_SCSI_STATUS_BUSY:
SBP2_ERR("SBP2_SCSI_STATUS_BUSY");
SCpnt->result = DID_BUS_BUSY << 16;
break;
case SBP2_SCSI_STATUS_CHECK_CONDITION:
SCpnt->result = CHECK_CONDITION << 1 | DID_OK << 16;
break;
case SBP2_SCSI_STATUS_SELECTION_TIMEOUT:
SBP2_ERR("SBP2_SCSI_STATUS_SELECTION_TIMEOUT");
SCpnt->result = DID_NO_CONNECT << 16;
scsi_print_command(SCpnt);
break;
case SBP2_SCSI_STATUS_CONDITION_MET:
case SBP2_SCSI_STATUS_RESERVATION_CONFLICT:
case SBP2_SCSI_STATUS_COMMAND_TERMINATED:
SBP2_ERR("Bad SCSI status = %x", scsi_status);
SCpnt->result = DID_ERROR << 16;
scsi_print_command(SCpnt);
break;
default:
SBP2_ERR("Unsupported SCSI status = %x", scsi_status);
SCpnt->result = DID_ERROR << 16;
}
/* If a bus reset is in progress and there was an error, complete
* the command as busy so that it will get retried. */
if (!hpsb_node_entry_valid(lu->ne)
&& (scsi_status != SBP2_SCSI_STATUS_GOOD)) {
SBP2_ERR("Completing command with busy (bus reset)");
SCpnt->result = DID_BUS_BUSY << 16;
}
/* Tell the SCSI stack that we're done with this command. */
done(SCpnt);
}
static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
{
struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];
lu->sdev = sdev;
sdev->allow_restart = 1;
if (lu->workarounds & SBP2_WORKAROUND_INQUIRY_36)
sdev->inquiry_len = 36;
return 0;
}
static int sbp2scsi_slave_configure(struct scsi_device *sdev)
{
struct sbp2_lu *lu = (struct sbp2_lu *)sdev->host->hostdata[0];
sdev->use_10_for_rw = 1;
if (sdev->type == TYPE_ROM)
sdev->use_10_for_ms = 1;
if (sdev->type == TYPE_DISK &&
lu->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
sdev->skip_ms_page_8 = 1;
if (lu->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
sdev->fix_capacity = 1;
return 0;
}
static void sbp2scsi_slave_destroy(struct scsi_device *sdev)
{
((struct sbp2_lu *)sdev->host->hostdata[0])->sdev = NULL;
return;
}
/*
* Called by scsi stack when something has really gone wrong.
* Usually called when a command has timed-out for some reason.
*/
static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
{
struct sbp2_lu *lu = (struct sbp2_lu *)SCpnt->device->host->hostdata[0];
struct sbp2_fwhost_info *hi = lu->hi;
struct sbp2_command_info *cmd;
unsigned long flags;
SBP2_INFO("aborting sbp2 command");
scsi_print_command(SCpnt);
if (sbp2util_node_is_available(lu)) {
sbp2_agent_reset(lu, 1);
/* Return a matching command structure to the free pool. */
spin_lock_irqsave(&lu->cmd_orb_lock, flags);
cmd = sbp2util_find_command_for_SCpnt(lu, SCpnt);
if (cmd) {
dma_sync_single_for_cpu(hi->host->device.parent,
cmd->command_orb_dma,
sizeof(struct sbp2_command_orb),
DMA_TO_DEVICE);
dma_sync_single_for_cpu(hi->host
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -