📄 sbp2.c
字号:
u64 addr; int retval; unsigned long flags; SBP2_DEBUG_ENTER(); cancel_delayed_work(&scsi_id->protocol_work); if (wait) flush_scheduled_work(); data = ntohl(SBP2_AGENT_RESET_DATA); addr = scsi_id->sbp2_command_block_agent_addr + SBP2_AGENT_RESET_OFFSET; if (wait) retval = hpsb_node_write(scsi_id->ne, addr, &data, 4); else retval = sbp2util_node_write_no_wait(scsi_id->ne, addr, &data, 4); if (retval < 0) { SBP2_ERR("hpsb_node_write failed.\n"); return -EIO; } /* * Need to make sure orb pointer is written on next command */ spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); scsi_id->last_orb = NULL; spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); return 0;}static void sbp2_prep_command_orb_sg(struct sbp2_command_orb *orb, struct sbp2scsi_host_info *hi, struct sbp2_command_info *command, unsigned int scsi_use_sg, struct scatterlist *sgpnt, u32 orb_direction, enum dma_data_direction dma_dir){ command->dma_dir = dma_dir; orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); orb->misc |= ORB_SET_DIRECTION(orb_direction); /* Special case if only one element (and less than 64KB in size) */ if ((scsi_use_sg == 1) && (sgpnt[0].length <= SBP2_MAX_SG_ELEMENT_LENGTH)) { SBP2_DEBUG("Only one s/g element"); command->dma_size = sgpnt[0].length; command->dma_type = CMD_DMA_PAGE; command->cmd_dma = pci_map_page(hi->host->pdev, sgpnt[0].page, sgpnt[0].offset, command->dma_size, command->dma_dir); SBP2_DMA_ALLOC("single page scatter element"); orb->data_descriptor_lo = command->cmd_dma; orb->misc |= ORB_SET_DATA_SIZE(command->dma_size); } else { struct sbp2_unrestricted_page_table *sg_element = &command->scatter_gather_element[0]; u32 sg_count, sg_len; dma_addr_t sg_addr; int i, count = pci_map_sg(hi->host->pdev, sgpnt, scsi_use_sg, dma_dir); SBP2_DMA_ALLOC("scatter list"); command->dma_size = scsi_use_sg; command->sge_buffer = sgpnt; /* use page tables (s/g) */ orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); orb->data_descriptor_lo = command->sge_dma; /* * Loop through and fill out our sbp-2 page tables * (and split up anything too large) */ for (i = 0, sg_count = 0 ; i < count; i++, sgpnt++) { sg_len = sg_dma_len(sgpnt); sg_addr = sg_dma_address(sgpnt); while (sg_len) { sg_element[sg_count].segment_base_lo = sg_addr; if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) { sg_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(SBP2_MAX_SG_ELEMENT_LENGTH); sg_addr += SBP2_MAX_SG_ELEMENT_LENGTH; sg_len -= SBP2_MAX_SG_ELEMENT_LENGTH; } else { sg_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(sg_len); sg_len = 0; } sg_count++; } } /* Number of page table (s/g) elements */ orb->misc |= ORB_SET_DATA_SIZE(sg_count); sbp2util_packet_dump(sg_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count, "sbp2 s/g list", command->sge_dma); /* Byte swap page tables if necessary */ sbp2util_cpu_to_be32_buffer(sg_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count); }}static void sbp2_prep_command_orb_no_sg(struct sbp2_command_orb *orb, struct sbp2scsi_host_info *hi, struct sbp2_command_info *command, struct scatterlist *sgpnt, u32 orb_direction, unsigned int scsi_request_bufflen, void *scsi_request_buffer, enum dma_data_direction dma_dir){ command->dma_dir = dma_dir; command->dma_size = scsi_request_bufflen; command->dma_type = CMD_DMA_SINGLE; command->cmd_dma = pci_map_single(hi->host->pdev, scsi_request_buffer, command->dma_size, command->dma_dir); orb->data_descriptor_hi = ORB_SET_NODE_ID(hi->host->node_id); orb->misc |= ORB_SET_DIRECTION(orb_direction); SBP2_DMA_ALLOC("single bulk"); /* * Handle case where we get a command w/o s/g enabled (but * check for transfers larger than 64K) */ if (scsi_request_bufflen <= SBP2_MAX_SG_ELEMENT_LENGTH) { orb->data_descriptor_lo = command->cmd_dma; orb->misc |= ORB_SET_DATA_SIZE(scsi_request_bufflen); } else { struct sbp2_unrestricted_page_table *sg_element = &command->scatter_gather_element[0]; u32 sg_count, sg_len; dma_addr_t sg_addr; /* * Need to turn this into page tables, since the * buffer is too large. */ orb->data_descriptor_lo = command->sge_dma; /* Use page tables (s/g) */ orb->misc |= ORB_SET_PAGE_TABLE_PRESENT(0x1); /* * fill out our sbp-2 page tables (and split up * the large buffer) */ sg_count = 0; sg_len = scsi_request_bufflen; sg_addr = command->cmd_dma; while (sg_len) { sg_element[sg_count].segment_base_lo = sg_addr; if (sg_len > SBP2_MAX_SG_ELEMENT_LENGTH) { sg_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(SBP2_MAX_SG_ELEMENT_LENGTH); sg_addr += SBP2_MAX_SG_ELEMENT_LENGTH; sg_len -= SBP2_MAX_SG_ELEMENT_LENGTH; } else { sg_element[sg_count].length_segment_base_hi = PAGE_TABLE_SET_SEGMENT_LENGTH(sg_len); sg_len = 0; } sg_count++; } /* Number of page table (s/g) elements */ orb->misc |= ORB_SET_DATA_SIZE(sg_count); sbp2util_packet_dump(sg_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count, "sbp2 s/g list", command->sge_dma); /* Byte swap page tables if necessary */ sbp2util_cpu_to_be32_buffer(sg_element, (sizeof(struct sbp2_unrestricted_page_table)) * sg_count); }}/* * This function is called to create the actual command orb and s/g list * out of the scsi command itself. */static void sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command, unchar *scsi_cmd, unsigned int scsi_use_sg, unsigned int scsi_request_bufflen, void *scsi_request_buffer, enum dma_data_direction dma_dir){ struct sbp2scsi_host_info *hi = scsi_id->hi; struct scatterlist *sgpnt = (struct scatterlist *)scsi_request_buffer; struct sbp2_command_orb *command_orb = &command->command_orb; u32 orb_direction; /* * Set-up our command ORB.. * * NOTE: We're doing unrestricted page tables (s/g), as this is * best performance (at least with the devices I have). This means * that data_size becomes the number of s/g elements, and * page_size should be zero (for unrestricted). */ command_orb->next_ORB_hi = ORB_SET_NULL_PTR(1); command_orb->next_ORB_lo = 0x0; command_orb->misc = ORB_SET_MAX_PAYLOAD(scsi_id->max_payload_size); command_orb->misc |= ORB_SET_SPEED(scsi_id->speed_code); command_orb->misc |= ORB_SET_NOTIFY(1); /* Notify us when complete */ if (dma_dir == DMA_NONE) orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; else if (dma_dir == DMA_TO_DEVICE && scsi_request_bufflen) orb_direction = ORB_DIRECTION_WRITE_TO_MEDIA; else if (dma_dir == DMA_FROM_DEVICE && scsi_request_bufflen) orb_direction = ORB_DIRECTION_READ_FROM_MEDIA; else { SBP2_WARN("Falling back to DMA_NONE"); orb_direction = ORB_DIRECTION_NO_DATA_TRANSFER; } /* Set-up our pagetable stuff */ if (orb_direction == ORB_DIRECTION_NO_DATA_TRANSFER) { SBP2_DEBUG("No data transfer"); command_orb->data_descriptor_hi = 0x0; command_orb->data_descriptor_lo = 0x0; command_orb->misc |= ORB_SET_DIRECTION(1); } else if (scsi_use_sg) { SBP2_DEBUG("Use scatter/gather"); sbp2_prep_command_orb_sg(command_orb, hi, command, scsi_use_sg, sgpnt, orb_direction, dma_dir); } else { SBP2_DEBUG("No scatter/gather"); sbp2_prep_command_orb_no_sg(command_orb, hi, command, sgpnt, orb_direction, scsi_request_bufflen, scsi_request_buffer, dma_dir); } /* Byte swap command ORB if necessary */ sbp2util_cpu_to_be32_buffer(command_orb, sizeof(struct sbp2_command_orb)); /* Put our scsi command in the command ORB */ memset(command_orb->cdb, 0, 12); memcpy(command_orb->cdb, scsi_cmd, COMMAND_SIZE(*scsi_cmd));}/* * This function is called in order to begin a regular SBP-2 command. */static void sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id, struct sbp2_command_info *command){ struct sbp2scsi_host_info *hi = scsi_id->hi; struct sbp2_command_orb *command_orb = &command->command_orb; struct sbp2_command_orb *last_orb; dma_addr_t last_orb_dma; u64 addr = scsi_id->sbp2_command_block_agent_addr; quadlet_t data[2]; size_t length; unsigned long flags; outstanding_orb_incr; SBP2_ORB_DEBUG("sending command orb %p, total orbs = %x", command_orb, global_outstanding_command_orbs); pci_dma_sync_single_for_device(hi->host->pdev, command->command_orb_dma, sizeof(struct sbp2_command_orb), PCI_DMA_TODEVICE); pci_dma_sync_single_for_device(hi->host->pdev, command->sge_dma, sizeof(command->scatter_gather_element), PCI_DMA_BIDIRECTIONAL); /* * Check to see if there are any previous orbs to use */ spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags); last_orb = scsi_id->last_orb; last_orb_dma = scsi_id->last_orb_dma; if (!last_orb) { /* * last_orb == NULL means: We know that the target's fetch agent * is not active right now. */ addr += SBP2_ORB_POINTER_OFFSET; data[0] = ORB_SET_NODE_ID(hi->host->node_id); data[1] = command->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. */ pci_dma_sync_single_for_cpu(hi->host->pdev, last_orb_dma, sizeof(struct sbp2_command_orb), PCI_DMA_TODEVICE); last_orb->next_ORB_lo = cpu_to_be32(command->command_orb_dma); wmb(); /* Tells hardware that this pointer is valid */ last_orb->next_ORB_hi = 0; pci_dma_sync_single_for_device(hi->host->pdev, last_orb_dma, sizeof(struct sbp2_command_orb), PCI_DMA_TODEVICE); addr += SBP2_DOORBELL_OFFSET; data[0] = 0; length = 4; } scsi_id->last_orb = command_orb; scsi_id->last_orb_dma = command->command_orb_dma; spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags); SBP2_ORB_DEBUG("write to %s register, command orb %p", last_orb ? "DOORBELL" : "ORB_POINTER", command_orb); if (sbp2util_node_write_no_wait(scsi_id->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(scsi_id->scsi_host); PREPARE_WORK(&scsi_id->protocol_work, last_orb ? sbp2util_write_doorbell: sbp2util_write_orb_pointer, scsi_id); schedule_work(&scsi_id->protocol_work); }}/* * This function is called in order to begin a regular SBP-2 command. */static int sbp2_send_command(struct scsi_id_instance_data *scsi_id, struct scsi_cmnd *SCpnt, void (*done)(struct scsi_cmnd *)){ unchar *cmd = (unchar *) SCpnt->cmnd; unsigned int request_bufflen = SCpnt->request_bufflen; struct sbp2_command_info *command; SBP2_DEBUG_ENTER(); SBP2_DEBUG("SCSI transfer size = %x", request_bufflen); SBP2_DEBUG("SCSI s/g elements = %x", (unsigned int)SCpnt->use_sg); /* * Allocate a command orb and s/g structure */ command = sbp2util_allocate_command_orb(scsi_id, SCpnt, done); if (!command) { return -EIO; } /* * Now actually fill in the comamnd orb and sbp2 s/g list */ sbp2_create_command_orb(scsi_id, command, cmd, SCpnt->use_sg, request_bufflen, SCpnt->request_buffer, SCpnt->sc_data_direction); sbp2util_packet_dump(&command->command_orb, sizeof(struct sbp2_command_orb), "sbp2 command orb", command->command_orb_dma); /* * Link up the orb, and ring the doorbell if needed */ sbp2_link_orb_command(scsi_id, command); 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){ SBP2_DEBUG_ENTER(); /* * 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; /* return scsi status */}/* * This function deals with status writes from the SBP-2 device */static int sbp2_handle
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -